From dd4e5598c8bebe894be48d5671c18822a8779a5f Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Mon, 11 May 2026 22:35:01 +0530 Subject: [PATCH 1/2] fix: restore last transition time in merge status conditions Signed-off-by: Rudrakh Panigrahi --- internal/gatewayapi/status/conditions.go | 10 ++++++++-- internal/gatewayapi/status/conditions_test.go | 17 +++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/internal/gatewayapi/status/conditions.go b/internal/gatewayapi/status/conditions.go index 576281cb3a..6de6f4a92f 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" ) @@ -36,7 +37,7 @@ func MergeConditions(conditions []metav1.Condition, updates ...metav1.Condition) for j := range conditions { if conditions[j].Type == update.Type { add = false - if !reflect.DeepEqual(conditions[j], update) { + if conditionChanged(&conditions[j], &update) { conditions[j].Status = update.Status conditions[j].Reason = update.Reason conditions[j].Message = update.Message @@ -53,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) } } From 600e467382344e75488dc29f95d5a9b6fd2c7e04 Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Tue, 12 May 2026 11:56:49 +0530 Subject: [PATCH 2/2] add release note Signed-off-by: Rudrakh Panigrahi --- release-notes/current.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/release-notes/current.yaml b/release-notes/current.yaml index a2a4673618..5c2c6edd30 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -15,6 +15,7 @@ bug fixes: | Fixed ListenerSet and its listeners incorrectly setting `Accepted: False` for InvalidCertificateRef and RefNotPermitted, inconsistent with Gateway behavior and the Gateway API spec. Fixed active HTTP health checks to use Backend endpoint hostnames before falling back to the effective Route hostname. Fixed HTTPS listeners with overlapping hostnames but disjoint certificate SANs to preserve HTTP/2 ALPN by default. + 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: |