Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
02b1d05
Add flightrecorder feature to the Datadog Operator
misteriaud Mar 20, 2026
fc41564
Fix gci lint: import ordering and alignment
misteriaud Mar 20, 2026
84503e6
Fix gci formatting on envvar.go
misteriaud Mar 20, 2026
561f8b5
Set memory requests/limits for flightrecorder container
misteriaud Mar 20, 2026
6a41092
[flightrecorder] Trigger via DD_EXPERIMENTAL_FLIGHTRECORDER_ENABLED e…
misteriaud Mar 23, 2026
fac2e76
[generated] Update CRDs and RBAC via make generate manifests
misteriaud Mar 23, 2026
c8f8a3e
[generated] Fix controller-gen version and revert unrelated generated…
misteriaud Mar 23, 2026
244c289
fix missing char in api/datadoghq/v2alpha1/zz_generated.openapi.go
misteriaud Mar 23, 2026
bfb8402
[flightrecorder] Enable flightrecorder on trace-agent container
misteriaud Mar 26, 2026
1053acf
[flightrecorder] Use annotation instead of env var override to enable…
misteriaud Mar 27, 2026
b0f32f4
[flightrecorder] Move socket path under /var/run/datadog/
misteriaud Mar 27, 2026
0624737
[flightrecorder] Simplify socket path to /var/run/datadog/flightrecor…
misteriaud Mar 27, 2026
46916db
[flightrecorder] Use /var/run/datadog/flightrecorder/ subdirectory fo…
misteriaud Mar 27, 2026
a7cdd01
[flightrecorder] Set env vars on recorder container and clean up example
misteriaud Mar 27, 2026
cd4c65c
[flightrecorder] Move constants to appropriate packages
misteriaud Mar 30, 2026
8395e7a
[flightrecorder] Use volumeMounts helper and remove default resources
misteriaud Mar 30, 2026
3fd3a66
[flightrecorder] Register in AllAgentContainers for pod-wide overrides
misteriaud Mar 30, 2026
338ebe9
[flightrecorder] Remove example YAML while feature is experimental
misteriaud Mar 30, 2026
4eaa4ed
[flightrecorder] Fix gci lint: constant alignment
misteriaud Mar 30, 2026
0880bbe
Merge branch 'main' into misteriaud/flightrecorder-feature
tbavelier Mar 31, 2026
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
2 changes: 2 additions & 0 deletions api/datadoghq/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ const (
HostProfiler AgentContainerName = "host-profiler"
// AgentDataPlaneContainerName is the name of the Agent Data Plane container
AgentDataPlaneContainerName AgentContainerName = "agent-data-plane"
// FlightRecorderContainerName is the name of the Flight Recorder container
FlightRecorderContainerName AgentContainerName = "flightrecorder"
// PrivateActionRunnerContainerName is the name of the Private Action Runner container
PrivateActionRunnerContainerName AgentContainerName = "private-action-runner"
// AllContainers is used internally to reference all containers in the pod
Expand Down
5 changes: 5 additions & 0 deletions internal/controller/datadogagent/common/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ const (

RunPathVolumeName = "pointerdir"
RunPathVolumeMount = "/opt/datadog-agent/run"

FlightRecorderSocketVolumeName = "flightrecorder-socket"
FlightRecorderSocketPath = "/var/run/datadog/flightrecorder"
FlightRecorderDataVolumeName = "flightrecorder-data"
FlightRecorderDataPath = "/data/signals"
)

// Field paths
Expand Down
30 changes: 30 additions & 0 deletions internal/controller/datadogagent/component/agent/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,8 @@ func agentOptimizedContainers(dda metav1.Object, requiredContainers []apicommon.
containers = append(containers, hostProfilerContainer(dda))
case apicommon.AgentDataPlaneContainerName:
containers = append(containers, agentDataPlaneContainer(dda))
case apicommon.FlightRecorderContainerName:
containers = append(containers, flightRecorderContainer(dda))
}
}

Expand Down Expand Up @@ -611,6 +613,21 @@ func agentDataPlaneContainer(dda metav1.Object) corev1.Container {
}
}

