Skip to content

Commit c816ef1

Browse files
committed
refactor: update GatewayConfig controller to remove finalizer management
This commit refactors the GatewayConfig controller to eliminate finalizer management, simplifying the lifecycle handling of GatewayConfig resources. The controller now focuses on notifying referencing Gateways of changes without blocking deletion based on references. Additionally, the documentation has been updated to reflect these changes, including the removal of finalizer behavior descriptions from the API and capability documentation. Signed-off-by: Shabab Qaisar <[email protected]>
1 parent 8984f5b commit c816ef1

File tree

10 files changed

+73
-246
lines changed

10 files changed

+73
-246
lines changed

api/v1alpha1/gateway_config.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,6 @@ import (
3030
// If the same environment variable name exists in both sources, the GatewayConfig
3131
// value takes precedence.
3232
//
33-
// Finalizer Behavior:
34-
// A finalizer is automatically added to the GatewayConfig when it is first referenced
35-
// by a Gateway. The finalizer is removed when no Gateways reference it, allowing
36-
// the GatewayConfig to be deleted. This prevents accidental deletion of configurations
37-
// that are still in use.
38-
//
3933
// +genclient
4034
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
4135
// +kubebuilder:object:root=true
@@ -78,8 +72,6 @@ type GatewayConfigExtProc struct {
7872
//
7973
// Common use cases include:
8074
// - OTEL tracing configuration (e.g., OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_ENDPOINT)
81-
// - Log level overrides
82-
// - Feature flags
8375
//
8476
// +optional
8577
// +kubebuilder:validation:MaxItems=32

internal/controller/ai_gateway_route.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ const (
4646
// GatewayConfigAnnotationKey is the annotation key used on Gateway objects to reference a GatewayConfig.
4747
// The value should be the name of the GatewayConfig resource in the same namespace as the Gateway.
4848
GatewayConfigAnnotationKey = "aigateway.envoyproxy.io/gateway-config"
49-
// GatewayConfigFinalizerName is the finalizer added to GatewayConfig resources to prevent deletion
50-
// while they are still referenced by Gateway objects.
51-
GatewayConfigFinalizerName = "aigateway.envoyproxy.io/gateway-config-protection"
5249
)
5350

5451
// AIGatewayRouteController implements [reconcile.TypedReconciler].

internal/controller/controller.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ func StartControllers(ctx context.Context, mgr manager.Manager, config *rest.Con
214214
// GatewayConfig controller for gateway-scoped configuration.
215215
gatewayConfigC := NewGatewayConfigController(c, logger.WithName("gateway-config"), gatewayEventChan)
216216
if err = TypedControllerBuilderForCRD(mgr, &aigv1a1.GatewayConfig{}).
217-
Watches(&gwapiv1.Gateway{}, handler.EnqueueRequestsFromMapFunc(gatewayConfigC.MapGatewayToGatewayConfig)).
218217
Complete(gatewayConfigC); err != nil {
219218
return fmt.Errorf("failed to create controller for GatewayConfig: %w", err)
220219
}
@@ -282,6 +281,8 @@ const (
282281
// k8sClientIndexAIServiceBackendToTargetingBackendSecurityPolicy is the index name that maps from an AIServiceBackend
283282
// to the BackendSecurityPolicy whose targetRefs contains the AIServiceBackend.
284283
k8sClientIndexAIServiceBackendToTargetingBackendSecurityPolicy = "AIServiceBackendToTargetingBackendSecurityPolicy"
284+
// k8sClientIndexGatewayToGatewayConfig maps from a GatewayConfig name to Gateways referencing it.
285+
k8sClientIndexGatewayToGatewayConfig = "GatewayToGatewayConfig"
285286

286287
// Indexes for MCP Gateway
287288
//
@@ -313,6 +314,12 @@ func ApplyIndexing(ctx context.Context, indexer func(ctx context.Context, obj cl
313314
return fmt.Errorf("failed to index field for BackendSecurityPolicy targetRefs: %w", err)
314315
}
315316

317+
err = indexer(ctx, &gwapiv1.Gateway{},
318+
k8sClientIndexGatewayToGatewayConfig, gatewayToGatewayConfigIndexFunc)
319+
if err != nil {
320+
return fmt.Errorf("failed to create index from GatewayConfig to Gateway: %w", err)
321+
}
322+
316323
// Apply indexes to MCP Gateways.
317324
err = indexer(ctx, &aigv1a1.MCPRoute{},
318325
k8sClientIndexMCPRouteToAttachedGateway, mcpRouteToAttachedGatewayIndexFunc)
@@ -336,6 +343,18 @@ func mcpRouteToAttachedGatewayIndexFunc(o client.Object) []string {
336343
return ret
337344
}
338345

346+
func gatewayToGatewayConfigIndexFunc(o client.Object) []string {
347+
gateway := o.(*gwapiv1.Gateway)
348+
if gateway.Annotations == nil {
349+
return nil
350+
}
351+
configName, ok := gateway.Annotations[GatewayConfigAnnotationKey]
352+
if !ok || configName == "" {
353+
return nil
354+
}
355+
return []string{configName}
356+
}
357+
339358
func aiGatewayRouteToAttachedGatewayIndexFunc(o client.Object) []string {
340359
aiGatewayRoute := o.(*aigv1a1.AIGatewayRoute)
341360
var ret []string

internal/controller/gateway_config.go

Lines changed: 17 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"k8s.io/client-go/util/retry"
1616
ctrl "sigs.k8s.io/controller-runtime"
1717
"sigs.k8s.io/controller-runtime/pkg/client"
18-
ctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1918
"sigs.k8s.io/controller-runtime/pkg/event"
2019
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2120
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -25,7 +24,7 @@ import (
2524

2625
// GatewayConfigController implements [reconcile.TypedReconciler] for [aigv1a1.GatewayConfig].
2726
//
28-
// This handles the GatewayConfig resource and manages finalizers based on Gateway references.
27+
// This handles the GatewayConfig resource and notifies referencing Gateways of changes.
2928
//
3029
// Exported for testing purposes.
3130
type GatewayConfigController struct {
@@ -78,116 +77,44 @@ func (c *GatewayConfigController) syncGatewayConfig(ctx context.Context, gateway
7877
return fmt.Errorf("failed to find referencing Gateways: %w", err)
7978
}
8079

81-
// Handle finalizer based on whether any Gateways reference this GatewayConfig.
8280
if gatewayConfig.DeletionTimestamp != nil {
83-
// GatewayConfig is being deleted.
84-
if len(referencingGateways) > 0 {
85-
// Cannot delete yet - Gateways still reference this GatewayConfig.
86-
c.logger.Info("GatewayConfig is being deleted but still has referencing Gateways",
87-
"namespace", gatewayConfig.Namespace, "name", gatewayConfig.Name,
88-
"referencingGateways", len(referencingGateways))
89-
return fmt.Errorf("cannot delete GatewayConfig: still referenced by %d Gateway(s)", len(referencingGateways))
90-
}
91-
// No more references, remove finalizer.
92-
if ctrlutil.ContainsFinalizer(gatewayConfig, GatewayConfigFinalizerName) {
93-
ctrlutil.RemoveFinalizer(gatewayConfig, GatewayConfigFinalizerName)
94-
if err := c.client.Update(ctx, gatewayConfig); err != nil {
95-
return fmt.Errorf("failed to remove finalizer: %w", err)
96-
}
97-
c.logger.Info("Removed finalizer from GatewayConfig",
98-
"namespace", gatewayConfig.Namespace, "name", gatewayConfig.Name)
99-
}
81+
c.notifyReferencingGateways(gatewayConfig, referencingGateways)
10082
return nil
10183
}
10284

103-
// GatewayConfig is not being deleted.
104-
// Add finalizer if there are referencing Gateways and finalizer is not present.
105-
if len(referencingGateways) > 0 && !ctrlutil.ContainsFinalizer(gatewayConfig, GatewayConfigFinalizerName) {
106-
ctrlutil.AddFinalizer(gatewayConfig, GatewayConfigFinalizerName)
107-
if err := c.client.Update(ctx, gatewayConfig); err != nil {
108-
return fmt.Errorf("failed to add finalizer: %w", err)
109-
}
110-
c.logger.Info("Added finalizer to GatewayConfig",
111-
"namespace", gatewayConfig.Namespace, "name", gatewayConfig.Name)
112-
}
113-
114-
// Remove finalizer if no Gateways reference this GatewayConfig.
115-
if len(referencingGateways) == 0 && ctrlutil.ContainsFinalizer(gatewayConfig, GatewayConfigFinalizerName) {
116-
ctrlutil.RemoveFinalizer(gatewayConfig, GatewayConfigFinalizerName)
117-
if err := c.client.Update(ctx, gatewayConfig); err != nil {
118-
return fmt.Errorf("failed to remove finalizer: %w", err)
119-
}
120-
c.logger.Info("Removed finalizer from GatewayConfig (no more references)",
121-
"namespace", gatewayConfig.Namespace, "name", gatewayConfig.Name)
122-
}
123-
12485
// Notify all referencing Gateways to reconcile.
125-
for _, gw := range referencingGateways {
126-
c.logger.Info("Notifying Gateway of GatewayConfig change",
127-
"gateway_namespace", gw.Namespace, "gateway_name", gw.Name,
128-
"gatewayconfig_name", gatewayConfig.Name)
129-
c.gatewayEventChan <- event.GenericEvent{Object: gw}
130-
}
86+
c.notifyReferencingGateways(gatewayConfig, referencingGateways)
13187

13288
return nil
13389
}
13490

13591
// findReferencingGateways finds all Gateways in the same namespace that reference this GatewayConfig.
13692
func (c *GatewayConfigController) findReferencingGateways(ctx context.Context, gatewayConfig *aigv1a1.GatewayConfig) ([]*gwapiv1.Gateway, error) {
13793
var gateways gwapiv1.GatewayList
138-
if err := c.client.List(ctx, &gateways, client.InNamespace(gatewayConfig.Namespace)); err != nil {
94+
if err := c.client.List(
95+
ctx,
96+
&gateways,
97+
client.InNamespace(gatewayConfig.Namespace),
98+
client.MatchingFields{k8sClientIndexGatewayToGatewayConfig: gatewayConfig.Name},
99+
); err != nil {
139100
return nil, fmt.Errorf("failed to list Gateways: %w", err)
140101
}
141102

142-
var referencingGateways []*gwapiv1.Gateway
103+
referencingGateways := make([]*gwapiv1.Gateway, 0, len(gateways.Items))
143104
for i := range gateways.Items {
144105
gw := &gateways.Items[i]
145-
if gw.Annotations == nil {
146-
continue
147-
}
148-
configName, ok := gw.Annotations[GatewayConfigAnnotationKey]
149-
if !ok {
150-
continue
151-
}
152-
if configName == gatewayConfig.Name {
153-
referencingGateways = append(referencingGateways, gw)
154-
}
106+
referencingGateways = append(referencingGateways, gw)
155107
}
156108

157109
return referencingGateways, nil
158110
}
159111

160-
// MapGatewayToGatewayConfig is a handler function that maps Gateway events to GatewayConfig reconcile requests.
161-
// This is used by the controller builder to watch Gateway resources.
162-
func (c *GatewayConfigController) MapGatewayToGatewayConfig(_ context.Context, obj client.Object) []reconcile.Request {
163-
gateway, ok := obj.(*gwapiv1.Gateway)
164-
if !ok {
165-
return nil
166-
}
167-
168-
// Check if this Gateway has a GatewayConfig annotation.
169-
if gateway.Annotations == nil {
170-
return nil
171-
}
172-
173-
configName, ok := gateway.Annotations[GatewayConfigAnnotationKey]
174-
if !ok || configName == "" {
175-
return nil
176-
}
177-
178-
// Return a reconcile request for the referenced GatewayConfig.
179-
// GatewayConfig must be in the same namespace as the Gateway.
180-
c.logger.Info("Gateway references GatewayConfig, triggering reconcile",
181-
"gateway_namespace", gateway.Namespace, "gateway_name", gateway.Name,
182-
"gatewayconfig_name", configName)
183-
184-
return []reconcile.Request{
185-
{
186-
NamespacedName: client.ObjectKey{
187-
Name: configName,
188-
Namespace: gateway.Namespace,
189-
},
190-
},
112+
func (c *GatewayConfigController) notifyReferencingGateways(gatewayConfig *aigv1a1.GatewayConfig, referencingGateways []*gwapiv1.Gateway) {
113+
for _, gw := range referencingGateways {
114+
c.logger.Info("Notifying Gateway of GatewayConfig change",
115+
"gateway_namespace", gw.Namespace, "gateway_name", gw.Name,
116+
"gatewayconfig_name", gatewayConfig.Name)
117+
c.gatewayEventChan <- event.GenericEvent{Object: gw}
191118
}
192119
}
193120

0 commit comments

Comments
 (0)