Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tags configured by the user to Jenkins traces #210

Merged
merged 1 commit into from
May 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,20 @@ public static Map<String, Set<String>> getBuildTags(Run run, EnvVars envVars) {

result = TagsUtil.merge(result, getTagsFromGlobalJobTags(jobName, globalJobTags));

result = TagsUtil.merge(result, getTagsFromPipelineAction(run));

return result;
}

/**
* Pipeline extraTags if any are configured in the Job from DatadogPipelineAction.
*
* @param run - Current build
* @return A {@link HashMap} containing the key,value pairs of tags if any.
*/
public static Map<String, Set<String>> getTagsFromPipelineAction(Run run) {
// pipeline defined tags
final Map<String, Set<String>> result = new HashMap<>();
DatadogPipelineAction action = run.getAction(DatadogPipelineAction.class);
if(action != null) {
List<String> pipelineTags = action.getTags();
Expand All @@ -163,6 +176,7 @@ public static Map<String, Set<String>> getBuildTags(Run run, EnvVars envVars) {
}
}
}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,17 @@ public Map<String, Set<String>> getTags() {
return allTags;
}

public Map<String, String> getTagsForTraces() {
Map<String, Set<String>> allTags = new HashMap<>();
try {
allTags = DatadogUtilities.getTagsFromGlobalTags();
} catch(NullPointerException e){
//noop
}
allTags = TagsUtil.merge(allTags, tags);
return TagsUtil.convertTagsToMapSingleValues(allTags);
}

