Repository: jenkinsci/prometheus-plugin (java)

Issue:
Issues with namespace
              @Waschndolos we are currently testing this fix and surprinsingly, we now have an issue on the "namespace" (or "prefix") of some metrics that are still using the default namespace/prefix instead of using the one we configured in the options... 
could it be related ?

![image](https://github.com/jenkinsci/prometheus-plugin/assets/9500406/bc10e589-698b-46c7-b4e1-3dcb461a10a7)


For example, we configured jenkins to change the default namespace to "gtp" instead of "default" and some metrics are still named "default_xxx" whereas other have the right prefix.

![image](https://github.com/jenkinsci/prometheus-plugin/assets/9500406/f0bdb71d-8d01-4a39-8ce5-89e211699e1e)

After searching a bit in the code, we saw that some metrics methods are not using the "prefix" arguments here : https://github.com/jenkinsci/prometheus-plugin/blob/master/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildCollectorFactory.java#L18-L49

but it does not seem to match exactly the metrics for which we have the default prefix... so not so sure it comes from there.

Additionnal details : 
- jenkins version : 2.426.2
- prometheus plugin version : 2.5.0
- we are using Configuration-as-Code plugin to configure all our plugins, including the prometheus-metrics one.

_Originally posted by @yogeek in https://github.com/jenkinsci/prometheus-plugin/issues/606#issuecomment-1887448328_
            

Pull request:
Fix Issue With Namespace Not Updating for Build Counters
Fixes #615 

### Changes proposed
* Return a new counter when the namespace is updated

### Checklist

- [x] Includes tests covering the new functionality?
- [x] Ready for review
- [x] Follows CONTRIBUTING rules

### Notify

![image](https://github.com/jenkinsci/prometheus-plugin/assets/48965785/bc61031b-7dc8-4594-b509-2353cd837e1f)


@Waschndolos


Patch:
diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManager.java b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManager.java
index f81133b..6dddf81 100644
--- a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManager.java
+++ b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManager.java
@@ -5,6 +5,8 @@ import java.util.HashMap;
 
 import org.jenkinsci.plugins.prometheus.collectors.CollectorType;
 import org.jenkinsci.plugins.prometheus.collectors.MetricCollector;
+import org.jenkinsci.plugins.prometheus.config.PrometheusConfiguration;
+import org.jenkinsci.plugins.prometheus.util.ConfigurationUtils;
 
 import hudson.model.Run;
 import io.prometheus.client.Collector;
@@ -80,6 +82,9 @@ public class CounterManager {
         // Prefix of the counter.
         private String prefix;
 
+        // namespace of the counter
+        private String namespace;
+
         /*
          * Creates new counter entry
          */
@@ -87,6 +92,7 @@ public class CounterManager {
             this.labels = labels;
             this.type = type;
             this.prefix = prefix;
+            this.namespace = ConfigurationUtils.getNamespace();
         }
 
         @Override
@@ -98,14 +104,21 @@ public class CounterManager {
 
             CounterEntry entry = (CounterEntry) obj;
 
+            // Compare the prefix
             if(this.prefix != null && !this.prefix.equals(entry.prefix)){
                 return false;
             }
 
+            // Compare the entry Counter type
             if(this.type != entry.type){
                 return false;
             }
 
+            // Compare namespace values.
+            if(this.namespace != null && !this.namespace.equals(entry.namespace)){
+                return false;
+            }
+
             // Compare labels
             return Arrays.equals(labels, entry.labels);
         }
@@ -114,7 +127,8 @@ public class CounterManager {
         public int hashCode() {
             int typeHash = type != null ? type.hashCode() : 0;
             int prefixHash = prefix != null ? prefix.hashCode() : 0;
-            int result = 31 * (typeHash + Arrays.hashCode(labels) + prefixHash);
+            int namespaceHash = namespace != null ? namespace.hashCode() : 0;
+            int result = 31 * (typeHash + Arrays.hashCode(labels) + prefixHash + namespaceHash);
             return result;
         }
     }


Test patch:
diff --git a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManagerTest.java b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManagerTest.java
index a352527..f284fc4 100644
--- a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManagerTest.java
+++ b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/CounterManagerTest.java
@@ -37,6 +37,27 @@ public class CounterManagerTest extends MockedRunCollectorTest {
         }
     }
 
+    @Test
+    public void TestNamespaceChangeReturnsNewCounter() {
+        String[] labels = new String[] { "TestLabel" };
+
+        try (MockedStatic<PrometheusConfiguration> configStatic = mockStatic(PrometheusConfiguration.class)) {
+            PrometheusConfiguration config = mock(PrometheusConfiguration.class);
+            
+            // Use default namespace for one counter
+            when(config.getDefaultNamespace()).thenReturn(getNamespace());
+            configStatic.when(PrometheusConfiguration::get).thenReturn(config);
+            var retrievedCounter = manager.getCounter(CollectorType.BUILD_SUCCESSFUL_COUNTER, labels, null);
+
+            // Second counter returns modified namespace
+            when(config.getDefaultNamespace()).thenReturn("modified_namespace");
+            var retrievedCounter2 = manager.getCounter(CollectorType.BUILD_SUCCESSFUL_COUNTER, labels, null);
+
+            // Should be a value reference comparison. They should not be the same metric since the namespace has changed.
+            Assert.assertNotEquals(retrievedCounter, retrievedCounter2);
+        }
+    }
+
     @Test
     public void TestLabelChangeReturnsNewCounter(){
           String[] label1 = new String[]{"labels"};