Skip to content
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
102 changes: 102 additions & 0 deletions controllers/gateway/listenerrule_configuration_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package gateway

import (
"context"
"fmt"
"github.com/go-logr/logr"
"k8s.io/client-go/tools/record"
elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
"sigs.k8s.io/aws-load-balancer-controller/pkg/config"
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/constants"
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/routeutils"
"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"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)

// NewListenerRuleConfigurationReconciler constructs a reconciler that responds to listener rule configuration changes
func NewListenerRuleConfigurationReconciler(k8sClient client.Client, eventRecorder record.EventRecorder, controllerConfig config.ControllerConfig, finalizerManager k8s.FinalizerManager, logger logr.Logger) Reconciler {

return &listenerRuleConfigurationReconciler{
k8sClient: k8sClient,
eventRecorder: eventRecorder,
logger: logger,
finalizerManager: finalizerManager,
workers: controllerConfig.GatewayClassMaxConcurrentReconciles,
}
}

// listenerRuleConfigurationReconciler reconciles listener rule configurations
type listenerRuleConfigurationReconciler struct {
k8sClient client.Client
logger logr.Logger
eventRecorder record.EventRecorder
finalizerManager k8s.FinalizerManager
workers int
}

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

if err := ctrl.Watch(source.Kind(mgr.GetCache(), &elbv2gw.ListenerRuleConfiguration{}, &handler.TypedEnqueueRequestForObject[*elbv2gw.ListenerRuleConfiguration]{})); err != nil {
return err
}

return nil
}

func (r *listenerRuleConfigurationReconciler) Reconcile(ctx context.Context, req reconcile.Request) (ctrl.Result, error) {
return runtime.HandleReconcileError(r.reconcile(ctx, req), r.logger)
}

func (r *listenerRuleConfigurationReconciler) reconcile(ctx context.Context, req reconcile.Request) error {
listenerRuleConf := &elbv2gw.ListenerRuleConfiguration{}
if err := r.k8sClient.Get(ctx, req.NamespacedName, listenerRuleConf); err != nil {
return client.IgnoreNotFound(err)
}

r.logger.V(1).Info("Reconcile request for listener rule configuration", "cfg", listenerRuleConf)

if listenerRuleConf.DeletionTimestamp == nil || listenerRuleConf.DeletionTimestamp.IsZero() {
return r.handleUpdate(listenerRuleConf)
}

return r.handleDelete(listenerRuleConf)
}

func (r *listenerRuleConfigurationReconciler) handleUpdate(listenerRuleConf *elbv2gw.ListenerRuleConfiguration) error {
if k8s.HasFinalizer(listenerRuleConf, shared_constants.ListenerRuleConfigurationFinalizer) {
return nil
}
return r.finalizerManager.AddFinalizers(context.Background(), listenerRuleConf, shared_constants.ListenerRuleConfigurationFinalizer)
}

func (r *listenerRuleConfigurationReconciler) handleDelete(listenerRuleConf *elbv2gw.ListenerRuleConfiguration) error {
if !k8s.HasFinalizer(listenerRuleConf, shared_constants.ListenerRuleConfigurationFinalizer) {
return nil
}

inUse, err := routeutils.IsListenerRuleConfigInUse(context.Background(), listenerRuleConf, r.k8sClient)

if err != nil {
return fmt.Errorf("skipping finalizer removal due failure to verify if listener rule configuration [%+v] is in use. Error : %w ", k8s.NamespacedName(listenerRuleConf), err)
}
// if the listener rule configuration is still in use, we should not delete it
if inUse {
return fmt.Errorf("failed to remove finalizers as listener rule configuration [%+v] is still in use", k8s.NamespacedName(listenerRuleConf))
}
return r.finalizerManager.RemoveFinalizers(context.Background(), listenerRuleConf, shared_constants.ListenerRuleConfigurationFinalizer)
}

