diff --git a/internal/provider/kubernetes/status.go b/internal/provider/kubernetes/status.go index c3d5553b0bf..ad9980395bd 100644 --- a/internal/provider/kubernetes/status.go +++ b/internal/provider/kubernetes/status.go @@ -66,16 +66,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(gwapiv1.HTTPRoute), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { h, ok := obj.(*gwapiv1.HTTPRoute) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - hCopy := h.DeepCopy() - hCopy.Status.Parents = val.Parents - return hCopy + + if isStatusEqual(h.Status.Parents, val.Parents) { + return true + } + + h.Status.Parents = val.Parents + return false }), }) }, @@ -96,16 +100,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(gwapiv1.GRPCRoute), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { h, ok := obj.(*gwapiv1.GRPCRoute) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - hCopy := h.DeepCopy() - hCopy.Status.Parents = val.Parents - return hCopy + + if isStatusEqual(h.Status.Parents, val.Parents) { + return true + } + + h.Status.Parents = val.Parents + return false }), }) }, @@ -128,16 +136,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(gwapiv1a2.TLSRoute), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*gwapiv1a2.TLSRoute) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status.Parents = val.Parents - return tCopy + + if isStatusEqual(t.Status.Parents, val.Parents) { + return true + } + + t.Status.Parents = val.Parents + return false }), }) }, @@ -160,16 +172,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(gwapiv1a2.TCPRoute), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*gwapiv1a2.TCPRoute) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status.Parents = val.Parents - return tCopy + + if isStatusEqual(t.Status.Parents, val.Parents) { + return true + } + + t.Status.Parents = val.Parents + return false }), }) }, @@ -192,16 +208,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(gwapiv1a2.UDPRoute), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*gwapiv1a2.UDPRoute) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status.Parents = val.Parents - return tCopy + + if isStatusEqual(t.Status.Parents, val.Parents) { + return true + } + + t.Status.Parents = val.Parents + return false }), }) }, @@ -224,16 +244,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(egv1a1.EnvoyPatchPolicy), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*egv1a1.EnvoyPatchPolicy) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status = *val - return tCopy + + if isStatusEqual(t.Status, *val) { + return true + } + + t.Status = *val + return false }), }) }, @@ -256,16 +280,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(egv1a1.ClientTrafficPolicy), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*egv1a1.ClientTrafficPolicy) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status = *val - return tCopy + + if isStatusEqual(t.Status, *val) { + return true + } + + t.Status = *val + return false }), }) }, @@ -288,16 +316,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(egv1a1.BackendTrafficPolicy), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*egv1a1.BackendTrafficPolicy) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status = *val - return tCopy + + if isStatusEqual(t.Status, *val) { + return true + } + + t.Status = *val + return false }), }) }, @@ -320,16 +352,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(egv1a1.SecurityPolicy), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*egv1a1.SecurityPolicy) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status = *val - return tCopy + + if isStatusEqual(t.Status, *val) { + return true + } + + t.Status = *val + return false }), }) }, @@ -350,16 +386,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(gwapiv1a3.BackendTLSPolicy), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*gwapiv1a3.BackendTLSPolicy) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status = *val - return tCopy + + if isStatusEqual(t.Status, *val) { + return true + } + + t.Status = *val + return false }), }) }, @@ -382,16 +422,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(egv1a1.EnvoyExtensionPolicy), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*egv1a1.EnvoyExtensionPolicy) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status = *val - return tCopy + + if isStatusEqual(t.Status, *val) { + return true + } + + t.Status = *val + return false }), }) }, @@ -414,16 +458,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(egv1a1.Backend), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*egv1a1.Backend) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Status = *val - return tCopy + + if isStatusEqual(t.Status, *val) { + return true + } + + t.Status = *val + return false }), }) }, @@ -450,16 +498,20 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context, ext r.statusUpdater.Send(Update{ NamespacedName: key.NamespacedName, Resource: &obj, - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { t, ok := obj.(*unstructured.Unstructured) if !ok { err := fmt.Errorf("unsupported object type %T", obj) errChan <- err panic(err) } - tCopy := t.DeepCopy() - tCopy.Object["status"] = *val - return tCopy + + if isStatusEqual(t.Object["status"], *val) { + return true + } + + t.Object["status"] = *val + return false }), }) }, @@ -499,16 +551,22 @@ func (r *gatewayAPIReconciler) updateStatusForGateway(ctx context.Context, gtw * r.statusUpdater.Send(Update{ NamespacedName: key, Resource: new(gwapiv1.Gateway), - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { g, ok := obj.(*gwapiv1.Gateway) if !ok { panic(fmt.Sprintf("unsupported object type %T", obj)) } - gCopy := g.DeepCopy() - gCopy.Status.Conditions = gtw.Status.Conditions - gCopy.Status.Addresses = gtw.Status.Addresses - gCopy.Status.Listeners = gtw.Status.Listeners - return gCopy + + if isStatusEqual(g.Status.Conditions, gtw.Status.Conditions) && + isStatusEqual(g.Status.Addresses, gtw.Status.Addresses) && + isStatusEqual(g.Status.Listeners, gtw.Status.Listeners) { + return true + } + + g.Status.Conditions = gtw.Status.Conditions + g.Status.Addresses = gtw.Status.Addresses + g.Status.Listeners = gtw.Status.Listeners + return false }), }) } @@ -524,13 +582,19 @@ func (r *gatewayAPIReconciler) updateStatusForGatewayClass( r.statusUpdater.Send(Update{ NamespacedName: types.NamespacedName{Name: gc.Name}, Resource: &gwapiv1.GatewayClass{}, - Mutator: MutatorFunc(func(obj client.Object) client.Object { + Mutator: MutatorFunc(func(obj client.Object) bool { gc, ok := obj.(*gwapiv1.GatewayClass) if !ok { panic(fmt.Sprintf("unsupported object type %T", obj)) } - return status.SetGatewayClassAccepted(gc.DeepCopy(), accepted, reason, msg) + gcp := status.SetGatewayClassAccepted(gc.DeepCopy(), accepted, reason, msg) + if isStatusEqual(gcp.Status, gc.Status) { + return true + } + + gc.Status = gcp.Status + return false }), }) } else { diff --git a/internal/provider/kubernetes/status_updater.go b/internal/provider/kubernetes/status_updater.go index 24adaedd563..cda700ce45e 100644 --- a/internal/provider/kubernetes/status_updater.go +++ b/internal/provider/kubernetes/status_updater.go @@ -37,16 +37,16 @@ type Update struct { // Mutator is an interface to hold mutator functions for status updates. type Mutator interface { - Mutate(obj client.Object) client.Object + Mutate(obj client.Object) bool } // MutatorFunc is a function adaptor for Mutators. -type MutatorFunc func(client.Object) client.Object +type MutatorFunc func(client.Object) bool // Mutate adapts the MutatorFunc to fit through the Mutator interface. -func (m MutatorFunc) Mutate(old client.Object) client.Object { +func (m MutatorFunc) Mutate(old client.Object) bool { if m == nil { - return nil + return false } return m(old) @@ -94,20 +94,15 @@ func (u *UpdateHandler) apply(update Update) { return err } - newObj := update.Mutator.Mutate(obj) - - if isStatusEqual(obj, newObj) { + if update.Mutator.Mutate(obj) { u.log.WithName(update.NamespacedName.Name). WithName(update.NamespacedName.Namespace). Info("status unchanged, bypassing update") - statusUpdateTotal.WithStatus(statusNoAction, kindLabel.Value(objKind)).Increment() return nil } - newObj.SetUID(obj.GetUID()) - - return u.client.Status().Update(context.Background(), newObj) + return u.client.Status().Update(context.Background(), obj) }); err != nil { u.log.Error(err, "unable to update status", "name", update.NamespacedName.Name, "namespace", update.NamespacedName.Namespace) @@ -188,100 +183,8 @@ func isStatusEqual(objA, objB interface{}) bool { return k == "lastTransitionTime" }), } - switch a := objA.(type) { - case *gwapiv1.GatewayClass: - if b, ok := objB.(*gwapiv1.GatewayClass); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *gwapiv1.Gateway: - if b, ok := objB.(*gwapiv1.Gateway); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *gwapiv1.HTTPRoute: - if b, ok := objB.(*gwapiv1.HTTPRoute); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *gwapiv1a2.TLSRoute: - if b, ok := objB.(*gwapiv1a2.TLSRoute); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *gwapiv1a2.TCPRoute: - if b, ok := objB.(*gwapiv1a2.TCPRoute); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *gwapiv1a2.UDPRoute: - if b, ok := objB.(*gwapiv1a2.UDPRoute); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *gwapiv1.GRPCRoute: - if b, ok := objB.(*gwapiv1.GRPCRoute); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *egv1a1.EnvoyPatchPolicy: - if b, ok := objB.(*egv1a1.EnvoyPatchPolicy); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *egv1a1.ClientTrafficPolicy: - if b, ok := objB.(*egv1a1.ClientTrafficPolicy); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *egv1a1.BackendTrafficPolicy: - if b, ok := objB.(*egv1a1.BackendTrafficPolicy); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *egv1a1.SecurityPolicy: - if b, ok := objB.(*egv1a1.SecurityPolicy); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *gwapiv1a3.BackendTLSPolicy: - if b, ok := objB.(*gwapiv1a3.BackendTLSPolicy); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *egv1a1.EnvoyExtensionPolicy: - if b, ok := objB.(*egv1a1.EnvoyExtensionPolicy); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - case *unstructured.Unstructured: - if b, ok := objB.(*unstructured.Unstructured); ok { - if cmp.Equal(a.Object["status"], b.Object["status"], opts) { - return true - } - } - case *egv1a1.Backend: - if b, ok := objB.(*egv1a1.Backend); ok { - if cmp.Equal(a.Status, b.Status, opts) { - return true - } - } - } - return false + return cmp.Equal(objA, objB, opts) } // kindOf returns the known kind string for the given Kubernetes object,