Interpolate params inside of params.

This patch enables parameters to be expanded inside of other parameters. For
instance:

- job-template:
    name: '{value-stream}_{pipeline_type}_foo'
    builders:
        - shell: 'echo "I am foo job!"'
    publishers:
        - trigger-parameterized-builds:
            - project: '{downstream}'
              current-parameters: False
              condition: ALWAYS
              git-revision: True

- job-template:
    name: '{value-stream}_{pipeline_type}_bar'
    builders:
        - shell: 'echo "I am bar job!"'
    publishers:
        - trigger-parameterized-builds:
            - project: '{downstream}'
              current-parameters: False
              condition: ALWAYS
              git-revision: True

- job-template:
    name: '{value-stream}_{pipeline_type}_baz'
    builders:
        - shell: 'echo "I am baz job!"'
    publishers:
        - trigger-parameterized-builds:
            - project: '{downstream}'
              current-parameters: False
              condition: ALWAYS
              git-revision: True

- job-group:
    name: 'pipeline2'
    pipeline_type: 'p2'
    jobs:
        - '{value-stream}_{pipeline_type}_foo':
            downstream: '{value-stream}_{pipeline_type}_bar'
        - '{value-stream}_{pipeline_type}_bar':
            downstream: '{value-stream}_{pipeline_type}_baz'
        - '{value-stream}_{pipeline_type}_baz':

- job-group:
    name: 'pipeline1'
    pipeline_type: 'p1'
    jobs:
        - '{value-stream}_{pipeline_type}_baz':
            downstream: '{value-stream}_{pipeline_type}_bar'
        - '{value-stream}_{pipeline_type}_bar':
            downstream: '{value-stream}_{pipeline_type}_foo'
        - '{value-stream}_{pipeline_type}_foo':

- project:
    name: derp
    jobs:
        - 'pipeline1':
            value-stream:
                - experimental
                - staging
        - 'pipeline2':
            value-stream:
                - experimental
                - staging
                - production

Prior to this commit, the setup above was not possible because the `downstream`
defined as parameters to be passed to job templates in the job groups would not
have the `value-stream` and `pipeline-type` parameters interpolated. The
trigger-parameterized-jobs `project` value for each of the 15 jobs created with
this setup would all look something like `{value-stream}_{pipeline_type}_foo`
which does not actually point to any particular job.

The only way I was previously able to specify a downstream job with parameters
was to put the `{value-stream}_{pipeline_type}_foo` string directly in the
job-template which meant that job templates were statically bound to each
other--I had no reasonable way to re-order or easily select subsets of the jobs
at the job-group or project level as can be seen here in the difference between
`pipeline1` and `pipeline2`.

Change-Id: I26dfb112cd4842c40c3d561f8de89f7654a07b8f
This commit is contained in:
Wayne 2014-12-18 20:33:23 -08:00
parent 3ae187b1a7
commit 0a2e4ed961
6 changed files with 228 additions and 4 deletions

View File

@ -261,6 +261,14 @@ For example:
.. literalinclude:: /../../tests/yamlparser/fixtures/yaml_anchor.yaml
JJB also supports interpolation of parameters within parameters. This allows a
little more flexibility when ordering template jobs as components in different
projects and job groups.
For example:
.. literalinclude:: /../../tests/yamlparser/fixtures/second_order_parameter_interpolation002.yaml
Custom Yaml Tags
----------------

View File

@ -209,12 +209,21 @@ class YamlParser(object):
return job
return self.applyDefaults(job)
def applyDefaults(self, data):
def applyDefaults(self, data, override_dict=None):
if override_dict is None:
override_dict = {}
whichdefaults = data.get('defaults', 'global')
defaults = self.data.get('defaults', {}).get(whichdefaults, {})
defaults = copy.deepcopy(self.data.get('defaults',
{}).get(whichdefaults, {}))
if defaults == {} and whichdefaults != 'global':
raise JenkinsJobsException("Unknown defaults set: '{0}'"
.format(whichdefaults))
for key in override_dict.keys():
if key in defaults.keys():
defaults[key] = override_dict[key]
newdata = {}
newdata.update(defaults)
newdata.update(data)
@ -325,8 +334,11 @@ class YamlParser(object):
def expandYamlForTemplateJob(self, project, template, jobs_filter=None):
dimensions = []
# reject keys that are not useful during yaml expansion
for k in ['jobs']:
project.pop(k)
for (k, v) in project.items():
if type(v) == list and k not in ['jobs']:
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
@ -335,7 +347,7 @@ class YamlParser(object):
checksums = set([])
for values in itertools.product(*dimensions):
params = copy.deepcopy(project)
params = self.applyDefaults(params)
params = self.applyDefaults(params, template)
expanded_values = {}
for (k, v) in values:
@ -347,6 +359,7 @@ class YamlParser(object):
expanded_values[k] = v
params.update(expanded_values)
params = deep_format(params, params)
expanded = deep_format(template, params)
# Keep track of the resulting expansions to avoid

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<displayName>experimental_foo</displayName>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<builders/>
<publishers/>
<buildWrappers/>
</project>

