github.com/cilium/cilium@v1.16.2/operator/pkg/gateway-api/controller.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/google/go-cmp/cmp" 10 "github.com/google/go-cmp/cmp/cmpopts" 11 "github.com/sirupsen/logrus" 12 corev1 "k8s.io/api/core/v1" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/types" 15 "sigs.k8s.io/controller-runtime/pkg/client" 16 "sigs.k8s.io/controller-runtime/pkg/event" 17 "sigs.k8s.io/controller-runtime/pkg/predicate" 18 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" 19 gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" 20 21 "github.com/cilium/cilium/operator/pkg/gateway-api/helpers" 22 "github.com/cilium/cilium/pkg/logging/logfields" 23 ) 24 25 const ( 26 // controllerName is the gateway controller name used in cilium. 27 controllerName = "io.cilium/gateway-controller" 28 backendServiceIndex = "backendServiceIndex" 29 backendServiceImportIndex = "backendServiceImportIndex" 30 gatewayIndex = "gatewayIndex" 31 gammaBackendServiceIndex = "gammaBackendServiceIndex" 32 gammaListenerServiceIndex = "gammaListenerServiceIndex" 33 ) 34 35 func hasMatchingController(ctx context.Context, c client.Client, controllerName string) func(object client.Object) bool { 36 return func(obj client.Object) bool { 37 scopedLog := log.WithFields(logrus.Fields{ 38 logfields.Controller: gateway, 39 logfields.Resource: obj.GetName(), 40 }) 41 gw, ok := obj.(*gatewayv1.Gateway) 42 if !ok { 43 return false 44 } 45 46 gwc := &gatewayv1.GatewayClass{} 47 key := types.NamespacedName{Name: string(gw.Spec.GatewayClassName)} 48 if err := c.Get(ctx, key, gwc); err != nil { 49 scopedLog.WithError(err).Error("Unable to get GatewayClass") 50 return false 51 } 52 53 return string(gwc.Spec.ControllerName) == controllerName 54 } 55 } 56 57 func getGatewaysForSecret(ctx context.Context, c client.Client, obj client.Object) []*gatewayv1.Gateway { 58 scopedLog := log.WithFields(logrus.Fields{ 59 logfields.Controller: gateway, 60 logfields.Resource: obj.GetName(), 61 }) 62 63 gwList := &gatewayv1.GatewayList{} 64 if err := c.List(ctx, gwList); err != nil { 65 scopedLog.WithError(err).Warn("Unable to list Gateways") 66 return nil 67 } 68 69 var gateways []*gatewayv1.Gateway 70 for _, gw := range gwList.Items { 71 for _, l := range gw.Spec.Listeners { 72 if l.TLS == nil { 73 continue 74 } 75 76 for _, cert := range l.TLS.CertificateRefs { 77 if !helpers.IsSecret(cert) { 78 continue 79 } 80 ns := helpers.NamespaceDerefOr(cert.Namespace, gw.GetNamespace()) 81 if string(cert.Name) == obj.GetName() && ns == obj.GetNamespace() { 82 gateways = append(gateways, &gw) 83 } 84 } 85 } 86 } 87 return gateways 88 } 89 90 func getGatewaysForNamespace(ctx context.Context, c client.Client, ns client.Object) []types.NamespacedName { 91 scopedLog := log.WithFields(logrus.Fields{ 92 logfields.Controller: gateway, 93 logfields.K8sNamespace: ns.GetName(), 94 }) 95 96 gwList := &gatewayv1.GatewayList{} 97 if err := c.List(ctx, gwList); err != nil { 98 scopedLog.WithError(err).Warn("Unable to list Gateways") 99 return nil 100 } 101 102 var gateways []types.NamespacedName 103 for _, gw := range gwList.Items { 104 for _, l := range gw.Spec.Listeners { 105 if l.AllowedRoutes == nil || l.AllowedRoutes.Namespaces == nil { 106 continue 107 } 108 109 switch *l.AllowedRoutes.Namespaces.From { 110 case gatewayv1.NamespacesFromAll: 111 gateways = append(gateways, client.ObjectKey{ 112 Namespace: gw.GetNamespace(), 113 Name: gw.GetName(), 114 }) 115 case gatewayv1.NamespacesFromSame: 116 if ns.GetName() == gw.GetNamespace() { 117 gateways = append(gateways, client.ObjectKey{ 118 Namespace: gw.GetNamespace(), 119 Name: gw.GetName(), 120 }) 121 } 122 case gatewayv1.NamespacesFromSelector: 123 nsList := &corev1.NamespaceList{} 124 err := c.List(ctx, nsList, client.MatchingLabels(l.AllowedRoutes.Namespaces.Selector.MatchLabels)) 125 if err != nil { 126 scopedLog.WithError(err).Warn("Unable to list Namespaces") 127 return nil 128 } 129 for _, item := range nsList.Items { 130 if item.GetName() == ns.GetName() { 131 gateways = append(gateways, client.ObjectKey{ 132 Namespace: gw.GetNamespace(), 133 Name: gw.GetName(), 134 }) 135 } 136 } 137 } 138 } 139 } 140 return gateways 141 } 142 143 // onlyStatusChanged returns true if and only if there is status change for underlying objects. 144 // Supported objects are GatewayClass, Gateway, HTTPRoute and GRPCRoute 145 func onlyStatusChanged() predicate.Predicate { 146 option := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") 147 return predicate.Funcs{ 148 UpdateFunc: func(e event.UpdateEvent) bool { 149 switch e.ObjectOld.(type) { 150 case *gatewayv1.GatewayClass: 151 o, _ := e.ObjectOld.(*gatewayv1.GatewayClass) 152 n, ok := e.ObjectNew.(*gatewayv1.GatewayClass) 153 if !ok { 154 return false 155 } 156 return !cmp.Equal(o.Status, n.Status, option) 157 case *gatewayv1.Gateway: 158 o, _ := e.ObjectOld.(*gatewayv1.Gateway) 159 n, ok := e.ObjectNew.(*gatewayv1.Gateway) 160 if !ok { 161 return false 162 } 163 return !cmp.Equal(o.Status, n.Status, option) 164 case *gatewayv1.HTTPRoute: 165 o, _ := e.ObjectOld.(*gatewayv1.HTTPRoute) 166 n, ok := e.ObjectNew.(*gatewayv1.HTTPRoute) 167 if !ok { 168 return false 169 } 170 return !cmp.Equal(o.Status, n.Status, option) 171 case *gatewayv1alpha2.TLSRoute: 172 o, _ := e.ObjectOld.(*gatewayv1alpha2.TLSRoute) 173 n, ok := e.ObjectNew.(*gatewayv1alpha2.TLSRoute) 174 if !ok { 175 return false 176 } 177 return !cmp.Equal(o.Status, n.Status, option) 178 case *gatewayv1.GRPCRoute: 179 o, _ := e.ObjectOld.(*gatewayv1.GRPCRoute) 180 n, ok := e.ObjectNew.(*gatewayv1.GRPCRoute) 181 if !ok { 182 return false 183 } 184 return !cmp.Equal(o.Status, n.Status, option) 185 default: 186 return false 187 } 188 }, 189 } 190 }