While working on my talk Writing a Hudson / Jenkins plugin (for EclipseCon NA 2014), I wanted to publish blog posts about the ideas mentioned in the talk; in this post I explain how you can interact with your CI server without using the web interface.
The Jenkins / Hudson remote API can be very convenient to quickly gather jobs status (and even to create or launch jobs ! but I won’t cover that aspect in this blog post); let’s have a look at some examples.
Contents
Have a look at the CLI to automate Jenkins configuration tasks.
It’s available at http://hudson-or-jenkins/cli : download the hudson-cli.jar or jenkins-cli.jar and get started with this first command :
1 |
$ java -jar jenkins-cli.jar -s http://jenkins.terracotta.eur.ad.sag:8080/ help |
Recently, I had to replace 50 jobs with a matrix job : once I created the matrix job, we decided to de activate the previous 50 jobs (before removing them for good)
Using the CLI, I could quickly login :
1 |
$ java -jar jenkins-cli.jar -s http://jenkins.xx:8080/ login --username anthony --password password |
then get the list of jobs :
1 |
$ java -jar jenkins-cli.jar -s http://jenkins.xx:8080/ list-jobs |
I saved the list of the 50 jobs I wanted to de activate in a text file, and I looped through this list :
1 |
$ while read p; do java -jar jenkins-cli.jar -s http://jenkins.xx:8080/ disable-job $p; done < list_of_jobs.txt |
More efficient than going through the UI, isn’t it ?
Every URL can be represented as XML or JSON
As explained in the Jenkins documentation and Hudson documentation, you just need to append /api/xml or /api/json or /api/python to any Jenkins URL to see the corresponding representation
You can configure the responses adding few arguments to the URL :
Depth
The default depth is 0, to get more details about your jobs, builds, etc, set the depth to 1 :
http://ci.jruby.org/api/xml?depth=1
This argument can be harmful to your CI server when bigger than 1 (it gets really deep past 1 …) since the responses tend to become really large.
Tree
The tree argument allows you to select some portion of the response
http://ci.jruby.org/api/xml?depth=1&tree=jobs[displayName,lastBuild[result]]
xpath and exclude (XML only)
Those arguments are probably the most powerful ones, sadly they are only available for xml responses :
If I only want to display jobs that were not successful, I can simply use this url :
And if I only want to see jobs with a name starting with « jruby », I can apply Xpath functions to the url :
http://ci.jruby.org/api/xml?xpath=hudson/job[starts-with(name,’jruby’)]&wrapper=mywrap
Few Jenkins and Hudson public instances URLs
It’s pretty easy to find some public Jenkins and Hudson instances to practice your latest URL filtering (google for « Hudson Dashboard » or « Jenkins Dashboard »), but it’s harder to find up to date and meaningful (few jobs) instances :
Jenkins :
Hudson :
- http://deadlock.netbeans.org/hudson/
- https://hudson.eclipse.org/hudson/
- https://hudson.eclipse.org/p2/
That said, try your local instance first
Jenkins and Hudson remote API scripts
The main interest of the Jenkins/Hudson remote API is to interact with it from your own software : I gather here some examples in few scripting languages
Groovy Script to parse Jenkins Hudson build results
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class BuildParser { static void main(String[] args) { if (args.length != 1) { println("Please run the script with a Jenkins or Hudson url as the only argument\n Example : groovy BuildParser.groovy http://ci.jruby.org"); return; } def url = args[0]; def xmlInputFilteringSuccess = new XmlParser().parse(url + "/api/xml?depth=1&tree=jobs[displayName,lastBuild[result]]&exclude=hudson/job[lastBuild[result=%27SUCCESS%27]]"); def xmlInputNoFilter = new XmlParser().parse(url + "/api/xml?depth=1&tree=jobs"); def jobs = xmlInputFilteringSuccess.job; println(jobs.size() + " jobs out of " + xmlInputNoFilter.job.size() + " are currently failing") jobs.each( { println( it.displayName.text() + " result is " + it.lastBuild.result.text()) } ) } } |
Python Script to parse Jenkins Hudson build results
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import ast import urllib import sys if len(sys.argv) != 2: print "Please run the script with a Jenkins or Hudson url as the only argument\n Example : python BuildParser.py http://ci.jruby.org" sys.exit(1) url = str(sys.argv[1]) print url xml_input_no_filter = ast.literal_eval(urllib.urlopen(url + "/api/python?depth=1&tree=jobs[displayName,lastBuild[result]]").read()) all_jobs = xml_input_no_filter['jobs'] non_successful_jobs = [row for row in all_jobs if 'SUCCESS' != row['lastBuild']['result']] print(str(len(non_successful_jobs)) + " jobs out of " + str(len(all_jobs)) + " are currently failing") for (i, item) in enumerate(non_successful_jobs): print "Job name : " + item['displayName'] + "Result : " + item['lastBuild']['result'] |
JavaScript code to parse Jenkins Hudson build results
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<!-- Strongly inspired by https://gist.github.com/alexschwartz/912787 --> <html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Build Parser</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function(){ var baseUrl; $('button').click(function(){ baseUrl = $("#baseUrl").val(); $("#jobUrl").html(baseUrl) var success = function(json) { var allJobs = json["jobs"]; var nonSuccessfulJobs = allJobs.filter(function( job ) { return job["lastBuild"] != null && job["lastBuild"]["result"] != "SUCCESS"; }); $("#downstream").html(nonSuccessfulJobs.length + " out of " + allJobs.length + " are currently failing"); $.each(nonSuccessfulJobs, function( index, value ) { $("#downstream").append("<br />Job name : " + value["displayName"] + " : "+ value["lastBuild"]["result"] ); }); }; $.ajax({ url: baseUrl + "/api/json", data: "depth=1&tree=jobs[displayName,lastBuild[result]]&jsonp=callBack", jsonpCallback: "callBack", dataType: 'jsonp', success: success }); }); }); </script> </head> <body marginwidth="50" marginheight="50" topmargin="50" leftmargin="50"> <h3>Input Data</h3> Hudson/Jenkins Base URL: <input type="text" id="baseUrl" value="https://hudson.eclipse.org/hudson/" size="80" /><br/> <button>update</button><br /> <h2>Output</h2> <h3>Failing Jobs</h3> <div id="downstream"> </div> </body></html> |
You can find those scripts on Github ! Or even execute them in your browser for the Javascript Jenkins Hudson Build parser script