Short version
In a Jenkins post-build groovy script, is there a way to have the Computer.waitUntilOnline
function time out after a certain period of time?
Background
We do testing for embedded devices, and our jenkins slaves are laptops connected to certain hardware setups. In certain situations we need to have a groovy post-build script reboot the computer, and wait for it to come online again. However, sometimes these machines don't come online again, and our groovy script just keeps waiting indefinitely.
The waitUntilOnline
function can throw an InterruptedException, but from what I can tell you need to be running multiple threads in order to to trigger that exception. Running multiple threads just to trigger a timeout seems like the wrong way to go about things.
I've also found some information on using timeout
. This is intended for Jenkins Pipelines, so I'm not sure I can use it in post-build groovy, and I've not gotten it working. I've tried various combinations, including :
timeout(20)
{
computer.waitUntilOnline()
}
and
timeout(20)
{
waitUntil{
try {
computer.waitUntilOnline()
}catch (exception){
manager.listener.logger.println("caught exception!");
}
}
}
but all seem to throw an exception like this :
groovy.lang.MissingMethodException: No signature of method: Script1.timeout() is applicable for argument types: (java.lang.Integer, Script1$_rebootAndWait_closure1) values: [20, Script1$_rebootAndWait_closure1@7b6564fc]
Any suggestions are appreciated.
EDIT :
I've also tried the @groovy.transform.TimedInterrupt
annotation, as mentioned in this question, and I'm getting weird results.
When I run the simple loopy example, I get the expected result : it prints some value for i. However, if I attempt to combine this with rebooting the computer, like so :
import hudson.util.RemotingDiagnostics
def runCmd(computer, cmd)
{
def channel = computer.getChannel()
str = RemotingDiagnostics.executeGroovy( """
p = '$cmd'.execute()
p.waitFor()
p.in.text
""", channel )
}
@groovy.transform.TimedInterrupt( 3L )
def loopy()
{
int i = 0
try {
while( true ) {
i++
}
}
catch( e ) {
manager.listener.logger.println("i is "+i);
}
}
def rebootAndWait(computer)
{
manager.listener.logger.println("about to loopy : " +computer.name);
cmd = 'shutdown /r /t 10 /c "Restarting after Jenkins test completed"'
// cmd = "cmd.exe /c echo loopy test > c:\\\\Users\\\\frederikvs\\\\fvs_test.txt"
runCmd(computer, cmd)
// eventually we want to wait here for the computer to come back online, but for now we'll just have loopy
loopy();
}
rebootAndWait(manager.build.getBuiltOn().toComputer())
I get weird results : sometimes I get the expected output of some value for i, and sometimes I get an uncaught exception :
java.util.concurrent.TimeoutException: Execution timed out after 3 units. Start time: Fri Aug 17 10:48:07 CEST 2018
at sun.reflect.GeneratedConstructorAccessor5665.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
at Script1.loopy(Script1.groovy)
at Script1.rebootAndWait(Script1.groovy:47)
at Script1$rebootAndWait.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
at Script1.run(Script1.groovy:51)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:585)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:623)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:594)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript.evaluate(SecureGroovyScript.java:343)
at org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildRecorder.perform(GroovyPostbuildRecorder.java:380)
at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:744)
at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:690)
at hudson.model.Build$BuildExecution.post2(Build.java:186)
at hudson.model.AbstractBuild$AbstractBuildExecution.post(AbstractBuild.java:635)
at hudson.model.Run.execute(Run.java:1819)
at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:429)
Here's the kicker : whether I get the value of i, or the exception, seems to depend on the previous run.
If I run it with the reboot command a few times, I always get the exception. If I run it with the echo command (currently commented out) a few times, I always get the expected result of printing i. But if I run it with the reboot command, then switch it around to the echo command and run that a few times, the first time with the echo it will give the exception, after that it'll give the value for i. And if I switch from the echo command to the reboot command, the first run with the reboot will also be fine (printing the value of i), and after that it'll start giving the exception.
I don't quite understand how the previous run can have an effect on the timeout of the present run...
Again, any input is appreciated!