- Enter your Datadog API key, which can be found
-
here.
- This field is optional, use it only if you would like to store your API key with
-
Jenkins Credentials.
-
diff --git a/src/main/resources/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration/help-targetApiKeyEntry.html b/src/main/resources/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration/help-targetApiKeyEntry.html
new file mode 100644
index 000000000..85559287d
--- /dev/null
+++ b/src/main/resources/org/datadog/jenkins/plugins/datadog/DatadogGlobalConfiguration/help-targetApiKeyEntry.html
@@ -0,0 +1,4 @@
+
+ Enter your Datadog API key, which can be found
+
here.
+ This field is optional, use it only if you would like to store your API key with
+
Jenkins Credentials.
+
diff --git a/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/config.jelly b/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/config.jelly
new file mode 100644
index 000000000..78c59de7a
--- /dev/null
+++ b/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/config.jelly
@@ -0,0 +1,45 @@
+
+
+
+ Additional environment variables to fine-tune the tracer behavior.
+ See Datadog docs for more details.
+
+
+ These are optional, use only if you want to change defaults.
+
+
diff --git a/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/help-languages.html b/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/help-languages.html
new file mode 100644
index 000000000..9d47dbc3f
--- /dev/null
+++ b/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/help-languages.html
@@ -0,0 +1,37 @@
+
+
Automatic tracer injection comes with some language-specific caveats.
+
+
Java
+
+ Injection works for any JVM-based language (Java, Kotlin, Groovy, etc).
+ Any Maven or Gradle build that the job executes will be traced.
+
+
+ MAVEN_OPTS
and GRADLE_OPTS
environment variables are used for injection.
+ If these variables are overridden inside the job (with their existing value being discarded rather than preserved),
+ the injection will not happen.
+
+
+
JS
+
+ The plugin assumes that npm
is available on the node that executes the job.
+
+
+ NODE_OPTIONS
environment variable is used for injection.
+ If this variable is overridden inside the job (with its existing value being discarded rather than preserved),
+ the injection will not happen.
+
+
+
Python
+
+ The plugin assumes that pip
is available on the node that executes the job.
+
+
+ The plugin assumes that the job uses the same pip
/python
versions as the default ones that are available on the execution node.
+
+
+ PYTEST_ADDOPTS
and PYTHONPATH
environment variables are used for injection.
+ If these variables are overridden inside the job (with their existing value being discarded rather than preserved),
+ the injection will not happen.
+
+
diff --git a/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/help-on.html b/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/help-on.html
new file mode 100644
index 000000000..ee4ea96bc
--- /dev/null
+++ b/src/main/resources/org/datadog/jenkins/plugins/datadog/tracer/DatadogTracerJobProperty/help-on.html
@@ -0,0 +1,5 @@
+
+ The name of the service or library being tested.
+ This value is used for populating test.service
tag in Datadog.
+
diff --git a/src/test/java/org/datadog/jenkins/plugins/datadog/tracer/TracerInjectionIT.java b/src/test/java/org/datadog/jenkins/plugins/datadog/tracer/TracerInjectionIT.java
new file mode 100644
index 000000000..baae85bb6
--- /dev/null
+++ b/src/test/java/org/datadog/jenkins/plugins/datadog/tracer/TracerInjectionIT.java
@@ -0,0 +1,195 @@
+package org.datadog.jenkins.plugins.datadog.tracer;
+
+import hudson.FilePath;
+import hudson.model.FreeStyleBuild;
+import hudson.model.FreeStyleProject;
+import hudson.model.Job;
+import hudson.model.TopLevelItem;
+import hudson.slaves.DumbSlave;
+import hudson.tasks.Shell;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import org.datadog.jenkins.plugins.datadog.DatadogGlobalConfiguration;
+import org.datadog.jenkins.plugins.datadog.DatadogUtilities;
+import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
+import org.jenkinsci.plugins.workflow.job.WorkflowJob;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.jvnet.hudson.test.FlagRule;
+import org.jvnet.hudson.test.JenkinsRule;
+
+public class TracerInjectionIT {
+
+ // "mvn" script does not quote MAVEN_OPTS properly, this is outside of our control
+ @ClassRule
+ public static TestRule noSpaceInTmpDirs = FlagRule.systemProperty("jenkins.test.noSpaceInTmpDirs", "true");
+
+ @ClassRule
+ public static JenkinsRule jenkinsRule = new JenkinsRule();
+ private static DumbSlave agentNode;
+
+ @BeforeClass
+ public static void suiteSetUp() throws Exception {
+ DatadogGlobalConfiguration datadogConfig = DatadogUtilities.getDatadogGlobalDescriptor();
+ datadogConfig.setReportWith("DSD");
+
+ agentNode = jenkinsRule.createOnlineSlave();
+ }
+
+ @AfterClass
+ public static void suiteTearDown() throws Exception {
+ jenkinsRule.disconnectSlave(agentNode);
+ }
+
+ @Test
+ public void testTracerInjectionInFreestyleProject() throws Exception {
+ FreeStyleProject project = givenFreestyleProject();
+ try {
+ givenProjectIsAMavenBuild(project);
+ givenTracerInjectionEnabled(project);
+ FreeStyleBuild build = whenRunningBuild(project);
+ thenTracerIsInjected(build);
+ } finally {
+ project.delete();
+ }
+ }
+
+ @Test
+ public void testTracerInjectionInFreestyleProjectExecutedOnAgentNode() throws Exception {
+ FreeStyleProject project = givenFreestyleProject();
+ try {
+ givenProjectIsBuiltOnAgentNode(project);
+ givenProjectIsAMavenBuild(project);
+ givenTracerInjectionEnabled(project);
+ FreeStyleBuild build = whenRunningBuild(project);
+ thenTracerIsInjected(build);
+ } finally {
+ project.delete();
+ }
+ }
+
+ @Test
+ public void testTracerInjectionInPipeline() throws Exception {
+ WorkflowJob pipeline = givenPipelineProjectBuiltOnMaster();
+ try {
+ givenPipelineIsAMavenBuild(pipeline, false);
+ givenTracerInjectionEnabled(pipeline);
+ WorkflowRun build = whenRunningBuild(pipeline);
+ thenTracerIsInjected(build);
+ } finally {
+ pipeline.delete();
+ }
+ }
+
+ @Test
+ public void testTracerInjectionInPipelineExecutedOnAgentNode() throws Exception {
+ WorkflowJob pipeline = givenPipelineProjectBuiltOnAgentNode();
+ try {
+ givenPipelineIsAMavenBuild(pipeline, true);
+ givenTracerInjectionEnabled(pipeline);
+ WorkflowRun build = whenRunningBuild(pipeline);
+ thenTracerIsInjected(build);
+ } finally {
+ pipeline.delete();
+ }
+ }
+
+ private FreeStyleProject givenFreestyleProject() throws IOException {
+ return jenkinsRule.createFreeStyleProject("freestyleProject");
+ }
+
+ private WorkflowJob givenPipelineProjectBuiltOnMaster() throws Exception {
+ return givenPipelineProject(false);
+ }
+
+ private WorkflowJob givenPipelineProjectBuiltOnAgentNode() throws Exception {
+ return givenPipelineProject(true);
+ }
+
+ private WorkflowJob givenPipelineProject(boolean builtOnAgentNode) throws Exception {
+ WorkflowJob job = jenkinsRule.jenkins.createProject(WorkflowJob.class, "pipelineProject");
+ String definition = "pipeline {\n" +
+ " agent {\n" +
+ " label '" + (builtOnAgentNode ? agentNode.getSelfLabel().getName() : "built-in") + "'\n" +
+ " }\n" +
+ " stages {\n" +
+ " stage('test'){\n" +
+ " steps {\n" +
+ " " + getMavenCommand() + "\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+ job.setDefinition(new CpsFlowDefinition(definition, true));
+ return job;
+ }
+
+ private String getMavenCommand() {
+ return isRunningOnWindows()
+ ? "bat \"./mvnw.cmd clean\""
+ : "sh \"./mvnw clean\"";
+ }
+
+ private boolean isRunningOnWindows() {
+ return System.getProperty("os.name").toLowerCase().contains("win");
+ }
+
+ private void givenProjectIsBuiltOnAgentNode(FreeStyleProject project) throws Exception {
+ project.setAssignedNode(agentNode);
+ }
+
+ private void givenProjectIsAMavenBuild(FreeStyleProject project) throws Exception {
+ copyMavenFilesToWorkspace(agentNode.getSelfLabel().equals(project.getAssignedLabel()), project);
+ project.getBuildersList().add(new Shell("./mvnw clean"));
+ }
+
+ private void givenPipelineIsAMavenBuild(WorkflowJob pipeline, boolean builtOnAgent) throws Exception {
+ copyMavenFilesToWorkspace(builtOnAgent, pipeline);
+ }
+
+ private void copyMavenFilesToWorkspace(boolean builtOnAgent, TopLevelItem item) throws Exception {
+ FilePath projectWorkspace;
+ if (builtOnAgent) {
+ projectWorkspace = agentNode.getWorkspaceFor(item);
+ } else {
+ projectWorkspace = jenkinsRule.jenkins.getWorkspaceFor(item);
+ }
+
+ URL mavenProjectUrl = TracerInjectionIT.class.getResource("/org/datadog/jenkins/plugins/datadog/tracer/test-maven-project");
+ File mavenProject = new File(mavenProjectUrl.getFile());
+ FilePath mavenProjectPath = new FilePath(mavenProject);
+ mavenProjectPath.copyRecursiveTo(projectWorkspace);
+ }
+
+ private void givenTracerInjectionEnabled(Job job) throws IOException {
+ DatadogTracerJobProperty