Hello, Jenkins geeks.
Once upon a time, I wrote a Jenkins Scripted Pipeline with parallel downstream jobs run. As a part of that, I need to get some data from the downstream jobs. In my case, I need to get built docker image tag.
And here is the story how to do that. I have a bunch of the services, which has its own pipelines, and I’d like to run builds in parallel. At the same time, each build generate, for example, docker image with unique tag. Let’s assume, that I want to get created tag back to the main job for reuse or generate aggregated report. How I can achieve that?
Actually, it is easy!
Main pipeline code block:
...
def parallelSteps = [
'Building service1' : {
build(
job: 'service1job',
parameters: [
string(name: 'myParam', value: 'myValue'),
],
propagate: false,
wait: true
)
},
'Building service2' : {
build(
job: 'service2job',
parameters: [
string(name: 'myOtherParam', value: 'myValue'),
],
propagate: false,
wait: true
)
},
...
]
...
Parameter wait
is mandatory!
Lets also set to not stop execution of the parallel steps on any failed parallel build:
parallelSteps.put('failFast', false)
And, finally, run our builds in parallel:
def parallelResults = parallel(parallelSteps)
Once all your jobs are done, results will be available in parallelResults
variable.
Now you can iterate over the completed builds and access build results for each build.
But, wait, what about sharing parameters from downstream builds to the upstream?
Technically, it’s the same as if you run the build without parallelism:
def myBuild = build(
job: 'service1job',
parameters: [
string(name: 'myParam', value: 'myValue'),
],
propagate: false,
wait: true
)
myBuild
is an instance of RunWrapper
, so you can access many parameters.
Same here. parallelResults
contains a Map<String, RunWrapper>
In order to get some string parameters from downstream build, you just need to use myBuild.getBuildVariables()
. That will be a Map<String, String>
.
This method returns only variables explicitly set via env.MY_PARAMETER_TO_RETURN='myValueToShare'
. In this case, getBuildVariables()
will contain MY_PARAMETER_TO_RETURN
with defined value. No other Environment variables returned!
But, lets get back to our docker tag…
So, each my pipeline builds the image and expose docker tag as env.APP_IMAGE_TAG=myimagetag
. Now I can easily collect tags from all parallel builds:
def myBuildTags = parallelResults.collectEntries { buildName, buildResult ->
return [name: buildName, imageTag: buildResult.getBuildVariables().get('APP_IMAGE_TAG')]
}
Voila!
Bonus
You can also group parallel exectution results by status:
def parallelResultsGrouped = parallelResults.groupBy { it.getValue().getResult() }
The parallelResultsGrouped
will contain something like this:
[
SUCCESS: [
'Building service1': <RunWrapper Object>,
'Building service2': <RunWrapper Object>,
'Building service3': <RunWrapper Object>,
]
]
If one of the downstream job fail, the map can look like this:
[
SUCCESS: [
'Building service1': <RunWrapper Object>,
'Building service2': <RunWrapper Object>,
],
FAILURE: [
'Building service3': <RunWrapper Object>,
]
]
Now you can access only successful builds or failed builds. Remember about other statuses, like ABORTED
and UNSTABLE
.
Happy Scripting!