Running same Jenkins job on multiple agents in parallel in declarative pipeline - jenkins

here's what I have:
#!/usr/bin/env groovy
pipeline {
agent none
stages {
stage('Checkout SCM') {
agent { label 'win' && 'apple' && 'rhel' }
steps {
echo "Cloning Repository"
checkout([$class: 'GitSCM',
branches: [[name: "*/develop"]],
doGenerateSubmoduleConfigurations: false,
extensions: [[$class: 'WipeWorkspace']],
submoduleCfg: [],
userRemoteConfigs: [[credentialsId: 'UNAME', url: 'URL']],
browser: [$class: 'BitbucketWeb', repoUrl: 'URL'],
])}}
stage('Building Win64, Linux764, MacOS') {
agent { label 'win&&rhel&&apple' }
steps {
script {
echo '************************'
echo '*****BUILDING JOBS******'
echo '************************'
sh 'python build.py'
sh 'cd ion-js && npm run prepublishOnly'
}}}
}
}
However I get the There are no nodes with the label ‘win && rhel && apple’ error. Does anyone happen to know how to run a declarative jenkins pipeline where one of the stages is ran on multiple agent labels in parallel?
I want to checkout the same git repo to 3 different nodes at the same time. I've tried agent { label 'win' && 'apple' && 'rhel' } and agent { label 'win&&apple&&rhel' } but it just says it can't find that label.
Here they say you can use || and using && should work but I'm not sure what I'm missing. I could write 3 different checkout stages but I figured there was a better way

To add more to answer from Micah,
Instead of repeating stage with different labels, you can define a function which can create a stage for you and will be executing the generate stage on different agent nodes of your choice.
def agents = ['win64', 'linux64', 'macos']
def generateStage(nodeLabel) {
return {
stage("Runs on ${nodeLabel}") {
node(nodeLabel) {
script {
echo "Running on ${nodeLabel}"
echo '************************'
echo '*****BUILDING JOBS******'
echo '************************'
sh 'python build.py'
sh 'cd ion-js && npm run prepublishOnly'
}
}
}
}
}
def parallelStagesMap = agents.collectEntries {
["${it}" : generateStage(it)]
}
pipeline {
agent none
stages {
stage('non-parallel stage') {
steps {
echo 'This stage will be executed first.'
}
}
stage('parallel stage') {
steps {
script {
parallel parallelStagesMap
}
}
}
}
}
Since we have parallel keyword while calling parallelStageMap, the same stage will be executed in parallel on the agent nodes.
ProTip: You can define more steps inside the function which are common to be executed on all agents.
If you wanted to defined label and stage name, you can added another argument named stagename and can parse into the function generateStage.

I have tried the same things with no success. The only solution I'm aware of is to have a parallel block and define the stage multiple times, for each agent/node/label.
stage('Building Win64, Linux764, MacOS') {
parallel {
stage('Win64') {
agent {
label 'win-10-x64'
}
steps {
...
}
}
stage('Linux64') {
agent {
label 'linux-x64'
}
steps {
...
}
}
stage('MacOS') {
agent {
label 'macos'
}
steps {
...
}
}
}
}

Related

Run multiple Jobs in parallel via Jenkins Declarative pipeline syntax