func flightRecorderContainer(dda metav1.Object) corev1.Container {
return corev1.Container{
Name: string(apicommon.FlightRecorderContainerName),
Image: agentImage(),
Command: []string{
"/opt/datadog-agent/embedded/bin/flightrecorder",
},
Env: commonEnvVars(dda),
VolumeMounts: volumeMountsForFlightRecorder(),
SecurityContext: &corev1.SecurityContext{
ReadOnlyRootFilesystem: apiutils.NewBoolPointer(true),
},
}
}

func initVolumeContainer() corev1.Container {
return corev1.Container{
Name: "init-volume",
Expand Down Expand Up @@ -898,3 +915,16 @@ func volumeMountsForAgentDataPlane() []corev1.VolumeMount {
common.GetVolumeMountForTmp(),
}
}

func volumeMountsForFlightRecorder() []corev1.VolumeMount {
return []corev1.VolumeMount{
{
Name: common.FlightRecorderSocketVolumeName,
MountPath: common.FlightRecorderSocketPath,
},
{
Name: common.FlightRecorderDataVolumeName,
MountPath: common.FlightRecorderDataPath,
},
}
}
1 change: 1 addition & 0 deletions internal/controller/datadogagent/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
_ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/enabledefault"
_ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/eventcollection"
_ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/externalmetrics"
_ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/flightrecorder"
_ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/gpu"
_ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/helmcheck"
_ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/hostprofiler"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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 2016-present Datadog, Inc.

package flightrecorder

import "github.com/DataDog/datadog-operator/internal/controller/datadogagent/common"

const (
flightRecorderSocketFile = common.FlightRecorderSocketPath + "/flightrecorder.sock"

// Env var names for the flightrecorder feature
ddFlightRecorderEnabled = "DD_FLIGHTRECORDER_ENABLED"
ddFlightRecorderSocketPath = "DD_FLIGHTRECORDER_SOCKET_PATH"
ddFlightRecorderOutputDir = "DD_FLIGHTRECORDER_OUTPUT_DIR"
)
162 changes: 162 additions & 0 deletions internal/controller/datadogagent/feature/flightrecorder/feature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// 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 2016-present Datadog, Inc.

package flightrecorder

import (
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common"
"github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/common"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature"
featureutils "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/utils"
)

func init() {
err := feature.Register(feature.FlightRecorderIDType, buildFlightRecorderFeature)
if err != nil {
panic(err)
}
}

func buildFlightRecorderFeature(options *feature.Options) feature.Feature {
f := &flightRecorderFeature{}

if options != nil {
f.logger = options.Logger
}

return f
}

type flightRecorderFeature struct {
logger logr.Logger

enabled bool
}

// ID returns the ID of the Feature
func (f *flightRecorderFeature) ID() feature.IDType {
return feature.FlightRecorderIDType
}

// Configure is used to configure the feature from a v2alpha1.DatadogAgent instance.
// FlightRecorder is enabled by setting the annotation agent.datadoghq.com/flightrecorder-enabled: "true"
// on the DatadogAgent resource.
func (f *flightRecorderFeature) Configure(dda metav1.Object, _ *v2alpha1.DatadogAgentSpec, _ *v2alpha1.RemoteConfigConfiguration) feature.RequiredComponents {
f.enabled = featureutils.HasFeatureEnableAnnotation(dda, featureutils.EnableFlightRecorderAnnotation)

var reqComp feature.RequiredComponents

if f.enabled {
reqComp.Agent = feature.RequiredComponent{
IsRequired: &f.enabled,
Containers: []apicommon.AgentContainerName{apicommon.FlightRecorderContainerName},
}
}

return reqComp
}

// ManageDependencies allows a feature to manage its dependencies.
func (f *flightRecorderFeature) ManageDependencies(_ feature.ResourceManagers, _ string) error {
return nil
}

// ManageClusterAgent allows a feature to configure the ClusterAgent's corev1.PodTemplateSpec.
func (f *flightRecorderFeature) ManageClusterAgent(_ feature.PodTemplateManagers, _ string) error {
return nil
}

// ManageSingleContainerNodeAgent allows a feature to configure the Agent container for the Node Agent's corev1.PodTemplateSpec
// if SingleContainerStrategy is enabled and can be used with the configured feature set.
func (f *flightRecorderFeature) ManageSingleContainerNodeAgent(managers feature.PodTemplateManagers, provider string) error {
return f.ManageNodeAgent(managers, provider)
}

// ManageNodeAgent allows a feature to configure the Node Agent's corev1.PodTemplateSpec.
func (f *flightRecorderFeature) ManageNodeAgent(managers feature.PodTemplateManagers, _ string) error {
if !f.enabled {
return nil
}

// Enable flightrecorder and configure the socket path on the agent containers.
for _, container := range []apicommon.AgentContainerName{
apicommon.CoreAgentContainerName,
apicommon.TraceAgentContainerName,
} {
managers.EnvVar().AddEnvVarToContainer(container, &corev1.EnvVar{
Name: ddFlightRecorderEnabled,
Value: "true",
})
managers.EnvVar().AddEnvVarToContainer(container, &corev1.EnvVar{
Name: ddFlightRecorderSocketPath,
Value: flightRecorderSocketFile,
})
}

// Configure the flightrecorder container with the socket and output paths.
managers.EnvVar().AddEnvVarToContainer(apicommon.FlightRecorderContainerName, &corev1.EnvVar{
Name: ddFlightRecorderSocketPath,
Value: flightRecorderSocketFile,
})
managers.EnvVar().AddEnvVarToContainer(apicommon.FlightRecorderContainerName, &corev1.EnvVar{
Name: ddFlightRecorderOutputDir,
Value: common.FlightRecorderDataPath,
})

// Shared socket volume (emptyDir) for Unix socket communication between agent and flightrecorder.
socketVol := corev1.Volume{
Name: common.FlightRecorderSocketVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
}
socketVolMount := corev1.VolumeMount{
Name: common.FlightRecorderSocketVolumeName,
MountPath: common.FlightRecorderSocketPath,
}
managers.Volume().AddVolume(&socketVol)
managers.VolumeMount().AddVolumeMountToContainers(
&socketVolMount,
[]apicommon.AgentContainerName{
apicommon.CoreAgentContainerName,
apicommon.TraceAgentContainerName,
apicommon.FlightRecorderContainerName,
},
)

// Data volume for Parquet output files, mounted only on the flightrecorder container.
dataVol := corev1.Volume{
Name: common.FlightRecorderDataVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
}
dataVolMount := corev1.VolumeMount{
Name: common.FlightRecorderDataVolumeName,
MountPath: common.FlightRecorderDataPath,
}
managers.Volume().AddVolume(&dataVol)
managers.VolumeMount().AddVolumeMountToContainer(
&dataVolMount,
apicommon.FlightRecorderContainerName,
)

return nil
}

// ManageClusterChecksRunner allows a feature to configure the ClusterChecksRunner's corev1.PodTemplateSpec.
func (f *flightRecorderFeature) ManageClusterChecksRunner(_ feature.PodTemplateManagers, _ string) error {
return nil
}

// ManageOtelAgentGateway allows a feature to configure the OTel Agent Gateway's corev1.PodTemplateSpec.
func (f *flightRecorderFeature) ManageOtelAgentGateway(_ feature.PodTemplateManagers, _ string) error {
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// 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 2016-present Datadog, Inc.

package flightrecorder

import (
"testing"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"

apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/common"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/fake"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/test"
featureutils "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/utils"
"github.com/DataDog/datadog-operator/pkg/testutils"
)

func Test_flightRecorderFeature(t *testing.T) {
flightRecorderEnabledEnvVar := &corev1.EnvVar{
Name: ddFlightRecorderEnabled,
Value: "true",
}
flightRecorderSocketPathEnvVar := &corev1.EnvVar{
Name: ddFlightRecorderSocketPath,
Value: flightRecorderSocketFile,
}
flightRecorderOutputDirEnvVar := &corev1.EnvVar{
Name: ddFlightRecorderOutputDir,
Value: common.FlightRecorderDataPath,
}

tests := test.FeatureTestSuite{
{
Name: "flightrecorder disabled (default)",
DDA: testutils.NewDatadogAgentBuilder().
BuildWithDefaults(),
WantConfigure: false,
Agent: test.NewDefaultComponentTest().WithWantFunc(
func(t testing.TB, mgrInterface feature.PodTemplateManagers) {
mgr := mgrInterface.(*fake.PodTemplateManagers)
agentEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommon.CoreAgentContainerName]
assert.NotContains(t, agentEnvVars, flightRecorderEnabledEnvVar, "DD_FLIGHTRECORDER_ENABLED should not be set when FlightRecorder is not enabled")
},
),
},
{
Name: "flightrecorder enabled via annotation",
DDA: testutils.NewDatadogAgentBuilder().
WithAnnotations(map[string]string{
featureutils.EnableFlightRecorderAnnotation: "true",
}).
BuildWithDefaults(),
WantConfigure: true,
Agent: test.NewDefaultComponentTest().WithWantFunc(
func(t testing.TB, mgrInterface feature.PodTemplateManagers) {
mgr := mgrInterface.(*fake.PodTemplateManagers)

// Check env vars on core agent and trace agent
agentEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommon.CoreAgentContainerName]
assert.Contains(t, agentEnvVars, flightRecorderEnabledEnvVar, "DD_FLIGHTRECORDER_ENABLED should be set on core agent")
assert.Contains(t, agentEnvVars, flightRecorderSocketPathEnvVar, "DD_FLIGHTRECORDER_SOCKET_PATH should be set on core agent")

traceAgentEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommon.TraceAgentContainerName]
assert.Contains(t, traceAgentEnvVars, flightRecorderEnabledEnvVar, "DD_FLIGHTRECORDER_ENABLED should be set on trace agent")
assert.Contains(t, traceAgentEnvVars, flightRecorderSocketPathEnvVar, "DD_FLIGHTRECORDER_SOCKET_PATH should be set on trace agent")

frEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommon.FlightRecorderContainerName]
assert.NotContains(t, frEnvVars, flightRecorderEnabledEnvVar, "DD_FLIGHTRECORDER_ENABLED should not be set on flightrecorder")
assert.Contains(t, frEnvVars, flightRecorderSocketPathEnvVar, "DD_FLIGHTRECORDER_SOCKET_PATH should be set on flightrecorder")
assert.Contains(t, frEnvVars, flightRecorderOutputDirEnvVar, "DD_FLIGHTRECORDER_OUTPUT_DIR should be set on flightrecorder")

// Check volumes
expectedSocketVol := &corev1.Volume{
Name: common.FlightRecorderSocketVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
}
expectedDataVol := &corev1.Volume{
Name: common.FlightRecorderDataVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
}
assert.Contains(t, mgr.VolumeMgr.Volumes, expectedSocketVol, "socket volume should be added")
assert.Contains(t, mgr.VolumeMgr.Volumes, expectedDataVol, "data volume should be added")

// Check volume mounts on core agent (socket only)
expectedSocketMount := &corev1.VolumeMount{
Name: common.FlightRecorderSocketVolumeName,
MountPath: common.FlightRecorderSocketPath,
}
coreAgentMounts := mgr.VolumeMountMgr.VolumeMountsByC[apicommon.CoreAgentContainerName]
assert.Contains(t, coreAgentMounts, expectedSocketMount, "core agent should have socket volume mount")

// Check volume mounts on trace agent (socket)
traceAgentMounts := mgr.VolumeMountMgr.VolumeMountsByC[apicommon.TraceAgentContainerName]
assert.Contains(t, traceAgentMounts, expectedSocketMount, "trace agent should have socket volume mount")

// Check volume mounts on flightrecorder container (socket + data)
expectedDataMount := &corev1.VolumeMount{
Name: common.FlightRecorderDataVolumeName,
MountPath: common.FlightRecorderDataPath,
}
frMounts := mgr.VolumeMountMgr.VolumeMountsByC[apicommon.FlightRecorderContainerName]
assert.Contains(t, frMounts, expectedSocketMount, "flightrecorder should have socket volume mount")
assert.Contains(t, frMounts, expectedDataMount, "flightrecorder should have data volume mount")
},
),
},
{
Name: "flightrecorder annotation not set to true",
DDA: testutils.NewDatadogAgentBuilder().
WithAnnotations(map[string]string{
featureutils.EnableFlightRecorderAnnotation: "false",
}).
BuildWithDefaults(),
WantConfigure: false,
Agent: test.NewDefaultComponentTest().WithWantFunc(
func(t testing.TB, mgrInterface feature.PodTemplateManagers) {
mgr := mgrInterface.(*fake.PodTemplateManagers)
agentEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommon.CoreAgentContainerName]
assert.NotContains(t, agentEnvVars, flightRecorderEnabledEnvVar, "DD_FLIGHTRECORDER_ENABLED should not be set when FlightRecorder is disabled")
},
),
},
}

tests.Run(t, buildFlightRecorderFeature)
}
Loading
Loading