Jenkins Groovy解析Terraform输出出错的问题解决
问题修复与实现方案
核心问题分析
- 未定义变量错误:代码中使用了未声明的
jsonArray变量,直接触发groovy.lang.MissingPropertyException异常。 - 嵌套IP列表处理:获取到的IP是嵌套列表格式
[[10.0.253.236], [10.0.253.234], ...],无法直接与VM名称一一匹配。 - 跨阶段数据传递问题:
currentBuild.description默认存储字符串,直接赋值列表会导致后续读取格式异常。
修改后的完整Pipeline代码
pipeline { agent any parameters { string(name: 'vmNames', defaultValue: 'host-01,host-02,host-03', description: 'Enter VM names (comma-separated)') string(name: 'xoaUrl', defaultValue: 'wss://xo.example.com', description: 'Enter XOA URL' ) string(name: 'xoaUser', defaultValue: 'red.crick', description: 'Enter XOA User' ) } environment { TERRAFORM_HOME = tool name: 'terraform', type: 'org.jenkinsci.plugins.terraform.TerraformInstallation' PATH = "${TERRAFORM_HOME}:${env.PATH}" XOA_URL = "${xoaUrl}" XOA_USER = "${xoaUser}" } stages { stage("Terraform init") { // 保留原有逻辑 } stage("Terraform plan") { // 保留原有逻辑 } stage("Terraform apply") { steps { dir('dev/xo/clusters/sandbox') { withCredentials([string(credentialsId: 'XOA_PASSWORD', variable: 'xoaPassword')]) { sh ''' export XOA_PASSWORD="${xoaPassword}" terraform apply -parallelism=1 -no-color "theplan" ''' script { def output = sh(script: 'terraform output -json', returnStdout: true).trim() echo "output is $output" // 读取嵌套IP列表并扁平化处理 def nestedIpAddresses = readJSON(text: output).ip_addresses.value def ipAddresses = nestedIpAddresses.flatten() // 转成一维列表:[10.0.253.236, 10.0.253.234, ...] echo "ipAddresses is $ipAddresses" echo "ipAddresses[0] is ${ipAddresses[0]}" // 将IP列表转为JSON字符串,存入currentBuild.description实现跨阶段传递 currentBuild.description = groovy.json.JsonOutput.toJson(ipAddresses) } } } } } stage("Set Hostname and Reboot to Get DNS Records Created.") { steps { script { // 解析currentBuild.description中的JSON字符串,还原为IP列表 def ipAddresses = readJSON(text: currentBuild.description) def hostnames = vmNames.split(',').collect { it.trim() } // 分割并去除空格 // 检查VM数量与IP数量是否匹配,避免索引越界 if (ipAddresses.size() != hostnames.size()) { error "VM数量(${hostnames.size()})与IP数量(${ipAddresses.size()})不匹配,终止流程" } withCredentials([sshUserPrivateKey(credentialsId: 'bpadmin-private-ssh-key', keyFileVariable: 'SSH_PRIVATE_KEY', usernameVariable: 'USER')]) { // 遍历匹配VM名称与对应IP for (int i = 0; i < ipAddresses.size(); i++) { def ipAddress = ipAddresses[i] def hostname = hostnames[i] echo "Setting hostname $hostname for IP address $ipAddress" // 可添加实际设置主机名的SSH命令,示例: // sh ''' // ssh -i "${SSH_PRIVATE_KEY}" -o StrictHostKeyChecking=no ${USER}@${ipAddress} "sudo hostnamectl set-hostname ${hostname} && sudo systemctl reboot" // ''' } } } } } } }
关键修改说明
- 扁平化IP列表:使用
flatten()方法将嵌套的IP列表转换为一维列表,解决嵌套格式无法直接匹配的问题。 - 修复未定义变量错误:删除引用未声明
jsonArray的代码,改用JsonOutput.toJson()将IP列表转为JSON字符串,通过currentBuild.description实现跨阶段数据传递。 - 跨阶段数据解析:在后续阶段通过
readJSON()将存储的JSON字符串还原为IP列表,保证数据格式正确。 - 添加边界检查:增加VM数量与IP数量的匹配校验,避免因数量不一致导致的索引越界错误。
- 优化主机名处理:对分割后的VM名称执行
trim()操作,移除可能存在的空格,确保匹配准确性。
内容的提问来源于stack exchange,提问作者Red Cricket
相关产品推荐
相关产品推荐

