Skip to content

Commit b72c859

Browse files
committed
basic sqs build trigger functionality
1 parent a857cfd commit b72c859

File tree

10 files changed

+626
-0
lines changed

10 files changed

+626
-0
lines changed

.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
target/
2+
.classpath
3+
.project
4+
.settings/
5+
*.iml
6+
*.ipr
7+
*.iws
8+
.DS_Store
9+
.idea/
10+
/.metadata/
11+
/runtime/
12+
work/

pom.xml

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>org.jenkins-ci.plugins</groupId>
8+
<artifactId>plugin</artifactId>
9+
<version>1.451</version>
10+
</parent>
11+
12+
<groupId>com.base2services.jenkins</groupId>
13+
<artifactId>github-sqs-plugin</artifactId>
14+
<version>1.0-SNAPSHOT</version>
15+
<packaging>hpi</packaging>
16+
<name>GitHub SQS Build Trigger Plugin</name>
17+
<url>http://wiki.jenkins-ci.org/display/JENKINS/GitHub+SQS+Plugin</url>
18+
19+
<scm>
20+
<connection>scm:git:git://github.com/jenkinsci/github-sqs-plugin.git</connection>
21+
<developerConnection>scm:git:[email protected]:jenkinsci/github-sqs-plugin.git</developerConnection>
22+
<url>https://github.com/jenkinsci/github-sqs-plugin</url>
23+
</scm>
24+
25+
<developers>
26+
<developer>
27+
<id>aaronwalker</id>
28+
<name>Aaron Walker</name>
29+
<url>http://aaronwalker.me</url>
30+
<timezone>+10:00</timezone>
31+
</developer>
32+
</developers>
33+
34+
<dependencies>
35+
<dependency>
36+
<groupId>com.coravy.hudson.plugins.github</groupId>
37+
<artifactId>github</artifactId>
38+
<version>1.2-SNAPSHOT</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.jenkins-ci.plugins</groupId>
42+
<artifactId>github-api</artifactId>
43+
<version>1.16</version>
44+
<exclusions>
45+
<exclusion>
46+
<groupId>org.jvnet.hudson</groupId>
47+
<artifactId>htmlunit</artifactId>
48+
</exclusion>
49+
</exclusions>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.jenkins-ci.plugins</groupId>
53+
<artifactId>multiple-scms</artifactId>
54+
<version>0.2</version>
55+
<optional>true</optional>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.jenkins-ci</groupId>
59+
<artifactId>htmlunit</artifactId>
60+
<version>2.6-jenkins-6</version>
61+
</dependency>
62+
<dependency>
63+
<groupId>com.amazonaws</groupId>
64+
<artifactId>aws-java-sdk</artifactId>
65+
<version>1.3.3</version>
66+
</dependency>
67+
</dependencies>
68+
69+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package com.base2services.jenkins;
2+
3+
import com.cloudbees.jenkins.GitHubPushCause;
4+
import com.cloudbees.jenkins.GitHubRepositoryName;
5+
import com.cloudbees.jenkins.GitHubTrigger;
6+
import hudson.Extension;
7+
import hudson.Util;
8+
import hudson.model.AbstractProject;
9+
import hudson.model.Hudson;
10+
import hudson.model.Item;
11+
import hudson.plugins.git.GitSCM;
12+
import hudson.scm.SCM;
13+
import hudson.triggers.Trigger;
14+
import hudson.triggers.TriggerDescriptor;
15+
import hudson.util.SequentialExecutionQueue;
16+
import hudson.util.StreamTaskListener;
17+
import net.sf.json.JSONObject;
18+
import org.eclipse.jgit.transport.RemoteConfig;
19+
import org.eclipse.jgit.transport.URIish;
20+
import org.jenkinsci.plugins.multiplescms.MultiSCM;
21+
import org.kohsuke.stapler.DataBoundConstructor;
22+
import org.kohsuke.stapler.StaplerRequest;
23+
24+
import java.io.File;
25+
import java.io.IOException;
26+
import java.io.PrintStream;
27+
import java.text.DateFormat;
28+
import java.util.ArrayList;
29+
import java.util.Date;
30+
import java.util.HashSet;
31+
import java.util.List;
32+
import java.util.Set;
33+
import java.util.concurrent.Executors;
34+
import java.util.logging.Level;
35+
import java.util.logging.Logger;
36+
37+
/**
38+
* Triggers a build when we receive a message from SQS.
39+
*
40+
* @author aaronwalker
41+
*/
42+
public class SqsBuildTrigger extends Trigger<AbstractProject> implements GitHubTrigger, Runnable {
43+
44+
private static final Logger LOGGER = Logger.getLogger(SqsBuildTrigger.class.getName());
45+
46+
@DataBoundConstructor
47+
public SqsBuildTrigger() {
48+
}
49+
50+
/**
51+
* Called when a POST is made.
52+
*/
53+
public void onPost() {
54+
getDescriptor().queue.execute(this);
55+
}
56+
57+
/**
58+
* Returns the file that records the last/current polling activity.
59+
*/
60+
public File getLogFile() {
61+
return new File(job.getRootDir(),"sqs-polling.log");
62+
}
63+
64+
public void run() {
65+
try {
66+
StreamTaskListener listener = new StreamTaskListener(getLogFile());
67+
68+
try {
69+
PrintStream logger = listener.getLogger();
70+
long start = System.currentTimeMillis();
71+
logger.println("Started on "+ DateFormat.getDateTimeInstance().format(new Date()));
72+
boolean result = job.poll(listener).hasChanges();
73+
logger.println("Done. Took "+ Util.getTimeSpanString(System.currentTimeMillis() - start));
74+
if(result) {
75+
logger.println("Changes found");
76+
job.scheduleBuild(new GitHubPushCause());
77+
} else {
78+
logger.println("No changes");
79+
}
80+
} finally {
81+
listener.close();
82+
}
83+
} catch (IOException e) {
84+
LOGGER.log(Level.SEVERE,"Failed to record SCM polling",e);
85+
}
86+
}
87+
88+
@Override
89+
public DescriptorImpl getDescriptor() {
90+
return (DescriptorImpl)super.getDescriptor();
91+
}
92+
93+
/**
94+
* Does this project read from a repository of the given user name and the
95+
* given repository name?
96+
*/
97+
public Set<GitHubRepositoryName> getGitHubRepositories() {
98+
Set<GitHubRepositoryName> r = new HashSet<GitHubRepositoryName>();
99+
if (Hudson.getInstance().getPlugin("multiple-scms") != null
100+
&& job.getScm() instanceof MultiSCM) {
101+
MultiSCM multiSCM = (MultiSCM) job.getScm();
102+
List<SCM> scmList = multiSCM.getConfiguredSCMs();
103+
for (SCM scm : scmList) {
104+
addRepositories(r, scm);
105+
}
106+
} else {
107+
addRepositories(r, job.getScm());
108+
}
109+
return r;
110+
}
111+
112+
/**
113+
* @since 1.1
114+
*/
115+
protected void addRepositories(Set<GitHubRepositoryName> r, SCM scm) {
116+
if (scm instanceof GitSCM) {
117+
GitSCM git = (GitSCM) scm;
118+
for (RemoteConfig rc : git.getRepositories()) {
119+
for (URIish uri : rc.getURIs()) {
120+
String url = uri.toString();
121+
GitHubRepositoryName repo = GitHubRepositoryName.create(url);
122+
if (repo != null) {
123+
r.add(repo);
124+
}
125+
}
126+
}
127+
}
128+
}
129+
130+
@Override
131+
public void start(AbstractProject project, boolean newInstance) {
132+
super.start(project, newInstance);
133+
if(newInstance && getDescriptor().isManageHook()) {
134+
//TODO: register sqs github web hook
135+
}
136+
}
137+
138+
@Override
139+
public void stop() {
140+
super.stop();
141+
}
142+
143+
@Extension
144+
public static class DescriptorImpl extends TriggerDescriptor {
145+
private transient final SequentialExecutionQueue queue = new SequentialExecutionQueue(Executors.newSingleThreadExecutor());
146+
147+
private boolean manageHook = true;
148+
private volatile List<SqsProfile> sqsProfiles = new ArrayList<SqsProfile>();
149+
150+
public DescriptorImpl() {
151+
load();
152+
}
153+
154+
@Override
155+
public boolean isApplicable(Item item) {
156+
return item instanceof AbstractProject;
157+
}
158+
159+
@Override
160+
public String getDisplayName() {
161+
return "Build when a message is published to an SQS Queue";
162+
}
163+
164+
public boolean isManageHook() {
165+
return manageHook;
166+
}
167+
168+
@Override
169+
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
170+
JSONObject sqs = json.getJSONObject("sqsProfiles");
171+
//sqsEnabled = "enabled".equals(sqs.getString("value"));
172+
sqsProfiles = req.bindJSONToList(SqsProfile.class,sqs);
173+
save();
174+
return true;
175+
}
176+
177+
public static DescriptorImpl get() {
178+
return Trigger.all().get(DescriptorImpl.class);
179+
}
180+
181+
public List<SqsProfile> getSqsProfiles() {
182+
return sqsProfiles;
183+
}
184+
185+
186+
}
187+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.base2services.jenkins;
2+
3+
import com.amazonaws.auth.AWSCredentials;
4+
import com.amazonaws.services.sqs.AmazonSQS;
5+
import com.amazonaws.services.sqs.AmazonSQSClient;
6+
import com.amazonaws.services.sqs.model.CreateQueueRequest;
7+
import com.amazonaws.services.sqs.model.ListQueuesResult;
8+
import hudson.Extension;
9+
import hudson.model.AbstractDescribableImpl;
10+
import hudson.model.Descriptor;
11+
import hudson.util.FormValidation;
12+
import hudson.util.Secret;
13+
import org.kohsuke.stapler.DataBoundConstructor;
14+
import org.kohsuke.stapler.QueryParameter;
15+
16+
import java.io.IOException;
17+
18+
/**
19+
* SqsProfile to access SQS
20+
*
21+
* @author aaronwalker
22+
*/
23+
public class SqsProfile extends AbstractDescribableImpl<SqsProfile> implements AWSCredentials{
24+
25+
public final String awsAccessKeyId;
26+
public final Secret awsSecretAccessKey;
27+
public final String sqsQueue;
28+
29+
private AmazonSQS client;
30+
31+
32+
@DataBoundConstructor
33+
public SqsProfile(String awsAccessKeyId, Secret awsSecretAccessKey, String sqsQueue) {
34+
this.awsAccessKeyId = awsAccessKeyId;
35+
this.awsSecretAccessKey = awsSecretAccessKey;
36+
this.sqsQueue = sqsQueue;
37+
this.client = null;
38+
}
39+
40+
public String getAWSAccessKeyId() {
41+
return awsAccessKeyId;
42+
}
43+
44+
public String getAWSSecretKey() {
45+
return awsSecretAccessKey.getPlainText();
46+
}
47+
48+
public AmazonSQS getSQSClient() {
49+
if(client == null) {
50+
client = new AmazonSQSClient(this);
51+
}
52+
return client;
53+
}
54+
55+
public String getSqsQueue() {
56+
return sqsQueue;
57+
}
58+
59+
public String getQueueUrl() {
60+
return createQueue(getSQSClient(),sqsQueue);
61+
}
62+
63+
/**
64+
* Create a Amazon SQS queue if it does already exists
65+
* @param sqs Amazon SQS client
66+
* @param queue the name of the queue
67+
* @return the queue url
68+
*/
69+
private String createQueue(AmazonSQS sqs, String queue) {
70+
for(String url : sqs.listQueues().getQueueUrls()) {
71+
if(url.endsWith("/" + queue)) {
72+
return url;
73+
}
74+
}
75+
//The queue wasn't found so we will create it
76+
return sqs.createQueue(new CreateQueueRequest(queue)).getQueueUrl();
77+
}
78+
79+
80+
@Extension
81+
public static class DescriptorImpl extends Descriptor<SqsProfile> {
82+
@Override
83+
public String getDisplayName() {
84+
return ""; // unused
85+
}
86+
87+
public FormValidation doValidate(@QueryParameter String awsAccessKeyId, @QueryParameter Secret awsSecretAccessKey, @QueryParameter String sqsQueue) throws IOException {
88+
boolean valid = false;
89+
try {
90+
AmazonSQS sqs = new AmazonSQSClient(new SqsProfile(awsAccessKeyId,awsSecretAccessKey,sqsQueue));
91+
ListQueuesResult result = sqs.listQueues();
92+
if(result != null) {
93+
return FormValidation.ok("Verified");
94+
} else {
95+
return FormValidation.error("Failed to validate the account");
96+
}
97+
} catch (RuntimeException ex) {
98+
return FormValidation.error("Failed to validate the account");
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)