Jenkins is an open source automation server that reliably builds, tests, and deploys software. Jenkins also comes with its own intuitive UI that allows you to create new jobs, schedule the execution of existing jobs, navigate into defined jobs on the system, and more.
However, occasions may arise when you need to programmatically perform the above functionalities including retrieving information, creating a new job, triggering a new build based on certain conditions, etc.
For requirements and functionalities such as these, Jenkins provides REST-like remote access APIs. Many wrapper projects have also been developed in Python, Ruby, and Java that consume the Jenkins JSON APIs and aim to allow access to all remote APIs that Jenkins provides.
One such Jenkins client project, which is written in Java and makes use of the Jenkins APIs, is jenkins-rest. In this article, I’ll show how to set up and start using jenkins-rest.
jenkins-rest is a Java client that is built on the top of jclouds for working with Jenkins REST API. jclouds, one of the main components, is used as the backend for communicating with Jenkins REST API.
The following sections describe the following:
- Setup of jenkins-rest client
- Different ways of connecting to Jenkins server using jenkins-rest
- A comprehensive example showing how to trigger a remote job using jenkins-rest
Maven Setup
The following dependency has to be added to the pom file:
<dependency> <groupId>io.github.cdancy</groupId> <artifactId>jenkins-rest</artifactId> <version>1.0.1</version> </dependency>
Connecting to Jenkins — Credentials
jenkins-rest can connect to the Jenkins server in one of the following ways:
admin:apiToken:
- Use
JenkinsClient.builder().apiToken("admin:apiToken")
Colon delimited username and password: admin:password:
- Use
JenkinsClient.builder().credentials("admin:password")
Base64 encoded username followed by password or api token:
- Use
JenkinsClient.builder().credentials("password")
- Use
JenkinsClient.builder().apiToken("apiToken")
The following Java method takes Jenkins URL, username, and the token as inputs and returns JenkinsClient.
public JenkinsClient getConnection( String url, String userName, String token) throws IOException { JenkinsClient client = JenkinsClient.builder().endPoint(url) .credentials(userName+”:”+token).build(); return client; }
Example
At a high level, the steps to trigger a job and get its corresponding status are as follows:
- Invoke
JobsApi build
method.IntegerResponse
object is returned that contains the queue id number of the submitted job. - Poll the
QueueApi queueItem
method using the queue id number from theInteger Response
. This returns aQueueItem
object containing the state of the build in the queue. The state could be one of the following:
a. Build cancellation before the build even starts (abort the polling, the build will never run)
b. Build pending (continue to wait or time out
c. Build executing with a build number (stop polling the queue) - Poll the
JobsApi buildInfo
method using theQueueItem
build number. This step returns a BuildInfo object. When the result method no longer returns null, the build is done. Theresult
method returns a string representing the build status (passed, failed, etc.).
The Java snippet below makes use of the methods from the jenkins-rest library to accomplish the above functionality:
public PipelineBuildInfo build(InputForPipelineExecution buildPipelineReq) throws IOException, InterruptedException { PipelineBuildInfo pipelineBuildInfo = new PipelineBuildInfo(); pipelineBuildInfo.setPipelineName(buildPipelineReq.getPipelineName()); pipelineBuildInfo.setPipelineId(buildPipelineReq.getPipelineId()); JenkinsClient client = getConnection(buildPipelineReq.getUrl(), buildPipelineReq.getUserName(), buildPipelineReq.getToken()); IntegerResponse queueId = client.api().jobsApi().build(null, buildPipelineReq.getPipelineName()); if (queueId.errors().size() > 0) { logger.debug(“The Queue Errors are ->” + queueId.errors()); pipelineBuildInfo.setMessage(queueId.errors().toString()); pipelineBuildInfo.setStatus(PipelineExecutionStatus.Failed.toString()); return pipelineBuildInfo; } logger.debug(“Check for the status”); QueueItem queueItem = client.api().queueApi().queueItem(queueId.value().intValue()); while (true) { if (queueItem.cancelled()) { logger.debug(“Job Cancelled”); pipelineBuildInfo.setMessage(queueItem.why()); pipelineBuildInfo.setStatus(PipelineExecutionStatus.Cancelled.toString()); break; } if (queueItem.executable() != null) { logger.debug(“Build is executing with # = “ + queueItem.executable().number()); break; } Thread.sleep(1000); queueItem = client.api().queueApi().queueItem(queueId.value().intValue()); } BuildInfo buildInfo = client.api().jobsApi().buildInfo(null, buildPipelineReq.getPipelineName(), queueItem.executable().number()); while (buildInfo.result() == null) { Thread.sleep(10000); buildInfo = client.api().jobsApi().buildInfo(null, buildPipelineReq.getPipelineName(), queueItem.executable().number()); } pipelineBuildInfo.setDuration(buildInfo.duration()); pipelineBuildInfo.setBuildNumber(buildInfo.number()); pipelineBuildInfo.setMessage(buildInfo.result()); pipelineBuildInfo.setStatus(PipelineExecutionStatus.Successful.toString()); logger.debug(“Build status: “ + buildInfo.result()); return pipelineBuildInfo; }
Conclusion
The real power of jenkins-rest lies in combining multiple Jenkins API calls for accomplishing a given use case. A use case to trigger a build would ideally involve the following steps: submit a job, track its progress, and report on the build status, which have been demonstrated in the example shown here.
References
Jenkins-rest GitHub page
Remote Access API
Disclaimer: The views expressed in this article are mine and my employer does not subscribe to the substance or veracity of my views.
Ready to find a job? Check out the latest job listings at Open Source JobHub.
Comments