【问题标题】:How to copy artifacts from parallel child jobs如何从并行子作业中复制工件
【发布时间】:2020-10-23 06:19:43
【问题描述】:

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

子作业接受一系列参数并针对目标服务执行 JMeter 测试。这是有效的,并且在每个构建中归档四个 CSV 和一个 XML 文件。

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

问题是如何配置 Copy Artifacts 插件以从子作业中检索工件,以便将它们存档在父作业中。

  1. 我尝试了 buildParameter 选项(CC_DGN_Test 是子作业的名称)。我在子作业中创建了一个名为ParentBuildTag 的参数,类型为Build selector for Copy ArtifactPermission 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()
    }
}

构建参数被填充到子作业中,如下所示:

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. 我尝试在节点阶段的“构建作业”命令之后进行存储,并在后期阶段取消存储。这导致构建作业命令之后的 stash 命令出错(“14:00:19 分支节点 1 失败”)和后期阶段的 unstash 命令出错(“错误:没有文件包含在 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()
        }
    }
}

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

【问题讨论】:

  • 你能解决这个问题吗?
  • @edt_devel 不,我做不到。目前,子作业有一个管道步骤,它们处理自己的工件。父作业的存在只是为了生成配置数量的子作业,并保存计划定义。

标签: jenkins jenkins-pipeline jenkins-plugins


【解决方案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"
    }
}

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-14
    • 1970-01-01
    • 1970-01-01
    • 2022-10-01
    • 2023-03-08
    • 1970-01-01
    相关资源
    最近更新 更多