I want to execute multiple jobs from a single pipeline using declarative syntax in parallel. Can this be possible!! I know we can make a declarative parallel pipeline using "parallel" parameter.
pipeline {
agent any
parallel{
stages {
stage('Test1') {
steps {
sh 'pip install -r requirements.txt'
}
}
stage('Test2') {
steps {
echo 'Stage 2'
sh 'behave -f allure_behave.formatter:AllureFormatter -o allure-results features/scenarios/**/*.feature'
}
}
stage('Test3') {
steps {
script {
allure([
includeProperties: false,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: 'allure-results']]
])
}
}
}
}
}
}
Below image will show you the proper flow that I want. Any approach how to do it?
// Pipeline project: SO-69680107-1-parallel-downstream-jobs-matrix
pipeline {
agent any
stages {
stage('Clean Workspace') {
steps {
cleanWs()
}
}
stage('Job matrix') {
matrix {
axes {
axis {
name 'job'
values 'SO-69680107-2', 'SO-69680107-3', 'SO-69680107-k' // , ...
}
}
stages {
stage('Run job') {
steps {
build "$job"
copyFiles( "$WORKSPACE\\..\\$job", "$WORKSPACE")
}
} // stage 'Run job'
}
} // matrix
} // stage 'Job matrix'
stage('List upstream workspace') {
steps {
bat "#dir /b \"$WORKSPACE\""
}
}
} // stages
}
def copyFiles( downstreamWorkspace, upstreamWorkspace ) {
dir("$downstreamWorkspace") {
bat """
#set prompt=\$g\$s
#echo Begin: %time%
dir /b
xcopy /f *.* \"$upstreamWorkspace\\\"
#echo End: %time%
"""
}
}
Template for downstream projects SO-69680107-2, SO-69680107-3, SO-69680107-k:
// Pipeline project: SO-69680107-X
pipeline {
agent any
stages {
stage('Stage X') {
steps {
sh 'set +x; echo "Step X" | tee SO-69680107-X.log; date; sleep 3; date'
}
}
}
}

Multiple parallel stages, jenkins checks out source on every stage

I'm not sure this is default behavior of Jenkins.
I have three parallel stages each assumes files produced in previous stage is present in the directory.
I run yarn install in stage 1 and yarn build in stage 2 and in stage 3 I have yarn package.
Each stage tries to checks out source and complains, for example, stage 2 complains that node_modules is not present, when I inspected I found after stage 1 workspace was reset.
Is there way to retain the directories/files in workspace after the stage is complete?
Any help is greatly appreciated.
EDIT 1
pipeline {
agent none
options {
skipDefaultCheckout true
}
environment {
BRANCH_NAME = 'mater'
}
stages {
stage ('Checkout source') {
agent any
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "${env.BRANCH_NAME}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
credentialsId: '5db9bedc-fa88-4f64-9e6a-3e9a9d5c999f',
url: 'git#gitserver.io:acme/acme-repo.git'
]]
])
}
}
stage ('Test') {
parallel {
stage ('[Test] Mac') {
agent {
node {
label 'iMac'
customWorkspace "/Users/acme/workspace/acme-repo/nightly/${env.BRANCH_NAME}"
}
}
steps {
sh 'npm install yarn -g'
sh 'yarn install --network-timeout 1000000'
sh 'yarn build'
sh 'yarn test -u'
}
}
stage ('[Test] Linux') {
agent {
node {
label 'master'
customWorkspace "/var/lib/jenkins/acme/acme-repo/nightly/${env.BRANCH_NAME}"
}
}
steps {
sh 'sudo npm install yarn -g'
sh 'yarn install --network-timeout 1000000'
sh 'yarn build'
sh 'yarn test -u'
}
}
stage ('[Test] Windows') {
agent {
node {
label 'win'
customWorkspace "E:\\jenkins_agent\\acme\\nightly\\${env.BRANCH_NAME}"
}
}
steps {
bat 'npm install yarn -g'
bat 'yarn install --network-timeout 1000000'
bat 'yarn build'
bat 'yarn test -u'
}
}
}
}
stage ('Package') {
parallel {
stage ('Mac') {
agent {
node {
label 'iMac'
customWorkspace "/Users/acme/workspace/acme-repo/nightly/${env.BRANCH_NAME}"
}
}
steps {
sh 'yarn package-mac'
}
}
stage ('Linux') {
agent {
node {
label 'master'
customWorkspace "/var/lib/jenkins/acme/acme-repo/nightly/${env.BRANCH_NAME}"
}
}
steps {
sh './node_modules/.bin/yarn package-linux'
}
}
stage ('Windows') {
agent {
node {
label 'win'
customWorkspace "E:\\jenkins_agent\\acme\\nightly\\${env.BRANCH_NAME}"
}
}
steps {
bat 'yarn package-win'
}
}
}
}
stage ('Publish') {
parallel {
stage ('[Publish] Mac') {
agent {
node {
label 'iMac'
customWorkspace "/Users/acme/workspace/acme-repo/nightly/${env.BRANCH_NAME}"
}
}
steps {
sh 'yarn publish-mac'
}
}
stage ('[Publish] Linux') {
agent {
node {
label 'master'
customWorkspace "/var/lib/jenkins/acme/acme-repo/nightly/${env.BRANCH_NAME}"
}
}
steps {
sh './node_modules/.bin/yarn publish-linux'
}
}
stage ('[Publish] Windows') {
agent {
node {
label 'win'
customWorkspace "E:\\jenkins_agent\\acme\\nightly\\${env.BRANCH_NAME}"
}
}
steps {
bat 'yarn publish-win'
}
}
}
}
stage ('Tag & Push') {
agent {
node {
label 'master'
customWorkspace "/var/lib/jenkins/acme/acme-repo/nightly/${env.BRANCH_NAME}"
}
}
steps {
sh "yarn version --patch"
sh 'git push git#gitserver.io:acme/acme-repo.git HEAD:$BRANCH_NAME'
}
}
}
}
Just to give more details - there are
checkout source
test (parallel)
package (parallel)
publish (parallel)
Each of these stages are defined for different environment. The problem is when running package stage, evidently, the workspace is cleaned up so there is nothing to run package
As it turns out, I need to use -
options {
// This is key setting that enables one checkout across different stages
skipDefaultCheckout true
}
to skip default checkout.

