36

I am triggering a parameterized Jenkins from from outside of jenkins via a http POST request:

I have enabled in the job configuration that the job can be triggered from outside and i can really trigger it by sending jenkins a request with a content like this:

POST
http://myJenkins.com/myJob/buildWithParameters?token=MYTOKEN Parameter: SCREEN_SIZE: 27

Triggering the job creation returns a successfull 201 CREATED http response.

My problem is that i dont know the id of the build job that was created. I want to monitor the state of the job. In order to do that i need to know the id. Otherwise, if i just take the latest build of that job, i could take wrong job.

Is there a reliable way to get the id of the created job?

Nakilon
  • 34,866
  • 14
  • 107
  • 142
nemoo
  • 3,269
  • 4
  • 38
  • 51
  • Here is the shell script i use for the same https://stackoverflow.com/questions/29150106/jenkins-rest-api-get-a-build-number-of-a-job-that-just-started/74162441#74162441 – anandhu Oct 22 '22 at 09:45

5 Answers5

53

Since Jenkins 1.519, enqueuing a build responds with a URL in the Location, pointing you to an item in the build queue:

$ nc localhost 8666
POST /jenkins/job/morgRemote/buildWithParameters?jenkins_status=1&jenkins_sleep=20&token=morgRemote HTTP/1.1
Host: localhost:8666

HTTP/1.1 201 Created
Location: http://localhost:8666/jenkins/queue/item/39/
Content-Length: 0
Server: Jetty(winstone-2.8)

Now if you add api/json (or api/xml and so on) to the end of it (so in this example it would be http://localhost:8666/jenkins/queue/item/39/api/json) then you will get a document that will contain build id for the given job. For json the retrieved object has executable attribute, which in turn has number and url attributes. number is the build id for the given job (35 here) and url is the jenkins build page url.

{
  "actions" : [
    {
      "parameters" : [
        {
          "name" : "jenkins_status",
          "value" : "1"
        },
        {
          "name" : "jenkins_sleep",
          "value" : "20"
        }
      ]
    },
    {
      "causes" : [
        {
          "shortDescription" : "Started by remote host 127.0.0.1",
          "addr" : "127.0.0.1",
          "note" : null
        }
      ]
    }
  ],
  "blocked" : false,
  "buildable" : false,
  "id" : 39,
  "inQueueSince" : 1423993879845,
  "params" : "\njenkins_status=1\njenkins_sleep=20",
  "stuck" : false,
  "task" : {
    "name" : "morgRemote",
    "url" : "http://localhost:8666/jenkins/job/morgRemote/",
    "color" : "red"
  },
  "url" : "queue/item/39/",
  "why" : null,
  "cancelled" : false,
  "executable" : {
    "number" : 35,
    "url" : "http://localhost:8666/jenkins/job/morgRemote/35/"
  }
}

be aware of 2 things:

  • inactive items in the build queue are garbage collected after few minutes, so you should retrieve build id ASAP
  • by default it takes few seconds between item is added to the queue until it gets build id. During this time executable and canceled attributes will be missing and why will be not null. You can change this behavior in "Advanced Project Options" of your job config by modifying "Quiet period" setting or in the jenkins global configuration.

:

  ...
  "url" : "queue/item/39/",
  "why" : "In the quiet period. Expires in 2.4 sec",
  "timestamp" : 1423993879845
}
Christopher Orr
  • 110,418
  • 27
  • 198
  • 193
morgwai
  • 2,513
  • 4
  • 25
  • 31
  • 2
    Thank you for your answer! This partially worked for me. I'm on v1.559 and if I have a NON parameterized build and use /build I get the location. When I use buildWithParameters I get an error... expected. On a parameterized build I do not get the correct location. If I use /build I get the job url and if I use buildWithParameters I get nothing in the Location header. Any ideas? – Ryan Cook Jan 06 '16 at 17:07
  • Regarding retrieving the build id ASAP because the queue item will get garbage collected see this issue: https://issues.jenkins-ci.org/browse/JENKINS-31039 – KCD Oct 30 '16 at 22:00
  • 3
    "by default it takes few seconds between item is added to the queue until it gets build id" - actually it could take seconds, hours, or days! Until Jenkins finds and assigns an executor to run the the job, the job has no build number. – Steven the Easily Amused Jul 20 '17 at 19:25
  • FYI: I am on **Jenkins ver. 2.138.4** with a quiet period of 1 second configured. the "executable" part would not appear if in the quiet period, and 1.5s delay was insufficient (`"why"` would be `"???"`). adding a 5s delay before reading the url solved it. – JoSSte Dec 11 '19 at 12:42
14

Update:

The other answer was added ~8 months after mine. I was unaware of the location header in the response at the time. That does sound like a good option for some cases. That said, based on the caveats in the answer and the comments (especially around parameterized builds), it appears that this answer still has some utility for some cases.

Original answer:

Unfortunately, they don't make this as straightforward as it could be. i.e. by, say, returning a JSON response with information like an id.

However, I believe a solid, though certainly non-trivial, workaround to that would be to leverage the cause parameter in the URL you use to trigger the build, and within that cause, add unique text that you can later parse to determine that you triggered it from your automation.

