groovy 如何在一个'options'块中使用'NODE_NAME'?- Jenkinsfile声明性管道

wlzqhblo  于 2022-11-01  发布在  Jenkins
关注(0)|答案(1)|浏览(266)

问题:如何在一个options块中使用NODE_NAME

我已经尝试过简单的解决方案,尝试使用env.NODE_NAME,但是在options块中,它的结果是null

options { lock(resource: "${env.NODE_NAME}") }

Jenkins Pipeline文档解释了这不起作用的原因:
stage中,options指令中的步骤在进入agent或检查任何when条件之前被调用。
因此,在此上下文中,它被设置为null
接下来,我尝试了各种方法来获取stage { steps {}}级别的env.NODE_NAME,并通过一个全局Groovy变量将其传递上去,但是,这似乎不起作用。
在下面的示例Jenkinsfile中,全局Groovy变量GLOBAL_NODE_NAME在每个stepsstepscript块中的作用就像是一个局部变量。它的作用就像是pipeline { stage { options {} }}块中的一个全局变量一样。上下文不会上升到全局Groovy脚本级别。

我尝试做的事情:

我想在options块中使用lockable-resources插件的lock语法来限制并发作业。如果有多个作业运行parallelRun Tests“阶段,Jenkins代理节点上的所有RAM都会被消耗。因此,我尝试锁定每个节点的资源来限制此特定节点上的并发作业。
我使用的最小Jenkinsfile如下所示:

String GLOBAL_NODE_NAME = 'GLOBAL'
pipeline {
    agent { label 'ec2-node' }

    stages {
        stage('Get Node Name') {
            steps {
                script {
                    println("env.NODE_NAME='${env.NODE_NAME}'")
                    GLOBAL_NODE_NAME = NODE_NAME
                    println("GLOBAL_NODE_NAME='${GLOBAL_NODE_NAME}'")
                }
            }
        }
        stage('Build') {
            steps {
                script {
                    println("DEBUG Interpolation GLOBAL_NODE_NAME='${GLOBAL_NODE_NAME}'")
                    println("DEBUG Raw Groovy Variable GLOBAL_NODE_NAME='" + GLOBAL_NODE_NAME + "'")
                }
                sh "echo Shell string Interpolation GLOBAL_NODE_NAME='${GLOBAL_NODE_NAME}'"
                // Do build steps here
            }
        }

        stage('Run Tests') {
            // Inverse order LIFO
            options {
                // Get NODE_NAME from the currentBuild b/c lightweight executor returns 'null'
                lock(inversePrecedence: true, resource: "${GLOBAL_NODE_NAME}")
            }
            parallel {
                stage('Unit Tests') {
                    steps {
                        echo "Inside Steps block: GLOBAL_NODE_NAME='${GLOBAL_NODE_NAME}'"
                        script {
                            println("DEBUG Interpolation GLOBAL_NODE_NAME='${GLOBAL_NODE_NAME}'")
                            println("DEBUG Raw Groovy Variable GLOBAL_NODE_NAME='" + GLOBAL_NODE_NAME + "'")
                        }
                        sh "echo Shell string Interpolation GLOBAL_NODE_NAME='${GLOBAL_NODE_NAME}'"

                        // Do Unit Tests
                    }
                }

                stage('Integration Tests') {
                    steps {
                        sh 'echo this so example Jenkinsfile is valid'
                        // Do Integration Tests
                    }
                }
            } // end parallel
        }  // end Run Tests
    } // end stages
} // end pipeline

上述示例作业的输出:

  • 阶段:Get Node Name
  • x1米25英寸1x
  • GLOBAL_NODE_NAME='EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)'
  • 阶段:Build
  • DEBUG Interpolation GLOBAL_NODE_NAME='EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)'
  • DEBUG Raw Groovy Variable GLOBAL_NODE_NAME='EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)'
echo Shell string Interpolation GLOBAL_NODE_NAME=EC2 (EC2-Jenkins) -
 Pipeline Builder (i-feeb1ec0de5caff01d)

 Shell string Interpolation GLOBAL_NODE_NAME=EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)
  • 阶段:Run Tests
    ***注:**在options块中,echoprintln不可用......但我们可以在https://<jenkins-host-here>/lockable-resources/页上的资源名称中看到输出:
  • GLOBAL_NODE_NAME被设置为“GLOBAL“,所以stage =〉step赋值没有工作!

*可锁定资源显示resource的值仍设置为GLOBAL

  • 并行阶段:Unit Tests
  • Inside Steps block: GLOBAL_NODE_NAME='EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)'
  • DEBUG Interpolation GLOBAL_NODE_NAME='EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)'
  • DEBUG Raw Groovy Variable GLOBAL_NODE_NAME='EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)'
