diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 1614b578c6e..a4fe3a25244 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -50,12 +50,11 @@ type gatewayAPIReconciler struct { statusUpdater status.Updater classController gwapiv1b1.GatewayController - resources *message.ProviderResources - referenceStore *providerReferenceStore + resources *message.ProviderResources } // newGatewayAPIController -func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources, referenceStore *providerReferenceStore) error { +func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources) error { ctx := context.Background() r := &gatewayAPIReconciler{ @@ -64,7 +63,6 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status. classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName), statusUpdater: su, resources: resources, - referenceStore: referenceStore, } c, err := controller.New("gatewayapi", mgr, controller.Options{Reconciler: r}) @@ -101,7 +99,6 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status. if err := c.Watch( &source.Kind{Type: &gwapiv1b1.HTTPRoute{}}, &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.validateHTTPRouteForReconcile), ); err != nil { return err } @@ -113,7 +110,6 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status. if err := c.Watch( &source.Kind{Type: &gwapiv1a2.TLSRoute{}}, &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.validateTLSRouteForReconcile), ); err != nil { return err } @@ -162,6 +158,15 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status. return nil } +type resourceMappings struct { + // Map for storing namespaces for Route, Service and Gateway objects. + allAssociatedNamespaces map[string]struct{} + // Map for storing service NamespaceNames referred by various Route objects. + allAssociatedBackendRefs map[types.NamespacedName]struct{} + // Map for storing referenceGrant NamespaceNames for BackendRefs, SecretRefs. + allAssociatedRefGrants map[types.NamespacedName]*gwapiv1a2.ReferenceGrant +} + func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { r.log.WithName(request.Name).Info("reconciling gatewayAPI object", "namespace", request.Namespace, "name", request.Name) @@ -241,9 +246,11 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. Namespaces: []*corev1.Namespace{}, } - // Add objects' namespaces into the map. - // Make sure to add only those objects' namespaces, that exist. - allAssociatedNamespaces := map[string]struct{}{} + resourceMap := &resourceMappings{ + allAssociatedNamespaces: map[string]struct{}{}, + allAssociatedBackendRefs: map[types.NamespacedName]struct{}{}, + allAssociatedRefGrants: map[types.NamespacedName]*gwapiv1a2.ReferenceGrant{}, + } // Find gateways for the acceptedGC // Find the Gateways that reference this Class. @@ -258,7 +265,7 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. for _, gtw := range gatewayList.Items { gtw := gtw r.log.Info("processing Gateway", "namespace", gtw.Namespace, "name", gtw.Name) - allAssociatedNamespaces[gtw.Namespace] = struct{}{} + resourceMap.allAssociatedNamespaces[gtw.Namespace] = struct{}{} for _, listener := range gtw.Spec.Listeners { listener := listener @@ -289,141 +296,66 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. continue } - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant } - allAssociatedNamespaces[secretNamespace] = struct{}{} + resourceMap.allAssociatedNamespaces[secretNamespace] = struct{}{} resourceTree.Secrets = append(resourceTree.Secrets, secret) } } } + } - routeServicesList := map[types.NamespacedName]struct{}{} - - // Get TLSRoute objects and check if it exists. - tlsRouteList := &gwapiv1a2.TLSRouteList{} - if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(gatewayTLSRouteIndex, utils.NamespacedName(>w).String()), - }); err != nil { - r.log.Error(err, "unable to find associated TLSRoutes") - return reconcile.Result{}, err - } - for _, tlsRoute := range tlsRouteList.Items { - tlsRoute := tlsRoute - r.log.Info("processing TLSRoute", "namespace", tlsRoute.Namespace, "name", tlsRoute.Name) - - for _, rule := range tlsRoute.Spec.Rules { - for _, backendRef := range rule.BackendRefs { - backendRef := backendRef - ref := gatewayapi.UpgradeBackendRef(backendRef) - if err := validateBackendRef(&ref); err != nil { - r.log.Error(err, "invalid backendRef") - continue - } - - backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, gtw.Namespace) - routeServicesList[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(backendRef.Name), - }] = struct{}{} - - if backendNamespace != tlsRoute.Namespace { - from := ObjectKindNamespacedName{kind: gatewayapi.KindTLSRoute, namespace: tlsRoute.Namespace, name: tlsRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)} - refGrant, err := r.findReferenceGrant(ctx, from, to) - if err != nil { - r.log.Error(err, "unable to find ReferenceGrant that links the Service to TLSRoute") - continue - } - - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - } - } - } - - allAssociatedNamespaces[tlsRoute.Namespace] = struct{}{} - resourceTree.TLSRoutes = append(resourceTree.TLSRoutes, &tlsRoute) - } - - // Get HTTPRoute objects and check if it exists. - httpRouteList := &gwapiv1b1.HTTPRouteList{} - if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(gatewayHTTPRouteIndex, utils.NamespacedName(>w).String()), - }); err != nil { - r.log.Error(err, "unable to find associated HTTPRoutes") - return reconcile.Result{}, err - } - for _, httpRoute := range httpRouteList.Items { - httpRoute := httpRoute - r.log.Info("processing HTTPRoute", "namespace", httpRoute.Namespace, "name", httpRoute.Name) - - for _, rule := range httpRoute.Spec.Rules { - for _, backendRef := range rule.BackendRefs { - backendRef := backendRef - if err := validateBackendRef(&backendRef.BackendRef); err != nil { - r.log.Error(err, "invalid backendRef") - continue - } + // Route Processing + // Get TLSRoute objects and check if it exists. + if err := r.processTLSRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + return reconcile.Result{}, err + } - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, httpRoute.Namespace) - routeServicesList[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(backendRef.Name), - }] = struct{}{} + // Get HTTPRoute objects and check if it exists. + if err := r.processHTTPRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + return reconcile.Result{}, err + } - if backendNamespace != httpRoute.Namespace { - from := ObjectKindNamespacedName{kind: gatewayapi.KindHTTPRoute, namespace: httpRoute.Namespace, name: httpRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)} - refGrant, err := r.findReferenceGrant(ctx, from, to) - if err != nil { - r.log.Error(err, "unable to find ReferenceGrant that links the Service to HTTPRoute") - continue - } + resourceTree.Gateways = append(resourceTree.Gateways, >w) + } - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - } - } - } + for serviceNamespaceName := range resourceMap.allAssociatedBackendRefs { + r.log.Info("processing Service", "namespace", serviceNamespaceName.Namespace, + "name", serviceNamespaceName.Name) - allAssociatedNamespaces[httpRoute.Namespace] = struct{}{} - resourceTree.HTTPRoutes = append(resourceTree.HTTPRoutes, &httpRoute) + service := new(corev1.Service) + err := r.client.Get(ctx, serviceNamespaceName, service) + if err != nil { + r.log.Error(err, "unable to find associated Services") + if kerrors.IsNotFound(err) { + return reconcile.Result{}, nil } + return reconcile.Result{}, err + } - for serviceNamespaceName := range routeServicesList { - r.log.Info("processing Service", "namespace", serviceNamespaceName.Namespace, - "name", serviceNamespaceName.Name) - - service := new(corev1.Service) - err := r.client.Get(ctx, serviceNamespaceName, service) - if err != nil { - if kerrors.IsNotFound(err) { - continue - } - r.log.Error(err, "unable to find associated Services") - return reconcile.Result{}, err - } + resourceMap.allAssociatedNamespaces[service.Namespace] = struct{}{} + resourceTree.Services = append(resourceTree.Services, service) + } - allAssociatedNamespaces[service.Namespace] = struct{}{} - resourceTree.Services = append(resourceTree.Services, service) - } - } + // Add all ReferenceGrants to the resourceTree + for _, referenceGrant := range resourceMap.allAssociatedRefGrants { + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, referenceGrant) + } - // For this particular Gateway, and all associated objects, check whether the - // namespace exists. Add to the resourceTree. - for ns := range allAssociatedNamespaces { - namespace, err := r.getNamespace(ctx, ns) - if err != nil { - if kerrors.IsNotFound(err) { - continue - } - r.log.Error(err, "unable to find the namespace") - return reconcile.Result{}, err + // For this particular Gateway, and all associated objects, check whether the + // namespace exists. Add to the resourceTree. + for ns := range resourceMap.allAssociatedNamespaces { + namespace, err := r.getNamespace(ctx, ns) + if err != nil { + r.log.Error(err, "unable to find the namespace") + if kerrors.IsNotFound(err) { + return reconcile.Result{}, nil } - - resourceTree.Namespaces = append(resourceTree.Namespaces, namespace) + return reconcile.Result{}, err } - resourceTree.Gateways = append(resourceTree.Gateways, >w) + resourceTree.Namespaces = append(resourceTree.Namespaces, namespace) } if err := updater(acceptedGC, true); err != nil { @@ -469,27 +401,6 @@ func (r *gatewayAPIReconciler) getNamespace(ctx context.Context, name string) (* return ns, nil } -func (r gatewayAPIReconciler) findOwningGateway(ctx context.Context, labels map[string]string) *gwapiv1b1.Gateway { - gwName, ok := labels[gatewayapi.OwningGatewayNameLabel] - if !ok { - return nil - } - - gwNamespace, ok := labels[gatewayapi.OwningGatewayNamespaceLabel] - if !ok { - return nil - } - - gatewayKey := types.NamespacedName{Namespace: gwNamespace, Name: gwName} - gtw := new(gwapiv1b1.Gateway) - if err := r.client.Get(ctx, gatewayKey, gtw); err != nil { - r.log.Error(err, "gateway not found") - return nil - } - - return gtw -} - func (r *gatewayAPIReconciler) statusUpdateForGateway(gtw *gwapiv1b1.Gateway, svc *corev1.Service, deploy *appsv1.Deployment) { // update scheduled condition status.UpdateGatewayStatusScheduledCondition(gtw, true) @@ -737,38 +648,6 @@ func (r *gatewayAPIReconciler) addFinalizer(ctx context.Context, gc *gwapiv1b1.G return nil } -// envoyDeploymentForGateway returns the Envoy Deployment, returning nil if the Deployment doesn't exist. -func (r *gatewayAPIReconciler) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*appsv1.Deployment, error) { - key := types.NamespacedName{ - Namespace: config.EnvoyGatewayNamespace, - Name: infraDeploymentName(gateway), - } - deployment := new(appsv1.Deployment) - if err := r.client.Get(ctx, key, deployment); err != nil { - if kerrors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - return deployment, nil -} - -// envoyServiceForGateway returns the Envoy service, returning nil if the service doesn't exist. -func (r *gatewayAPIReconciler) envoyServiceForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*corev1.Service, error) { - key := types.NamespacedName{ - Namespace: config.EnvoyGatewayNamespace, - Name: infraServiceName(gateway), - } - svc := new(corev1.Service) - if err := r.client.Get(ctx, key, svc); err != nil { - if kerrors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - return svc, nil -} - // subscribeAndUpdateStatus subscribes to gateway API object status updates and // writes it into the Kubernetes API Server. func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { diff --git a/internal/provider/kubernetes/helpers.go b/internal/provider/kubernetes/helpers.go index 136ba71c02f..aa7348bf0ca 100644 --- a/internal/provider/kubernetes/helpers.go +++ b/internal/provider/kubernetes/helpers.go @@ -24,6 +24,12 @@ const ( gatewayClassFinalizer = gwapiv1b1.GatewayClassFinalizerGatewaysExist ) +type ObjectKindNamespacedName struct { + kind string + namespace string + name string +} + // validateParentRefs validates the provided routeParentReferences, returning the // referenced Gateways managed by Envoy Gateway. The only supported parentRef // is a Gateway. diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go index 14d573db5f6..33f8420a411 100644 --- a/internal/provider/kubernetes/kubernetes.go +++ b/internal/provider/kubernetes/kubernetes.go @@ -50,11 +50,8 @@ func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResour return nil, fmt.Errorf("failed to add status update handler %v", err) } - // Initialize kubernetes provider referenceStore to store additional object mappings. - referenceStore := newProviderReferenceStore() - // Create and register the controllers with the manager. - if err := newGatewayAPIController(mgr, svr, updateHandler.Writer(), resources, referenceStore); err != nil { + if err := newGatewayAPIController(mgr, svr, updateHandler.Writer(), resources); err != nil { return nil, fmt.Errorf("failted to create gatewayapi controller: %w", err) } diff --git a/internal/provider/kubernetes/predicates.go b/internal/provider/kubernetes/predicates.go index 7e3e7387dd8..2d62a72382d 100644 --- a/internal/provider/kubernetes/predicates.go +++ b/internal/provider/kubernetes/predicates.go @@ -8,10 +8,12 @@ package kubernetes import ( "context" + "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/provider/utils" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -65,59 +67,6 @@ func (r *gatewayAPIReconciler) validateGatewayForReconcile(obj client.Object) bo return true } -// validateTLSRouteForReconcile checks whether the HTTPRoute refers any valid Gateway. -func (r *gatewayAPIReconciler) validateHTTPRouteForReconcile(obj client.Object) bool { - hr, ok := obj.(*gwapiv1b1.HTTPRoute) - if !ok { - r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) - return false - } - - parentReferences := hr.Spec.ParentRefs - return r.validateRouteParentReferences(parentReferences, hr.Namespace) -} - -// validateTLSRouteForReconcile checks whether the TLSRoute refers any valid Gateway. -func (r *gatewayAPIReconciler) validateTLSRouteForReconcile(obj client.Object) bool { - tr, ok := obj.(*gwapiv1a2.TLSRoute) - if !ok { - r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) - return false - } - - parentReferences := gatewayapi.UpgradeParentReferences(tr.Spec.ParentRefs) - return r.validateRouteParentReferences(parentReferences, tr.Namespace) -} - -// validateRouteParentReferences checks whether the parent references of a given Route -// object, point to valid Gateways. -func (r *gatewayAPIReconciler) validateRouteParentReferences(refs []gwapiv1b1.ParentReference, defaultNamespace string) bool { - for _, ref := range refs { - if ref.Kind != nil && *ref.Kind == gatewayapi.KindGateway { - key := types.NamespacedName{ - Namespace: gatewayapi.NamespaceDerefOr(ref.Namespace, defaultNamespace), - Name: string(ref.Name), - } - - gw := &gwapiv1b1.Gateway{} - if err := r.client.Get(context.Background(), key, gw); err != nil { - r.log.Error(err, "failed to get gateway", "namespace", key.Namespace, "name", key.Name) - return false - } - - if !r.validateGatewayForReconcile(gw) { - return false - } - - // Even if one of the parent references points to a valid Gateway, we - // must reconcile the Route object. - return true - } - } - - return true -} - // validateSecretForReconcile checks whether the Secret belongs to a valid Gateway. func (r *gatewayAPIReconciler) validateSecretForReconcile(obj client.Object) bool { secret, ok := obj.(*corev1.Secret) @@ -223,3 +172,57 @@ func (r *gatewayAPIReconciler) validateDeploymentForReconcile(obj client.Object) // There is no need to reconcile the Deployment any further. return false } + +// envoyDeploymentForGateway returns the Envoy Deployment, returning nil if the Deployment doesn't exist. +func (r *gatewayAPIReconciler) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*appsv1.Deployment, error) { + key := types.NamespacedName{ + Namespace: config.EnvoyGatewayNamespace, + Name: infraDeploymentName(gateway), + } + deployment := new(appsv1.Deployment) + if err := r.client.Get(ctx, key, deployment); err != nil { + if kerrors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + return deployment, nil +} + +// envoyServiceForGateway returns the Envoy service, returning nil if the service doesn't exist. +func (r *gatewayAPIReconciler) envoyServiceForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*corev1.Service, error) { + key := types.NamespacedName{ + Namespace: config.EnvoyGatewayNamespace, + Name: infraServiceName(gateway), + } + svc := new(corev1.Service) + if err := r.client.Get(ctx, key, svc); err != nil { + if kerrors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + return svc, nil +} + +// findOwningGateway attempts finds a Gateway using "labels". +func (r gatewayAPIReconciler) findOwningGateway(ctx context.Context, labels map[string]string) *gwapiv1b1.Gateway { + gwName, ok := labels[gatewayapi.OwningGatewayNameLabel] + if !ok { + return nil + } + + gwNamespace, ok := labels[gatewayapi.OwningGatewayNamespaceLabel] + if !ok { + return nil + } + + gatewayKey := types.NamespacedName{Namespace: gwNamespace, Name: gwName} + gtw := new(gwapiv1b1.Gateway) + if err := r.client.Get(ctx, gatewayKey, gtw); err != nil { + r.log.Error(err, "gateway not found") + return nil + } + + return gtw +} diff --git a/internal/provider/kubernetes/routes.go b/internal/provider/kubernetes/routes.go new file mode 100644 index 00000000000..b13a494f812 --- /dev/null +++ b/internal/provider/kubernetes/routes.go @@ -0,0 +1,120 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package kubernetes + +import ( + "context" + + "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/provider/utils" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// processTLSRoutes finds TLSRoutes corresponding to a gatewayNamespaceName, further checks for +// the backend references and pushes the TLSRoutes to the resourceTree. +func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayNamespaceName string, + resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { + tlsRouteList := &gwapiv1a2.TLSRouteList{} + if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(gatewayTLSRouteIndex, gatewayNamespaceName), + }); err != nil { + r.log.Error(err, "unable to find associated TLSRoutes") + return err + } + + for _, tlsRoute := range tlsRouteList.Items { + tlsRoute := tlsRoute + r.log.Info("processing TLSRoute", "namespace", tlsRoute.Namespace, "name", tlsRoute.Name) + + for _, rule := range tlsRoute.Spec.Rules { + for _, backendRef := range rule.BackendRefs { + backendRef := backendRef + ref := gatewayapi.UpgradeBackendRef(backendRef) + if err := validateBackendRef(&ref); err != nil { + r.log.Error(err, "invalid backendRef") + continue + } + + backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, tlsRoute.Namespace) + resourceMap.allAssociatedBackendRefs[types.NamespacedName{ + Namespace: backendNamespace, + Name: string(backendRef.Name), + }] = struct{}{} + + if backendNamespace != tlsRoute.Namespace { + from := ObjectKindNamespacedName{kind: gatewayapi.KindTLSRoute, namespace: tlsRoute.Namespace, name: tlsRoute.Name} + to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)} + refGrant, err := r.findReferenceGrant(ctx, from, to) + if err != nil { + r.log.Error(err, "unable to find ReferenceGrant that links the Service to TLSRoute") + continue + } + + resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant + } + } + } + + resourceMap.allAssociatedNamespaces[tlsRoute.Namespace] = struct{}{} + resourceTree.TLSRoutes = append(resourceTree.TLSRoutes, &tlsRoute) + } + + return nil +} + +// processHTTPRoutes finds HTTPRoutes corresponding to a gatewayNamespaceName, further checks for +// the backend references and pushes the HTTPRoutes to the resourceTree. +func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNamespaceName string, + resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { + httpRouteList := &gwapiv1b1.HTTPRouteList{} + if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(gatewayHTTPRouteIndex, gatewayNamespaceName), + }); err != nil { + r.log.Error(err, "unable to find associated HTTPRoutes") + return err + } + for _, httpRoute := range httpRouteList.Items { + httpRoute := httpRoute + r.log.Info("processing HTTPRoute", "namespace", httpRoute.Namespace, "name", httpRoute.Name) + + for _, rule := range httpRoute.Spec.Rules { + for _, backendRef := range rule.BackendRefs { + backendRef := backendRef + if err := validateBackendRef(&backendRef.BackendRef); err != nil { + r.log.Error(err, "invalid backendRef") + continue + } + + backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, httpRoute.Namespace) + resourceMap.allAssociatedBackendRefs[types.NamespacedName{ + Namespace: backendNamespace, + Name: string(backendRef.Name), + }] = struct{}{} + + if backendNamespace != httpRoute.Namespace { + from := ObjectKindNamespacedName{kind: gatewayapi.KindHTTPRoute, namespace: httpRoute.Namespace, name: httpRoute.Name} + to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)} + refGrant, err := r.findReferenceGrant(ctx, from, to) + if err != nil { + r.log.Error(err, "unable to find ReferenceGrant that links the Service to HTTPRoute") + continue + } + + resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant + } + } + } + + resourceMap.allAssociatedNamespaces[httpRoute.Namespace] = struct{}{} + resourceTree.HTTPRoutes = append(resourceTree.HTTPRoutes, &httpRoute) + } + + return nil +} diff --git a/internal/provider/kubernetes/store.go b/internal/provider/kubernetes/store.go deleted file mode 100644 index 3ecc514fda5..00000000000 --- a/internal/provider/kubernetes/store.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package kubernetes - -import ( - "sync" - - "k8s.io/apimachinery/pkg/types" -) - -// providerReferenceStore maintains additional mappings related to Kubernetes provider -// resources. The mappings are regularly updated from the reconcilers based -// on the existence of the object in the Kubernetes datastore. -type providerReferenceStore struct { - mu sync.Mutex - - // routeToServicesMappings maintains a mapping of a Route object, - // and the Services it references. For instance - // HTTPRoute/ns1/route1 -> { ns1/svc1, ns1/svc2, ns2/svc1 } - // TLSRoute/ns1/route1 -> { ns1/svc1, ns2/svc2 } - routeToServicesMappings map[ObjectKindNamespacedName]map[types.NamespacedName]struct{} -} - -type ObjectKindNamespacedName struct { - kind string - namespace string - name string -} - -func newProviderReferenceStore() *providerReferenceStore { - return &providerReferenceStore{ - routeToServicesMappings: map[ObjectKindNamespacedName]map[types.NamespacedName]struct{}{}, - } -} - -func (p *providerReferenceStore) getRouteToServicesMapping(route ObjectKindNamespacedName) map[types.NamespacedName]struct{} { - p.mu.Lock() - defer p.mu.Unlock() - - return p.routeToServicesMappings[route] -} - -func (p *providerReferenceStore) updateRouteToServicesMapping(route ObjectKindNamespacedName, service types.NamespacedName) { - p.mu.Lock() - defer p.mu.Unlock() - - if len(p.routeToServicesMappings[route]) == 0 { - p.routeToServicesMappings[route] = map[types.NamespacedName]struct{}{service: {}} - } else { - p.routeToServicesMappings[route][service] = struct{}{} - } -} - -func (p *providerReferenceStore) removeRouteToServicesMapping(route ObjectKindNamespacedName, service types.NamespacedName) { - p.mu.Lock() - defer p.mu.Unlock() - - delete(p.routeToServicesMappings[route], service) - if len(p.routeToServicesMappings[route]) == 0 { - delete(p.routeToServicesMappings, route) - } -} - -func (p *providerReferenceStore) isServiceReferredByRoutes(service types.NamespacedName) bool { - p.mu.Lock() - defer p.mu.Unlock() - - for _, svcs := range p.routeToServicesMappings { - if _, ok := svcs[service]; ok { - return true - } - } - return false -} diff --git a/internal/provider/kubernetes/store_test.go b/internal/provider/kubernetes/store_test.go deleted file mode 100644 index 66c3c4bfdf3..00000000000 --- a/internal/provider/kubernetes/store_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package kubernetes - -import ( - "testing" - - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/types" -) - -func TestProviderReferenceStore(t *testing.T) { - cache := newProviderReferenceStore() - - testCases := []struct { - name string - test func(t *testing.T, c *providerReferenceStore) - }{ - { - name: "route to service mappings", - test: testRouteToServicesMappings, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - tc.test(t, cache) - }) - } -} - -func testRouteToServicesMappings(t *testing.T, cache *providerReferenceStore) { - httpr1 := ObjectKindNamespacedName{"HTTPRoute", "ns1", "r1"} - tlsr1 := ObjectKindNamespacedName{"TLSRoute", "ns1", "r1"} - - ns1svc1 := types.NamespacedName{Namespace: "ns1", Name: "svc1"} - ns1svc2 := types.NamespacedName{Namespace: "ns1", Name: "svc2"} - - // Add HTTPRoute/ns1/r1 -> ns1/svc1 mapping - cache.updateRouteToServicesMapping(httpr1, ns1svc1) - require.Equal(t, map[types.NamespacedName]struct{}{ns1svc1: {}}, cache.getRouteToServicesMapping(httpr1)) - - // Add HTTPRoute/ns1/r1 -> ns1/svc2 mapping - // Add TLSRoute/ns1/r1 -> ns1/svc2 mapping - cache.updateRouteToServicesMapping(tlsr1, ns1svc2) - cache.updateRouteToServicesMapping(httpr1, ns1svc2) - require.Equal(t, map[types.NamespacedName]struct{}{ns1svc2: {}}, cache.getRouteToServicesMapping(tlsr1)) - require.Equal(t, map[types.NamespacedName]struct{}{ns1svc1: {}, ns1svc2: {}}, cache.getRouteToServicesMapping(httpr1)) - - // Remove HTTPRoute/ns1/r1 -> ns1/svc1 mapping - cache.removeRouteToServicesMapping(httpr1, ns1svc1) - require.Equal(t, map[types.NamespacedName]struct{}{ns1svc2: {}}, cache.getRouteToServicesMapping(httpr1)) - - // Remove TLSRoute/ns1/r1 -> ns1/svc2 mapping - cache.removeRouteToServicesMapping(tlsr1, ns1svc2) - require.Equal(t, map[types.NamespacedName]struct{}(nil), cache.getRouteToServicesMapping(tlsr1)) - - // Verify that ns1svc2 is still referred by another route (HTTPRoute/ns1/r1) - require.Equal(t, true, cache.isServiceReferredByRoutes(ns1svc2)) - - // Verify that ns1svc1 is not referred by any other route - require.Equal(t, false, cache.isServiceReferredByRoutes(ns1svc1)) -}