Can I specify node using Scripted Pipeline in Jenkins?

Ihave noticed that Jenkins pipeline file -- Jenkinsfile which have two syntax
Declarative
Scripted
I have made Declarative Script work to specify node to run my task. However I don't know how to modify my script to Scripted syntax.
My Declarative Script
pipeline {
agent none
stages {
stage('Build') {
agent { label 'my-label​' }
steps {
echo 'Building..'
sh '''
'''
}
}
stage('Test') {
agent { label 'my-label​' }
steps {
echo 'Testing..'
sh '''
'''
}
}
stage('Deploy') {
agent { label 'my-label​' }
steps {
echo 'Deploying....'
sh '''
'''
}
}
}
}
I have tried to use in this way:
node('my-label') {
stage 'SCM'
git xxxx
stage 'Build'
sh ''' '''
}
But it seems Jenkins cannot find my node to run.
How about this simple example?
stage("one") {
node("linux") {
echo "One"
}
}
stage("two") {
node("linux") {
echo "two"
}
}
stage("three") {
node("linux") {
echo "three"
}
}
Or the below answer, this way you are guaranteed to have the stages run on the same node if there are multiple nodes with the same label and run interrupted by another job.
The above example will release the node after every stage, the below example will hold the node for all three stages.
node("linux") {
stage("one") {
echo "One"
}
stage("two") {
echo "two"
}
stage("three") {
echo "three"
}
}

Declarative pipeline when condition in post

