Skip to content

Commit

Permalink
Adding configurable webhook endpoint for exposing jenkins commands wi…
Browse files Browse the repository at this point in the history
…th a Slack outgoing-webhook
  • Loading branch information
dpires committed Jan 27, 2016
1 parent 61f2cf7 commit a4934d8
Show file tree
Hide file tree
Showing 19 changed files with 1,033 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
101 changes: 101 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,101 @@
package jenkins.plugins.slack.webhook;


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

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

import java.lang.reflect.Method;

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

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




public class CommandRouter<T> {

public CommandRouter() { }

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

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

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

return this;
}

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

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

T message = null;

for (Route 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 = (T)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 {
public Pattern regex;
public String command;
public String commandDescription;
public RouterCommand routerCommand;

public Route(String regex,
String command,
String commandDescription,
RouterCommand 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";
}
}
}

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 a4934d8

Please sign in to comment.