Repository: cloudnative-pg/cloudnative-pg

Issue:
[Bug]: Duplicated metrics collected when enabling both cluster and pooler pod monitors
### Is there an existing issue already for this bug?

- [x] I have searched for an existing issue, and could not find anything. I believe this is a new bug.

### I have read the troubleshooting guide

- [x] I have read the troubleshooting guide and I think this is a new bug.

### I am running a supported version of CloudNativePG

- [x] I have read the troubleshooting guide and I think this is a new bug.

### Contact Details

_No response_

### Version

1.25 (latest patch)

### What version of Kubernetes are you using?

1.31

### What is your Kubernetes environment?

Other

### How did you install the operator?

Helm

### What happened?

When creating a cluster with podMonitor enabled, the pod monitor are using the following pod selector:

```yaml
  selector:
    matchLabels:
      cnpg.io/cluster: analytics-pg17
```

This selector also matches other extra components like the pgbouncers that are deployed using a `Pooler` and can also specify their own pod monitors.

The cluster pod monitor should also include an extra label to limit its scope to the pods that are owned by the `Cluster` CRD.

For instance:

```yaml
  selector:
    matchLabels:
      cnpg.io/cluster: analytics-pg17
      cnpg.io/podRole: instance
```



### Cluster resource

```shell
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: analytics-pg17
spec:
  …
  monitoring:
    enablePodMonitor: true
  …
```

### Relevant log output

```shell

```

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Discussion:

Solution patch:
diff --git a/pkg/specs/pgbouncer/podmonitor.go b/pkg/specs/pgbouncer/podmonitor.go
index ff7962e3..4ea6abb3 100644
--- a/pkg/specs/pgbouncer/podmonitor.go
+++ b/pkg/specs/pgbouncer/podmonitor.go
@@ -63,7 +63,10 @@ func (c PoolerPodMonitorManager) BuildPodMonitor() *monitoringv1.PodMonitor {
 
 	spec := monitoringv1.PodMonitorSpec{
 		Selector: metav1.LabelSelector{
-			MatchLabels: meta.Labels,
+			MatchLabels: map[string]string{
+				utils.PgbouncerNameLabel: c.pooler.Name,
+				utils.PodRoleLabelName:   string(utils.PodRolePooler),
+			},
 		},
 		PodMetricsEndpoints: []monitoringv1.PodMetricsEndpoint{endpoint},
 	}
diff --git a/pkg/specs/podmonitor.go b/pkg/specs/podmonitor.go
index 4a7ab863..7d1e00bf 100644
--- a/pkg/specs/podmonitor.go
+++ b/pkg/specs/podmonitor.go
@@ -24,6 +24,7 @@ import (
 
 	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
 	"github.com/cloudnative-pg/cloudnative-pg/pkg/certs"
+	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
 )
 
 // ClusterPodMonitorManager builds the PodMonitor for the cluster resource
@@ -74,7 +75,10 @@ func (c ClusterPodMonitorManager) BuildPodMonitor() *monitoringv1.PodMonitor {
 
 	spec := monitoringv1.PodMonitorSpec{
 		Selector: metav1.LabelSelector{
-			MatchLabels: meta.Labels,
+			MatchLabels: map[string]string{
+				utils.ClusterLabelName: c.cluster.Name,
+				utils.PodRoleLabelName: string(utils.PodRoleInstance),
+			},
 		},
 		PodMetricsEndpoints: []monitoringv1.PodMetricsEndpoint{endpoint},
 	}



Test patch:
diff --git a/pkg/specs/pgbouncer/podmonitor_test.go b/pkg/specs/pgbouncer/podmonitor_test.go
index 66a16fd6..49db8ab4 100644
--- a/pkg/specs/pgbouncer/podmonitor_test.go
+++ b/pkg/specs/pgbouncer/podmonitor_test.go
@@ -76,6 +76,7 @@ var _ = Describe("PoolerPodMonitorManager", func() {
 
 			Expect(podMonitor.Spec.Selector.MatchLabels).To(Equal(map[string]string{
 				utils.PgbouncerNameLabel: pooler.Name,
+				utils.PodRoleLabelName:   string(utils.PodRolePooler),
 			}))
 
 			Expect(podMonitor.Spec.PodMetricsEndpoints).To(HaveLen(1))
diff --git a/pkg/specs/podmonitor_test.go b/pkg/specs/podmonitor_test.go
index b043eb5f..1673d6f2 100644
--- a/pkg/specs/podmonitor_test.go
+++ b/pkg/specs/podmonitor_test.go
@@ -62,8 +62,14 @@ var _ = Describe("PodMonitor test", func() {
 		It("should create a valid monitoringv1.PodMonitor object", func() {
 			mgr := NewClusterPodMonitorManager(cluster.DeepCopy())
 			monitor := mgr.BuildPodMonitor()
-			Expect(monitor.Labels[utils.ClusterLabelName]).To(Equal(cluster.Name))
-			Expect(monitor.Spec.Selector.MatchLabels[utils.ClusterLabelName]).To(Equal(cluster.Name))
+			Expect(monitor.Labels).To(BeEquivalentTo(map[string]string{
+				utils.ClusterLabelName: cluster.Name,
+			}))
+			Expect(monitor.Spec.Selector.MatchLabels).To(BeEquivalentTo(map[string]string{
+				utils.ClusterLabelName: cluster.Name,
+				utils.PodRoleLabelName: string(utils.PodRoleInstance),
+			}))
+
 			Expect(monitor.Spec.PodMetricsEndpoints).To(ContainElement(expectedEndpoint))
 		})