You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

当terraform.yaml无部署项时,如何删除Jenkins生成的种子作业?

解决方案:当terraform.yaml的deployments为空时删除对应Jenkins种子作业

问题分析

当前的CreateTerraformDeployJobs仅在deployments有内容时生成作业DSL脚本,jobDsl步骤只会对比当前DSL与历史生成的作业定义来删除不再存在的作业。但当deployments为空时,DSL脚本为空,无法触发自动删除逻辑,导致该代码库生成的子作业残留。

我们需要主动识别并删除当前代码库对应的所有Jenkins作业,以下是两种可行的实现方式:


方法一:修改CreateTerraformDeployJobs函数,内置空场景处理

直接在原函数中加入判断,当deployments为空时,通过Jenkins API筛选并删除目标作业:

修改后的完整代码:

/*
    基于terraform.yaml内容创建Terraform部署任务
*/
def call(Map terraformConfig, String repoPath, Map inlineParams = [:]) {
    dslScripts = []

    deployments = terraformConfig["deployments"]
    latestVersion = terraformConfig["version"]

    if (deployments) {
        // 原有生成作业的逻辑保持不变
        deployments.each { deployment ->
            deployment["environments"].each { environment, setting ->
                if (deployment.subcomponent) {
                    deploymentPath = "${deployment.namespace}/${deployment.application}/${deployment.component}-${deployment.subcomponent}-${environment}"
                } else {
                    deploymentPath = "${deployment.namespace}/${deployment.application}/${deployment.component}-${environment}"
                    deployment.subcomponent = ""
                }
                if (setting.approvers) {
                    approvers = setting.approvers.join(',')
                } else {
                    approvers = "authenticated"
                }
                repo_list_for_tfvars_param = [
                    "neoload-infra",
                    "devops-ec2-infra"
                ]
                repo_name = repoPath.replace("DET/", "").toString().trim()
                repo_exists = repo_list_for_tfvars_param.contains(repo_name)
                
                def jobScript
                if (repo_exists) {
                    jobScript = """
                        pipelineJob("${deploymentPath}") {
                            description("Generated for repoPath: ${repoPath}")
                            parameters {
                                stringParam('Version', 'master', '要部署的基础设施版本(例如 0.1.0, master, dev, feature/foobar 等)')
                                stringParam('ExtraArgs', '', '额外的Terraform参数,例如 \"-parallelism=1\" 或 \"-target=random_id.managed_8bytes\"。适用于所有阶段:plan/apply/destroy')
                                stringParam('tfVars', '', "Terraform变量,例如 -var='instance_count=3' -var='instance_type=t3.small'。变量必须在Terraform代码中定义,且适用于所有阶段:plan/apply/destroy")
                                choiceParam('DebugLogLevel', ['disabled', 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'], 'Terraform调试日志级别。适用于所有阶段:plan/apply/destroy')
                                stringParam('Destroy', '', '要销毁此环境,请在此文本框中输入 \"Please destroy all resources\" 或 \"Please destroy all resources with force\"。第一种选项流程:plan -destroy => apply;第二种:plan => destroy。')
                                stringParam('AutoApprove', '', '要自动批准构建或销毁此环境,请在此文本框中输入 \"Please run with auto approve\"。')
                            }
                            environmentVariables {
                                env('namespace', '${deployment.namespace}')
                                env('application', '${deployment.application}')
                                env('environment', '${environment}')
                                env('component', '${deployment.component}')
                                env('subcomponent', '${deployment.subcomponent}')
                                env('aws_account_id', '${setting.aws_account_id}')
                                env('aws_region', '${setting.aws_region}')
                                env('deploymentPath', '${deploymentPath}')
                                env('buildJobUrl', '${JOB_URL}')
                                env('repoPath', '${repoPath}')
                                env('WEBEX_SPACE_ID', '${WEBEX_SPACE_ID}')
                                env('approvers', '${approvers}')
                            }
                            logRotator {
                                numToKeep(50)
                            }
                            definition {
                                cps {
                                    script("""
                                        @Library(value='tls-lib@master', changelog=false) _
                                        TerraformDeployPipeline(params, ${inlineParams})
                                    """.stripIndent())
                                sandbox(true)
                                }
                            }
                        }
                    """.stripIndent()
                } else {
                    jobScript = """
                        pipelineJob("${deploymentPath}") {
                            description("Generated for repoPath: ${repoPath}")
                            parameters {
                                stringParam('Version', 'master', '要部署的基础设施版本(例如 0.1.0, master, dev, feature/foobar 等)')
                                stringParam('ExtraArgs', '', '额外的Terraform参数,例如 \"-parallelism=1\" 或 \"-target=random_id.managed_8bytes\"。适用于所有阶段:plan/apply/destroy')
                                choiceParam('DebugLogLevel', ['disabled', 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'], 'Terraform调试日志级别。适用于所有阶段:plan/apply/destroy')
                                stringParam('Destroy', '', '要销毁此环境,请在此文本框中输入 \"Please destroy all resources\" 或 \"Please destroy all resources with force\"。第一种选项流程:plan -destroy => apply;第二种:plan => destroy。')
                                stringParam('AutoApprove', '', '要自动批准构建或销毁此环境,请在此文本框中输入 \"Please run with auto approve\"。')
                            }
                            environmentVariables {
                                env('namespace', '${deployment.namespace}')
                                env('application', '${deployment.application}')
                                env('environment', '${environment}')
                                env('component', '${deployment.component}')
                                env('subcomponent', '${deployment.subcomponent}')
                                env('aws_account_id', '${setting.aws_account_id}')
                                env('aws_region', '${setting.aws_region}')
                                env('deploymentPath', '${deploymentPath}')
                                env('buildJobUrl', '${JOB_URL}')
                                env('repoPath', '${repoPath}')
                                env('WEBEX_SPACE_ID', '${WEBEX_SPACE_ID}')
                                env('approvers', '${approvers}')
                            }
                            logRotator {
                                numToKeep(50)
                            }
                            definition {
                                cps {
                                    script("""
                                        @Library(value='tls-lib@master', changelog=false) _
                                        TerraformDeployPipeline(params, ${inlineParams})
                                    """.stripIndent())
                                sandbox(true)
                                }
                            }
                        }
                    """.stripIndent()
                }
                dslScripts.add(jobScript)
            }
        }
    } else {
        // 当deployments为空时,生成删除该repoPath对应作业的脚本
        def deleteScript = """
            import jenkins.model.Jenkins
            import hudson.model.AbstractProject

            def jenkinsInstance = Jenkins.get()
            def targetRepoPath = "${repoPath}"
            def jobsToDelete = []

            // 遍历所有作业,筛选出当前代码库生成的作业
            jenkinsInstance.getAllItems(AbstractProject.class).each { job ->
                if (job.description?.contains("Generated for repoPath: \${targetRepoPath}")) {
                    jobsToDelete.add(job.fullName)
                }
            }

            // 执行删除操作
            jobsToDelete.each { jobFullName ->
                def job = jenkinsInstance.getItemByFullName(jobFullName)
                if (job) {
                    println "Deleting job: \${jobFullName}"
                    job.delete()
                }
            }
        """.stripIndent()
        dslScripts.add(deleteScript)
    }

    jobDsl(
        scriptText: dslScripts.join('\n'),
        removedJobAction: 'DELETE',
        removedViewAction: 'DELETE',
        lookupStrategy: 'JENKINS_ROOT'
    )
}

