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 }