diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 39d8a378a3e5..7c6a78d6497b 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -269,15 +269,25 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. if len(resourceTree.Gateways) == 0 { r.log.Info("No gateways found for accepted gatewayclass") + if err := r.removeEpFinalizer(ctx, resourceTree.EnvoyProxy); err != nil { + r.log.Error(err, fmt.Sprintf("failed to remove finalizer from envoy proxy %s", + resourceTree.EnvoyProxy.Name)) + return reconcile.Result{}, err + } // If needed, remove the finalizer from the accepted GatewayClass. - if err := r.removeFinalizer(ctx, acceptedGC); err != nil { + if err := r.removeGcFinalizer(ctx, acceptedGC); err != nil { r.log.Error(err, fmt.Sprintf("failed to remove finalizer from gatewayclass %s", acceptedGC.Name)) return reconcile.Result{}, err } } else { + if err := r.addEpFinalizer(ctx, resourceTree.EnvoyProxy); err != nil { + r.log.Error(err, fmt.Sprintf("failed adding finalizer to envoy proxy %s", + resourceTree.EnvoyProxy.Name)) + return reconcile.Result{}, err + } // finalize the accepted GatewayClass. - if err := r.addFinalizer(ctx, acceptedGC); err != nil { + if err := r.addGcFinalizer(ctx, acceptedGC); err != nil { r.log.Error(err, fmt.Sprintf("failed adding finalizer to gatewayclass %s", acceptedGC.Name)) return reconcile.Result{}, err @@ -867,8 +877,8 @@ func secretGatewayIndexFunc(rawObj client.Object) []string { return secretReferences } -// removeFinalizer removes the gatewayclass finalizer from the provided gc, if it exists. -func (r *gatewayAPIReconciler) removeFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error { +// removeGcFinalizer removes the gatewayclass finalizer from the provided gc, if it exists. +func (r *gatewayAPIReconciler) removeGcFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error { if slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) { base := client.MergeFrom(gc.DeepCopy()) gc.Finalizers = slice.RemoveString(gc.Finalizers, gatewayClassFinalizer) @@ -879,8 +889,8 @@ func (r *gatewayAPIReconciler) removeFinalizer(ctx context.Context, gc *gwapiv1b return nil } -// addFinalizer adds the gatewayclass finalizer to the provided gc, if it doesn't exist. -func (r *gatewayAPIReconciler) addFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error { +// addGcFinalizer adds the gatewayclass finalizer to the provided gc, if it doesn't exist. +func (r *gatewayAPIReconciler) addGcFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error { if !slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) { base := client.MergeFrom(gc.DeepCopy()) gc.Finalizers = append(gc.Finalizers, gatewayClassFinalizer) @@ -891,6 +901,30 @@ func (r *gatewayAPIReconciler) addFinalizer(ctx context.Context, gc *gwapiv1b1.G return nil } +// removeEpFinalizer removes the Envoy Proxy finalizer from the provided ep, if it exists. +func (r *gatewayAPIReconciler) removeEpFinalizer(ctx context.Context, ep *egcfgv1a1.EnvoyProxy) error { + if slice.ContainsString(ep.Finalizers, gatewayClassFinalizer) { + base := client.MergeFrom(ep.DeepCopy()) + ep.Finalizers = slice.RemoveString(ep.Finalizers, gatewayClassFinalizer) + if err := r.client.Patch(ctx, ep, base); err != nil { + return fmt.Errorf("failed to remove finalizer from Envoy Proxy %s: %w", ep.Name, err) + } + } + return nil +} + +// addEpFinalizer adds the Envoy Proxy finalizer to the provided ep, if it doesn't exist. +func (r *gatewayAPIReconciler) addEpFinalizer(ctx context.Context, ep *egcfgv1a1.EnvoyProxy) error { + if !slice.ContainsString(ep.Finalizers, gatewayClassFinalizer) { + base := client.MergeFrom(ep.DeepCopy()) + ep.Finalizers = append(ep.Finalizers, gatewayClassFinalizer) + if err := r.client.Patch(ctx, ep, base); err != nil { + return fmt.Errorf("failed to add finalizer to Envoy Proxy %s: %w", ep.Name, err) + } + } + return nil +} + // subscribeAndUpdateStatus subscribes to gateway API object status updates and // writes it into the Kubernetes API Server. func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { diff --git a/internal/provider/kubernetes/controller_test.go b/internal/provider/kubernetes/controller_test.go index e1ffacd67f65..708dc38a3326 100644 --- a/internal/provider/kubernetes/controller_test.go +++ b/internal/provider/kubernetes/controller_test.go @@ -79,7 +79,7 @@ func TestAddGatewayClassFinalizer(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(tc.gc).Build() - err := r.addFinalizer(ctx, tc.gc) + err := r.addGcFinalizer(ctx, tc.gc) require.NoError(t, err) key := types.NamespacedName{Name: tc.gc.Name} err = r.client.Get(ctx, key, tc.gc) @@ -89,6 +89,75 @@ func TestAddGatewayClassFinalizer(t *testing.T) { } } +func TestAddEnvoyProxyFinalizer(t *testing.T) { + testCases := []struct { + name string + ep *egcfgv1a1.EnvoyProxy + expect []string + }{ + { + name: "gatewayclass with no finalizers", + ep: &egcfgv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + expect: []string{gatewayClassFinalizer}, + }, + } + // Create the reconciler. + r := new(gatewayAPIReconciler) + ctx := context.Background() + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(tc.ep).Build() + err := r.addEpFinalizer(ctx, tc.ep) + require.NoError(t, err) + key := types.NamespacedName{Name: tc.ep.Name} + err = r.client.Get(ctx, key, tc.ep) + require.NoError(t, err) + require.Equal(t, tc.expect, tc.ep.Finalizers) + }) + } +} + +func TestRemoveEnvoyProxyFinalizer(t *testing.T) { + testCases := []struct { + name string + ep *egcfgv1a1.EnvoyProxy + expect []string + }{ + { + name: "envoyproxy with existing envoyproxy finalizer", + ep: &egcfgv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Finalizers: []string{gatewayClassFinalizer}, + }, + }, + expect: nil, + }, + } + + // Create the reconciler. + r := new(gatewayAPIReconciler) + ctx := context.Background() + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(tc.ep).Build() + err := r.removeEpFinalizer(ctx, tc.ep) + require.NoError(t, err) + key := types.NamespacedName{Name: tc.ep.Name} + err = r.client.Get(ctx, key, tc.ep) + require.NoError(t, err) + require.Equal(t, tc.expect, tc.ep.Finalizers) + }) + } +} func TestRemoveGatewayClassFinalizer(t *testing.T) { testCases := []struct { name string @@ -143,7 +212,7 @@ func TestRemoveGatewayClassFinalizer(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(tc.gc).Build() - err := r.removeFinalizer(ctx, tc.gc) + err := r.removeGcFinalizer(ctx, tc.gc) require.NoError(t, err) key := types.NamespacedName{Name: tc.gc.Name} err = r.client.Get(ctx, key, tc.gc)