Move subcommand logic into subcommand classes.

Also, add copyright notices to subcommand files and fix mock import
paths in tests.

Change-Id: Ia8a96d5d5c460387968437d86cf7144ef382b20c
This commit is contained in:
Wayne Warren 2016-01-02 00:17:00 -08:00
parent 7373201882
commit 99fd084099
8 changed files with 179 additions and 95 deletions

View File

@ -18,12 +18,10 @@ import os
import logging
import platform
import sys
import time
from stevedore import extension
import yaml
from jenkins_jobs.builder import Builder
from jenkins_jobs.parser import YamlParser
from jenkins_jobs.cli.parser import create_parser
from jenkins_jobs.config import JJBConfig
from jenkins_jobs import utils
@ -129,83 +127,13 @@ class JenkinsJobs(object):
self.options.path = paths
def execute(self):
options = self.options
builder = Builder(self.jjb_config)
if options.command == 'delete':
parser = YamlParser(self.jjb_config, builder.plugins_list)
extension_manager = extension.ExtensionManager(
namespace='jjb.cli.subcommands',
invoke_on_load=True,)
fn = options.path
for jobs_glob in options.name:
parser = YamlParser(self.jjb_config, builder.plugins_list)
if fn:
parser.load_files(fn)
parser.expandYaml([jobs_glob])
jobs = [j['name'] for j in parser.jobs]
else:
jobs = [jobs_glob]
builder.delete_job(jobs)
elif options.command == 'delete-all':
if not utils.confirm(
'Sure you want to delete *ALL* jobs from Jenkins '
'server?\n(including those not managed by Jenkins '
'Job Builder)'):
sys.exit('Aborted')
logger.info("Deleting all jobs")
builder.delete_all_jobs()
elif options.command == 'update':
if options.n_workers < 0:
self.parser.error(
'Number of workers must be equal or greater than 0')
logger.info("Updating jobs in {0} ({1})".format(
options.path, options.names))
orig = time.time()
# Generate XML
parser = YamlParser(self.jjb_config, builder.plugins_list)
parser.load_files(options.path)
parser.expandYaml(options.names)
parser.generateXML()
jobs = parser.jobs
step = time.time()
logging.debug('%d XML files generated in %ss',
len(jobs), str(step - orig))
jobs, num_updated_jobs = builder.update_jobs(
parser.xml_jobs,
n_workers=options.n_workers)
logger.info("Number of jobs updated: %d", num_updated_jobs)
if options.delete_old:
n = builder.delete_old_managed(keep=parser.xml_jobs)
logger.info("Number of jobs deleted: %d", n)
elif options.command == 'test':
logger.info("Updating jobs in {0} ({1})".format(
options.path, options.name))
orig = time.time()
# Generate XML
parser = YamlParser(self.jjb_config, builder.plugins_list)
parser.load_files(options.path)
parser.expandYaml(options.name)
parser.generateXML()
jobs = parser.jobs
step = time.time()
logging.debug('%d XML files generated in %ss',
len(jobs), str(step - orig))
builder.update_jobs(parser.xml_jobs, output=options.output_dir,
n_workers=1)
ext = extension_manager[self.options.command]
ext.obj.execute(self.options, self.jjb_config)
def main():

View File

@ -1,4 +1,21 @@
#!/usr/bin/env python
# Copyright (C) 2015 Wayne Warren
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from jenkins_jobs.builder import Builder
from jenkins_jobs.parser import YamlParser
import jenkins_jobs.cli.subcommand.base as base
@ -19,5 +36,21 @@ class DeleteSubCommand(base.BaseSubCommand):
help='''colon-separated list of paths to YAML files or
directories''')
def execute(self, config):
raise NotImplementedError
def execute(self, options, jjb_config):
builder = Builder(jjb_config)
parser = YamlParser(jjb_config, builder.plugins_list)
fn = options.path
for jobs_glob in options.name:
parser = YamlParser(jjb_config, builder.plugins_list)
if fn:
parser.load_files(fn)
parser.expandYaml([jobs_glob])
jobs = [j['name'] for j in parser.jobs]
else:
jobs = [jobs_glob]
builder.delete_job(jobs)

View File

@ -1,7 +1,30 @@
#!/usr/bin/env python
# Copyright (C) 2015 Wayne Warren
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import sys
from jenkins_jobs import utils
from jenkins_jobs.builder import Builder
import jenkins_jobs.cli.subcommand.base as base
logger = logging.getLogger(__name__)
class DeleteAllSubCommand(base.BaseSubCommand):
def parse_args(self, subparser):
@ -12,5 +35,14 @@ class DeleteAllSubCommand(base.BaseSubCommand):
self.parse_option_recursive_exclude(delete_all)
def execute(self, config):
raise NotImplementedError
def execute(self, options, jjb_config):
builder = Builder(jjb_config)
if not utils.confirm(
'Sure you want to delete *ALL* jobs from Jenkins '
'server?\n(including those not managed by Jenkins '
'Job Builder)'):
sys.exit('Aborted')
logger.info("Deleting all jobs")
builder.delete_all_jobs()

View File

