Jenkins任务持续触发MissingPropertyException问题求助
Jenkins脚本持续报MissingPropertyException问题排查与解决
问题描述
在Jenkins任务中执行Groovy脚本时,反复遇到MissingPropertyException,即使已定义相关变量。
初始报错信息
ERROR: Build step failed with exception groovy.lang.MissingPropertyException: No such property: manager for class: Script1 at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:66) at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:310) at Script1.isAnyJobRunningOrQueued(Script1.groovy:90) at Script1$isAnyJobRunningOrQueued.callCurrent(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:157) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:169) at Script1.run(Script1.groovy:48) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:574) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:612) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:583) at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript.evaluate(SecureGroovyScript.java:442) at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript.evaluate(SecureGroovyScript.java:379) at hudson.plugins.groovy.SystemGroovy.run(SystemGroovy.java:95) at hudson.plugins.groovy.SystemGroovy.perform(SystemGroovy.java:59) at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20) at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:816) at hudson.model.Build$BuildExecution.build(Build.java:199) at hudson.model.Build$BuildExecution.doRun(Build.java:164) at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:524) at hudson.model.Run.execute(Run.java:1899) at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:44) at hudson.model.ResourceController.execute(ResourceController.java:107) at hudson.model.Executor.run(Executor.java:449)
脚本用途及原代码
脚本用于当除clean_test清理任务外,无其他Jenkins任务运行或排队时执行清理操作,原代码如下:
import hudson.model.* def runningBuilds = [] def queuedBuilds = [] def manager = Hudson.instance def queue = manager.queue if (queue.items.size() > 0) { queuedBuilds = queue.items.collect { it.task.url } } for (computer in manager.computers) { for (executor in computer.executors) { if (executor.isBusy()) { def currentBuildUrl = executor.currentExecutable.url if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) runningBuilds << currentBuildUrl println "excluding current job from running builds: $currentBuildUrl" } } } } def maxRetries = 70 def retryIntervalSec = 10 def retries = 0 while (retries < maxRetries) { if (runningBuilds.isEmpty() && queuedBuilds.isEmpty()) { break } if (retries == maxRetries - 1) { println "Max retries reached. Exiting..." return } println "Waiting for running and queued builds to complete..." sleep(retryIntervalSec * 1000) retries++ } def queueAfterWait = manager.queue if (!isAnyJobRunningOrQueued(queueAfterWait)) { // Your cleanup logic here println "Running cleanup job on master node..." println "List current files in /tmp folder" println runCommand("ls -ltrh /tmp/") println "-------------------------------------------------------------------------------------------------------------" def diskSpaceBeforeCleanup = runCommand("ls -ltrh /tmp | head -n 1") println "TMP folder disk space before cleanup is $diskSpaceBeforeCleanup" // runCommand("rm -rf /tmp/<files>") println "-------------------------------------------------------------------------------------------------------------" def diskSpaceAfterCleanup = runCommand("ls -ltrh /tmp | head -n 1") println "TMP folder disk space after cleanup is $diskSpaceAfterCleanup" println "-------------------------------------------------------------------------------------------------------------" } else { println "Skipping cleanup as other Jenkins jobs are running or queued." println "Running builds:" if (runningBuilds.isEmpty()) { println "No running builds." } else { println runningBuilds.join('\n') } println "Queued builds:" if (queuedBuilds.isEmpty()) { println "No queued builds." } else { println queuedBuilds.join('\n') } } def runCommand(String command) { def process = command.execute() process.waitFor() return process.text.trim() } def isAnyJobRunningOrQueued(queue) { // Check running builds on all computers for (computer in manager.computers) { for (executor in computer.executors) { if (executor.isBusy()) { return true } } } // Check queued builds if (queue.items.size() > 0) { return true } return false }
尝试的修改及新报错
尝试改用Jenkins.instance替代Hudson.instance,甚至直接使用Jenkins.instance.queue,修改片段如下:
def jenkins = Jenkins.instance def queue = jenkins.queue // 或直接写 def queue = Jenkins.instance.queue
但出现新报错:
groovy.lang.MissingPropertyException: No such property: Jenkins for class: Script1 at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:66) at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:310) at Script1.run(Script1.groovy:6)
解决方案
1. 导入正确的Jenkins核心类
Jenkins类属于jenkins.model包,而非hudson.model,需添加对应导入语句:
import jenkins.model.Jenkins import hudson.model.Queue
新版本Jenkins已全面迁移到Jenkins类,老版本兼容的Hudson不再推荐使用。
2. 修复变量作用域问题
原脚本中isAnyJobRunningOrQueued方法直接引用外层manager变量,Groovy脚本中方法访问外层变量存在作用域限制。修改方法为接收Jenkins实例参数,避免依赖外层变量:
def isAnyJobRunningOrQueued(Jenkins jenkinsInstance, Queue queue) { // 检查运行中的任务,排除当前清理任务 for (computer in jenkinsInstance.computers) { for (executor in computer.executors) { if (executor.isBusy()) { def currentBuildUrl = executor.currentExecutable.url if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) { return true } } } } // 检查排队中的任务,排除当前清理任务 return queue.items.any { !it.task.url.contains('clean_test') } }
3. 修正语法错误
原脚本中if (!currentBuildUrl.matches(...))未加大括号,导致只有第一行属于if块,println语句会无条件执行,修正后:
if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) { runningBuilds << currentBuildUrl println "excluding current job from running builds: $currentBuildUrl" }
4. 完整修改后的脚本
import jenkins.model.Jenkins import hudson.model.Queue def runningBuilds = [] def queuedBuilds = [] // 使用Jenkins.get()替代instance,更安全的单例获取方式 def jenkinsInstance = Jenkins.get() def queue = jenkinsInstance.queue // 过滤掉清理任务的排队项 if (queue.items.size() > 0) { queuedBuilds = queue.items.collect { it.task.url }.findAll { !it.contains('clean_test') } } // 收集非清理任务的运行项 for (computer in jenkinsInstance.computers) { for (executor in computer.executors) { if (executor.isBusy()) { def currentBuildUrl = executor.currentExecutable.url if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) { runningBuilds << currentBuildUrl println "excluding current job from running builds: $currentBuildUrl" } } } } def maxRetries = 70 def retryIntervalSec = 10 def retries = 0 while (retries < maxRetries) { // 每次循环重新检查任务状态,避免列表过期 def currentRunning = [] def currentQueued = [] for (computer in jenkinsInstance.computers) { for (executor in computer.executors) { if (executor.isBusy()) { def url = executor.currentExecutable.url if (!url.matches('.*/clean_test/\\d+/')) { currentRunning << url } } } } currentQueued = jenkinsInstance.queue.items.collect { it.task.url }.findAll { !it.contains('clean_test') } if (currentRunning.isEmpty() && currentQueued.isEmpty()) { break } if (retries == maxRetries - 1) { println "Max retries reached. Exiting..." return } println "Waiting for running and queued builds to complete..." sleep(retryIntervalSec * 1000) retries++ } // 最终确认无其他任务后执行清理 if (!isAnyJobRunningOrQueued(jenkinsInstance, jenkinsInstance.queue)) { println "Running cleanup job on master node..." println "List current files in /tmp folder" println runCommand("ls -ltrh /tmp/") println "-------------------------------------------------------------------------------------------------------------" // 用df命令更准确获取磁盘空间,替代原ls方式 def diskSpaceBeforeCleanup = runCommand("df -h /tmp | grep -v Filesystem") println "TMP folder disk space before cleanup: $diskSpaceBeforeCleanup" // 执行清理命令,取消注释下方代码 // runCommand("rm -rf /tmp/<your-target-files>") println "-------------------------------------------------------------------------------------------------------------" def diskSpaceAfterCleanup = runCommand("df -h /tmp | grep -v Filesystem") println "TMP folder disk space after cleanup: $diskSpaceAfterCleanup" println "-------------------------------------------------------------------------------------------------------------" } else { println "Skipping cleanup as other Jenkins jobs are running or queued." println "Running builds:" def currentRunning = [] for (computer in jenkinsInstance.computers) { for (executor in computer.executors) { if (executor.isBusy()) { def url = executor.currentExecutable.url if (!url.matches('.*/clean_test/\\d+/')) { currentRunning << url } } } } if (currentRunning.isEmpty()) { println "No running builds." } else { println currentRunning.join('\n') } println "Queued builds:" def currentQueued = jenkinsInstance.queue.items.collect { it.task.url }.findAll { !it.contains('clean_test') } if (currentQueued.isEmpty()) { println "No queued builds." } else { println currentQueued.join('\n') } } def runCommand(String command) { def process = command.execute() process.waitFor() return process.text.trim() } def isAnyJobRunningOrQueued(Jenkins jenkinsInstance, Queue queue) { for (computer in jenkinsInstance.computers) { for (executor in computer.executors) { if (executor.isBusy()) { def currentBuildUrl = executor.currentExecutable.url if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) { return true } } } } return queue.items.any { !it.task.url.contains('clean_test') } }
5. 额外注意事项
- 脚本权限授权:在Jenkins后台进入
Manage Jenkins > In-process Script Approval,批准脚本中用到的方法签名(如Jenkins.get()、Queue.items等),否则会触发权限拦截。 - 实例获取方式:使用
Jenkins.get()替代Jenkins.instance,前者是官方推荐的单例获取方式,可避免空指针异常。 - 磁盘空间检测:原脚本用
ls获取磁盘空间不准确,改用df -h /tmp能真实反映分区使用情况。
内容的提问来源于stack exchange,提问作者Adithi
相关产品推荐
相关产品推荐

