From fa67fac344536d8c82f37ac3cc9cacb27ab73dcb Mon Sep 17 00:00:00 2001 From: Chaminda Divitotawela Date: Sat, 21 Oct 2017 08:46:28 +1000 Subject: [PATCH] Ability to send custom data when using pipeline --- pom.xml | 2 +- .../logstash/LogstashBuildWrapper.java | 2 +- .../plugins/logstash/LogstashNotifier.java | 6 +++-- .../plugins/logstash/LogstashWriter.java | 11 +++++----- .../logstash/persistence/BuildData.java | 21 ++++++++++++++++-- .../logstash/LogstashNotifier/config.jelly | 3 +++ .../LogstashNotifier/help-customData.html | 4 ++++ .../logstash/LogstashNotifierTest.java | 22 +++++++++---------- .../plugins/logstash/LogstashWriterTest.java | 6 ++--- .../logstash/persistence/BuildDataTest.java | 22 +++++++++---------- 10 files changed, 63 insertions(+), 36 deletions(-) create mode 100644 src/main/resources/jenkins/plugins/logstash/LogstashNotifier/help-customData.html diff --git a/pom.xml b/pom.xml index 518f0589..0b9c017a 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ logstash hpi - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT Logstash A Logstash agent to send Jenkins logs to a Logstash indexer. https://wiki.jenkins-ci.org/display/JENKINS/Logstash+Plugin diff --git a/src/main/java/jenkins/plugins/logstash/LogstashBuildWrapper.java b/src/main/java/jenkins/plugins/logstash/LogstashBuildWrapper.java index 2e4a0100..13530630 100644 --- a/src/main/java/jenkins/plugins/logstash/LogstashBuildWrapper.java +++ b/src/main/java/jenkins/plugins/logstash/LogstashBuildWrapper.java @@ -108,7 +108,7 @@ public DescriptorImpl getDescriptor() { // Method to encapsulate calls for unit-testing LogstashWriter getLogStashWriter(AbstractBuild build, OutputStream errorStream) { - return new LogstashWriter(build, errorStream, null); + return new LogstashWriter(build, errorStream, null, "{}"); } /** diff --git a/src/main/java/jenkins/plugins/logstash/LogstashNotifier.java b/src/main/java/jenkins/plugins/logstash/LogstashNotifier.java index 722ecc93..3643eee4 100644 --- a/src/main/java/jenkins/plugins/logstash/LogstashNotifier.java +++ b/src/main/java/jenkins/plugins/logstash/LogstashNotifier.java @@ -56,11 +56,13 @@ public class LogstashNotifier extends Notifier implements SimpleBuildStep { public int maxLines; public boolean failBuild; + public String customData; @DataBoundConstructor - public LogstashNotifier(int maxLines, boolean failBuild) { + public LogstashNotifier(int maxLines, boolean failBuild, String customData) { this.maxLines = maxLines; this.failBuild = failBuild; + this.customData = customData; } @Override @@ -85,7 +87,7 @@ private boolean perform(Run run, TaskListener listener) { // Method to encapsulate calls for unit-testing LogstashWriter getLogStashWriter(Run run, OutputStream errorStream, TaskListener listener) { - return new LogstashWriter(run, errorStream, listener); + return new LogstashWriter(run, errorStream, listener, customData); } public BuildStepMonitor getRequiredMonitorService() { diff --git a/src/main/java/jenkins/plugins/logstash/LogstashWriter.java b/src/main/java/jenkins/plugins/logstash/LogstashWriter.java index 68134d30..53407311 100644 --- a/src/main/java/jenkins/plugins/logstash/LogstashWriter.java +++ b/src/main/java/jenkins/plugins/logstash/LogstashWriter.java @@ -61,7 +61,7 @@ public class LogstashWriter { final LogstashIndexerDao dao; private boolean connectionBroken; - public LogstashWriter(Run run, OutputStream error, TaskListener listener) { + public LogstashWriter(Run run, OutputStream error, TaskListener listener, String customData) { this.errorStream = error != null ? error : System.err; this.build = run; this.listener = listener; @@ -71,7 +71,7 @@ public LogstashWriter(Run run, OutputStream error, TaskListener listener) this.buildData = null; } else { this.jenkinsUrl = getJenkinsUrl(); - this.buildData = getBuildData(); + this.buildData = getBuildData(customData); } } @@ -133,11 +133,12 @@ LogstashIndexerDao getDao() throws InstantiationException { return IndexerDaoFactory.getInstance(descriptor.type, descriptor.host, descriptor.port, descriptor.key, descriptor.username, descriptor.password); } - BuildData getBuildData() { + BuildData getBuildData(String customData) { + if (build instanceof AbstractBuild) { - return new BuildData((AbstractBuild) build, new Date(), listener); + return new BuildData((AbstractBuild) build, new Date(), listener, customData); } else { - return new BuildData(build, new Date(), listener); + return new BuildData(build, new Date(), listener, customData); } } diff --git a/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java b/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java index abb8a5be..daf5b3e8 100644 --- a/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java +++ b/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java @@ -50,6 +50,7 @@ import static java.util.logging.Level.WARNING; import java.io.IOException; import java.lang.invoke.MethodHandles; +import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; @@ -120,6 +121,7 @@ public TestData(Action action) { protected String displayName; protected String fullDisplayName; protected String description; + protected JSONObject custom; protected String url; protected String buildHost; protected String buildLabel; @@ -135,7 +137,7 @@ public TestData(Action action) { protected TestData testResults = null; // Freestyle project build - public BuildData(AbstractBuild build, Date currentTime, TaskListener listener) { + public BuildData(AbstractBuild build, Date currentTime, TaskListener listener, String customData) { initData(build, currentTime); Node node = build.getBuiltOn(); @@ -171,6 +173,14 @@ public BuildData(AbstractBuild build, Date currentTime, TaskListener liste } } } + + // Parse JSON string. If fails add error message and continue + try{ + custom = JSONObject.fromObject(customData); + }catch (JSONException e){ + custom = JSONObject.fromObject("{\"error\":\"custom data json parse failed\"}"); + } + try { buildVariables.putAll(build.getEnvironment(listener)); } catch (Exception e) { @@ -183,7 +193,7 @@ public BuildData(AbstractBuild build, Date currentTime, TaskListener liste } // Pipeline project build - public BuildData(Run build, Date currentTime, TaskListener listener) { + public BuildData(Run build, Date currentTime, TaskListener listener, String customData) { initData(build, currentTime); Executor executor = build.getExecutor(); @@ -198,6 +208,13 @@ public BuildData(Run build, Date currentTime, TaskListener listener) { rootProjectDisplayName = displayName; rootBuildNum = buildNum; + // Parse JSON string. If fails add error message and continue + try{ + custom = JSONObject.fromObject(customData); + }catch (JSONException e){ + custom = JSONObject.fromObject("{\"error\":\"custom data json parse failed\"}"); + } + try { // TODO: sensitive variables are not filtered, c.f. https://stackoverflow.com/questions/30916085 buildVariables = build.getEnvironment(listener); diff --git a/src/main/resources/jenkins/plugins/logstash/LogstashNotifier/config.jelly b/src/main/resources/jenkins/plugins/logstash/LogstashNotifier/config.jelly index 59d03ec1..2a9893e3 100644 --- a/src/main/resources/jenkins/plugins/logstash/LogstashNotifier/config.jelly +++ b/src/main/resources/jenkins/plugins/logstash/LogstashNotifier/config.jelly @@ -3,6 +3,9 @@ + + + diff --git a/src/main/resources/jenkins/plugins/logstash/LogstashNotifier/help-customData.html b/src/main/resources/jenkins/plugins/logstash/LogstashNotifier/help-customData.html new file mode 100644 index 00000000..33f8382e --- /dev/null +++ b/src/main/resources/jenkins/plugins/logstash/LogstashNotifier/help-customData.html @@ -0,0 +1,4 @@ +
+

