From e53d93a6bcab68aa02cac2b18fcc0f7f2d51b459 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 17 Feb 2026 14:38:25 +0100 Subject: [PATCH] DRA integration: fix data race Gomega matchers cannot be used concurrently, they get mutated. Each user must get its own separate instance. WARNING: DATA RACE Write at 0x00c0195da678 by goroutine 322445: github.com/onsi/gomega/matchers.(*AndMatcher).Match() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/matchers/and.go:18 +0x44 github.com/onsi/gomega/internal.(*AsyncAssertion).pollMatcher() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/async_assertion.go:387 +0xbe github.com/onsi/gomega/internal.(*AsyncAssertion).match() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/async_assertion.go:415 +0x47b github.com/onsi/gomega/internal.(*AsyncAssertion).Should() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/async_assertion.go:145 +0xc4 k8s.io/kubernetes/test/integration/dra.testShareResourceClaimSequentially.func3() /home/prow/go/src/k8s.io/kubernetes/test/integration/dra/resourceclaim_test.go:104 +0x361 k8s.io/kubernetes/test/integration/dra.testShareResourceClaimSequentially.func5() /home/prow/go/src/k8s.io/kubernetes/test/integration/dra/resourceclaim_test.go:139 +0xa1 sync.(*WaitGroup).Go.func1() /home/prow/go/src/k8s.io/kubernetes/_output/local/go/cache/mod/golang.org/toolchain@v0.0.1-go1.25.7.linux-amd64/src/sync/waitgroup.go:239 +0x5d Previous write at 0x00c0195da678 by goroutine 322438: github.com/onsi/gomega/matchers.(*AndMatcher).Match() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/matchers/and.go:18 +0x44 github.com/onsi/gomega/internal.(*AsyncAssertion).pollMatcher() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/async_assertion.go:387 +0xbe github.com/onsi/gomega/internal.(*AsyncAssertion).match() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/async_assertion.go:415 +0x47b github.com/onsi/gomega/internal.(*AsyncAssertion).Should() /home/prow/go/src/k8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/async_assertion.go:145 +0xc4 k8s.io/kubernetes/test/integration/dra.testShareResourceClaimSequentially.func3() /home/prow/go/src/k8s.io/kubernetes/test/integration/dra/resourceclaim_test.go:104 +0x361 k8s.io/kubernetes/test/integration/dra.testShareResourceClaimSequentially.func5() /home/prow/go/src/k8s.io/kubernetes/test/integration/dra/resourceclaim_test.go:139 +0xa1 sync.(*WaitGroup).Go.func1() /home/prow/go/src/k8s.io/kubernetes/_output/local/go/cache/mod/golang.org/toolchain@v0.0.1-go1.25.7.linux-amd64/src/sync/waitgroup.go:239 +0x5d --- test/integration/dra/resourceclaim_test.go | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/test/integration/dra/resourceclaim_test.go b/test/integration/dra/resourceclaim_test.go index a1838189b73..d57c60ba1ab 100644 --- a/test/integration/dra/resourceclaim_test.go +++ b/test/integration/dra/resourceclaim_test.go @@ -75,24 +75,28 @@ func testShareResourceClaimSequentially(tCtx ktesting.TContext) { podStartTimeout := 5 * time.Minute * time.Duration(numMaxPods) ensureDuration := time.Minute // Don't check for too long, even if it is less precise. - podIsScheduled := gomega.HaveField("Spec.NodeName", gomega.Not(gomega.BeEmpty())) - podIsPending := gomega.And( - gomega.HaveField("Status.Phase", gomega.Equal(v1.PodPending)), - gomega.HaveField("Status.Conditions", gomega.ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Type": gomega.Equal(v1.PodScheduled), - "Status": gomega.Equal(v1.ConditionFalse), - // With the current scheduler code, creating too many pods is treated as an error, - // so this would have to be "SchedulerError". Maybe it shouldn't be an error? - // "Reason": gomega.Equal(v1.PodReasonUnschedulable), - }))), - ) + podIsScheduled := func() gomega.OmegaMatcher { + return gomega.HaveField("Spec.NodeName", gomega.Not(gomega.BeEmpty())) + } + podIsPending := func() gomega.OmegaMatcher { + return gomega.And( + gomega.HaveField("Status.Phase", gomega.Equal(v1.PodPending)), + gomega.HaveField("Status.Conditions", gomega.ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Type": gomega.Equal(v1.PodScheduled), + "Status": gomega.Equal(v1.ConditionFalse), + // With the current scheduler code, creating too many pods is treated as an error, + // so this would have to be "SchedulerError". Maybe it shouldn't be an error? + // "Reason": gomega.Equal(v1.PodReasonUnschedulable), + }))), + ) + } assertPodScheduledEventually := func(tCtx ktesting.TContext, pod *v1.Pod) { tCtx.Helper() tCtx.AssertEventually(tCtx.Client().CoreV1().Pods(pod.Namespace).Get). WithArguments(pod.Name, metav1.GetOptions{}). WithTimeout(podStartTimeout). WithPolling(10*time.Second). - Should(podIsScheduled, "Pod %s should get scheduled.", pod.Name) + Should(podIsScheduled(), "Pod %s should get scheduled.", pod.Name) } assertPodPendingEventually := func(tCtx ktesting.TContext, pod *v1.Pod) { @@ -101,7 +105,7 @@ func testShareResourceClaimSequentially(tCtx ktesting.TContext) { WithArguments(pod.Name, metav1.GetOptions{}). WithTimeout(ensureDuration). WithPolling(10*time.Second). - Should(podIsPending, "Pod %s should remain pending.", pod.Name) + Should(podIsPending(), "Pod %s should remain pending.", pod.Name) } // We don't know the order. All that matters is that all of them get scheduled eventually.