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