В декларативном проекте jenkins - могу ли я установить ярлык агента динамически?

Есть ли способ установить ярлык агента динамически, а не как обычную строку?

Задача состоит из двух этапов:

  1. Первый этап - всегда работает на "хозяине". В конце этого этапа я узнаю, на каком агенте должен пройти второй этап.
  2. Второй этап - должен действовать на агента, принятого на первом этапе.

Моя (не работающая) попытка выглядит так:

pipeline {
    agent { label 'master' }
    stages {
        stage('Stage1') {
            steps {
                script {
                    env.node_name = "my_node_label"
                }
                echo "node_name: ${env.node_name}"
            }
        }

        stage('Stage2') {
            agent { label "${env.node_name}" }
            steps {
                echo "node_name: ${env.node_name}"
            }
        }
    }
}

Первое эхо работает отлично, и печатается "my_node_label". Второй этап не запускается на агенте с надписью "my_node_label", а консольная печать:

Нет узлов с меткой 'null

Может быть, это может помочь - если я просто поставлю "$ {env}" в поле метки, я вижу, что это класс java, поскольку он печатает:

Нет узлов с меткой'[email protected]

Ответ 1

Чтобы узнать, как это работает, используйте объект GString для выполнения println и одновременно возвращайте переменную для имени агента. Вы можете видеть на выходе, что эта строка хорошо оценивается перед любым другим кодом конвейера.

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
                }
            }
    }

    }
}

Консольный вывод (обратите внимание, что на самом деле у меня нет узла в этом экземпляре с именем Windows, поэтому я прервал его после того, как он не смог его найти):

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

Обратите внимание, как строка Right Now the Agent Name is Windows появляется очень рано на выходе. Это объясняет, почему ваше значение равно null. Этот оператор оценивается задолго до того, как ваш скрипт изменяет переменную.

Я могу попытаться использовать ленивый GString чтобы получить переменную позже.

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

К сожалению, это вызывает ошибку, потому что ожидает тип String. По-видимому, он может принуждать нелазную GString к String самостоятельно, но не к ленивой версии. Поэтому, когда я принуждаю принуждение к String, конечно, он оценивает переменную в это время (что опять же, до того, как фактически выполняется код конвейера).

agent { label agentLabel as String }

Вы можете решить проблему, обратившись к методу распределения старого узла:

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
                }
            }
        }
    }
}

Вы можете увидеть на этом консольном выходе, что теперь он правильно находит узел Linux и заканчивает конвейер. Ранняя оценка while agentName == Windows никогда не происходит:

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

Это, вероятно, будет работать без ленивого GString и типа принуждения позже, но я не пробовал это.

Ответ 2

Вот как я это сделал: смешайте сценарий и декларативный конвейер. Сначала я использовал скриптовый синтаксис, чтобы найти, например, ветку, в которой я включен. Затем определите переменную AGENT_LABEL. Этот var можно использовать в любом месте по декларативному трубопроводу

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"
            }
        }
    }
}

Ответ 3

это может быть что-то о контексте блока сценария.

это работает, используя метку "докер" на втором этапе:

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}"
            }
        }
    }
}

это не так (получается одинаково) Нет узлов с меткой "ошибка нуля":

def hotLabel = null

pipeline {
    agent { label 'master' }
    stages {
        stage('Stage1') {
            steps {
                script {
                    hotLabel = "docker"
                }
            }
        }

        stage('Stage2') {
            agent { label "${hotLabel}" }
            steps {
                echo "node_name: ${hotLabel}"
            }
        }
    }
}

Ответ 4

Это сработало для меня:

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

Ответ 5

Я хотел, чтобы рабочий процесс зависел от параметризованного задания, чтобы динамически вводить переменную. Я нашел, что следующее решение хорошо работает, просто используя строковые операции:

String Parameter

pipeline {

   agent { label 'LBL && '+nodeLabel }
   ...
}