-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding configurable webhook endpoint for exposing jenkins commands with a Slack outgoing-webhook #160
Conversation
Thanks for this contribution. I'm going to open this up for code review from others. |
Thank you for this pull request! Please check this document for how the Jenkins project handles pull requests. |
@@ -55,6 +60,11 @@ | |||
<version>20131018</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.hamcrest</groupId> | |||
<artifactId>hamcrest-all</artifactId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this dependency can be scoped to test.
My experience is that the build time increases from about 20-30 seconds to 1:20-1:30 seconds with these changes, and most of it is spent running tests. It's not a crucial issue, but it is likely to become a frustration for developers, particularly if you like following a TDD approach. Is there any specific reason for the extra time, and can it be reduced (without reducing test coverage)? I thought it was possibly related to having to start up a Jenkins instance, but I also have that happening in another branch I'm working on, and it builds in around 30 seconds. |
I can see the test time being an issue. The reasoning is that the i'm waiting for projects to be created and builds to finish with a sleep. I'll look into using a Future callback for those tests, although the outcome is probably going to be the same time. |
I'm not that familiar with the outgoing webhooks integration. I assume that these URLs on the Jenkins server would need to be accessible from the slack servers for this to work, right? Just trying to get my head around the general idea and how it would be used. |
Yes, Slack sends a POST to an endpoint which is why this /webhook endpoint is unprotected as Slack controls the body of the request so you can't add additional authorization parameters etc. I have usage description in my original plugin before I created this PR: |
globalConfig = GlobalConfiguration.all().get(GlobalConfig.class); | ||
} | ||
|
||
@Override |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pom file specifies that the compiler should target JDK 1.5, which actually means that these @OVERRIDES shouldn't be allowed because they're implementations of an interface rather than overrides of a parent class, which was only allowed from 1.6 on. I definitely prefer to have the annotation, and it works for building because our more modern compilers aren't strictly enforcing it, but my IDE doesn't like them.
However, why are we stuck using 1.5? I followed the path of where it comes from:
org.jenkins-ci.plugins.slack pom.xml
--> org.jenkins-ci.plugins.plugin 1.567
-> org.jenkins-ci.jenkins 1.33
org.jenkins-ci.jenkins 1.33 was last updated in 2013, so maybe we can move to the latest org.jenkins-ci.plugins.plugin. Even that will only give us 1.6, which isn't a supported version of Java anymore.
Not sure this has anything to do with this review, but @samrocketman do you know if there are any plans to move Jenkins in general to a newer version of Java? Otherwise, maybe the next thing I can work on is updating these versions to the latest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Jenkins project seems to want Java 1.7 and later. ref https://jenkins-ci.org/blog/2015/04/06/good-bye-java6/
Here's a link to another plugin I support updating the compatibility.
Either an alternative way to implement this, or an idea for a future feature: send messages over Amazon SQS instead of posting over http directly to the Jenkins server. https://slack.com/apps/A0F827G56-amazon-sqs I know my company would be more interested in this route. |
You can also use a Slack bot like Hubot or Lita to implement something similar to this PR, however this is the simplest solution as it requires zero dependencies/integrations. |
I've modified the endpoint to be configurable. Without setting the endpoint Jenkins will return a standard 404 like any other unknown endpoints. |
|
||
@Override | ||
public String getUrlName() { | ||
return "/"+globalConfig.getSlackOutgoingWebhookURL(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better. Thanks for the changes. Looking at jenkins.model.Jenkins.getDynamic(String) which calls this, it would be better to return null when the url from the configuration is null/empty.
public Object getDynamic(String token) {
for (Action a : getActions()) {
String url = a.getUrlName();
if (url==null) continue;
if (url.equals(token) || url.equals('/' + token))
return a;
}
// etc.
return null;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good find. Done.
@@ -0,0 +1,5 @@ | |||
<div> | |||
This is the webhook endpoint to send requests to. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to point out here that leaving it blank will disable the functionality, and that the endpoint needs to be publicly accessible from the Slack servers. I think that will alleviate some concerns, and clarify why it isn't working when someone hasn't opened their firewall etc.
triggerWord+" list projects", "Return a list of buildable projects", | ||
this, | ||
"listProjects") | ||
.addRoute("^"+triggerWord+" run ([a-zA-Z0-9_\\-\\.]+)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commands work well, but do not support projects that have spaces in the name. Not sure if there are other characters that Jenkins allows that are not accepted here.
I've updated the command regex to support all valid/safe characters (and charsets) for a project name and updated the tests. |
Can this be added as a merge candidate? |
I've been through most of the code in this pull request and @dpires has fixed everything I've commented on so far. Will try to get through the rest of it asap, but it's looking good so far. Not sure what it takes to make it a merge candidate. |
@Extension | ||
public class WebhookEndpoint implements UnprotectedRootAction { | ||
|
||
private String slackUser; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed that it looks like WebhookEndpoint is a singleton; there is only ever 1 instantiated per Jenkins instance. If I'm correct, you have a race condition on the slackUser member variable. I think you'll need to pass it around in method calls instead of using a member variable. Maybe you could encapsulate all the required information in an object.
The globalConfig seems fine. I was concerned it would not dynamically pickup configuration changes, but it does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good find, that is a race condition.
return message; | ||
} | ||
|
||
private Method getHandlerMethod(Object handlerInstance, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is not being used after the class was refactored.
This is getting really close to getting a thumbs up from me. There are a few unaddressed comments (none of which are critical) and I'd like to see if I can get the test to run a little faster, but otherwise it's looking good. |
I've added a condensed project list when projects are > 10. Still has all information, just without 3 line breaks. I've tried it with 30+ projects and it's not that bad, still readable. |
a4934d8
to
abacc96
Compare
…th a Slack outgoing-webhook
I think @dpires has addressed all the concerns I raised. Thanks! @samrocketman this pull request looks good to me. The only thing that would be nice to improve is the run time for the unit tests, but it's not terrible and I'm not sure whether it is actually possible to improve it substantially or not. Let me know if I can do anything more to help get this merged. |
Adding configurable webhook endpoint for exposing jenkins commands with a Slack outgoing-webhook
Thanks @kmadel! |
@dpires There appears to be certain Jenkins configurations where your changes cause a NPE:
May be related to #189 |
@dpires @kmadel those of us who upgraded to version 2.0 are experiencing a NPE. See here for details: https://issues.jenkins-ci.org/browse/JENKINS-33556 the solution is either to downgrade to version 1.8.1 or set a for the Outgoing Webhook URL Endpoint setting. We should probably track this on a different issue. Let me know if you want me to create one. |
public String getUrlName() { | ||
String url = globalConfig.getSlackOutgoingWebhookURL(); | ||
if (url == null || url.equals("")) | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it needs to return a default value other wise it will cause a NPE here:
Issue reported here: https://issues.jenkins-ci.org/browse/JENKINS-33556
@dpires originally had a default value for the endpoint, but when I reviewed the code I was concerned that some administrators might not like that this is enabled by default as they might see it as a security risk. The version of Jenkins we tested against specifically skipped endpoints that returned null for the URL, which is why @dpires ended up taking the current approach. Hopefully there is an alternative to conditionally enabling the endpoint, otherwise a quick fix that would maybe be okay is to default it to a randomly generated string. |
The NPE was a really upsetting surprise on our instance. Please remove version 2.0.0 from jenkins plugin directory until you fix it! |
I've changed the url to a random uuid string if the endpoint is not configured. See pr #190 |
@ssbarnea unfortunately I'm not sure I have access to remove it. Jenkins synchronizes the update center every 8 hours roughly. |
The web endpoint can be disabled permanently with a script console script I crafted. |
@dpires I found out about this because, even after the fix, this feature got in my way with its bad UI. TL; DR. That warning sign is spam, stop using it. What's worse, docs are missing enough context that to figure out that I can ignore this whole thing, I needed to figure it out in full by skimming this pull request (spending >= half an hour). (In case you wonder, I'm no newbie). I was not looking to use this feature. I was just reviewing my settings after upgrading the Slack plugin, because the plugin update form told me that the configuration format changed, so I feared I would need to setup the connection from scratch. After upgrading to 2.0.1, my "Manage Jenkins" page has a section "Slack Webhook Settings" which tells me (in yellow, with warning signs) "Please set a Slack outgoing webhook token" and "Please set a url endpoint". I only want to keep using the existing integration with Slack. Do I now need to fill those as well? The GUI suggests yes, but it seems that I should ignore them unless I need some unspecified functionality.
In fairness, lots of things in Jenkins show little care for UX, but this was pretty bad. |
@Blaisorblade these are good points and somebody else did remark that it would work better with a checkbox for enabling the feature (with it disabled by default). I think that would have alleviated your confusion because you would have been able to ignore it more easily. I think I'd be interested in making that improvement (if nobody else does in the mean time) if we can get a few other things moving first. I did want to point out that all this work is voluntary; people spending time to contribute something they feel will be valuable to others. @dpires invested his own time to add this feature and others agreed it could be useful. |
@OwensCode Thanks for your kind answer.
Well, sorry for the tone of my comment. The UX was frustrating, but venting that frustration to developers who didn't realize it is a bad habit, I'm trying to stop.
I agree. Better descriptions would also help, but writing for somebody who knows less can be hard, so I guess I should contribute those. |
I've added an endpoint extension (/webhook/) to allow Slack outgoing-webhook functionality.
There is a new Slack Webhook Settings configuration option to add an outgoing-webhook token.
I have placed everything in a new jenkins.plugins.slack.webhook package.