View File

@ -0,0 +1,13 @@
- job-template:
name: '{value-stream}_{pipeline-type}'
display-name: '{value-stream}_{pipeline-type}'
- project:
name: derp
jobs:
- '{value-stream}_{pipeline-type}':
pipeline-type: 'foo'
- defaults:
name: 'global'
value-stream: 'experimental'

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<displayName>experimental p2 bar</displayName>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<builders/>
<publishers>
<hudson.plugins.parameterizedtrigger.BuildTrigger>
<configs>
<hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
<configs>
<hudson.plugins.git.GitRevisionBuildParameters>
<combineQueuedCommits>false</combineQueuedCommits>
</hudson.plugins.git.GitRevisionBuildParameters>
</configs>
<projects/>
<condition>ALWAYS</condition>
<triggerWithNoParameters>false</triggerWithNoParameters>
</hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
</configs>
</hudson.plugins.parameterizedtrigger.BuildTrigger>
</publishers>
<buildWrappers/>
</project>
<BLANKLINE>
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<displayName>experimental p2 foo</displayName>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<builders/>
<publishers>
<hudson.plugins.parameterizedtrigger.BuildTrigger>
<configs>
<hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
<configs>
<hudson.plugins.git.GitRevisionBuildParameters>
<combineQueuedCommits>false</combineQueuedCommits>
</hudson.plugins.git.GitRevisionBuildParameters>
</configs>
<projects>experimental_p2_bar</projects>
<condition>ALWAYS</condition>
<triggerWithNoParameters>false</triggerWithNoParameters>
</hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
</configs>
</hudson.plugins.parameterizedtrigger.BuildTrigger>
</publishers>
<buildWrappers/>
</project>
<BLANKLINE>
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<displayName>production p1 bar</displayName>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<builders/>
<publishers>
<hudson.plugins.parameterizedtrigger.BuildTrigger>
<configs>
<hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
<configs>
<hudson.plugins.git.GitRevisionBuildParameters>
<combineQueuedCommits>false</combineQueuedCommits>
</hudson.plugins.git.GitRevisionBuildParameters>
</configs>
<projects>production_p1_foo</projects>
<condition>ALWAYS</condition>
<triggerWithNoParameters>false</triggerWithNoParameters>
</hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
</configs>
</hudson.plugins.parameterizedtrigger.BuildTrigger>
</publishers>
<buildWrappers/>
</project>
<BLANKLINE>
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<displayName>production p1 foo</displayName>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<builders/>
<publishers>
<hudson.plugins.parameterizedtrigger.BuildTrigger>
<configs>
<hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
<configs>
<hudson.plugins.git.GitRevisionBuildParameters>
<combineQueuedCommits>false</combineQueuedCommits>
</hudson.plugins.git.GitRevisionBuildParameters>
</configs>
<projects/>
<condition>ALWAYS</condition>
<triggerWithNoParameters>false</triggerWithNoParameters>
</hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
</configs>
</hudson.plugins.parameterizedtrigger.BuildTrigger>
</publishers>
<buildWrappers/>
</project>

View File

@ -0,0 +1,47 @@
- job-template:
name: '{value-stream}_{project-id}_foo'
display-name: '{value-stream} {project-id} foo'
publishers:
- trigger-parameterized-builds:
- project: '{downstream}'
current-parameters: False
condition: ALWAYS
git-revision: True
- job-template:
name: '{value-stream}_{project-id}_bar'
display-name: '{value-stream} {project-id} bar'
publishers:
- trigger-parameterized-builds:
- project: '{downstream}'
current-parameters: False
condition: ALWAYS
git-revision: True
- job-group:
name: 'pipeline2'
project-id: 'p2'
jobs:
- '{value-stream}_{project-id}_foo':
downstream: '{value-stream}_{project-id}_bar'
- '{value-stream}_{project-id}_bar':
- job-group:
name: 'pipeline1'
project-id: 'p1'
jobs:
- '{value-stream}_{project-id}_bar':
downstream: '{value-stream}_{project-id}_foo'
- '{value-stream}_{project-id}_foo':
- project:
name: derp
jobs:
- 'pipeline1':
value-stream: 'production'
- 'pipeline2':
value-stream: 'experimental'
- defaults:
name: 'global'
downstream: ''