Skip to content
1 change: 1 addition & 0 deletions modules/ecs_fargate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ No modules.
| <a name="input_dd_is_datadog_dependency_enabled"></a> [dd\_is\_datadog\_dependency\_enabled](#input\_dd\_is\_datadog\_dependency\_enabled) | Whether the Datadog Agent container is a dependency for other containers | `bool` | `false` | no |
| <a name="input_dd_log_collection"></a> [dd\_log\_collection](#input\_dd\_log\_collection) | Configuration for Datadog Log Collection | <pre>object({<br/> enabled = optional(bool, false)<br/> fluentbit_config = optional(object({<br/> registry = optional(string, "public.ecr.aws/aws-observability/aws-for-fluent-bit")<br/> image_version = optional(string, "stable")<br/> cpu = optional(number)<br/> memory_limit_mib = optional(number)<br/> is_log_router_essential = optional(bool, false)<br/> is_log_router_dependency_enabled = optional(bool, false)<br/> environment = optional(list(object({<br/> name = string<br/> value = string<br/> })), [])<br/> log_router_health_check = optional(object({<br/> command = optional(list(string))<br/> interval = optional(number)<br/> retries = optional(number)<br/> start_period = optional(number)<br/> timeout = optional(number)<br/> }),<br/> {<br/> command = ["CMD-SHELL", "exit 0"]<br/> interval = 5<br/> retries = 3<br/> start_period = 15<br/> timeout = 5<br/> }<br/> )<br/> firelens_options = optional(object({<br/> config_file_type = optional(string)<br/> config_file_value = optional(string)<br/> }))<br/> log_driver_configuration = optional(object({<br/> host_endpoint = optional(string, "http-intake.logs.datadoghq.com")<br/> tls = optional(bool)<br/> compress = optional(string)<br/> service_name = optional(string)<br/> source_name = optional(string)<br/> message_key = optional(string)<br/> }),<br/> {<br/> host_endpoint = "http-intake.logs.datadoghq.com"<br/> }<br/> )<br/> mountPoints = optional(list(object({<br/> sourceVolume : string,<br/> containerPath : string,<br/> readOnly : bool<br/> })), [])<br/> dependsOn = optional(list(object({<br/> containerName : string,<br/> condition : string<br/> })), [])<br/> }),<br/> {<br/> fluentbit_config = {<br/> registry = "public.ecr.aws/aws-observability/aws-for-fluent-bit"<br/> image_version = "stable"<br/> log_driver_configuration = {<br/> host_endpoint = "http-intake.logs.datadoghq.com"<br/> }<br/> }<br/> }<br/> )<br/> })</pre> | <pre>{<br/> "enabled": false,<br/> "fluentbit_config": {<br/> "is_log_router_essential": false,<br/> "log_driver_configuration": {<br/> "host_endpoint": "http-intake.logs.datadoghq.com"<br/> }<br/> }<br/>}</pre> | no |
| <a name="input_dd_memory_limit_mib"></a> [dd\_memory\_limit\_mib](#input\_dd\_memory\_limit\_mib) | Datadog Agent container memory limit in MiB | `number` | `null` | no |
| <a name="input_dd_readonly_root_filesystem"></a> [dd\_readonly\_root\_filesystem](#input\_dd\_readonly\_root\_filesystem) | Datadog Agent container runs with read-only root filesystem enabled | `bool` | `false` | no |
| <a name="input_dd_registry"></a> [dd\_registry](#input\_dd\_registry) | Datadog Agent image registry | `string` | `"public.ecr.aws/datadog/agent"` | no |
| <a name="input_dd_service"></a> [dd\_service](#input\_dd\_service) | The task service name. Used for tagging (UST) | `string` | `null` | no |
| <a name="input_dd_site"></a> [dd\_site](#input\_dd\_site) | Datadog Site | `string` | `"datadoghq.com"` | no |
Expand Down
151 changes: 111 additions & 40 deletions modules/ecs_fargate/datadog.tf
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,27 @@ locals {
}
] : []

dd_agent_mount = concat(
local.apm_dsd_mount,
var.dd_readonly_root_filesystem ? [
{
containerPath = "/etc/datadog-agent"
sourceVolume = "agent-config"
readOnly = false
},
{
containerPath = "/tmp"
sourceVolume = "agent-tmp"
readOnly = false
},
{
containerPath = "/opt/datadog-agent/run"
sourceVolume = "agent-run"
readOnly = false
}
] : []
)

apm_socket_var = local.is_apm_socket_mount ? [
{
name = "DD_TRACE_AGENT_URL"
Expand Down Expand Up @@ -223,6 +244,18 @@ locals {
)
]

rofs_volumes = var.dd_readonly_root_filesystem ? [
{
name = "agent-config"
},
{
name = "agent-tmp"
},
{
name = "agent-run"
}
] : []

# Volume configuration for task
apm_dsd_volume = local.is_apm_dsd_volume ? [
{
Expand All @@ -238,6 +271,7 @@ locals {

modified_volumes = concat(
[for k, v in coalesce(var.volumes, []) : v],
local.rofs_volumes,
local.apm_dsd_volume,
local.cws_volume,
)
Expand All @@ -264,6 +298,10 @@ locals {
name = "DD_INSTALL_INFO_INSTALLER_VERSION"
value = local.install_info_installer_version
},
{
name = "DD_LOG_FILE"
value = "/opt/datadog-agent/run/logs"
}
]

dynamic_env = [
Expand Down Expand Up @@ -308,52 +346,85 @@ locals {
local.dd_environment,
)

dd_agent_dependency = concat(
var.dd_readonly_root_filesystem ? [
{
condition = "SUCCESS"
containerName = "init-volume"
}
] : [],
try(var.dd_log_collection.fluentbit_config.is_log_router_dependency_enabled, false) && local.dd_firelens_log_configuration != null ? local.log_router_dependency : [],
)

# Datadog Agent container definition
dd_agent_container = [
merge(
dd_agent_container = concat(
var.dd_readonly_root_filesystem ? [
{
name = "datadog-agent"
image = "${var.dd_registry}:${var.dd_image_version}"
essential = var.dd_essential
environment = local.dd_agent_env
dockerLabels = var.dd_docker_labels
cpu = var.dd_cpu
memory = var.dd_memory_limit_mib
secrets = var.dd_api_key_secret != null ? [
cpu = 0
memory = 128
name = "init-volume"
image = "${var.dd_registry}:${var.dd_image_version}"
essential = false
readOnlyRootFilesystem = true
command = ["/bin/sh", "-c", "cp -vnR /etc/datadog-agent/* /agent-config/ && exit 0"]
mountPoints = [
{
name = "DD_API_KEY"
valueFrom = var.dd_api_key_secret.arn
sourceVolume = "agent-config"
containerPath = "/agent-config"
readOnly = false
}
] : []
portMappings = [
{
containerPort = 8125
hostPort = 8125
protocol = "udp"
},
{
containerPort = 8126
hostPort = 8126
protocol = "tcp"
]
}
] : [],
[
merge(
{
name = "datadog-agent"
image = "${var.dd_registry}:${var.dd_image_version}"
essential = var.dd_essential
environment = local.dd_agent_env
dockerLabels = var.dd_docker_labels
cpu = var.dd_cpu
memory = var.dd_memory_limit_mib

readonlyRootFilesystem = var.dd_readonly_root_filesystem
secrets = var.dd_api_key_secret != null ? [
{
name = "DD_API_KEY"
valueFrom = var.dd_api_key_secret.arn
}
] : []
portMappings = [
{
containerPort = 8125
hostPort = 8125
protocol = "udp"
},
{
containerPort = 8126
hostPort = 8126
protocol = "tcp"
}
],

mountPoints = local.dd_agent_mount,
logConfiguration = local.dd_firelens_log_configuration,
dependsOn = local.dd_agent_dependency
systemControls = []
volumesFrom = []
},
try(var.dd_health_check.command == null, true) ? {} : {
healthCheck = {
command = var.dd_health_check.command
interval = var.dd_health_check.interval
timeout = var.dd_health_check.timeout
retries = var.dd_health_check.retries
startPeriod = var.dd_health_check.start_period
}
],
mountPoints = local.apm_dsd_mount,
logConfiguration = local.dd_firelens_log_configuration,
dependsOn = try(var.dd_log_collection.fluentbit_config.is_log_router_dependency_enabled, false) && local.dd_firelens_log_configuration != null ? local.log_router_dependency : [],
systemControls = []
volumesFrom = []
},
try(var.dd_health_check.command == null, true) ? {} : {
healthCheck = {
command = var.dd_health_check.command
interval = var.dd_health_check.interval
timeout = var.dd_health_check.timeout
retries = var.dd_health_check.retries
startPeriod = var.dd_health_check.start_period
}
}
)
]
)
]
)

dd_log_environment = var.dd_log_collection.fluentbit_config.environment != null ? var.dd_log_collection.fluentbit_config.environment : []

Expand Down
4 changes: 4 additions & 0 deletions modules/ecs_fargate/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ resource "aws_ecs_task_definition" "this" {
condition = var.dd_log_collection.enabled == false || (var.dd_log_collection.enabled == true && local.is_linux == true)
error_message = "Log collection is not supported on Windows. Please set `dd_log_collection.enabled` to `false`."
}
precondition {
condition = var.dd_readonly_root_filesystem == false || (var.dd_readonly_root_filesystem == true && local.is_linux == true)
error_message = "Readonly root filesystem is only supported on Linux. Please set `dd_readonly_root_filesystem` to `false`."
}
# Must provide only one of the two Datadog API key options
precondition {
condition = (var.dd_api_key == null && var.dd_api_key_secret != null) || (var.dd_api_key != null && var.dd_api_key_secret == null)
Expand Down
7 changes: 7 additions & 0 deletions modules/ecs_fargate/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ variable "dd_is_datadog_dependency_enabled" {
nullable = false
}

variable "dd_readonly_root_filesystem" {
description = "Datadog Agent container runs with read-only root filesystem enabled"
type = bool
default = false
nullable = false
}

variable "dd_health_check" {
description = "Datadog Agent health check configuration"
type = object({
Expand Down
1 change: 1 addition & 0 deletions smoke_tests/ecs_fargate/all-dd-disabled.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module "dd_task_all_dd_disabled" {
dd_tags = "team:cont-p, owner:container-monitoring"
dd_essential = true
dd_is_datadog_dependency_enabled = false
dd_readonly_root_filesystem = false

dd_environment = []

Expand Down
1 change: 1 addition & 0 deletions smoke_tests/ecs_fargate/all-dd-inputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module "dd_task_all_dd_inputs" {
dd_tags = "team:cont-p, owner:container-monitoring"
dd_essential = true
dd_is_datadog_dependency_enabled = true
dd_readonly_root_filesystem = true

dd_environment = [
{
Expand Down
2 changes: 2 additions & 0 deletions smoke_tests/ecs_fargate/all-windows.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module "dd_task_all_windows" {
dd_site = var.dd_site
dd_service = var.dd_service

dd_readonly_root_filesystem = false

dd_apm = {
enabled = true
}
Expand Down
2 changes: 2 additions & 0 deletions smoke_tests/ecs_fargate/apm-dsd-tcp-udp.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ module "dd_task_apm_dsd_tcp_udp" {
dd_tags = "team:cont-p, owner:container-monitoring"
dd_essential = true

dd_readonly_root_filesystem = true

dd_dogstatsd = {
enabled = true,
socket_enabled = false,
Expand Down
2 changes: 2 additions & 0 deletions smoke_tests/ecs_fargate/logging-only.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ module "dd_task_logging_only" {
dd_service = var.dd_service
dd_essential = true

dd_readonly_root_filesystem = false

dd_dogstatsd = {
enabled = false,
}
Expand Down
1 change: 1 addition & 0 deletions smoke_tests/ecs_fargate/ust-docker-labels.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module "dd_task_ust_docker_labels" {
dd_essential = true

dd_is_datadog_dependency_enabled = true
dd_readonly_root_filesystem = true

dd_log_collection = {
enabled = true,
Expand Down
3 changes: 1 addition & 2 deletions tests/all_dd_disabled_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ func (s *ECSFargateSuite) TestAllDDDisabled() {
s.Equal(int32(3), *agentContainer.HealthCheck.Retries, "Agent health check retries should be 3")
s.Equal(int32(60), *agentContainer.HealthCheck.StartPeriod, "Agent health check start period should be 60")

// Verify no mount points (apm/dsd volumes should not be present)
s.Equal(0, len(agentContainer.MountPoints), "Expected no mount points for datadog-agent when features are disabled")
s.Equal(0, len(agentContainer.MountPoints), "Expected no mount points when features are disabled")

// Test dummy container
dummyContainer, found := GetContainer(containers, "dummy-container")
Expand Down
9 changes: 8 additions & 1 deletion tests/all_dd_inputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ func (s *ECSFargateSuite) TestAllDDInputs() {

err := json.Unmarshal([]byte(task["container_definitions"]), &containers)
s.NoError(err, "Failed to parse container definitions")
s.Equal(6, len(containers), "Expected 6 containers in the task definition")
s.Equal(7, len(containers), "Expected 6 containers in the task definition")

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add an assertion here that confirms the presence of the relevant volumes and volumeMounts for ROFS?

initContainer, found := GetContainer(containers, "init-volume")
s.True(found, "Container init-volume not found in definitions")
AssertMountPoint(s.T(), initContainer, MountInitVolume)

// Test Agent Container
agentContainer, found := GetContainer(containers, "datadog-agent")
Expand All @@ -37,6 +41,9 @@ func (s *ECSFargateSuite) TestAllDDInputs() {
AssertPortMapping(s.T(), agentContainer, PortUDP)
AssertPortMapping(s.T(), agentContainer, PortTCP)
AssertMountPoint(s.T(), agentContainer, MountDdSocket)
AssertMountPoint(s.T(), agentContainer, MountAgentConfig)
AssertMountPoint(s.T(), agentContainer, MountAgentTmp)
AssertMountPoint(s.T(), agentContainer, MountAgentRun)
AssertContainerDependency(s.T(), agentContainer, DependencyLogRouter)

expectedAgentEnvvars := map[string]string{
Expand Down
9 changes: 6 additions & 3 deletions tests/apm_dsd_tcp_udp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (s *ECSFargateSuite) TestApmDsdTcpUdp() {

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

// Test Agent Container
agentContainer, found := GetContainer(containers, "datadog-agent")
Expand Down Expand Up @@ -60,8 +60,11 @@ func (s *ECSFargateSuite) TestApmDsdTcpUdp() {
}
AssertNotEnvVars(s.T(), agentContainer, disabledSocketEnvVars)

// Verify no mount points (as sockets are not used)
s.Equal(0, len(agentContainer.MountPoints), "Expected no mount points for datadog-agent when socket is disabled")
s.Equal(3, len(agentContainer.MountPoints), "Expected 3 mount points when socket is disabled")
// Verify none are apm/dsd volume mount points
for _, mountPoint := range agentContainer.MountPoints {
s.NotEqual("dd-socket", *mountPoint.SourceVolume, "Mount point should not be dd-socket")
}

// Test DogStatsD App Container
dogstatsdContainer, found := GetContainer(containers, "datadog-dogstatsd-app")
Expand Down
3 changes: 1 addition & 2 deletions tests/logging_only_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ func (s *ECSFargateSuite) TestLoggingOnly() {
s.Equal("datadog-log-router", *agentContainer.DependsOn[0].ContainerName, "Agent should depend on datadog-log-router")
s.Equal(types.ContainerConditionHealthy, agentContainer.DependsOn[0].Condition, "Agent should depend on log router being healthy")

// Verify no mount points
s.Equal(0, len(agentContainer.MountPoints), "Expected no mount points for datadog-agent")
s.Equal(0, len(agentContainer.MountPoints), "Expected 2 mount points for datadog-agent")

// Test Log Router Container
logRouterContainer, found := GetContainer(containers, "datadog-log-router")
Expand Down
2 changes: 1 addition & 1 deletion tests/ust_docker_labels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (s *ECSFargateSuite) TestUSTDockerLabels() {

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

// Expected UST docker labels that should be present on all application containers
expectedUSTLabels := map[string]string{
Expand Down
4 changes: 4 additions & 0 deletions tests/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
var (
MountDdSocket = types.MountPoint{SourceVolume: aws.String("dd-sockets"), ContainerPath: aws.String("/var/run/datadog"), ReadOnly: aws.Bool(false)}
MountCWS = types.MountPoint{SourceVolume: aws.String("cws-instrumentation-volume"), ContainerPath: aws.String("/cws-instrumentation-volume"), ReadOnly: aws.Bool(false)}
MountInitVolume = types.MountPoint{SourceVolume: aws.String("agent-config"), ContainerPath: aws.String("/agent-config"), ReadOnly: aws.Bool(false)}
MountAgentConfig = types.MountPoint{SourceVolume: aws.String("agent-config"), ContainerPath: aws.String("/etc/datadog-agent"), ReadOnly: aws.Bool(false)}
MountAgentTmp = types.MountPoint{SourceVolume: aws.String("agent-tmp"), ContainerPath: aws.String("/tmp"), ReadOnly: aws.Bool(false)}
MountAgentRun = types.MountPoint{SourceVolume: aws.String("agent-run"), ContainerPath: aws.String("/opt/datadog-agent/run"), ReadOnly: aws.Bool(false)}
PortTCP = types.PortMapping{ContainerPort: aws.Int32(8126), HostPort: aws.Int32(8126), Protocol: types.TransportProtocolTcp}
PortUDP = types.PortMapping{ContainerPort: aws.Int32(8125), HostPort: aws.Int32(8125), Protocol: types.TransportProtocolUdp}
DependencyAgent = types.ContainerDependency{ContainerName: aws.String("datadog-agent"), Condition: types.ContainerConditionHealthy}
Expand Down