diff --git a/apis/v1alpha1/targetallocator_types.go b/apis/v1alpha1/targetallocator_types.go index ac9c7becad..4b0bc2e619 100644 --- a/apis/v1alpha1/targetallocator_types.go +++ b/apis/v1alpha1/targetallocator_types.go @@ -99,4 +99,10 @@ type TargetAllocatorSpec struct { // ReadinessProbe defines the readiness probe configuration for the Target Allocator container. // +optional ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` + // Telemetry defines the self-telemetry configuration for the TargetAllocator. + // When set, the TargetAllocator exports its own metrics via OTLP in addition + // to the Prometheus /metrics endpoint. + // + // +optional + Telemetry v1beta1.TargetAllocatorTelemetry `json:"telemetry,omitempty"` } diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 18fc7f9f29..d40cae45be 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -1688,6 +1688,7 @@ func (in *TargetAllocatorSpec) DeepCopyInto(out *TargetAllocatorSpec) { *out = new(v1.Probe) (*in).DeepCopyInto(*out) } + in.Telemetry.DeepCopyInto(&out.Telemetry) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetAllocatorSpec. diff --git a/apis/v1beta1/opentelemetrycollector_types.go b/apis/v1beta1/opentelemetrycollector_types.go index 2e1527bb88..e9e949d994 100644 --- a/apis/v1beta1/opentelemetrycollector_types.go +++ b/apis/v1beta1/opentelemetrycollector_types.go @@ -237,6 +237,12 @@ type TargetAllocatorEmbedded struct { // +kubebuilder:default:="30s" // +kubebuilder:validation:Format:=duration CollectorTargetReloadInterval *metav1.Duration `json:"collectorTargetReloadInterval,omitempty"` + // Telemetry defines the self-telemetry configuration for the TargetAllocator. + // When set, the TargetAllocator exports its own metrics via OTLP in addition + // to the Prometheus /metrics endpoint. + // + // +optional + Telemetry TargetAllocatorTelemetry `json:"telemetry,omitempty"` } // Probe defines the OpenTelemetry's pod probe config. @@ -315,6 +321,55 @@ type MetricsConfigSpec struct { DisablePrometheusAnnotations bool `json:"disablePrometheusAnnotations,omitempty"` } +// TargetAllocatorTelemetry defines the self-telemetry configuration for the TargetAllocator. +type TargetAllocatorTelemetry struct { + // Metrics defines the metrics export settings for the TargetAllocator's own telemetry. + // +optional + Metrics TargetAllocatorMetricsConfig `json:"metrics,omitempty"` +} + +// TargetAllocatorMetricsConfig holds metric-export settings for the TargetAllocator's own telemetry. +type TargetAllocatorMetricsConfig struct { + // OTLP configures an optional OTLP metric exporter for the TargetAllocator's self-telemetry. + // When set, metrics are exported via OTLP in addition to the existing Prometheus /metrics endpoint. + // +optional + OTLP *TargetAllocatorOTLPConfig `json:"otlp,omitempty"` +} + +// TargetAllocatorOTLPConfig holds connection settings for the TargetAllocator's OTLP self-telemetry exporter. +type TargetAllocatorOTLPConfig struct { + // Endpoint is the OTLP receiver address (e.g. "https://example.com:4318" for HTTP, + // "example.com:4317" for gRPC). + // +kubebuilder:validation:Required + Endpoint string `json:"endpoint"` + // Protocol selects the transport: "grpc" (default) or "http". + // +optional + // +kubebuilder:validation:Enum=grpc;http + Protocol string `json:"protocol,omitempty"` + // Headers are additional key/value pairs sent with every export request, + // e.g. for authentication. + // +optional + Headers map[string]string `json:"headers,omitempty"` + // Insecure disables TLS. Only suitable for local development. + // +optional + Insecure bool `json:"insecure,omitempty"` + // Temporality sets the aggregation temporality for exported metrics. + // Valid values are "cumulative" (default), "delta", and "lowmemory". + // "delta" exports all instruments as delta; "lowmemory" uses delta for + // counters and histograms and cumulative for gauges. + // +optional + // +kubebuilder:validation:Enum=cumulative;delta;lowmemory + Temporality string `json:"temporality,omitempty"` + // ExportInterval is the time between two consecutive exports. + // Defaults to 60s. + // +optional + ExportInterval *metav1.Duration `json:"exportInterval,omitempty"` + // Timeout is the max duration for a single export attempt. + // Defaults to 10s. + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` +} + // ScaleSubresourceStatus defines the observed state of the OpenTelemetryCollector's // scale subresource. type ScaleSubresourceStatus struct { diff --git a/apis/v1beta1/zz_generated.deepcopy.go b/apis/v1beta1/zz_generated.deepcopy.go index 463fc965a8..299a66bff9 100644 --- a/apis/v1beta1/zz_generated.deepcopy.go +++ b/apis/v1beta1/zz_generated.deepcopy.go @@ -832,6 +832,7 @@ func (in *TargetAllocatorEmbedded) DeepCopyInto(out *TargetAllocatorEmbedded) { *out = new(metav1.Duration) **out = **in } + in.Telemetry.DeepCopyInto(&out.Telemetry) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetAllocatorEmbedded. @@ -844,6 +845,58 @@ func (in *TargetAllocatorEmbedded) DeepCopy() *TargetAllocatorEmbedded { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetAllocatorMetricsConfig) DeepCopyInto(out *TargetAllocatorMetricsConfig) { + *out = *in + if in.OTLP != nil { + in, out := &in.OTLP, &out.OTLP + *out = new(TargetAllocatorOTLPConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetAllocatorMetricsConfig. +func (in *TargetAllocatorMetricsConfig) DeepCopy() *TargetAllocatorMetricsConfig { + if in == nil { + return nil + } + out := new(TargetAllocatorMetricsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetAllocatorOTLPConfig) DeepCopyInto(out *TargetAllocatorOTLPConfig) { + *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ExportInterval != nil { + in, out := &in.ExportInterval, &out.ExportInterval + *out = new(metav1.Duration) + **out = **in + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetAllocatorOTLPConfig. +func (in *TargetAllocatorOTLPConfig) DeepCopy() *TargetAllocatorOTLPConfig { + if in == nil { + return nil + } + out := new(TargetAllocatorOTLPConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetAllocatorPrometheusCR) DeepCopyInto(out *TargetAllocatorPrometheusCR) { *out = *in @@ -936,6 +989,22 @@ func (in *TargetAllocatorPrometheusCR) DeepCopy() *TargetAllocatorPrometheusCR { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetAllocatorTelemetry) DeepCopyInto(out *TargetAllocatorTelemetry) { + *out = *in + in.Metrics.DeepCopyInto(&out.Metrics) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetAllocatorTelemetry. +func (in *TargetAllocatorTelemetry) DeepCopy() *TargetAllocatorTelemetry { + if in == nil { + return nil + } + out := new(TargetAllocatorTelemetry) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Telemetry) DeepCopyInto(out *Telemetry) { *out = *in diff --git a/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml b/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml index af3baa687b..87b6454c69 100644 --- a/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml +++ b/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml @@ -8653,6 +8653,40 @@ spec: type: object serviceAccount: type: string + telemetry: + properties: + metrics: + properties: + otlp: + properties: + endpoint: + type: string + exportInterval: + type: string + headers: + additionalProperties: + type: string + type: object + insecure: + type: boolean + protocol: + enum: + - grpc + - http + type: string + temporality: + enum: + - cumulative + - delta + - lowmemory + type: string + timeout: + type: string + required: + - endpoint + type: object + type: object + type: object tolerations: items: properties: diff --git a/bundle/community/manifests/opentelemetry.io_targetallocators.yaml b/bundle/community/manifests/opentelemetry.io_targetallocators.yaml index 0e6f929247..9110b6f540 100644 --- a/bundle/community/manifests/opentelemetry.io_targetallocators.yaml +++ b/bundle/community/manifests/opentelemetry.io_targetallocators.yaml @@ -2921,6 +2921,40 @@ spec: type: string shareProcessNamespace: type: boolean + telemetry: + properties: + metrics: + properties: + otlp: + properties: + endpoint: + type: string + exportInterval: + type: string + headers: + additionalProperties: + type: string + type: object + insecure: + type: boolean + protocol: + enum: + - grpc + - http + type: string + temporality: + enum: + - cumulative + - delta + - lowmemory + type: string + timeout: + type: string + required: + - endpoint + type: object + type: object + type: object terminationGracePeriodSeconds: format: int64 type: integer diff --git a/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml b/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml index 8cb95a9dd1..f03ac3b2b4 100644 --- a/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml +++ b/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml @@ -8652,6 +8652,40 @@ spec: type: object serviceAccount: type: string + telemetry: + properties: + metrics: + properties: + otlp: + properties: + endpoint: + type: string + exportInterval: + type: string + headers: + additionalProperties: + type: string + type: object + insecure: + type: boolean + protocol: + enum: + - grpc + - http + type: string + temporality: + enum: + - cumulative + - delta + - lowmemory + type: string + timeout: + type: string + required: + - endpoint + type: object + type: object + type: object tolerations: items: properties: diff --git a/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml b/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml index 0e6f929247..9110b6f540 100644 --- a/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml +++ b/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml @@ -2921,6 +2921,40 @@ spec: type: string shareProcessNamespace: type: boolean + telemetry: + properties: + metrics: + properties: + otlp: + properties: + endpoint: + type: string + exportInterval: + type: string + headers: + additionalProperties: + type: string + type: object + insecure: + type: boolean + protocol: + enum: + - grpc + - http + type: string + temporality: + enum: + - cumulative + - delta + - lowmemory + type: string + timeout: + type: string + required: + - endpoint + type: object + type: object + type: object terminationGracePeriodSeconds: format: int64 type: integer diff --git a/cmd/otel-allocator/internal/config/config.go b/cmd/otel-allocator/internal/config/config.go index d3abbeb8b3..e1442cd3a1 100644 --- a/cmd/otel-allocator/internal/config/config.go +++ b/cmd/otel-allocator/internal/config/config.go @@ -71,6 +71,7 @@ type Config struct { FilterStrategy string `yaml:"filter_strategy,omitempty"` PrometheusCR PrometheusCRConfig `yaml:"prometheus_cr,omitempty"` HTTPS HTTPSServerConfig `yaml:"https,omitempty"` + Telemetry TelemetryConfig `yaml:"telemetry,omitempty"` CollectorNotReadyGracePeriod time.Duration `yaml:"collector_not_ready_grace_period,omitempty"` } @@ -101,6 +102,41 @@ type HTTPSServerConfig struct { TLSKeyFilePath string `yaml:"tls_key_file_path,omitempty"` } +// TelemetryConfig mirrors the collector's service.telemetry structure for the Target Allocator. +type TelemetryConfig struct { + Metrics MetricsConfig `yaml:"metrics,omitempty"` +} + +// MetricsConfig holds metric-export settings for the Target Allocator's own telemetry. +type MetricsConfig struct { + // OTLP configures an optional OTLP metric exporter. When set, metrics are + // exported via OTLP in addition to the existing Prometheus /metrics endpoint. + OTLP *OTLPExporterConfig `yaml:"otlp,omitempty"` +} + +// OTLPExporterConfig holds connection settings for the OTLP metric exporter. +type OTLPExporterConfig struct { + // Endpoint is the OTLP receiver base URL or address. + // For HTTP, provide the base URL without the signal path + // (e.g. "https://example.com/api/v2/otlp"); /v1/metrics is appended automatically. + // For gRPC, provide host:port (e.g. "example.com:4317"). + Endpoint string `yaml:"endpoint"` + // Protocol selects the transport: "grpc" (default) or "http". + Protocol string `yaml:"protocol,omitempty"` + // Headers are additional key/value pairs sent with every export request. + Headers map[string]string `yaml:"headers,omitempty"` + // Insecure disables TLS — only suitable for local development. + Insecure bool `yaml:"insecure,omitempty"` + // Temporality sets the aggregation temporality: "cumulative" (default), "delta", or "lowmemory". + // "delta" exports all instruments as delta; "lowmemory" uses delta for counters and + // histograms and cumulative for gauges. + Temporality string `yaml:"temporality,omitempty"` + // ExportInterval is the time between two consecutive exports (default 60s). + ExportInterval time.Duration `yaml:"export_interval,omitempty"` + // Timeout is the max duration for a single export attempt (default 10s). + Timeout time.Duration `yaml:"timeout,omitempty"` +} + // StringToModelOrTimeDurationHookFunc returns a DecodeHookFuncType // that converts string to time.Duration, which can also be used // as model.Duration. diff --git a/cmd/otel-allocator/main.go b/cmd/otel-allocator/main.go index 4fd948cfed..f49716fc97 100644 --- a/cmd/otel-allocator/main.go +++ b/cmd/otel-allocator/main.go @@ -9,6 +9,7 @@ import ( "fmt" "os" "os/signal" + "strings" "syscall" "github.com/oklog/run" @@ -18,6 +19,8 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" otelprom "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" "go.opentelemetry.io/otel/metric" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "k8s.io/client-go/kubernetes" @@ -85,7 +88,18 @@ func main() { if promErr != nil { panic(promErr) } - meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(metricExporter)) + meterProviderOpts := []sdkmetric.Option{sdkmetric.WithReader(metricExporter)} + + if cfg.Telemetry.Metrics.OTLP != nil { + otlpReader, otlpErr := newOTLPMetricReader(ctx, cfg.Telemetry.Metrics.OTLP) + if otlpErr != nil { + setupLog.Error(otlpErr, "Failed to create OTLP metric reader") + os.Exit(1) + } + meterProviderOpts = append(meterProviderOpts, sdkmetric.WithReader(otlpReader)) + } + + meterProvider := sdkmetric.NewMeterProvider(meterProviderOpts...) otel.SetMeterProvider(meterProvider) allocatorPrehook = prehook.New(cfg.FilterStrategy, log) @@ -298,3 +312,78 @@ func main() { } setupLog.Info("Target allocator exited.") } + +// newOTLPMetricReader creates a PeriodicExportingMetricReader backed by either +// an OTLP/gRPC or OTLP/HTTP exporter depending on cfg.Protocol. +func newOTLPMetricReader(ctx context.Context, cfg *config.OTLPExporterConfig) (sdkmetric.Reader, error) { + temporality := temporalitySelector(cfg.Temporality) + readerOpts := periodicReaderOptions(cfg) + switch cfg.Protocol { + case "http": + // WithEndpointURL does not append /v1/metrics automatically. We do it here + // so the endpoint convention matches the OTel collector's otlphttp exporter + // (where users specify the base URL, not the full signal path). + // Skip if the caller already included the signal path. + endpoint := strings.TrimRight(cfg.Endpoint, "/") + if !strings.HasSuffix(endpoint, "/v1/metrics") { + endpoint += "/v1/metrics" + } + opts := []otlpmetrichttp.Option{ + otlpmetrichttp.WithEndpointURL(endpoint), + otlpmetrichttp.WithTemporalitySelector(temporality), + } + if cfg.Insecure { + opts = append(opts, otlpmetrichttp.WithInsecure()) + } + if len(cfg.Headers) > 0 { + opts = append(opts, otlpmetrichttp.WithHeaders(cfg.Headers)) + } + exp, err := otlpmetrichttp.New(ctx, opts...) + if err != nil { + return nil, err + } + return sdkmetric.NewPeriodicReader(exp, readerOpts...), nil + default: // "grpc" + opts := []otlpmetricgrpc.Option{ + otlpmetricgrpc.WithEndpoint(cfg.Endpoint), + otlpmetricgrpc.WithTemporalitySelector(temporality), + } + if cfg.Insecure { + opts = append(opts, otlpmetricgrpc.WithInsecure()) + } + if len(cfg.Headers) > 0 { + opts = append(opts, otlpmetricgrpc.WithHeaders(cfg.Headers)) + } + exp, err := otlpmetricgrpc.New(ctx, opts...) + if err != nil { + return nil, err + } + return sdkmetric.NewPeriodicReader(exp, readerOpts...), nil + } +} + +// periodicReaderOptions builds PeriodicReaderOptions from the export interval and timeout config. +func periodicReaderOptions(cfg *config.OTLPExporterConfig) []sdkmetric.PeriodicReaderOption { + var opts []sdkmetric.PeriodicReaderOption + if cfg.ExportInterval > 0 { + opts = append(opts, sdkmetric.WithInterval(cfg.ExportInterval)) + } + if cfg.Timeout > 0 { + opts = append(opts, sdkmetric.WithTimeout(cfg.Timeout)) + } + return opts +} + +// temporalitySelector maps a config string to an SDK TemporalitySelector. +// "delta" → all instruments delta; "lowmemory" → delta for counters/histograms, +// cumulative for gauges; anything else → cumulative (SDK default). +func temporalitySelector(t string) sdkmetric.TemporalitySelector { + switch t { + case "delta": + return sdkmetric.DeltaTemporalitySelector + case "lowmemory": + return sdkmetric.LowMemoryTemporalitySelector + default: + return sdkmetric.DefaultTemporalitySelector + } +} diff --git a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml index 36a85d9409..69fb9bb639 100644 --- a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml +++ b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml @@ -8639,6 +8639,40 @@ spec: type: object serviceAccount: type: string + telemetry: + properties: + metrics: + properties: + otlp: + properties: + endpoint: + type: string + exportInterval: + type: string + headers: + additionalProperties: + type: string + type: object + insecure: + type: boolean + protocol: + enum: + - grpc + - http + type: string + temporality: + enum: + - cumulative + - delta + - lowmemory + type: string + timeout: + type: string + required: + - endpoint + type: object + type: object + type: object tolerations: items: properties: diff --git a/config/crd/bases/opentelemetry.io_targetallocators.yaml b/config/crd/bases/opentelemetry.io_targetallocators.yaml index 3801ac5023..15ad2c1964 100644 --- a/config/crd/bases/opentelemetry.io_targetallocators.yaml +++ b/config/crd/bases/opentelemetry.io_targetallocators.yaml @@ -2919,6 +2919,40 @@ spec: type: string shareProcessNamespace: type: boolean + telemetry: + properties: + metrics: + properties: + otlp: + properties: + endpoint: + type: string + exportInterval: + type: string + headers: + additionalProperties: + type: string + type: object + insecure: + type: boolean + protocol: + enum: + - grpc + - http + type: string + temporality: + enum: + - cumulative + - delta + - lowmemory + type: string + timeout: + type: string + required: + - endpoint + type: object + type: object + type: object terminationGracePeriodSeconds: format: int64 type: integer diff --git a/docs/api/opentelemetrycollectors.md b/docs/api/opentelemetrycollectors.md index 28743abe3a..079e2312d8 100644 --- a/docs/api/opentelemetrycollectors.md +++ b/docs/api/opentelemetrycollectors.md @@ -32324,6 +32324,15 @@ the targetallocator.
the operator will not automatically Create a ServiceAccount for the TargetAllocator.
false + + telemetry + object + + Telemetry defines the self-telemetry configuration for the TargetAllocator. +When set, the TargetAllocator exports its own metrics via OTLP in addition +to the Prometheus /metrics endpoint.
+ + false tolerations []object @@ -36168,6 +36177,144 @@ PodSecurityContext, the value specified in SecurityContext takes precedence.
+### OpenTelemetryCollector.spec.targetAllocator.telemetry +[↩ Parent](#opentelemetrycollectorspectargetallocator-1) + + + +Telemetry defines the self-telemetry configuration for the TargetAllocator. +When set, the TargetAllocator exports its own metrics via OTLP in addition +to the Prometheus /metrics endpoint. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
metricsobject + Metrics defines the metrics export settings for the TargetAllocator's own telemetry.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.telemetry.metrics +[↩ Parent](#opentelemetrycollectorspectargetallocatortelemetry) + + + +Metrics defines the metrics export settings for the TargetAllocator's own telemetry. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
otlpobject + OTLP configures an optional OTLP metric exporter for the TargetAllocator's self-telemetry. +When set, metrics are exported via OTLP in addition to the existing Prometheus /metrics endpoint.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.telemetry.metrics.otlp +[↩ Parent](#opentelemetrycollectorspectargetallocatortelemetrymetrics) + + + +OTLP configures an optional OTLP metric exporter for the TargetAllocator's self-telemetry. +When set, metrics are exported via OTLP in addition to the existing Prometheus /metrics endpoint. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
endpointstring + Endpoint is the OTLP receiver address (e.g. "https://example.com:4318" for HTTP, +"example.com:4317" for gRPC).
+
true
exportIntervalstring + ExportInterval is the time between two consecutive exports. +Defaults to 60s.
+
false
headersmap[string]string + Headers are additional key/value pairs sent with every export request, +e.g. for authentication.
+
false
insecureboolean + Insecure disables TLS. Only suitable for local development.
+
false
protocolenum + Protocol selects the transport: "grpc" (default) or "http".
+
+ Enum: grpc, http
+
false
temporalityenum + Temporality sets the aggregation temporality for exported metrics. +Valid values are "cumulative" (default), "delta", and "lowmemory". +"delta" exports all instruments as delta; "lowmemory" uses delta for +counters and histograms and cumulative for gauges.
+
+ Enum: cumulative, delta, lowmemory
+
false
timeoutstring + Timeout is the max duration for a single export attempt. +Defaults to 10s.
+
false
+ + ### OpenTelemetryCollector.spec.targetAllocator.tolerations[index] [↩ Parent](#opentelemetrycollectorspectargetallocator-1) diff --git a/docs/api/targetallocators.md b/docs/api/targetallocators.md index 563e34618b..e21dede392 100644 --- a/docs/api/targetallocators.md +++ b/docs/api/targetallocators.md @@ -418,6 +418,15 @@ the operator will not automatically Create a ServiceAccount.
ShareProcessNamespace indicates if the pod's containers should share process namespace.
false + + telemetry + object + + Telemetry defines the self-telemetry configuration for the TargetAllocator. +When set, the TargetAllocator exports its own metrics via OTLP in addition +to the Prometheus /metrics endpoint.
+ + false terminationGracePeriodSeconds integer @@ -11999,6 +12008,144 @@ PodSecurityContext, the value specified in SecurityContext takes precedence.
+### TargetAllocator.spec.telemetry +[↩ Parent](#targetallocatorspec) + + + +Telemetry defines the self-telemetry configuration for the TargetAllocator. +When set, the TargetAllocator exports its own metrics via OTLP in addition +to the Prometheus /metrics endpoint. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
metricsobject + Metrics defines the metrics export settings for the TargetAllocator's own telemetry.
+
false
+ + +### TargetAllocator.spec.telemetry.metrics +[↩ Parent](#targetallocatorspectelemetry) + + + +Metrics defines the metrics export settings for the TargetAllocator's own telemetry. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
otlpobject + OTLP configures an optional OTLP metric exporter for the TargetAllocator's self-telemetry. +When set, metrics are exported via OTLP in addition to the existing Prometheus /metrics endpoint.
+
false
+ + +### TargetAllocator.spec.telemetry.metrics.otlp +[↩ Parent](#targetallocatorspectelemetrymetrics) + + + +OTLP configures an optional OTLP metric exporter for the TargetAllocator's self-telemetry. +When set, metrics are exported via OTLP in addition to the existing Prometheus /metrics endpoint. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
endpointstring + Endpoint is the OTLP receiver address (e.g. "https://example.com:4318" for HTTP, +"example.com:4317" for gRPC).
+
true
exportIntervalstring + ExportInterval is the time between two consecutive exports. +Defaults to 60s.
+
false
headersmap[string]string + Headers are additional key/value pairs sent with every export request, +e.g. for authentication.
+
false
insecureboolean + Insecure disables TLS. Only suitable for local development.
+
false
protocolenum + Protocol selects the transport: "grpc" (default) or "http".
+
+ Enum: grpc, http
+
false
temporalityenum + Temporality sets the aggregation temporality for exported metrics. +Valid values are "cumulative" (default), "delta", and "lowmemory". +"delta" exports all instruments as delta; "lowmemory" uses delta for +counters and histograms and cumulative for gauges.
+
+ Enum: cumulative, delta, lowmemory
+
false
timeoutstring + Timeout is the max duration for a single export attempt. +Defaults to 10s.
+
false
+ + ### TargetAllocator.spec.tolerations[index] [↩ Parent](#targetallocatorspec) diff --git a/internal/manifests/collector/targetallocator.go b/internal/manifests/collector/targetallocator.go index f9c7c6b48f..497370a6d6 100644 --- a/internal/manifests/collector/targetallocator.go +++ b/internal/manifests/collector/targetallocator.go @@ -59,6 +59,7 @@ func TargetAllocator(params manifests.Params) (*v1alpha1.TargetAllocator, error) PrometheusCR: taSpec.PrometheusCR, Observability: taSpec.Observability, CollectorNotReadyGracePeriod: taSpec.CollectorNotReadyGracePeriod, + Telemetry: taSpec.Telemetry, }, }, nil } diff --git a/internal/manifests/targetallocator/configmap.go b/internal/manifests/targetallocator/configmap.go index 8eef95b409..c6de186b40 100644 --- a/internal/manifests/targetallocator/configmap.go +++ b/internal/manifests/targetallocator/configmap.go @@ -145,6 +145,33 @@ func ConfigMap(params Params) (*corev1.ConfigMap, error) { taConfig["collector_not_ready_grace_period"] = taSpec.CollectorNotReadyGracePeriod.Duration } + if otlp := taSpec.Telemetry.Metrics.OTLP; otlp != nil { + otlpMap := map[string]any{"endpoint": otlp.Endpoint} + if otlp.Protocol != "" { + otlpMap["protocol"] = otlp.Protocol + } + if len(otlp.Headers) > 0 { + otlpMap["headers"] = otlp.Headers + } + if otlp.Insecure { + otlpMap["insecure"] = true + } + if otlp.Temporality != "" { + otlpMap["temporality"] = otlp.Temporality + } + if otlp.ExportInterval != nil { + otlpMap["export_interval"] = otlp.ExportInterval.Duration + } + if otlp.Timeout != nil { + otlpMap["timeout"] = otlp.Timeout.Duration + } + taConfig["telemetry"] = map[string]any{ + "metrics": map[string]any{ + "otlp": otlpMap, + }, + } + } + taConfigYAML, err := yaml.Marshal(taConfig) if err != nil { return &corev1.ConfigMap{}, err diff --git a/internal/manifests/targetallocator/configmap_test.go b/internal/manifests/targetallocator/configmap_test.go index 1ce8630012..e082a2774e 100644 --- a/internal/manifests/targetallocator/configmap_test.go +++ b/internal/manifests/targetallocator/configmap_test.go @@ -880,6 +880,85 @@ func TestGetGlobalConfig(t *testing.T) { } } +func TestTelemetryOTLP(t *testing.T) { + t.Run("should emit telemetry block with all OTLP fields", func(t *testing.T) { + targetAllocator := targetAllocatorInstance() + targetAllocator.Spec.Telemetry = v1beta1.TargetAllocatorTelemetry{ + Metrics: v1beta1.TargetAllocatorMetricsConfig{ + OTLP: &v1beta1.TargetAllocatorOTLPConfig{ + Endpoint: "https://ingest.example.com:4318", + Protocol: "http", + Headers: map[string]string{"Authorization": "Api-Token secret"}, + Insecure: true, + Temporality: "delta", + ExportInterval: &metav1.Duration{Duration: 30 * time.Second}, + Timeout: &metav1.Duration{Duration: 15 * time.Second}, + }, + }, + } + params := Params{ + Collector: collectorInstance(), + TargetAllocator: targetAllocator, + Config: config.New(), + Log: logr.Discard(), + } + + actual, err := ConfigMap(params) + require.NoError(t, err) + + data := actual.Data[targetAllocatorFilename] + assert.Contains(t, data, "telemetry:") + assert.Contains(t, data, "endpoint: https://ingest.example.com:4318") + assert.Contains(t, data, "protocol: http") + assert.Contains(t, data, "Authorization: Api-Token secret") + assert.Contains(t, data, "insecure: true") + assert.Contains(t, data, "temporality: delta") + assert.Contains(t, data, "export_interval: 30s") + assert.Contains(t, data, "timeout: 15s") + }) + + t.Run("should omit optional fields when not set", func(t *testing.T) { + targetAllocator := targetAllocatorInstance() + targetAllocator.Spec.Telemetry = v1beta1.TargetAllocatorTelemetry{ + Metrics: v1beta1.TargetAllocatorMetricsConfig{ + OTLP: &v1beta1.TargetAllocatorOTLPConfig{ + Endpoint: "https://ingest.example.com:4318", + }, + }, + } + params := Params{ + Collector: collectorInstance(), + TargetAllocator: targetAllocator, + Config: config.New(), + Log: logr.Discard(), + } + + actual, err := ConfigMap(params) + require.NoError(t, err) + + data := actual.Data[targetAllocatorFilename] + assert.Contains(t, data, "telemetry:") + assert.Contains(t, data, "endpoint: https://ingest.example.com:4318") + assert.NotContains(t, data, "protocol:") + assert.NotContains(t, data, "insecure:") + assert.NotContains(t, data, "temporality:") + assert.NotContains(t, data, "export_interval:") + assert.NotContains(t, data, "timeout:") + }) + + t.Run("no telemetry block when OTLP is nil", func(t *testing.T) { + params := Params{ + Collector: collectorInstance(), + TargetAllocator: targetAllocatorInstance(), + Config: config.New(), + Log: logr.Discard(), + } + actual, err := ConfigMap(params) + require.NoError(t, err) + assert.NotContains(t, actual.Data[targetAllocatorFilename], "telemetry:") + }) +} + func TestGetCollectorNotReadyGracePeriod(t *testing.T) { collector := collectorInstance() targetAllocator := targetAllocatorInstanceWithCollectorNotReadyGracePeriod()