ScheduleBuild2 and determining if job was never started.

478 views
Skip to first unread message

Niksan

unread,
May 19, 2016, 9:37:22 AM5/19/16
to Jenkins Users
So, you can fire jobs off in Groovy using ScheduleBuild2 which returns a future.  By its nature, Jenkins will purge any duplicate build requests at some point.

How can we tell given a future if that job was purged by Jenkins itself to know it never actually ran? Or does that future return that it was cancelled, and if so, how can one determine who cancelled it, whether a user or Jenkins itself.

Daniel Beck

unread,
May 19, 2016, 3:09:22 PM5/19/16
to jenkins...@googlegroups.com

> On 19.05.2016, at 15:37, 'Niksan' via Jenkins Users <jenkins...@googlegroups.com> wrote:
>
> So, you can fire jobs off in Groovy using ScheduleBuild2 which returns a future. By its nature, Jenkins will purge any duplicate build requests at some point.

ParameterizedJobMixin#scheduleBuild2(int, List) returns a Queue.Item, or null of scheduling failed. If the item was merged, you get a previously existing Queue.Item.

Using its #getId() you can continue to query the Queue#getItem(long) for state transitions, e.g. whether it becomes a Queue.LeftItem, and if so, check its #isCancelled().

Niksan

unread,
May 20, 2016, 5:33:08 AM5/20/16
to Jenkins Users, m...@beckweb.net
I'm not sure under what circumstances an item merge occurs, but that would be problematic, as it would be waiting on someone else's job and tying a node up needlessly.

Scraping though the code on GitHub last night I saw there is a check to see if scheduling should happen, one of the simple checks being a like for like and returns with ScheduleResult.Refused
which is a shame that's not propagated up, however, it does seem that you can assume when the future is done and getJob yields null, it never started, I guess with a mixture of cancel checks and what not could determine the actual cause.

I'd have to run some tests to see what means what.

Niksan

unread,
May 20, 2016, 6:44:53 AM5/20/16
to Jenkins Users, m...@beckweb.net
I did some tests and it appears to merge, or rather, schedulebuild2 just returns the instance of the already queued job from a previous scheduledbuild2 as you suggested, that's incredibly annoying, so I guess one has to take a snapshot of the build queue then schedulebuild2, if it's already in the queue, assume a refused.  And of course hope the queue doesn't change between those operations.  Wow. :)

Niksan

unread,
May 20, 2016, 7:41:44 AM5/20/16
to Jenkins Users, m...@beckweb.net
Also, how is one supposed to getId() from a hudson.model.queue.FutureImpl exactly?


On Thursday, May 19, 2016 at 8:09:22 PM UTC+1, Daniel Beck wrote:

Daniel Beck

unread,
May 20, 2016, 8:29:41 AM5/20/16
to jenkins...@googlegroups.com

> On 20.05.2016, at 13:41, 'Niksan' via Jenkins Users <jenkins...@googlegroups.com> wrote:
>
> Also, how is one supposed to getId() from a hudson.model.queue.FutureImpl exactly?

I mentioned the wrong method signature, I was referring to static ParameterizedJobMixin#scheduleBuild2(Job,int,Action…) that returns a Queue.Item.

Niksan

unread,
May 20, 2016, 8:33:23 AM5/20/16
to Jenkins Users, m...@beckweb.net
Ok, thanks to Daniel with the initial pointers I was able to start hunting down the correct path at least. Short of not being able to lock the queue, it will suffice.

The future has a variable in there called 'task' I didn't see this in the docs at all and saw it doing a .dump() of the object, with this in hand I was able to do the following.

  def queue = Jenkins.getInstance().getQueue()
 
def ids = queue.getItems()*.getId()
 
def job = jobToTrigger.scheduleBuild2( 1, new Cause.UpstreamCause( build ) )
   
 
if(!ids.contains(queue.getItem(job.task).getId())) {
    println
("Adding job ${job.task}")
    futureJobs
.add( job )
 
} else {
    println
("Job aleady queued")
 
}

It's doesn't look great for me, and I'll be suprised if there's not a better approach, but something is better than nothing I guess.

Niksan

unread,
May 20, 2016, 10:24:08 AM5/20/16
to Jenkins Users, m...@beckweb.net
And it turns out that code was splodge, man, so tedious. :D

Anyhoo, for reference and if anyone else finds it useful, the correct, more lightweight solution is

def triggerJob( jobToTrigger, cause, parameters ) {
 
 
def currentJobFutures = Jenkins.getInstance().getQueue().getItems(jobToTrigger)*.getFuture()
  newJobFuture
= parameters!=null ? jobToTrigger.scheduleBuild2( 0, cause, parameters ) : jobToTrigger.scheduleBuild2( 0, cause )
   
 
return currentJobFutures.contains(newJobFuture) ? null : newJobFuture
}
Reply all
Reply to author
Forward
0 new messages