From 99aee4519ccb41a20dc4cef2abafd3eff244af7d Mon Sep 17 00:00:00 2001 From: Chris Bandy Date: Sat, 1 Mar 2025 14:24:38 -0600 Subject: [PATCH] Provide a way to enable resource detectors Resource detectors are a simple way to gather resource attributes from the environment automatically. They are especially useful in managed or cloud environments. Issue: PGO-2193 --- ...res-operator.crunchydata.com_pgadmins.yaml | 40 +++++++++++++++++-- ...ator.crunchydata.com_postgresclusters.yaml | 40 +++++++++++++++++-- internal/collector/config.go | 29 ++++++++++++++ internal/collector/config_test.go | 35 ++++++++++++++++ internal/collector/naming.go | 1 + internal/collector/patroni.go | 1 + internal/collector/patroni_test.go | 10 +++++ internal/collector/pgadmin.go | 2 + internal/collector/pgadmin_test.go | 12 ++++++ internal/collector/pgbackrest.go | 1 + internal/collector/pgbackrest_test.go | 10 +++++ internal/collector/pgbouncer.go | 1 + internal/collector/pgbouncer_test.go | 10 +++++ internal/collector/postgres.go | 2 + internal/collector/postgres_test.go | 12 ++++++ .../v1beta1/instrumentation_types.go | 40 +++++++++++++++++-- .../v1beta1/zz_generated.deepcopy.go | 29 ++++++++++++++ 17 files changed, 266 insertions(+), 9 deletions(-) diff --git a/config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml b/config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml index 1a6f12d690..1d3f1635a8 100644 --- a/config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml +++ b/config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml @@ -1605,6 +1605,38 @@ spec: description: Config is the place for users to configure exporters and provide files. properties: + detectors: + description: |- + Resource detectors add identifying attributes to logs and metrics. These run in the order they are defined. + More info: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/-/processor/resourcedetectionprocessor#readme + items: + properties: + attributes: + additionalProperties: + type: boolean + description: |- + Attributes to use from this detector. Detectors usually add every attribute + they know automatically. Names omitted here behave according to detector defaults. + maxProperties: 30 + minProperties: 1 + type: object + x-kubernetes-map-type: atomic + name: + description: 'Name of the resource detector to enable: + `aks`, `eks`, `gcp`, etc.' + maxLength: 20 + minLength: 1 + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map exporters: description: |- Exporters allows users to configure OpenTelemetry exporters that exist @@ -1937,7 +1969,9 @@ spec: - path type: object type: object + minItems: 1 type: array + x-kubernetes-list-type: atomic type: object image: description: |- @@ -1989,12 +2023,12 @@ spec: - message: minRecords cannot be larger than maxRecords rule: '!has(self.maxRecords) || self.minRecords <= self.maxRecords' exporters: - description: |- - Exporters allows users to specify which exporters they want to use in - the logs pipeline. + description: The names of exporters that should send logs. items: type: string + minItems: 1 type: array + x-kubernetes-list-type: set retentionPeriod: description: |- How long to retain log files locally. An RFC 3339 duration or a number diff --git a/config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml b/config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml index 606ae4db59..abd625f827 100644 --- a/config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml +++ b/config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml @@ -11181,6 +11181,38 @@ spec: description: Config is the place for users to configure exporters and provide files. properties: + detectors: + description: |- + Resource detectors add identifying attributes to logs and metrics. These run in the order they are defined. + More info: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/-/processor/resourcedetectionprocessor#readme + items: + properties: + attributes: + additionalProperties: + type: boolean + description: |- + Attributes to use from this detector. Detectors usually add every attribute + they know automatically. Names omitted here behave according to detector defaults. + maxProperties: 30 + minProperties: 1 + type: object + x-kubernetes-map-type: atomic + name: + description: 'Name of the resource detector to enable: + `aks`, `eks`, `gcp`, etc.' + maxLength: 20 + minLength: 1 + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map exporters: description: |- Exporters allows users to configure OpenTelemetry exporters that exist @@ -11513,7 +11545,9 @@ spec: - path type: object type: object + minItems: 1 type: array + x-kubernetes-list-type: atomic type: object image: description: |- @@ -11565,12 +11599,12 @@ spec: - message: minRecords cannot be larger than maxRecords rule: '!has(self.maxRecords) || self.minRecords <= self.maxRecords' exporters: - description: |- - Exporters allows users to specify which exporters they want to use in - the logs pipeline. + description: The names of exporters that should send logs. items: type: string + minItems: 1 type: array + x-kubernetes-list-type: set retentionPeriod: description: |- How long to retain log files locally. An RFC 3339 duration or a number diff --git a/internal/collector/config.go b/internal/collector/config.go index 4f6e563c32..f8ac307b35 100644 --- a/internal/collector/config.go +++ b/internal/collector/config.go @@ -137,6 +137,35 @@ func NewConfig(spec *v1beta1.InstrumentationSpec) *Config { config.Processors[LogsBatchProcessor] = processor } + // Create a resource detection processor according to the API spec. + // When nothing is specified, the processor does nothing. + { + // https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor#section-readme + processor := map[string]any{"override": false, "timeout": "30s"} + + if spec != nil && spec.Config != nil { + names := make([]string, len(spec.Config.Detectors)) + for i, detector := range spec.Config.Detectors { + names[i] = detector.Name + + if len(detector.Attributes) > 0 { + attributes := make(map[string]any, len(detector.Attributes)) + for k, v := range detector.Attributes { + attributes[k] = map[string]any{"enabled": v} + } + processor[detector.Name] = map[string]any{ + "resource_attributes": attributes, + } + } + } + processor["detectors"] = names + } else { + processor["detectors"] = []string{} + } + + config.Processors[ResourceDetectionProcessor] = processor + } + // If there are exporters defined in the spec, add them to the config. if spec != nil && spec.Config != nil && spec.Config.Exporters != nil { for k, v := range spec.Config.Exporters { diff --git a/internal/collector/config_test.go b/internal/collector/config_test.go index 5fbc551761..c38ae99059 100644 --- a/internal/collector/config_test.go +++ b/internal/collector/config_test.go @@ -33,6 +33,10 @@ processors: send_batch_size: 8192 timeout: 200ms groupbyattrs/compact: {} + resourcedetection: + detectors: [] + override: false + timeout: 30s receivers: {} service: extensions: [] @@ -64,6 +68,10 @@ processors: send_batch_size: 8192 timeout: 200ms groupbyattrs/compact: {} + resourcedetection: + detectors: [] + override: false + timeout: 30s receivers: {} service: extensions: [] @@ -109,6 +117,33 @@ service: `)) }) }) + + t.Run("Detectors", func(t *testing.T) { + var spec *v1beta1.InstrumentationSpec + require.UnmarshalInto(t, &spec, `{ + config: { + detectors: [ + { name: gcp }, + { name: aks, attributes: { k8s.cluster.name: true } }, + ], + }, + }`) + + result, err := NewConfig(spec).ToYAML() + assert.NilError(t, err) + assert.Assert(t, cmp.Contains(result, ` + resourcedetection: + aks: + resource_attributes: + k8s.cluster.name: + enabled: true + detectors: + - gcp + - aks + override: false + timeout: 30s +`)) + }) } func TestGenerateLogrotateConfig(t *testing.T) { diff --git a/internal/collector/naming.go b/internal/collector/naming.go index a555752b65..964d3d4d13 100644 --- a/internal/collector/naming.go +++ b/internal/collector/naming.go @@ -13,6 +13,7 @@ const Prometheus = "prometheus" const PGBouncerMetrics = "metrics/pgbouncer" const PostgresMetrics = "metrics/postgres" const PatroniMetrics = "metrics/patroni" +const ResourceDetectionProcessor = "resourcedetection" const SqlQuery = "sqlquery" diff --git a/internal/collector/patroni.go b/internal/collector/patroni.go index 987c542f58..60305b458b 100644 --- a/internal/collector/patroni.go +++ b/internal/collector/patroni.go @@ -120,6 +120,7 @@ func EnablePatroniLogging(ctx context.Context, Processors: []ComponentID{ "resource/patroni", "transform/patroni_logs", + ResourceDetectionProcessor, LogsBatchProcessor, CompactingProcessor, }, diff --git a/internal/collector/patroni_test.go b/internal/collector/patroni_test.go index 93f7e133e7..e2d3a84e58 100644 --- a/internal/collector/patroni_test.go +++ b/internal/collector/patroni_test.go @@ -58,6 +58,10 @@ processors: - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/patroni_logs: log_statements: - context: log @@ -92,6 +96,7 @@ service: processors: - resource/patroni - transform/patroni_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -148,6 +153,10 @@ processors: - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/patroni_logs: log_statements: - context: log @@ -182,6 +191,7 @@ service: processors: - resource/patroni - transform/patroni_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: diff --git a/internal/collector/pgadmin.go b/internal/collector/pgadmin.go index c2a197dca9..e22ed621f0 100644 --- a/internal/collector/pgadmin.go +++ b/internal/collector/pgadmin.go @@ -101,6 +101,7 @@ func EnablePgAdminLogging(ctx context.Context, spec *v1beta1.InstrumentationSpec Processors: []ComponentID{ "resource/pgadmin", "transform/pgadmin_log", + ResourceDetectionProcessor, LogsBatchProcessor, CompactingProcessor, }, @@ -113,6 +114,7 @@ func EnablePgAdminLogging(ctx context.Context, spec *v1beta1.InstrumentationSpec Processors: []ComponentID{ "resource/pgadmin", "transform/pgadmin_log", + ResourceDetectionProcessor, LogsBatchProcessor, CompactingProcessor, }, diff --git a/internal/collector/pgadmin_test.go b/internal/collector/pgadmin_test.go index 8d9ec472c6..c4d5acfab6 100644 --- a/internal/collector/pgadmin_test.go +++ b/internal/collector/pgadmin_test.go @@ -66,6 +66,10 @@ collector.yaml: | - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgadmin_log: log_statements: - context: log @@ -102,6 +106,7 @@ collector.yaml: | processors: - resource/pgadmin - transform/pgadmin_log + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -112,6 +117,7 @@ collector.yaml: | processors: - resource/pgadmin - transform/pgadmin_log + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -181,6 +187,10 @@ collector.yaml: | - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgadmin_log: log_statements: - context: log @@ -217,6 +227,7 @@ collector.yaml: | processors: - resource/pgadmin - transform/pgadmin_log + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -227,6 +238,7 @@ collector.yaml: | processors: - resource/pgadmin - transform/pgadmin_log + - resourcedetection - batch/logs - groupbyattrs/compact receivers: diff --git a/internal/collector/pgbackrest.go b/internal/collector/pgbackrest.go index d712365b2b..569748ed9c 100644 --- a/internal/collector/pgbackrest.go +++ b/internal/collector/pgbackrest.go @@ -104,6 +104,7 @@ func NewConfigForPgBackrestRepoHostPod( Processors: []ComponentID{ "resource/pgbackrest", "transform/pgbackrest_logs", + ResourceDetectionProcessor, LogsBatchProcessor, CompactingProcessor, }, diff --git a/internal/collector/pgbackrest_test.go b/internal/collector/pgbackrest_test.go index 97df0cf35d..f1ebf14e4f 100644 --- a/internal/collector/pgbackrest_test.go +++ b/internal/collector/pgbackrest_test.go @@ -62,6 +62,10 @@ processors: - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgbackrest_logs: log_statements: - context: log @@ -99,6 +103,7 @@ service: processors: - resource/pgbackrest - transform/pgbackrest_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -157,6 +162,10 @@ processors: - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgbackrest_logs: log_statements: - context: log @@ -194,6 +203,7 @@ service: processors: - resource/pgbackrest - transform/pgbackrest_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: diff --git a/internal/collector/pgbouncer.go b/internal/collector/pgbouncer.go index 403b95a3de..f1f150f6f4 100644 --- a/internal/collector/pgbouncer.go +++ b/internal/collector/pgbouncer.go @@ -159,6 +159,7 @@ func EnablePgBouncerLogging(ctx context.Context, Processors: []ComponentID{ "resource/pgbouncer", "transform/pgbouncer_logs", + ResourceDetectionProcessor, LogsBatchProcessor, CompactingProcessor, }, diff --git a/internal/collector/pgbouncer_test.go b/internal/collector/pgbouncer_test.go index 371cc850cd..df8427fbbd 100644 --- a/internal/collector/pgbouncer_test.go +++ b/internal/collector/pgbouncer_test.go @@ -58,6 +58,10 @@ processors: - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgbouncer_logs: log_statements: - context: log @@ -93,6 +97,7 @@ service: processors: - resource/pgbouncer - transform/pgbouncer_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -150,6 +155,10 @@ processors: - action: insert key: k8s.pod.name value: ${env:K8S_POD_NAME} + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgbouncer_logs: log_statements: - context: log @@ -185,6 +194,7 @@ service: processors: - resource/pgbouncer - transform/pgbouncer_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: diff --git a/internal/collector/postgres.go b/internal/collector/postgres.go index 299364db6b..cfc0b88245 100644 --- a/internal/collector/postgres.go +++ b/internal/collector/postgres.go @@ -225,6 +225,7 @@ func EnablePostgresLogging( Processors: []ComponentID{ "resource/postgres", "transform/postgres_logs", + ResourceDetectionProcessor, LogsBatchProcessor, CompactingProcessor, }, @@ -279,6 +280,7 @@ func EnablePostgresLogging( Processors: []ComponentID{ "resource/pgbackrest", "transform/pgbackrest_logs", + ResourceDetectionProcessor, LogsBatchProcessor, CompactingProcessor, }, diff --git a/internal/collector/postgres_test.go b/internal/collector/postgres_test.go index d934a920f4..a6736d66cc 100644 --- a/internal/collector/postgres_test.go +++ b/internal/collector/postgres_test.go @@ -84,6 +84,10 @@ processors: - action: insert key: db.version value: "99" + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgbackrest_logs: log_statements: - context: log @@ -228,6 +232,7 @@ service: processors: - resource/pgbackrest - transform/pgbackrest_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -238,6 +243,7 @@ service: processors: - resource/postgres - transform/postgres_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -319,6 +325,10 @@ processors: - action: insert key: db.version value: "99" + resourcedetection: + detectors: [] + override: false + timeout: 30s transform/pgbackrest_logs: log_statements: - context: log @@ -463,6 +473,7 @@ service: processors: - resource/pgbackrest - transform/pgbackrest_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: @@ -473,6 +484,7 @@ service: processors: - resource/postgres - transform/postgres_logs + - resourcedetection - batch/logs - groupbyattrs/compact receivers: diff --git a/pkg/apis/postgres-operator.crunchydata.com/v1beta1/instrumentation_types.go b/pkg/apis/postgres-operator.crunchydata.com/v1beta1/instrumentation_types.go index 6ec31c9e1f..8c6272d1f1 100644 --- a/pkg/apis/postgres-operator.crunchydata.com/v1beta1/instrumentation_types.go +++ b/pkg/apis/postgres-operator.crunchydata.com/v1beta1/instrumentation_types.go @@ -12,7 +12,6 @@ type InstrumentationSpec struct { // Image name to use for collector containers. When omitted, the value // comes from an operator environment variable. // +optional - // +operator-sdk:csv:customresourcedefinitions:type=spec,order=1 Image string `json:"image,omitempty"` // Resources holds the resource requirements for the collector container. @@ -31,6 +30,16 @@ type InstrumentationSpec struct { // InstrumentationConfigSpec allows users to configure their own exporters, // add files, etc. type InstrumentationConfigSpec struct { + // Resource detectors add identifying attributes to logs and metrics. These run in the order they are defined. + // More info: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/-/processor/resourcedetectionprocessor#readme + // --- + // +kubebuilder:validation:MaxItems=10 + // +kubebuilder:validation:MinItems=1 + // +listMapKey=name + // +listType=map + // +optional + Detectors []OpenTelemetryResourceDetector `json:"detectors,omitempty"` + // Exporters allows users to configure OpenTelemetry exporters that exist // in the collector image. // +kubebuilder:pruning:PreserveUnknownFields @@ -41,6 +50,9 @@ type InstrumentationConfigSpec struct { // Files allows the user to mount projected volumes into the collector // Pod so that files can be referenced by the collector as needed. + // --- + // +kubebuilder:validation:MinItems=1 + // +listType=atomic // +optional Files []corev1.VolumeProjection `json:"files,omitempty"` } @@ -53,8 +65,10 @@ type InstrumentationLogsSpec struct { // +optional Batches *OpenTelemetryLogsBatchSpec `json:"batches,omitempty"` - // Exporters allows users to specify which exporters they want to use in - // the logs pipeline. + // The names of exporters that should send logs. + // --- + // +kubebuilder:validation:MinItems=1 + // +listType=set // +optional Exporters []string `json:"exporters,omitempty"` @@ -132,3 +146,23 @@ func (s *OpenTelemetryLogsBatchSpec) Default() { *s.MinRecords = 8192 } } + +// --- +// +structType=atomic +type OpenTelemetryResourceDetector struct { + // Name of the resource detector to enable: `aks`, `eks`, `gcp`, etc. + // --- + // +kubebuilder:validation:MaxLength=20 + // +kubebuilder:validation:MinLength=1 + // +required + Name string `json:"name"` + + // Attributes to use from this detector. Detectors usually add every attribute + // they know automatically. Names omitted here behave according to detector defaults. + // --- + // +kubebuilder:validation:MaxProperties=30 + // +kubebuilder:validation:MinProperties=1 + // +mapType=atomic + // +optional + Attributes map[string]bool `json:"attributes,omitempty"` +} diff --git a/pkg/apis/postgres-operator.crunchydata.com/v1beta1/zz_generated.deepcopy.go b/pkg/apis/postgres-operator.crunchydata.com/v1beta1/zz_generated.deepcopy.go index 26a7138059..875d1ce000 100644 --- a/pkg/apis/postgres-operator.crunchydata.com/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/postgres-operator.crunchydata.com/v1beta1/zz_generated.deepcopy.go @@ -430,6 +430,13 @@ func (in *InstanceSidecars) DeepCopy() *InstanceSidecars { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstrumentationConfigSpec) DeepCopyInto(out *InstrumentationConfigSpec) { *out = *in + if in.Detectors != nil { + in, out := &in.Detectors, &out.Detectors + *out = make([]OpenTelemetryResourceDetector, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } out.Exporters = in.Exporters.DeepCopy() if in.Files != nil { in, out := &in.Files, &out.Files @@ -600,6 +607,28 @@ func (in *OpenTelemetryLogsBatchSpec) DeepCopy() *OpenTelemetryLogsBatchSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryResourceDetector) DeepCopyInto(out *OpenTelemetryResourceDetector) { + *out = *in + if in.Attributes != nil { + in, out := &in.Attributes, &out.Attributes + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryResourceDetector. +func (in *OpenTelemetryResourceDetector) DeepCopy() *OpenTelemetryResourceDetector { + if in == nil { + return nil + } + out := new(OpenTelemetryResourceDetector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OptionalSecretKeyRef) DeepCopyInto(out *OptionalSecretKeyRef) { *out = *in