Skip to content

tg / lb conf finalizers #4230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions controllers/gateway/eventhandlers/gateway_class_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,23 @@ import (

// NewEnqueueRequestsForGatewayClassEvent creates handler for GatewayClass resources
func NewEnqueueRequestsForGatewayClassEvent(
k8sClient client.Client, eventRecorder record.EventRecorder, gwController string, finalizerManager k8s.FinalizerManager, logger logr.Logger) handler.TypedEventHandler[*gatewayv1.GatewayClass, reconcile.Request] {
k8sClient client.Client, eventRecorder record.EventRecorder, gwController string, logger logr.Logger) handler.TypedEventHandler[*gatewayv1.GatewayClass, reconcile.Request] {
return &enqueueRequestsForGatewayClassEvent{
k8sClient: k8sClient,
finalizerManager: finalizerManager,
eventRecorder: eventRecorder,
gwController: gwController,
logger: logger,
k8sClient: k8sClient,
eventRecorder: eventRecorder,
gwController: gwController,
logger: logger,
}
}

var _ handler.TypedEventHandler[*gatewayv1.GatewayClass, reconcile.Request] = (*enqueueRequestsForGatewayClassEvent)(nil)

// enqueueRequestsForGatewayClassEvent handles GatewayClass events
type enqueueRequestsForGatewayClassEvent struct {
k8sClient client.Client
eventRecorder record.EventRecorder
gwController string
finalizerManager k8s.FinalizerManager
logger logr.Logger
k8sClient client.Client
eventRecorder record.EventRecorder
gwController string
logger logr.Logger
}

