diff --git a/internal/gatewayapi/status/conditions.go b/internal/gatewayapi/status/conditions.go index 5f8a167115..e3f398a0f5 100644 --- a/internal/gatewayapi/status/conditions.go +++ b/internal/gatewayapi/status/conditions.go @@ -14,9 +14,10 @@ package status import ( - "reflect" "unicode" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -31,15 +32,16 @@ func MergeConditions(conditions []metav1.Condition, updates ...metav1.Condition) var additions []metav1.Condition for i := range updates { updates[i].Message = truncateConditionMessage(updates[i].Message) + update := updates[i] add := true for j := range conditions { if conditions[j].Type == updates[i].Type { add = false - if !reflect.DeepEqual(conditions[j], updates[i]) { - conditions[j].Status = updates[i].Status - conditions[j].Reason = updates[i].Reason - conditions[j].Message = updates[i].Message - conditions[j].ObservedGeneration = updates[i].ObservedGeneration + if conditionChanged(&conditions[j], &update) { + conditions[j].Status = update.Status + conditions[j].Reason = update.Reason + conditions[j].Message = update.Message + conditions[j].ObservedGeneration = update.ObservedGeneration break } } @@ -52,6 +54,11 @@ func MergeConditions(conditions []metav1.Condition, updates ...metav1.Condition) return conditions } +func conditionChanged(a, b *metav1.Condition) bool { + opts := cmpopts.IgnoreFields(metav1.Condition{}, "Type", "LastTransitionTime") + return !cmp.Equal(*a, *b, opts) +} + func newCondition(t string, status metav1.ConditionStatus, reason, msg string, og int64) metav1.Condition { return metav1.Condition{ Type: t, diff --git a/internal/gatewayapi/status/conditions_test.go b/internal/gatewayapi/status/conditions_test.go index b3ed1b2912..e43a7c7199 100644 --- a/internal/gatewayapi/status/conditions_test.go +++ b/internal/gatewayapi/status/conditions_test.go @@ -15,7 +15,6 @@ package status import ( "errors" - "reflect" "strings" "testing" @@ -41,6 +40,20 @@ func TestConditionChanged(t *testing.T) { a: metav1.Condition{}, b: metav1.Condition{}, }, + { + name: "condition LastTransitionTime should be ignored", + expected: false, + a: metav1.Condition{ + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.Unix(0, 0), + }, + b: metav1.Condition{ + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.Unix(1, 0), + }, + }, { name: "check condition reason differs", expected: true, @@ -70,7 +83,7 @@ func TestConditionChanged(t *testing.T) { } for _, tc := range testCases { - if got := !reflect.DeepEqual(tc.a, tc.b); got != tc.expected { + if got := conditionChanged(&tc.a, &tc.b); got != tc.expected { assert.Equal(t, tc.expected, got, tc.name) } } diff --git a/release-notes/current.yaml b/release-notes/current.yaml index 1100c1d3d3..42abc08586 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -45,6 +45,7 @@ bug fixes: | Fixed a control plane panic caused by concurrent Status mutation racing with the watchable Map coalesce goroutine. Fixed BackendTrafficPolicy rate limit `requests` values above uint32 max (4294967295) being silently truncated modulo 2^32 by the rate limit service and Envoy token bucket. The field now rejects such values at admission time with a clear schema validation error. See envoyproxy/ai-gateway#2012. Fixed status conditions not being updated when a route is rejected due to multiple errors. + Fixed Gateway getting stuck at `Programmed=False` after its LoadBalancer Service IP was restored, by ignoring `LastTransitionTime` when comparing status conditions. # Enhancements that improve performance. performance improvements: |