Skip to content

Commit 6c2d628

Browse files
committed
Functionality to trigger a build
1 parent dbfb3a7 commit 6c2d628

File tree

4 files changed

+205
-3
lines changed

4 files changed

+205
-3
lines changed

pom.xml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,23 @@
2121
<dependency>
2222
<groupId>com.offbytwo.jenkins</groupId>
2323
<artifactId>jenkins-client</artifactId>
24-
<version>0.3.6</version>
24+
<version>0.3.7-SNAPSHOT</version>
2525
</dependency>
2626
<dependency>
2727
<groupId>org.jtwig</groupId>
2828
<artifactId>jtwig-core</artifactId>
2929
<version>5.65</version>
3030
</dependency>
31+
<dependency>
32+
<groupId>org.slf4j</groupId>
33+
<artifactId>slf4j-api</artifactId>
34+
<version>1.7.21</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.slf4j</groupId>
38+
<artifactId>slf4j-log4j12</artifactId>
39+
<version>1.7.21</version>
40+
</dependency>
3141
</dependencies>
3242
<repositories>
3343
<repository>
@@ -48,4 +58,4 @@
4858
</plugin>
4959
</plugins>
5060
</build>
51-
</project>
61+
</project>

src/main/java/com/redhat/digkins/DiggerClient.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
import com.offbytwo.jenkins.JenkinsServer;
44
import com.redhat.digkins.services.CreateJobService;
5-
import com.redhat.digkins.util.JenkinsAuth;
5+
import com.redhat.digkins.services.TriggerBuildService;
66
import com.redhat.digkins.util.DiggerClientException;
7+
import com.redhat.digkins.util.JenkinsAuth;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
710

11+
import java.io.IOException;
812
import java.net.URI;
913
import java.net.URISyntaxException;
1014

@@ -13,6 +17,10 @@
1317
*/
1418
public class DiggerClient {
1519

20+
private static final Logger LOG = LoggerFactory.getLogger(DiggerClient.class);
21+
22+
public static final long DEFAULT_BUILD_TIMEOUT = 60 * 1000;
23+
1624
private final JenkinsServer jenkins;
1725

1826
public DiggerClient(JenkinsAuth auth) throws URISyntaxException {
@@ -54,4 +62,50 @@ public void createJob(String name, String gitRepo, String gitBranch) throws Digg
5462
}
5563
}
5664