func (h *enqueueRequestsForGatewayClassEvent) Create(ctx context.Context, e event.TypedCreateEvent[*gatewayv1.GatewayClass], queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
Expand All @@ -60,7 +58,12 @@ func (h *enqueueRequestsForGatewayClassEvent) Generic(ctx context.Context, e eve
}

func (h *enqueueRequestsForGatewayClassEvent) enqueueImpactedGateways(ctx context.Context, gwClass *gatewayv1.GatewayClass, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
gwList := gatewayutils.GetGatewaysManagedByGatewayClass(ctx, h.k8sClient, gwClass, h.gwController)
gwList, err := gatewayutils.GetGatewaysManagedByGatewayClass(ctx, h.k8sClient, gwClass)

if err != nil {
h.logger.Error(err, "failed to get gateways managed by gatewayclass", "gwClass", gwClass.Name)
return
}

for _, gw := range gwList {
h.logger.V(1).Info("enqueue gateway for gatewayclass event",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/gatewayutils"
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
"sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
Expand All @@ -19,13 +18,12 @@ import (

// NewEnqueueRequestsForLoadBalancerConfigurationEvent creates handler for LoadBalancerConfiguration resources
func NewEnqueueRequestsForLoadBalancerConfigurationEvent(gwClassEventChan chan<- event.TypedGenericEvent[*gatewayv1.GatewayClass],
k8sClient client.Client, eventRecorder record.EventRecorder, gwControllers sets.Set[string], finalizerManager k8s.FinalizerManager, logger logr.Logger) handler.TypedEventHandler[*elbv2gw.LoadBalancerConfiguration, reconcile.Request] {
k8sClient client.Client, eventRecorder record.EventRecorder, gwControllers sets.Set[string], logger logr.Logger) handler.TypedEventHandler[*elbv2gw.LoadBalancerConfiguration, reconcile.Request] {
return &enqueueRequestsForLoadBalancerConfigurationEvent{
gwClassEventChan: gwClassEventChan,
k8sClient: k8sClient,
eventRecorder: eventRecorder,
gwControllers: gwControllers,
finalizerManager: finalizerManager,
logger: logger,
}
}
Expand All @@ -38,7 +36,6 @@ type enqueueRequestsForLoadBalancerConfigurationEvent struct {
k8sClient client.Client
eventRecorder record.EventRecorder
gwControllers sets.Set[string]
finalizerManager k8s.FinalizerManager
logger logr.Logger
}

Expand All @@ -51,14 +48,6 @@ func (h *enqueueRequestsForLoadBalancerConfigurationEvent) Create(ctx context.Co
func (h *enqueueRequestsForLoadBalancerConfigurationEvent) Update(ctx context.Context, e event.TypedUpdateEvent[*elbv2gw.LoadBalancerConfiguration], queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
lbconfigNew := e.ObjectNew
h.logger.V(1).Info("enqueue loadbalancerconfiguration update event", "loadbalancerconfiguration", k8s.NamespacedName(lbconfigNew))
// to remove finalizers on this residual unused lb config so that the deletion can be done on these
if !lbconfigNew.DeletionTimestamp.IsZero() && k8s.HasFinalizer(lbconfigNew, shared_constants.LoadBalancerConfigurationFinalizer) && !gatewayutils.IsLBConfigInUse(ctx, lbconfigNew, nil, nil, h.k8sClient) {
if err := h.finalizerManager.RemoveFinalizers(ctx, lbconfigNew, shared_constants.LoadBalancerConfigurationFinalizer); err != nil {
h.logger.V(1).Info("failed to remove finalizers on load balancer configuration as its currently in use", "load balancer configuration", lbconfigNew.Name)
return
}
return
}
h.enqueueImpactedGatewayClass(ctx, lbconfigNew, queue)
}

Expand All @@ -75,7 +64,12 @@ func (h *enqueueRequestsForLoadBalancerConfigurationEvent) Generic(ctx context.C
}

func (h *enqueueRequestsForLoadBalancerConfigurationEvent) enqueueImpactedGatewayClass(ctx context.Context, lbconfig *elbv2gw.LoadBalancerConfiguration, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
gwClasses := gatewayutils.GetImpactedGatewayClassesFromLbConfig(ctx, h.k8sClient, lbconfig, h.gwControllers)
gwClasses, err := gatewayutils.GetImpactedGatewayClassesFromLbConfig(ctx, h.k8sClient, lbconfig, h.gwControllers)
if err != nil {
h.logger.Error(err, "failed to get impacted gatewayClasses from loadbalancerconfiguration event",
"loadbalancerconfiguration", k8s.NamespacedName(lbconfig))
return
}
for _, gwClass := range gwClasses {
h.logger.V(1).Info("enqueue gatewayClass for loadbalancerconfiguration event",
"loadbalancerconfiguration", k8s.NamespacedName(lbconfig),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ func (h *enqueueRequestsForLoadBalancerConfigurationEvent) Generic(ctx context.C
func (h *enqueueRequestsForLoadBalancerConfigurationEvent) enqueueImpactedService(ctx context.Context, lbconfig *elbv2gw.LoadBalancerConfiguration, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
// NOTE: That LB Config changes for GatewayClass are done a little differently.
// LB config change -> gateway class reconciler -> patch status for new version of LB config on Gateway Class -> Trigger the Gateway Class event handler.
gateways := gatewayutils.GetImpactedGatewaysFromLbConfig(ctx, h.k8sClient, lbconfig, h.gwController)
gateways, err := gatewayutils.GetImpactedGatewaysFromLbConfig(ctx, h.k8sClient, lbconfig, h.gwController)
if err != nil {
h.logger.Error(err, "failed to get impacted gateways from loadbalancerconfiguration", "loadbalancerconfiguration", k8s.NamespacedName(lbconfig))
return
}
for _, gw := range gateways {
h.logger.V(1).Info("enqueue gateway for loadbalancerconfiguration event",
"loadbalancerconfiguration", k8s.NamespacedName(lbconfig),
Expand Down
3 changes: 0 additions & 3 deletions controllers/gateway/eventhandlers/service_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ func (h *enqueueRequestsForServiceEvent) Update(ctx context.Context, e event.Typ
func (h *enqueueRequestsForServiceEvent) Delete(ctx context.Context, e event.TypedDeleteEvent[*corev1.Service], queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
svc := e.Object
h.logger.V(1).Info("enqueue service delete event", "service", svc.Name)
// remove target group configuration finalizer when service is deleted
routeutils.RemoveTargetGroupConfigurationFinalizer(ctx, svc, h.k8sClient, h.logger, h.eventRecorder)

h.enqueueImpactedRoutes(ctx, svc)
}

Expand Down
37 changes: 34 additions & 3 deletions controllers/gateway/gateway_class_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/gatewayutils"
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
"sigs.k8s.io/aws-load-balancer-controller/pkg/runtime"
"sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
Expand Down Expand Up @@ -42,6 +43,7 @@ func NewGatewayClassReconciler(k8sClient client.Client, eventRecorder record.Eve
updateGwClassAcceptedFn: updateGatewayClassAcceptedCondition,
updateLastProcessedConfigFn: updateGatewayClassLastProcessedConfig,
configResolverFn: gatewayutils.ResolveLoadBalancerConfig,
gatewayResolverFn: gatewayutils.GetGatewaysManagedByGatewayClass,
}
}

Expand All @@ -57,12 +59,13 @@ type gatewayClassReconciler struct {
updateGwClassAcceptedFn func(ctx context.Context, k8sClient client.Client, gwClass *gwv1.GatewayClass, status metav1.ConditionStatus, reason string, message string) error
updateLastProcessedConfigFn func(ctx context.Context, k8sClient client.Client, gwClass *gwv1.GatewayClass, lbConf *elbv2gw.LoadBalancerConfiguration) error
configResolverFn func(ctx context.Context, k8sClient client.Client, reference *gwv1.ParametersReference) (*elbv2gw.LoadBalancerConfiguration, error)
gatewayResolverFn func(ctx context.Context, k8sClient client.Client, gwClass *gwv1.GatewayClass) ([]*gwv1.Gateway, error)
}

func (r *gatewayClassReconciler) SetupWatches(_ context.Context, ctrl controller.Controller, mgr ctrl.Manager) error {

gwClassEventChan := make(chan event.TypedGenericEvent[*gwv1.GatewayClass])
lbEventHandler := gatewayclasseventhandlers.NewEnqueueRequestsForLoadBalancerConfigurationEvent(gwClassEventChan, r.k8sClient, r.eventRecorder, r.enabledControllers, r.finalizerManager, r.logger)
lbEventHandler := gatewayclasseventhandlers.NewEnqueueRequestsForLoadBalancerConfigurationEvent(gwClassEventChan, r.k8sClient, r.eventRecorder, r.enabledControllers, r.logger)

if err := ctrl.Watch(source.Kind(mgr.GetCache(), &gwv1.GatewayClass{}, &handler.TypedEnqueueRequestForObject[*gwv1.GatewayClass]{})); err != nil {
return err
Expand Down Expand Up @@ -100,6 +103,21 @@ func (r *gatewayClassReconciler) reconcile(ctx context.Context, req reconcile.Re
return nil
}

if gwClass.DeletionTimestamp == nil || gwClass.DeletionTimestamp.IsZero() {
return r.handleUpdate(ctx, gwClass)
}

return r.handleDelete(ctx, gwClass)
}

func (r *gatewayClassReconciler) handleUpdate(ctx context.Context, gwClass *gwv1.GatewayClass) error {
if !k8s.HasFinalizer(gwClass, shared_constants.GatewayClassFinalizer) {
err := r.finalizerManager.AddFinalizers(context.Background(), gwClass, shared_constants.GatewayClassFinalizer)
if err != nil {
return err
}
}

var lbConf *elbv2gw.LoadBalancerConfiguration

lbConf, err := r.configResolverFn(ctx, r.k8sClient, gwClass.Spec.ParametersRef)
Expand All @@ -113,19 +131,32 @@ func (r *gatewayClassReconciler) reconcile(ctx context.Context, req reconcile.Re

err = r.updateLastProcessedConfigFn(ctx, r.k8sClient, gwClass, lbConf)
if err != nil {
r.logger.Error(err, "Unable to update last processed annotation")
return err
}

err = r.updateGwClassAcceptedFn(ctx, r.k8sClient, gwClass, metav1.ConditionTrue, string(gwv1.GatewayClassReasonAccepted), string(gwv1.GatewayClassReasonAccepted))
if err != nil {
r.logger.Error(err, "Unable to update condition")
return err
}

return nil
}

func (r *gatewayClassReconciler) handleDelete(ctx context.Context, gwClass *gwv1.GatewayClass) error {
if !k8s.HasFinalizer(gwClass, shared_constants.GatewayClassFinalizer) {
return nil
}

refCount, err := r.gatewayResolverFn(ctx, r.k8sClient, gwClass)
if err != nil {
return err
}
if len(refCount) != 0 {
return fmt.Errorf("unable to delete GatewayClass [%+v], as it is still referenced by Gateways", gwClass.Name)
}
return r.finalizerManager.RemoveFinalizers(ctx, gwClass, shared_constants.GatewayClassFinalizer)
}

func (r *gatewayClassReconciler) getNotFoundMessage(paramRef *gwv1.ParametersReference) string {
var ns string
if paramRef.Namespace == nil {
Expand Down
Loading
Loading