diff --git a/api/datadoghq/v2alpha1/datadogagent_types.go b/api/datadoghq/v2alpha1/datadogagent_types.go index dc6822a31..8b0b524be 100644 --- a/api/datadoghq/v2alpha1/datadogagent_types.go +++ b/api/datadoghq/v2alpha1/datadogagent_types.go @@ -810,6 +810,12 @@ type OtelCollectorFeatureConfig struct { // +listType=atomic Ports []*corev1.ContainerPort `json:"ports,omitempty"` + // Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. + // If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API + // Default: true + // +optional + CreateRbac *bool `json:"createRbac,omitempty"` + // OTelCollector Config Relevant to the Core agent // +optional CoreConfig *CoreConfig `json:"coreConfig,omitempty"` diff --git a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go index 5b36dfb99..5ee10e121 100644 --- a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go +++ b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go @@ -2444,6 +2444,11 @@ func (in *OtelCollectorFeatureConfig) DeepCopyInto(out *OtelCollectorFeatureConf } } } + if in.CreateRbac != nil { + in, out := &in.CreateRbac, &out.CreateRbac + *out = new(bool) + **out = **in + } if in.CoreConfig != nil { in, out := &in.CoreConfig, &out.CoreConfig *out = new(CoreConfig) diff --git a/api/datadoghq/v2alpha1/zz_generated.openapi.go b/api/datadoghq/v2alpha1/zz_generated.openapi.go index 536d97d48..60d0c8ae1 100644 --- a/api/datadoghq/v2alpha1/zz_generated.openapi.go +++ b/api/datadoghq/v2alpha1/zz_generated.openapi.go @@ -1481,6 +1481,13 @@ func schema_datadog_operator_api_datadoghq_v2alpha1_OtelCollectorFeatureConfig(r }, }, }, + "createRbac": { + SchemaProps: spec.SchemaProps{ + Description: "Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API Default: true", + Type: []string{"boolean"}, + Format: "", + }, + }, "coreConfig": { SchemaProps: spec.SchemaProps{ Description: "OTelCollector Config Relevant to the Core agent", diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml index f2aad34be..540c71dfe 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml @@ -1814,6 +1814,12 @@ spec: the core agent. type: string type: object + createRbac: + description: |- + Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. + If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API + Default: true + type: boolean enabled: description: |- Enabled enables the OTel Agent. @@ -9214,6 +9220,12 @@ spec: the core agent. type: string type: object + createRbac: + description: |- + Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. + If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API + Default: true + type: boolean enabled: description: |- Enabled enables the OTel Agent. diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json index fcdefce70..0b124e7d5 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json @@ -1852,6 +1852,10 @@ }, "type": "object" }, + "createRbac": { + "description": "Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector.\nIf true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API\nDefault: true", + "type": "boolean" + }, "enabled": { "description": "Enabled enables the OTel Agent.\nDefault: false", "type": "boolean" @@ -9095,6 +9099,10 @@ }, "type": "object" }, + "createRbac": { + "description": "Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector.\nIf true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API\nDefault: true", + "type": "boolean" + }, "enabled": { "description": "Enabled enables the OTel Agent.\nDefault: false", "type": "boolean" diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml index 98ad09dbc..5d21c3721 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml @@ -1814,6 +1814,12 @@ spec: the core agent. type: string type: object + createRbac: + description: |- + Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. + If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API + Default: true + type: boolean enabled: description: |- Enabled enables the OTel Agent. diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json index 3334ac08e..ff7b5d932 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json @@ -1856,6 +1856,10 @@ }, "type": "object" }, + "createRbac": { + "description": "Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector.\nIf true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API\nDefault: true", + "type": "boolean" + }, "enabled": { "description": "Enabled enables the OTel Agent.\nDefault: false", "type": "boolean" diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml index b36e254f1..6ed95fb62 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml @@ -1814,6 +1814,12 @@ spec: the core agent. type: string type: object + createRbac: + description: |- + Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. + If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API + Default: true + type: boolean enabled: description: |- Enabled enables the OTel Agent. @@ -9264,6 +9270,12 @@ spec: the core agent. type: string type: object + createRbac: + description: |- + Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. + If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API + Default: true + type: boolean enabled: description: |- Enabled enables the OTel Agent. diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json index 138948cba..a053793c6 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json @@ -1852,6 +1852,10 @@ }, "type": "object" }, + "createRbac": { + "description": "Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector.\nIf true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API\nDefault: true", + "type": "boolean" + }, "enabled": { "description": "Enabled enables the OTel Agent.\nDefault: false", "type": "boolean" @@ -9160,6 +9164,10 @@ }, "type": "object" }, + "createRbac": { + "description": "Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector.\nIf true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API\nDefault: true", + "type": "boolean" + }, "enabled": { "description": "Enabled enables the OTel Agent.\nDefault: false", "type": "boolean" diff --git a/docs/configuration.v2alpha1.md b/docs/configuration.v2alpha1.md index 7c6f4253c..667a0a465 100644 --- a/docs/configuration.v2alpha1.md +++ b/docs/configuration.v2alpha1.md @@ -153,6 +153,7 @@ spec: | features.otelCollector.coreConfig.enabled | Marks otelcollector as enabled in core agent. | | features.otelCollector.coreConfig.extensionTimeout | Extension URL provides the timout of the ddflareextension to the core agent. | | features.otelCollector.coreConfig.extensionURL | Extension URL provides the URL of the ddflareextension to the core agent. | +| features.otelCollector.createRbac | Set CreateRbac to false to prevent automatic creation of ClusterRole for the otelCollector. If true, check OTel Collector config for k8sattributes processor and create required ClusterRole to access Kubernetes API Default: true | | features.otelCollector.enabled | Enables the OTel Agent. Default: false | | features.otelCollector.ports | Contains the ports for the otel-agent. Defaults: otel-grpc:4317 / otel-http:4318. Note: setting 4317 or 4318 manually is *only* supported if name match default names (otel-grpc, otel-http). If not, this will lead to a port conflict. This limitation will be lifted once annotations support is removed. | | features.otlp.receiver.protocols.grpc.enabled | Enable the OTLP/gRPC endpoint. Host port is enabled by default and can be disabled. | diff --git a/internal/controller/datadogagent/defaults/datadogagent_default.go b/internal/controller/datadogagent/defaults/datadogagent_default.go index 452415030..bba712bb8 100644 --- a/internal/controller/datadogagent/defaults/datadogagent_default.go +++ b/internal/controller/datadogagent/defaults/datadogagent_default.go @@ -28,6 +28,7 @@ const ( defaultLogContainerSymlinksPath string = "/var/log/containers" defaultOtelCollectorEnabled bool = false + defaultOtelCollectorCreateRBAC bool = true defaultLiveProcessCollectionEnabled bool = false defaultLiveContainerCollectionEnabled bool = true defaultProcessDiscoveryEnabled bool = true @@ -246,6 +247,7 @@ func defaultFeaturesConfig(ddaSpec *v2alpha1.DatadogAgentSpec) { ddaSpec.Features.OtelCollector = &v2alpha1.OtelCollectorFeatureConfig{} } apiutils.DefaultBooleanIfUnset(&ddaSpec.Features.OtelCollector.Enabled, defaultOtelCollectorEnabled) + apiutils.DefaultBooleanIfUnset(&ddaSpec.Features.OtelCollector.CreateRbac, defaultOtelCollectorCreateRBAC) // LiveProcessCollection Feature if ddaSpec.Features.LiveProcessCollection == nil { diff --git a/internal/controller/datadogagent/defaults/datadogagent_default_test.go b/internal/controller/datadogagent/defaults/datadogagent_default_test.go index ad2ce4130..fc0083a4e 100644 --- a/internal/controller/datadogagent/defaults/datadogagent_default_test.go +++ b/internal/controller/datadogagent/defaults/datadogagent_default_test.go @@ -237,7 +237,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -363,7 +364,8 @@ func Test_defaultFeatures(t *testing.T) { Enabled: apiutils.NewBoolPointer(valueFalse), }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -456,7 +458,8 @@ func Test_defaultFeatures(t *testing.T) { Enabled: apiutils.NewBoolPointer(valueFalse), }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -601,7 +604,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -755,7 +759,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -904,7 +909,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -1053,7 +1059,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -1211,7 +1218,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -1360,7 +1368,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -1512,7 +1521,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -1656,7 +1666,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -1825,7 +1836,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -1922,7 +1934,8 @@ func Test_defaultFeatures(t *testing.T) { ddaSpec: &v2alpha1.DatadogAgentSpec{ Features: &v2alpha1.DatadogFeatures{ OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(valueTrue), + Enabled: apiutils.NewBoolPointer(valueTrue), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, }, }, @@ -1975,7 +1988,159 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(valueTrue), + Enabled: apiutils.NewBoolPointer(valueTrue), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), + }, + ASM: &v2alpha1.ASMFeatureConfig{ + Threats: &v2alpha1.ASMThreatsConfig{ + Enabled: apiutils.NewBoolPointer(defaultAdmissionASMThreatsEnabled), + }, + SCA: &v2alpha1.ASMSCAConfig{ + Enabled: apiutils.NewBoolPointer(defaultAdmissionASMSCAEnabled), + }, + IAST: &v2alpha1.ASMIASTConfig{ + Enabled: apiutils.NewBoolPointer(defaultAdmissionASMIASTEnabled), + }, + }, + CSPM: &v2alpha1.CSPMFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultCSPMEnabled), + }, + CWS: &v2alpha1.CWSFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultCWSEnabled), + }, + NPM: &v2alpha1.NPMFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultNPMEnabled), + }, + USM: &v2alpha1.USMFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultUSMEnabled), + }, + Dogstatsd: &v2alpha1.DogstatsdFeatureConfig{ + OriginDetectionEnabled: apiutils.NewBoolPointer(defaultDogstatsdOriginDetectionEnabled), + HostPortConfig: &v2alpha1.HostPortConfig{Enabled: apiutils.NewBoolPointer(defaultDogstatsdHostPortEnabled)}, + UnixDomainSocketConfig: &v2alpha1.UnixDomainSocketConfig{ + Enabled: apiutils.NewBoolPointer(defaultDogstatsdSocketEnabled), + Path: apiutils.NewStringPointer(defaultDogstatsdHostSocketPath), + }, + }, + OTLP: &v2alpha1.OTLPFeatureConfig{Receiver: v2alpha1.OTLPReceiverConfig{Protocols: v2alpha1.OTLPProtocolsConfig{ + GRPC: &v2alpha1.OTLPGRPCConfig{ + Enabled: apiutils.NewBoolPointer(defaultOTLPGRPCEnabled), + HostPortConfig: nil, + Endpoint: apiutils.NewStringPointer(defaultOTLPGRPCEndpoint), + }, + HTTP: &v2alpha1.OTLPHTTPConfig{ + Enabled: apiutils.NewBoolPointer(defaultOTLPHTTPEnabled), + HostPortConfig: nil, + Endpoint: apiutils.NewStringPointer(defaultOTLPHTTPEndpoint), + }, + }}}, + RemoteConfiguration: &v2alpha1.RemoteConfigurationFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultRemoteConfigurationEnabled), + }, + EventCollection: &v2alpha1.EventCollectionFeatureConfig{ + CollectKubernetesEvents: apiutils.NewBoolPointer(defaultCollectKubernetesEvents), + }, + OrchestratorExplorer: &v2alpha1.OrchestratorExplorerFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultOrchestratorExplorerEnabled), + ScrubContainers: apiutils.NewBoolPointer(defaultOrchestratorExplorerScrubContainers), + }, + ExternalMetricsServer: &v2alpha1.ExternalMetricsServerFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultExternalMetricsServerEnabled), + }, + KubeStateMetricsCore: &v2alpha1.KubeStateMetricsCoreFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultKubeStateMetricsCoreEnabled), + }, + ClusterChecks: &v2alpha1.ClusterChecksFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultClusterChecksEnabled), + UseClusterChecksRunners: apiutils.NewBoolPointer(defaultUseClusterChecksRunners), + }, + AdmissionController: &v2alpha1.AdmissionControllerFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultAdmissionControllerEnabled), + Validation: &v2alpha1.AdmissionControllerValidationConfig{ + Enabled: apiutils.NewBoolPointer(defaultAdmissionControllerValidationEnabled), + }, + Mutation: &v2alpha1.AdmissionControllerMutationConfig{ + Enabled: apiutils.NewBoolPointer(defaultAdmissionControllerMutationEnabled), + }, + MutateUnlabelled: apiutils.NewBoolPointer(defaultAdmissionControllerMutateUnlabelled), + ServiceName: apiutils.NewStringPointer(defaultAdmissionServiceName), + CWSInstrumentation: &v2alpha1.CWSInstrumentationConfig{ + Enabled: apiutils.NewBoolPointer(DefaultAdmissionControllerCWSInstrumentationEnabled), + }, + KubernetesAdmissionEvents: &v2alpha1.KubernetesAdmissionEventsConfig{ + Enabled: apiutils.NewBoolPointer(defaultAdmissionControllerKubernetesAdmissionEventsEnabled), + }, + }, + PrometheusScrape: &v2alpha1.PrometheusScrapeFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultPrometheusScrapeEnabled), + }, + HelmCheck: &v2alpha1.HelmCheckFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultHelmCheckEnabled), + }, + }, + }, + }, + { + name: "OTel Collector CreateRbac is disabled", + ddaSpec: &v2alpha1.DatadogAgentSpec{ + Features: &v2alpha1.DatadogFeatures{ + OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ + Enabled: apiutils.NewBoolPointer(valueTrue), + CreateRbac: apiutils.NewBoolPointer(valueFalse), + }, + }, + }, + want: &v2alpha1.DatadogAgentSpec{ + Features: &v2alpha1.DatadogFeatures{ + LogCollection: &v2alpha1.LogCollectionFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultLogCollectionEnabled), + }, + LiveProcessCollection: &v2alpha1.LiveProcessCollectionFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultLiveProcessCollectionEnabled), + }, + LiveContainerCollection: &v2alpha1.LiveContainerCollectionFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultLiveContainerCollectionEnabled), + }, + ProcessDiscovery: &v2alpha1.ProcessDiscoveryFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultProcessDiscoveryEnabled), + }, + OOMKill: &v2alpha1.OOMKillFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultOOMKillEnabled), + }, + TCPQueueLength: &v2alpha1.TCPQueueLengthFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultTCPQueueLengthEnabled), + }, + EBPFCheck: &v2alpha1.EBPFCheckFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultEBPFCheckEnabled), + }, + ServiceDiscovery: &v2alpha1.ServiceDiscoveryFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultServiceDiscoveryEnabled), + }, + GPU: &v2alpha1.GPUFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultGPUMonitoringEnabled), + }, + APM: &v2alpha1.APMFeatureConfig{ + Enabled: apiutils.NewBoolPointer(defaultAPMEnabled), + HostPortConfig: &v2alpha1.HostPortConfig{ + Port: apiutils.NewInt32Pointer(defaultAPMHostPort), + Enabled: apiutils.NewBoolPointer(defaultAPMHostPortEnabled), + }, + UnixDomainSocketConfig: &v2alpha1.UnixDomainSocketConfig{ + Enabled: apiutils.NewBoolPointer(defaultAPMSocketEnabled), + Path: apiutils.NewStringPointer(defaultAPMSocketHostPath), + }, + SingleStepInstrumentation: &v2alpha1.SingleStepInstrumentation{ + Enabled: apiutils.NewBoolPointer(defaultAPMSingleStepInstrEnabled), + LanguageDetection: &v2alpha1.LanguageDetectionConfig{Enabled: apiutils.NewBoolPointer(defaultLanguageDetectionEnabled)}, + Injector: &v2alpha1.InjectorConfig{}, + }, + ErrorTrackingStandalone: &v2alpha1.ErrorTrackingStandalone{ + Enabled: apiutils.NewBoolPointer(defaultAPMErrorTrackingStandalone), + }, + }, + OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ + Enabled: apiutils.NewBoolPointer(valueTrue), + CreateRbac: apiutils.NewBoolPointer(valueFalse), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -2147,7 +2312,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -2299,7 +2465,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ @@ -2463,7 +2630,8 @@ func Test_defaultFeatures(t *testing.T) { }, }, OtelCollector: &v2alpha1.OtelCollectorFeatureConfig{ - Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + Enabled: apiutils.NewBoolPointer(defaultOtelCollectorEnabled), + CreateRbac: apiutils.NewBoolPointer(defaultOtelCollectorCreateRBAC), }, ASM: &v2alpha1.ASMFeatureConfig{ Threats: &v2alpha1.ASMThreatsConfig{ diff --git a/internal/controller/datadogagent/feature/otelcollector/configmap_test.go b/internal/controller/datadogagent/feature/otelcollector/configmap_test.go index be3ef8c9d..af8d38e0c 100644 --- a/internal/controller/datadogagent/feature/otelcollector/configmap_test.go +++ b/internal/controller/datadogagent/feature/otelcollector/configmap_test.go @@ -8,7 +8,6 @@ package otelcollector import ( "testing" - "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature/otelcollector/defaultconfig" "github.com/stretchr/testify/assert" @@ -37,10 +36,8 @@ func Test_buildOtelCollectorConfigMap(t *testing.T) { Name: "-otel-agent-config", } otelCollectorFeature.configMapName = "-otel-agent-config" - otelCollectorFeature.customConfig = &v2alpha1.CustomConfig{} - otelCollectorFeature.customConfig.ConfigData = &defaultconfig.DefaultOtelCollectorConfig - configMap, err := otelCollectorFeature.buildOTelAgentCoreConfigMap() + configMap, err := otelCollectorFeature.buildOTelAgentCoreConfigMap(&defaultconfig.DefaultOtelCollectorConfig) assert.NoError(t, err) assert.Equal(t, configMapWant, configMap) } diff --git a/internal/controller/datadogagent/feature/otelcollector/const.go b/internal/controller/datadogagent/feature/otelcollector/const.go index dc24a5fd4..e6d5cd700 100644 --- a/internal/controller/datadogagent/feature/otelcollector/const.go +++ b/internal/controller/datadogagent/feature/otelcollector/const.go @@ -5,9 +5,20 @@ package otelcollector +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + const ( otelAgentVolumeName = "otel-agent-config-volume" otelConfigFileName = "otel-config.yaml" // DefaultOTelAgentConf default otel agent ConfigMap name defaultOTelAgentConf string = "otel-agent-config" ) + +// getRBACResourceName return the RBAC resources name +func getRBACResourceName(owner metav1.Object) string { + return fmt.Sprintf("%s-%s", owner.GetName(), "otel-agent") +} diff --git a/internal/controller/datadogagent/feature/otelcollector/feature.go b/internal/controller/datadogagent/feature/otelcollector/feature.go index 23b81beab..f8b24a10e 100644 --- a/internal/controller/datadogagent/feature/otelcollector/feature.go +++ b/internal/controller/datadogagent/feature/otelcollector/feature.go @@ -1,10 +1,12 @@ package otelcollector import ( + "fmt" "strconv" "strings" "github.com/go-logr/logr" + "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -17,6 +19,7 @@ import ( "github.com/DataDog/datadog-operator/internal/controller/datadogagent/object" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/object/configmap" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/object/volume" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/store" "github.com/DataDog/datadog-operator/pkg/constants" "github.com/DataDog/datadog-operator/pkg/controller/utils/comparison" "github.com/DataDog/datadog-operator/pkg/images" @@ -41,11 +44,13 @@ func buildOtelCollectorFeature(options *feature.Options) feature.Feature { } type otelCollectorFeature struct { - customConfig *v2alpha1.CustomConfig - owner metav1.Object - configMapName string - ports []*corev1.ContainerPort - coreAgentConfig coreAgentConfig + customConfig *v2alpha1.CustomConfig + owner metav1.Object + configMapName string + ports []*corev1.ContainerPort + coreAgentConfig coreAgentConfig + createRBAC bool + serviceAccountName string customConfigAnnotationKey string customConfigAnnotationValue string @@ -95,6 +100,9 @@ func (o *otelCollectorFeature) Configure(dda *v2alpha1.DatadogAgent) feature.Req o.ports = dda.Spec.Features.OtelCollector.Ports } + o.createRBAC = apiutils.BoolValue(dda.Spec.Features.OtelCollector.CreateRbac) + o.serviceAccountName = constants.GetAgentServiceAccount(dda.Name, &dda.Spec) + var reqComp feature.RequiredComponents if apiutils.BoolValue(dda.Spec.Features.OtelCollector.Enabled) { reqComp = feature.RequiredComponents{ @@ -106,64 +114,147 @@ func (o *otelCollectorFeature) Configure(dda *v2alpha1.DatadogAgent) feature.Req }, }, } - } return reqComp } -func (o *otelCollectorFeature) buildOTelAgentCoreConfigMap() (*corev1.ConfigMap, error) { +// getEffectiveConfig returns the effective OpenTelemetry configuration +// It handles both custom config and default config cases +func (o *otelCollectorFeature) getEffectiveConfig(store store.StoreClient) (string, error) { + // if custom config is provided via ConfigData if o.customConfig != nil && o.customConfig.ConfigData != nil { - cm, err := configmap.BuildConfigMapConfigData(o.owner.GetNamespace(), o.customConfig.ConfigData, o.configMapName, otelConfigFileName) - if err != nil { - return nil, err + return *o.customConfig.ConfigData, nil + } + + // if custom config is provided via ConfigMap + if o.customConfig != nil && o.customConfig.ConfigMap != nil { + ns := o.owner.GetNamespace() + name := o.customConfig.ConfigMap.Name + + obj, ok := store.Get(kubernetes.ConfigMapKind, ns, name) + if !ok { + return "", fmt.Errorf("unable to get ConfigMap %s/%s from the store", ns, name) } - // Add md5 hash annotation for configMap - o.customConfigAnnotationKey = object.GetChecksumAnnotationKey(feature.OtelAgentIDType) - o.customConfigAnnotationValue, err = comparison.GenerateMD5ForSpec(o.customConfig.ConfigData) - if err != nil { - return cm, err + cm, ok := obj.(*corev1.ConfigMap) + if !ok { + return "", fmt.Errorf("configMap %s/%s is not a corev1.ConfigMap", ns, name) } - if o.customConfigAnnotationKey != "" && o.customConfigAnnotationValue != "" { - annotations := object.MergeAnnotationsLabels(o.logger, cm.Annotations, map[string]string{o.customConfigAnnotationKey: o.customConfigAnnotationValue}, "*") - cm.SetAnnotations(annotations) + configData, ok := cm.Data[otelConfigFileName] + if !ok { + return "", fmt.Errorf("configMap %s/%s does not contain otel-config.yaml", ns, name) } - return cm, nil + return configData, nil + } + + // use default config and override ports if needed + configData := defaultconfig.DefaultOtelCollectorConfig + for _, port := range o.ports { + if port.Name == "otel-grpc" { + configData = strings.Replace(configData, "4317", strconv.Itoa(int(port.ContainerPort)), 1) + } + if port.Name == "otel-http" { + configData = strings.Replace(configData, "4318", strconv.Itoa(int(port.ContainerPort)), 1) + } } - return nil, nil + + return configData, nil } -func (o *otelCollectorFeature) ManageDependencies(managers feature.ResourceManagers) error { - // check if an otel collector config was provided. If not, use default. +func (o *otelCollectorFeature) buildOTelAgentCoreConfigMap(configData *string) (*corev1.ConfigMap, error) { + if configData == nil { + return nil, fmt.Errorf("otelCollector configData is nil") + } + if *configData == "" { + return nil, fmt.Errorf("otelCollector configData is empty") + } + + // if custom config is not provided, build one with the configData if o.customConfig == nil { - o.customConfig = &v2alpha1.CustomConfig{} + o.customConfig = &v2alpha1.CustomConfig{ConfigData: configData} } - if o.customConfig.ConfigData == nil && o.customConfig.ConfigMap == nil { - var defaultConfig = defaultconfig.DefaultOtelCollectorConfig - for _, port := range o.ports { - if port.Name == "otel-grpc" { - defaultConfig = strings.Replace(defaultConfig, "4317", strconv.Itoa(int(port.ContainerPort)), 1) - } - if port.Name == "otel-http" { - defaultConfig = strings.Replace(defaultConfig, "4318", strconv.Itoa(int(port.ContainerPort)), 1) + + cm, err := configmap.BuildConfigMapConfigData(o.owner.GetNamespace(), o.customConfig.ConfigData, o.configMapName, otelConfigFileName) + if err != nil { + return nil, err + } + + // Add md5 hash annotation for configMap + o.customConfigAnnotationKey = object.GetChecksumAnnotationKey(feature.OtelAgentIDType) + o.customConfigAnnotationValue, err = comparison.GenerateMD5ForSpec(o.customConfig.ConfigData) + if err != nil { + return cm, err + } + + if o.customConfigAnnotationKey != "" && o.customConfigAnnotationValue != "" { + annotations := object.MergeAnnotationsLabels(o.logger, cm.Annotations, map[string]string{o.customConfigAnnotationKey: o.customConfigAnnotationValue}, "*") + cm.SetAnnotations(annotations) + } + + return cm, nil +} + +// isK8sattributesRBACRequired checks if the OTel configuration has k8sattributes processor(s) enabled +// and whether any of them are configured with passthrough mode +func (o *otelCollectorFeature) isK8sattributesRBACRequired(configData string) (bool, error) { + // otelConfig represents the OpenTelemetry Collector configuration structure + type otelConfig struct { + Processors map[string]struct { + Passthrough bool `yaml:"passthrough"` + } `yaml:"processors"` + } + + var config otelConfig + if err := yaml.Unmarshal([]byte(configData), &config); err != nil { + o.logger.Error(err, "failed to parse OpenTelemetry configuration") + return false, err + } + + required := false + for processorName, processorConfig := range config.Processors { + if strings.HasPrefix(processorName, "k8sattributes") { + // if any k8sattributes processor is not in passthrough mode, we need RBAC + if !processorConfig.Passthrough { + required = true + break } } - o.customConfig.ConfigData = &defaultConfig } - // create configMap if customConfig is provided - configMap, err := o.buildOTelAgentCoreConfigMap() + return required, nil +} + +func (o *otelCollectorFeature) ManageDependencies(managers feature.ResourceManagers) error { + configData, err := o.getEffectiveConfig(managers.Store()) if err != nil { return err } - if configMap != nil { + // if custom config is not provided via external ConfigMap, we need to create a configMap + if !(o.customConfig != nil && o.customConfig.ConfigMap != nil) { + configMap, err := o.buildOTelAgentCoreConfigMap(&configData) + if err != nil { + return err + } + if err := managers.Store().AddOrUpdate(kubernetes.ConfigMapKind, configMap); err != nil { return err } } + + // Manage RBAC permission + if o.createRBAC { + rbacRequired, err := o.isK8sattributesRBACRequired(configData) + if err != nil { + return err + } + if rbacRequired { + managers.RBACManager().AddClusterPolicyRules(o.owner.GetNamespace(), getRBACResourceName(o.owner), o.serviceAccountName, getK8sAttributesRBACPolicyRules()) + } + } + return nil } diff --git a/internal/controller/datadogagent/feature/otelcollector/feature_test.go b/internal/controller/datadogagent/feature/otelcollector/feature_test.go index 7ded97efe..97bacac1b 100644 --- a/internal/controller/datadogagent/feature/otelcollector/feature_test.go +++ b/internal/controller/datadogagent/feature/otelcollector/feature_test.go @@ -15,6 +15,7 @@ import ( "github.com/DataDog/datadog-operator/pkg/images" "github.com/DataDog/datadog-operator/pkg/kubernetes" "github.com/DataDog/datadog-operator/pkg/testutils" + rbacv1 "k8s.io/api/rbac/v1" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" @@ -72,9 +73,52 @@ var ( value: "60", }, } + + otelCollectorWithK8sattr = ` +receivers: + otlp: +exporters: + datadog: + api: + key: "" +processors: + k8sattributes: + k8sattributes/2: + passthrough: false +service: + pipelines: + metrics: + receivers: [otlp] + processors: [k8sattributes] + exporters: [datadog] + logs: + receivers: [otlp] + processors: [k8sattributes/2] + exporters: [datadog]` + + otelCollectorWithK8sPassthrough = ` +receivers: + otlp: +exporters: + datadog: + api: + key: "" +processors: + k8sattributes: + passthrough: true +service: + pipelines: + metrics: + receivers: [otlp] + processors: [k8sattributes] + exporters: [datadog]` ) -var defaultAnnotations = map[string]string{"checksum/otel_agent-custom-config": "c609e2fb7352676a67f0423b58970d43"} +var ( + defaultAnnotations = map[string]string{"checksum/otel_agent-custom-config": "c609e2fb7352676a67f0423b58970d43"} + k8sattrAnnotations = map[string]string{"checksum/otel_agent-custom-config": "63221702e9c19d4ab236b9ac9707b60e"} + k8sPassAnnotations = map[string]string{"checksum/otel_agent-custom-config": "509ba152a3e96df2ffd1b750b0610145"} +) func Test_otelCollectorFeature_Configure(t *testing.T) { tests := test.FeatureTestSuite{ @@ -111,9 +155,16 @@ func Test_otelCollectorFeature_Configure(t *testing.T) { WithOTelCollectorEnabled(true). WithOTelCollectorConfigMap(). Build(), - WantConfigure: true, - WantDependenciesFunc: testExpectedDepsCreatedCM, - Agent: testExpectedAgent(apicommon.OtelAgent, defaultExpectedPorts, "user-provided-config-map", defaultExpectedEnvVars, map[string]string{}), + StoreInitFunc: initExternalCM( + "otel-config.yaml", + defaultconfig.DefaultOtelCollectorConfig, + ), + WantConfigure: true, + WantDependenciesFunc: func(t testing.TB, store store.StoreClient) { + testConfigMapIsNotCreated(t, store) + testRBACIsNotCreated(t, store) + }, + Agent: testExpectedAgent(apicommon.OtelAgent, defaultExpectedPorts, "user-provided-config-map", defaultExpectedEnvVars, map[string]string{}), }, { Name: "otel agent enabled without config", @@ -141,6 +192,129 @@ func Test_otelCollectorFeature_Configure(t *testing.T) { map[string]string{"checksum/otel_agent-custom-config": "8fd9e6854714be53bd838063a4111c96"}, ), }, + { + Name: "otel agent enabled with k8sattribute config without RBAC", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfigData(otelCollectorWithK8sattr). + WithOTelCollectorCreateRBAC(false). + Build(), + WantConfigure: true, + WantDependenciesFunc: func(t testing.TB, store store.StoreClient) { + testConfigMapIsCreated(t, store, otelCollectorWithK8sattr) + testRBACIsNotCreated(t, store) + }, + Agent: testExpectedAgent( + apicommon.OtelAgent, + defaultExpectedPorts, + defaultLocalObjectReferenceName, + defaultExpectedEnvVars, + k8sattrAnnotations, + ), + }, + { + Name: "otel agent enabled without k8sattribute config with RBAC", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfig(). + WithOTelCollectorCreateRBAC(true). + Build(), + WantConfigure: true, + WantDependenciesFunc: func(t testing.TB, store store.StoreClient) { + testConfigMapIsCreated(t, store, defaultconfig.DefaultOtelCollectorConfig) + testRBACIsNotCreated(t, store) + }, + Agent: testExpectedAgent( + apicommon.OtelAgent, + defaultExpectedPorts, + defaultLocalObjectReferenceName, + defaultExpectedEnvVars, + defaultAnnotations, + ), + }, + { + Name: "otel agent enabled with k8sattribute passthrough config with RBAC", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfigData(otelCollectorWithK8sPassthrough). + WithOTelCollectorCreateRBAC(true). + Build(), + WantConfigure: true, + WantDependenciesFunc: func(t testing.TB, store store.StoreClient) { + testConfigMapIsCreated(t, store, otelCollectorWithK8sPassthrough) + testRBACIsNotCreated(t, store) + }, + Agent: testExpectedAgent( + apicommon.OtelAgent, + defaultExpectedPorts, + defaultLocalObjectReferenceName, + defaultExpectedEnvVars, + k8sPassAnnotations, + ), + }, + { + Name: "otel agent enabled with external configMap with RBAC", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfigMap(). + WithOTelCollectorCreateRBAC(true). + Build(), + StoreInitFunc: initExternalCM("otel-config.yaml", otelCollectorWithK8sattr), + WantConfigure: true, + WantDependenciesFunc: func(t testing.TB, store store.StoreClient) { + testConfigMapIsNotCreated(t, store) + testRBACIsCreated(t, store) + }, + Agent: testExpectedAgent( + apicommon.OtelAgent, + defaultExpectedPorts, + "user-provided-config-map", + defaultExpectedEnvVars, + map[string]string{}, + ), + }, + { + Name: "otel agent enabled with RBAC and invalid config", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfigData("invalid yaml as otel config"). + WithOTelCollectorCreateRBAC(true). + Build(), + WantConfigure: true, + WantManageDependenciesErr: true, + }, + { + Name: "otel agent enabled with RBAC and invalid external CM", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfigMap(). + WithOTelCollectorCreateRBAC(true). + Build(), + StoreInitFunc: initExternalCM("otel-config.yaml", "invalid yaml as otel config"), + WantConfigure: true, + WantManageDependenciesErr: true, + }, + { + Name: "otel agent enabled with RBAC and external CM with wrong key", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfigMap(). + WithOTelCollectorCreateRBAC(true). + Build(), + StoreInitFunc: initExternalCM("wrong-path.yaml", otelCollectorWithK8sattr), + WantConfigure: true, + WantManageDependenciesErr: true, + }, + { + Name: "otel agent enabled with empty config", + DDA: testutils.NewDatadogAgentBuilder(). + WithOTelCollectorEnabled(true). + WithOTelCollectorConfigData(""). + WithOTelCollectorCreateRBAC(true). + Build(), + WantConfigure: true, + WantManageDependenciesErr: true, + }, // coreconfig { Name: "otel agent coreconfig enabled", @@ -405,4 +579,69 @@ func testExpectedDepsCreatedCM(t testing.TB, store store.StoreClient) { apiutils.IsEqualStruct(configMap.Data, expectedCM), "ConfigMap \ndiff = %s", cmp.Diff(configMap.Data, expectedCM), ) + + // check RBAC + _, found = store.Get(kubernetes.ClusterRoleBindingKind, "", "-otel-agent") + assert.False(t, found) +} + +// func initExternalCM(store store.StoreClient) { +// obj, _ := store.GetOrCreate(kubernetes.ConfigMapKind, "", "user-provided-config-map") +// cm := obj.(*corev1.ConfigMap) +// cm.Data = map[string]string{"otel-config.yaml": otelCollectorWithK8sattr} +// store.AddOrUpdate(kubernetes.ConfigMapKind, cm) +// } + +func initExternalCM(k, v string) func(store store.StoreClient) { + return func(store store.StoreClient) { + obj, _ := store.GetOrCreate(kubernetes.ConfigMapKind, "", "user-provided-config-map") + cm := obj.(*corev1.ConfigMap) + cm.Data = map[string]string{k: v} + store.AddOrUpdate(kubernetes.ConfigMapKind, cm) + } +} + +func testConfigMapIsCreated(t testing.TB, store store.StoreClient, configData string) { + configMapObject, found := store.Get(kubernetes.ConfigMapKind, "", "-otel-agent-config") + assert.True(t, found) + + configMap := configMapObject.(*corev1.ConfigMap) + expectedCM := map[string]string{"otel-config.yaml": configData} + assert.True(t, apiutils.IsEqualStruct(configMap.Data, expectedCM), "ConfigMap \ndiff = %s", cmp.Diff(configMap.Data, expectedCM)) +} + +func testConfigMapIsNotCreated(t testing.TB, store store.StoreClient) { + _, found := store.Get(kubernetes.ConfigMapKind, "", "-otel-agent-config") + assert.False(t, found) +} + +func testRBACIsCreated(t testing.TB, store store.StoreClient) { + _, found := store.Get(kubernetes.ClusterRoleBindingKind, "", "-otel-agent") + assert.True(t, found) + + obj, found := store.Get(kubernetes.ClusterRolesKind, "", "-otel-agent") + assert.True(t, found) + role := obj.(*rbacv1.ClusterRole) + assert.Equal(t, role.Rules, []rbacv1.PolicyRule{ + rbacv1.PolicyRule{ + APIGroups: []string{""}, + Resources: []string{"pods", "namespaces"}, + Verbs: []string{"get", "list", "watch"}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{"apps"}, + Resources: []string{"replicasets"}, + Verbs: []string{"get", "list", "watch"}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{"extensions"}, + Resources: []string{"replicasets"}, + Verbs: []string{"get", "list", "watch"}, + }, + }) +} + +func testRBACIsNotCreated(t testing.TB, store store.StoreClient) { + _, found := store.Get(kubernetes.ClusterRoleBindingKind, "", "-otel-agent") + assert.False(t, found) } diff --git a/internal/controller/datadogagent/feature/otelcollector/rbac.go b/internal/controller/datadogagent/feature/otelcollector/rbac.go new file mode 100644 index 000000000..1066fbc9c --- /dev/null +++ b/internal/controller/datadogagent/feature/otelcollector/rbac.go @@ -0,0 +1,48 @@ +package otelcollector + +import ( + rbacv1 "k8s.io/api/rbac/v1" + + "github.com/DataDog/datadog-operator/pkg/kubernetes/rbac" +) + +// getK8sAttributesRBACPolicyRules generates the cluster role permissions +// required for the OpenTelemetry collector with k8sattributes processor. +func getK8sAttributesRBACPolicyRules() []rbacv1.PolicyRule { + return []rbacv1.PolicyRule{ + { + APIGroups: []string{rbac.CoreAPIGroup}, + Resources: []string{ + rbac.PodsResource, + rbac.NamespaceResource, + }, + Verbs: []string{ + rbac.GetVerb, + rbac.ListVerb, + rbac.WatchVerb, + }, + }, + { + APIGroups: []string{rbac.AppsAPIGroup}, + Resources: []string{ + rbac.ReplicasetsResource, + }, + Verbs: []string{ + rbac.GetVerb, + rbac.ListVerb, + rbac.WatchVerb, + }, + }, + { + APIGroups: []string{rbac.ExtensionsAPIGroup}, + Resources: []string{ + rbac.ReplicasetsResource, + }, + Verbs: []string{ + rbac.GetVerb, + rbac.ListVerb, + rbac.WatchVerb, + }, + }, + } +} diff --git a/pkg/testutils/builder.go b/pkg/testutils/builder.go index 3937c1565..e4e233de6 100644 --- a/pkg/testutils/builder.go +++ b/pkg/testutils/builder.go @@ -406,6 +406,17 @@ func (builder *DatadogAgentBuilder) WithOTelCollectorConfig() *DatadogAgentBuild return builder } +func (builder *DatadogAgentBuilder) WithOTelCollectorConfigData(configData string) *DatadogAgentBuilder { + builder.datadogAgent.Spec.Features.OtelCollector.Conf = &v2alpha1.CustomConfig{} + builder.datadogAgent.Spec.Features.OtelCollector.Conf.ConfigData = apiutils.NewStringPointer(configData) + return builder +} + +func (builder *DatadogAgentBuilder) WithOTelCollectorCreateRBAC(createRBAC bool) *DatadogAgentBuilder { + builder.datadogAgent.Spec.Features.OtelCollector.CreateRbac = apiutils.NewBoolPointer(createRBAC) + return builder +} + func (builder *DatadogAgentBuilder) WithOTelCollectorCoreConfigEnabled(enabled bool) *DatadogAgentBuilder { if builder.datadogAgent.Spec.Features.OtelCollector.CoreConfig == nil { builder.datadogAgent.Spec.Features.OtelCollector.CoreConfig = &v2alpha1.CoreConfig{}