diff --git a/pom.xml b/pom.xml
index 28b06c86..beac531d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -121,6 +121,19 @@
workflow-step-api
true
+
+ org.jenkins-ci.plugins
+ pipeline-stage-step
+ 2.3
+
+
+ org.jenkins-ci.plugins.workflow
+ workflow-durable-task-step
+
+
+ org.jenkins-ci.plugins.workflow
+ workflow-job
+
io.jenkins
configuration-as-code
@@ -140,19 +153,19 @@
org.mockito
mockito-core
- 2.13.0
+ 2.25.0
test
org.powermock
powermock-api-mockito2
- 2.0.0-beta.5
+ 2.0.2
test
org.powermock
powermock-module-junit4
- 2.0.0-beta.5
+ 2.0.2
test
@@ -188,11 +201,6 @@
workflow-cps
test
-
- org.jenkins-ci.plugins.workflow
- workflow-job
- test
-
org.jenkins-ci.plugins.workflow
workflow-basic-steps
@@ -203,11 +211,6 @@
workflow-scm-step
test
-
- org.jenkins-ci.plugins.workflow
- workflow-durable-task-step
- test
-
diff --git a/src/main/java/jenkins/plugins/logstash/LogstashConsoleLogFilter.java b/src/main/java/jenkins/plugins/logstash/LogstashConsoleLogFilter.java
index d5995163..9eb7ec0a 100644
--- a/src/main/java/jenkins/plugins/logstash/LogstashConsoleLogFilter.java
+++ b/src/main/java/jenkins/plugins/logstash/LogstashConsoleLogFilter.java
@@ -18,13 +18,8 @@ public class LogstashConsoleLogFilter extends ConsoleLogFilter implements Serial
private static final Logger LOGGER = Logger.getLogger(LogstashConsoleLogFilter.class.getName());
- private transient Run, ?> run;
public LogstashConsoleLogFilter() {}
- public LogstashConsoleLogFilter(Run, ?> run)
- {
- this.run = run;
- }
private static final long serialVersionUID = 1L;
@Override
@@ -49,15 +44,7 @@ public OutputStream decorateLogger(Run build, OutputStream logger) throws IOExce
return logger;
}
}
- if (run != null)
- {
- LogstashWriter logstash = getLogStashWriter(run, logger);
- return new LogstashOutputStream(logger, logstash);
- }
- else
- {
- return logger;
- }
+ return logger;
}
LogstashWriter getLogStashWriter(Run, ?> build, OutputStream errorStream)
diff --git a/src/main/java/jenkins/plugins/logstash/LogstashWriter.java b/src/main/java/jenkins/plugins/logstash/LogstashWriter.java
index 2523814a..aad77e3a 100644
--- a/src/main/java/jenkins/plugins/logstash/LogstashWriter.java
+++ b/src/main/java/jenkins/plugins/logstash/LogstashWriter.java
@@ -35,8 +35,11 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
import java.io.IOException;
import java.io.OutputStream;
+import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Date;
@@ -51,22 +54,31 @@
* @author Liam Newman
* @since 1.0.5
*/
-public class LogstashWriter {
+@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
+public class LogstashWriter implements Serializable {
private final OutputStream errorStream;
- private final Run, ?> build;
+ private final transient Run, ?> build;
private final TaskListener listener;
private final BuildData buildData;
private final String jenkinsUrl;
private final LogstashIndexerDao dao;
private boolean connectionBroken;
- private final Charset charset;
+ private final String charset;
+ private final String stageName;
+ private final String agentName;
public LogstashWriter(Run, ?> run, OutputStream error, TaskListener listener, Charset charset) {
+ this(run, error, listener, charset, null, null);
+ }
+
+ public LogstashWriter(Run, ?> run, OutputStream error, TaskListener listener, Charset charset, String stageName, String agentName) {
this.errorStream = error != null ? error : System.err;
+ this.stageName = stageName;
+ this.agentName = agentName;
this.build = run;
this.listener = listener;
- this.charset = charset;
+ this.charset = charset.toString();
this.dao = this.getDaoOrNull();
if (this.dao == null) {
this.jenkinsUrl = "";
@@ -82,7 +94,7 @@ public LogstashWriter(Run, ?> run, OutputStream error, TaskListener listener,
*
* @return the charset
*/
- public Charset getCharset()
+ public String getCharset()
{
return charset;
}
@@ -154,12 +166,12 @@ BuildData getBuildData() {
if (build instanceof AbstractBuild) {
return new BuildData((AbstractBuild, ?>) build, new Date(), listener);
} else {
- return new BuildData(build, new Date(), listener);
+ return new BuildData(build, new Date(), listener, stageName, agentName);
}
}
String getJenkinsUrl() {
- return Jenkins.getInstance().getRootUrl();
+ return Jenkins.get().getRootUrl();
}
/**
@@ -207,8 +219,10 @@ private LogstashIndexerDao getDaoOrNull() {
private void logErrorMessage(String msg) {
try {
connectionBroken = true;
- errorStream.write(msg.getBytes(charset));
- errorStream.flush();
+ if (errorStream != null) {
+ errorStream.write(msg.getBytes(charset));
+ errorStream.flush();
+ }
} catch (IOException ex) {
// This should never happen, but if it does we just have to let it go.
ex.printStackTrace();
diff --git a/src/main/java/jenkins/plugins/logstash/configuration/ElasticSearch.java b/src/main/java/jenkins/plugins/logstash/configuration/ElasticSearch.java
index efac5c11..ba366344 100644
--- a/src/main/java/jenkins/plugins/logstash/configuration/ElasticSearch.java
+++ b/src/main/java/jenkins/plugins/logstash/configuration/ElasticSearch.java
@@ -8,6 +8,8 @@
import javax.activation.MimeType;
import javax.activation.MimeTypeParseException;
+import javax.annotation.Nonnull;
+
import java.security.cert.CertificateException;
import org.apache.commons.lang.StringUtils;
@@ -132,7 +134,7 @@ public boolean equals(Object obj)
if (getClass() != obj.getClass())
return false;
ElasticSearch other = (ElasticSearch) obj;
- if (!Secret.toString(password).equals(other.getPassword().getPlainText()))
+ if (!Secret.toString(password).equals(Secret.toString(other.getPassword())))
{
return false;
}
@@ -188,35 +190,26 @@ public ElasticSearchDao createIndexerInstance()
ElasticSearchDao esDao = new ElasticSearchDao(getUri(), username, Secret.toString(password));
esDao.setMimeType(getMimeType());
- try {
- esDao.setCustomKeyStore(getCustomKeyStore());
- } catch (KeyStoreException | CertificateException |
- NoSuchAlgorithmException | KeyManagementException | IOException e) {
- LOGGER.log(Level.WARNING, e.getMessage(), e);
- }
- return esDao;
- }
-
- private KeyStore getCustomKeyStore() {
- KeyStore customKeyStore = null;
-
- // Fetch custom alias+certificate as a keystore (if present)
if (!StringUtils.isBlank(customServerCertificateId)) {
- StandardCertificateCredentials certificateCredentials = getCredentials(customServerCertificateId);
- if (certificateCredentials != null) {
- // Fetch keystore containing custom certificate
- customKeyStore = certificateCredentials.getKeyStore();
+ try {
+ StandardCertificateCredentials certificateCredentials = getCredentials(customServerCertificateId);
+ if (certificateCredentials != null) {
+ esDao.setCustomKeyStore(certificateCredentials.getKeyStore(),
+ Secret.toString(certificateCredentials.getPassword()));
+ }
+ } catch (KeyStoreException | CertificateException |
+ NoSuchAlgorithmException | IOException e) {
+ LOGGER.log(Level.WARNING, e.getMessage(), e);
}
}
-
- return customKeyStore;
+ return esDao;
}
private StandardCertificateCredentials getCredentials(String credentials)
{
return (StandardCertificateCredentials) CredentialsMatchers.firstOrNull(
- CredentialsProvider.lookupCredentials(StandardCredentials.class,
- Jenkins.getInstance(), ACL.SYSTEM, Collections.emptyList()),
+ CredentialsProvider.lookupCredentials(StandardCertificateCredentials.class,
+ Jenkins.get(), ACL.SYSTEM, Collections.emptyList()),
CredentialsMatchers.withId(credentials)
);
}
@@ -247,7 +240,7 @@ public ListBoxModel doFillCustomServerCertificateIdItems(
CredentialsMatchers.instanceOf(StandardCertificateCredentials.class)
),
CredentialsProvider.lookupCredentials(StandardCredentials.class,
- Jenkins.getInstance(),
+ Jenkins.get(),
ACL.SYSTEM,
Collections.emptyList()
)
diff --git a/src/main/java/jenkins/plugins/logstash/configuration/RabbitMq.java b/src/main/java/jenkins/plugins/logstash/configuration/RabbitMq.java
index 7ff98667..1047547e 100644
--- a/src/main/java/jenkins/plugins/logstash/configuration/RabbitMq.java
+++ b/src/main/java/jenkins/plugins/logstash/configuration/RabbitMq.java
@@ -125,7 +125,7 @@ public boolean equals(Object obj)
if (getClass() != obj.getClass())
return false;
RabbitMq other = (RabbitMq) obj;
- if (!Secret.toString(password).equals(other.getPassword().getPlainText()))
+ if (!Secret.toString(password).equals(Secret.toString(other.getPassword())))
{
return false;
}
diff --git a/src/main/java/jenkins/plugins/logstash/configuration/Redis.java b/src/main/java/jenkins/plugins/logstash/configuration/Redis.java
index 4242ca42..373743be 100644
--- a/src/main/java/jenkins/plugins/logstash/configuration/Redis.java
+++ b/src/main/java/jenkins/plugins/logstash/configuration/Redis.java
@@ -55,7 +55,7 @@ public boolean equals(Object obj)
if (getClass() != obj.getClass())
return false;
Redis other = (Redis) obj;
- if (!Secret.toString(password).equals(other.getPassword().getPlainText()))
+ if (!Secret.toString(password).equals(Secret.toString(other.getPassword())))
{
return false;
}
diff --git a/src/main/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDao.java b/src/main/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDao.java
index 0fea9d4d..47a16241 100644
--- a/src/main/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDao.java
+++ b/src/main/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDao.java
@@ -24,6 +24,7 @@
package jenkins.plugins.logstash.persistence;
+import java.io.Serializable;
import java.util.Calendar;
import java.util.List;
@@ -36,7 +37,7 @@
* @author Rusty Gerard
* @since 1.0.0
*/
-public abstract class AbstractLogstashIndexerDao implements LogstashIndexerDao {
+public abstract class AbstractLogstashIndexerDao implements LogstashIndexerDao, Serializable {
@Override
public JSONObject buildPayload(BuildData buildData, String jenkinsUrl, List logLines) {
diff --git a/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java b/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java
index 4379a4de..d94b409a 100644
--- a/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java
+++ b/src/main/java/jenkins/plugins/logstash/persistence/BuildData.java
@@ -48,6 +48,7 @@
import static java.util.logging.Level.WARNING;
import java.io.IOException;
+import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import net.sf.json.JSONObject;
@@ -56,21 +57,25 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
/**
* POJO for mapping build info to JSON.
*
* @author Rusty Gerard
* @since 1.0.0
*/
-public class BuildData {
+@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
+public class BuildData implements Serializable {
+
// ISO 8601 date format
private final static Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getCanonicalName());
- public static class TestData {
+ public static class TestData implements Serializable {
private final int totalCount, skipCount, failCount, passCount;
private final List failedTestsWithErrorDetail;
private final List failedTests;
- public static class FailedTest {
+ public static class FailedTest implements Serializable {
private final String fullName, errorDetails;
public FailedTest(String fullName, String errorDetails) {
super();
@@ -160,6 +165,8 @@ public List getFailedTests()
private String url;
private String buildHost;
private String buildLabel;
+ private String stageName;
+ private String agentName;
private int buildNum;
private long buildDuration;
private transient String timestamp; // This belongs in the root object
@@ -212,9 +219,11 @@ 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 stageName, String agentName) {
initData(build, currentTime);
+ this.agentName = agentName;
+ this.stageName = stageName;
rootProjectName = projectName;
rootFullProjectName = fullProjectName;
rootProjectDisplayName = displayName;
@@ -262,14 +271,15 @@ private void initData(Run, ?> build, Date currentTime) {
public void updateResult()
{
- if (result == null && build.getResult() != null)
- {
- Result result = build.getResult();
- this.result = result == null ? null : result.toString();
- }
- Action testResultAction = build.getAction(AbstractTestResultAction.class);
- if (testResults == null && testResultAction != null) {
- testResults = new TestData(testResultAction);
+ if (build != null) {
+ if (result == null && build.getResult() != null) {
+ Result result = build.getResult();
+ this.result = result == null ? null : result.toString();
+ }
+ Action testResultAction = build.getAction(AbstractTestResultAction.class);
+ if (testResults == null && testResultAction != null) {
+ testResults = new TestData(testResultAction);
+ }
}
}
@@ -443,4 +453,20 @@ public TestData getTestResults() {
public void setTestResults(TestData testResults) {
this.testResults = testResults;
}
+
+ public String getStageName() {
+ return stageName;
+ }
+
+ public void setStageName(String stageName) {
+ this.stageName = stageName;
+ }
+
+ public String getAgentName() {
+ return agentName;
+ }
+
+ public void setAgentName(String agentName) {
+ this.agentName = agentName;
+ }
}
diff --git a/src/main/java/jenkins/plugins/logstash/persistence/ElasticSearchDao.java b/src/main/java/jenkins/plugins/logstash/persistence/ElasticSearchDao.java
index 96feb347..4ceafb10 100644
--- a/src/main/java/jenkins/plugins/logstash/persistence/ElasticSearchDao.java
+++ b/src/main/java/jenkins/plugins/logstash/persistence/ElasticSearchDao.java
@@ -36,6 +36,7 @@
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
@@ -52,6 +53,7 @@
import com.google.common.collect.Range;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jenkins.plugins.logstash.utils.SSLHelper;
@@ -61,9 +63,10 @@
* @author Liam Newman
* @since 1.0.4
*/
+@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
public class ElasticSearchDao extends AbstractLogstashIndexerDao {
- private final HttpClientBuilder clientBuilder;
+ private transient HttpClientBuilder clientBuilder;
private final URI uri;
private final String auth;
private final Range successCodes = closedOpen(200,300);
@@ -71,7 +74,8 @@ public class ElasticSearchDao extends AbstractLogstashIndexerDao {
private String username;
private String password;
private String mimeType;
- private KeyStore customKeyStore;
+ private byte[] keystoreBytes;
+ private String keyStorePassword;
//primary constructor used by indexer factory
public ElasticSearchDao(URI uri, String username, String password) {
@@ -106,7 +110,36 @@ public ElasticSearchDao(URI uri, String username, String password) {
auth = null;
}
- clientBuilder = factory == null ? HttpClientBuilder.create() : factory;
+ clientBuilder = factory;
+ }
+
+ private byte[] getKeystoreBytes() {
+ return keystoreBytes;
+ }
+
+ private String getKeyStorePassword() {
+ return keyStorePassword;
+ }
+
+ private synchronized HttpClientBuilder getClientBuilder() throws IOException {
+ if (clientBuilder == null) {
+ clientBuilder = HttpClientBuilder.create();
+ if (getKeystoreBytes() != null) {
+ KeyStore trustStore;
+ try {
+ trustStore = KeyStore.getInstance("PKCS12");
+ String pwd = getKeyStorePassword();
+ if (pwd == null) {
+ pwd = "";
+ }
+ trustStore.load(new ByteArrayInputStream(getKeystoreBytes()), pwd.toCharArray());
+ SSLHelper.setClientBuilderSSLContext(clientBuilder, trustStore);
+ } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ return clientBuilder;
}
@@ -152,19 +185,18 @@ public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
- public KeyStore getCustomKeyStore() {
- return this.customKeyStore;
- }
-
String getAuth()
{
return auth;
}
- public void setCustomKeyStore(KeyStore customKeyStore) throws
- CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {
- SSLHelper.setClientBuilderSSLContext(this.clientBuilder, customKeyStore);
- this.customKeyStore = customKeyStore;
+ public void setCustomKeyStore(KeyStore customKeyStore, String keyStorePassword) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
+ if (customKeyStore != null) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ customKeyStore.store(bos, keyStorePassword.toCharArray());
+ keystoreBytes = bos.toByteArray();
+ this.keyStorePassword = keyStorePassword;
+ }
}
HttpPost getHttpPost(String data) {
@@ -185,7 +217,7 @@ HttpPost getHttpPost(String data) {
public void push(String data) throws IOException {
HttpPost post = getHttpPost(data);
- try (CloseableHttpClient httpClient = clientBuilder.build(); CloseableHttpResponse response = httpClient.execute(post)) {
+ try (CloseableHttpClient httpClient = getClientBuilder().build(); CloseableHttpResponse response = httpClient.execute(post)) {
if (!successCodes.contains(response.getStatusLine().getStatusCode())) {
throw new IOException(this.getErrorMessage(response));
}
diff --git a/src/main/java/jenkins/plugins/logstash/persistence/HostBasedLogstashIndexerDao.java b/src/main/java/jenkins/plugins/logstash/persistence/HostBasedLogstashIndexerDao.java
index 0ede70f6..41cc0c3d 100644
--- a/src/main/java/jenkins/plugins/logstash/persistence/HostBasedLogstashIndexerDao.java
+++ b/src/main/java/jenkins/plugins/logstash/persistence/HostBasedLogstashIndexerDao.java
@@ -31,6 +31,7 @@
* @since 2.0.0
*/
public abstract class HostBasedLogstashIndexerDao extends AbstractLogstashIndexerDao {
+
private final String host;
private final int port;
diff --git a/src/main/java/jenkins/plugins/logstash/persistence/RabbitMqDao.java b/src/main/java/jenkins/plugins/logstash/persistence/RabbitMqDao.java
index 552d3668..fbbda53c 100644
--- a/src/main/java/jenkins/plugins/logstash/persistence/RabbitMqDao.java
+++ b/src/main/java/jenkins/plugins/logstash/persistence/RabbitMqDao.java
@@ -33,6 +33,8 @@
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
/**
* RabbitMQ Data Access Object.
*
@@ -42,13 +44,15 @@
* @author Rusty Gerard
* @since 1.0.0
*/
+@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
public class RabbitMqDao extends HostBasedLogstashIndexerDao {
- private final ConnectionFactory pool;
+
+ private transient ConnectionFactory pool;
private final String queue;
private final String username;
private final String password;
- private final Charset charset;
+ private final String charset;
private final String virtualHost;
@@ -68,7 +72,7 @@ public RabbitMqDao(String host, int port, String key, String username, String pa
this.queue = queue;
this.username = username;
this.password = password;
- this.charset = charset;
+ this.charset = charset.toString();
this.virtualHost = vhost;
if (StringUtils.isBlank(queue)) {
@@ -78,17 +82,31 @@ public RabbitMqDao(String host, int port, String key, String username, String pa
// The ConnectionFactory must be a singleton
// We assume this is used as a singleton as well
// Calling this method means the configuration has changed and the pool must be re-initialized
- pool = factory == null ? new ConnectionFactory() : factory;
- pool.setHost(host);
- pool.setPort(port);
- if (virtualHost != null)
- {
- pool.setVirtualHost(virtualHost);
+ pool = factory;
+ initPool();
+ }
+
+ private synchronized ConnectionFactory getPool() {
+ if (pool == null) {
+ pool = new ConnectionFactory();
+ initPool();
}
+ return pool;
+ }
- if (!StringUtils.isBlank(username) && !StringUtils.isBlank(password)) {
- pool.setPassword(password);
- pool.setUsername(username);
+ private void initPool() {
+ if (pool != null)
+ {
+ pool.setHost(getHost());
+ pool.setPort(getPort());
+ if (virtualHost != null) {
+ pool.setVirtualHost(virtualHost);
+ }
+
+ if (!StringUtils.isBlank(username) && !StringUtils.isBlank(password)) {
+ pool.setPassword(password);
+ pool.setUsername(username);
+ }
}
}
@@ -126,7 +144,7 @@ public void push(String data) throws IOException {
Connection connection = null;
Channel channel = null;
try {
- connection = pool.newConnection();
+ connection = getPool().newConnection();
channel = connection.createChannel();
// Ensure the queue exists
diff --git a/src/main/java/jenkins/plugins/logstash/persistence/RedisDao.java b/src/main/java/jenkins/plugins/logstash/persistence/RedisDao.java
index 65f964f9..63848b12 100644
--- a/src/main/java/jenkins/plugins/logstash/persistence/RedisDao.java
+++ b/src/main/java/jenkins/plugins/logstash/persistence/RedisDao.java
@@ -28,6 +28,7 @@
import org.apache.commons.lang.StringUtils;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@@ -40,8 +41,10 @@
* @author Rusty Gerard
* @since 1.0.0
*/
+@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
public class RedisDao extends HostBasedLogstashIndexerDao {
- private final JedisPool pool;
+
+ private transient JedisPool pool;
private final String password;
private final String key;
@@ -68,7 +71,13 @@ public RedisDao(String host, int port, String key, String password) {
// The JedisPool must be a singleton
// We assume this is used as a singleton as well
- pool = factory == null ? new JedisPool(new JedisPoolConfig(), host, port) : factory;
+ pool = factory;
+ }
+
+ private synchronized void getJedisPool() {
+ if (pool == null) {
+ pool = new JedisPool(new JedisPoolConfig(), getHost(), getPort());
+ }
}
public String getPassword()
@@ -86,6 +95,7 @@ public void push(String data) throws IOException {
Jedis jedis = null;
boolean connectionBroken = false;
try {
+ getJedisPool();
jedis = pool.getResource();
if (!StringUtils.isBlank(password)) {
jedis.auth(password);
diff --git a/src/main/java/jenkins/plugins/logstash/persistence/SyslogDao.java b/src/main/java/jenkins/plugins/logstash/persistence/SyslogDao.java
index 86f96d41..755ace6a 100644
--- a/src/main/java/jenkins/plugins/logstash/persistence/SyslogDao.java
+++ b/src/main/java/jenkins/plugins/logstash/persistence/SyslogDao.java
@@ -7,13 +7,16 @@
import com.cloudbees.syslog.Severity;
import com.cloudbees.syslog.sender.UdpSyslogMessageSender;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
/*
* TODO: add support for TcpSyslogMessageSender
*/
+@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
public class SyslogDao extends HostBasedLogstashIndexerDao {
private MessageFormat messageFormat = MessageFormat.RFC_3164;
- private final UdpSyslogMessageSender messageSender;
+ private transient UdpSyslogMessageSender messageSender;
public SyslogDao(String host, int port) {
this(null, host, port);
@@ -27,7 +30,7 @@ public SyslogDao(String host, int port) {
public SyslogDao(UdpSyslogMessageSender udpSyslogMessageSender, String host, int port) {
super(host, port);
- messageSender = udpSyslogMessageSender == null ? new UdpSyslogMessageSender() : udpSyslogMessageSender;
+ messageSender = udpSyslogMessageSender;
}
public void setMessageFormat(MessageFormat format) {
@@ -38,12 +41,19 @@ public MessageFormat getMessageFormat() {
return messageFormat;
}
+ private synchronized void getMessageSender() {
+ if (messageSender == null) {
+ messageSender = new UdpSyslogMessageSender();
+ }
+ }
+
@Override
public void push(String data) throws IOException {
// Making the JSON document compliant to Common Event Expression (CEE)
// Ref: http://www.rsyslog.com/json-elasticsearch/
data = " @cee: " + data;
// SYSLOG Configuration
+ getMessageSender();
messageSender.setDefaultMessageHostname(getHost());
messageSender.setDefaultAppName("jenkins:");
messageSender.setDefaultFacility(Facility.USER);
diff --git a/src/main/java/jenkins/plugins/logstash/pipeline/GlobalDecorator.java b/src/main/java/jenkins/plugins/logstash/pipeline/GlobalDecorator.java
new file mode 100644
index 00000000..a8509f1c
--- /dev/null
+++ b/src/main/java/jenkins/plugins/logstash/pipeline/GlobalDecorator.java
@@ -0,0 +1,65 @@
+package jenkins.plugins.logstash.pipeline;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import hudson.Extension;
+import hudson.model.Queue;
+import hudson.model.Run;
+import jenkins.plugins.logstash.LogstashConfiguration;
+import jenkins.plugins.logstash.LogstashOutputStream;
+import jenkins.plugins.logstash.LogstashWriter;
+
+@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
+public class GlobalDecorator extends TaskListenerDecorator {
+ private static final Logger LOGGER = Logger.getLogger(GlobalDecorator.class.getName());
+
+ private transient Run, ?> run;
+ private String stageName;
+ private String agentName;
+
+ public GlobalDecorator(WorkflowRun run) {
+ this(run, null, null);
+ }
+ public GlobalDecorator(WorkflowRun run, String stageName, String agentName) {
+ LOGGER.log(Level.INFO, "Creating decorator for {0}", run.toString());
+ this.run = run;
+ this.stageName = stageName;
+ this.agentName = agentName;
+ }
+
+ @Override
+ public OutputStream decorate(OutputStream logger) throws IOException, InterruptedException {
+ LogstashWriter writer = new LogstashWriter(run, logger, null, StandardCharsets.UTF_8, stageName, agentName);
+ LogstashOutputStream out = new LogstashOutputStream(logger, writer);
+ return out;
+ }
+
+ @Extension
+ public static final class Factory implements TaskListenerDecorator.Factory {
+
+ @Override
+ public TaskListenerDecorator of(FlowExecutionOwner owner) {
+ if (!LogstashConfiguration.getInstance().isEnableGlobally()) {
+ return null;
+ }
+ try {
+ Queue.Executable executable = owner.getExecutable();
+ if (executable instanceof WorkflowRun) {
+ return new GlobalDecorator((WorkflowRun) executable);
+ }
+ } catch (IOException x) {
+ LOGGER.log(Level.WARNING, null, x);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/jenkins/plugins/logstash/pipeline/LogstashStep.java b/src/main/java/jenkins/plugins/logstash/pipeline/LogstashStep.java
index 142f97c1..17a660fd 100644
--- a/src/main/java/jenkins/plugins/logstash/pipeline/LogstashStep.java
+++ b/src/main/java/jenkins/plugins/logstash/pipeline/LogstashStep.java
@@ -3,9 +3,18 @@
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.annotation.Nonnull;
+import org.jenkinsci.plugins.workflow.actions.LabelAction;
+import org.jenkinsci.plugins.workflow.actions.WorkspaceAction;
+import org.jenkinsci.plugins.workflow.graph.BlockStartNode;
+import org.jenkinsci.plugins.workflow.graph.FlowNode;
+import org.jenkinsci.plugins.workflow.graph.StepNode;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl;
import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback;
import org.jenkinsci.plugins.workflow.steps.BodyInvoker;
@@ -13,13 +22,17 @@
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
+import org.jenkinsci.plugins.workflow.support.steps.ExecutorStep;
+import org.jenkinsci.plugins.workflow.support.steps.StageStep;
import org.kohsuke.stapler.DataBoundConstructor;
+import com.google.common.collect.ImmutableSet;
+
import hudson.Extension;
-import hudson.console.ConsoleLogFilter;
import hudson.model.Run;
+import hudson.model.TaskListener;
import jenkins.YesNoMaybe;
-import jenkins.plugins.logstash.LogstashConsoleLogFilter;
+import jenkins.plugins.logstash.LogstashConfiguration;
/**
* This is the pipeline counterpart of the LogstashJobProperty.
@@ -27,6 +40,7 @@
*/
public class LogstashStep extends Step {
+ private static final Logger LOGGER = Logger.getLogger(LogstashStep.class.getName());
/** Constructor. */
@DataBoundConstructor
public LogstashStep() {}
@@ -56,20 +70,69 @@ public void onResume()
@Override
public boolean start() throws Exception {
StepContext context = getContext();
- context
- .newBodyInvoker()
- .withContext(createConsoleLogFilter(context))
- .withCallback(BodyExecutionCallback.wrap(context))
- .start();
+ BodyInvoker invoker = context.newBodyInvoker().withCallback(BodyExecutionCallback.wrap(context));
+ if (LogstashConfiguration.getInstance().isEnableGlobally()) {
+ context.get(TaskListener.class).getLogger().println("The logstash step is unnecessary when logstash is enabled for all builds.");
+ } else {
+ invoker.withContext(getMergedDecorator(context));
+ }
+ invoker.start();
return false;
}
- private ConsoleLogFilter createConsoleLogFilter(StepContext context)
+ private TaskListenerDecorator getMergedDecorator(StepContext context)
throws IOException, InterruptedException {
- ConsoleLogFilter original = context.get(ConsoleLogFilter.class);
- Run, ?> build = context.get(Run.class);
- ConsoleLogFilter subsequent = new LogstashConsoleLogFilter(build);
- return BodyInvoker.mergeConsoleLogFilters(original, subsequent);
+ Run, ?> run = context.get(Run.class);
+ FlowNode node = context.get(FlowNode.class);
+ FlowNode stageNode = getStageNode(node);
+ String stageName = null;
+ if (stageNode != null) {
+ LabelAction labelAction = stageNode.getAction(LabelAction.class);
+ if (labelAction != null) {
+ stageName = labelAction.getDisplayName();
+ }
+ }
+ String agentName = getAgentName(node);
+ return TaskListenerDecorator.merge(context.get(TaskListenerDecorator.class), new GlobalDecorator((WorkflowRun) run, stageName, agentName));
+ }
+
+ private String getAgentName(FlowNode node) {
+ for (BlockStartNode bsn : node.iterateEnclosingBlocks()) {
+ if (bsn instanceof StepNode) {
+ StepDescriptor descriptor = ((StepNode) bsn).getDescriptor();
+ if (descriptor instanceof ExecutorStep.DescriptorImpl) {
+ WorkspaceAction workspaceAction = bsn.getAction(WorkspaceAction.class);
+ if (workspaceAction != null) {
+ return workspaceAction.getNode();
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private FlowNode getStageNode(FlowNode node) {
+ for (BlockStartNode bsn : node.iterateEnclosingBlocks()) {
+ if (isStageNode(bsn)) {
+ return bsn;
+ }
+ }
+ return null;
+ }
+
+ private boolean isStageNode(FlowNode node) {
+ if (node instanceof StepNode) {
+ StepDescriptor descriptor = ((StepNode) node).getDescriptor();
+ if (descriptor instanceof StageStep.DescriptorImpl) {
+ LabelAction labelAction = node.getAction(LabelAction.class);
+ if (labelAction != null) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
/** {@inheritDoc} */
@@ -102,11 +165,8 @@ public boolean takesImplicitBlockArgument() {
}
@Override
- public Set extends Class>> getRequiredContext()
- {
- Set> contexts = new HashSet<>();
- contexts.add(Run.class);
- return contexts;
+ public Set extends Class>> getRequiredContext() {
+ return ImmutableSet.of(Run.class, FlowNode.class);
}
}
diff --git a/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java b/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java
index 7752bf38..163626cf 100644
--- a/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java
+++ b/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java
@@ -1,8 +1,8 @@
package jenkins.plugins.logstash;
import static org.hamcrest.core.StringContains.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import java.io.ByteArrayOutputStream;
@@ -33,7 +33,7 @@ public void before() throws Exception {
buffer = new ByteArrayOutputStream();
Mockito.doNothing().when(mockWriter).write(anyString());
when(mockWriter.isConnectionBroken()).thenReturn(false);
- when(mockWriter.getCharset()).thenReturn(Charset.defaultCharset());
+ when(mockWriter.getCharset()).thenReturn(Charset.defaultCharset().toString());
}
@After
diff --git a/src/test/java/jenkins/plugins/logstash/PipelineTest.java b/src/test/java/jenkins/plugins/logstash/PipelineTest.java
index ec67b616..98820341 100644
--- a/src/test/java/jenkins/plugins/logstash/PipelineTest.java
+++ b/src/test/java/jenkins/plugins/logstash/PipelineTest.java
@@ -1,7 +1,8 @@
package jenkins.plugins.logstash;
import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.MatcherAssert.assertThat;
import java.util.List;
@@ -15,6 +16,7 @@
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.JenkinsRule;
+import hudson.model.Slave;
import jenkins.plugins.logstash.configuration.MemoryIndexer;
import jenkins.plugins.logstash.persistence.MemoryDao;
import net.sf.json.JSONObject;
@@ -54,6 +56,45 @@ public void logstash() throws Exception
assertThat(data.getString("result"),equalTo("SUCCESS"));
}
+ @Test
+ public void logstashStageAndAgent() throws Exception
+ {
+ Slave slave = j.createOnlineSlave();
+ String agentName = slave.getNodeName();
+ WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
+ p.setDefinition(new CpsFlowDefinition("stage('stage1') { " +
+ "node('" + agentName + "') {\n" +
+ "logstash {\n" +
+ "currentBuild.result = 'SUCCESS'\n" +
+ "echo 'Message'\n" +
+ "}}}", true));
+ j.assertBuildStatusSuccess(p.scheduleBuild2(0).get());
+ List dataLines = memoryDao.getOutput();
+ assertThat(dataLines.size(), equalTo(1));
+ JSONObject firstLine = dataLines.get(0);
+ JSONObject data = firstLine.getJSONObject("data");
+ assertThat(data.getString("result"),equalTo("SUCCESS"));
+ assertThat(data.getString("stageName"),equalTo("stage1"));
+ assertThat(data.getString("agentName"),equalTo(agentName));
+ }
+
+ @Test
+ public void globalLogstash() throws Exception
+ {
+ LogstashConfiguration config = LogstashConfiguration.getInstance();
+ config.setEnableGlobally(true);
+ WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
+ p.setDefinition(new CpsFlowDefinition(
+ "currentBuild.result = 'SUCCESS'\n" +
+ "echo 'Message'\n", true));
+ j.assertBuildStatusSuccess(p.scheduleBuild2(0).get());
+ List dataLines = memoryDao.getOutput();
+ assertThat(dataLines.size(), greaterThan(0));
+ JSONObject lastLine = dataLines.get(dataLines.size()-1);
+ JSONObject data = lastLine.getJSONObject("data");
+ assertThat(data.getString("result"),equalTo("SUCCESS"));
+ }
+
@Test
public void logstashSendNotifier() throws Exception
{
diff --git a/src/test/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDaoTest.java b/src/test/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDaoTest.java
index f88ced1f..57e0f7f2 100644
--- a/src/test/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDaoTest.java
+++ b/src/test/java/jenkins/plugins/logstash/persistence/AbstractLogstashIndexerDaoTest.java
@@ -79,6 +79,7 @@ public void buildPayloadSuccessTwoLines() throws Exception {
private AbstractLogstashIndexerDao getInstance() {
return new AbstractLogstashIndexerDao() {
+
@Override
public void push(String data) throws IOException {}
diff --git a/src/test/java/jenkins/plugins/logstash/persistence/ElasticSearchSSLCertsTest.java b/src/test/java/jenkins/plugins/logstash/persistence/ElasticSearchSSLCertsTest.java
index 333f65a6..1ceed6d9 100644
--- a/src/test/java/jenkins/plugins/logstash/persistence/ElasticSearchSSLCertsTest.java
+++ b/src/test/java/jenkins/plugins/logstash/persistence/ElasticSearchSSLCertsTest.java
@@ -40,14 +40,11 @@ public class ElasticSearchSSLCertsTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
- private static final KeyStore NO_CLIENT_KEYSTORE = null;
private static final SSLContext NO_SSL_CONTEXT = null;
- private static final TrustManager[] NO_SERVER_TRUST_MANAGER = null;
private static final char[] KEYPASS_AND_STOREPASS_VALUE = "aaaaaa".toCharArray();
private static final String JAVA_KEYSTORE = "jks";
- private static final String CLIENT_KEYSTORE = "elasticsearch-sslcerts/keystore.ks";
- private static final String CLIENT_TRUSTSTORE = "elasticsearch-sslcerts/truststore.ks";
+ private static final String CLIENT_KEYSTORE = "elasticsearch-sslcerts/cert.pkcs12";
@Test
public void NoSSLPost_NoSSLServer_Returns200OK() throws Exception {
@@ -98,7 +95,7 @@ public void SSLPost_SSLServer_UpdatedTrustStore_Returns200OK() throws Exception
ElasticSearchDao dao = new ElasticSearchDao(new URI("https://" + baseUrl), "", "");
KeyStore keyStore = getStore(CLIENT_KEYSTORE, KEYPASS_AND_STOREPASS_VALUE);
- dao.setCustomKeyStore(keyStore);
+ dao.setCustomKeyStore(keyStore, "aaaaaa");
try {
dao.push("");
@@ -116,7 +113,7 @@ public void SSLPost_NoSSLServer_ThrowsSSLException() throws Exception {
ElasticSearchDao dao = new ElasticSearchDao(new URI("https://" + baseUrl), "", "");
KeyStore keyStore = getStore(CLIENT_KEYSTORE, KEYPASS_AND_STOREPASS_VALUE);
- dao.setCustomKeyStore(keyStore);
+ dao.setCustomKeyStore(keyStore, "aaaaaa");
try {
thrown.expect(IsInstanceOf.instanceOf(SSLException.class));