Skip to content

Commit

Permalink
feat(opentelemetry): implement unified service tagging
Browse files Browse the repository at this point in the history
Signed-off-by: Wassim DHIF <[email protected]>
  • Loading branch information
wdhif committed May 30, 2024
1 parent 78e4822 commit 1b68351
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 12 deletions.
34 changes: 34 additions & 0 deletions comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ const (
envVarVersion = "DD_VERSION"
envVarService = "DD_SERVICE"

// OpenTelemetry SDK - Environment variables
// https://opentelemetry.io/docs/languages/sdk-configuration/general
// https://opentelemetry.io/docs/specs/semconv/resource/
envVarOtelService = "OTEL_SERVICE_NAME"
envVarOtelResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES"

// Docker label keys
dockerLabelEnv = "com.datadoghq.tags.env"
dockerLabelVersion = "com.datadoghq.tags.version"
Expand All @@ -66,6 +72,16 @@ var (
envVarService: tagKeyService,
}

otelStandardEnvKeys = map[string]string{
envVarOtelService: tagKeyService,
}

otelResourceAttributesMapping = map[string]string{
"service.name": tagKeyService,
"service.version": tagKeyVersion,
"deployment.environment": tagKeyEnv,
}

lowCardOrchestratorEnvKeys = map[string]string{
"MARATHON_APP_ID": "marathon_app",

Expand Down Expand Up @@ -212,6 +228,9 @@ func (c *WorkloadMetaCollector) handleContainer(ev workloadmeta.Event) []*types.
// standard tags from environment
c.extractFromMapWithFn(container.EnvVars, standardEnvKeys, tags.AddStandard)

// standard tags in OpenTelemetry SDK format from environment
c.addOpenTelemetryStandardTags(container, tags)

// orchestrator tags from environment
c.extractFromMapWithFn(container.EnvVars, lowCardOrchestratorEnvKeys, tags.AddLow)
c.extractFromMapWithFn(container.EnvVars, orchCardOrchestratorEnvKeys, tags.AddOrchestrator)
Expand Down Expand Up @@ -650,6 +669,9 @@ func (c *WorkloadMetaCollector) extractTagsFromPodContainer(pod *workloadmeta.Ku
// enrich with standard tags from environment variables
c.extractFromMapWithFn(container.EnvVars, standardEnvKeys, tags.AddStandard)

// standard tags in OpenTelemetry SDK format from environment
c.addOpenTelemetryStandardTags(container, tags)

// container-specific tags provided through pod annotation
annotation := fmt.Sprintf(podContainerTagsAnnotationFormat, containerName)
c.extractTagsFromJSONInMap(annotation, pod.Annotations, tags)
Expand Down Expand Up @@ -743,6 +765,18 @@ func (c *WorkloadMetaCollector) extractTagsFromJSONInMap(key string, input map[s
}
}

func (c *WorkloadMetaCollector) addOpenTelemetryStandardTags(container *workloadmeta.Container, tags *taglist.TagList) {
if otelResourceAttributes, ok := container.EnvVars[envVarOtelResourceAttributes]; ok {
for _, pair := range strings.Split(otelResourceAttributes, ",") {
fields := strings.SplitN(pair, "=", 2)
if tag, ok := otelResourceAttributesMapping[fields[0]]; ok {
tags.AddStandard(tag, fields[1])
}
}
}
c.extractFromMapWithFn(container.EnvVars, otelStandardEnvKeys, tags.AddStandard)
}

func buildTaggerEntityID(entityID workloadmeta.EntityID) string {
switch entityID.Kind {
case workloadmeta.KindContainer:
Expand Down
102 changes: 102 additions & 0 deletions comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
func TestHandleKubePod(t *testing.T) {
const (
fullyFleshedContainerID = "foobarquux"
otelEnvContainerID = "otelcontainer"
noEnvContainerID = "foobarbaz"
containerName = "agent"
runtimeContainerName = "k8s_datadog-agent_agent"
Expand Down Expand Up @@ -84,6 +85,20 @@ func TestHandleKubePod(t *testing.T) {
"DD_VERSION": version,
},
})
store.Set(&workloadmeta.Container{
EntityID: workloadmeta.EntityID{
Kind: workloadmeta.KindContainer,
ID: otelEnvContainerID,
},
EntityMeta: workloadmeta.EntityMeta{
Name: runtimeContainerName,
},
Image: image,
EnvVars: map[string]string{
"OTEL_SERVICE_NAME": svc,
"OTEL_RESOURCE_ATTRIBUTES": fmt.Sprintf("service.name=%s,service.version=%s,deployment.environment=%s", svc, version, env),
},
})
store.Set(&workloadmeta.Container{
EntityID: workloadmeta.EntityID{
Kind: workloadmeta.KindContainer,
Expand Down Expand Up @@ -279,6 +294,57 @@ func TestHandleKubePod(t *testing.T) {
},
},
},
{
name: "pod with fully formed container, standard tags from env with opentelemetry sdk",
pod: workloadmeta.KubernetesPod{
EntityID: podEntityID,
EntityMeta: workloadmeta.EntityMeta{
Name: podName,
Namespace: podNamespace,
},
Containers: []workloadmeta.OrchestratorContainer{
{
ID: otelEnvContainerID,
Name: containerName,
Image: image,
},
},
},
expected: []*types.TagInfo{
{
Source: podSource,
Entity: podTaggerEntityID,
HighCardTags: []string{},
OrchestratorCardTags: []string{
fmt.Sprintf("pod_name:%s", podName),
},
LowCardTags: []string{
fmt.Sprintf("kube_namespace:%s", podNamespace),
},
StandardTags: []string{},
},
{
Source: podSource,
Entity: fmt.Sprintf("container_id://%s", otelEnvContainerID),
HighCardTags: []string{
fmt.Sprintf("container_id:%s", otelEnvContainerID),
fmt.Sprintf("display_container_name:%s_%s", runtimeContainerName, podName),
},
OrchestratorCardTags: []string{
fmt.Sprintf("pod_name:%s", podName),
},
LowCardTags: append([]string{
fmt.Sprintf("kube_namespace:%s", podNamespace),
fmt.Sprintf("kube_container_name:%s", containerName),
"image_id:datadog/agent@sha256:a63d3f66fb2f69d955d4f2ca0b229385537a77872ffc04290acae65aed5317d2",
"image_name:datadog/agent",
"image_tag:latest",
"short_image:agent",
}, standardTags...),
StandardTags: standardTags,
},
},
},
{
name: "pod with container, standard tags from labels",
pod: workloadmeta.KubernetesPod{
Expand Down Expand Up @@ -990,6 +1056,42 @@ func TestHandleContainer(t *testing.T) {
},
},
},
{
name: "tags from environment with opentelemetry sdk",
container: workloadmeta.Container{
EntityID: entityID,
EntityMeta: workloadmeta.EntityMeta{
Name: containerName,
},
EnvVars: map[string]string{
// env as tags
"TEAM": "container-integrations",
"TIER": "node",

// otel standard tags
"OTEL_SERVICE_NAME": svc,
"OTEL_RESOURCE_ATTRIBUTES": fmt.Sprintf("service.name=%s,service.version=%s,deployment.environment=%s", svc, version, env),
},
},
envAsTags: map[string]string{
"team": "owner_team",
},
expected: []*types.TagInfo{
{
Source: containerSource,
Entity: taggerEntityID,
HighCardTags: []string{
fmt.Sprintf("container_name:%s", containerName),
fmt.Sprintf("container_id:%s", entityID.ID),
},
OrchestratorCardTags: []string{},
LowCardTags: append([]string{
"owner_team:container-integrations",
}, standardTags...),
StandardTags: standardTags,
},
},
},
{
name: "tags from labels",
container: workloadmeta.Container{
Expand Down
25 changes: 13 additions & 12 deletions pkg/util/containers/env_vars_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@ import (

var (
defaultEnvVarsIncludeList = []string{
"DD_ENV",
"DD_VERSION",
"DD_SERVICE",
"CHRONOS_JOB_NAME",
"CHRONOS_JOB_OWNER",
"NOMAD_TASK_NAME",
"NOMAD_JOB_NAME",
"NOMAD_GROUP_NAME",
"NOMAD_NAMESPACE",
"NOMAD_DC",
"MESOS_TASK_ID",
"DD_ENV",
"DD_SERVICE",
"DD_VERSION",
"DOCKER_DD_AGENT", // included to be able to detect agent containers
"ECS_CONTAINER_METADATA_URI",
"ECS_CONTAINER_METADATA_URI_V4",
"DOCKER_DD_AGENT", // included to be able to detect agent containers
// Included to ease unit tests without requiring a mock
"TEST_ENV",
"MESOS_TASK_ID",
"NOMAD_DC",
"NOMAD_GROUP_NAME",
"NOMAD_JOB_NAME",
"NOMAD_NAMESPACE",
"NOMAD_TASK_NAME",
"OTEL_RESOURCE_ATTRIBUTES",
"OTEL_SERVICE_NAME",
"TEST_ENV", // Included to ease unit tests without requiring a mock
}

envFilterOnce sync.Once
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Each section from every release note are combined when the
# CHANGELOG.rst is rendered. So the text needs to be worded so that
# it does not depend on any information only available in another
# section. This may mean repeating some details, but each section
# must be readable independently of the other.
#
# Each section note must be formatted as reStructuredText.
---
features:
- |
Implement OpenTelemetry SDK Resource Attributes as Unified Service Tags.

0 comments on commit 1b68351

Please sign in to comment.