public void setTags(Map<String, Set<String>> tags) {
this.tags = tags;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.datadog.jenkins.plugins.datadog.model;

import hudson.model.InvisibleAction;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

public class CIGlobalTagsAction extends InvisibleAction implements Serializable {

private final Map<String, String> tags;

public CIGlobalTagsAction(final Map<String, String> tags) {
this.tags = tags != null ? tags : new HashMap<>();
}

public Map<String, String> getTags() {
return tags;
}

public void putAll(Map<String, String> tags) {
this.tags.putAll(tags);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
import org.datadog.jenkins.plugins.datadog.DatadogUtilities;
import org.datadog.jenkins.plugins.datadog.model.BuildData;
import org.datadog.jenkins.plugins.datadog.model.BuildPipelineNode;
import org.datadog.jenkins.plugins.datadog.model.CIGlobalTagsAction;
import org.datadog.jenkins.plugins.datadog.model.PipelineNodeInfoAction;
import org.datadog.jenkins.plugins.datadog.model.PipelineQueueInfoAction;
import org.datadog.jenkins.plugins.datadog.model.StageBreakdownAction;
import org.datadog.jenkins.plugins.datadog.model.StageData;
import org.datadog.jenkins.plugins.datadog.steps.DatadogPipelineAction;
import org.datadog.jenkins.plugins.datadog.util.json.JsonUtils;

import java.util.ArrayList;
Expand Down Expand Up @@ -79,6 +81,9 @@ public void startBuildTrace(final BuildData buildData, Run run) {

final PipelineQueueInfoAction pipelineQueueInfoAction = new PipelineQueueInfoAction();
run.addAction(pipelineQueueInfoAction);

final CIGlobalTagsAction ciGlobalTags = new CIGlobalTagsAction(buildData.getTagsForTraces());
run.addAction(ciGlobalTags);
}

public void finishBuildTrace(final BuildData buildData, final Run<?,?> run) {
Expand Down Expand Up @@ -209,6 +214,15 @@ public void finishBuildTrace(final BuildData buildData, final Run<?,?> run) {
buildSpan.setTag(CITags.ERROR, true);
}

// CI Tags propagation
final CIGlobalTagsAction ciGlobalTagsAction = run.getAction(CIGlobalTagsAction.class);
if(ciGlobalTagsAction != null) {
final Map<String, String> tags = ciGlobalTagsAction.getTags();
for(Map.Entry<String, String> tagEntry : tags.entrySet()) {
buildSpan.setTag(tagEntry.getKey(), tagEntry.getValue());
}
}

// If the build is a Jenkins Pipeline, the queue time is included in the root span duration.
// We need to adjust the endTime of the root span subtracting the queue time reported by its child span.
// The propagated queue time is set DatadogTracePipelineLogic#updateBuildData method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import datadog.trace.api.DDTags;
import datadog.trace.api.interceptor.MutableSpan;
import hudson.EnvVars;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import io.opentracing.Span;
Expand All @@ -20,18 +19,18 @@
import org.datadog.jenkins.plugins.datadog.model.BuildData;
import org.datadog.jenkins.plugins.datadog.model.BuildPipeline;
import org.datadog.jenkins.plugins.datadog.model.BuildPipelineNode;
import org.datadog.jenkins.plugins.datadog.model.CIGlobalTagsAction;
import org.datadog.jenkins.plugins.datadog.model.GitCommitAction;
import org.datadog.jenkins.plugins.datadog.model.GitRepositoryAction;
import org.datadog.jenkins.plugins.datadog.model.PipelineNodeInfoAction;
import org.datadog.jenkins.plugins.datadog.model.StageBreakdownAction;
import org.datadog.jenkins.plugins.datadog.model.StageData;
import org.datadog.jenkins.plugins.datadog.util.TagsUtil;
import org.datadog.jenkins.plugins.datadog.util.git.GitUtils;
import org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode;
import org.jenkinsci.plugins.workflow.graph.BlockEndNode;
import org.jenkinsci.plugins.workflow.graph.BlockStartNode;
import org.jenkinsci.plugins.workflow.graph.FlowEndNode;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.graph.FlowStartNode;
import org.jenkinsci.plugins.workflow.graphanalysis.DepthFirstScanner;

import java.io.PrintWriter;
Expand Down Expand Up @@ -77,6 +76,7 @@ public void execute(Run run, FlowNode flowNode) {
final BuildPipelineNode pipelineNode = buildPipelineNode(flowNode);
updateStageBreakdown(run, pipelineNode);
updateBuildData(buildData, run, pipelineNode, flowNode);
updateCIGlobalTags(run);
return;
}

Expand All @@ -97,6 +97,7 @@ public void execute(Run run, FlowNode flowNode) {

final SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new BuildTextMapAdapter(buildSpanAction.getBuildSpanPropatation()));
final BuildPipelineNode root = pipeline.buildTree();

try {
sendTrace(tracer, run, buildData, root, spanContext);
} catch (Exception e){
Expand Down Expand Up @@ -364,6 +365,15 @@ private Map<String, Object> buildTraceTags(final Run run, final BuildPipelineNod
tags.put(BuildPipelineNode.NodeType.STAGE.getTagName() + CITags._NAME, current.getStageName());
}

// CI Tags propagation
final CIGlobalTagsAction ciGlobalTagsAction = run.getAction(CIGlobalTagsAction.class);
if(ciGlobalTagsAction != null) {
final Map<String, String> globalTags = ciGlobalTagsAction.getTags();
for(Map.Entry<String, String> globalTagEntry : globalTags.entrySet()) {
tags.put(globalTagEntry.getKey(), globalTagEntry.getValue());
}
}

return tags;
}

Expand Down Expand Up @@ -472,4 +482,14 @@ private void updateStageBreakdown(final Run<?,?> run, BuildPipelineNode pipeline

stageBreakdownAction.put(stageData.getName(), stageData);
}

private void updateCIGlobalTags(Run run) {
final CIGlobalTagsAction ciGlobalTagsAction = run.getAction(CIGlobalTagsAction.class);
if(ciGlobalTagsAction == null) {
return;
}

final Map<String, String> tags = TagsUtil.convertTagsToMapSingleValues(DatadogUtilities.getTagsFromPipelineAction(run));
ciGlobalTagsAction.putAll(tags);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ of this software and associated documentation files (the "Software"), to deal
package org.datadog.jenkins.plugins.datadog.util;

import net.sf.json.JSONArray;
import org.datadog.jenkins.plugins.datadog.model.BuildData;

import java.util.*;
import java.util.logging.Logger;

public class TagsUtil {

private static transient final Logger LOGGER = Logger.getLogger(TagsUtil.class.getName());

public static Map<String, Set<String>> merge(Map<String, Set<String>> dest, Map<String, Set<String>> orig) {
if (dest == null) {
dest = new HashMap<>();
Expand Down Expand Up @@ -99,4 +103,21 @@ public static Map<String,Set<String>> addTagToTags(Map<String, Set<String>> tags
tags.get(name).add(value);
return tags;
}

public static Map<String, String> convertTagsToMapSingleValues(Map<String, Set<String>> tags) {
if(tags == null) {
return Collections.emptyMap();
}

final Map<String, String> result = new HashMap<>();
for (Map.Entry<String, Set<String>> entry : tags.entrySet()) {
if(entry.getValue() != null && entry.getValue().size() == 1) {
result.put(entry.getKey(), entry.getValue().iterator().next());
} else {
LOGGER.warning("Unsupported multi-value tag in this context: Tag '"+ entry.getKey() + "' will be ignored.");
}
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public void beforeEach() throws IOException {
DatadogGlobalConfiguration cfg = DatadogUtilities.getDatadogGlobalDescriptor();
cfg.setCollectBuildTraces(true);
cfg.setTraceServiceName(SAMPLE_SERVICE_NAME);
cfg.setGlobalJobTags(null);
cfg.setGlobalTags(null);
EnvVars.masterEnvVars.remove("ENV_VAR");

clientStub = new DatadogClientStub();
ClientFactory.setTestClient(clientStub);
Expand Down Expand Up @@ -174,6 +177,28 @@ public void testTracesDisabled() throws Exception {
assertEquals(0, tracerWriter.size());
}

@Test
public void testCITagsOnTraces() throws Exception {
DatadogGlobalConfiguration cfg = DatadogUtilities.getDatadogGlobalDescriptor();
cfg.setGlobalJobTags("(.*?)_job, global_job_tag:$ENV_VAR");
cfg.setGlobalTags("global_tag:$ENV_VAR");
EnvVars.masterEnvVars.put("ENV_VAR", "value");

final FreeStyleProject project = jenkinsRule.createFreeStyleProject("buildIntegrationSuccessTags_job");
project.scheduleBuild2(0).get();

final ListWriter tracerWriter = clientStub.tracerWriter();
tracerWriter.waitForTraces(1);
assertEquals(1, tracerWriter.size());

final List<DDSpan> buildTrace = tracerWriter.get(0);
assertEquals(1, buildTrace.size());

final DDSpan buildSpan = buildTrace.get(0);
assertEquals("value", buildSpan.getTag("global_job_tag"));
assertEquals("value", buildSpan.getTag("global_tag"));
}


private void assertGitVariables(DDSpan span, String defaultBranch) {
assertEquals("Initial commit\n", span.getTag(CITags.GIT_COMMIT_MESSAGE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public void beforeEach() throws IOException {
DatadogGlobalConfiguration cfg = DatadogUtilities.getDatadogGlobalDescriptor();
cfg.setCollectBuildTraces(true);
cfg.setTraceServiceName(SAMPLE_SERVICE_NAME);
cfg.setGlobalJobTags(null);
cfg.setGlobalTags(null);
EnvVars.masterEnvVars.remove("ENV_VAR");

listener = new DatadogGraphListener();
clientStub = new DatadogClientStub();
Expand Down Expand Up @@ -713,6 +716,50 @@ public void testStagesNodeNames_complexPipelineStages01() throws Exception {
assertEquals(worker03.getNodeName(), ciStep.getTag(CITags.NODE_NAME));
}

@Test
public void testGlobalTagsPropagationsTraces() throws Exception {
DatadogGlobalConfiguration cfg = DatadogUtilities.getDatadogGlobalDescriptor();
cfg.setGlobalJobTags("(.*?)_job, global_job_tag:$ENV_VAR");
cfg.setGlobalTags("global_tag:$ENV_VAR");
EnvVars.masterEnvVars.put("ENV_VAR", "value");

jenkinsRule.createOnlineSlave(new LabelAtom("testGlobalTags"));
WorkflowJob job = jenkinsRule.jenkins.createProject(WorkflowJob.class, "pipelineIntegration-GlobalTagsPropagation_job");
String definition = IOUtils.toString(
this.getClass().getResourceAsStream("testPipelineGlobalTags.txt"),
"UTF-8"
);
job.setDefinition(new CpsFlowDefinition(definition, true));
job.scheduleBuild2(0).get();

//Traces
final ListWriter tracerWriter = clientStub.tracerWriter();
tracerWriter.waitForTraces(2);
assertEquals(2, tracerWriter.size());

final List<DDSpan> buildTrace = tracerWriter.get(0);
assertEquals(1, buildTrace.size());
final DDSpan buildSpan = buildTrace.get(0);
assertEquals("value", buildSpan.getTag("global_tag"));
assertEquals("value", buildSpan.getTag("global_job_tag"));
assertEquals("pipeline_tag_v2", buildSpan.getTag("pipeline_tag"));
assertEquals("pipeline_tag", buildSpan.getTag("pipeline_tag_v2"));

final List<DDSpan> pipelineTrace = tracerWriter.get(1);
assertEquals(2, pipelineTrace.size());
final DDSpan stageSpan = pipelineTrace.get(0);
assertEquals("value", stageSpan.getTag("global_tag"));
assertEquals("value", stageSpan.getTag("global_job_tag"));
assertEquals("pipeline_tag_v2", stageSpan.getTag("pipeline_tag"));
assertEquals("pipeline_tag", stageSpan.getTag("pipeline_tag_v2"));

final DDSpan stepSpan = pipelineTrace.get(1);
assertEquals("value", stepSpan.getTag("global_tag"));
assertEquals("value", stepSpan.getTag("global_job_tag"));
assertEquals("pipeline_tag_v2", stepSpan.getTag("pipeline_tag"));
assertEquals("pipeline_tag", stepSpan.getTag("pipeline_tag_v2"));
}

private void assertNodeNameParallelBlock(DDSpan stageSpan, DumbSlave worker01, DumbSlave worker02) {
switch ((String)stageSpan.getResourceName()){
case "Prepare01":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ of this software and associated documentation files (the "Software"), to deal
import org.junit.Assert;
import org.junit.Test;

import javax.xml.crypto.Data;
import java.util.*;

public class TagsUtilTest {
Expand Down Expand Up @@ -101,4 +102,28 @@ public void testMerge(){

}

@Test
public void testTagsToMapSingleValues() {
Map<String, String> emptyTags = TagsUtil.convertTagsToMapSingleValues(null);
Assert.assertTrue(emptyTags.isEmpty());

Map<String, Set<String>> singleValueTags = new HashMap<>();
DatadogClientStub.addTagToMap(singleValueTags, "tagKey1", "tagValue1");
DatadogClientStub.addTagToMap(singleValueTags, "tagKey2", "tagValue2");
Map<String, String> resultSingleValues = TagsUtil.convertTagsToMapSingleValues(singleValueTags);
Assert.assertEquals(2, resultSingleValues.size());
Assert.assertEquals("tagValue1", resultSingleValues.get("tagKey1"));
Assert.assertEquals("tagValue2", resultSingleValues.get("tagKey2"));


Map<String, Set<String>> multipleValueTags = new HashMap<>();
DatadogClientStub.addTagToMap(multipleValueTags, "tagKey1", "tagValue1");
DatadogClientStub.addTagToMap(multipleValueTags, "tagKey2", "tagValue2_1");
DatadogClientStub.addTagToMap(multipleValueTags, "tagKey2", "tagValue2_2");
Map<String, String> resultMultipleValues = TagsUtil.convertTagsToMapSingleValues(multipleValueTags);
Assert.assertEquals(1, resultMultipleValues.size());
Assert.assertEquals("tagValue1", resultMultipleValues.get("tagKey1"));
Assert.assertNull(resultMultipleValues.get("tagKey2"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
def DD_TEST = "pipeline_tag_v2"
pipeline {
agent {
label "testGlobalTags"
}
options {
datadog(collectLogs: false, tags: ["pipeline_tag:${DD_TEST}", "${DD_TEST}:pipeline_tag"])
}
stages {
stage('Stage'){
steps {
echo 'Done'
}
}
}
}