From 4dd267ee0db9a69b6b06d081464de4d72dfa6fe4 Mon Sep 17 00:00:00 2001 From: David Caro Date: Wed, 7 Aug 2013 18:55:13 +0200 Subject: [PATCH] Added globbed parameters to the job specification Now you can specify a globbed parameter when updating or deleting a job, it will parse the yaml files and select those jobs that match to be updated/deleted For example: jenkins-jobs --config ~/jenkins.ini update myjobs/ \*only_those\* Will only update the jobs that have 'only_those' on their name. For the delete subcommand the option '-p' lets you specify the path where to load the job list from, so you can use globs for the jobs in that list (it does not allow you to use globs with non-managed jobs) Change-Id: I5bb1074845fb143c7c3120c138a6b138d3548305 Signed-off-by: David Caro --- jenkins_jobs/builder.py | 71 ++++++++++++++++++++++++++++------------- jenkins_jobs/cmd.py | 6 ++-- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/jenkins_jobs/builder.py b/jenkins_jobs/builder.py index d3dea174a..b7de312a8 100644 --- a/jenkins_jobs/builder.py +++ b/jenkins_jobs/builder.py @@ -27,6 +27,7 @@ import pkg_resources import logging import copy import itertools +import fnmatch from jenkins_jobs.errors import JenkinsJobsException logger = logging.getLogger(__name__) @@ -61,6 +62,19 @@ def deep_format(obj, paramdict): return ret +def matches(what, where): + """ + Checks if the given string matches against the given list of glob patterns + + :arg str what: String that we want to test if matches + :arg list where: list of glob patters to match + """ + for pattern in where: + if re.match(fnmatch.translate(pattern), what): + return True + return False + + class YamlParser(object): def __init__(self, config=None): self.registry = ModuleRegistry(config) @@ -120,7 +134,8 @@ class YamlParser(object): changed = True for job in self.data.get('job', {}).values(): - if jobs_filter and job['name'] not in jobs_filter: + if jobs_filter and not matches(job['name'], jobs_filter): + logger.debug("Ignoring job {}".format(job['name'])) continue logger.debug("XMLifying job '{0}'".format(job['name'])) job = self.applyDefaults(job) @@ -205,7 +220,7 @@ class YamlParser(object): # We also want to skip XML generation whenever the user did # not ask for that job. job_name = expanded.get('name') - if jobs_filter and job_name not in jobs_filter: + if jobs_filter and not matches(job_name, jobs_filter): continue logger.debug("Generating XML for template job {0}" @@ -432,10 +447,17 @@ class Builder(object): self.global_config = config self.ignore_cache = ignore_cache - def delete_job(self, name): - self.jenkins.delete_job(name) - if(self.cache.is_cached(name)): - self.cache.set(name, '') + def load_files(self, fn): + if os.path.isdir(fn): + files_to_process = [os.path.join(fn, f) + for f in os.listdir(fn) + if (f.endswith('.yml') or f.endswith('.yaml'))] + else: + files_to_process = [fn] + self.parser = YamlParser(self.global_config) + for in_file in files_to_process: + logger.debug("Parsing YAML file {0}".format(in_file)) + self.parser.parse(in_file) def delete_old_managed(self, keep): jobs = self.jenkins.get_jobs() @@ -446,30 +468,33 @@ class Builder(object): .format(job['name'])) self.delete_job(job['name']) + def delete_job(self, glob_name, fn=None): + if fn: + self.load_files(fn) + self.parser.generateXML(glob_name) + jobs = [j.name + for j in self.parser.jobs + if matches(j.name, [glob_name])] + else: + jobs = [glob_name] + for job in jobs: + self.jenkins.delete_job(job) + if(self.cache.is_cached(job)): + self.cache.set(job, '') + def delete_all_jobs(self): jobs = self.jenkins.get_jobs() for job in jobs: self.delete_job(job['name']) def update_job(self, fn, names=None, output_dir=None): - if os.path.isdir(fn): - files_to_process = [os.path.join(fn, f) - for f in os.listdir(fn) - if (f.endswith('.yml') or f.endswith('.yaml'))] - else: - files_to_process = [fn] - parser = YamlParser(self.global_config) - for in_file in files_to_process: - logger.debug("Parsing YAML file {0}".format(in_file)) - parser.parse(in_file) - if names: - logger.debug("Will filter out jobs not in %s" % names) - parser.generateXML(names) + self.load_files(fn) + self.parser.generateXML(names) - parser.jobs.sort(lambda a, b: cmp(a.name, b.name)) + self.parser.jobs.sort(lambda a, b: cmp(a.name, b.name)) - for job in parser.jobs: - if names and job.name not in names: + for job in self.parser.jobs: + if names and not matches(job.name, names): continue if output_dir: if names: @@ -492,4 +517,4 @@ class Builder(object): self.cache.set(job.name, md5) else: logger.debug("'{0}' has not changed".format(job.name)) - return parser.jobs + return self.parser.jobs diff --git a/jenkins_jobs/cmd.py b/jenkins_jobs/cmd.py index 03780b560..546da86fc 100755 --- a/jenkins_jobs/cmd.py +++ b/jenkins_jobs/cmd.py @@ -45,6 +45,8 @@ def main(): parser_test.add_argument('name', help='name(s) of job(s)', nargs='*') parser_delete = subparser.add_parser('delete') parser_delete.add_argument('name', help='name of job', nargs='+') + parser_delete.add_argument('-p', '--path', default=None, + help='Path to YAML file or directory') subparser.add_parser('delete-all', help='Delete *ALL* jobs from Jenkins server, ' 'including those not managed by Jenkins Job ' @@ -99,8 +101,8 @@ def main(): if options.command == 'delete': for job in options.name: - logger.info("Deleting job {0}".format(job)) - builder.delete_job(job) + logger.info("Deleting jobs in [{0}]".format(job)) + builder.delete_job(job, options.path) elif options.command == 'delete-all': confirm('Sure you want to delete *ALL* jobs from Jenkins server?\n' '(including those not managed by Jenkins Job Builder)')