Repository: projectcapsule/capsule

Issue:
e2e test occasionally fail for ResourcePool Scheduling
# Bug description

E2E tests are unstable due to ResourcePool Scheduling - Ordered occasionally failing.
# How to reproduce

E2E test fail due to expected message not matching claim.Status.Condition.Message

What we expect:
`requested: limits.cpu=2, queued: limits.cpu=4; requested: requests.cpu=2, queued: requests.cpu=4
`
What occasionally claim.Status.Condition.Message contains:
`requested: requests.cpu=2, queued: requests.cpu=4; requested: limits.cpu=2, queued: limits.cpu=4
`
Example failed e2e job [here](https://github.com/projectcapsule/capsule/actions/runs/17323293921/job/49186125178?pr=1619
).

# Additional context

Problem  sources: https://github.com/projectcapsule/capsule/blob/8dbbe3c4c44b65e2548253af8303f9503118f83e/e2e/resourcepool_test.go#L1148


Discussion:
@Raj-G07: @Svarrogh1337 I would like to work on this issue , just for clarification you want to change only what inside the Equal("") , or something else!@oliverbaehler: We are unsure if the problem is the test or rather in the reaourepool logic. I suspect its about the order how the resources are printed, because that was quiet hacky to implement. But maybe theres also a value mismatch, which would be more sever. 

Part of this issue is to investigate the why@prometherion: The root cause is that maps are accessed in random order in Go, making unpredictable.

We should avoid expecting an order, and rather, use a `set.String` filling it with the sanitized entries and checking all of them are there.

Or, ordering the entries when iterating over the `resource` field which is a map.@oliverbaehler: Thing is we are checking the status text in these tests, so we weither have to not check the string as its unpredictable or something else@Svarrogh1337: @Raj-G07 you can go ahead and work on it and have in mind what @oliverbaehler and @prometherion shared.
If you have any questions, don't hesitate to ask.@github-actions: This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.

Solution patch:
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index ee3fbe3..48cffe5 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -37,6 +37,6 @@ jobs:
           path: results.sarif
           retention-days: 5
       - name: Upload to code-scanning
-        uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6
+        uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
         with:
           sarif_file: results.sarif
diff --git a/hack/distro/monitoring/kube-prometheus-stack/release.flux.yaml b/hack/distro/monitoring/kube-prometheus-stack/release.flux.yaml
index 812220e..338d070 100644
--- a/hack/distro/monitoring/kube-prometheus-stack/release.flux.yaml
+++ b/hack/distro/monitoring/kube-prometheus-stack/release.flux.yaml
@@ -13,7 +13,7 @@ spec:
   chart:
     spec:
       chart: kube-prometheus-stack
-      version: "79.11.0"
+      version: "79.12.0"
       sourceRef:
         kind: HelmRepository
         name: kube-prometheus-stack