Custom json string to send to Logstash.
+ Expected json string. If parse fail, error message is added in custom field

+
diff --git a/src/test/java/jenkins/plugins/logstash/LogstashNotifierTest.java b/src/test/java/jenkins/plugins/logstash/LogstashNotifierTest.java index 27a30376..c648fb5c 100644 --- a/src/test/java/jenkins/plugins/logstash/LogstashNotifierTest.java +++ b/src/test/java/jenkins/plugins/logstash/LogstashNotifierTest.java @@ -36,8 +36,8 @@ public class LogstashNotifierTest { static class MockLogstashNotifier extends LogstashNotifier { LogstashWriter writer; - MockLogstashNotifier(int maxLines, boolean failBuild, LogstashWriter writer) { - super(maxLines, failBuild); + MockLogstashNotifier(int maxLines, boolean failBuild, LogstashWriter writer, String customData) { + super(maxLines, failBuild, customData); this.writer = writer; } @@ -95,7 +95,7 @@ public void before() throws Exception { when(mockWriter.isConnectionBroken()).thenReturn(false); Mockito.doNothing().when(mockWriter).writeBuildLog(anyInt()); - notifier = new MockLogstashNotifier(3, false, mockWriter); + notifier = new MockLogstashNotifier(3, false, mockWriter, "{\"key\":\"value\"}"); } @After @@ -143,7 +143,7 @@ public void performBadWriterDoNotFailBuild() throws Exception { // Initialize mocks when(mockWriter.isConnectionBroken()).thenReturn(true); - notifier = new MockLogstashNotifier(3, false, mockWriter); + notifier = new MockLogstashNotifier(3, false, mockWriter, "{}"); // Unit under test boolean result = notifier.perform(mockBuild, mockLauncher, mockListener); @@ -163,7 +163,7 @@ public void performStepBadWriterDoNotFailBuild() throws Exception { // Initialize mocks when(mockWriter.isConnectionBroken()).thenReturn(true); - notifier = new MockLogstashNotifier(3, false, mockWriter); + notifier = new MockLogstashNotifier(3, false, mockWriter,"{}"); // Unit under test notifier.perform(mockRun, null, mockLauncher, mockListener); @@ -183,7 +183,7 @@ public void performBadWriterDoFailBuild() throws Exception { // Initialize mocks when(mockWriter.isConnectionBroken()).thenReturn(true); - notifier = new MockLogstashNotifier(3, true, mockWriter); + notifier = new MockLogstashNotifier(3, true, mockWriter, "{}"); // Unit under test boolean result = notifier.perform(mockBuild, mockLauncher, mockListener); @@ -203,7 +203,7 @@ public void performStepBadWriterDoFailBuild() throws Exception { // Initialize mocks when(mockWriter.isConnectionBroken()).thenReturn(true); - notifier = new MockLogstashNotifier(3, true, mockWriter); + notifier = new MockLogstashNotifier(3, true, mockWriter, "{}"); // Unit under test notifier.perform(mockRun, null, mockLauncher, mockListener); @@ -234,7 +234,7 @@ public Object answer(InvocationOnMock invocationOnMock) throws Throwable { } }).when(mockWriter).writeBuildLog(anyInt()); - notifier = new MockLogstashNotifier(3, true, mockWriter); + notifier = new MockLogstashNotifier(3, true, mockWriter, "{}"); assertEquals("Errors were written", "", errorBuffer.toString()); // Unit under test @@ -257,7 +257,7 @@ public void performAllLines() throws Exception { Mockito.doNothing().when(mockWriter).writeBuildLog(anyInt()); - notifier = new MockLogstashNotifier(-1, true, mockWriter); + notifier = new MockLogstashNotifier(-1, true, mockWriter, "{}"); // Unit under test boolean result = notifier.perform(mockBuild, mockLauncher, mockListener); @@ -279,7 +279,7 @@ public void performZeroLines() throws Exception { Mockito.doNothing().when(mockWriter).writeBuildLog(anyInt()); - notifier = new MockLogstashNotifier(0, true, mockWriter); + notifier = new MockLogstashNotifier(0, true, mockWriter, "{}"); // Unit under test boolean result = notifier.perform(mockBuild, mockLauncher, mockListener); @@ -296,7 +296,7 @@ public void performZeroLines() throws Exception { @Test public void getRequiredMonitorService() throws Exception { - Notifier notifier = new LogstashNotifier(1, false); + Notifier notifier = new LogstashNotifier(1, false, "{}"); // Unit under test BuildStepMonitor synchronizationMonitor = notifier.getRequiredMonitorService(); diff --git a/src/test/java/jenkins/plugins/logstash/LogstashWriterTest.java b/src/test/java/jenkins/plugins/logstash/LogstashWriterTest.java index 8e643e39..507f2cc9 100644 --- a/src/test/java/jenkins/plugins/logstash/LogstashWriterTest.java +++ b/src/test/java/jenkins/plugins/logstash/LogstashWriterTest.java @@ -37,7 +37,7 @@ static LogstashWriter createLogstashWriter(final AbstractBuild testBuild, final String url, final LogstashIndexerDao indexer, final BuildData data) { - return new LogstashWriter(testBuild, error, null) { + return new LogstashWriter(testBuild, error, null, "{}") { @Override LogstashIndexerDao getDao() throws InstantiationException { if (indexer == null) { @@ -48,12 +48,12 @@ LogstashIndexerDao getDao() throws InstantiationException { } @Override - BuildData getBuildData() { + BuildData getBuildData(String customData) { assertNotNull("BuildData should never be requested for missing dao.", this.dao); // For testing, providing null data means use the actual method if (data == null) { - return super.getBuildData(); + return super.getBuildData(customData); } else { return data; } diff --git a/src/test/java/jenkins/plugins/logstash/persistence/BuildDataTest.java b/src/test/java/jenkins/plugins/logstash/persistence/BuildDataTest.java index 51dfb09b..7da74d51 100644 --- a/src/test/java/jenkins/plugins/logstash/persistence/BuildDataTest.java +++ b/src/test/java/jenkins/plugins/logstash/persistence/BuildDataTest.java @@ -45,7 +45,7 @@ public class BuildDataTest { static final String FULL_STRING = "{\"id\":\"TEST_JOB_123\",\"result\":\"SUCCESS\",\"fullProjectName\":\"parent/BuildDataTest\"," + "\"projectName\":\"BuildDataTest\",\"displayName\":\"BuildData Test\",\"fullDisplayName\":\"BuildData Test #123456\"," - + "\"description\":\"Mock project for testing BuildData\",\"url\":\"http://localhost:8080/jenkins/jobs/PROJECT_NAME/123\"," + + "\"description\":\"Mock project for testing BuildData\", \"custom\": {}, \"url\":\"http://localhost:8080/jenkins/jobs/PROJECT_NAME/123\"," + "\"buildHost\":\"master\",\"buildLabel\":\"master\",\"buildNum\":123456,\"buildDuration\":60," + "\"rootProjectName\":\"RootBuildDataTest\",\"rootFullProjectName\":\"parent/RootBuildDataTest\"," + "\"rootProjectDisplayName\":\"Root BuildData Test\",\"rootBuildNum\":456,\"buildVariables\":{}," @@ -155,7 +155,7 @@ public void constructorSuccessBuiltOnNull() throws Exception { when(mockBuild.getBuiltOn()).thenReturn(null); // Unit under test - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener, "{}"); // build.getDuration() is always 0 in Notifiers Assert.assertEquals("Incorrect buildDuration", 60L, buildData.getBuildDuration()); @@ -176,7 +176,7 @@ public void constructorSuccessBuiltOnMaster() throws Exception { when(mockNode.getLabelString()).thenReturn(""); // Unit under test - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener, "{}"); // build.getDuration() is always 0 in Notifiers Assert.assertEquals("Incorrect buildDuration", 60L, buildData.getBuildDuration()); @@ -197,7 +197,7 @@ public void constructorSuccessBuiltOnSlave() throws Exception { when(mockNode.getLabelString()).thenReturn("Test Slave"); // Unit under test - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener, "{}"); // build.getDuration() is always 0 in Notifiers Assert.assertEquals("Incorrect buildDuration", 60L, buildData.getBuildDuration()); @@ -222,7 +222,7 @@ public void constructorSuccessTestFailures() throws Exception { when(mockTestResultAction.getFailedTests()).thenReturn(Arrays.asList(mockTestResult)); // Unit under test - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener,"{}"); Assert.assertEquals("Incorrect test results", 123, buildData.testResults.totalCount); Assert.assertEquals("Incorrect test results", 0, buildData.testResults.skipCount); @@ -240,7 +240,7 @@ public void constructorSuccessNoTests() throws Exception { when(mockBuild.getAction(AbstractTestResultAction.class)).thenReturn(null); // Unit under test - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener, "{}"); Assert.assertEquals("Incorrect test results", null, buildData.testResults); @@ -277,7 +277,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { when(mockBuild.getSensitiveBuildVariables()).thenReturn(new HashSet<>(Arrays.asList(sensitiveVarKey))); // Unit under test - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener, "{}"); // Verify results Assert.assertEquals("Wrong number of environment variables", 2, buildData.getBuildVariables().size()); @@ -317,7 +317,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { when(mockBuild.getEnvironment(mockListener)).thenReturn(new EnvVars(varKey, buildVarVal)); // Unit under test - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener,"{}"); // Verify results Assert.assertEquals("Wrong number of environment variables", 1, buildData.getBuildVariables().size()); @@ -335,7 +335,7 @@ public void toJsonSuccess() throws Exception when(mockBuild.getId()).thenReturn("TEST_JOB_123"); when(mockBuild.getUrl()).thenReturn("http://localhost:8080/jenkins/jobs/PROJECT_NAME/123"); - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener, "{}"); // Unit under test JSONObject result = buildData.toJson(); @@ -353,7 +353,7 @@ public void fullName() throws Exception when(mockBuild.getId()).thenReturn("TEST_JOB_123"); when(mockBuild.getUrl()).thenReturn("http://localhost:8080/jenkins/jobs/PROJECT_NAME/123"); - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener,"{}"); Assert.assertEquals(buildData.getFullProjectName(), "parent/BuildDataTest"); @@ -367,7 +367,7 @@ public void rootProjectFullName() throws Exception when(mockBuild.getId()).thenReturn("TEST_JOB_123"); when(mockBuild.getUrl()).thenReturn("http://localhost:8080/jenkins/jobs/PROJECT_NAME/123"); - BuildData buildData = new BuildData(mockBuild, mockDate, mockListener); + BuildData buildData = new BuildData(mockBuild, mockDate, mockListener,"{}"); Assert.assertEquals(buildData.getRootFullProjectName(), "parent/RootBuildDataTest");