As far as declarative pipelines go in Jenkins, I'm having trouble with the when keyword.
I keep getting the error No such DSL method 'when' found among steps. I'm sort of new to Jenkins 2 declarative pipelines and don't think I am mixing up scripted pipelines with declarative ones.
The goal of this pipeline is to run mvn deploy after a successful Sonar run and send out mail notifications of a failure or success. I only want the artifacts to be deployed when on master or a release branch.
The part I'm having difficulties with is in the post section. The Notifications stage is working great. Note that I got this to work without the when clause, but really need it or an equivalent.
pipeline {
agent any
tools {
maven 'M3'
jdk 'JDK8'
}
stages {
stage('Notifications') {
steps {
sh 'mkdir tmpPom'
sh 'mv pom.xml tmpPom/pom.xml'
checkout([$class: 'GitSCM', branches: [[name: 'origin/master']], doGenerateSubmoduleConfigurations: false, submoduleCfg: [], userRemoteConfigs: [[url: 'https://repository.git']]])
sh 'mvn clean test'
sh 'rm pom.xml'
sh 'mv tmpPom/pom.xml ../pom.xml'
}
}
}
post {
success {
script {
currentBuild.result = 'SUCCESS'
}
when {
branch 'master|release/*'
}
steps {
sh 'mvn deploy'
}
sendNotification(recipients,
null,
'https://link.to.sonar',
currentBuild.result,
)
}
failure {
script {
currentBuild.result = 'FAILURE'
}
sendNotification(recipients,
null,
'https://link.to.sonar',
currentBuild.result
)
}
}
}
In the documentation of declarative pipelines, it's mentioned that you can't use when in the post block. when is allowed only inside a stage directive.
So what you can do is test the conditions using an if in a script:
post {
success {
script {
if (env.BRANCH_NAME == 'master')
currentBuild.result = 'SUCCESS'
}
}
// failure block
}
Using a GitHub Repository and the Pipeline plugin I have something along these lines:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh '''
make
'''
}
}
}
post {
always {
sh '''
make clean
'''
}
success {
script {
if (env.BRANCH_NAME == 'master') {
emailext (
to: 'engineers#green-planet.com',
subject: "${env.JOB_NAME} #${env.BUILD_NUMBER} master is fine",
body: "The master build is happy.\n\nConsole: ${env.BUILD_URL}.\n\n",
attachLog: true,
)
} else if (env.BRANCH_NAME.startsWith('PR')) {
// also send email to tell people their PR status
} else {
// this is some other branch
}
}
}
}
}
And that way, notifications can be sent based on the type of branch being built. See the pipeline model definition and also the global variable reference available on your server at http://your-jenkins-ip:8080/pipeline-syntax/globals#env for details.
Ran into the same issue with post. Worked around it by annotating the variable with #groovy.transform.Field. This was based on info I found in the Jenkins docs for defining global variables.
e.g.
#!groovy
pipeline {
agent none
stages {
stage("Validate") {
parallel {
stage("Ubuntu") {
agent {
label "TEST_MACHINE"
}
steps {{
sh "run tests command"
recordFailures('Ubuntu', 'test-results.xml')
junit 'test-results.xml'
}
}
}
}
}
post {
unsuccessful {
notify()
}
}
}
// Make testFailures global so it can be accessed from a 'post' step
#groovy.transform.Field
def testFailures = [:]
def recordFailures(key, resultsFile) {
def failures = ... parse test-results.xml script for failures ...
if (failures) {
testFailures[key] = failures
}
}
def notify() {
if (testFailures) {
... do something here ...
}
}

Jenkins declarative pipeline parallel steps executors

I am migrating a job from multijob to a Jenkins Declarative pipeline job. I am unable to run the parallel steps on multiple executors.
For example in the pipeline below, I see only one executor being used when I run the pipeline.
I was wondering why only a single executor is used. The idea is that each parallel step would be inoking a make target that would build a docker image.
pipeline {
agent none
stages {
stage('build libraries') {
agent { label 'master' }
steps {
parallel(
"nodejs_lib": {
dir(path: 'nodejs_lib') {
sh 'sleep 110'
}
},
"python_lib": {
dir(path: 'python_lib') {
sh 'sleep 100'
}
}
)
}
}
}
options {
ansiColor('gnome-terminal')
buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '30'))
timestamps()
}
}
You can try the following way to perform parallel tasks execution for your pipeline job:
def tasks = [:]
tasks["TasK No.1"] = {
stage ("TASK1"){
node('master') {
sh '<docker_build_command_here>'
}
}
}
tasks["task No.2"] = {
stage ("TASK2"){
node('master') {
sh '<docker_build_command_here>'
}
}
}
tasks["task No.3"] = {
stage ("TASK3"){
node('remote_node') {
sh '<docker_build_command_here>'
}
}
}
parallel tasks
If you want to execute parallel tasks on a single node and also want to have the same workspace for both the tasks then you can go with the following approach:
node('master') {
def tasks = [:]
tasks["TasK No.1"] = {
stage ("TASK1"){
sh '<docker_build_command_here>'
}
}
tasks["task No.2"] = {
stage ("TASK2"){
sh '<docker_build_command_here>'
}
}
parallel tasks
}

Resources