@ -1,8 +1,30 @@
import sys
#!/usr/bin/env python
# Copyright (C) 2015 Wayne Warren
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import sys
import time
from jenkins_jobs.builder import Builder
from jenkins_jobs.parser import YamlParser
import jenkins_jobs.cli.subcommand.base as base
logger = logging.getLogger(__name__)
class TestSubCommand(base.BaseSubCommand):
def parse_args(self, subparser):
test = subparser.add_parser('test')
@ -29,5 +51,23 @@ class TestSubCommand(base.BaseSubCommand):
'name',
help='name(s) of job(s)', nargs='*')
def execute(self, config):
raise NotImplementedError
def execute(self, options, jjb_config):
builder = Builder(jjb_config)
logger.info("Updating jobs in {0} ({1})".format(
options.path, options.name))
orig = time.time()
# Generate XML
parser = YamlParser(jjb_config, builder.plugins_list)
parser.load_files(options.path)
parser.expandYaml(options.name)
parser.generateXML()
jobs = parser.jobs
step = time.time()
logging.debug('%d XML files generated in %ss',
len(jobs), str(step - orig))
builder.update_jobs(parser.xml_jobs, output=options.output_dir,
n_workers=1)

View File

@ -1,7 +1,30 @@
#!/usr/bin/env python
# Copyright (C) 2015 Wayne Warren
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import time
from jenkins_jobs.builder import Builder
from jenkins_jobs.parser import YamlParser
from jenkins_jobs.errors import JenkinsJobsException
import jenkins_jobs.cli.subcommand.base as base
logger = logging.getLogger(__name__)
class UpdateSubCommand(base.BaseSubCommand):
def parse_args(self, subparser):
update = subparser.add_parser('update')
@ -29,5 +52,33 @@ class UpdateSubCommand(base.BaseSubCommand):
help='''number of workers to use, 0 for autodetection and 1 for
just one worker.''')
def execute(self, config):
raise NotImplementedError
def execute(self, options, jjb_config):
builder = Builder(jjb_config)
if options.n_workers < 0:
raise JenkinsJobsException(
'Number of workers must be equal or greater than 0')
logger.info("Updating jobs in {0} ({1})".format(
options.path, options.names))
orig = time.time()
# Generate XML
parser = YamlParser(jjb_config, builder.plugins_list)
parser.load_files(options.path)
parser.expandYaml(options.names)
parser.generateXML()
jobs = parser.jobs
step = time.time()
logging.debug('%d XML files generated in %ss',
len(jobs), str(step - orig))
jobs, num_updated_jobs = builder.update_jobs(
parser.xml_jobs,
n_workers=options.n_workers)
logger.info("Number of jobs updated: %d", num_updated_jobs)
if options.delete_old:
n = builder.delete_old_managed(keep=parser.xml_jobs)
logger.info("Number of jobs deleted: %d", n)

View File

@ -27,7 +27,7 @@ from tests.cmd.test_cmd import CmdTestsBase
@mock.patch('jenkins_jobs.builder.Jenkins.get_plugins_info', mock.MagicMock)
class DeleteTests(CmdTestsBase):
@mock.patch('jenkins_jobs.cli.entry.Builder.delete_job')
@mock.patch('jenkins_jobs.cli.subcommand.delete.Builder.delete_job')
def test_delete_single_job(self, delete_job_mock):
"""
Test handling the deletion of a single Jenkins job.
@ -36,7 +36,7 @@ class DeleteTests(CmdTestsBase):
args = ['--conf', self.default_config_file, 'delete', 'test_job']
self.execute_jenkins_jobs_with_args(args)
@mock.patch('jenkins_jobs.cli.entry.Builder.delete_job')
@mock.patch('jenkins_jobs.cli.subcommand.delete.Builder.delete_job')
def test_delete_multiple_jobs(self, delete_job_mock):
"""
Test handling the deletion of multiple Jenkins jobs.

View File

@ -130,7 +130,7 @@ class TestTests(CmdTestsBase):
e = self.assertRaises(UnicodeError, jenkins_jobs.execute)
self.assertIn("'ascii' codec can't encode character", str(e))
@mock.patch('jenkins_jobs.cli.entry.YamlParser.generateXML')
@mock.patch('jenkins_jobs.cli.subcommand.test.YamlParser.generateXML')
@mock.patch('jenkins_jobs.parser.ModuleRegistry')
def test_plugins_info_stub_option(self, registry_mock, generateXML_mock):
"""
@ -154,7 +154,7 @@ class TestTests(CmdTestsBase):
registry_mock.assert_called_with(mock.ANY,
plugins_info_list)
@mock.patch('jenkins_jobs.cli.entry.YamlParser.generateXML')
@mock.patch('jenkins_jobs.cli.subcommand.test.YamlParser.generateXML')
@mock.patch('jenkins_jobs.parser.ModuleRegistry')
def test_bogus_plugins_info_stub_option(self, registry_mock,
generateXML_mock):

View File

@ -122,8 +122,8 @@ class UpdateTests(CmdTestsBase):
path = os.path.join(self.fixtures_path, 'cmd-002.yaml')
args = ['--conf', self.default_config_file, 'update', path]
with mock.patch(
'jenkins_jobs.cli.entry.Builder.update_job') as update_mock:
import_path = 'jenkins_jobs.cli.subcommand.update.Builder.update_job'
with mock.patch(import_path) as update_mock:
update_mock.return_value = ([], 0)
self.execute_jenkins_jobs_with_args(args)
# unless the timeout is set, should only call with 3 arguments
@ -143,8 +143,8 @@ class UpdateTests(CmdTestsBase):
'non-default-timeout.ini')
args = ['--conf', config_file, 'update', path]
with mock.patch(
'jenkins_jobs.cli.entry.Builder.update_job') as update_mock:
import_path = 'jenkins_jobs.cli.subcommand.update.Builder.update_job'
with mock.patch(import_path) as update_mock:
update_mock.return_value = ([], 0)
self.execute_jenkins_jobs_with_args(args)
# when timeout is set, the fourth argument to the Jenkins api init