如何从并行的子作业中复制工件

时间:2019-09-12 15:06:05

标签: jenkins jenkins-pipeline jenkins-plugins

我正在使用Jenkins自动执行并行的JMeter测试。这被设置为两个单独的Jenkins管道作业,即父作业和子作业。

子作业需要一系列参数,并针对目标服务执行JMeter测试。这可以正常工作,并且在每个版本上都存档四个CSV和一个XML文件。

父作业在不同节点上并行执行多次子作业。目前,它在测试中执行两次,但最终最终一次生成10或20个子作业。并行执行有效,子作业每次执行父项时都会记录两次构建,并且它们的工件已存档。

问题是如何配置“复制工件”插件以从子作业中检索工件,以便可以将其存储在父作业中。

  1. 我尝试了buildParameter选项(CC_DGN_Test是子作业的名称)。我在子作业ParentBuildTag中创建了一个参数,类型为Build selector for Copy Artifact。选中Permission to Copy Artifact复选框,并将Projects to allow copy artifacts字段设置为*
post {
    always {
        script {
            print "buildParameter('${BUILD_TAG}') == " + buildParameter("${BUILD_TAG}")
            copyArtifacts optional: false, projectName: 'CC_DGN_Test', selector: buildParameter("${BUILD_TAG}")
            archiveArtifacts "*.xml"
        }
        cleanWs()
    }
}

将build参数填充到子作业中,如下所示:

stage('Node 2') {
    agent { node { label 'PIPELINE' } }
    steps {
        script {
            node2 = build job: 'CC_DGN_Test',
                parameters: [
                    string(name: 'dummy', value: "2"),
                    string(name: 'ParentBuildTag', value: "${BUILD_TAG}"),
                    string(name: 'Labels', value: "JMETER"),
                    ...additional parameters snipped...
                ]
        }
    }
}

控制台日志显示错误:

Error when executing always post condition:
hudson.AbortException: Unable to find a build for artifact copy from: CC_DGN_Test
    at hudson.plugins.copyartifact.CopyArtifact.perform(CopyArtifact.java:412)
    at org.jenkinsci.plugins.workflow.steps.CoreStep$Execution.run(CoreStep.java:80)
    at org.jenkinsci.plugins.workflow.steps.CoreStep$Execution.run(CoreStep.java:67)
    at org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution.lambda$start$0(SynchronousNonBlockingStepExecution.java:47)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

什么都不会复制到父级。构建标记已正确打印到控制台日志(从post {}中的打印语句)。

08:18:52 buildParameter('jenkins-CC_DGN_TrickleTest-45') == @buildParameter(<anonymous>=jenkins-CC_DGN_TrickleTest-45)

这种方法看起来很有希望,但是我认为存在语法问题...我想我应该告诉copyArtifacts插件使用ParentBuildTag参数,其值为'jenkins-CC_DGN_TrickleTest-45',但是我没有找到描述语法的示例。

  1. 我已经尝试为子工作使用特定的内部版本号。
stage('Node 2') {
    agent { node { label 'PIPELINE' } }
    steps {
        script {
            node2 = build job: 'CC_DGN_Test',
                parameters: [
                    string(name: 'dummy', value: "2"),
                    string(name: 'ParentBuildTag', value: "${BUILD_TAG}"),
                    string(name: 'Labels', value: "JMETER"),
                    ...additional parameters snipped...
                ]
            print "Build number (node 2) = " + node2.number //prints build number to console e.g. "Build number (node 2) = 102"
            copyArtifacts optional: false, filter: '*.xml, *.csv', fingerprintArtifacts: true, projectName: 'CC_DGN_Test', selector: specific(node2.number)
        }
    }
}

内部版本号已正确打印到控制台日志中,但未记录任何错误,并且未复制任何内容。

  1. 我已经为buildParameter方法尝试了其他语法,但这种方法无效。
properties([parameters([
    [$class: 'BuildSelectorParameter',
    defaultSelector: upstream(fallbackToLastSuccessful: true),
    description: '',
    name: 'ParentBuildTag']])
])
copyArtifacts(
    projectName: 'CC_DGN_Test',
    selector: [
        class: 'ParameterizedBuildSelector', 
        parameterName: 'ParentBuildTag'
    ]
);

