Jenkins Declarative Pipelines introduced a couple of syntactic changes to the classic scripted pipeline using Groovy DSL. By the virtue of being new, there are some caveats that need a bit of workarounds at this stage.

Running steps with different agents

The syntax of the new pipelines is fairly simple. Below is the example of a working, basic pipeline taken from the documentation itself

pipeline {  
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
    post { 
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

One of the strengths introduced in the new pipeline is the specification of agent. For my current builds I had to use two agents and stash/unstash some of the contents between the nodes.

If you would like to execute some stages in a separate node than the others, you can do that in the following way:

  1. Define agent none for pipeline itself
  2. Specify agent after each stage block
pipeline {  
    agent none
    stages {
        stage ('Stage 1') {
            agent { node 'node-one' }
                steps {
                    echo 'Perform Stage 1 on node-one'
                }
        }
        stage ('Stage 2') {
            agent { node 'node-two' }
                steps {
                    echo 'Perform Stage 2 on node-two'
                }
        }
    }
}     

It's important that you specify agent none just below pipeline directive. You can of course specify an agent there, but it will mean that this agent becomes default one for the whole pipeline. You can still perform any step afterwards by specifying an agent under stage. However, in such case the worker on the node which is specified under pipeline will be occupied until the stage with separate agent is finished.

Below you can see the behaviour in question:

This is the scenario in cases when agent is specified right below pipeline directive:

pipeline {  
    agent {node 'master'}
}

However, when specifying agent per stage you can avoid occupying an executor. Consider the following pipeline:

pipeline {  
    agent none;
    stages {
        stage('This should run on ssh-slave') {
            agent {node 'sshslave'}
            steps {
                sh 'ping -c 60 127.0.0.1'
            }
        }
        stage('this should run on master') {
            agent {node 'master'}
            steps {
                sh 'ping -c 60 127.0.0.1'
            }
        }
    }
}

In this example we are firstly running ping command on sshslave and then on master. As there's no agent specified for the whole pipeline, we are occupying only the current node:

Followed by execution on master:

And in the pipeline stage view:

Tags: jenkins