To further uniqueify the specific job, in case multiple are running around the same time, even from your automation, also include a unique ID of some type (it could simply be a sequence ID from your RDBMS or similar) inside of the cause parameter.

Then, you can use the JSON API to get information about the job you're remotely triggering. Again, it's somewhat indirect, but doable:

Hit a URL of the form:

http://<server>:<port>/job/<jobname>/lastBuild/api/json

You can add ?pretty=true to pretty-print it within the browser for better human readability.

That will get you the JSON of the last build. It will contain the causes attribute within the actions attribute, and in there (in another nested attribute named shortDescription) you would find the cause parameter you added, if this was one of the builds you triggered.

You could parse out both the special static text, and your generated ID to see if they match. If they do, you can then get the Jenkins id out of the JSON as well (search for buildNumber, which is nested).

If that build is either not triggered by your automation at all, or was, but the ID doesn't match, you can repeat the process for the N - 1 build until you find what you're looking for.

That JSON would be of the form:

http://<server>:<port>/job/<jobname>/<buildNumber>/api/json
khampson
  • 14,700
  • 4
  • 41
  • 43
  • can you please elaborate on how to add the `cause` parameter? I've search for Jenkins documentation and could not find any reference to this parameter. – Muxa Sep 21 '15 at 22:46
  • It's a parameter that you specify in the URL you use to trigger the build via *curl* or similar. e.g. add the parameter `cause=foo_100` to the URL. – khampson Sep 24 '15 at 01:53
  • Great response! There is potential for a race condition where your triggered job execution is still in the queue when you are searching through the builds history for the unique ID. You can avoid this by using the Location that is included in the 201 created response and basically wait for it to leave the queue. – jpadams Jan 28 '16 at 17:37
3

Turns out builds have the original queue ID. Furthermore you can query for only the build with your queue Id and poll it until you get a result because 404 should normally imply it is queued.

Pull the Queue ID off the Location header, e.g. 39

/jenkins/queue/item/39/

Repeatedly query for a build with that queue number until you get an answer

"http://{jenkins}job/{job}/api/xml?tree=builds[number,queueId]&xpath=//build[queueId=\"{queueId}\"]";

<build>
  <number>411</number>
  <queueId>39</queueId>
</build>

From this result you can pull the build number with the xpath /build/number/text()

(Note I could not use &xpath=//build[queueId={queueId}]/number/text() in the url because "primitive XPath result sets forbidden; implement jenkins.security.SecureRequester")

It is equally as lame as polling the queue api for an "executable" build. One advantage is the endpoint will persist longer - until the build is deleted, compared to an arbitrary time (which seems to be about 5 minutes).

KCD
  • 9,873
  • 5
  • 66
  • 75
  • Thanks for that! Also the xpath stuff makes things a lot easier, don't need a JSON parser to find the right bit of data. – Frans Aug 17 '17 at 14:58
2

I was able to use Java Jenkins api to achieve what you are looking for. https://github.com/jenkinsci/java-client-api

Sample Code:

JenkinsServer jenkinsServer = new JenkinsServer(newURI("http://yourJenkinsServer:8080/"));
JobWithDetails smokeTestJob = jenkinsServer.getJob("YourJobName");        
Map<String,String> jobParams = new HashMap<String,String>();
QueueReference queueReference = smokeTestJob.build(jobParams);

do {
  Thread.sleep(2000L);
  queueItem = jenkinsServer.getQueueItem(queueReference);
  log("Job still in queue"+queueItem.getExecutable());
  } while (queueItem.getExecutable() == null);

Build build = jenkinsServer.getBuild(queueItem);

while(build.details().isBuilding()){
  log("Job still running");
  Thread.sleep(10000L);
}

log("Result="+build.details().getResult());
user2225713
  • 253
  • 2
  • 9
1

Wanted to add to morgwai answer using the Location header.

I just solved this problem. The key after the Location header is to poll the job queue entry till it has an 'executable' entry that gives the job number that has been started.

I give the full answer to a similar question that I saw first that did not completely answer the problem:

https://stackoverflow.com/a/48531874/9291603

JSON from Jenkins job queue entry with the executable entry:

{
    "_class": "hudson.model.Queue$LeftItem",
    "actions": [
        {
            "_class": "hudson.model.CauseAction",
            "causes": [
                {
                    "_class": "hudson.model.Cause$RemoteCause",
                    "addr": "10.20.30.60",
                    "note": null,
                    "shortDescription": "Started by remote host 10.20.30.60"
                }
            ]
        }
    ],
    "blocked": false,
    "buildable": false,
    "cancelled": false,
    "executable": {
        "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun",
        "number": 45,
        "url": "http://192.168.115.187:8080/job/rf_systest/45/"
    },
    "id": 95,
    "inQueueSince": 1517342648136,
    "params": "",
    "stuck": false,
    "task": {
        "_class": "org.jenkinsci.plugins.workflow.job.WorkflowJob",
        "color": "blue_anime",
        "name": "rf_systest",
        "url": "http://192.168.115.187:8080/job/rf_systest/"
    },
    "url": "queue/item/95/",
    "why": null
}
P. Hawkins
  • 391
  • 3
  • 5