Test patch:
diff --git a/e2e/resourcepool_test.go b/e2e/resourcepool_test.go
index a30e281..9eb6543 100644
--- a/e2e/resourcepool_test.go
+++ b/e2e/resourcepool_test.go
@@ -5,6 +5,8 @@ package e2e
 
 import (
 	"context"
+	"slices"
+	"strings"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -832,7 +834,13 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
 			err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
 			Expect(err).Should(Succeed())
 
-			Expect(claim.Status.Condition.Message).To(Equal("requested: requests.cpu=4, available: requests.cpu=2"), "Actual message"+claim.Status.Condition.Message)
+			conditions := extractResourcePoolMessage(claim.Status.Condition.Message)
+			expected := []string{
+				"requested.requests.cpu=4",
+				"available.requests.cpu=2",
+			}
+
+			Expect(containsAll(conditions, expected)).To(BeTrue(), "Actual message"+claim.Status.Condition.Message)
 			Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
 			Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
 			Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
@@ -934,7 +942,13 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
 			err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
 			Expect(err).Should(Succeed())
 
-			Expect(claim.Status.Condition.Message).To(Equal("requested: requests.cpu=4, available: requests.cpu=0"), "Actual message"+claim.Status.Condition.Message)
+			conditions := extractResourcePoolMessage(claim.Status.Condition.Message)
+			expected := []string{
+				"requested.requests.cpu=4",
+				"available.requests.cpu=0",
+			}
+
+			Expect(containsAll(conditions, expected)).To(BeTrue(), "Actual message"+claim.Status.Condition.Message)
 			Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
 			Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
 			Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
@@ -1090,7 +1104,13 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
 			err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
 			Expect(err).Should(Succeed())
 
-			Expect(claim.Status.Condition.Message).To(Equal("requested: requests.cpu=4, available: requests.cpu=2"), "Actual message"+claim.Status.Condition.Message)
+			conditions := extractResourcePoolMessage(claim.Status.Condition.Message)
+			expected := []string{
+				"requested.requests.cpu=4",
+				"available.requests.cpu=2",
+			}
+
+			Expect(containsAll(conditions, expected)).To(BeTrue(), "Actual message"+claim.Status.Condition.Message)
 			Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
 			Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
 			Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
@@ -1117,7 +1137,13 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
 			err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
 			Expect(err).Should(Succeed())
 
-			Expect(claim.Status.Condition.Message).To(Equal("requested: limits.cpu=4, available: limits.cpu=2"), "Actual message"+claim.Status.Condition.Message)
+			conditions := extractResourcePoolMessage(claim.Status.Condition.Message)
+			expected := []string{
+				"requested.limits.cpu=4",
+				"available.limits.cpu=2",
+			}
+
+			Expect(containsAll(conditions, expected)).To(BeTrue(), "Actual message"+claim.Status.Condition.Message)
 			Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
 			Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
 			Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
@@ -1145,7 +1171,15 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
 			err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
 			Expect(err).Should(Succeed())
 
-			Expect(claim.Status.Condition.Message).To(Equal("requested: limits.cpu=2, queued: limits.cpu=4; requested: requests.cpu=2, queued: requests.cpu=4"), "Actual message"+claim.Status.Condition.Message)
+			conditions := extractResourcePoolMessage(claim.Status.Condition.Message)
+			expected := []string{
+				"requested.limits.cpu=2",
+				"queued.limits.cpu=4",
+				"requested.requests.cpu=2",
+				"queued.requests.cpu=4",
+			}
+
+			Expect(containsAll(conditions, expected)).To(BeTrue(), "Actual message"+claim.Status.Condition.Message)
 			Expect(claim.Status.Condition.Reason).To(Equal(meta.QueueExhaustedReason))
 			Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
 			Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
@@ -1275,8 +1309,15 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
 
 			err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
 			Expect(err).Should(Succeed())
+			conditions := extractResourcePoolMessage(claim.Status.Condition.Message)
+			expected := []string{
+				"requested.limits.cpu=2",
+				"available.limits.cpu=0",
+				"requested.requests.cpu=2",
+				"available.requests.cpu=0",
+			}
 
-			Expect(claim.Status.Condition.Message).To(Equal("requested: limits.cpu=2, available: limits.cpu=0; requested: requests.cpu=2, available: requests.cpu=0"), "Actual message "+claim.Status.Condition.Message)
+			Expect(containsAll(conditions, expected)).To(BeTrue(), "Actual message"+claim.Status.Condition.Message)
 			Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
 			Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
 			Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
@@ -2024,3 +2065,35 @@ func isBoundToPool(pool *capsulev1beta2.ResourcePool, claim *capsulev1beta2.Reso
 
 	return true
 }
+
+func containsAll[T comparable](haystack []T, needles []T) bool {
+	for _, n := range needles {
+		if !slices.Contains(haystack, n) {
+			return false
+		}
+	}
+	return true
+}
+
+func extractResourcePoolMessage(msg string) []string {
+	var out []string
+
+	parts := strings.FieldsFunc(msg, func(r rune) bool {
+		return r == ',' || r == ';'
+	})
+
+	for _, p := range parts {
+		p = strings.TrimSpace(p)
+
+		kv := strings.SplitN(p, ": ", 2)
+		if len(kv) != 2 {
+			continue
+		}
+
+		kind := kv[0]
+		value := kv[1]
+
+		out = append(out, kind+"."+value)
+	}
+	return out
+}