Adds proxy support
- Adds unauthenticated/authenticated proxy support to the WebhookClient class.
- Adds proxy configuration options via gerrit.config.
Change-Id: I71b5476c825b58d25c660cb3583e31292997195f
diff --git a/README.md b/README.md
index 9e6c441..d828f14 100644
--- a/README.md
+++ b/README.md
@@ -126,9 +126,41 @@
added to a review.
publish-on-wip-ready - boolean(true/false)
Whether a Slack notification should be published when a
- work-in-progress change is marked ready. (defaults to the value for
- publish-on-patch-set-created)
+ work-in-progress change is marked ready (defaults to the value for
+ publish-on-patch-set-created).
publish-on-private-to-public - boolean(true/false)
Whether a Slack notification should be published when a
- private change is changed to public. (defaults to the value for
- publish-on-patch-set-created)
+ private change is changed to public (defaults to the value for
+ publish-on-patch-set-created).
+
+
+Proxy Configuration
+-------------------
+
+If a proxy is needed to connect externally to Slack, a proxy server may
+be configured via Gerrit's main configuration file. The path to this file will
+vary based on where Gerrit was installed. This example assumes that Gerrit was
+installed in _/usr/local/gerrit_.
+
+Edit _/usr/local/gerrit/etc/gerrit.config_ and add the following block.
+
+ [plugin "slack-integration"]
+ proxy-host = <hostname or IP address of the proxy server>
+ proxy-port = <port of the proxy server>
+
+
+Proxy Configuration Options
+---------------------------
+
+The following configuration options are available
+
+ proxy-host - String
+ The host of the proxy server, e.g. my.proxy.host (defaults to null).
+ proxy-port - int
+ The port of the proxy server (defaults to 8080)
+ proxy-username - String
+ The username, if needed, to authenticate to the proxy server
+ (defaults to null).
+ proxy-password - String
+ The password, if needed, for the specified username to authenticate to
+ the proxy server, (defaults to null).
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java b/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java
index 3b9bc5e..05e2768 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java
@@ -96,7 +96,7 @@
if (messageGenerator.shouldPublish()) {
WebhookClient client;
- client = new WebhookClient();
+ client = new WebhookClient(config);
client.publish(messageGenerator.generate(), config.getWebhookUrl());
}
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java b/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java
index df0b088..c609d7d 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java
@@ -17,14 +17,20 @@
package com.cisco.gerrit.plugins.slack.client;
+import com.cisco.gerrit.plugins.slack.config.ProjectConfig;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.Authenticator;
import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
import java.net.MalformedURLException;
+import java.net.PasswordAuthentication;
+import java.net.Proxy;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
+import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +44,17 @@
/** The class logger instance. */
private static final Logger LOGGER = LoggerFactory.getLogger(WebhookClient.class);
+ private ProjectConfig config;
+
+ /**
+ * Creates a new WebhookClient.
+ *
+ * @param config The ProjectConfig instance to use.
+ */
+ public WebhookClient(ProjectConfig config) {
+ this.config = config;
+ }
+
/**
* Publish a message to the provided Slack webhook URL.
*
@@ -94,7 +111,7 @@
DataOutputStream request;
request = new DataOutputStream(connection.getOutputStream());
- request.write(message.getBytes("UTF-8"));
+ request.write(message.getBytes(StandardCharsets.UTF_8));
request.flush();
request.close();
} catch (IOException e) {
@@ -119,7 +136,33 @@
*/
private HttpURLConnection openConnection(String webhookUrl) {
try {
- return (HttpURLConnection) new URL(webhookUrl).openConnection();
+ HttpURLConnection connection;
+ if (StringUtils.isNotBlank(config.getProxyHost())) {
+ LOGGER.info("Connecting via proxy");
+ if (StringUtils.isNotBlank(config.getProxyUsername())) {
+ Authenticator authenticator;
+ authenticator =
+ new Authenticator() {
+ public PasswordAuthentication getPasswordAuthentication() {
+ return (new PasswordAuthentication(
+ config.getProxyUsername(), config.getProxyPassword().toCharArray()));
+ }
+ };
+ Authenticator.setDefault(authenticator);
+ }
+
+ Proxy proxy;
+ proxy =
+ new Proxy(
+ Proxy.Type.HTTP,
+ new InetSocketAddress(config.getProxyHost(), config.getProxyPort()));
+
+ connection = (HttpURLConnection) new URL(webhookUrl).openConnection(proxy);
+ } else {
+ LOGGER.info("Connecting directly");
+ connection = (HttpURLConnection) new URL(webhookUrl).openConnection();
+ }
+ return connection;
} catch (MalformedURLException e) {
throw new RuntimeException("Unable to create webhook URL: " + webhookUrl, e);
} catch (IOException e) {
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java b/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java
index ee0879d..088cc4f 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java
@@ -31,6 +31,7 @@
public class ProjectConfig {
/** The name of the plugin config section to lookup within the gerrit.config file. */
public static final String CONFIG_NAME = "slack-integration";
+
/** The class logger instance. */
private static final Logger LOGGER = LoggerFactory.getLogger(ProjectConfig.class);
@@ -49,6 +50,11 @@
private boolean publishOnWipReady;
private boolean publishOnPrivateToPublic;
+ private String proxyHost;
+ private int proxyPort;
+ private String proxyUsername;
+ private String proxyPassword;
+
/**
* Creates a new instance of the ProjectConfig class for the given project.
*
@@ -131,6 +137,16 @@
configFactory
.getFromProjectConfigWithInheritance(projectNameKey, CONFIG_NAME)
.getBoolean("publish-on-private-to-public", publishOnPatchSetCreated);
+
+ proxyHost = configFactory.getFromGerritConfig(CONFIG_NAME).getString("proxy-host", null);
+
+ proxyPort = configFactory.getFromGerritConfig(CONFIG_NAME).getInt("proxy-port", 8080);
+
+ proxyUsername =
+ configFactory.getFromGerritConfig(CONFIG_NAME).getString("proxy-username", null);
+
+ proxyPassword =
+ configFactory.getFromGerritConfig(CONFIG_NAME).getString("proxy-password", null);
} catch (NoSuchProjectException e) {
LOGGER.warn("The specified project could not be found: " + project);
}
@@ -191,4 +207,20 @@
public boolean shouldPublishOnPrivateToPublic() {
return publishOnPrivateToPublic;
}
+
+ public String getProxyHost() {
+ return proxyHost;
+ }
+
+ public int getProxyPort() {
+ return proxyPort;
+ }
+
+ public String getProxyUsername() {
+ return proxyUsername;
+ }
+
+ public String getProxyPassword() {
+ return proxyPassword;
+ }
}
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java
index b4f8849..8f02fcc 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java
@@ -18,18 +18,64 @@
package com.cisco.gerrit.plugins.slack.client;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.cisco.gerrit.plugins.slack.config.ProjectConfig;
import com.cisco.gerrit.plugins.slack.message.MessageTemplate;
import com.cisco.gerrit.plugins.slack.util.ResourceHelper;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
import java.io.InputStream;
import java.util.Properties;
+import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({Project.NameKey.class})
public class WebhookClientIntegrationTest {
+ private static final String PROJECT_NAME = "test-project";
+
+ private Project.NameKey mockNameKey = mock(Project.NameKey.class);
+ private PluginConfigFactory mockConfigFactory = mock(PluginConfigFactory.class);
+ private PluginConfig mockPluginConfig = mock(PluginConfig.class);
+
+ @Before
+ public void setup() throws Exception {
+ PowerMockito.mockStatic(Project.NameKey.class);
+ when(Project.NameKey.parse(PROJECT_NAME)).thenReturn(mockNameKey);
+ }
+
+ private ProjectConfig getConfig() throws Exception {
+ Project.NameKey projectNameKey;
+ projectNameKey = Project.NameKey.parse(PROJECT_NAME);
+
+ // Setup mocks
+ when(mockConfigFactory.getFromProjectConfigWithInheritance(
+ projectNameKey, ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
+ // Internal proxy integration test config
+ // when(mockPluginConfig.getString("proxy-host", null)).thenReturn("127.0.0.1");
+ // when(mockPluginConfig.getInt("proxy-port", 8080)).thenReturn(8080);
+ // when(mockPluginConfig.getString("proxy-username", null)).thenReturn("user");
+ // when(mockPluginConfig.getString("proxy-password", null)).thenReturn("password");
+
+ return new ProjectConfig(mockConfigFactory, PROJECT_NAME);
+ }
+
@Test
public void canPublishMessage() throws Exception {
WebhookClient client;
- client = new WebhookClient();
+ client = new WebhookClient(getConfig());
InputStream testProperties;
testProperties = ResourceHelper.loadNamedResourceAsStream("test.properties");
@@ -61,7 +107,7 @@
@Test
public void canPublishMessageWithLongMessage() throws Exception {
WebhookClient client;
- client = new WebhookClient();
+ client = new WebhookClient(getConfig());
InputStream testProperties;
testProperties = ResourceHelper.loadNamedResourceAsStream("test.properties");
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java
index 4c3c57e..efd5c6f 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java
@@ -61,6 +61,9 @@
projectNameKey, ProjectConfig.CONFIG_NAME))
.thenReturn(mockPluginConfig);
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
when(mockPluginConfig.getBoolean("enabled", false)).thenReturn(true);
when(mockPluginConfig.getString("webhookurl", "")).thenReturn("https://webook/");
when(mockPluginConfig.getString("channel", "general")).thenReturn("test-channel");
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java
index 9a793c8..0e8e1a8 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java
@@ -72,6 +72,9 @@
projectNameKey, ProjectConfig.CONFIG_NAME))
.thenReturn(mockPluginConfig);
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
when(mockPluginConfig.getBoolean("enabled", false)).thenReturn(true);
when(mockPluginConfig.getString("webhookurl", "")).thenReturn("https://webook/");
when(mockPluginConfig.getString("channel", "general")).thenReturn("testchannel");
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java
index cc62aa5..026ab08 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java
@@ -77,6 +77,9 @@
projectNameKey, ProjectConfig.CONFIG_NAME))
.thenReturn(mockPluginConfig);
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
when(mockPluginConfig.getBoolean("enabled", false)).thenReturn(true);
when(mockPluginConfig.getString("webhookurl", "")).thenReturn("https://webook/");
when(mockPluginConfig.getString("channel", "general")).thenReturn("testchannel");
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java
index 80e8cf8..1eb7a48 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java
@@ -78,6 +78,9 @@
projectNameKey, ProjectConfig.CONFIG_NAME))
.thenReturn(mockPluginConfig);
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
when(mockPluginConfig.getBoolean("enabled", false)).thenReturn(true);
when(mockPluginConfig.getString("webhookurl", "")).thenReturn("https://webook/");
when(mockPluginConfig.getString("channel", "general")).thenReturn("testchannel");
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/PrivateStateChangedGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/PrivateStateChangedGeneratorTest.java
index 7460dc3..038c061 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/PrivateStateChangedGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/PrivateStateChangedGeneratorTest.java
@@ -72,6 +72,9 @@
projectNameKey, ProjectConfig.CONFIG_NAME))
.thenReturn(mockPluginConfig);
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
when(mockPluginConfig.getBoolean("enabled", false)).thenReturn(true);
when(mockPluginConfig.getString("webhookurl", "")).thenReturn("https://webook/");
when(mockPluginConfig.getString("channel", "general")).thenReturn("testchannel");
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java
index 23720a9..9ba5fe0 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java
@@ -77,6 +77,9 @@
projectNameKey, ProjectConfig.CONFIG_NAME))
.thenReturn(mockPluginConfig);
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
when(mockPluginConfig.getBoolean("enabled", false)).thenReturn(true);
when(mockPluginConfig.getString("webhookurl", "")).thenReturn("https://webook/");
when(mockPluginConfig.getString("channel", "general")).thenReturn("testchannel");
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/WorkInProgressStateChangedGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/WorkInProgressStateChangedGeneratorTest.java
index d9a62c8..056b363 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/WorkInProgressStateChangedGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/WorkInProgressStateChangedGeneratorTest.java
@@ -72,6 +72,9 @@
projectNameKey, ProjectConfig.CONFIG_NAME))
.thenReturn(mockPluginConfig);
+ when(mockConfigFactory.getFromGerritConfig(ProjectConfig.CONFIG_NAME))
+ .thenReturn(mockPluginConfig);
+
when(mockPluginConfig.getBoolean("enabled", false)).thenReturn(true);
when(mockPluginConfig.getString("webhookurl", "")).thenReturn("https://webook/");
when(mockPluginConfig.getString("channel", "general")).thenReturn("testchannel");