github.com/cilium/cilium@v1.16.2/operator/pkg/gateway-api/tlsroute.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  	gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
    22  	gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
    23  	mcsapiv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
    24  
    25  	"github.com/cilium/cilium/operator/pkg/gateway-api/helpers"
    26  	"github.com/cilium/cilium/pkg/logging/logfields"
    27  )
    28  
    29  // tlsRouteReconciler reconciles a TLSRoute object
    30  type tlsRouteReconciler struct {
    31  	client.Client
    32  	Scheme *runtime.Scheme
    33  }
    34  
    35  func newTLSRouteReconciler(mgr ctrl.Manager) *tlsRouteReconciler {
    36  	return &tlsRouteReconciler{
    37  		Client: mgr.GetClient(),
    38  		Scheme: mgr.GetScheme(),
    39  	}
    40  }
    41  
    42  // SetupWithManager sets up the controller with the Manager.
    43  func (r *tlsRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
    44  	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &gatewayv1alpha2.TLSRoute{}, backendServiceIndex,
    45  		func(rawObj client.Object) []string {
    46  			route, ok := rawObj.(*gatewayv1alpha2.TLSRoute)
    47  			if !ok {
    48  				return nil
    49  			}
    50  			var backendServices []string
    51  			for _, rule := range route.Spec.Rules {
    52  				for _, backend := range rule.BackendRefs {
    53  					namespace := helpers.NamespaceDerefOr(backend.Namespace, route.Namespace)
    54  					backendServiceName, err := helpers.GetBackendServiceName(r.Client, namespace, backend.BackendObjectReference)
    55  					if err != nil {
    56  						log.WithFields(logrus.Fields{
    57  							logfields.Controller: "tlsRoute",
    58  							logfields.Resource:   client.ObjectKeyFromObject(rawObj),
    59  						}).WithError(err).Error("Failed to get backend service name")
    60  						continue
    61  					}
    62  					backendServices = append(backendServices,
    63  						types.NamespacedName{
    64  							Namespace: helpers.NamespaceDerefOr(backend.Namespace, route.Namespace),
    65  							Name:      backendServiceName,
    66  						}.String(),
    67  					)
    68  				}
    69  			}
    70  			return backendServices
    71  		},
    72  	); err != nil {
    73  		return err
    74  	}
    75  
    76  	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &gatewayv1alpha2.TLSRoute{}, backendServiceImportIndex,
    77  		func(rawObj client.Object) []string {
    78  			hr, ok := rawObj.(*gatewayv1alpha2.TLSRoute)
    79  			if !ok {
    80  				return nil
    81  			}
    82  			var backendServiceImports []string
    83  			for _, rule := range hr.Spec.Rules {
    84  				for _, backend := range rule.BackendRefs {
    85  					if !helpers.IsServiceImport(backend.BackendObjectReference) {
    86  						continue
    87  					}
    88  					backendServiceImports = append(backendServiceImports,
    89  						types.NamespacedName{
    90  							Namespace: helpers.NamespaceDerefOr(backend.Namespace, hr.Namespace),
    91  							Name:      string(backend.Name),
    92  						}.String(),
    93  					)
    94  				}
    95  			}
    96  			return backendServiceImports
    97  		},
    98  	); err != nil {
    99  		return err
   100  	}
   101  
   102  	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &gatewayv1alpha2.TLSRoute{}, gatewayIndex,
   103  		func(rawObj client.Object) []string {
   104  			hr := rawObj.(*gatewayv1alpha2.TLSRoute)
   105  			var gateways []string
   106  			for _, parent := range hr.Spec.ParentRefs {
   107  				if !helpers.IsGateway(parent) {
   108  					continue
   109  				}
   110  				gateways = append(gateways,
   111  					types.NamespacedName{
   112  						Namespace: helpers.NamespaceDerefOr(parent.Namespace, hr.Namespace),
   113  						Name:      string(parent.Name),
   114  					}.String(),
   115  				)
   116  			}
   117  			return gateways
   118  		},
   119  	); err != nil {
   120  		return err
   121  	}
   122  
   123  	builder := ctrl.NewControllerManagedBy(mgr).
   124  		// Watch for changes to TLSRoute
   125  		For(&gatewayv1alpha2.TLSRoute{}).
   126  		// Watch for changes to Backend services
   127  		Watches(&corev1.Service{}, r.enqueueRequestForBackendService()).
   128  		// Watch for changes to Reference Grants
   129  		Watches(&gatewayv1beta1.ReferenceGrant{}, r.enqueueRequestForReferenceGrant()).
   130  		// Watch for changes to Gateways and enqueue TLSRoutes that reference them
   131  		Watches(&gatewayv1.Gateway{}, r.enqueueRequestForGateway(),
   132  			builder.WithPredicates(
   133  				predicate.NewPredicateFuncs(hasMatchingController(context.Background(), mgr.GetClient(), controllerName)),
   134  			))
   135  
   136  	if helpers.HasServiceImportSupport(r.Client.Scheme()) {
   137  		// Watch for changes to Backend Service Imports
   138  		builder = builder.Watches(&mcsapiv1alpha1.ServiceImport{}, r.enqueueRequestForBackendServiceImport())
   139  	}
   140  
   141  	return builder.Complete(r)
   142  }
   143  
   144  // enqueueRequestForBackendService makes sure that TLS Routes are reconciled
   145  // if the backend services are updated.
   146  func (r *tlsRouteReconciler) enqueueRequestForBackendService() handler.EventHandler {
   147  	return handler.EnqueueRequestsFromMapFunc(r.enqueueFromIndex(backendServiceIndex))
   148  }
   149  
   150  // enqueueRequestForBackendServiceImport makes sure that TLS Routes are reconciled
   151  // if the backend Service Imports are updated.
   152  func (r *tlsRouteReconciler) enqueueRequestForBackendServiceImport() handler.EventHandler {
   153  	return handler.EnqueueRequestsFromMapFunc(r.enqueueFromIndex(backendServiceImportIndex))
   154  }
   155  
   156  // enqueueRequestForReferenceGrant makes sure that all TLS Routes are reconciled
   157  // if a ReferenceGrant changes
   158  func (r *tlsRouteReconciler) enqueueRequestForReferenceGrant() handler.EventHandler {
   159  	return handler.EnqueueRequestsFromMapFunc(r.enqueueAll())
   160  }
   161  
   162  func (r *tlsRouteReconciler) enqueueRequestForGateway() handler.EventHandler {
   163  	return handler.EnqueueRequestsFromMapFunc(r.enqueueFromIndex(gatewayIndex))
   164  }
   165  
   166  func (r *tlsRouteReconciler) enqueueFromIndex(index string) handler.MapFunc {
   167  	return func(ctx context.Context, o client.Object) []reconcile.Request {
   168  		scopedLog := log.WithFields(logrus.Fields{
   169  			logfields.Controller: "tlsRoute",
   170  			logfields.Resource:   client.ObjectKeyFromObject(o),
   171  		})
   172  		rList := &gatewayv1alpha2.TLSRouteList{}
   173  
   174  		if err := r.Client.List(context.Background(), rList, &client.ListOptions{
   175  			FieldSelector: fields.OneTermEqualSelector(index, client.ObjectKeyFromObject(o).String()),
   176  		}); err != nil {
   177  			scopedLog.WithError(err).Error("Failed to get related TLSRoutes")
   178  			return []reconcile.Request{}
   179  		}
   180  
   181  		requests := make([]reconcile.Request, 0, len(rList.Items))
   182  		for _, item := range rList.Items {
   183  			route := client.ObjectKey{
   184  				Namespace: item.GetNamespace(),
   185  				Name:      item.GetName(),
   186  			}
   187  			requests = append(requests, reconcile.Request{
   188  				NamespacedName: route,
   189  			})
   190  			scopedLog.WithField("tlsRoute", route).Info("Enqueued TLSRoute for resource")
   191  		}
   192  		return requests
   193  	}
   194  }
   195  
   196  func (r *tlsRouteReconciler) enqueueAll() handler.MapFunc {
   197  	return func(ctx context.Context, o client.Object) []reconcile.Request {
   198  		scopedLog := log.WithFields(logrus.Fields{
   199  			logfields.Controller: "tlsRoute",
   200  			logfields.Resource:   client.ObjectKeyFromObject(o),
   201  		})
   202  		trList := &gatewayv1alpha2.TLSRouteList{}
   203  
   204  		if err := r.Client.List(ctx, trList, &client.ListOptions{}); err != nil {
   205  			scopedLog.WithError(err).Error("Failed to get TLSRoutes")
   206  			return []reconcile.Request{}
   207  		}
   208  
   209  		requests := make([]reconcile.Request, 0, len(trList.Items))
   210  		for _, item := range trList.Items {
   211  			route := client.ObjectKey{
   212  				Namespace: item.GetNamespace(),
   213  				Name:      item.GetName(),
   214  			}
   215  			requests = append(requests, reconcile.Request{
   216  				NamespacedName: route,
   217  			})
   218  			scopedLog.WithField("tlsRoute", route).Info("Enqueued TLSRoute for resource")
   219  		}
   220  		return requests
   221  	}
   222  }