From 69e05fc1883710f3fe8e95d1acb134b1341d8d2e Mon Sep 17 00:00:00 2001 From: Emil Lehre Arnesen <38581916+emilarnesen@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:49:42 +0100 Subject: [PATCH] Add enableFeatures field to MonitoringStack prometheusConfig --- pkg/apis/monitoring/v1alpha1/types.go | 10 ++++++ .../v1alpha1/zz_generated.deepcopy.go | 5 +++ .../monitoring/monitoring-stack/components.go | 13 +++++++ .../monitoring-stack/components_test.go | 36 +++++++++++++++++++ test/e2e/monitoring_stack_controller_test.go | 1 + 5 files changed, 65 insertions(+) diff --git a/pkg/apis/monitoring/v1alpha1/types.go b/pkg/apis/monitoring/v1alpha1/types.go index ebb06d4f6..b28c0c179 100644 --- a/pkg/apis/monitoring/v1alpha1/types.go +++ b/pkg/apis/monitoring/v1alpha1/types.go @@ -244,6 +244,16 @@ type PrometheusConfig struct { // The resulting endpoint is /api/v1/otlp/v1/metrics. // +optional EnableOtlpHttpReceiver *bool `json:"enableOtlpHttpReceiver,omitempty"` + // Enable access to Prometheus feature flags. By default, no features are enabled. + // Enabling features which are disabled by default is entirely outside the + // scope of what the maintainers will support and by doing so, you accept + // that this behaviour may break at any time without notice. + // + // For more information see https://prometheus.io/docs/prometheus/latest/feature_flags/ + // + // +optional + // +kubebuilder:validation:items:MinLength=1 + EnableFeatures []string `json:"enableFeatures,omitempty"` // Default interval between scrapes. // +optional ScrapeInterval *monv1.Duration `json:"scrapeInterval,omitempty"` diff --git a/pkg/apis/monitoring/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/monitoring/v1alpha1/zz_generated.deepcopy.go index 04bfc0e67..fcb18e924 100644 --- a/pkg/apis/monitoring/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/monitoring/v1alpha1/zz_generated.deepcopy.go @@ -247,6 +247,11 @@ func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { *out = new(bool) **out = **in } + if in.EnableFeatures != nil { + in, out := &in.EnableFeatures, &out.EnableFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.ScrapeInterval != nil { in, out := &in.ScrapeInterval, &out.ScrapeInterval *out = new(monitoringv1.Duration) diff --git a/pkg/controllers/monitoring/monitoring-stack/components.go b/pkg/controllers/monitoring/monitoring-stack/components.go index 76514cf9b..9c904ef70 100644 --- a/pkg/controllers/monitoring/monitoring-stack/components.go +++ b/pkg/controllers/monitoring/monitoring-stack/components.go @@ -203,6 +203,7 @@ func newPrometheus( ExternalLabels: config.ExternalLabels, EnableRemoteWriteReceiver: config.EnableRemoteWriteReceiver, EnableOTLPReceiver: config.EnableOtlpHttpReceiver, + EnableFeatures: toEnableFeatures(config.EnableFeatures), }, Retention: ms.Spec.Retention, RetentionSize: ms.Spec.RetentionSize, @@ -565,3 +566,15 @@ func podLabels(component string, msName string) map[string]string { "app.kubernetes.io/part-of": msName, } } + +// toEnableFeatures converts a slice of strings to a slice of EnableFeature. +func toEnableFeatures(features []string) []monv1.EnableFeature { + if len(features) == 0 { + return nil + } + result := make([]monv1.EnableFeature, len(features)) + for i, f := range features { + result[i] = monv1.EnableFeature(f) + } + return result +} diff --git a/pkg/controllers/monitoring/monitoring-stack/components_test.go b/pkg/controllers/monitoring/monitoring-stack/components_test.go index e5feee830..8533524d6 100644 --- a/pkg/controllers/monitoring/monitoring-stack/components_test.go +++ b/pkg/controllers/monitoring/monitoring-stack/components_test.go @@ -14,6 +14,42 @@ import ( stack "github.com/rhobs/observability-operator/pkg/apis/monitoring/v1alpha1" ) +func TestToEnableFeatures(t *testing.T) { + tt := []struct { + name string + input []string + expected []monv1.EnableFeature + }{ + { + name: "nil input returns nil", + input: nil, + expected: nil, + }, + { + name: "empty input returns nil", + input: []string{}, + expected: nil, + }, + { + name: "single feature", + input: []string{"exemplar-storage"}, + expected: []monv1.EnableFeature{"exemplar-storage"}, + }, + { + name: "multiple features", + input: []string{"exemplar-storage", "otlp-write-receiver"}, + expected: []monv1.EnableFeature{"exemplar-storage", "otlp-write-receiver"}, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + actual := toEnableFeatures(tc.input) + assert.DeepEqual(t, tc.expected, actual) + }) + } +} + func TestStorageSpec(t *testing.T) { validPVCSpec := &corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, diff --git a/test/e2e/monitoring_stack_controller_test.go b/test/e2e/monitoring_stack_controller_test.go index 717cb5f90..b38fcfa34 100644 --- a/test/e2e/monitoring_stack_controller_test.go +++ b/test/e2e/monitoring_stack_controller_test.go @@ -822,6 +822,7 @@ func assertPrometheusManagedFields(t *testing.T) { }, EnableRemoteWriteReceiver: true, EnableOtlpHttpReceiver: func(b bool) *bool { return &b }(true), + EnableFeatures: []string{"exemplar-storage"}, WebTLSConfig: &stack.WebTLSConfig{ Certificate: stack.SecretKeySelector{ Name: "prom-test-managedfields-tls-secret",