func (r *listenerRuleConfigurationReconciler) SetupWithManager(_ context.Context, mgr ctrl.Manager) (controller.Controller, error) {
return controller.New(constants.ListenerRuleConfigurationController, mgr, controller.Options{
MaxConcurrentReconciles: r.workers,
Reconciler: r,
})

}
22 changes: 21 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func main() {

targetGroupConfigurationReconciler := gateway.NewTargetGroupConfigurationReconciler(
mgr.GetClient(),
mgr.GetEventRecorderFor(gateway_constants.LoadBalancerConfigurationController),
mgr.GetEventRecorderFor(gateway_constants.TargetGroupConfigurationController),
controllerCFG,
serviceReferenceCounter,
finalizerManager,
Expand All @@ -338,6 +338,26 @@ func main() {
os.Exit(1)
}

listenerRuleConfigurationReconciler := gateway.NewListenerRuleConfigurationReconciler(
mgr.GetClient(),
mgr.GetEventRecorderFor(gateway_constants.ListenerRuleConfigurationController),
controllerCFG,
finalizerManager,
mgr.GetLogger().WithName("listenerruleconfiguration-controller"),
)

listenerRuleCfgController, err := listenerRuleConfigurationReconciler.SetupWithManager(ctx, mgr)
if err != nil {
setupLog.Error(err, "Unable to set up ListenerRuleConfiguration Manager")
os.Exit(1)
}

err = listenerRuleConfigurationReconciler.SetupWatches(ctx, listenerRuleCfgController, mgr)
if err != nil {
setupLog.Error(err, "Unable to set up ListenerRuleConfiguration Watches")
os.Exit(1)
}

go func() {
setupLog.Info("starting gateway route reconciler")
routeReconciler.Run()
Expand Down
3 changes: 3 additions & 0 deletions pkg/gateway/constants/controller_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,7 @@ const (

//TargetGroupConfigurationController the controller that reconciles TargetGroupConfiguration changes
TargetGroupConfigurationController = "aws-lbc-targetgroupconfiguration-controller"

//ListenerRuleConfigurationController the controller that reconciles ListenerRuleConfiguration changes
ListenerRuleConfigurationController = "aws-lbc-listenerruleconfiguration-controller"
)
45 changes: 45 additions & 0 deletions pkg/gateway/routeutils/listener_rule_config_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package routeutils

import (
"context"
"k8s.io/apimachinery/pkg/types"
elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func IsListenerRuleConfigInUse(ctx context.Context, listenerRuleConfig *elbv2gw.ListenerRuleConfiguration, k8sClient client.Client) (bool, error) {
l7Routes, err := ListL7Routes(ctx, k8sClient)
if err != nil {
return false, err
}
filteredRoutesByListenerRuleCfg := FilterRoutesByListenerRuleCfg(l7Routes, listenerRuleConfig)

return len(filteredRoutesByListenerRuleCfg) > 0, nil
}

// FilterRoutesByListenerRuleCfg filters a slice of routes based on ListenerRuleConfiguration reference.
// Returns a new slice containing only routes that reference the specified ListenerRuleConfiguration.
func FilterRoutesByListenerRuleCfg(routes []preLoadRouteDescriptor, ruleConfig *elbv2gw.ListenerRuleConfiguration) []preLoadRouteDescriptor {
if ruleConfig == nil || len(routes) == 0 {
return []preLoadRouteDescriptor{}
}
filteredRoutes := make([]preLoadRouteDescriptor, 0, len(routes))
for _, route := range routes {
if isListenerRuleConfigReferredByRoute(route, k8s.NamespacedName(ruleConfig)) {
filteredRoutes = append(filteredRoutes, route)
}
}
return filteredRoutes
}

// isListenerRuleConfigReferredByRoute checks if a route references a specific ruleConfig.
func isListenerRuleConfigReferredByRoute(route preLoadRouteDescriptor, ruleConfig types.NamespacedName) bool {
for _, config := range route.GetListenerRuleConfigs() {
namespace := route.GetRouteNamespacedName().Namespace
if string(config.Name) == ruleConfig.Name && namespace == ruleConfig.Namespace {
return true
}
}
return false
}
Loading
Loading