diff --git a/api/datadoghq/v2alpha1/datadogagent_types.go b/api/datadoghq/v2alpha1/datadogagent_types.go index 9606de3b56..bff9c2b2e5 100644 --- a/api/datadoghq/v2alpha1/datadogagent_types.go +++ b/api/datadoghq/v2alpha1/datadogagent_types.go @@ -119,6 +119,19 @@ type DatadogFeatures struct { HelmCheck *HelmCheckFeatureConfig `json:"helmCheck,omitempty"` // ControlPlaneMonitoring configuration. ControlPlaneMonitoring *ControlPlaneMonitoringFeatureConfig `json:"controlPlaneMonitoring,omitempty"` + // KubernetesActions configuration. + KubernetesActions *KubernetesActionsFeatureConfig `json:"kubernetesActions,omitempty"` +} + +// KubernetesActionsFeatureConfig allows configuration of the Kubernetes Actions feature. +// When enabled, the Cluster Agent is granted RBAC to perform remediation actions +// (deleting pods, restarting deployments) driven by the Datadog Kubernetes Actions product. +// +k8s:openapi-gen=true +type KubernetesActionsFeatureConfig struct { + // Enabled enables the Kubernetes Actions feature on the Cluster Agent. + // Default: false + // +optional + Enabled *bool `json:"enabled,omitempty"` } // Configuration structs for each feature in DatadogFeatures. All parameters are optional and have default values when necessary. diff --git a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go index 3ce3956403..269b12de2d 100644 --- a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go +++ b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go @@ -1636,6 +1636,11 @@ func (in *DatadogFeatures) DeepCopyInto(out *DatadogFeatures) { *out = new(ControlPlaneMonitoringFeatureConfig) (*in).DeepCopyInto(*out) } + if in.KubernetesActions != nil { + in, out := &in.KubernetesActions, &out.KubernetesActions + *out = new(KubernetesActionsFeatureConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatadogFeatures. @@ -2372,6 +2377,26 @@ func (in *KubeletConfig) DeepCopy() *KubeletConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesActionsFeatureConfig) DeepCopyInto(out *KubernetesActionsFeatureConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesActionsFeatureConfig. +func (in *KubernetesActionsFeatureConfig) DeepCopy() *KubernetesActionsFeatureConfig { + if in == nil { + return nil + } + out := new(KubernetesActionsFeatureConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubernetesAdmissionEventsConfig) DeepCopyInto(out *KubernetesAdmissionEventsConfig) { *out = *in diff --git a/api/datadoghq/v2alpha1/zz_generated.openapi.go b/api/datadoghq/v2alpha1/zz_generated.openapi.go index ba169b2555..4ac1a99a2c 100644 --- a/api/datadoghq/v2alpha1/zz_generated.openapi.go +++ b/api/datadoghq/v2alpha1/zz_generated.openapi.go @@ -38,6 +38,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.FIPSConfig": schema_datadog_operator_api_datadoghq_v2alpha1_FIPSConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.HelmCheckFeatureConfig": schema_datadog_operator_api_datadoghq_v2alpha1_HelmCheckFeatureConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.KubeStateMetricsCoreFeatureConfig": schema_datadog_operator_api_datadoghq_v2alpha1_KubeStateMetricsCoreFeatureConfig(ref), + "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.KubernetesActionsFeatureConfig": schema_datadog_operator_api_datadoghq_v2alpha1_KubernetesActionsFeatureConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LocalService": schema_datadog_operator_api_datadoghq_v2alpha1_LocalService(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.MultiCustomConfig": schema_datadog_operator_api_datadoghq_v2alpha1_MultiCustomConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.NetworkPolicyConfig": schema_datadog_operator_api_datadoghq_v2alpha1_NetworkPolicyConfig(ref), @@ -900,11 +901,17 @@ func schema_datadog_operator_api_datadoghq_v2alpha1_DatadogFeatures(ref common.R Ref: ref("github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ControlPlaneMonitoringFeatureConfig"), }, }, + "kubernetesActions": { + SchemaProps: spec.SchemaProps{ + Description: "KubernetesActions configuration.", + Ref: ref("github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.KubernetesActionsFeatureConfig"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.APMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ASMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.AdmissionControllerFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.AutoscalingFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.CSPMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.CWSFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ClusterChecksFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ControlPlaneMonitoringFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.DataPlaneFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.DogstatsdFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.EBPFCheckFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.EventCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ExternalMetricsServerFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.GPUFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.HelmCheckFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.KubeStateMetricsCoreFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LiveContainerCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LiveProcessCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LogCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.NPMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OOMKillFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OTLPFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OrchestratorExplorerFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OtelAgentGatewayFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OtelCollectorFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ProcessDiscoveryFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.PrometheusScrapeFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.RemoteConfigurationFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.SBOMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ServiceDiscoveryFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.TCPQueueLengthFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.USMFeatureConfig"}, + "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.APMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ASMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.AdmissionControllerFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.AutoscalingFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.CSPMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.CWSFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ClusterChecksFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ControlPlaneMonitoringFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.DataPlaneFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.DogstatsdFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.EBPFCheckFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.EventCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ExternalMetricsServerFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.GPUFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.HelmCheckFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.KubeStateMetricsCoreFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.KubernetesActionsFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LiveContainerCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LiveProcessCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LogCollectionFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.NPMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OOMKillFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OTLPFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OrchestratorExplorerFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OtelAgentGatewayFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OtelCollectorFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ProcessDiscoveryFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.PrometheusScrapeFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.RemoteConfigurationFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.SBOMFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.ServiceDiscoveryFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.TCPQueueLengthFeatureConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.USMFeatureConfig"}, } } @@ -1328,6 +1335,26 @@ func schema_datadog_operator_api_datadoghq_v2alpha1_KubeStateMetricsCoreFeatureC } } +func schema_datadog_operator_api_datadoghq_v2alpha1_KubernetesActionsFeatureConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "KubernetesActionsFeatureConfig allows configuration of the Kubernetes Actions feature. When enabled, the Cluster Agent is granted RBAC to perform remediation actions (deleting pods, restarting deployments) driven by the Datadog Kubernetes Actions product.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "enabled": { + SchemaProps: spec.SchemaProps{ + Description: "Enabled enables the Kubernetes Actions feature on the Cluster Agent. Default: false", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_datadog_operator_api_datadoghq_v2alpha1_LocalService(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml index 0fd41618bc..736bf93e32 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml @@ -1936,6 +1936,15 @@ spec: Default: true type: boolean type: object + kubernetesActions: + description: KubernetesActions configuration. + properties: + enabled: + description: |- + Enabled enables the Kubernetes Actions feature on the Cluster Agent. + Default: false + type: boolean + type: object liveContainerCollection: description: LiveContainerCollection configuration. properties: @@ -10643,6 +10652,15 @@ spec: Default: true type: boolean type: object + kubernetesActions: + description: KubernetesActions configuration. + properties: + enabled: + description: |- + Enabled enables the Kubernetes Actions feature on the Cluster Agent. + Default: false + type: boolean + type: object liveContainerCollection: description: LiveContainerCollection configuration. properties: diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json index 3c61a4a54a..3c169897e8 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json @@ -2006,6 +2006,17 @@ }, "type": "object" }, + "kubernetesActions": { + "additionalProperties": false, + "description": "KubernetesActions configuration.", + "properties": { + "enabled": { + "description": "Enabled enables the Kubernetes Actions feature on the Cluster Agent.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "liveContainerCollection": { "additionalProperties": false, "description": "LiveContainerCollection configuration.", @@ -10432,6 +10443,17 @@ }, "type": "object" }, + "kubernetesActions": { + "additionalProperties": false, + "description": "KubernetesActions configuration.", + "properties": { + "enabled": { + "description": "Enabled enables the Kubernetes Actions feature on the Cluster Agent.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "liveContainerCollection": { "additionalProperties": false, "description": "LiveContainerCollection configuration.", diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml index 5bc20fa1e9..7bd8a6afc9 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml @@ -1936,6 +1936,15 @@ spec: Default: true type: boolean type: object + kubernetesActions: + description: KubernetesActions configuration. + properties: + enabled: + description: |- + Enabled enables the Kubernetes Actions feature on the Cluster Agent. + Default: false + type: boolean + type: object liveContainerCollection: description: LiveContainerCollection configuration. properties: diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json index a7488a18ca..3af2806c53 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json @@ -2010,6 +2010,17 @@ }, "type": "object" }, + "kubernetesActions": { + "additionalProperties": false, + "description": "KubernetesActions configuration.", + "properties": { + "enabled": { + "description": "Enabled enables the Kubernetes Actions feature on the Cluster Agent.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "liveContainerCollection": { "additionalProperties": false, "description": "LiveContainerCollection configuration.", diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml index 61b96259f8..1ff050d15e 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml @@ -1940,6 +1940,15 @@ spec: Default: true type: boolean type: object + kubernetesActions: + description: KubernetesActions configuration. + properties: + enabled: + description: |- + Enabled enables the Kubernetes Actions feature on the Cluster Agent. + Default: false + type: boolean + type: object liveContainerCollection: description: LiveContainerCollection configuration. properties: @@ -10736,6 +10745,15 @@ spec: Default: true type: boolean type: object + kubernetesActions: + description: KubernetesActions configuration. + properties: + enabled: + description: |- + Enabled enables the Kubernetes Actions feature on the Cluster Agent. + Default: false + type: boolean + type: object liveContainerCollection: description: LiveContainerCollection configuration. properties: diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json index 0f72aabd93..1f01e6775a 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json @@ -2006,6 +2006,17 @@ }, "type": "object" }, + "kubernetesActions": { + "additionalProperties": false, + "description": "KubernetesActions configuration.", + "properties": { + "enabled": { + "description": "Enabled enables the Kubernetes Actions feature on the Cluster Agent.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "liveContainerCollection": { "additionalProperties": false, "description": "LiveContainerCollection configuration.", @@ -10531,6 +10542,17 @@ }, "type": "object" }, + "kubernetesActions": { + "additionalProperties": false, + "description": "KubernetesActions configuration.", + "properties": { + "enabled": { + "description": "Enabled enables the Kubernetes Actions feature on the Cluster Agent.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "liveContainerCollection": { "additionalProperties": false, "description": "LiveContainerCollection configuration.", diff --git a/docs/configuration.v2alpha1.md b/docs/configuration.v2alpha1.md index 2bc0e20346..60fd03a0ba 100644 --- a/docs/configuration.v2alpha1.md +++ b/docs/configuration.v2alpha1.md @@ -142,6 +142,7 @@ spec: | features.kubeStateMetricsCore.conf.configMap.items | Maps a ConfigMap data `key` to a file `path` mount. | | features.kubeStateMetricsCore.conf.configMap.name | Is the name of the ConfigMap. | | features.kubeStateMetricsCore.enabled | Enables Kube State Metrics Core. Default: true | +| features.kubernetesActions.enabled | Enables the Kubernetes Actions feature on the Cluster Agent. Default: false | | features.liveContainerCollection.enabled | Enables container collection for the Live Container View. Default: true | | features.liveProcessCollection.enabled | Enables Process monitoring. Default: false | | features.liveProcessCollection.scrubProcessArguments | ScrubProcessArguments enables scrubbing of sensitive data in process command-lines (passwords, tokens, etc. ). Default: true | diff --git a/docs/configuration_public.md b/docs/configuration_public.md index c141925584..ba6bd5cbdd 100644 --- a/docs/configuration_public.md +++ b/docs/configuration_public.md @@ -252,6 +252,9 @@ spec: `features.kubeStateMetricsCore.enabled` : Enables Kube State Metrics Core. Default: true +`features.kubernetesActions.enabled` +: Enables the Kubernetes Actions feature on the Cluster Agent. Default: false + `features.liveContainerCollection.enabled` : Enables container collection for the Live Container View. Default: true diff --git a/internal/controller/datadogagent/controller.go b/internal/controller/datadogagent/controller.go index 15449b6930..297ef8a5f5 100644 --- a/internal/controller/datadogagent/controller.go +++ b/internal/controller/datadogagent/controller.go @@ -43,6 +43,7 @@ import ( _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/helmcheck" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/hostprofiler" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/instrumentationcrd" + _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/kubernetesactions" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/kubernetesstatecore" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/livecontainer" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/liveprocess" diff --git a/internal/controller/datadogagent/feature/ids.go b/internal/controller/datadogagent/feature/ids.go index dc968983bc..06720e82f0 100644 --- a/internal/controller/datadogagent/feature/ids.go +++ b/internal/controller/datadogagent/feature/ids.go @@ -89,4 +89,6 @@ const ( DataPlaneIDType = "data_plane" // FlightRecorderIDType Flight Recorder feature. FlightRecorderIDType = "flightrecorder" + // KubernetesActionsIDType Kubernetes Actions feature. + KubernetesActionsIDType = "kubernetes_actions" ) diff --git a/internal/controller/datadogagent/feature/kubernetesactions/const.go b/internal/controller/datadogagent/feature/kubernetesactions/const.go new file mode 100644 index 0000000000..1e82dd2d3c --- /dev/null +++ b/internal/controller/datadogagent/feature/kubernetesactions/const.go @@ -0,0 +1,28 @@ +// 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 kubernetesactions + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + kubernetesActionsRBACPrefix = "kubernetes-actions" + + // DDKubeActionsEnabled is the env var that turns on the Kubernetes Actions + // product in the Cluster Agent. + DDKubeActionsEnabled = "DD_KUBEACTIONS_ENABLED" + + // ClusterAgentMinVersion is the minimum Cluster Agent version that supports + // the Kubernetes Actions product. + ClusterAgentMinVersion = "7.79.0" +) + +func getRBACResourceName(owner metav1.Object, suffix string) string { + return fmt.Sprintf("%s-%s-%s-%s", owner.GetNamespace(), owner.GetName(), kubernetesActionsRBACPrefix, suffix) +} diff --git a/internal/controller/datadogagent/feature/kubernetesactions/feature.go b/internal/controller/datadogagent/feature/kubernetesactions/feature.go new file mode 100644 index 0000000000..d78db32e07 --- /dev/null +++ b/internal/controller/datadogagent/feature/kubernetesactions/feature.go @@ -0,0 +1,114 @@ +// 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 kubernetesactions + +import ( + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common" + "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1" + apiutils "github.com/DataDog/datadog-operator/api/utils" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/common" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature" + "github.com/DataDog/datadog-operator/pkg/constants" + "github.com/DataDog/datadog-operator/pkg/images" + "github.com/DataDog/datadog-operator/pkg/utils" +) + +func clusterAgentVersion(ddaSpec *v2alpha1.DatadogAgentSpec) string { + if ddaSpec == nil { + return images.AgentLatestVersion + } + if clusterAgent, ok := ddaSpec.Override[v2alpha1.ClusterAgentComponentName]; ok { + if clusterAgent.Image != nil { + return common.GetAgentVersionFromImage(*clusterAgent.Image) + } + } + return images.AgentLatestVersion +} + +func init() { + err := feature.Register(feature.KubernetesActionsIDType, buildKubernetesActionsFeature) + if err != nil { + panic(err) + } +} + +func buildKubernetesActionsFeature(options *feature.Options) feature.Feature { + f := &kubernetesActionsFeature{ + rbacSuffix: common.ClusterAgentSuffix, + } + if options != nil { + f.logger = options.Logger + } + return f +} + +type kubernetesActionsFeature struct { + owner metav1.Object + serviceAccountName string + rbacSuffix string + logger logr.Logger +} + +func (f *kubernetesActionsFeature) ID() feature.IDType { + return feature.KubernetesActionsIDType +} + +func (f *kubernetesActionsFeature) Configure(dda metav1.Object, ddaSpec *v2alpha1.DatadogAgentSpec, _ *v2alpha1.RemoteConfigConfiguration) (reqComp feature.RequiredComponents) { + f.owner = dda + + if ddaSpec.Features == nil || ddaSpec.Features.KubernetesActions == nil || !apiutils.BoolValue(ddaSpec.Features.KubernetesActions.Enabled) { + return reqComp + } + + if !utils.IsAboveMinVersion(clusterAgentVersion(ddaSpec), ClusterAgentMinVersion, nil) { + f.logger.V(1).Info("cluster agent version is too low for Kubernetes Actions", "min", ClusterAgentMinVersion) + return reqComp + } + + f.serviceAccountName = constants.GetClusterAgentServiceAccount(dda.GetName(), ddaSpec) + + reqComp = feature.RequiredComponents{ + ClusterAgent: feature.RequiredComponent{ + IsRequired: ptr.To(true), + Containers: []apicommon.AgentContainerName{apicommon.ClusterAgentContainerName}, + }, + } + return reqComp +} + +func (f *kubernetesActionsFeature) ManageDependencies(managers feature.ResourceManagers) error { + rbacName := getRBACResourceName(f.owner, f.rbacSuffix) + return managers.RBACManager().AddClusterPolicyRules(f.owner.GetNamespace(), rbacName, f.serviceAccountName, kubernetesActionsRBACPolicyRules) +} + +func (f *kubernetesActionsFeature) ManageClusterAgent(managers feature.PodTemplateManagers) error { + managers.EnvVar().AddEnvVarToContainer(apicommon.ClusterAgentContainerName, &corev1.EnvVar{ + Name: DDKubeActionsEnabled, + Value: "true", + }) + return nil +} + +func (f *kubernetesActionsFeature) ManageSingleContainerNodeAgent(_ feature.PodTemplateManagers) error { + return nil +} + +func (f *kubernetesActionsFeature) ManageNodeAgent(_ feature.PodTemplateManagers) error { + return nil +} + +func (f *kubernetesActionsFeature) ManageClusterChecksRunner(_ feature.PodTemplateManagers) error { + return nil +} + +func (f *kubernetesActionsFeature) ManageOtelAgentGateway(_ feature.PodTemplateManagers) error { + return nil +} diff --git a/internal/controller/datadogagent/feature/kubernetesactions/feature_test.go b/internal/controller/datadogagent/feature/kubernetesactions/feature_test.go new file mode 100644 index 0000000000..86a8a39724 --- /dev/null +++ b/internal/controller/datadogagent/feature/kubernetesactions/feature_test.go @@ -0,0 +1,91 @@ +// 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 kubernetesactions + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + + apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common" + apiutils "github.com/DataDog/datadog-operator/api/utils" + "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" + "github.com/DataDog/datadog-operator/pkg/testutils" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" +) + +func Test_kubernetesActionsFeature_Configure(t *testing.T) { + tests := test.FeatureTestSuite{ + { + Name: "KubernetesActions not configured", + DDA: testutils.NewDatadogAgentBuilder().Build(), + WantConfigure: false, + }, + { + Name: "KubernetesActions explicitly disabled", + DDA: testutils.NewDatadogAgentBuilder(). + WithKubernetesActionsEnabled(false). + Build(), + WantConfigure: false, + }, + { + Name: "KubernetesActions enabled but cluster agent version too low", + DDA: testutils.NewDatadogAgentBuilder(). + WithName("ddaDCA"). + WithKubernetesActionsEnabled(true). + WithClusterAgentTag("7.78.0"). + Build(), + WantConfigure: false, + }, + { + Name: "KubernetesActions enabled at minimum cluster agent version", + DDA: testutils.NewDatadogAgentBuilder(). + WithName("ddaDCA"). + WithKubernetesActionsEnabled(true). + WithClusterAgentTag("7.79.0"). + Build(), + WantConfigure: true, + ClusterAgent: test.NewDefaultComponentTest().WithWantFunc(kubernetesActionsClusterAgentWantFunc), + }, + { + Name: "KubernetesActions enabled", + DDA: testutils.NewDatadogAgentBuilder(). + WithName("ddaDCA"). + WithKubernetesActionsEnabled(true). + Build(), + WantConfigure: true, + ClusterAgent: test.NewDefaultComponentTest().WithWantFunc(kubernetesActionsClusterAgentWantFunc), + }, + } + + tests.Run(t, buildKubernetesActionsFeature) +} + +func kubernetesActionsClusterAgentWantFunc(t testing.TB, mgrInterface feature.PodTemplateManagers) { + mgr := mgrInterface.(*fake.PodTemplateManagers) + dcaEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommon.ClusterAgentContainerName] + + want := []*corev1.EnvVar{ + { + Name: DDKubeActionsEnabled, + Value: "true", + }, + } + assert.True(t, apiutils.IsEqualStruct(dcaEnvVars, want), "DCA envvars \ndiff = %s", cmp.Diff(dcaEnvVars, want)) +} + +func Test_kubernetesActionsFeature_NoOpManagers(t *testing.T) { + f := buildKubernetesActionsFeature(nil) + + assert.NoError(t, f.ManageSingleContainerNodeAgent(nil)) + assert.NoError(t, f.ManageNodeAgent(nil)) + assert.NoError(t, f.ManageClusterChecksRunner(nil)) + assert.NoError(t, f.ManageOtelAgentGateway(nil)) +} diff --git a/internal/controller/datadogagent/feature/kubernetesactions/rbac.go b/internal/controller/datadogagent/feature/kubernetesactions/rbac.go new file mode 100644 index 0000000000..477c7db14b --- /dev/null +++ b/internal/controller/datadogagent/feature/kubernetesactions/rbac.go @@ -0,0 +1,32 @@ +// 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 kubernetesactions + +import ( + rbacv1 "k8s.io/api/rbac/v1" + + "github.com/DataDog/datadog-operator/pkg/kubernetes/rbac" +) + +// kubernetesActionsRBACPolicyRules are the cluster-scoped rules the Cluster Agent +// needs to remediate workloads on behalf of the Kubernetes Actions product. +var kubernetesActionsRBACPolicyRules = []rbacv1.PolicyRule{ + { + APIGroups: []string{rbac.CoreAPIGroup}, + Resources: []string{rbac.PodsResource}, + Verbs: []string{rbac.GetVerb, rbac.ListVerb, rbac.DeleteVerb}, + }, + { + APIGroups: []string{rbac.AppsAPIGroup}, + Resources: []string{rbac.DeploymentsResource}, + Verbs: []string{rbac.GetVerb, rbac.ListVerb, rbac.PatchVerb, rbac.UpdateVerb}, + }, + { + APIGroups: []string{rbac.AppsAPIGroup}, + Resources: []string{"deployments/status"}, + Verbs: []string{rbac.GetVerb}, + }, +} diff --git a/internal/controller/datadogagent/feature/kubernetesactions/rbac_test.go b/internal/controller/datadogagent/feature/kubernetesactions/rbac_test.go new file mode 100644 index 0000000000..1ccb09ceee --- /dev/null +++ b/internal/controller/datadogagent/feature/kubernetesactions/rbac_test.go @@ -0,0 +1,38 @@ +// 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 kubernetesactions + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/DataDog/datadog-operator/pkg/kubernetes/rbac" +) + +func TestKubernetesActionsRBACPolicyRules(t *testing.T) { + rules := kubernetesActionsRBACPolicyRules + assert.Len(t, rules, 3, "expected pods, deployments, deployments/status rules") + + byResource := map[string]int{} + for i, r := range rules { + for _, res := range r.Resources { + byResource[res] = i + } + } + + podRule := rules[byResource[rbac.PodsResource]] + assert.Equal(t, []string{rbac.CoreAPIGroup}, podRule.APIGroups) + assert.ElementsMatch(t, []string{rbac.GetVerb, rbac.ListVerb, rbac.DeleteVerb}, podRule.Verbs) + + deployRule := rules[byResource[rbac.DeploymentsResource]] + assert.Equal(t, []string{rbac.AppsAPIGroup}, deployRule.APIGroups) + assert.ElementsMatch(t, []string{rbac.GetVerb, rbac.ListVerb, rbac.PatchVerb, rbac.UpdateVerb}, deployRule.Verbs) + + statusRule := rules[byResource["deployments/status"]] + assert.Equal(t, []string{rbac.AppsAPIGroup}, statusRule.APIGroups) + assert.ElementsMatch(t, []string{rbac.GetVerb}, statusRule.Verbs) +} diff --git a/internal/controller/datadogagentinternal/controller.go b/internal/controller/datadogagentinternal/controller.go index ff192c0d7e..f1decbc8d8 100644 --- a/internal/controller/datadogagentinternal/controller.go +++ b/internal/controller/datadogagentinternal/controller.go @@ -39,6 +39,7 @@ import ( _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/externalmetrics" _ "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/kubernetesactions" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/kubernetesstatecore" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/livecontainer" _ "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/liveprocess" diff --git a/pkg/testutils/builder.go b/pkg/testutils/builder.go index e7a4f94f60..8539cd2f14 100644 --- a/pkg/testutils/builder.go +++ b/pkg/testutils/builder.go @@ -1007,6 +1007,18 @@ func (builder *DatadogAgentBuilder) WithHelmCheckValuesAsTags(valuesAsTags map[s return builder } +func (builder *DatadogAgentBuilder) initKubernetesActions() { + if builder.datadogAgent.Spec.Features.KubernetesActions == nil { + builder.datadogAgent.Spec.Features.KubernetesActions = &v2alpha1.KubernetesActionsFeatureConfig{} + } +} + +func (builder *DatadogAgentBuilder) WithKubernetesActionsEnabled(enabled bool) *DatadogAgentBuilder { + builder.initKubernetesActions() + builder.datadogAgent.Spec.Features.KubernetesActions.Enabled = ptr.To(enabled) + return builder +} + // Global Kubelet func (builder *DatadogAgentBuilder) WithGlobalKubeletConfig(hostCAPath, agentCAPath string, tlsVerify bool, podResourcesSocketDir string) *DatadogAgentBuilder { diff --git a/pkg/testutils/builder_test.go b/pkg/testutils/builder_test.go new file mode 100644 index 0000000000..4f10e9b994 --- /dev/null +++ b/pkg/testutils/builder_test.go @@ -0,0 +1,31 @@ +// 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 testutils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDatadogAgentBuilder_WithKubernetesActionsEnabled(t *testing.T) { + builder := NewDatadogAgentBuilder() + require.Nil(t, builder.datadogAgent.Spec.Features.KubernetesActions) + + returnedBuilder := builder.WithKubernetesActionsEnabled(true) + require.Same(t, builder, returnedBuilder) + require.NotNil(t, builder.datadogAgent.Spec.Features.KubernetesActions) + require.NotNil(t, builder.datadogAgent.Spec.Features.KubernetesActions.Enabled) + assert.True(t, *builder.datadogAgent.Spec.Features.KubernetesActions.Enabled) + + initialKubernetesActionsConfig := builder.datadogAgent.Spec.Features.KubernetesActions + builder.WithKubernetesActionsEnabled(false) + + assert.Same(t, initialKubernetesActionsConfig, builder.datadogAgent.Spec.Features.KubernetesActions) + require.NotNil(t, builder.datadogAgent.Spec.Features.KubernetesActions.Enabled) + assert.False(t, *builder.datadogAgent.Spec.Features.KubernetesActions.Enabled) +}