在声明性詹金斯管道中 - 我可以动态设置代理标签吗?
In a declarative jenkins pipeline - can I set the agent label dynamically?
有没有办法动态设置代理标签而不是纯字符串?
作业有 2 个阶段:
- 第一阶段 - 始终在 "master" 代理上运行。在这个阶段结束时,我将知道第二阶段应该在哪个代理上 运行。
- 第二阶段 - 应该 运行 在第一阶段决定代理。
我的(无效)尝试如下所示:
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}"
}
}
}
}
第一个 echo 工作正常并且打印了 "my_node_label"。
第二阶段在标记为 "my_node_label" 的代理上 运行 失败,控制台打印:
There are no nodes with the label ‘null’
也许它可以提供帮助 - 如果我只是将“${env}”放在标签字段中,我可以看到这是一个 java class,因为它打印:
There are no nodes with the label ‘org.jenkinsci.plugins.workflow.cps.EnvActionImpl@79c0ce06’
这可能与脚本块的上下文有关。
这有效,在第二阶段使用 'docker' 标签:
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}"
}
}
}
}
这不是(得到相同的 There are no nodes with the label ‘null’ 错误):
def hotLabel = null
pipeline {
agent { label 'master' }
stages {
stage('Stage1') {
steps {
script {
hotLabel = "docker"
}
}
}
stage('Stage2') {
agent { label "${hotLabel}" }
steps {
echo "node_name: ${hotLabel}"
}
}
}
}
要查看其工作原理,请使用 GString
对象同时执行 println
和 return agentName 的变量。您可以从输出中看到,该行在任何其他管道代码之前进行了很好的评估。
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
行是如何出现在输出的最前面的。这解释了为什么您的值为空。在您的脚本修改变量之前很久就对该语句进行了评估。
稍后我可能会尝试使用惰性 GString
来获取变量。
agentLabel = "${-> println 'Right Now the Agent Name is ' + agentName; return agentName}"
不幸的是,这会引发错误,因为它需要一种字符串类型。显然它可以自己将非惰性 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
并稍后键入强制,这可能会起作用,但我没有尝试过。
这是我的做法:混合脚本化和声明式管道。首先,我使用脚本语法来查找,例如,我所在的分支。然后定义 AGENT_LABEL 变量。这个变量可以在声明管道的任何地方使用
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"
}
}
}
}
这对我有用:
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
我希望工作流源自参数化作业以动态注入变量。我发现以下解决方案仅使用内联字符串操作就可以很好地工作:
pipeline {
agent { label 'LBL && '+nodeLabel }
...
}
我使用三元运算符让我的动态变化。
在下面,如果 Jenkins 管道名称以“prod”结尾,则使用的标签是“myagent-prd”。否则,它只是“myagent”。
def ENVIRONMENT_NAME="${JOB_NAME}".tokenize('-').last().toLowerCase()
pipeline {
agent {
label "myagent${ENVIRONMENT_NAME == "prod" ? "-prd" : "" }"
}
对于我工作的管道作业,我需要解决类似的问题。我只想为整个管道设置一个代理!
我通过创建一个 returns 节点名作为字符串的函数解决了这个问题,我可以直接在管道中调用它。
在我的例子中,节点名是 Jenkins 环境变量 $JOB_BASE_NAME 的一部分。
但是您可以使用 Jenkins 脚本块中允许的任何逻辑,我想这是一个很大的优势。
// determine agent to run tests on
def agent_selector() {
if (env.JOB_BASE_NAME.contains('Nodename1')) {
return "Nodename1"
} else if (env.JOB_BASE_NAME.contains('Nodename2')) {
return "Nodename2"
} else {
println("Could not get Agent from 'JOB_BASE_NAME' !")
error('Aborting Build.')
}
}
// start of pipeline
pipeline {
agent {label agent_selector()}
stages {
stage('Stagestuff') {
steps {
echo "Hello World"
}
}
}
}
有没有办法动态设置代理标签而不是纯字符串?
作业有 2 个阶段:
- 第一阶段 - 始终在 "master" 代理上运行。在这个阶段结束时,我将知道第二阶段应该在哪个代理上 运行。
- 第二阶段 - 应该 运行 在第一阶段决定代理。
我的(无效)尝试如下所示:
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}"
}
}
}
}
第一个 echo 工作正常并且打印了 "my_node_label"。 第二阶段在标记为 "my_node_label" 的代理上 运行 失败,控制台打印:
There are no nodes with the label ‘null’
也许它可以提供帮助 - 如果我只是将“${env}”放在标签字段中,我可以看到这是一个 java class,因为它打印:
There are no nodes with the label ‘org.jenkinsci.plugins.workflow.cps.EnvActionImpl@79c0ce06’
这可能与脚本块的上下文有关。
这有效,在第二阶段使用 'docker' 标签:
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}"
}
}
}
}
这不是(得到相同的 There are no nodes with the label ‘null’ 错误):
def hotLabel = null
pipeline {
agent { label 'master' }
stages {
stage('Stage1') {
steps {
script {
hotLabel = "docker"
}
}
}
stage('Stage2') {
agent { label "${hotLabel}" }
steps {
echo "node_name: ${hotLabel}"
}
}
}
}
要查看其工作原理,请使用 GString
对象同时执行 println
和 return agentName 的变量。您可以从输出中看到,该行在任何其他管道代码之前进行了很好的评估。
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
行是如何出现在输出的最前面的。这解释了为什么您的值为空。在您的脚本修改变量之前很久就对该语句进行了评估。
稍后我可能会尝试使用惰性 GString
来获取变量。
agentLabel = "${-> println 'Right Now the Agent Name is ' + agentName; return agentName}"
不幸的是,这会引发错误,因为它需要一种字符串类型。显然它可以自己将非惰性 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
并稍后键入强制,这可能会起作用,但我没有尝试过。
这是我的做法:混合脚本化和声明式管道。首先,我使用脚本语法来查找,例如,我所在的分支。然后定义 AGENT_LABEL 变量。这个变量可以在声明管道的任何地方使用
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"
}
}
}
}
这对我有用:
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
我希望工作流源自参数化作业以动态注入变量。我发现以下解决方案仅使用内联字符串操作就可以很好地工作:
pipeline {
agent { label 'LBL && '+nodeLabel }
...
}
我使用三元运算符让我的动态变化。
在下面,如果 Jenkins 管道名称以“prod”结尾,则使用的标签是“myagent-prd”。否则,它只是“myagent”。
def ENVIRONMENT_NAME="${JOB_NAME}".tokenize('-').last().toLowerCase()
pipeline {
agent {
label "myagent${ENVIRONMENT_NAME == "prod" ? "-prd" : "" }"
}
对于我工作的管道作业,我需要解决类似的问题。我只想为整个管道设置一个代理! 我通过创建一个 returns 节点名作为字符串的函数解决了这个问题,我可以直接在管道中调用它。
在我的例子中,节点名是 Jenkins 环境变量 $JOB_BASE_NAME 的一部分。 但是您可以使用 Jenkins 脚本块中允许的任何逻辑,我想这是一个很大的优势。
// determine agent to run tests on
def agent_selector() {
if (env.JOB_BASE_NAME.contains('Nodename1')) {
return "Nodename1"
} else if (env.JOB_BASE_NAME.contains('Nodename2')) {
return "Nodename2"
} else {
println("Could not get Agent from 'JOB_BASE_NAME' !")
error('Aborting Build.')
}
}
// start of pipeline
pipeline {
agent {label agent_selector()}
stages {
stage('Stagestuff') {
steps {
echo "Hello World"
}
}
}
}