Add support for view-templates

Allow views to also be configured via templates similar to
job-templates.

This adds a new project key called "views" and parser type called
"view-template" allowing the user to add custom views.

Example:

- view-template:
    name: '{name}-template-{seq}'
    description: 'testing view templates feature'
    view-type: list
    regex: 'test-view-.*'

- project:
    name: 'test-view'
    views:
        - '{name}-template-{seq}'
    seq:
        - a
        - b
        - c

Change-Id: I6eb225f24bc3c7d790c7dcab6391735c579ac71a
Signed-off-by: Thanh Ha <thanh.ha@linuxfoundation.org>
This commit is contained in:
Thanh Ha 2016-11-09 12:03:01 -05:00 committed by Sorin Sbarnea
parent b72235c462
commit 418e7c628d
2 changed files with 171 additions and 0 deletions

View File

@ -201,6 +201,33 @@ create a view, you must define a view in a YAML file and have a variable called
Views are processed differently than Jobs and therefore will not work within a `Project`_ or a `Job Template`_.
.. _view-template:
View Template
^^^^^^^^^^^^^
Allow views to also be configured via templates similar to job-templates. This
is useful when you have multiple views defined that have similar configuration
except for a few variables. View Templates can be passed variables to fill in
sections automatically via a project configuration using the new 'views' key.
Minimal Example::
- view-template:
name: '{name}-template-{seq}'
description: 'testing view templates feature'
view-type: list
regex: 'test-view-.*'
- project:
name: 'test-view'
views:
- '{name}-template-{seq}'
seq:
- a
- b
- c
.. _macro:
Macro

View File

@ -316,6 +316,69 @@ class YamlParser(object):
raise JenkinsJobsException("Failed to find suitable "
"template named '{0}'"
.format(jobname))
for viewspec in project.get('views', []):
if isinstance(viewspec, dict):
# Singleton dict containing dict of view-specific params
viewname, viewparams = next(iter(viewspec.items()))
if not isinstance(viewparams, dict):
viewparams = {}
else:
viewname = viewspec
viewparams = {}
view = self._getView(viewname)
if view:
# Just naming an existing defined view
if viewname in seen:
self._handle_dups("Duplicate view '{0}' specified "
"for project '{1}'"
.format(viewname, project['name']))
seen.add(viewname)
continue
# see if it's a view group
group = self._getViewGroup(viewname)
if group:
for group_viewspec in group['views']:
if isinstance(group_viewspec, dict):
group_viewname, group_viewparams = \
next(iter(group_viewspec.items()))
if not isinstance(group_viewparams, dict):
group_viewparams = {}
else:
group_viewname = group_viewspec
group_viewparams = {}
view = self._getView(group_viewname)
if view:
if group_viewname in seen:
self._handle_dups(
"Duplicate view '{0}' specified for "
"project '{1}'".format(group_viewname,
project['name']))
seen.add(group_viewname)
continue
template = self._getViewTemplate(group_viewname)
# Allow a group to override parameters set by a project
d = type(project)(project)
d.update(viewparams)
d.update(group)
d.update(group_viewparams)
# Except name, since the group's name is not useful
d['name'] = project['name']
if template:
self._expandYamlForTemplateView(
d, template, jobs_glob)
continue
# see if it's a template
template = self._getViewTemplate(viewname)
if template:
d = type(project)(project)
d.update(viewparams)
self._expandYamlForTemplateView(d, template, jobs_glob)
else:
raise JenkinsJobsException("Failed to find suitable "
"template named '{0}'"
.format(viewname))
# check for duplicate generated jobs
seen = set()
# walk the list in reverse so that last definition wins
@ -325,6 +388,17 @@ class YamlParser(object):
"specified".format(job['name']))
self.jobs.remove(job)
seen.add(job['name'])
# check for duplicate generated views
seen_views = set()
# walk the list in reverse so that last definition wins
for view in self.views[::-1]:
if view['name'] in seen_views:
self._handle_dups("Duplicate definitions for view '{0}' "
"specified".format(view['name']))
self.views.remove(view)
seen_views.add(view['name'])
return self.jobs, self.views
def _expandYamlForTemplateJob(self, project, template, jobs_glob=None):
@ -411,3 +485,73 @@ class YamlParser(object):
# The \n\n is not hard coded, because they get stripped if the
# project does not otherwise have a description.
return "\n\n" + MAGIC_MANAGE_STRING
# Views related
def _getView(self, name):
view = self.data.get('view', {}).get(name, None)
if not view:
return view
return self._applyDefaults(view)
def _getViewGroup(self, name):
return self.data.get('view-group', {}).get(name, None)
def _getViewTemplate(self, name):
print(name)
view = self.data.get('view-template', {}).get(name, None)
if not view:
return view
return self._applyDefaults(view)
def _expandYamlForTemplateView(self, project, template, views_glob=None):
dimensions = []
template_name = template['name']
# reject keys that are not useful during yaml expansion
for k in ['views']:
project.pop(k)
excludes = project.pop('exclude', [])
for (k, v) in project.items():
tmpk = '{{{0}}}'.format(k)
if tmpk not in template_name:
continue
if type(v) == list:
dimensions.append(zip([k] * len(v), v))
# XXX somewhat hackish to ensure we actually have a single
# pass through the loop
if len(dimensions) == 0:
dimensions = [(("", ""),)]
for values in itertools.product(*dimensions):
params = copy.deepcopy(project)
params = self._applyDefaults(params, template)
expanded_values = {}
for (k, v) in values:
if isinstance(v, dict):
inner_key = next(iter(v))
expanded_values[k] = inner_key
expanded_values.update(v[inner_key])
else:
expanded_values[k] = v
params.update(expanded_values)
params = deep_format(params, params)
if combination_matches(params, excludes):
logger.debug('Excluding combination %s', str(params))
continue
for key in template.keys():
if key not in params:
params[key] = template[key]
params['template-name'] = template_name
expanded = deep_format(
template, params,
self.jjb_config.yamlparser['allow_empty_variables'])
view_name = expanded.get('name')
if views_glob and not matches(view_name, views_glob):
continue
self._formatDescription(expanded)
self.views.append(expanded)