From fa1756287544b55d98ffefd6724de6fbab11f304 Mon Sep 17 00:00:00 2001 From: Nikita Tkachenko Date: Thu, 4 Jan 2024 19:23:17 +0100 Subject: [PATCH] Support declarative Test Visibility configuration in pipeline scripts --- README.md | 42 ++++++++++- .../plugins/datadog/steps/DatadogOptions.java | 24 ++++++- .../datadog/steps/DatadogPipelineAction.java | 8 ++- .../plugins/datadog/steps/TestVisibility.java | 56 +++++++++++++++ .../datadog/tracer/TracerInjectionIT.java | 70 +++++++++++++++---- .../test-maven-pipeline-with-datadog-step.txt | 15 ++++ .../datadog/tracer/test-maven-pipeline.txt | 12 ++++ 7 files changed, 208 insertions(+), 19 deletions(-) create mode 100644 src/main/java/org/datadog/jenkins/plugins/datadog/steps/TestVisibility.java create mode 100644 src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline-with-datadog-step.txt create mode 100644 src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline.txt diff --git a/README.md b/README.md index 1267bcd87..b34c8d2f2 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,47 @@ The plugin can automatically configure Datadog tags = new ArrayList(); + private List tags = new ArrayList<>(); + private TestVisibility testVisibility; /** Constructor. */ @DataBoundConstructor @@ -47,6 +50,15 @@ public void setCollectLogs(boolean collectLogs) { this.collectLogs = collectLogs; } + public TestVisibility getTestVisibility() { + return testVisibility; + } + + @DataBoundSetter + public void setTestVisibility(TestVisibility testVisibility) { + this.testVisibility = testVisibility; + } + public List getTags() { return tags; } @@ -58,7 +70,7 @@ public void setTags(List tags) { @Override public StepExecution start(StepContext context) { - DatadogPipelineAction action = new DatadogPipelineAction(this.collectLogs, this.tags); + DatadogPipelineAction action = new DatadogPipelineAction(this.collectLogs, this.tags, this.testVisibility); return new ExecutionImpl(context, action); } @@ -90,6 +102,13 @@ public boolean start() throws Exception { } else { taskLogger.println("You already defined a datadog step"); } + + TestVisibility testVisibility = action.getTestVisibility(); + if (testVisibility != null) { + Job job = run.getParent(); + job.addProperty(new DatadogTracerJobProperty<>(testVisibility.getEnabled(), testVisibility.getServiceName(), testVisibility.getLanguages(), testVisibility.getAdditionalVariables())); + } + BodyInvoker invoker = context.newBodyInvoker().withCallback(BodyExecutionCallback.wrap(context)); if (this.action.isCollectLogs()) { if (DatadogUtilities.getDatadogGlobalDescriptor().isCollectBuildLogs()) { @@ -110,7 +129,6 @@ public void stop(@Nonnull Throwable cause) throws Exception { StepContext context = getContext(); context.get(TaskListener.class).getLogger().println("Stop DatadogStep"); context.get(TaskListener.class).getLogger().println(cause.getMessage()); - } } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/steps/DatadogPipelineAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/steps/DatadogPipelineAction.java index 17eed0bbd..9aa17b4ea 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/steps/DatadogPipelineAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/steps/DatadogPipelineAction.java @@ -10,10 +10,12 @@ public class DatadogPipelineAction implements Action, Serializable { private boolean collectLogs; private List tags; + private TestVisibility testVisibility; - public DatadogPipelineAction(boolean collectLogs, List tags) { + public DatadogPipelineAction(boolean collectLogs, List tags, TestVisibility testVisibility) { this.collectLogs = collectLogs; this.tags = tags; + this.testVisibility = testVisibility; } public List getTags() { @@ -24,6 +26,10 @@ public boolean isCollectLogs() { return collectLogs; } + public TestVisibility getTestVisibility() { + return testVisibility; + } + @CheckForNull @Override public String getIconFileName() { diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/steps/TestVisibility.java b/src/main/java/org/datadog/jenkins/plugins/datadog/steps/TestVisibility.java new file mode 100644 index 000000000..411153aae --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/steps/TestVisibility.java @@ -0,0 +1,56 @@ +package org.datadog.jenkins.plugins.datadog.steps; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import org.datadog.jenkins.plugins.datadog.tracer.TracerLanguage; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; + +public class TestVisibility implements Serializable { + private boolean enabled; + private String serviceName; + private Collection languages = Collections.emptyList(); + private Map additionalVariables = Collections.emptyMap(); + + @DataBoundConstructor + public TestVisibility() { + } + + public boolean getEnabled() { + return enabled; + } + + @DataBoundSetter + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getServiceName() { + return serviceName; + } + + @DataBoundSetter + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public Collection getLanguages() { + return languages; + } + + @DataBoundSetter + public void setLanguages(Collection languages) { + this.languages = languages; + } + + public Map getAdditionalVariables() { + return additionalVariables; + } + + @DataBoundSetter + public void setAdditionalVariables(Map additionalVariables) { + this.additionalVariables = additionalVariables; + } +} 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 index baae85bb6..58632ea57 100644 --- a/src/test/java/org/datadog/jenkins/plugins/datadog/tracer/TracerInjectionIT.java +++ b/src/test/java/org/datadog/jenkins/plugins/datadog/tracer/TracerInjectionIT.java @@ -9,13 +9,21 @@ import hudson.tasks.Shell; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.URL; +import java.nio.charset.Charset; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.io.IOUtils; 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.jetbrains.annotations.NotNull; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -26,7 +34,7 @@ public class TracerInjectionIT { - // "mvn" script does not quote MAVEN_OPTS properly, this is outside of our control + // "mvn" script does not quote MAVEN_OPTS properly, this is outside our control @ClassRule public static TestRule noSpaceInTmpDirs = FlagRule.systemProperty("jenkins.test.noSpaceInTmpDirs", "true"); @@ -100,6 +108,18 @@ public void testTracerInjectionInPipelineExecutedOnAgentNode() throws Exception } } + @Test + public void testTracerInjectionViaPipelineStep() throws Exception { + WorkflowJob pipeline = givenPipelineProjectWithTracerEnabledStep(); + try { + givenPipelineIsAMavenBuild(pipeline, false); + WorkflowRun build = whenRunningBuild(pipeline); + thenTracerIsInjected(build); + } finally { + pipeline.delete(); + } + } + private FreeStyleProject givenFreestyleProject() throws IOException { return jenkinsRule.createFreeStyleProject("freestyleProject"); } @@ -113,23 +133,45 @@ private WorkflowJob givenPipelineProjectBuiltOnAgentNode() throws Exception { } private WorkflowJob givenPipelineProject(boolean builtOnAgentNode) throws Exception { + Map replacements = new HashMap<>(); + replacements.put("AGENT_LABEL", builtOnAgentNode ? agentNode.getSelfLabel().getName() : "built-in"); + replacements.put("PIPELINE_STEPS", getMavenCommand()); + + String pipelineDefinition = buildPipelineDefinition("test-maven-pipeline.txt", replacements); 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)); + job.setDefinition(new CpsFlowDefinition(pipelineDefinition, true)); return job; } + private WorkflowJob givenPipelineProjectWithTracerEnabledStep() throws Exception { + Map replacements = new HashMap<>(); + replacements.put("PIPELINE_STEPS", getMavenCommand()); + + String pipelineDefinition = buildPipelineDefinition("test-maven-pipeline-with-datadog-step.txt", replacements); + WorkflowJob job = jenkinsRule.jenkins.createProject(WorkflowJob.class, "pipelineProject"); + job.setDefinition(new CpsFlowDefinition(pipelineDefinition, true)); + return job; + } + + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$([A-Z_]+)"); + + @NotNull + private static String buildPipelineDefinition(String pipelineName, Map replacements) throws IOException { + String pipelineDefinition; + try (InputStream is = TracerInjectionIT.class.getResourceAsStream(pipelineName)) { + StringBuffer pipelineBuilder = new StringBuffer(); + String pipelineTemplate = IOUtils.toString(is, Charset.defaultCharset()); + Matcher m = PLACEHOLDER_PATTERN.matcher(pipelineTemplate); + while (m.find()) { + String placeholder = m.group(1); + m.appendReplacement(pipelineBuilder, replacements.get(placeholder)); + } + m.appendTail(pipelineBuilder); + pipelineDefinition = pipelineBuilder.toString(); + } + return pipelineDefinition; + } + private String getMavenCommand() { return isRunningOnWindows() ? "bat \"./mvnw.cmd clean\"" diff --git a/src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline-with-datadog-step.txt b/src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline-with-datadog-step.txt new file mode 100644 index 000000000..bb58ec600 --- /dev/null +++ b/src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline-with-datadog-step.txt @@ -0,0 +1,15 @@ +pipeline { + agent { + label 'built-in' + } + options { + datadog(testVisibility: [ enabled: true, serviceName: "my-service", languages: ["JAVA"], additionalVariables: ["my-var": "value"] ]) + } + stages { + stage('test') { + steps { + $PIPELINE_STEPS + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline.txt b/src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline.txt new file mode 100644 index 000000000..065ab8339 --- /dev/null +++ b/src/test/resources/org/datadog/jenkins/plugins/datadog/tracer/test-maven-pipeline.txt @@ -0,0 +1,12 @@ +pipeline { + agent { + label '$AGENT_LABEL' + } + stages { + stage('test') { + steps { + $PIPELINE_STEPS + } + } + } +} \ No newline at end of file