0

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!

frederikvs
  • 43
  • 6
  • 1
    Possible duplicate: https://stackoverflow.com/a/10417240/4279361 – Joerg S Aug 15 '18 at 14:22
  • @JoergS Thanks, I hadn't seen that post yet. (I'd found some other documentation on that annotation, but it wasn't as clear on how to use it.) Unfortunately, it doesn't really help. The simple example of incrementing a variable works, but as soon as I add the complexity of rebooting the windows machine, something goes wrong. I've updated my question to reflect what I've tried and what the result was. Thanks again for your input! – frederikvs Aug 17 '18 at 09:05
  • Thinking of it from a different point of view it might be very difficult to use a groovy script in some freestyle job to achieve this - as the freestyle job usually expects a working connection to that machine. However for pipeline jobs I'd have an approach posted here: https://stackoverflow.com/questions/50234159/how-to-continue-jenkins-pipeline-after-a-host-reboot/51862423#51862423 – Joerg S Aug 17 '18 at 11:01

0 Answers0