diff --git a/docker/controller-node/Dockerfile b/docker/controller-node/Dockerfile index 1af9b262..0c477834 100644 --- a/docker/controller-node/Dockerfile +++ b/docker/controller-node/Dockerfile @@ -45,4 +45,4 @@ RUN cat /tmp/jenkins-casc.yaml | envsubst > /var/jenkins_home/casc/jenkins-casc. RUN mkdir -p /var/jenkins_home/shared RUN mkdir -p /var/jenkins_home/plugins -ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5055" +ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false -XX:+PrintConcurrentLocks -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5055" diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration.java b/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration.java index d7ecc736..2ce9dbde 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration.java @@ -25,6 +25,7 @@ of this software and associated documentation files (the "Software"), to deal package org.datadog.jenkins.plugins.datadog; +import static org.datadog.jenkins.plugins.datadog.configuration.DatadogAgentConfiguration.DatadogAgentConfigurationDescriptor.*; import static org.datadog.jenkins.plugins.datadog.configuration.DatadogAgentConfiguration.DatadogAgentConfigurationDescriptor.getDefaultAgentHost; import static org.datadog.jenkins.plugins.datadog.configuration.DatadogAgentConfiguration.DatadogAgentConfigurationDescriptor.getDefaultAgentLogCollectionPort; import static org.datadog.jenkins.plugins.datadog.configuration.DatadogAgentConfiguration.DatadogAgentConfigurationDescriptor.getDefaultAgentPort; @@ -44,6 +45,7 @@ of this software and associated documentation files (the "Software"), to deal import hudson.util.XStream2; import java.io.File; import java.io.IOException; +import java.util.*; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -51,7 +53,11 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import jenkins.model.GlobalConfiguration; import jenkins.model.Jenkins; import net.sf.json.JSONObject; @@ -66,6 +72,7 @@ of this software and associated documentation files (the "Software"), to deal import org.datadog.jenkins.plugins.datadog.configuration.api.key.DatadogCredentialsApiKey; import org.datadog.jenkins.plugins.datadog.configuration.api.key.DatadogTextApiKey; import org.datadog.jenkins.plugins.datadog.util.SuppressFBWarnings; +import org.datadog.jenkins.plugins.datadog.util.conversion.PatternListConverter; import org.datadog.jenkins.plugins.datadog.util.conversion.PolymorphicReflectionConverter; import org.kohsuke.stapler.*; import org.kohsuke.stapler.DataBoundConstructor; @@ -135,10 +142,15 @@ public class DatadogGlobalConfiguration extends GlobalConfiguration { @XStreamConverter(PolymorphicReflectionConverter.class) private DatadogClientConfiguration datadogClientConfiguration; + + @XStreamConverter(PatternListConverter.class) + private List excluded = null; + + @XStreamConverter(PatternListConverter.class) + private List included = null; + private String ciInstanceName = DEFAULT_CI_INSTANCE_NAME; private String hostname = null; - private String excluded = null; - private String included = null; private String globalTagFile = null; private String globalTags = null; private String globalJobTags = null; @@ -221,25 +233,25 @@ public void loadEnvVariables() { } String excludedEnvVar = System.getenv(EXCLUDED_PROPERTY); - if(StringUtils.isBlank(excludedEnvVar)){ + if (StringUtils.isNotBlank(excludedEnvVar)) { + this.excluded = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile); + } else { // backwards compatibility excludedEnvVar = System.getenv(BLACKLIST_PROPERTY); if(StringUtils.isNotBlank(excludedEnvVar)){ - this.excluded = excludedEnvVar; + this.excluded = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile); } - } else { - this.excluded = excludedEnvVar; } String includedEnvVar = System.getenv(INCLUDED_PROPERTY); - if(StringUtils.isBlank(includedEnvVar)){ + if (StringUtils.isNotBlank(includedEnvVar)) { + this.included = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile); + } else { // backwards compatibility includedEnvVar = System.getenv(WHITELIST_PROPERTY); if(StringUtils.isNotBlank(includedEnvVar)){ - this.included = includedEnvVar; + this.included = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile); } - } else { - this.included = includedEnvVar; } String globalTagFileEnvVar = System.getenv(GLOBAL_TAG_FILE_PROPERTY); @@ -357,6 +369,28 @@ public FormValidation doCheckCiInstanceName(@QueryParameter("ciInstanceName") fi return FormValidation.ok(); } + @RequirePOST + public FormValidation doCheckIncluded(@QueryParameter("included") final String included) { + return doCheckPatterns(included); + } + + @RequirePOST + public FormValidation doCheckExcluded(@QueryParameter("excluded") final String excluded) { + return doCheckPatterns(excluded); + } + + private static FormValidation doCheckPatterns(String commaSeparatedPatterns) { + List patterns = DatadogUtilities.cstrToList(commaSeparatedPatterns); + for (String pattern : patterns) { + try { + Pattern.compile(pattern); + } catch (PatternSyntaxException e) { + return FormValidation.error(pattern + " is not a valid regular expression"); + } + } + return FormValidation.ok(); + } + /** * Indicates if this builder can be used with all kinds of project types. * @@ -426,8 +460,23 @@ public boolean configure(final StaplerRequest req, final JSONObject formData) th } setHostname(formData.getString("hostname")); - setExcluded(formData.getString("excluded")); - setIncluded(formData.getString("included")); + + String excludedFormData = formData.getString("excluded"); + FormValidation excludedValidation = doCheckExcluded(excludedFormData); + if (excludedValidation.kind == Kind.ERROR) { + throw new FormException(excludedValidation.getMessage(), "excluded"); + } else { + setExcluded(excludedFormData); + } + + String includedFormData = formData.getString("included"); + FormValidation includedValidation = doCheckIncluded(includedFormData); + if (includedValidation.kind == Kind.ERROR) { + throw new FormException(includedValidation.getMessage(), "included"); + } else { + setIncluded(includedFormData); + } + setGlobalTagFile(formData.getString("globalTagFile")); setGlobalTags(formData.getString("globalTags")); setGlobalJobTags(formData.getString("globalJobTags")); @@ -440,7 +489,6 @@ public boolean configure(final StaplerRequest req, final JSONObject formData) th String includeEvents = formData.getString("includeEvents"); String excludeEvents = formData.getString("excludeEvents"); FormValidation configStatus = validateEventFilteringConfig(emitSecurityEvents, emitSystemEvents, includeEvents, excludeEvents); - if (configStatus.kind == Kind.ERROR) { String message = configStatus.getMessage(); String formField = !message.contains("included") ? "excludeEvents" : "includeEvents"; @@ -515,6 +563,19 @@ public void setHostname(final String hostname) { this.hostname = hostname; } + public boolean isJobExcluded(@Nonnull final String jobName) { + if (excluded == null || excluded.isEmpty()) { + return false; + } + for (Pattern pattern : excluded) { + Matcher matcher = pattern.matcher(jobName); + if (matcher.matches()) { + return true; + } + } + return false; + } + /** * Getter function for the excluded global configuration, containing * a comma-separated list of jobs to exclude from monitoring. @@ -522,7 +583,7 @@ public void setHostname(final String hostname) { * @return a String array containing the excluded global configuration. */ public String getExcluded() { - return excluded; + return DatadogUtilities.listToCstr(excluded, Pattern::toString); } /** @@ -532,7 +593,20 @@ public String getExcluded() { * @param jobs - a comma-separated list of jobs to exclude from monitoring. */ public void setExcluded(final String jobs) { - this.excluded = jobs; + this.excluded = DatadogUtilities.cstrToList(jobs, Pattern::compile); + } + + public boolean isJobIncluded(@Nonnull final String jobName) { + if (included == null || included.isEmpty()) { + return true; + } + for (Pattern pattern : included) { + Matcher matcher = pattern.matcher(jobName); + if (matcher.matches()) { + return true; + } + } + return false; } /** @@ -542,7 +616,7 @@ public void setExcluded(final String jobs) { * @return a String array containing the included global configuration. */ public String getIncluded() { - return included; + return DatadogUtilities.listToCstr(included, Pattern::toString); } /** @@ -552,7 +626,7 @@ public String getIncluded() { * @param jobs - a comma-separated list of jobs to include for monitoring. */ public void setIncluded(final String jobs) { - this.included = jobs; + this.included = DatadogUtilities.cstrToList(jobs, Pattern::compile); } /** @@ -1014,13 +1088,13 @@ public String getTraceServiceName() { /** @deprecated use {@link #getExcluded()} */ @Deprecated public String getBlacklist() { - return excluded; + return DatadogUtilities.listToCstr(excluded, Pattern::toString); } /** @deprecated use {@link #getIncluded()} */ @Deprecated public String getWhitelist() { - return included; + return DatadogUtilities.listToCstr(included, Pattern::toString); } /** @deprecated use {@link #getEnableCiVisibility()} */ @@ -1161,10 +1235,10 @@ protected Object readResolve() { this.ciInstanceName = this.traceServiceName; } if (StringUtils.isNotBlank(this.blacklist)) { - this.excluded = this.blacklist; + this.excluded = DatadogUtilities.cstrToList(this.blacklist, Pattern::compile); } if (StringUtils.isNotBlank(this.whitelist)) { - this.included = this.whitelist; + this.included = DatadogUtilities.cstrToList(this.whitelist, Pattern::compile); } if (DATADOG_AGENT_CLIENT_TYPE.equals(reportWith)) { this.datadogClientConfiguration = new DatadogAgentConfiguration(this.targetHost, this.targetPort, this.targetLogCollectionPort, this.targetTraceCollectionPort); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogUtilities.java b/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogUtilities.java index 3b8e35ff..31c1d56e 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogUtilities.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogUtilities.java @@ -364,17 +364,7 @@ private static boolean isJobExcluded(@Nonnull final String jobName) { if (datadogGlobalConfig == null) { return false; } - final String excludedProp = datadogGlobalConfig.getExcluded(); - List excluded = cstrToList(excludedProp); - for (String excludedJob : excluded) { - Pattern excludedJobPattern = Pattern.compile(excludedJob); - Matcher jobNameMatcher = excludedJobPattern.matcher(jobName); - if (jobNameMatcher.matches()) { - return true; - } - } - return false; - + return datadogGlobalConfig.isJobExcluded(jobName); } /** @@ -388,16 +378,7 @@ private static boolean isJobIncluded(@Nonnull final String jobName) { if (datadogGlobalConfig == null) { return true; } - final String includedProp = datadogGlobalConfig.getIncluded(); - final List included = cstrToList(includedProp); - for (String includedJob : included) { - Pattern includedJobPattern = Pattern.compile(includedJob); - Matcher jobNameMatcher = includedJobPattern.matcher(jobName); - if (jobNameMatcher.matches()) { - return true; - } - } - return included.isEmpty(); + return datadogGlobalConfig.isJobIncluded(jobName); } /** @@ -410,6 +391,23 @@ public static List cstrToList(final String str) { return convertRegexStringToList(str, ","); } + /** + * Converts a comma-separated string into a list + * + * @param str - A string containing a comma separated list of items. + * @param converter - Converter function that is applied to every list element + */ + public static List cstrToList(String str, Function converter) { + return convertRegexStringToList(str, ",").stream().map(converter).collect(Collectors.toList()); + } + + /** + * Converts a list to a comma-separated string + */ + public static String listToCstr(List list, Function converter) { + return list != null ? list.stream().map(converter).collect(Collectors.joining(",")) : null; + } + /** * Converts a string List into a List Object * diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogApiConfiguration.java b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogApiConfiguration.java index f205b97d..378cbd01 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogApiConfiguration.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogApiConfiguration.java @@ -141,7 +141,7 @@ public Descriptor getDescriptor() { } @Extension - public static final class DatadogApiConfigurationDescriptor extends DatadogClientConfigurationDescriptor { + public static final class DatadogApiConfigurationDescriptor extends DatadogClientConfiguration.DatadogClientConfigurationDescriptor { public DatadogApiConfigurationDescriptor() { load(); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/intake/DatadogIntakeSite.java b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/intake/DatadogIntakeSite.java index 6cd70ab0..985f0456 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/intake/DatadogIntakeSite.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/intake/DatadogIntakeSite.java @@ -58,7 +58,7 @@ public Descriptor getDescriptor() { } @Extension - public static final class DatadogIntakeSiteDescriptor extends DatadogIntakeDescriptor { + public static final class DatadogIntakeSiteDescriptor extends DatadogIntake.DatadogIntakeDescriptor { public DatadogIntakeSiteDescriptor() { load(); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogCredentialsApiKey.java b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogCredentialsApiKey.java index 81668eb6..10a32fc1 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogCredentialsApiKey.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogCredentialsApiKey.java @@ -73,7 +73,7 @@ public Descriptor getDescriptor() { } @Extension - public static final class DatadogCredentialsApiKeyDescriptor extends DatadogApiKeyDescriptor { + public static final class DatadogCredentialsApiKeyDescriptor extends DatadogApiKey.DatadogApiKeyDescriptor { public DatadogCredentialsApiKeyDescriptor() { load(); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogTextApiKey.java b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogTextApiKey.java index 75867f21..a649535e 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogTextApiKey.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/api/key/DatadogTextApiKey.java @@ -45,7 +45,7 @@ public Descriptor getDescriptor() { } @Extension - public static final class DatadogTextApiKeyDescriptor extends DatadogApiKeyDescriptor { + public static final class DatadogTextApiKeyDescriptor extends DatadogApiKey.DatadogApiKeyDescriptor { public DatadogTextApiKeyDescriptor() { load(); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/PatternListConverter.java b/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/PatternListConverter.java new file mode 100644 index 00000000..53550c42 --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/PatternListConverter.java @@ -0,0 +1,34 @@ +package org.datadog.jenkins.plugins.datadog.util.conversion; + +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import java.util.List; +import java.util.regex.Pattern; +import org.apache.commons.lang.StringUtils; +import org.datadog.jenkins.plugins.datadog.DatadogUtilities; + +public class PatternListConverter implements Converter { + + @Override + public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { + String string = DatadogUtilities.listToCstr((List) source, Pattern::toString); + context.convertAnother(string); + } + + @Override + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + String string = (String) context.convertAnother(null, String.class); + if (StringUtils.isBlank(string)) { + return null; + } + return DatadogUtilities.cstrToList(string, Pattern::compile); + } + + @Override + public boolean canConvert(Class type) { + return List.class.isAssignableFrom(type); + } +}