65+
/**
66+
* Triggers a build for the given job and waits until it leaves the queue and actually starts.
67+
* <p>
68+
* Jenkins puts the build requests in a queue and once there is a slave available, it starts building
69+
* it and a build number is assigned to the build.
70+
* <p>
71+
* This method will block until there is a build number, or the given timeout period is passed. If the build is still in the queue
72+
* after the given timeout period, -1 is returned as the build number.
73+
* <p>
74+
* Please note that timeout period is never meant to be very precise. It has the resolution of {@link TriggerBuildService#POLL_PERIOD} because
75+
* timeout is checked before every pull.
76+
* <p>
77+
* Similarly, -1 is returned if the build is stuck or cancelled on Jenkins side.
78+
*
79+
* @param jobName name of the job to trigger the build
80+
* @param timeout how many milliseconds should this call block before returning -1. Should be larger than {@link TriggerBuildService#FIRST_CHECK_DELAY}
81+
* @return the build number. -1 if build is cancelled, stuck or in queue more than the given timeout period
82+
* @throws DiggerClientException if connection problems occur during connecting to Jenkins
83+
*/
84+
public long build(String jobName, long timeout) throws DiggerClientException {
85+
final TriggerBuildService triggerBuildService = new TriggerBuildService(jenkins);
86+
try {
87+
return triggerBuildService.build(jobName, timeout);
88+
} catch (IOException e) {
89+
LOG.debug("Exception while connecting to Jenkins", e);
90+
throw new DiggerClientException(e);
91+
} catch (InterruptedException e) {
92+
LOG.debug("Exception while sleeping between checks", e);
93+
throw new DiggerClientException(e);
94+
}
95+
}
96+
97+
/**
98+
* Triggers a build for the given job and waits until it leaves the queue and actually starts.
99+
* <p>
100+
* Calls {@link #build(String, long)} with a default timeout of {@link #DEFAULT_BUILD_TIMEOUT}.
101+
*
102+
* @param jobName name of the job
103+
* @return the build number
104+
* @throws DiggerClientException if connection problems occur during connecting to Jenkins
105+
* @see #build(String, long)
106+
*/
107+
public long build(String jobName) throws DiggerClientException {
108+
return this.build(jobName, DEFAULT_BUILD_TIMEOUT);
109+
}
110+
57111
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.redhat.digkins.services;
2+
3+
import com.offbytwo.jenkins.JenkinsServer;
4+
import com.offbytwo.jenkins.model.Executable;
5+
import com.offbytwo.jenkins.model.JobWithDetails;
6+
import com.offbytwo.jenkins.model.QueueItem;
7+
import com.offbytwo.jenkins.model.QueueReference;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import java.io.IOException;
12+
13+
/**
14+
* Provides functionality to trigger a build.
15+
**/
16+
public class TriggerBuildService {
17+
18+
private static final Logger LOG = LoggerFactory.getLogger(TriggerBuildService.class);
19+
20+
/**
21+
* How long should we wait before we start checking the queue item status.
22+
*/
23+
public static final long FIRST_CHECK_DELAY = 5 * 1000L;
24+
25+
/**
26+
* How long should we wait before checking the queue item status for next time.
27+
*/
28+
public static final long POLL_PERIOD = 2 * 1000L;
29+
30+
31+
private JenkinsServer jenkins;
32+
33+
/**
34+
* @param jenkins jenkins api instance
35+
*/
36+
public TriggerBuildService(JenkinsServer jenkins) {
37+
this.jenkins = jenkins;
38+
}
39+
40+
/**
41+
* See the documentation in {@link com.redhat.digkins.DiggerClient#build(String, long)}
42+
*
43+
* @param jobName name of the job
44+
* @param timeout timeout
45+
* @return the build number
46+
* @throws IOException if connection problems occur during connecting to Jenkins
47+
* @throws InterruptedException if a problem occurs during sleeping between checks
48+
* @see com.redhat.digkins.DiggerClient#build(String, long)
49+
*/
50+
public long build(String jobName, long timeout) throws IOException, InterruptedException {
51+
final long timeoutTime = System.currentTimeMillis() + timeout;
52+
53+
LOG.debug("Going to build job with name: {}", jobName);
54+
LOG.debug("Going to timeout in {} msecs if build didn't start executing", timeout);
55+
56+
JobWithDetails job = jenkins.getJob(jobName);
57+
if (job == null) {
58+
throw new IllegalArgumentException("Unable to find job for name '" + jobName + "'");
59+
}
60+
61+
final QueueReference queueReference = job.build();
62+
if (queueReference == null) {
63+
// this is probably an implementation problem we have here
64+
throw new IllegalStateException("Queue reference cannot be null!");
65+
}
66+
LOG.debug("Build triggered; queue item reference: {}", queueReference.getQueueItemUrlPart());
67+
68+
// wait for N seconds, then fetch the queue item.
69+
// do it until we have an executable.
70+
// we would have an executable when the build leaves queue and starts building.
71+
72+
LOG.debug("Going to sleep {} msecs", FIRST_CHECK_DELAY);
73+
Thread.sleep(FIRST_CHECK_DELAY);
74+
75+
QueueItem queueItem;
76+
while (true) {
77+
queueItem = jenkins.getQueueItem(queueReference);
78+
LOG.debug("Queue item : {}", queueItem);
79+
80+
if (queueItem == null) {
81+
// this is probably an implementation problem we have here
82+
throw new IllegalStateException("Queue item cannot be null!");
83+
}
84+
85+
LOG.debug("Build item cancelled:{}, blocked:{}, buildable:{}, stuck:{}", queueItem.isCancelled(), queueItem.isBlocked(), queueItem.isBuildable(), queueItem.isStuck());
86+
87+
if (queueItem.isCancelled()) {
88+
LOG.debug("Queue item is cancelled. Returning -1");
89+
return -1;
90+
} else if (queueItem.isStuck()) {
91+
LOG.debug("Queue item is stuck. Returning -1");
92+
return -1;
93+
}
94+
95+
// do not return -1 if blocked.
96+
// we will wait until it is unblocked.
97+
98+
final Executable executable = queueItem.getExecutable();
99+
100+
if (executable != null) {
101+
LOG.debug("Build has an executable. Returning build number: {}", executable.getNumber());
102+
return executable.getNumber();
103+
} else {
104+
LOG.debug("Build did not start executing yet.");
105+
if (timeoutTime < System.currentTimeMillis()) {
106+
LOG.debug("Timeout period has not exceeded yet. Sleeping for {} msecs", POLL_PERIOD);
107+
Thread.sleep(POLL_PERIOD);
108+
} else {
109+
LOG.debug("Timeout period has exceeded. Returning -1.");
110+
return -1;
111+
}
112+
}
113+
114+
}
115+
}
116+
}

src/main/resources/log4j.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
3+
<log4j:configuration debug="false"
4+
xmlns:log4j='http://jakarta.apache.org/log4j/'>
5+
6+
<appender name="console" class="org.apache.log4j.ConsoleAppender">
7+
<layout class="org.apache.log4j.PatternLayout">
8+
<param name="ConversionPattern"
9+
value="%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] %c{1}.%M(%L) - %m%n"/>
10+
</layout>
11+
</appender>
12+
13+
<logger name="com.redhat.digkins">
14+
<level value="DEBUG"/>
15+
</logger>
16+
17+
<root>
18+
<level value="INFO" />
19+
<appender-ref ref="console" />
20+
</root>
21+
22+
</log4j:configuration>

0 commit comments

Comments
 (0)