github.com/cilium/cilium@v1.16.2/operator/pkg/gateway-api/httproute_gamma.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package gateway_api
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/sirupsen/logrus"
    10  	corev1 "k8s.io/api/core/v1"
    11  	"k8s.io/apimachinery/pkg/fields"
    12  	"k8s.io/apimachinery/pkg/runtime"
    13  	"k8s.io/apimachinery/pkg/types"
    14  	ctrl "sigs.k8s.io/controller-runtime"
    15  	"sigs.k8s.io/controller-runtime/pkg/builder"
    16  	"sigs.k8s.io/controller-runtime/pkg/client"
    17  	"sigs.k8s.io/controller-runtime/pkg/handler"
    18  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    19  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    20  	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
    21  	gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
    22  
    23  	"github.com/cilium/cilium/operator/pkg/gateway-api/helpers"
    24  	"github.com/cilium/cilium/operator/pkg/model/translation"
    25  	"github.com/cilium/cilium/pkg/logging/logfields"
    26  )
    27  
    28  // gammaHttpRouteReconciler reconciles a HTTPRoute object
    29  type gammaHttpRouteReconciler struct {
    30  	client.Client
    31  	Scheme     *runtime.Scheme
    32  	translator translation.Translator
    33  }
    34  
    35  func newGammaHttpRouteReconciler(mgr ctrl.Manager, translator translation.Translator) *gammaHttpRouteReconciler {
    36  	return &gammaHttpRouteReconciler{
    37  		Client:     mgr.GetClient(),
    38  		Scheme:     mgr.GetScheme(),
    39  		translator: translator,
    40  	}
    41  }
    42  
    43  // SetupWithManager sets up the controller with the Manager.
    44  func (r *gammaHttpRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
    45  	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &gatewayv1.HTTPRoute{}, gammaBackendServiceIndex,
    46  		func(rawObj client.Object) []string {
    47  			route, ok := rawObj.(*gatewayv1.HTTPRoute)
    48  			if !ok {
    49  				return nil
    50  			}
    51  
    52  			if !r.hasGammaParent()(route) {
    53  				return nil
    54  			}
    55  			var backendServices []string
    56  			for _, rule := range route.Spec.Rules {
    57  				for _, backend := range rule.BackendRefs {
    58  					namespace := helpers.NamespaceDerefOr(backend.Namespace, route.Namespace)
    59  					backendServiceName, err := helpers.GetBackendServiceName(r.Client, namespace, backend.BackendObjectReference)
    60  					if err != nil {
    61  						log.WithFields(logrus.Fields{
    62  							logfields.Controller: "gammaHttpRoute",
    63  							logfields.Resource:   client.ObjectKeyFromObject(rawObj),
    64  						}).WithError(err).Error("Failed to get backend service name")
    65  						continue
    66  					}
    67  					backendServices = append(backendServices,
    68  						types.NamespacedName{
    69  							Namespace: helpers.NamespaceDerefOr(backend.Namespace, route.Namespace),
    70  							Name:      backendServiceName,
    71  						}.String(),
    72  					)
    73  				}
    74  			}
    75  			return backendServices
    76  		},
    77  	); err != nil {
    78  		return err
    79  	}
    80  
    81  	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &gatewayv1.HTTPRoute{}, gammaListenerServiceIndex,
    82  		func(rawObj client.Object) []string {
    83  			hr, ok := rawObj.(*gatewayv1.HTTPRoute)
    84  			if !ok {
    85  				return nil
    86  			}
    87  
    88  			if !r.hasGammaParent()(hr) {
    89  				return nil
    90  			}
    91  
    92  			var services []string
    93  			for _, parent := range hr.Spec.ParentRefs {
    94  				if !helpers.IsGammaService(parent) {
    95  					continue
    96  				}
    97  				services = append(services,
    98  					types.NamespacedName{
    99  						Namespace: helpers.NamespaceDerefOr(parent.Namespace, hr.Namespace),
   100  						Name:      string(parent.Name),
   101  					}.String(),
   102  				)
   103  			}
   104  			return services
   105  		},
   106  	); err != nil {
   107  		return err
   108  	}
   109  
   110  	builder := ctrl.NewControllerManagedBy(mgr).
   111  		// Watch for changes to HTTPRoute
   112  		For(&gatewayv1.HTTPRoute{},
   113  			builder.WithPredicates(predicate.NewPredicateFuncs(r.hasGammaParent()))).
   114  		// Watch for changes to Backend services
   115  		Watches(&corev1.Service{}, r.enqueueRequestForBackendService()).
   116  		// Watch for changes to GAMMA Listening services
   117  		Watches(&corev1.Service{}, r.enqueueRequestForGammaService()).
   118  		// Watch for changes to Reference Grants
   119  		Watches(&gatewayv1beta1.ReferenceGrant{}, r.enqueueRequestForReferenceGrant())
   120  
   121  	return builder.Complete(r)
   122  }
   123  
   124  func (r *gammaHttpRouteReconciler) hasGammaParent() func(object client.Object) bool {
   125  	return func(obj client.Object) bool {
   126  		hr, ok := obj.(*gatewayv1.HTTPRoute)
   127  		if !ok {
   128  			return false
   129  		}
   130  
   131  		for _, parent := range hr.Spec.ParentRefs {
   132  			if helpers.IsGammaService(parent) {
   133  				return true
   134  			}
   135  		}
   136  
   137  		return false
   138  	}
   139  }
   140  
   141  // enqueueRequestForBackendService makes sure that HTTP Routes are reconciled
   142  // if the backend services are updated.
   143  func (r *gammaHttpRouteReconciler) enqueueRequestForBackendService() handler.EventHandler {
   144  	return handler.EnqueueRequestsFromMapFunc(r.enqueueFromIndex(gammaBackendServiceIndex))
   145  }
   146  
   147  // enqueueRequestForReferenceGrant makes sure that all HTTP Routes are reconciled
   148  // if a ReferenceGrant changes
   149  func (r *gammaHttpRouteReconciler) enqueueRequestForReferenceGrant() handler.EventHandler {
   150  	return handler.EnqueueRequestsFromMapFunc(r.enqueueAll())
   151  }
   152  
   153  func (r *gammaHttpRouteReconciler) enqueueRequestForGammaService() handler.EventHandler {
   154  	return handler.EnqueueRequestsFromMapFunc(r.enqueueFromIndex(gammaListenerServiceIndex))
   155  }
   156  
   157  func (r *gammaHttpRouteReconciler) enqueueFromIndex(index string) handler.MapFunc {
   158  	return func(ctx context.Context, o client.Object) []reconcile.Request {
   159  		scopedLog := log.WithFields(logrus.Fields{
   160  			logfields.Controller: "httpRoute",
   161  			logfields.Resource:   client.ObjectKeyFromObject(o),
   162  		})
   163  		hrList := &gatewayv1.HTTPRouteList{}
   164  
   165  		if err := r.Client.List(ctx, hrList, &client.ListOptions{
   166  			FieldSelector: fields.OneTermEqualSelector(index, client.ObjectKeyFromObject(o).String()),
   167  		}); err != nil {
   168  			scopedLog.WithError(err).Error("Failed to get related HTTPRoutes")
   169  			return []reconcile.Request{}
   170  		}
   171  
   172  		requests := make([]reconcile.Request, 0, len(hrList.Items))
   173  		for _, item := range hrList.Items {
   174  			route := client.ObjectKey{
   175  				Namespace: item.GetNamespace(),
   176  				Name:      item.GetName(),
   177  			}
   178  			requests = append(requests, reconcile.Request{
   179  				NamespacedName: route,
   180  			})
   181  			scopedLog.WithField("httpRoute", route).Info("Enqueued HTTPRoute for resource")
   182  		}
   183  		return requests
   184  	}
   185  }
   186  
   187  func (r *gammaHttpRouteReconciler) enqueueAll() handler.MapFunc {
   188  	return func(ctx context.Context, o client.Object) []reconcile.Request {
   189  		scopedLog := log.WithFields(logrus.Fields{
   190  			logfields.Controller: "httpRoute",
   191  			logfields.Resource:   client.ObjectKeyFromObject(o),
   192  		})
   193  		hrList := &gatewayv1.HTTPRouteList{}
   194  
   195  		if err := r.Client.List(ctx, hrList, &client.ListOptions{}); err != nil {
   196  			scopedLog.WithError(err).Error("Failed to get HTTPRoutes")
   197  			return []reconcile.Request{}
   198  		}
   199  
   200  		requests := make([]reconcile.Request, 0, len(hrList.Items))
   201  		for _, item := range hrList.Items {
   202  			route := client.ObjectKey{
   203  				Namespace: item.GetNamespace(),
   204  				Name:      item.GetName(),
   205  			}
   206  			requests = append(requests, reconcile.Request{
   207  				NamespacedName: route,
   208  			})
   209  			scopedLog.WithField("httpRoute", route).Info("Enqueued HTTPRoute for resource")
   210  		}
   211  		return requests
   212  	}
   213  }