关键修改点

  1. 作业标记:在生成每个作业时添加description字段,明确标记该作业所属的代码库repoPath,确保后续删除时能精准识别。
  2. 空场景处理:当deployments为空时,生成Groovy脚本通过Jenkins API遍历所有作业,筛选出带有对应标记的作业并执行删除。
  3. 权限要求:确保执行流水线的账号拥有Job/Delete权限,否则无法完成删除操作。

方法二:在构建流水线中单独处理删除逻辑

如果不想修改CreateTerraformDeployJobs函数,可以在构建流水线的Seed阶段加入判断,直接调用独立的删除函数:

修改构建流水线的Seed阶段

stage ("Seed") {
    when {
        branch 'master'
    }
    environment {
        repoPath = "${GIT_URL.split(':')[1].replace('.git', '')}"
    }
    steps {
        script {
            if (terraformConfig.deployments) {
                CreateTerraformDeployJobs(terraformConfig, repoPath, params)
            } else {
                deleteRepoGeneratedJobs(repoPath)
            }
        }
    }
}

添加删除函数deleteRepoGeneratedJobs

def deleteRepoGeneratedJobs(String repoPath) {
    script {
        def jenkins = Jenkins.get()
        def targetRepo = repoPath
        def jobsToDelete = []

        jenkins.getAllItems(AbstractProject.class).each { job ->
            // 通过作业的description识别目标作业
            if (job.description?.contains("Generated for repoPath: ${targetRepo}")) {
                jobsToDelete.add(job.fullName)
            }
        }

        if (jobsToDelete.isEmpty()) {
            println "未找到属于代码库 ${targetRepo} 的作业,无需删除"
        } else {
            println "将删除 ${jobsToDelete.size()} 个作业:"
            jobsToDelete.each { jobName ->
                def job = jenkins.getItemByFullName(jobName)
                if (job) {
                    println "删除作业:${jobName}"
                    job.delete()
                } else {
                    println "作业 ${jobName} 不存在,跳过"
                }
            }
        }
    }
}

注意事项

  1. 权限配置:确保Jenkins允许流水线脚本执行Jenkins API操作,并且执行账号拥有Job/Delete和Script Approval权限。
  2. 标记可靠性:使用description标记作业比依赖环境变量更稳定,避免环境变量被覆盖或丢失导致识别失败。
  3. 测试验证:在生产环境部署前,先在测试环境验证删除逻辑,防止误删无关作业。

内容的提问来源于stack exchange,提问作者hashim vayalar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:20:54