2
votes

I'm currently using the Build Flow plugin, which seems to have been abandoned in favor of Pipelines in Jenkins 2.0.

Running into some problems re-building our existing jobs using the new pipelines.

Currently, I have code similar to this:

ignore(FAILURE) {
  join = parallel([
    job1: {build('job1')},
    job2: {build('job2')},
    job3: {build('job3')}
  ])
}
results = [join.job1.result.toString(), join.job2.result.toString(), join.job2.result.toString()]

if(join.job1.result.toString() == 'SUCCESS') {
  buildList << join.job1.lastBuild.getDisplayName()
}

The goal here is to run multiple existing jobs in parallel, and then access information about the builds that completed. This has been working without issue in the Build Flow plugin.

I have been unable find a way to access this data using the new Pipelines.

echo 'Checking streams for latest builds'
join = [:] 

join['Job1'] = { build job: 'Job1', parameters: [[$class: 'StringParameterValue', name: 'TimeWindow', value: '1200']], propagate: false} 
join['Job2'] = { build job: 'Job2', parameters: [[$class: 'StringParameterValue', name: 'TimeWindow', value: '1200']], propagate: false} 
join['Job3'] = { build job: 'Job3', parameters: [[$class: 'StringParameterValue', name: 'TimeWindow', value: '1200']], propagate: false}

parallel join

A dump of join['Job1'] doesn't give access to an AbstractBuild or similar, the way the Build Flow plugin does. Instead, it shows:

<org.jenkinsci.plugins.workflow.cps.CpsClosure2@2eac6ed9
def=com.cloudbees.groovy.cps.impl.CpsClosureDef@59647704
delegate=WorkflowScript@3aa1807f 
owner=WorkflowScript@3aa1807f
thisObject=WorkflowScript@3aa1807f 
resolveStrategy=0 
directive=0
parameterTypes=null 
maximumNumberOfParameters=0 
bcw=null>

Using the new Pipelines, is there a way to access data like job1.result, job1.lastBuild, job1.lastBuild.getDisplayName()?

3

3 Answers

3
votes

You can access that data by using the Jenkins API after the parallel step:

Jenkins.instance.getItemByFullName('Job1').lastBuild
4
votes

a little late but you can also define the runWrapper object returned by build command in your closure and place it in a map defined outside of the parallel command.

Here's an example. Note: I am using propagate: false so that exceptions (JUnit test failures, etc) are not thrown. You would have to decide how you want to handle exceptions, try/catch/finally, etc.

Example Pipeline Job to execute (needs to be parameterized with a string param commandStr):

env.PASSED_CMD="${params.commandStr}"
stage('command-exec') {
    node {
        sh "${commandStr}"
    }
}

Executing job (config):

buildRuns = [:]
buildResults = [:]
def buildClosure(String jobKey, String paramAValue) {
    return {
        def runWrapper = build(
            job: 'command-test-job',
            propagate: false,
            parameters: [[$class: 'StringParameterValue', name: 'commandStr', value: paramAValue]]
        )
        buildResults."$jobKey" = runWrapper
    }
}
buildRuns."job1" = buildClosure("job1", "echo 'HI' && exit 0")
buildRuns."job2" = buildClosure("job2", "echo 'HO' && exit 0")
parallel buildRuns
for(k in buildRuns.keySet()) {
    def runResult = buildResults."$k"
    echo "$k -> ${runResult.result}"
    echo "$k -> ${runResult.buildVariables.PASSED_CMD}"
}

The build log shows:

[Pipeline] parallel
[Pipeline] [job1] { (Branch: job1)
[Pipeline] [job2] { (Branch: job2)
[Pipeline] [job1] build (Building command-test-job)
[job1] Scheduling project: command-test-job
[Pipeline] [job2] build (Building command-test-job)
[job2] Scheduling project: command-test-job
[job1] Starting building: command-test-job #7
[job2] Starting building: command-test-job #8
[Pipeline] [job2] }
[Pipeline] [job1] }
[Pipeline] // parallel
[Pipeline] echo
job1 -> SUCCESS
[Pipeline] echo
job1 -> echo 'HI' && exit 0
[Pipeline] echo
job2 -> SUCCESS
[Pipeline] echo
job2 -> echo 'HO' && exit 0
[Pipeline] End of Pipeline
Finished: SUCCESS
4
votes

This is very similar to Steve-B's Answer, but you don't actually need to define the runwrapper explicitly or place it in the additional map before hand.

tl;dr You can just store the parallel build to a hashMap and access that map by directly looping over it's keySet instead


Take this answer with a grain of salt, I am using an older version of pipeline (Jenkins 2.7.2 and Pipeline 2.2).

You can store the parallel build results to a hashMap and loop over the map's keySet to get some information about the build.

def create_build_job(job_name, pool_label="master", propagate=false) {
  build job: job_name, parameters: [[$class: 'LabelParameterValue', name: "node_label", label: "${pool_label}"]], propagate: propagate, wait:true
}

def buildmap = [:]
def build_results

stage 'Perform Build'
    //test1 is set to fail, test2 is set to succeed
    buildmap['test1'] = {create_build_job('test1', "your_node_label")}
    buildmap['test2'] = {create_build_job('test2', "your_node_label")}

    build_results = parallel buildmap

    for(k in build_results.keySet()){
      println build_results["${k}"].getProperties()
    }

For this pipeline I'm just dumping all of the properties of the RunWrapper stored in item in the map, however you can access each property directly, so if you want the result of the build you can just do:

build_results["${k}"].result

The console output produced by this pipeline (with any potentially identifying information redacted is:

Started by user <user>
[Pipeline] stage (Perform Build)
Entering stage Perform Build
Proceeding
[Pipeline] parallel
[Pipeline] [test1] { (Branch: test1)
[Pipeline] [test2] { (Branch: test2)
[Pipeline] [test1] build (Building test1)
[test1] Scheduling project: test1
[test1] Starting building: test1 #11
[Pipeline] [test2] build (Building test2)
[test2] Scheduling project: test2
[test2] Starting building: test2 #11
[Pipeline] }
[Pipeline] }
[Pipeline] // parallel
[Pipeline] echo
{rawBuild=test1 #11, class=class org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper, absoluteUrl=<jenkins_url>/job/test1/11/, buildVariables={}, previousBuild=org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper@1480013a, id=11, nextBuild=null, changeSets=[], result=FAILURE, description=null, startTimeInMillis=1509667550519, timeInMillis=1509667550510, duration=956, number=11, displayName=#11}
[Pipeline] echo
{rawBuild=test2 #11, class=class org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper, absoluteUrl=<jenkins_url>/job/test2/11/, buildVariables={}, previousBuild=org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper@2d9c7128, id=11, nextBuild=null, changeSets=[], result=SUCCESS, description=null, startTimeInMillis=1509667550546, timeInMillis=1509667550539, duration=992, number=11, displayName=#11}
[Pipeline] End of Pipeline
Finished: SUCCESS