In a declarative jenkins pipeline - can I set the agent label dynamically?

前端 未结 6 1009
-上瘾入骨i
-上瘾入骨i 2020-12-01 14:18

Is there a way to set the agent label dynamically and not as plain string?

The job has 2 stages:

  1. First stage - Runs on a \"master\" agent, always. At t
相关标签:
6条回答
  • 2020-12-01 14:26

    To see how this works, use a GString object to do a println and return the variable for the agentName at the same time. You can see from the output that this line evaluates well before any of the other pipeline code.

    agentName = "Windows"
    agentLabel = "${println 'Right Now the Agent Name is ' + agentName; return agentName}"
    
    pipeline {
        agent none
    
        stages {
            stage('Prep') {
                steps {
                    script {
                        agentName = "Linux"
                    }
                }
            }
            stage('Checking') {
                steps {
                    script {
                        println agentLabel
                        println agentName
                    }
                }
            }
            stage('Final') {
                agent { label agentLabel }
    
                steps {
                    script {
                        println agentLabel
                        println agentName
                    }
                }
        }
    
        }
    }
    

    Console output (note that I don't actually have node on this instance labeled Windows, so I aborted after it couldn't find it):

    Started by user Admin
    [Pipeline] echo
    Right Now the Agent Name is Windows
    [Pipeline] stage
    [Pipeline] { (Prep)
    [Pipeline] script
    [Pipeline] {
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] { (Checking)
    [Pipeline] script
    [Pipeline] {
    [Pipeline] echo
    Windows
    [Pipeline] echo
    Linux
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] { (Final)
    [Pipeline] node
    Still waiting to schedule task
    There are no nodes with the label ‘Windows’
    Aborted by Admin
    [Pipeline] // node
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] End of Pipeline
    ERROR: Queue task was cancelled
    Finished: ABORTED
    

    Notice how the line Right Now the Agent Name is Windows appears very early in the output. This explains why your value is null. That statement is evaluated long before your script modifies the variable.

    I might try to use a lazy GString to get the variable later.

    agentLabel = "${-> println 'Right Now the Agent Name is ' + agentName; return agentName}"
    

    Unfortunately, this throws an error because it is expecting a type of String. Apparently it can coerce the non-lazy GString to a String on its own, but not the lazy version. So when I force coercion to a String, of course, it evaluates the variable at that time (which is again, before the pipeline code actually runs).

    agent { label agentLabel as String }
    

    You can solve the problem by falling back to the old node allocation method:

    agentName = "Windows"
    agentLabel = "${-> println 'Right Now the Agent Name is ' + agentName; return agentName}"
    
    pipeline {
        agent none
    
        stages {
            stage('Prep') {
                steps {
                    script {
                        agentName = "Linux"
                    }
                }
            }
            stage('Checking') {
                steps {
                    script {
                        println agentLabel
                        println agentName
                    }
                }
            }
            stage('Final') {
    
                steps {
                    node( agentLabel as String ) {  // Evaluate the node label later
                        echo "TEST"
                    }
                    script {
                        println agentLabel
                        println agentName
                    }
                }
            }
        }
    }
    

    You can see from this console output that it now properly finds the Linux node and finishes the pipeline. The early evaluation while agentName == Windows never happens:

    Started by user Admin
    [Pipeline] stage
    [Pipeline] { (Prep)
    [Pipeline] script
    [Pipeline] {
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] { (Checking)
    [Pipeline] script
    [Pipeline] {
    [Pipeline] echo
    Right Now the Agent Name is Linux
    [Pipeline] echo
    Linux
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] { (Final)
    [Pipeline] echo
    Right Now the Agent Name is Linux
    [Pipeline] node
    Running on Slave 1 in /home/jenkinsslave/jenkins/workspace/test
    [Pipeline] {
    [Pipeline] echo
    TEST
    [Pipeline] }
    [Pipeline] // node
    [Pipeline] script
    [Pipeline] {
    [Pipeline] echo
    Right Now the Agent Name is Linux
    [Pipeline] echo
    Linux
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] End of Pipeline
    Finished: SUCCESS
    

    This would probably work without the lazy GString and type coercion later, but I didn't try that.

    0 讨论(0)
  • 2020-12-01 14:26

    Here is how I made it: mix scripted and declarative pipeline. First I've used scripted syntax to find, for example, branch I'm on. Then define AGENT_LABEL variable. This var can be used anywhere along the declarative pipeline

    def AGENT_LABEL = null
    
    node('master') {
      stage('Checkout and set agent'){
         checkout scm
         ### Or just use any other approach to figure out agent label: read file, etc
         if (env.BRANCH_NAME == 'master') {
            AGENT_LABEL = "prod"
         } else {
            AGENT_LABEL = "dev"
         }
       }
    }
    
    pipeline {
        agent {
           label "${AGENT_LABEL}"
        }
    
        stages {
            stage('Normal build') {
               steps {
                  echo "Running in ${AGENT_LABEL}"
                  sh "hostname"
               }
            } 
    
            stage ("Docker build") {
               agent{
                 dockerfile {
                    dir 'Dockerfiles'
                    label "${AGENT_LABEL}"
                 }
                }
                steps{
                    sh "hostname"
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 14:26

    This worked for me:

    env.agentName = ""
    
    branch_name = "10.1.0"
    pipeline {
        agent none
    
        stages {
            stage('Prep') {
                steps {
                    script {
                        println branch_name
                        if ("${branch_name}" == "9.2.0") {
                            env.agentName = "9.2agent"
                        } else {
                            env.agentName = "10.1agent"
                        }
                    }
                }
            }
    
            stage('Finish') {
                steps {
                    node (agentName as String) { println env.agentName }
                    script {
                        println agentName
                    }
                }
            }
    
        }
    }
    
    Output:
    SuccessConsole Output
    Started by user build
    Running in Durability level: MAX_SURVIVABILITY
    [Pipeline] stage
    [Pipeline] { (Prep)
    [Pipeline] script
    [Pipeline] {
    [Pipeline] echo
    10.1.0
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] { (Finish)
    [Pipeline] node
    Running on 10.1agent in /home/build/jenkins/workspace/testlabel
    [Pipeline] {
    [Pipeline] echo
    rbreg6
    [Pipeline] }
    [Pipeline] // node
    [Pipeline] script
    [Pipeline] {
    [Pipeline] echo
    rbreg6
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] End of Pipeline
    Finished: SUCCESS
    
    Changing the branch name to 9.2.0:
    Started by user build
    Running in Durability level: MAX_SURVIVABILITY
    [Pipeline] stage
    [Pipeline] { (Prep)
    [Pipeline] script
    [Pipeline] {
    [Pipeline] echo
    9.2.0
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] { (Finish)
    [Pipeline] node
    Running on 9.2agent in /shared/build/workspace/testlabel
    [Pipeline] {
    [Pipeline] echo
    rbregistry
    [Pipeline] }
    [Pipeline] // node
    [Pipeline] script
    [Pipeline] {
    [Pipeline] echo
    rbregistry
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] End of Pipeline
    Finished: SUCCESS
    
    0 讨论(0)
  • 2020-12-01 14:32

    it might be something about the context of the script block.

    this works, using a label of 'docker' in second stage:

    def hotLabel = 'docker'
    
    pipeline {
        agent { label 'master' }
        stages {
            stage('Stage1') {
                steps {
                    echo "node_name: ${hotLabel}"
                }
            }
    
            stage('Stage2') {
                agent { label "${hotLabel}" }
                steps {
                    echo "node_name: ${hotLabel}"
                }
            }
        }
    }
    

    this does not (gets the same There are no nodes with the label ‘null’ error):

    def hotLabel = null
    
    pipeline {
        agent { label 'master' }
        stages {
            stage('Stage1') {
                steps {
                    script {
                        hotLabel = "docker"
                    }
                }
            }
    
            stage('Stage2') {
                agent { label "${hotLabel}" }
                steps {
                    echo "node_name: ${hotLabel}"
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 14:32

    I used a ternary operator to have mine dynamically change.

    For below, if the Jenkins pipeline name ends in "prod" the label used is "myagent-prd". Otherwise, it's just "myagent".

    def ENVIRONMENT_NAME="${JOB_NAME}".tokenize('-').last().toLowerCase()
    
    pipeline {
        agent {
          label "myagent${ENVIRONMENT_NAME == "prod" ? "-prd" : "" }"
        }
    
    0 讨论(0)
  • 2020-12-01 14:38

    I wanted the workflow to stem from a parameterized job to dynamically inject the variable. I found the following solution worked well simply using in-line string operations:

    pipeline {
    
       agent { label 'LBL && '+nodeLabel }
       ...
    }
    
    0 讨论(0)
提交回复
热议问题