+ echo Shell string Interpolation GLOBAL_NODE_NAME=EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)

   Shell string Interpolation GLOBAL_NODE_NAME=EC2 (EC2-Jenkins) - Pipeline Builder (i-feeb1ec0de5caff01d)

EDIT:这是最终的工作解决方案

多亏了@AdamSmith的answer,它让我找到了正确的方向。下面是最终成功的Jenkinsfile结构的框架:

/* groovylint-disable DuplicateStringLiteral */
/* groovylint-disable-next-line CompileStatic */
String debugBegin = '============================== DEBUG ENV =============================='
String debugEnd = '============================== END DEBUG =============================='
/* groovylint-disable NestedBlockDepth */
/* groovylint-disable-next-line CompileStatic */
pipeline {
    agent { label 'ec2-node' }

    stages {
        stage('Build') {
            steps {
                script {
                   // Evaluates in 'heavyweight' executor context
                    println("Running on env.NODE_NAME='${env.NODE_NAME}'") // env.NODE_NAME works here
                }
                // Do build steps here
            }
        }

        stage('Run Tests') {
            options {
                /*
                Note: Cannot get NODE_NAME from this context!
                options is evaluated inside 'lightweight' executor, so currentBuild returns 'null'
                and any other method trying to pass env.NODE_NAME, or
                NODE_NAME up from the node / 'heavyweight' executor context did not work!

                lock(inversePrecedence: true, resource: "cannot-get-node-name") // Anything I tried here did not work!
                This context executes on jenkins master, and I could not find a way to pass the value back from a node
                */
                timeout(time: 15, unit: 'MINUTES')
            }
            steps {
                // Inverse order LIFO
                // lock NODE_NAME b/c parallel tests are RAM intensive
                lock(inversePrecedence: true, resource: "${NODE_NAME}") {
                    script { // hack so parallel syntax is made available here
                        parallel Test: { // map: {} syntax to pass to scripted-pipeline 'parallel'
                            stage('Test') {
                                try { // Switched from post { always {}} to => try {} finally {} here
                                    // because syntax did not work otherwise
                                    echo "Inside Steps block: NODE_NAME='${NODE_NAME}'"
                                    println("DEBUG Interpolation NODE_NAME='${NODE_NAME}'")
                                    println("DEBUG Raw Groovy Variable NODE_NAME='" + NODE_NAME + "'")
                                    sh "echo Shell string Interpolation GLOBAL_NODE_NAME='${NODE_NAME}'"
                                    echo "${debugBegin}"
                                    sh 'env' // STAGE_NAME is now 'Test'
                                    echo "${debugEnd}"
                                    sh 'make test'
                                    echo 'Tests Succeeded!'
                                }
                                finally {
                                    junit '**/path/to/test-reports/*.xml'
                                    sh 'make cleanup'
                                }
                            }
                        },
                        IntegrationTests: {
                            stage('Integration Tests') {
                                try {
                                    echo "${debugBegin}"
                                    sh 'env' // STAGE_NAME is now 'Integration Tests'
                                    echo "${debugEnd}"
                                    sh 'make integration'
                                    echo 'Integration Tests Succeeded!'
                                }
                                finally {
                                    sh 'make cleanup'
                                }
                            }
                        } // end parallel map
                    } // end script
                } // end lock NODE_NAME
            } // end steps
        } // end stage Run Tests
    } // end stages
} // end pipeline
niwlg2el

niwlg2el1#

你不能,但是由于locksteps块中 * 也 * 可用,你可以这样处理它。由于这些事情的性质,你可能不得不在这里使用script退出窗口来继续并行(抱歉)

stage('Run Tests') {
    steps {
        // Inverse order LIFO
        lock(inversePrecedence: true, resource: "${NODE_NAME}") {
            script {
                parallel [
                    'Unit Tests': {
                        echo "Inside Steps block: NODE_NAME='${NODE_NAME}'"
                        println("DEBUG Interpolation NODE_NAME='${NODE_NAME}'")
                        println("DEBUG Raw Groovy Variable NODE_NAME='" + NODE_NAME+ "'")
                        sh "echo Shell string Interpolation GLOBAL_NODE_NAME='${GLOBAL_NODE_NAME}'"
                    },
                    'Integration Tests': {
                        sh 'echo this so example Jenkinsfile is valid'
                    }
                ]
            } // end script
        } // end lock
    }  // end steps
} // end stage 'Run Tests'

相关问题