
org.aerogear.digger.client.services.BuildService Maven / Gradle / Ivy
Show all versions of digger-java-client Show documentation
/**
* Copyright 2016-2017 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.aerogear.digger.client.services;
import com.offbytwo.jenkins.JenkinsServer;
import com.offbytwo.jenkins.model.*;
import org.aerogear.digger.client.DiggerClient;
import org.aerogear.digger.client.model.BuildTriggerStatus;
import org.aerogear.digger.client.model.LogStreamingOptions;
import org.aerogear.digger.client.util.DiggerClientException;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Provides functionality to trigger a build.
**/
public class BuildService {
private static final Logger LOG = LoggerFactory.getLogger(BuildService.class);
/**
* Default value of {@link #firstCheckDelay}
*/
public static final long DEFAULT_FIRST_CHECK_DELAY = 5 * 1000L;
/**
* Default value of {@link #pollPeriod}
*/
public static final long DEFAULT_POLL_PERIOD = 2 * 1000L;
private long firstCheckDelay;
private long pollPeriod;
/**
* @param firstCheckDelay how long should we wait (in milliseconds) before we start checking the queue item status
* @param pollPeriod how long should we wait (in milliseconds) before checking the queue item status for next time
*/
public BuildService(long firstCheckDelay, long pollPeriod) {
this.firstCheckDelay = firstCheckDelay;
this.pollPeriod = pollPeriod;
}
/**
* Get build logs for specific job and build number
*
* @param jobName name of the job
* @param buildNumber job build number
* @return String with file contents that can be saved or piped to socket
* @throws DiggerClientException when problem with fetching artifacts from jenkins
*/
public String getBuildLogs(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException, IOException {
Build build = this.getBuild(jenkins, jobName, buildNumber);
BuildWithDetails buildWithDetails = build.details();
return buildWithDetails.getConsoleOutputText();
}
/**
* Returns the build history for a job. As reported by {@link JobWithDetails#getBuilds()} it will return max 100 most-recent builds.
*
* Please note that this approach will take some time since we first fetch the builds in 1 call, then fetch build details in 1 call per build.
*
* @param jenkins Jenkins server client
* @param jobName name of the job
* @return the build history
* @throws DiggerClientException if connection problems occur
*/
public List getBuildHistory(JenkinsServer jenkins, String jobName) throws DiggerClientException {
final JobWithDetails job;
try {
job = jenkins.getJob(jobName);
if (job == null) {
LOG.error("Cannot fetch job from jenkins {0}", jobName);
throw new DiggerClientException("Cannot fetch job from jenkins");
}
final List builds = job.getBuilds();
final List returnValue = new ArrayList();
for (Build build : builds) {
try {
returnValue.add(build.details());
} catch (IOException e) {
LOG.error("Error while fetching the details for job {} with build number", jobName, build.getNumber(), e);
// re-raise it so the the outer block catches it and creates a DiggerClientException out of it
throw e;
}
}
return returnValue;
} catch (IOException e) {
LOG.error("Problem when getting the build history for job {0}", jobName, e);
throw new DiggerClientException(e);
}
}
/**
*
* @param jenkinsServer Jenkins server client
* @param jobName name of the job
* @param params build parameters to override defaults in the job
* @return The QueueReference
* @throws IOException if connection problems occur during connecting to Jenkins
* @throws InterruptedException if a problem occurs during sleeping between checks
*/
public BuildTriggerStatus triggerBuild(JenkinsServer jenkinsServer, String jobName, Map params) throws IOException, InterruptedException {
LOG.debug("Getting QueueReference for Job '{}'", jobName);
QueueReference queueReference = null;
JobWithDetails job = jenkinsServer.getJob(jobName);
if (job == null) {
LOG.debug("Unable to find job for name '{}'", jobName);
throw new IllegalArgumentException("Unable to find job for name '" + jobName + "'");
} else {
queueReference = MapUtils.isEmpty(params) ? job.build() : job.build(params);
if (queueReference == null) {
// this is probably an implementation problem we have here
LOG.debug("Queue reference cannot be null!");
throw new IllegalStateException("Queue reference cannot be null!");
}
LOG.debug("Queue item reference: {}", queueReference.getQueueItemUrlPart());
return new BuildTriggerStatus(BuildTriggerStatus.State.TRIGGERED, job.getNextBuildNumber(), queueReference);
}
}
/**
* See the documentation in {@link DiggerClient#build(String, long, Map)}
*
* @param jenkinsServer Jenkins server client
* @param jobName name of the job
* @param timeout timeout
* @param params build parameters to override defaults in the job
* @return the build status
* @throws IOException if connection problems occur during connecting to Jenkins
* @throws InterruptedException if a problem occurs during sleeping between checks
* @see DiggerClient#build(String, long, Map)
*/
public BuildTriggerStatus pollBuild(JenkinsServer jenkinsServer, String jobName, QueueReference queueReference, long timeout, Map params) throws IOException, InterruptedException {
final long whenToTimeout = System.currentTimeMillis() + timeout;
LOG.debug("Going to build job with name: {}", jobName);
LOG.debug("Going to timeout in {} msecs if build didn't start executing", timeout);
LOG.debug("Build triggered; queue item reference: {}", queueReference.getQueueItemUrlPart());
// wait for N seconds, then fetch the queue item.
// do it until we have an executable.
// we would have an executable when the build leaves queue and starts building.
LOG.debug("Going to sleep {} msecs", firstCheckDelay);
Thread.sleep(firstCheckDelay);
QueueItem queueItem;
while (true) {
queueItem = jenkinsServer.getQueueItem(queueReference);
LOG.debug("Queue item : {}", queueItem);
if (queueItem == null) {
// this is probably an implementation problem we have here
LOG.debug("Queue item cannot be null!");
throw new IllegalStateException("Queue item cannot be null!");
}
LOG.debug("Build item cancelled:{}, blocked:{}, buildable:{}, stuck:{}", queueItem.isCancelled(), queueItem.isBlocked(), queueItem.isBuildable(), queueItem.isStuck());
if (queueItem.isCancelled()) {
LOG.debug("Queue item is cancelled. Returning CANCELLED_IN_QUEUE");
return new BuildTriggerStatus(BuildTriggerStatus.State.CANCELLED_IN_QUEUE, -1, queueReference);
} else if (queueItem.isStuck()) {
LOG.debug("Queue item is stuck. Returning STUCK_IN_QUEUE");
return new BuildTriggerStatus(BuildTriggerStatus.State.STUCK_IN_QUEUE, -1, queueReference);
}
// do not return -1 if blocked.
// we will wait until it is unblocked.
final Executable executable = queueItem.getExecutable();
if (executable != null) {
LOG.debug("Build has an executable. Returning build number: {}", executable.getNumber());
return new BuildTriggerStatus(BuildTriggerStatus.State.STARTED_BUILDING, executable.getNumber().intValue(), queueReference);
} else {
LOG.debug("Build did not start executing yet.");
if (whenToTimeout > System.currentTimeMillis()) {
LOG.debug("Timeout period has not exceeded yet. Sleeping for {} msecs", pollPeriod);
Thread.sleep(pollPeriod);
} else {
LOG.debug("Timeout period has exceeded. Returning TIMED_OUT.");
return new BuildTriggerStatus(BuildTriggerStatus.State.TIMED_OUT, -1, queueReference);
}
}
}
}
/**
* See the documentation in {@link DiggerClient#build(String, long)}
*
* @param jenkinsServer Jenkins server client
* @param jobName name of the job
* @param timeout timeout
* @return the build status
* @throws IOException if connection problems occur during connecting to Jenkins
* @throws InterruptedException if a problem occurs during sleeping between checks
* @see DiggerClient#build(String, long)
*/
public BuildTriggerStatus build(JenkinsServer jenkinsServer, String jobName, long timeout) throws IOException, InterruptedException {
BuildTriggerStatus buildTriggerStatus = this.triggerBuild(jenkinsServer, jobName, null);
return this.pollBuild(jenkinsServer, jobName, buildTriggerStatus.getQueueReference(), timeout, null);
}
/**
* Retrieve a build from a specific job
*
* @param jenkins the jenkins instance
* @param jobName the name of the job
* @param buildNumber the build number
* @return {@link Build}
* @throws DiggerClientException
*/
public Build getBuild(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException {
try {
JobWithDetails job = jenkins.getJob(jobName);
if (job == null) {
LOG.error("Cannot fetch job from jenkins {0}", jobName);
throw new DiggerClientException("Cannot fetch job from jenkins");
}
Build build = job.getBuildByNumber(buildNumber);
return build;
} catch (IOException e) {
LOG.error("Problem when fetching logs for {0} {1}", jobName, buildNumber, e);
throw new DiggerClientException(e);
}
}
/**
* Get the build details of a job
*
* @param jenkins the jenkins instance
* @param jobName the name of the job
* @param buildNumber the build number
* @return {@link BuildWithDetails}
* @throws DiggerClientException
*/
public BuildWithDetails getBuildDetails(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException, IOException {
Build build = this.getBuild(jenkins, jobName, buildNumber);
BuildWithDetails buildWithDetails = build.details();
return buildWithDetails;
}
/**
* Cancel a build
* @param jenkins the jenkins instance
* @param jobName the name of the job
* @param buildNumber the build number
* @return {@link BuildWithDetails}
* @throws DiggerClientException
* @throws IOException
*/
public BuildWithDetails cancelBuild(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException, IOException {
Build build = this.getBuild(jenkins, jobName, buildNumber);
build.Stop();
BuildWithDetails buildWithDetails = build.details();
return buildWithDetails;
}
/**
* Start streaming the logs of the given build.
* See {@link DiggerClient#streamLogs(String, int, LogStreamingOptions)}
*/
public void streamBuildLogs(JenkinsServer jenkins, String jobName, int buildNumber, LogStreamingOptions options) throws DiggerClientException, IOException, InterruptedException {
BuildWithDetails buildDetails = this.getBuildDetails(jenkins, jobName, buildNumber);
buildDetails.streamConsoleOutput(options.getStreamListener(), options.getPollingInterval(), options.getPollingTimeout());
}
}