Skip to content
Draft
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
5 changes: 4 additions & 1 deletion examples/ecs_fargate/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ module "datadog_ecs_fargate_task" {
# Configure Datadog
dd_api_key = var.dd_api_key
dd_site = var.dd_site
dd_service = var.dd_service
dd_tags = "team:cont-p, owner:container-monitoring"
dd_essential = true
dd_is_datadog_dependency_enabled = true

dd_service = var.dd_service
dd_env = var.dd_env
dd_version = var.dd_version

dd_environment = [
{
name = "DD_CUSTOM_FEATURE",
Expand Down
14 changes: 13 additions & 1 deletion examples/ecs_fargate/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,19 @@ variable "dd_api_key_secret_arn" {
}

variable "dd_service" {
description = "Service name for resource filtering in Datadog"
description = "The service name for resource filtering and UST tagging in Datadog"
type = string
default = null
}

variable "dd_env" {
description = "The environment for resource filtering and UST tagging in Datadog"
type = string
default = null
}

variable "dd_version" {
description = "The version for resource filtering and UST tagging in Datadog"
type = string
default = null
}
Expand Down
1 change: 1 addition & 0 deletions modules/ecs_fargate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ No modules.
| <a name="input_dd_cluster_name"></a> [dd\_cluster\_name](#input\_dd\_cluster\_name) | Datadog cluster name | `string` | `null` | no |
| <a name="input_dd_cpu"></a> [dd\_cpu](#input\_dd\_cpu) | Datadog Agent container CPU units | `number` | `null` | no |
| <a name="input_dd_cws"></a> [dd\_cws](#input\_dd\_cws) | Configuration for Datadog Cloud Workload Security (CWS) | <pre>object({<br/> enabled = optional(bool, false)<br/> cpu = optional(number)<br/> memory_limit_mib = optional(number)<br/> })</pre> | <pre>{<br/> "enabled": false<br/>}</pre> | no |
| <a name="input_dd_docker_labels"></a> [dd\_docker\_labels](#input\_dd\_docker\_labels) | Datadog Agent container docker labels | `map(string)` | `{}` | no |
| <a name="input_dd_dogstatsd"></a> [dd\_dogstatsd](#input\_dd\_dogstatsd) | Configuration for Datadog DogStatsD | <pre>object({<br/> enabled = optional(bool, true)<br/> origin_detection_enabled = optional(bool, true)<br/> dogstatsd_cardinality = optional(string, "orchestrator")<br/> socket_enabled = optional(bool, true)<br/> })</pre> | <pre>{<br/> "dogstatsd_cardinality": "orchestrator",<br/> "enabled": true,<br/> "origin_detection_enabled": true,<br/> "socket_enabled": true<br/>}</pre> | no |
| <a name="input_dd_env"></a> [dd\_env](#input\_dd\_env) | The task environment name. Used for tagging (UST) | `string` | `null` | no |
| <a name="input_dd_environment"></a> [dd\_environment](#input\_dd\_environment) | Datadog Agent container environment variables. Highest precedence and overwrites other environment variables defined by the module. For example, `dd_environment = [ { name = 'DD_VAR', value = 'DD_VAL' } ]` | `list(map(string))` | <pre>[<br/> {}<br/>]</pre> | no |
Expand Down
38 changes: 32 additions & 6 deletions modules/ecs_fargate/datadog.tf
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ locals {
] : [],
)

ust_docker_labels = merge(
var.dd_env != null ? {
"com.datadoghq.tags.env" = var.dd_env
} : {},
var.dd_service != null ? {
"com.datadoghq.tags.service" = var.dd_service
} : {},
var.dd_version != null ? {
"com.datadoghq.tags.version" = var.dd_version
} : {},
)

application_env_vars = concat(
var.dd_apm.profiling != null ? [
{
Expand Down Expand Up @@ -169,6 +181,12 @@ locals {
local.ust_env_vars,
local.application_env_vars,
),
# Merge UST docker labels with any existing docker labels.
dockerLabels = merge(
local.ust_docker_labels,
// Placing this after local.ust_docker_labels ensures user defined UST labels are not overwritten.
lookup(container, "dockerLabels", {}),
),
# Append new volume mounts to any existing mountPoints.
mountPoints = concat(
lookup(container, "mountPoints", []),
Expand Down Expand Up @@ -292,16 +310,22 @@ locals {
local.dd_environment,
)

dd_agent_docker_labels = merge(
local.ust_docker_labels,
var.dd_docker_labels,
)

# Datadog Agent container definition
dd_agent_container = [
merge(
{
name = "datadog-agent"
image = "${var.dd_registry}:${var.dd_image_version}"
essential = var.dd_essential
environment = local.dd_agent_env
cpu = var.dd_cpu
memory = var.dd_memory_limit_mib
name = "datadog-agent"
image = "${var.dd_registry}:${var.dd_image_version}"
essential = var.dd_essential
environment = local.dd_agent_env
dockerLabels = local.dd_agent_docker_labels
cpu = var.dd_cpu
memory = var.dd_memory_limit_mib
secrets = var.dd_api_key_secret != null ? [
{
name = "DD_API_KEY"
Expand Down Expand Up @@ -367,6 +391,7 @@ locals {
user = "0"
mountPoints = var.dd_log_collection.fluentbit_config.mountPoints
environment = local.dd_log_agent_env
dockerLabels = local.dd_agent_docker_labels
portMappings = []
systemControls = []
volumesFrom = []
Expand Down Expand Up @@ -397,6 +422,7 @@ locals {
command = ["/cws-instrumentation", "setup", "--cws-volume-mount", "/cws-instrumentation-volume"]
mountPoints = local.cws_mount
environment = local.ust_env_vars
dockerLabels = local.dd_agent_docker_labels
portMappings = []
systemControls = []
volumesFrom = []
Expand Down
6 changes: 6 additions & 0 deletions modules/ecs_fargate/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ variable "dd_environment" {
nullable = false
}

variable "dd_docker_labels" {
description = "Datadog Agent container docker labels"
type = map(string)
default = {}
}

variable "dd_tags" {
description = "Datadog Agent global tags (eg. `key1:value1, key2:value2`)"
type = string
Expand Down
4 changes: 4 additions & 0 deletions smoke_tests/ecs_fargate/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ output "role-parsing-with-path" {
output "role-parsing-without-path" {
value = module.dd_task_role_parsing_without_path
}

output "ust-docker-labels" {
value = module.dd_task_ust_docker_labels
}
56 changes: 56 additions & 0 deletions smoke_tests/ecs_fargate/ust-docker-labels.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Unless explicitly stated otherwise all files in this repository are licensed
# under the Apache License Version 2.0.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2025-present Datadog, Inc.

################################################################################
# Task Definition: UST Docker Labels Test
################################################################################

module "dd_task_ust_docker_labels" {
source = "../../modules/ecs_fargate"

# Configure Datadog with UST tags
dd_api_key = var.dd_api_key
dd_site = var.dd_site
dd_service = "ust-test-service"
dd_env = "ust-test-env"
dd_version = "1.2.3"
dd_tags = "team:test"
dd_essential = true

dd_is_datadog_dependency_enabled = true

dd_log_collection = {
enabled = true,
}

dd_cws = {
enabled = true,
}

dd_docker_labels = {
"com.datadoghq.tags.service" : "docker-agent-service",
}

# Configure Task Definition with multiple containers
family = "${var.test_prefix}-ust-docker-labels"
container_definitions = jsonencode([
{
name = "dummy-app",
image = "nginx:latest",
essential = true,
},
{
name = "app-overwritten-ust",
image = "nginx:latest",
essential = false,
dockerLabels = {
"com.datadoghq.tags.service" : "overwritten_name",
"custom.label" = "custom-value"
}
}
])

requires_compatibilities = ["FARGATE"]
}
58 changes: 58 additions & 0 deletions tests/ust_docker_labels_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2025-present Datadog, Inc.

package test

import (
"encoding/json"
"log"

"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/gruntwork-io/terratest/modules/terraform"
)

// TestUSTDockerLabels tests that UST docker labels are propagated to all container definitions
// when dd_service, dd_env, and dd_version are set
func (s *ECSFargateSuite) TestUSTDockerLabels() {
log.Println("TestUSTDockerLabels: Running test...")

// Retrieve the task output for the "ust-docker-labels" module
var containers []types.ContainerDefinition
task := terraform.OutputMap(s.T(), s.terraformOptions, "ust-docker-labels")
s.Equal(s.testPrefix+"-ust-docker-labels", task["family"], "Unexpected task family name")

err := json.Unmarshal([]byte(task["container_definitions"]), &containers)
s.NoError(err, "Failed to parse container definitions")
s.Equal(5, len(containers), "Expected 4 containers in the task definition (3 app containers + 1 agent)")

// Expected UST docker labels that should be present on all application containers
expectedUSTLabels := map[string]string{
"com.datadoghq.tags.service": "ust-test-service",
"com.datadoghq.tags.env": "ust-test-env",
"com.datadoghq.tags.version": "1.2.3",
}

dummyApp, found := GetContainer(containers, "dummy-app")
s.True(found, "Container dummy-app not found in definitions")
AssertDockerLabels(s.T(), dummyApp, expectedUSTLabels)

// Expect UST docker labels to be present on all Datadog containers with
// overwritten labels when UST docker labels are specified.
datadogContainers := []string{"datadog-agent", "datadog-log-router", "cws-instrumentation-init"}
expectedUSTLabels["com.datadoghq.tags.service"] = "docker-agent-service"
for _, containerName := range datadogContainers {
container, found := GetContainer(containers, containerName)
s.True(found, "Container %s not found in definitions", containerName)
AssertDockerLabels(s.T(), container, expectedUSTLabels)
}

// Expect UST docker labels to be overwritten on application container if docker labels
// are specified in the container definition.
overwrittenLabels, found := GetContainer(containers, "app-overwritten-ust")
s.True(found, "Container app-overwritten-ust not found in definitions")
expectedUSTLabels["com.datadoghq.tags.service"] = "overwritten_name"
AssertDockerLabels(s.T(), overwrittenLabels, expectedUSTLabels)

}
11 changes: 11 additions & 0 deletions tests/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,14 @@ func AssertContainerDependency(t *testing.T, container types.ContainerDefinition
assert.True(t, found, "Expected dependency (container:%s, condition:%s) not found in %s container",
*expectedDependency.ContainerName, expectedDependency.Condition, *container.Name)
}

// AssertDockerLabels checks if the expected docker labels are all present in the container
func AssertDockerLabels(t *testing.T, container types.ContainerDefinition, expectedLabels map[string]string) {
assert.NotNil(t, container.Name, "Container name cannot be nil")

for key, expectedValue := range expectedLabels {
value, found := container.DockerLabels[key]
assert.True(t, found, "Docker label %s not found in %s container", key, *container.Name)
assert.Equal(t, expectedValue, value, "Docker label %s value does not match expected in %s container", key, *container.Name)
}
}