Skip to content

Commit

Permalink
Merge pull request #160 from dpires/master
Browse files Browse the repository at this point in the history
Adding configurable webhook endpoint for exposing jenkins commands with a Slack outgoing-webhook
  • Loading branch information
kmadel committed Mar 7, 2016
2 parents e6d06c2 + c5a45e0 commit 734d4bc
Show file tree
Hide file tree
Showing 19 changed files with 1,029 additions and 0 deletions.
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
</scm>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.4</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
Expand All @@ -54,6 +59,12 @@
<artifactId>json</artifactId>
<version>20131018</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
97 changes: 97 additions & 0 deletions src/main/java/jenkins/plugins/slack/webhook/CommandRouter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package jenkins.plugins.slack.webhook;


import java.util.List;
import java.util.ArrayList;

import java.util.regex.Pattern;
import java.util.regex.Matcher;

import jenkins.plugins.slack.webhook.exception.CommandRouterException;
import jenkins.plugins.slack.webhook.exception.RouteNotFoundException;




public class CommandRouter<T> {

public CommandRouter() { }

public List<Route<T>> routes = new ArrayList<Route<T>>();

public CommandRouter<T> addRoute(String regex,
String command,
String commandDescription,
RouterCommand<T> routerCommand) {

this.routes.add(new CommandRouter.Route<T>(regex,
command,
commandDescription,
routerCommand));

return this;
}

public List<Route<T>> getRoutes() {
return this.routes;
}

public T route(String command) throws CommandRouterException,
RouteNotFoundException {

T message = null;

for (Route<T> pa : routes) {

Matcher matcher = pa.regex.matcher(command);

boolean matches = matcher.matches();

if (matches) {

String[] parametersArray = null;

if (matcher.groupCount() == 0) {
parametersArray = new String[] { command };
} else {
parametersArray = new String[matcher.groupCount()];

for (int i = 1; i <= matcher.groupCount(); i++) {
parametersArray[i-1] = matcher.group(i);
}
}

try {
message = pa.routerCommand.execute(parametersArray);
} catch (Exception ex) {
throw new CommandRouterException(ex.getMessage());
}

if (message == null)
throw new RouteNotFoundException("No route found for given command", command);

return message;
}
}

return message;
}

public static class Route<T> {
public Pattern regex;
public String command;
public String commandDescription;
public RouterCommand<T> routerCommand;

public Route(String regex,
String command,
String commandDescription,
RouterCommand<T> routerCommand) {

this.regex = Pattern.compile(regex);
this.routerCommand = routerCommand;
this.command = command;
this.commandDescription = commandDescription;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package jenkins.plugins.slack.webhook;


import jenkins.model.Jenkins;

import hudson.model.Build;
import hudson.model.Result;
import hudson.model.Project;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;

import hudson.security.ACL;

import java.io.IOException;

import java.util.List;
import java.util.ArrayList;

import org.kohsuke.stapler.interceptor.RequirePOST;

import jenkins.plugins.slack.webhook.model.SlackPostData;
import jenkins.plugins.slack.webhook.model.SlackTextMessage;
import jenkins.plugins.slack.webhook.model.SlackWebhookCause;

import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;




public class GetProjectLogCommand extends SlackRouterCommand implements RouterCommand<SlackTextMessage> {

public GetProjectLogCommand(SlackPostData data) {
super(data);
}

@Override
public SlackTextMessage execute(String... args) {
String projectName = args[0];
String buildNumber = args[1];

SecurityContext ctx = ACL.impersonate(ACL.SYSTEM);

List<String> log = new ArrayList<String>();

try {
Project project =
Jenkins.getInstance().getItemByFullName(projectName, Project.class);

if (project == null)
return new SlackTextMessage("Could not find project ("+projectName+")\n");

AbstractBuild build =
project.getBuildByNumber(Integer.parseInt(buildNumber));

if (build == null)
return new SlackTextMessage("Could not find build #"+buildNumber+" for ("+projectName+")\n");

log = build.getLog(25);

} catch (IOException ex) {
return new SlackTextMessage("Error occured returning log: "+ex.getMessage());
} finally {
SecurityContextHolder.setContext(ctx);
}

String response = "*"+projectName+"* *#"+buildNumber+"*\n";
response += "```";
for (String line : log) {
response += line + "\n";
}
response += "```";

return new SlackTextMessage(response);
}
}
67 changes: 67 additions & 0 deletions src/main/java/jenkins/plugins/slack/webhook/GlobalConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package jenkins.plugins.slack.webhook;


import hudson.Extension;

import net.sf.json.JSONObject;

import jenkins.model.GlobalConfiguration;

import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

import hudson.util.FormValidation;

import hudson.model.Descriptor.FormException;





@Extension
public class GlobalConfig extends GlobalConfiguration {

private String slackOutgoingWebhookToken;
private String slackOutgoingWebhookURL;

public GlobalConfig() {
load();
}

public String getSlackOutgoingWebhookToken() {
return slackOutgoingWebhookToken;
}

public void setSlackOutgoingWebhookToken(String slackOutgoingWebhookToken) {
this.slackOutgoingWebhookToken = slackOutgoingWebhookToken;
}

public FormValidation doCheckSlackOutgoingWebhookToken(@QueryParameter String value) {
if (value == null || value.trim().isEmpty())
return FormValidation.warning("Please set a Slack outgoing webhook token");

return FormValidation.ok();
}

public String getSlackOutgoingWebhookURL() {
return slackOutgoingWebhookURL;
}

public void setSlackOutgoingWebhookURL(String slackOutgoingWebhookURL) {
this.slackOutgoingWebhookURL = slackOutgoingWebhookURL;
}

public FormValidation doCheckSlackOutgoingWebhookURL(@QueryParameter String value) {
if (value == null || value.trim().isEmpty())
return FormValidation.warning("Please set a url endpoint");

return FormValidation.ok();
}

@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
req.bindJSON(this, json);
save();
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package jenkins.plugins.slack.webhook;


import jenkins.model.Jenkins;

import hudson.model.Build;
import hudson.model.Result;
import hudson.model.Project;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;

import hudson.security.ACL;

import java.io.IOException;

import java.util.List;
import java.util.ArrayList;

import org.kohsuke.stapler.interceptor.RequirePOST;

import jenkins.plugins.slack.webhook.model.SlackPostData;
import jenkins.plugins.slack.webhook.model.SlackTextMessage;
import jenkins.plugins.slack.webhook.model.SlackWebhookCause;

import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;




public class ListProjectsCommand extends SlackRouterCommand implements RouterCommand<SlackTextMessage> {

public ListProjectsCommand(SlackPostData data) {
super(data);
}

@Override
public SlackTextMessage execute(String... args) {

SecurityContext ctx = ACL.impersonate(ACL.SYSTEM);

String response = "*Projects:*\n";

List<AbstractProject> jobs =
Jenkins.getInstance().getAllItems(AbstractProject.class);

SecurityContextHolder.setContext(ctx);

for (AbstractProject job : jobs) {
if (job.isBuildable()) {
AbstractBuild lastBuild = job.getLastBuild();
String buildNumber = "TBD";
String status = "TBD";
if (lastBuild != null) {

buildNumber = Integer.toString(lastBuild.getNumber());

if (lastBuild.isBuilding()) {
status = "BUILDING";
}

Result result = lastBuild.getResult();

if (result != null) {
status = result.toString();
}
}

if (jobs.size() <= 10) {
response += ">*"+job.getDisplayName() + "*\n>*Last Build:* #"+buildNumber+"\n>*Status:* "+status;
response += "\n\n\n";
} else {
response += ">*"+job.getDisplayName() + "* :: *Last Build:* #"+buildNumber+" :: *Status:* "+status;
response += "\n\n";
}
}
}

if (jobs == null || jobs.size() == 0)
response += ">_No projects found_";

return new SlackTextMessage(response);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package jenkins.plugins.slack.webhook;




public interface RouterCommand<T> {
public T execute(String... args);
}
Loading

0 comments on commit 734d4bc

Please sign in to comment.