summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Abramowitz <marc@marc-abramowitz.com>2014-04-05 23:13:11 -0700
committerThanh Ha <zxiiro@linux.com>2018-03-09 10:58:21 -0500
commit8d4671e3a7d00c7b00ba0c25f220b480669cb41d (patch)
tree52afba1cfc98131623057a211171174641f8c5b1
parent72dfe4118655fbd6ee2f5bc1fc79cecd4ecdd4a1 (diff)
Add a "list" subcommand
Add a list subcommand that allows listing of all the jobs defined in yaml configuration, or on the Jenkins master, with the option of taking glob parameters to allow matching some job names. Also supports the recursive and exclude path control options. jenkins-jobs list 'wikimedia-fundraising*' jenkins-jobs list -r -p configs/ 'wikimedia-fundraising*' Co-Authored-By: Darragh Bailey <dbailey@hpe.com> Co-Authored-By: Sorin Sbarnea <ssbarnea@redhat.com> Change-Id: I897b9ed35561e455dc6b89c3bacec74b54777903 Signed-off-by: Sorin Sbarnea <ssbarnea@redhat.com>
Notes
Notes (review): Code-Review+2: Thanh Ha <zxiiro@gmail.com> Code-Review+2: Sorin Sbarnea <ssbarnea@redhat.com> Workflow+1: Darragh Bailey <daragh.bailey@gmail.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Wed, 14 Mar 2018 13:55:11 +0000 Reviewed-on: https://review.openstack.org/87090 Project: openstack-infra/jenkins-job-builder Branch: refs/heads/master
-rw-r--r--jenkins_jobs/cli/parser.py2
-rw-r--r--jenkins_jobs/cli/subcommand/list.py74
-rw-r--r--setup.cfg1
-rw-r--r--tests/cmd/subcommands/test_list.py85
4 files changed, 161 insertions, 1 deletions
diff --git a/jenkins_jobs/cli/parser.py b/jenkins_jobs/cli/parser.py
index cdf0fc3..eb0ff39 100644
--- a/jenkins_jobs/cli/parser.py
+++ b/jenkins_jobs/cli/parser.py
@@ -81,7 +81,7 @@ def create_parser():
81 81
82 subparser = parser.add_subparsers( 82 subparser = parser.add_subparsers(
83 dest='command', 83 dest='command',
84 help="update, test or delete job") 84 help="update, test, list or delete job")
85 85
86 extension_manager = extension.ExtensionManager( 86 extension_manager = extension.ExtensionManager(
87 namespace='jjb.cli.subcommands', 87 namespace='jjb.cli.subcommands',
diff --git a/jenkins_jobs/cli/subcommand/list.py b/jenkins_jobs/cli/subcommand/list.py
new file mode 100644
index 0000000..df2fd35
--- /dev/null
+++ b/jenkins_jobs/cli/subcommand/list.py
@@ -0,0 +1,74 @@
1#!/usr/bin/env python
2# Copyright (C) 2018 Sorin Sbarnea
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15import logging
16import sys
17import jenkins_jobs.cli.subcommand.base as base
18import jenkins_jobs.utils as utils
19import jenkins_jobs.builder as builder
20import jenkins_jobs.parser as parser
21import jenkins_jobs.registry as registry
22
23
24def list_duplicates(seq):
25 seen = set()
26 return set(x for x in seq if x in seen or seen.add(x))
27
28
29class ListSubCommand(base.BaseSubCommand):
30
31 def parse_args(self, subparser):
32 list = subparser.add_parser('list', help="List jobs")
33
34 self.parse_option_recursive_exclude(list)
35
36 list.add_argument('names',
37 help='name(s) of job(s)',
38 nargs='*',
39 default=None)
40 list.add_argument('-p', '--path', default=None,
41 help='path to YAML file or directory')
42
43 def execute(self, options, jjb_config):
44 self.jjb_config = jjb_config
45 self.jenkins = builder.JenkinsManager(jjb_config)
46
47 jobs = self.get_jobs(options.names, options.path)
48
49 logging.info("Matching jobs: %d", len(jobs))
50 stdout = utils.wrap_stream(sys.stdout)
51
52 for job in jobs:
53 stdout.write((job + '\n').encode('utf-8'))
54
55 def get_jobs(self, jobs_glob=None, fn=None):
56 if fn:
57 r = registry.ModuleRegistry(self.jjb_config,
58 self.jenkins.plugins_list)
59 p = parser.YamlParser(self.jjb_config)
60 p.load_files(fn)
61 p.expandYaml(r, jobs_glob)
62 jobs = [j['name'] for j in p.jobs]
63 else:
64 jobs = [j['name'] for j in self.jenkins.get_jobs()
65 if not jobs_glob or parser.matches(j['name'], jobs_glob)]
66
67 jobs = sorted(jobs)
68 for duplicate in list_duplicates(jobs):
69 logging.warning("Found duplicate job name '%s', likely bug.",
70 duplicate)
71
72 logging.debug("Builder.get_jobs: returning %r", jobs)
73
74 return jobs
diff --git a/setup.cfg b/setup.cfg
index ecb3023..6a75197 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -48,6 +48,7 @@ jjb.cli.subcommands =
48 delete=jenkins_jobs.cli.subcommand.delete:DeleteSubCommand 48 delete=jenkins_jobs.cli.subcommand.delete:DeleteSubCommand
49 delete-all=jenkins_jobs.cli.subcommand.delete_all:DeleteAllSubCommand 49 delete-all=jenkins_jobs.cli.subcommand.delete_all:DeleteAllSubCommand
50 get-plugins-info=jenkins_jobs.cli.subcommand.get_plugins_info:GetPluginsInfoSubCommand 50 get-plugins-info=jenkins_jobs.cli.subcommand.get_plugins_info:GetPluginsInfoSubCommand
51 list=jenkins_jobs.cli.subcommand.list:ListSubCommand
51jenkins_jobs.projects = 52jenkins_jobs.projects =
52 externaljob=jenkins_jobs.modules.project_externaljob:ExternalJob 53 externaljob=jenkins_jobs.modules.project_externaljob:ExternalJob
53 flow=jenkins_jobs.modules.project_flow:Flow 54 flow=jenkins_jobs.modules.project_flow:Flow
diff --git a/tests/cmd/subcommands/test_list.py b/tests/cmd/subcommands/test_list.py
new file mode 100644
index 0000000..1e413ee
--- /dev/null
+++ b/tests/cmd/subcommands/test_list.py
@@ -0,0 +1,85 @@
1#!/usr/bin/env python
2# Copyright (C) 2018 Sorin Sbarnea
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15import io
16import os
17
18from testscenarios.testcase import TestWithScenarios
19
20from tests.base import mock
21from tests.cmd.test_cmd import CmdTestsBase
22
23
24@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info',
25 mock.MagicMock)
26class ListFromJenkinsTests(TestWithScenarios, CmdTestsBase):
27
28 scenarios = [
29 ('single',
30 dict(jobs=['job1'], globs=[], found=['job1'])),
31 ('multiple',
32 dict(jobs=['job1', 'job2'], globs=[], found=['job1', 'job2'])),
33 ('multiple_with_glob',
34 dict(jobs=['job1', 'job2', 'job3'], globs=["job[1-2]"],
35 found=['job1', 'job2'])),
36 ('multiple_with_multi_glob',
37 dict(jobs=['job1', 'job2', 'job3', 'job4'],
38 globs=["job1", "job[24]"],
39 found=['job1', 'job2', 'job4'])),
40 ]
41
42 @mock.patch('jenkins_jobs.builder.JenkinsManager.get_jobs')
43 def test_list(self, get_jobs_mock):
44
45 def _get_jobs():
46 return [{'name': name} for name in self.jobs]
47
48 get_jobs_mock.side_effect = _get_jobs
49 console_out = io.BytesIO()
50
51 args = ['--conf', self.default_config_file, 'list'] + self.globs
52
53 with mock.patch('sys.stdout', console_out):
54 self.execute_jenkins_jobs_with_args(args)
55
56 self.assertEqual(console_out.getvalue().decode('utf-8').rstrip(),
57 ('\n'.join(self.found)))
58
59
60@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info',
61 mock.MagicMock)
62class ListFromYamlTests(TestWithScenarios, CmdTestsBase):
63
64 scenarios = [
65 ('all',
66 dict(globs=[], found=['bam001', 'bar001', 'bar002', 'baz001'])),
67 ('some',
68 dict(globs=["*am*", "*002", "bar001"],
69 found=['bam001', 'bar001', 'bar002'])),
70 ]
71
72 def test_list(self):
73 path = os.path.join(self.fixtures_path, 'cmd-002.yaml')
74
75 console_out = io.BytesIO()
76 with mock.patch('sys.stdout', console_out):
77 self.execute_jenkins_jobs_with_args(
78 ['--conf',
79 self.default_config_file,
80 'list',
81 '-p',
82 path] + self.globs)
83
84 self.assertEqual(console_out.getvalue().decode('utf-8').rstrip(),
85 ('\n'.join(self.found)))