再次,我怀疑我需要告诉它对于ParentBuildTag使用什么值,但是我从中借用的语法示例并未显示如何执行此操作。 “上游...”部分只是我从示例中复制的内容,不是我认为我需要的内容,但是包含在我的测试中似乎无害。

  1. 我已经尝试在节点阶段在“ build job”命令之后进行隐藏,而在后期阶段进行隐藏。这些导致在构建作业命令之后在stash命令处出现错误(“ 14:00:19在分支节点1中失败”),并且在后期阶段在unstash命令处导致错误(“ ERROR:stash'node1xml中未包含文件) '“)。
stash includes: '*.xml', name: 'node1xml'
unstash 'node1xml'
  1. 我尝试将子作业的最低要求部分移至父作业,而不调用子作业。只要我以不同的方式命名它们,它就确实存储了一些构件(某些构件没有对底层JMeter脚本进行重大更改就无法更改),但是我宁愿不必将变量传递给JMeter脚本以根据Jenkins构建参数修改文件名。这太合我的口味了,我喜欢每个孩子的工作都有单独的创建记录。

以下是当前的父级作业配置,为简洁起见,这里将其剪裁掉了:

pipeline {
    agent { node { label 'PIPELINE' } }
    options {
        timeout(time: 1, unit: 'HOURS')
        buildDiscarder(logRotator(numToKeepStr: '100'))
        timestamps()
    }
    environment {
        node1 = ""
        node2 = ""
    }

    stages {
        stage('Clean Up') {
            steps {
                cleanWs()
            }
        }

        stage('Test') {
            parallel {
                stage('Node 1') {
                    agent { node { label 'PIPELINE' } }
                    steps {
                        script {
                            node1 = build job: 'CC_DGN_Test',
                                parameters: [
                                    string(name: 'dummy', value: "1"),
                                    string(name: 'ParentBuildTag', value: "${BUILD_TAG}"),
                                    string(name: 'Labels', value: "JMETER"),
                                    ...additional parameters snipped...
                                ]
                        }
                    }
                }

                stage('Node 2') {
                    agent { node { label 'PIPELINE' } }
                    steps {
                        script {
                            node2 = build job: 'CC_DGN_Test',
                                parameters: [
                                    string(name: 'dummy', value: "2"),
                                    string(name: 'ParentBuildTag', value: "${BUILD_TAG}"),
                                    string(name: 'Labels', value: "JMETER"),
                                    ...additional parameters snipped...
                                ]
                        }
                    }
                }
            }
        }
    }

    post {
        always {
            script {
                copyArtifacts optional: false, projectName: 'CC_DGN_Test', selector: buildParameter("${BUILD_TAG}")
                archiveArtifacts "*.xml"
            }
            cleanWs()
        }
    }
}

我的目标是根据当前配置,在作业完成后,父作业总共包含八个CSV和两个XML,但是当前父作业没有任何存档。 copyArtifact语法在哪里出问题?

1 个答案:

答案 0 :(得分:1)

您的观点2。方法是正确的方法。您只需要将node2.number转换为字符串:

selector: specific("${node2.number}")

您也可以使用方法来调用子作业。这是一个示例脚本:

#! groovy
pipeline {
    environment {
        childJobName = "Testing/MyChildJob"
    }
    stages {
        stage('Child Jobs') {
            parallel {
                stage('ChildJob1') {
                    steps {
                        runJob(childJobName, '@tag1 @tag2', 'job1')
                    }
                }
                stage('ChildJob2') {
                    steps {
                        runJob(childJobName, '@tag3 @tag4', 'job2')
                    }
                }
            }
        }
    }
    post {
        cleanup{
            cleanWs()
        }
    }
}

def runJob(String jobName, String tags, String rootReportDir) {

    def childJob = build job: jobName, propagate: false, wait: true, parameters: [string(name: 'TAGS', value: tags)]
    copyArtifacts filter: "report.html", projectName: jobName, selector: specific("${childJob.number}"), target: rootReportDir
    archiveArtifacts artifacts: "${rootReportDir}/report.html"

    if (childJob.result == "FAILURE") {
        bat "exit 1"
    }
}

在此示例中,子作业都是相同的詹金斯作业。父作业将不同的参数传递给它们。

由子作业生成的报告文件被复制到父作业的rootReportDir中。每个子作业的rootReportDir应该是唯一的,以便每个报表在归档到父作业时都具有唯一的路径。