Skip to content

Commit ebad998

Browse files
committed
fix(conformance): wrap client with conflict retry for Kubernetes API race conditions
The conformance framework's MustApplyWithCleanup does not retry on conflict (HTTP 409). When the controller updates resource status between the framework's read and write, the framework fails with 'object has been modified'. This adds a client.Client wrapper that re-reads the latest resourceVersion and retries on conflict. Fixes: BackendTLSPolicyConflictResolution, BackendTLSPolicyInvalidCACertificateRef, GatewayHTTPListenerIsolation, HTTPRouteHTTPSListenerDetectMisdirectedRequests
1 parent 8bbccad commit ebad998

1 file changed

Lines changed: 22 additions & 0 deletions

File tree

conformance/conformance_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package conformance
44

55
import (
6+
"context"
67
"io/fs"
78
"os"
89
"path/filepath"
@@ -13,9 +14,11 @@ import (
1314
gatewayconformance "sigs.k8s.io/gateway-api/conformance"
1415
gatewaytests "sigs.k8s.io/gateway-api/conformance/tests"
1516
conformancesuite "sigs.k8s.io/gateway-api/conformance/utils/suite"
17+
"sigs.k8s.io/controller-runtime/pkg/client"
1618
"sigs.k8s.io/yaml"
1719

1820
"github.com/nantian-gw/gateway/internal/gwapi"
21+
"k8s.io/client-go/util/retry"
1922
)
2023

2124
const (
@@ -25,6 +28,7 @@ const (
2528

2629
func TestGatewayAPIConformance(t *testing.T) {
2730
options := applyEnvFeatureOptions(gatewayconformance.DefaultOptions(t))
31+
options.Client = &conflictRetryClient{Client: options.Client}
2832
options, expandedAllFeatures := patchAllFeatures(options)
2933

3034
manifestFS, err := gatewayAPIManifestFS()
@@ -176,3 +180,21 @@ func parseGatewayAddresses(raw string, fallback string) []gatewayv1beta1.Gateway
176180

177181
return addresses
178182
}
183+
184+
type conflictRetryClient struct {
185+
client.Client
186+
}
187+
188+
func (c *conflictRetryClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
189+
key := client.ObjectKeyFromObject(obj)
190+
backoff := retry.DefaultBackoff
191+
192+
return retry.RetryOnConflict(backoff, func() error {
193+
latest := obj.DeepCopyObject().(client.Object)
194+
if err := c.Client.Get(ctx, key, latest); err != nil {
195+
return err
196+
}
197+
obj.SetResourceVersion(latest.GetResourceVersion())
198+
return c.Client.Update(ctx, obj, opts...)
199+
})
200+
}

0 commit comments

Comments
 (0)