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..ce5d966f 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration.java @@ -25,10 +25,12 @@ 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; import static org.datadog.jenkins.plugins.datadog.configuration.DatadogAgentConfiguration.DatadogAgentConfigurationDescriptor.getDefaultAgentTraceCollectionPort; +import static org.datadog.jenkins.plugins.datadog.configuration.api.intake.DatadogIntakeSite.DatadogIntakeSiteDescriptor.getSite; import static org.datadog.jenkins.plugins.datadog.configuration.api.key.DatadogTextApiKey.DatadogTextApiKeyDescriptor.getDefaultKey; import com.thoughtworks.xstream.annotations.XStreamConverter; @@ -44,6 +46,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; @@ -202,7 +205,7 @@ public void loadEnvVariables() { String clientType = System.getenv(REPORT_WITH_PROPERTY); if (DATADOG_AGENT_CLIENT_TYPE.equals(clientType)) { this.datadogClientConfiguration = new DatadogAgentConfiguration( - getDefaultAgentHost(), getDefaultAgentPort(), getDefaultAgentLogCollectionPort(), getDefaultAgentTraceCollectionPort()); + getDefaultAgentHost(), getDefaultAgentPort(), getDefaultAgentLogCollectionPort(), getDefaultAgentTraceCollectionPort(), getSite()); } else { DatadogIntake intake = DatadogIntake.getDefaultIntake(); DatadogTextApiKey apiKey = new DatadogTextApiKey(getDefaultKey()); @@ -1167,7 +1170,7 @@ protected Object readResolve() { this.included = this.whitelist; } if (DATADOG_AGENT_CLIENT_TYPE.equals(reportWith)) { - this.datadogClientConfiguration = new DatadogAgentConfiguration(this.targetHost, this.targetPort, this.targetLogCollectionPort, this.targetTraceCollectionPort); + this.datadogClientConfiguration = new DatadogAgentConfiguration(this.targetHost, this.targetPort, this.targetLogCollectionPort, this.targetTraceCollectionPort, getSite()); } if (DATADOG_API_CLIENT_TYPE.equals(reportWith)) { DatadogIntakeUrls intake = new DatadogIntakeUrls(this.targetApiURL, this.targetLogIntakeURL, this.targetWebhookIntakeURL); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration.java b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration.java index 273ffcbd..7495fa4a 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration.java @@ -4,24 +4,27 @@ import hudson.RelativePath; import hudson.model.Descriptor; import hudson.util.FormValidation; -import java.net.MalformedURLException; -import java.net.Socket; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.logging.Logger; -import javax.annotation.Nonnull; +import hudson.util.ListBoxModel; import jenkins.model.Jenkins; import org.apache.commons.lang.StringUtils; import org.datadog.jenkins.plugins.datadog.DatadogClient; import org.datadog.jenkins.plugins.datadog.DatadogUtilities; import org.datadog.jenkins.plugins.datadog.clients.DatadogAgentClient; +import org.datadog.jenkins.plugins.datadog.configuration.api.intake.DatadogSite; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.interceptor.RequirePOST; +import javax.annotation.Nonnull; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Logger; + @Symbol("datadogAgentConfiguration") public class DatadogAgentConfiguration extends DatadogClientConfiguration { @@ -45,13 +48,19 @@ public class DatadogAgentConfiguration extends DatadogClientConfiguration { private final Integer agentPort; private final Integer agentLogCollectionPort; private final Integer agentTraceCollectionPort; + private final DatadogSite site; @DataBoundConstructor - public DatadogAgentConfiguration(String agentHost, Integer agentPort, Integer agentLogCollectionPort, Integer agentTraceCollectionPort) { + public DatadogAgentConfiguration(String agentHost, Integer agentPort, Integer agentLogCollectionPort, Integer agentTraceCollectionPort, String site) { + this(agentHost, agentPort, agentLogCollectionPort, agentTraceCollectionPort, StringUtils.isNotBlank(site) ? DatadogSite.valueOf(site) : null); + } + + public DatadogAgentConfiguration(String agentHost, Integer agentPort, Integer agentLogCollectionPort, Integer agentTraceCollectionPort, DatadogSite site) { this.agentHost = agentHost; this.agentPort = agentPort; this.agentLogCollectionPort = agentLogCollectionPort; this.agentTraceCollectionPort = agentTraceCollectionPort; + this.site = site; } public String getAgentHost() { @@ -70,6 +79,10 @@ public Integer getAgentTraceCollectionPort() { return agentTraceCollectionPort; } + public DatadogSite getSite() { + return site; + } + @Override public DatadogClient createClient() { return new DatadogAgentClient(agentHost, agentPort, agentLogCollectionPort, agentTraceCollectionPort); @@ -127,6 +140,11 @@ public Map toEnvironmentVariables() { return variables; } + @Override + public String getSiteName() { + return site != null ? site.getSiteName() : null; + } + @Override public Descriptor getDescriptor() { Jenkins jenkins = Jenkins.getInstanceOrNull(); @@ -218,6 +236,17 @@ public FormValidation doCheckTraceConnectivity(@QueryParameter("agentHost") fina return FormValidation.ok("Success!"); } + @RequirePOST + public ListBoxModel doFillSiteItems() { + DatadogSite[] siteValues = DatadogSite.values(); + ListBoxModel.Option[] values = new ListBoxModel.Option[siteValues.length + 1]; + values[0] = new ListBoxModel.Option(""); + for (int i = 0; i < siteValues.length; i++) { + values[i + 1] = new ListBoxModel.Option(siteValues[i].name()); + } + return new ListBoxModel(values); + } + public static String getDefaultAgentHost() { Map envVars = System.getenv(); @@ -331,11 +360,12 @@ public boolean equals(Object o) { return Objects.equals(agentHost, that.agentHost) && Objects.equals(agentPort, that.agentPort) && Objects.equals(agentLogCollectionPort, that.agentLogCollectionPort) - && Objects.equals(agentTraceCollectionPort, that.agentTraceCollectionPort); + && Objects.equals(agentTraceCollectionPort, that.agentTraceCollectionPort) + && Objects.equals(site, that.site); } @Override public int hashCode() { - return Objects.hash(agentHost, agentPort, agentLogCollectionPort, agentTraceCollectionPort); + return Objects.hash(agentHost, agentPort, agentLogCollectionPort, agentTraceCollectionPort, site); } } \ No newline at end of file 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..3fd411d2 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 @@ -131,6 +131,11 @@ public Map toEnvironmentVariables() { return variables; } + @Override + public String getSiteName() { + return intake.getSiteName(); + } + @Override public Descriptor getDescriptor() { Jenkins jenkins = Jenkins.getInstanceOrNull(); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogClientConfiguration.java b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogClientConfiguration.java index 4db0b3b8..30662200 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogClientConfiguration.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/configuration/DatadogClientConfiguration.java @@ -2,13 +2,15 @@ import hudson.model.Describable; import hudson.model.Descriptor; +import jenkins.model.Jenkins; +import org.datadog.jenkins.plugins.datadog.DatadogClient; + +import javax.annotation.Nullable; import java.io.Serializable; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; -import jenkins.model.Jenkins; -import org.datadog.jenkins.plugins.datadog.DatadogClient; public abstract class DatadogClientConfiguration implements Describable, Serializable { @@ -20,6 +22,9 @@ public abstract class DatadogClientConfiguration implements Describable toEnvironmentVariables(); + @Nullable + public abstract String getSiteName(); + public static abstract class DatadogClientConfigurationDescriptor extends Descriptor { public static List all() { Jenkins jenkins = Jenkins.getInstanceOrNull(); 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..8cc44d0f 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 @@ -7,6 +7,7 @@ import org.kohsuke.stapler.DataBoundConstructor; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.Objects; @@ -74,7 +75,8 @@ public String getHelpFile() { return getHelpFile("siteBlock"); } - public static DatadogSite getDefaultSite() { + @Nullable + public static DatadogSite getSite() { String site = System.getenv().get(DATADOG_SITE_PROPERTY); if (site != null) { try { @@ -84,6 +86,14 @@ public static DatadogSite getDefaultSite() { "Illegal " + DATADOG_SITE_PROPERTY + " environment property value set: " + site + ". Allowed values are " + Arrays.toString(DatadogSite.values()), e); } + } + return null; + } + + public static DatadogSite getDefaultSite() { + DatadogSite site = getSite(); + if (site != null) { + return site; } else { return DEFAULT_DATADOG_SITE_VALUE; } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListener.java b/src/main/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListener.java index 355dad5e..f35b6c00 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListener.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListener.java @@ -25,23 +25,40 @@ of this software and associated documentation files (the "Software"), to deal package org.datadog.jenkins.plugins.datadog.listeners; +import static org.datadog.jenkins.plugins.datadog.DatadogUtilities.cleanUpTraceActions; +import static org.datadog.jenkins.plugins.datadog.traces.TracerConstants.SPAN_ID_ENVVAR_KEY; +import static org.datadog.jenkins.plugins.datadog.traces.TracerConstants.TRACE_ID_ENVVAR_KEY; + import com.cloudbees.workflow.rest.external.RunExt; import com.cloudbees.workflow.rest.external.StageNodeExt; import hudson.Extension; import hudson.Launcher; import hudson.model.*; import hudson.model.listeners.RunListener; +import java.io.IOException; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import javax.annotation.Nonnull; +import org.apache.commons.lang.StringUtils; import org.datadog.jenkins.plugins.datadog.DatadogClient; import org.datadog.jenkins.plugins.datadog.DatadogEvent; import org.datadog.jenkins.plugins.datadog.DatadogGlobalConfiguration; import org.datadog.jenkins.plugins.datadog.DatadogUtilities; import org.datadog.jenkins.plugins.datadog.clients.ClientHolder; +import org.datadog.jenkins.plugins.datadog.configuration.DatadogClientConfiguration; import org.datadog.jenkins.plugins.datadog.events.BuildAbortedEventImpl; import org.datadog.jenkins.plugins.datadog.events.BuildFinishedEventImpl; import org.datadog.jenkins.plugins.datadog.events.BuildStartedEventImpl; import org.datadog.jenkins.plugins.datadog.metrics.Metrics; import org.datadog.jenkins.plugins.datadog.metrics.MetricsClient; import org.datadog.jenkins.plugins.datadog.model.*; +import org.datadog.jenkins.plugins.datadog.model.BuildData; +import org.datadog.jenkins.plugins.datadog.model.GitCommitAction; +import org.datadog.jenkins.plugins.datadog.model.GitRepositoryAction; +import org.datadog.jenkins.plugins.datadog.model.PipelineQueueInfoAction; +import org.datadog.jenkins.plugins.datadog.model.TraceInfoAction; import org.datadog.jenkins.plugins.datadog.traces.BuildSpanAction; import org.datadog.jenkins.plugins.datadog.traces.BuildSpanManager; import org.datadog.jenkins.plugins.datadog.traces.message.TraceSpan; @@ -50,18 +67,6 @@ of this software and associated documentation files (the "Software"), to deal import org.datadog.jenkins.plugins.datadog.util.SuppressFBWarnings; import org.jenkinsci.plugins.workflow.job.WorkflowRun; -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; - -import static org.datadog.jenkins.plugins.datadog.DatadogUtilities.cleanUpTraceActions; -import static org.datadog.jenkins.plugins.datadog.traces.TracerConstants.SPAN_ID_ENVVAR_KEY; -import static org.datadog.jenkins.plugins.datadog.traces.TracerConstants.TRACE_ID_ENVVAR_KEY; - - /** * This class registers an {@link RunListener} to trigger events and calculate metrics: * - When a build initializes, the {@link #onInitialize(Run)} method will be invoked. @@ -362,6 +367,16 @@ public void onFinalized(Run run) { } BuildData buildData = BuildData.create(run, null); + + DatadogGlobalConfiguration datadogConfiguration = DatadogUtilities.getDatadogGlobalDescriptor(); + if (datadogConfiguration != null && datadogConfiguration.getEnableCiVisibility()) { + DatadogClientConfiguration clientConfiguration = datadogConfiguration.getDatadogClientConfiguration(); + String siteName = clientConfiguration.getSiteName(); + if (StringUtils.isNotBlank(siteName)) { + run.addAction(new DatadogLinkAction(buildData, siteName)); + } + } + traceWriter.submitBuild(buildData, run); logger.fine("End DatadogBuildListener#onFinalized"); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/DatadogLinkAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/DatadogLinkAction.java new file mode 100644 index 00000000..12c4d4ba --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/DatadogLinkAction.java @@ -0,0 +1,70 @@ +package org.datadog.jenkins.plugins.datadog.model; + +import com.thoughtworks.xstream.XStream; +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 hudson.model.Action; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import static org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter.ignoreOldData; + +public class DatadogLinkAction implements Action { + + private final String url; + + public DatadogLinkAction(BuildData buildData, String siteName) { + String query = String.format("ci_level:pipeline @ci.pipeline.name:\"%s\" @ci.pipeline.number:%s", buildData.getJobName(), buildData.getBuildNumber("")); + String urlEncodedQuery = URLEncoder.encode(query, StandardCharsets.UTF_8); + this.url = String.format("https://app.%s/ci/pipeline-executions?query=%s", siteName, urlEncodedQuery); + } + + private DatadogLinkAction(String url) { + this.url = url; + } + + @Override + public String getIconFileName() { + return "/plugin/datadog/icons/dd_icon_rgb.svg"; + } + + @Override + public String getDisplayName() { + return "View in Datadog"; + } + + @Override + public String getUrlName() { + return url; + } + + public static final class ConverterImpl extends DatadogConverter { + public ConverterImpl(XStream xs) { + super(ignoreOldData(), new ConverterV1()); + } + } + + public static final class ConverterV1 extends VersionedConverter { + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); + } + + @Override + public void marshal(DatadogLinkAction action, HierarchicalStreamWriter writer, MarshallingContext context) { + writeField("url", action.url, writer, context); + } + + @Override + public DatadogLinkAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + String url = readField(reader, context, String.class); + return new DatadogLinkAction(url); + } + } +} diff --git a/src/main/resources/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration/config.jelly b/src/main/resources/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration/config.jelly index 39b64252..20c6e789 100644 --- a/src/main/resources/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration/config.jelly +++ b/src/main/resources/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration/config.jelly @@ -25,4 +25,8 @@ method="checkTraceConnectivity" with="agentHost,agentTraceCollectionPort" /> + + + + diff --git a/src/main/resources/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration/help-site.html b/src/main/resources/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration/help-site.html new file mode 100644 index 00000000..dba2ad97 --- /dev/null +++ b/src/main/resources/org/datadog/jenkins/plugins/datadog/configuration/DatadogAgentConfiguration/help-site.html @@ -0,0 +1,4 @@ +
+ Select your Datadog site. + See Datadog documentation for more information about sites. +
diff --git a/src/main/webapp/icons/dd_icon_rgb.png b/src/main/webapp/icons/dd_icon_rgb.png new file mode 100644 index 00000000..59877115 Binary files /dev/null and b/src/main/webapp/icons/dd_icon_rgb.png differ diff --git a/src/test/java/org/datadog/jenkins/plugins/datadog/apm/TracerInjectionIT.java b/src/test/java/org/datadog/jenkins/plugins/datadog/apm/TracerInjectionIT.java index a87fd21a..132c275b 100644 --- a/src/test/java/org/datadog/jenkins/plugins/datadog/apm/TracerInjectionIT.java +++ b/src/test/java/org/datadog/jenkins/plugins/datadog/apm/TracerInjectionIT.java @@ -1,5 +1,6 @@ package org.datadog.jenkins.plugins.datadog.apm; +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; @@ -55,7 +56,7 @@ public static void suiteSetUp() throws Exception { // There is no agent and the injected tracers will fail to actually send anything, // but for the purposes of this test this is enough, since it is only asserted that the tracer initialisation was reported in the logs datadogConfig.setDatadogClientConfiguration(new DatadogAgentConfiguration( - getDefaultAgentHost(), getDefaultAgentPort(), getDefaultAgentLogCollectionPort(), getDefaultAgentTraceCollectionPort())); + getDefaultAgentHost(), getDefaultAgentPort(), getDefaultAgentLogCollectionPort(), getDefaultAgentTraceCollectionPort(), "")); agentNode = jenkinsRule.createOnlineSlave(); }