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  }