Repository: open-telemetry/opentelemetry-operator
Pull request #3790 (2025-03-10 18:53:02)

Refactor namespace selector handling in target allocator
**Description:**

Currently namespace selectors work correctly in target allocator by accident. By default, Prometheus config generation which we import from prometheus-operator only watches the Prometheus CR namespace for ServiceMonitors. We don't set that, and it just so happens that the config generation code treats that as "all namespaces". This is not the case anymore in newer versions of prometheus-operator, and so we are forced to fix this problem.

This PR explicitly sets default selectors to "all namespaces" in the target allocator config. We also set our internal Prometheus CR namespace to the collector namespace. This doesn't change anything by default, but it makes target allocator behave more similarly to Prometheus.

**Link to tracking Issue(s):**

Part of https://github.com/open-telemetry/opentelemetry-operator/issues/3782.




Patch:
diff --git a/.chloggen/allow-non-identifying.yaml b/.chloggen/allow-non-identifying.yaml
new file mode 100755
index 00000000..f87da39c
--- /dev/null
+++ b/.chloggen/allow-non-identifying.yaml
@@ -0,0 +1,16 @@
+# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
+change_type: enhancement
+
+# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
+component: opamp
+
+# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
+note: Allows specifying non-identifying attributes for the Bridge, also follows OpAMP guidelines for reporting instance.service.id
+
+# One or more tracking issues related to the change
+issues: [2301]
+
+# (Optional) One or more lines of additional information to render under the primary note.
+# These lines will be padded with 2 spaces and then inserted directly into the document.
+# Use pipe (|) for multiline entries.
+subtext:
diff --git a/.chloggen/prometheus_operator_feature_gate_stable.yaml b/.chloggen/prometheus_operator_feature_gate_stable.yaml
new file mode 100755
index 00000000..0f3c2f69
--- /dev/null
+++ b/.chloggen/prometheus_operator_feature_gate_stable.yaml
@@ -0,0 +1,16 @@
+# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
+change_type: breaking
+
+# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
+component: all
+
+# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
+note: Move feature gate `operator.observability.prometheus` to stable
+
+# One or more tracking issues related to the change
+issues: [3669]
+
+# (Optional) One or more lines of additional information to render under the primary note.
+# These lines will be padded with 2 spaces and then inserted directly into the document.
+# Use pipe (|) for multiline entries.
+subtext:
diff --git a/apis/v1alpha1/opampbridge_types.go b/apis/v1alpha1/opampbridge_types.go
index 285e0749..81f91221 100644
--- a/apis/v1alpha1/opampbridge_types.go
+++ b/apis/v1alpha1/opampbridge_types.go
@@ -23,6 +23,9 @@ type OpAMPBridgeSpec struct {
 	// ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.)
 	// +optional
 	ComponentsAllowed map[string][]string `json:"componentsAllowed,omitempty"`
+	// Description allows the customization of the non identifying attributes for the OpAMP Bridge.
+	// +optional
+	Description *AgentDescription `json:"description,omitempty"`
 	// Resources to set on the OpAMPBridge pods.
 	// +optional
 	Resources v1.ResourceRequirements `json:"resources,omitempty"`
@@ -111,6 +114,12 @@ type OpAMPBridgeStatus struct {
 	Version string `json:"version,omitempty"`
 }
 
+type AgentDescription struct {
+	// NonIdentifyingAttributes are a map of key-value pairs that may be specified to provide
+	// extra information about the agent to the OpAMP server.
+	NonIdentifyingAttributes map[string]string `json:"non_identifying_attributes"`
+}
+
 // +kubebuilder:object:root=true
 // +kubebuilder:subresource:status
 // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go
index f4544a51..0caa0157 100644
--- a/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/v1alpha1/zz_generated.deepcopy.go
@@ -17,6 +17,28 @@ import (
 	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AgentDescription) DeepCopyInto(out *AgentDescription) {
+	*out = *in
+	if in.NonIdentifyingAttributes != nil {
+		in, out := &in.NonIdentifyingAttributes, &out.NonIdentifyingAttributes
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentDescription.
+func (in *AgentDescription) DeepCopy() *AgentDescription {
+	if in == nil {
+		return nil
+	}
+	out := new(AgentDescription)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ApacheHttpd) DeepCopyInto(out *ApacheHttpd) {
 	*out = *in
@@ -612,6 +634,11 @@ func (in *OpAMPBridgeSpec) DeepCopyInto(out *OpAMPBridgeSpec) {
 			(*out)[key] = outVal
 		}
 	}
+	if in.Description != nil {
+		in, out := &in.Description, &out.Description
+		*out = new(AgentDescription)
+		(*in).DeepCopyInto(*out)
+	}
 	in.Resources.DeepCopyInto(&out.Resources)
 	if in.NodeSelector != nil {
 		in, out := &in.NodeSelector, &out.NodeSelector
diff --git a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml
index 4635bf6b..fbd456d6 100644
--- a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml
+++ b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml
@@ -99,7 +99,7 @@ metadata:
     categories: Logging & Tracing,Monitoring
     certified: "false"
     containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
-    createdAt: "2025-03-05T15:50:41Z"
+    createdAt: "2025-03-12T19:10:16Z"
     description: Provides the OpenTelemetry components, including the Collector
     operators.operatorframework.io/builder: operator-sdk-v1.29.0
     operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
diff --git a/bundle/community/manifests/opentelemetry.io_opampbridges.yaml b/bundle/community/manifests/opentelemetry.io_opampbridges.yaml
index b73e5dba..cfb4bcdc 100644
--- a/bundle/community/manifests/opentelemetry.io_opampbridges.yaml
+++ b/bundle/community/manifests/opentelemetry.io_opampbridges.yaml
@@ -489,6 +489,15 @@ spec:
                     type: string
                   type: array
                 type: object
+              description:
+                properties:
+                  non_identifying_attributes:
+                    additionalProperties:
+                      type: string
+                    type: object
+                required:
+                - non_identifying_attributes
+                type: object
               endpoint:
                 type: string
               env:
diff --git a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml
index 030ad795..7e8e6e61 100644
--- a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml
+++ b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml
@@ -99,7 +99,7 @@ metadata:
     categories: Logging & Tracing,Monitoring
     certified: "false"
     containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
-    createdAt: "2025-03-05T15:50:41Z"
+    createdAt: "2025-03-12T19:10:16Z"
     description: Provides the OpenTelemetry components, including the Collector
     operators.operatorframework.io/builder: operator-sdk-v1.29.0
     operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
diff --git a/bundle/openshift/manifests/opentelemetry.io_opampbridges.yaml b/bundle/openshift/manifests/opentelemetry.io_opampbridges.yaml
index b73e5dba..cfb4bcdc 100644
--- a/bundle/openshift/manifests/opentelemetry.io_opampbridges.yaml
+++ b/bundle/openshift/manifests/opentelemetry.io_opampbridges.yaml
@@ -489,6 +489,15 @@ spec:
                     type: string
                   type: array
                 type: object
+              description:
+                properties:
+                  non_identifying_attributes:
+                    additionalProperties:
+                      type: string
+                    type: object
+                required:
+                - non_identifying_attributes
+                type: object
               endpoint:
                 type: string
               env:
diff --git a/cmd/operator-opamp-bridge/agent/agent.go b/cmd/operator-opamp-bridge/agent/agent.go
index 97ad9a88..b566ede2 100644
--- a/cmd/operator-opamp-bridge/agent/agent.go
+++ b/cmd/operator-opamp-bridge/agent/agent.go
@@ -58,7 +58,7 @@ func NewAgent(logger logr.Logger, applier operator.ConfigApplier, config *config
 		applier:             applier,
 		logger:              logger,
 		appliedKeys:         map[kubeResourceKey]bool{},
-		instanceId:          config.GetNewInstanceId(),
+		instanceId:          config.GetInstanceId(),
 		agentDescription:    config.GetDescription(),
 		remoteConfigEnabled: config.RemoteConfigEnabled(),
 		opampClient:         opampClient,
diff --git a/cmd/operator-opamp-bridge/config/config.go b/cmd/operator-opamp-bridge/config/config.go
index 80f5665a..d41bff82 100644
--- a/cmd/operator-opamp-bridge/config/config.go
+++ b/cmd/operator-opamp-bridge/config/config.go
@@ -81,6 +81,7 @@ type Config struct {
 	ListenAddr         string       `yaml:"listenAddr,omitempty"`
 	ClusterConfig      *rest.Config `yaml:"-"`
 	RootLogger         logr.Logger  `yaml:"-"`
+	instanceId         uuid.UUID    `yaml:"-"`
 
 	// ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.)
 	ComponentsAllowed map[string][]string `yaml:"componentsAllowed,omitempty"`
@@ -89,10 +90,20 @@ type Config struct {
 	Capabilities      map[Capability]bool `yaml:"capabilities"`
 	HeartbeatInterval time.Duration       `yaml:"heartbeatInterval,omitempty"`
 	Name              string              `yaml:"name,omitempty"`
+	AgentDescription  AgentDescription    `yaml:"description,omitempty"`
+}
+
+// AgentDescription is copied from the OpAMP Extension in the collector.
+// https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/ccc3e6ed6386d404eb4beddd257ff979d2a346f4/extension/opampextension/config.go#L48
+type AgentDescription struct {
+	// NonIdentifyingAttributes are a map of key-value pairs that may be specified to provide
+	// extra information about the agent to the OpAMP server.
+	NonIdentifyingAttributes map[string]string `yaml:"non_identifying_attributes"`
 }
 
 func NewConfig(logger logr.Logger) *Config {
 	return &Config{
+		instanceId: mustGetInstanceId(),
 		RootLogger: logger,
 	}
 }
@@ -150,17 +161,33 @@ func (c *Config) GetAgentVersion() string {
 	return agentVersion
 }
 
+func (c *Config) GetInstanceId() uuid.UUID {
+	return c.instanceId
+}
+
 func (c *Config) GetDescription() *protobufs.AgentDescription {
 	return &protobufs.AgentDescription{
 		IdentifyingAttributes: []*protobufs.KeyValue{
 			keyValuePair("service.name", c.GetAgentType()),
+			keyValuePair("service.instance.id", c.GetInstanceId().String()),
 			keyValuePair("service.version", c.GetAgentVersion()),
 		},
-		NonIdentifyingAttributes: []*protobufs.KeyValue{
+		NonIdentifyingAttributes: append(
+			c.AgentDescription.nonIdentifyingAttributes(),
 			keyValuePair("os.family", runtime.GOOS),
 			keyValuePair("host.name", hostname),
-		},
+		),
+	}
+}
+
+func (ad *AgentDescription) nonIdentifyingAttributes() []*protobufs.KeyValue {
+	toReturn := make([]*protobufs.KeyValue, len(ad.NonIdentifyingAttributes))
+	i := 0
+	for k, v := range ad.NonIdentifyingAttributes {
+		toReturn[i] = keyValuePair(k, v)
+		i++
 	}
+	return toReturn
 }
 
 func keyValuePair(key string, value string) *protobufs.KeyValue {
@@ -174,7 +201,7 @@ func keyValuePair(key string, value string) *protobufs.KeyValue {
 	}
 }
 
-func (c *Config) GetNewInstanceId() uuid.UUID {
+func mustGetInstanceId() uuid.UUID {
 	u, err := uuid.NewV7()
 	if err != nil {
 		// This really should never happen and if it does we should fail.
@@ -183,6 +210,11 @@ func (c *Config) GetNewInstanceId() uuid.UUID {
 	return u
 }
 
+func (c *Config) GetNewInstanceId() uuid.UUID {
+	c.instanceId = mustGetInstanceId()
+	return c.instanceId
+}
+
 func (c *Config) RemoteConfigEnabled() bool {
 	capabilities := c.GetCapabilities()
 	return capabilities&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig != 0
diff --git a/cmd/otel-allocator/internal/config/config.go b/cmd/otel-allocator/internal/config/config.go
index 5d7678b4..ab840939 100644
--- a/cmd/otel-allocator/internal/config/config.go
+++ b/cmd/otel-allocator/internal/config/config.go
@@ -290,7 +290,11 @@ func CreateDefaultConfig() Config {
 		AllocationFallbackStrategy: "",
 		FilterStrategy:             DefaultFilterStrategy,
 		PrometheusCR: PrometheusCRConfig{
-			ScrapeInterval: DefaultCRScrapeInterval,
+			ScrapeInterval:                  DefaultCRScrapeInterval,
+			ServiceMonitorNamespaceSelector: &metav1.LabelSelector{},
+			PodMonitorNamespaceSelector:     &metav1.LabelSelector{},
+			ScrapeConfigNamespaceSelector:   &metav1.LabelSelector{},
+			ProbeNamespaceSelector:          &metav1.LabelSelector{},
 		},
 	}
 }
diff --git a/cmd/otel-allocator/internal/watcher/promOperator.go b/cmd/otel-allocator/internal/watcher/promOperator.go
index f484a90d..89e395cc 100644
--- a/cmd/otel-allocator/internal/watcher/promOperator.go
+++ b/cmd/otel-allocator/internal/watcher/promOperator.go
@@ -65,6 +65,9 @@ func NewPrometheusCRWatcher(ctx context.Context, logger logr.Logger, cfg allocat
 
 	// TODO: We should make these durations configurable
 	prom := &monitoringv1.Prometheus{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: cfg.CollectorNamespace,
+		},
 		Spec: monitoringv1.PrometheusSpec{
 			CommonPrometheusFields: monitoringv1.CommonPrometheusFields{
 				ScrapeInterval:                  monitoringv1.Duration(cfg.PrometheusCR.ScrapeInterval.String()),
diff --git a/config/crd/bases/opentelemetry.io_opampbridges.yaml b/config/crd/bases/opentelemetry.io_opampbridges.yaml
index 55e1b0ae..480bc6f5 100644
--- a/config/crd/bases/opentelemetry.io_opampbridges.yaml
+++ b/config/crd/bases/opentelemetry.io_opampbridges.yaml
@@ -486,6 +486,15 @@ spec:
                     type: string
                   type: array
                 type: object
+              description:
+                properties:
+                  non_identifying_attributes:
+                    additionalProperties:
+                      type: string
+                    type: object
+                required:
+                - non_identifying_attributes
+                type: object
               endpoint:
                 type: string
               env:
diff --git a/docs/api/opampbridges.md b/docs/api/opampbridges.md
index ecb84bf8..9b90bad4 100644
--- a/docs/api/opampbridges.md
+++ b/docs/api/opampbridges.md
@@ -111,6 +111,13 @@ OpAMPBridgeSpec defines the desired state of OpAMPBridge.
           ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.)<br/>
         </td>
         <td>false</td>
+      </tr><tr>
+        <td><b><a href="#opampbridgespecdescription">description</a></b></td>
+        <td>object</td>
+        <td>
+          Description allows the customization of the non identifying attributes for the OpAMP Bridge.<br/>
+        </td>
+        <td>false</td>
       </tr><tr>
         <td><b><a href="#opampbridgespecenvindex">env</a></b></td>
         <td>[]object</td>
@@ -1919,6 +1926,34 @@ merge patch.<br/>
 </table>
 
 
+### OpAMPBridge.spec.description
+<sup><sup>[↩ Parent](#opampbridgespec)</sup></sup>
+
+
+
+Description allows the customization of the non identifying attributes for the OpAMP Bridge.
+
+<table>
+    <thead>
+        <tr>
+            <th>Name</th>
+            <th>Type</th>
+            <th>Description</th>
+            <th>Required</th>
+        </tr>
+    </thead>
+    <tbody><tr>
+        <td><b>non_identifying_attributes</b></td>
+        <td>map[string]string</td>
+        <td>
+          NonIdentifyingAttributes are a map of key-value pairs that may be specified to provide
+extra information about the agent to the OpAMP server.<br/>
+        </td>
+        <td>true</td>
+      </tr></tbody>
+</table>
+
+
 ### OpAMPBridge.spec.env[index]
 <sup><sup>[↩ Parent](#opampbridgespec)</sup></sup>
 
diff --git a/go.mod b/go.mod
index 0edf5dcb..78cf3c2c 100644
--- a/go.mod
+++ b/go.mod
@@ -23,7 +23,7 @@ require (
 	github.com/oklog/run v1.1.0
 	github.com/open-telemetry/opamp-go v0.15.0
 	github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e
-	github.com/operator-framework/api v0.29.0
+	github.com/operator-framework/api v0.30.0
 	github.com/operator-framework/operator-lib v0.17.0
 	github.com/prometheus-operator/prometheus-operator v0.77.2
 	github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.77.2
@@ -188,7 +188,7 @@ require (
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
-	github.com/spf13/cobra v1.8.1 // indirect
+	github.com/spf13/cobra v1.9.1 // indirect
 	github.com/stretchr/objx v0.5.2 // indirect
 	github.com/tklauser/go-sysconf v0.3.13 // indirect
 	github.com/tklauser/numcpus v0.7.0 // indirect
@@ -205,21 +205,21 @@ require (
 	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 	go.uber.org/atomic v1.11.0 // indirect
 	golang.org/x/arch v0.8.0 // indirect
-	golang.org/x/crypto v0.35.0 // indirect
+	golang.org/x/crypto v0.36.0 // indirect
 	golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
 	golang.org/x/mod v0.22.0 // indirect
-	golang.org/x/net v0.36.0 // indirect
+	golang.org/x/net v0.37.0 // indirect
 	golang.org/x/oauth2 v0.28.0 // indirect
-	golang.org/x/sync v0.11.0 // indirect
-	golang.org/x/sys v0.30.0 // indirect
-	golang.org/x/term v0.29.0 // indirect
-	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/sync v0.12.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
+	golang.org/x/term v0.30.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
 	golang.org/x/time v0.8.0 // indirect
 	golang.org/x/tools v0.29.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/api v0.198.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e // indirect
 	google.golang.org/grpc v1.71.0 // indirect
 	google.golang.org/protobuf v1.36.5 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
diff --git a/go.sum b/go.sum
index da91c3ef..c87b40e3 100644
--- a/go.sum
+++ b/go.sum
@@ -123,7 +123,7 @@ github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 h1:boJj011Hh+874zpIySe
 github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
 github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
 github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
-github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -511,8 +511,8 @@ github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e h1:cxgCNo/R769CO23AK
 github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
-github.com/operator-framework/api v0.29.0 h1:TxAR8RCO+I4FjRrY4PSMgnlmbxNWeD8pzHXp7xwHNmw=
-github.com/operator-framework/api v0.29.0/go.mod h1:0whQE4mpMDd2zyHkQe+bFa3DLoRs6oGWCbu8dY/3pyc=
+github.com/operator-framework/api v0.30.0 h1:44hCmGnEnZk/Miol5o44dhSldNH0EToQUG7vZTl29kk=
+github.com/operator-framework/api v0.30.0/go.mod h1:FYxAPhjtlXSAty/fbn5YJnFagt6SpJZJgFNNbvDe5W0=
 github.com/operator-framework/operator-lib v0.17.0 h1:cbz51wZ9+GpWR1ZYP4CSKSSBxDlWxmmnseaHVZZjZt4=
 github.com/operator-framework/operator-lib v0.17.0/go.mod h1:TGopBxIE8L6E/Cojzo26R3NFp1eNlqhQNmzqhOblaLw=
 github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI=
@@ -595,9 +595,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
-github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
 github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -696,8 +695,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
-golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
+golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
+golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -774,8 +773,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
-golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
-golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
+golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
+golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -797,8 +796,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
-golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -859,16 +858,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
 golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
-golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
-golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -880,8 +879,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -994,10 +993,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
 google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
-google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e h1:YA5lmSs3zc/5w+xsRcHqpETkaYyK63ivEPzNTcUUlSA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
diff --git a/internal/manifests/opampbridge/configmap.go b/internal/manifests/opampbridge/configmap.go
index e3afd6bc..bef00ef2 100644
--- a/internal/manifests/opampbridge/configmap.go
+++ b/internal/manifests/opampbridge/configmap.go
@@ -39,6 +39,12 @@ func ConfigMap(params manifests.Params) (*corev1.ConfigMap, error) {
 		config["componentsAllowed"] = params.OpAMPBridge.Spec.ComponentsAllowed
 	}
 
+	if params.OpAMPBridge.Spec.Description != nil {
+		config["description"] = map[string]any{
+			"non_identifying_attributes": params.OpAMPBridge.Spec.Description.NonIdentifyingAttributes,
+		}
+	}
+
 	configYAML, err := yaml.Marshal(config)
 	if err != nil {
 		return &corev1.ConfigMap{}, err
diff --git a/pkg/featuregate/featuregate.go b/pkg/featuregate/featuregate.go
index f13a83e5..d8a92d6c 100644
--- a/pkg/featuregate/featuregate.go
+++ b/pkg/featuregate/featuregate.go
@@ -38,9 +38,10 @@ var (
 	// PrometheusOperatorIsAvailable is the feature gate that enables features associated to the Prometheus Operator.
 	PrometheusOperatorIsAvailable = featuregate.GlobalRegistry().MustRegister(
 		"operator.observability.prometheus",
-		featuregate.StageBeta,
+		featuregate.StageStable,
 		featuregate.WithRegisterDescription("enables features associated to the Prometheus Operator"),
 		featuregate.WithRegisterFromVersion("v0.82.0"),
+		featuregate.WithRegisterToVersion("v0.122.0"),
 	)
 	// SetGolangFlags is the feature gate that enables automatically setting GOMEMLIMIT and GOMAXPROCS for the
 	// collector, bridge, and target allocator.


Test patch:
diff --git a/cmd/operator-opamp-bridge/config/config_test.go b/cmd/operator-opamp-bridge/config/config_test.go
index 99590337..96dfde40 100644
--- a/cmd/operator-opamp-bridge/config/config_test.go
+++ b/cmd/operator-opamp-bridge/config/config_test.go
@@ -10,7 +10,10 @@ import (
 	"time"
 
 	"github.com/go-logr/logr"
+	"github.com/google/uuid"
+	"github.com/open-telemetry/opamp-go/protobufs"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestLoad(t *testing.T) {
@@ -18,6 +21,7 @@ func TestLoad(t *testing.T) {
 		file         string
 		envVariables map[string]string
 	}
+	instanceId := uuid.New()
 	tests := []struct {
 		name    string
 		args    args
@@ -30,6 +34,7 @@ func TestLoad(t *testing.T) {
 				file: "./testdata/agent.yaml",
 			},
 			want: &Config{
+				instanceId: instanceId,
 				RootLogger: logr.Discard(),
 				Endpoint:   "ws://127.0.0.1:4320/v1/opamp",
 				Capabilities: map[Capability]bool{
@@ -55,6 +60,7 @@ func TestLoad(t *testing.T) {
 				file: "./testdata/agenthttpbasic.yaml",
 			},
 			want: &Config{
+				instanceId:        instanceId,
 				RootLogger:        logr.Discard(),
 				Endpoint:          "http://127.0.0.1:4320/v1/opamp",
 				HeartbeatInterval: 45 * time.Second,
@@ -82,6 +88,7 @@ func TestLoad(t *testing.T) {
 				file: "./testdata/agentbasiccomponentsallowed.yaml",
 			},
 			want: &Config{
+				instanceId: instanceId,
 				RootLogger: logr.Discard(),
 				Endpoint:   "ws://127.0.0.1:4320/v1/opamp",
 				Capabilities: map[Capability]bool{
@@ -133,6 +140,7 @@ func TestLoad(t *testing.T) {
 				},
 			},
 			want: &Config{
+				instanceId: instanceId,
 				RootLogger: logr.Discard(),
 				Endpoint:   "ws://127.0.0.1:4320/v1/opamp",
 				Headers: map[string]string{
@@ -158,6 +166,41 @@ func TestLoad(t *testing.T) {
 			},
 			wantErr: assert.NoError,
 		},
+		{
+			name: "base case with nonidentify attributes",
+			args: args{
+				file: "./testdata/agentwithdescription.yaml",
+				envVariables: map[string]string{
+					"MY_ENV_VAR_1": "my-env-variable-1-value",
+					"MY_ENV_VAR_2": "my-env-variable-2-value",
+				},
+			},
+			want: &Config{
+				instanceId: instanceId,
+				RootLogger: logr.Discard(),
+				Endpoint:   "ws://127.0.0.1:4320/v1/opamp",
+				AgentDescription: AgentDescription{
+					NonIdentifyingAttributes: map[string]string{
+						"custom.attribute": "custom-value",
+					},
+				},
+				Capabilities: map[Capability]bool{
+					AcceptsRemoteConfig:            true,
+					ReportsEffectiveConfig:         true,
+					ReportsOwnTraces:               true,
+					ReportsOwnMetrics:              true,
+					ReportsOwnLogs:                 true,
+					AcceptsOpAMPConnectionSettings: true,
+					AcceptsOtherConnectionSettings: true,
+					AcceptsRestartCommand:          true,
+					ReportsHealth:                  true,
+					ReportsRemoteConfig:            true,
+					AcceptsPackages:                false,
+					ReportsPackageStatuses:         false,
+				},
+			},
+			wantErr: assert.NoError,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -173,9 +216,41 @@ func TestLoad(t *testing.T) {
 				return
 			}
 			// there are some fields we don't care about, so we ignore them.
+			got.instanceId = tt.want.instanceId
 			got.ClusterConfig = tt.want.ClusterConfig
 			got.RootLogger = tt.want.RootLogger
 			assert.Equalf(t, tt.want, got, "Load(%v)", tt.args.file)
 		})
 	}
 }
+
+func TestGetDescription(t *testing.T) {
+	got := NewConfig(logr.Discard())
+	instanceId := uuid.New()
+	got.instanceId = instanceId
+	err := LoadFromFile(got, "./testdata/agentwithdescription.yaml")
+	require.NoError(t, err, fmt.Sprintf("Load(%v)", "./testdata/agentwithdescription.yaml"))
+	desc := got.GetDescription()
+	assert.Len(t, desc.IdentifyingAttributes, 3)
+	assert.Contains(t, desc.IdentifyingAttributes, &protobufs.KeyValue{Key: "service.instance.id", Value: &protobufs.AnyValue{
+		Value: &protobufs.AnyValue_StringValue{StringValue: instanceId.String()},
+	}})
+	assert.Len(t, desc.NonIdentifyingAttributes, 3)
+	assert.Contains(t, desc.NonIdentifyingAttributes, &protobufs.KeyValue{Key: "custom.attribute", Value: &protobufs.AnyValue{
+		Value: &protobufs.AnyValue_StringValue{StringValue: "custom-value"},
+	}})
+}
+
+func TestGetDescriptionNoneSet(t *testing.T) {
+	got := NewConfig(logr.Discard())
+	instanceId := uuid.New()
+	got.instanceId = instanceId
+	err := LoadFromFile(got, "./testdata/agent.yaml")
+	require.NoError(t, err, fmt.Sprintf("Load(%v)", "./testdata/agent.yaml"))
+	desc := got.GetDescription()
+	assert.Len(t, desc.IdentifyingAttributes, 3)
+	assert.Contains(t, desc.IdentifyingAttributes, &protobufs.KeyValue{Key: "service.instance.id", Value: &protobufs.AnyValue{
+		Value: &protobufs.AnyValue_StringValue{StringValue: instanceId.String()},
+	}})
+	assert.Len(t, desc.NonIdentifyingAttributes, 2)
+}
diff --git a/cmd/operator-opamp-bridge/config/testdata/agentwithdescription.yaml b/cmd/operator-opamp-bridge/config/testdata/agentwithdescription.yaml
new file mode 100644
index 00000000..b2b3e360
--- /dev/null
+++ b/cmd/operator-opamp-bridge/config/testdata/agentwithdescription.yaml
@@ -0,0 +1,17 @@
+endpoint: ws://127.0.0.1:4320/v1/opamp
+description:
+  non_identifying_attributes:
+    custom.attribute: "custom-value"
+capabilities:
+  AcceptsRemoteConfig: true
+  ReportsEffectiveConfig: true
+  AcceptsPackages: false
+  ReportsPackageStatuses: false
+  ReportsOwnTraces: true
+  ReportsOwnMetrics: true
+  ReportsOwnLogs: true
+  AcceptsOpAMPConnectionSettings: true
+  AcceptsOtherConnectionSettings: true
+  AcceptsRestartCommand: true
+  ReportsHealth: true
+  ReportsRemoteConfig: true
diff --git a/cmd/otel-allocator/internal/config/config_test.go b/cmd/otel-allocator/internal/config/config_test.go
index 394ac583..b34653ee 100644
--- a/cmd/otel-allocator/internal/config/config_test.go
+++ b/cmd/otel-allocator/internal/config/config_test.go
@@ -51,8 +51,12 @@ func TestLoad(t *testing.T) {
 				},
 				FilterStrategy: DefaultFilterStrategy,
 				PrometheusCR: PrometheusCRConfig{
-					Enabled:        true,
-					ScrapeInterval: model.Duration(time.Second * 60),
+					Enabled:                         true,
+					ScrapeInterval:                  model.Duration(time.Second * 60),
+					ServiceMonitorNamespaceSelector: &metav1.LabelSelector{},
+					PodMonitorNamespaceSelector:     &metav1.LabelSelector{},
+					ScrapeConfigNamespaceSelector:   &metav1.LabelSelector{},
+					ProbeNamespaceSelector:          &metav1.LabelSelector{},
 				},
 				HTTPS: HTTPSServerConfig{
 					Enabled:         true,
@@ -142,7 +146,11 @@ func TestLoad(t *testing.T) {
 							"release": "test",
 						},
 					},
-					ScrapeInterval: DefaultCRScrapeInterval,
+					ServiceMonitorNamespaceSelector: &metav1.LabelSelector{},
+					PodMonitorNamespaceSelector:     &metav1.LabelSelector{},
+					ScrapeConfigNamespaceSelector:   &metav1.LabelSelector{},
+					ProbeNamespaceSelector:          &metav1.LabelSelector{},
+					ScrapeInterval:                  DefaultCRScrapeInterval,
 				},
 				PromConfig: &promconfig.Config{
 					GlobalConfig: promconfig.GlobalConfig{
@@ -213,7 +221,11 @@ func TestLoad(t *testing.T) {
 							"release": "test",
 						},
 					},
-					ScrapeInterval: DefaultCRScrapeInterval,
+					ServiceMonitorNamespaceSelector: &metav1.LabelSelector{},
+					PodMonitorNamespaceSelector:     &metav1.LabelSelector{},
+					ScrapeConfigNamespaceSelector:   &metav1.LabelSelector{},
+					ProbeNamespaceSelector:          &metav1.LabelSelector{},
+					ScrapeInterval:                  DefaultCRScrapeInterval,
 				},
 				PromConfig: &promconfig.Config{
 					GlobalConfig: promconfig.GlobalConfig{
@@ -308,7 +320,11 @@ func TestLoad(t *testing.T) {
 							},
 						},
 					},
-					ScrapeInterval: DefaultCRScrapeInterval,
+					ServiceMonitorNamespaceSelector: &metav1.LabelSelector{},
+					PodMonitorNamespaceSelector:     &metav1.LabelSelector{},
+					ScrapeConfigNamespaceSelector:   &metav1.LabelSelector{},
+					ProbeNamespaceSelector:          &metav1.LabelSelector{},
+					ScrapeInterval:                  DefaultCRScrapeInterval,
 				},
 				PromConfig: &promconfig.Config{
 					GlobalConfig: promconfig.GlobalConfig{
@@ -403,7 +419,11 @@ func TestLoad(t *testing.T) {
 							},
 						},
 					},
-					ScrapeInterval: DefaultCRScrapeInterval,
+					ServiceMonitorNamespaceSelector: &metav1.LabelSelector{},
+					PodMonitorNamespaceSelector:     &metav1.LabelSelector{},
+					ScrapeConfigNamespaceSelector:   &metav1.LabelSelector{},
+					ProbeNamespaceSelector:          &metav1.LabelSelector{},
+					ScrapeInterval:                  DefaultCRScrapeInterval,
 				},
 				PromConfig: &promconfig.Config{
 					GlobalConfig: promconfig.GlobalConfig{
diff --git a/cmd/otel-allocator/internal/watcher/promOperator_test.go b/cmd/otel-allocator/internal/watcher/promOperator_test.go
index 771a8d3d..cb87bfa4 100644
--- a/cmd/otel-allocator/internal/watcher/promOperator_test.go
+++ b/cmd/otel-allocator/internal/watcher/promOperator_test.go
@@ -43,7 +43,7 @@ var defaultScrapeProtocols = []promconfig.ScrapeProtocol{
 }
 
 func TestLoadConfig(t *testing.T) {
-
+	namespace := "test"
 	tests := []struct {
 		name            string
 		serviceMonitors []*monitoringv1.ServiceMonitor
@@ -60,7 +60,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "simple",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.ServiceMonitorSpec{
 						JobLabel: "test",
@@ -76,7 +76,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "simple",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.PodMonitorSpec{
 						JobLabel: "test",
@@ -109,7 +109,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "endpointslice",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -131,7 +131,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "pod",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -149,7 +149,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "auth",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.ServiceMonitorSpec{
 						JobLabel: "auth",
@@ -202,7 +202,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "endpointslice",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -227,7 +227,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "bearer",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.PodMonitorSpec{
 						JobLabel: "bearer",
@@ -270,7 +270,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "pod",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -295,7 +295,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "valid-sm",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.ServiceMonitorSpec{
 						JobLabel: "test",
@@ -311,7 +311,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "valid-pm",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.PodMonitorSpec{
 						JobLabel: "test",
@@ -325,7 +325,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "invalid-pm",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.PodMonitorSpec{
 						JobLabel: "test",
@@ -366,7 +366,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "endpointslice",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -388,7 +388,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "pod",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -406,7 +406,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "valid-sm",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.ServiceMonitorSpec{
 						JobLabel: "test",
@@ -420,7 +420,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "invalid-sm",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.ServiceMonitorSpec{
 						JobLabel: "test",
@@ -444,7 +444,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "valid-pm",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.PodMonitorSpec{
 						JobLabel: "test",
@@ -477,7 +477,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "endpointslice",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -499,7 +499,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "pod",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -517,7 +517,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "sm-1",
-						Namespace: "test",
+						Namespace: namespace,
 						Labels: map[string]string{
 							"testsvc": "testsvc",
 						},
@@ -537,7 +537,7 @@ func TestLoadConfig(t *testing.T) {
 						Namespace: "test",
 					},
 					Spec: monitoringv1.ServiceMonitorSpec{
-						JobLabel: "test",
+						JobLabel: namespace,
 						Endpoints: []monitoringv1.Endpoint{
 							{
 								Port: "web",
@@ -570,7 +570,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "endpointslice",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -588,7 +588,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "pm-1",
-						Namespace: "test",
+						Namespace: namespace,
 						Labels: map[string]string{
 							"testpod": "testpod",
 						},
@@ -605,7 +605,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "pm-2",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.PodMonitorSpec{
 						JobLabel: "test",
@@ -641,7 +641,7 @@ func TestLoadConfig(t *testing.T) {
 							&kubeDiscovery.SDConfig{
 								Role: "pod",
 								NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{
-									Names:               []string{"test"},
+									Names:               []string{namespace},
 									IncludeOwnNamespace: false,
 								},
 								HTTPClientConfig: config.DefaultHTTPClientConfig,
@@ -659,7 +659,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "scrapeconfig-test-1",
-						Namespace: "test",
+						Namespace: namespace,
 						Labels: map[string]string{
 							"testpod": "testpod",
 						},
@@ -723,7 +723,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "probe-test-1",
-						Namespace: "test",
+						Namespace: namespace,
 						Labels: map[string]string{
 							"testpod": "testpod",
 						},
@@ -771,7 +771,7 @@ func TestLoadConfig(t *testing.T) {
 										},
 									},
 									Labels: map[model.LabelName]model.LabelValue{
-										"namespace": "test",
+										"namespace": model.LabelValue(namespace),
 									},
 									Source: "0",
 								},
@@ -803,7 +803,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "sm-2",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.ServiceMonitorSpec{
 						JobLabel: "test",
@@ -873,7 +873,7 @@ func TestLoadConfig(t *testing.T) {
 				{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "pm-2",
-						Namespace: "test",
+						Namespace: namespace,
 					},
 					Spec: monitoringv1.PodMonitorSpec{
 						JobLabel: "test",
@@ -926,7 +926,7 @@ func TestLoadConfig(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			w, _ := getTestPrometheusCRWatcher(t, tt.serviceMonitors, tt.podMonitors, tt.probes, tt.scrapeConfigs, tt.cfg)
+			w, _ := getTestPrometheusCRWatcher(t, namespace, tt.serviceMonitors, tt.podMonitors, tt.probes, tt.scrapeConfigs, tt.cfg)
 
 			// Start namespace informers in order to populate cache.
 			go w.nsInformer.Run(w.stopChannel)
@@ -957,6 +957,7 @@ func TestLoadConfig(t *testing.T) {
 
 func TestNamespaceLabelUpdate(t *testing.T) {
 	var err error
+	namespace := "test"
 	podMonitors := []*monitoringv1.PodMonitor{
 		{
 			ObjectMeta: metav1.ObjectMeta{
@@ -975,7 +976,7 @@ func TestNamespaceLabelUpdate(t *testing.T) {
 		{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "pm-2",
-				Namespace: "test",
+				Namespace: namespace,
 			},
 			Spec: monitoringv1.PodMonitorSpec{
 				JobLabel: "test",
@@ -1031,7 +1032,7 @@ func TestNamespaceLabelUpdate(t *testing.T) {
 		ScrapeConfigs: []*promconfig.ScrapeConfig{},
 	}
 
-	w, source := getTestPrometheusCRWatcher(t, nil, podMonitors, nil, nil, cfg)
+	w, source := getTestPrometheusCRWatcher(t, namespace, nil, podMonitors, nil, nil, cfg)
 	events := make(chan Event, 1)
 	eventInterval := 5 * time.Millisecond
 
@@ -1079,10 +1080,11 @@ func TestNamespaceLabelUpdate(t *testing.T) {
 
 func TestRateLimit(t *testing.T) {
 	var err error
+	namespace := "test"
 	serviceMonitor := &monitoringv1.ServiceMonitor{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "simple",
-			Namespace: "test",
+			Namespace: namespace,
 		},
 		Spec: monitoringv1.ServiceMonitorSpec{
 			JobLabel: "test",
@@ -1097,7 +1099,7 @@ func TestRateLimit(t *testing.T) {
 	eventInterval := 500 * time.Millisecond
 	cfg := allocatorconfig.Config{}
 
-	w, _ := getTestPrometheusCRWatcher(t, nil, nil, nil, nil, cfg)
+	w, _ := getTestPrometheusCRWatcher(t, namespace, nil, nil, nil, nil, cfg)
 	defer w.Close()
 	w.eventInterval = eventInterval
 
@@ -1158,7 +1160,15 @@ func TestRateLimit(t *testing.T) {
 
 // getTestPrometheusCRWatcher creates a test instance of PrometheusCRWatcher with fake clients
 // and test secrets.
-func getTestPrometheusCRWatcher(t *testing.T, svcMonitors []*monitoringv1.ServiceMonitor, podMonitors []*monitoringv1.PodMonitor, probes []*monitoringv1.Probe, scrapeConfigs []*promv1alpha1.ScrapeConfig, cfg allocatorconfig.Config) (*PrometheusCRWatcher, *fcache.FakeControllerSource) {
+func getTestPrometheusCRWatcher(
+	t *testing.T,
+	namespace string,
+	svcMonitors []*monitoringv1.ServiceMonitor,
+	podMonitors []*monitoringv1.PodMonitor,
+	probes []*monitoringv1.Probe,
+	scrapeConfigs []*promv1alpha1.ScrapeConfig,
+	cfg allocatorconfig.Config,
+) (*PrometheusCRWatcher, *fcache.FakeControllerSource) {
 	mClient := fakemonitoringclient.NewSimpleClientset()
 	for _, sm := range svcMonitors {
 		if sm != nil {
@@ -1225,6 +1235,9 @@ func getTestPrometheusCRWatcher(t *testing.T, svcMonitors []*monitoringv1.Servic
 	serviceDiscoveryRole := monitoringv1.ServiceDiscoveryRole("EndpointSlice")
 
 	prom := &monitoringv1.Prometheus{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: namespace,
+		},
 		Spec: monitoringv1.PrometheusSpec{
 			CommonPrometheusFields: monitoringv1.CommonPrometheusFields{
 				ScrapeInterval:                  monitoringv1.Duration("30s"),
diff --git a/internal/manifests/collector/collector_test.go b/internal/manifests/collector/collector_test.go
index e8e297c2..62cc6ede 100644
--- a/internal/manifests/collector/collector_test.go
+++ b/internal/manifests/collector/collector_test.go
@@ -11,7 +11,6 @@ import (
 	"github.com/go-logr/logr"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
-	otelColFeatureGate "go.opentelemetry.io/collector/featuregate"
 	v1 "k8s.io/api/authorization/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
 
@@ -21,7 +20,6 @@ import (
 	"github.com/open-telemetry/opentelemetry-operator/internal/config"
 	"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
 	irbac "github.com/open-telemetry/opentelemetry-operator/internal/rbac"
-	"github.com/open-telemetry/opentelemetry-operator/pkg/featuregate"
 )
 
 func TestNeedsCheckSaPermissions(t *testing.T) {
@@ -139,7 +137,6 @@ func TestBuild(t *testing.T) {
 		params          manifests.Params
 		expectedObjects int
 		wantErr         bool
-		featureGate     *otelColFeatureGate.Gate
 	}{
 		{
 			name: "deployment mode builds expected manifests",
@@ -229,7 +226,6 @@ func TestBuild(t *testing.T) {
 			},
 			expectedObjects: 6,
 			wantErr:         false,
-			featureGate:     featuregate.PrometheusOperatorIsAvailable,
 		},
 		{
 			name: "metrics enabled adds service monitors",
@@ -265,7 +261,6 @@ func TestBuild(t *testing.T) {
 			},
 			expectedObjects: 9,
 			wantErr:         false,
-			featureGate:     featuregate.PrometheusOperatorIsAvailable,
 		},
 		{
 			name: "check sa permissions",
@@ -304,20 +299,11 @@ func TestBuild(t *testing.T) {
 			},
 			expectedObjects: 9,
 			wantErr:         true,
-			featureGate:     featuregate.PrometheusOperatorIsAvailable,
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if tt.featureGate != nil {
-				err := otelColFeatureGate.GlobalRegistry().Set(tt.featureGate.ID(), true)
-				require.NoError(t, err)
-				defer func() {
-					err := otelColFeatureGate.GlobalRegistry().Set(tt.featureGate.ID(), false)
-					require.NoError(t, err)
-				}()
-			}
 
 			objects, err := Build(tt.params)
 			if tt.wantErr {
diff --git a/internal/manifests/opampbridge/configmap_test.go b/internal/manifests/opampbridge/configmap_test.go
index 6001ef7d..da1d6c2e 100644
--- a/internal/manifests/opampbridge/configmap_test.go
+++ b/internal/manifests/opampbridge/configmap_test.go
@@ -46,6 +46,9 @@ componentsAllowed:
   - memory_limiter
   receivers:
   - otlp
+description:
+  non_identifying_attributes:
+    hello: world
 endpoint: ws://opamp-server:4320/v1/opamp
 headers:
   authorization: access-12345-token
@@ -85,6 +88,11 @@ headers:
 					Image:    tc.image,
 					Endpoint: "ws://opamp-server:4320/v1/opamp",
 					Headers:  map[string]string{"authorization": "access-12345-token"},
+					Description: &v1alpha1.AgentDescription{
+						NonIdentifyingAttributes: map[string]string{
+							"hello": "world",
+						},
+					},
 					Capabilities: map[v1alpha1.OpAMPBridgeCapability]bool{
 						v1alpha1.OpAMPBridgeCapabilityReportsStatus:                  true,
 						v1alpha1.OpAMPBridgeCapabilityAcceptsRemoteConfig:            true,