From 805f8967136a705f267141f5bd3b0e7b7b7ababe Mon Sep 17 00:00:00 2001 From: Jim Fitzpatrick Date: Thu, 30 May 2024 10:27:23 +0100 Subject: [PATCH] Fix the out-of-order creation issue for authPolices. --- controllers/authpolicy_controller.go | 4 +- controllers/ratelimitpolicy_controller.go | 4 +- pkg/library/kuadrant/referrer.go | 15 +++ pkg/library/mappers/gateway.go | 2 +- pkg/library/mappers/httproute.go | 135 +++++++++++++++++++--- 5 files changed, 142 insertions(+), 18 deletions(-) diff --git a/controllers/authpolicy_controller.go b/controllers/authpolicy_controller.go index fcb2e828a..b7f62860f 100644 --- a/controllers/authpolicy_controller.go +++ b/controllers/authpolicy_controller.go @@ -262,7 +262,7 @@ func (r *AuthPolicyReconciler) reconcileRouteParentGatewayPolicies(ctx context.C // SetupWithManager sets up the controller with the Manager. func (r *AuthPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { - httpRouteEventMapper := mappers.NewHTTPRouteEventMapper(mappers.WithLogger(r.Logger().WithName("httpRouteEventMapper"))) + httpRouteEventMapper := mappers.NewHTTPRouteEventMapper(mappers.WithLogger(r.Logger().WithName("httpRouteEventMapper")), mappers.WithClient(mgr.GetClient())) gatewayEventMapper := mappers.NewGatewayEventMapper(mappers.WithLogger(r.Logger().WithName("gatewayEventMapper")), mappers.WithClient(mgr.GetClient())) return ctrl.NewControllerManagedBy(mgr). @@ -271,7 +271,7 @@ func (r *AuthPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &gatewayapiv1.HTTPRoute{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, object client.Object) []reconcile.Request { - return httpRouteEventMapper.MapToPolicy(object, &api.AuthPolicy{}) + return httpRouteEventMapper.MapToPolicy(object, schema.GroupVersionKind{Group: "kuadrant.io", Version: "kuadrant.io/v1beta2", Kind: "AuthPolicy"}) }), ). Watches(&gatewayapiv1.Gateway{}, diff --git a/controllers/ratelimitpolicy_controller.go b/controllers/ratelimitpolicy_controller.go index 5c75300d4..48ad70f8a 100644 --- a/controllers/ratelimitpolicy_controller.go +++ b/controllers/ratelimitpolicy_controller.go @@ -226,7 +226,7 @@ func (r *RateLimitPolicyReconciler) deleteNetworkResourceDirectBackReference(ctx // SetupWithManager sets up the controller with the Manager. func (r *RateLimitPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { - httpRouteEventMapper := mappers.NewHTTPRouteEventMapper(mappers.WithLogger(r.Logger().WithName("httpRouteEventMapper"))) + httpRouteEventMapper := mappers.NewHTTPRouteEventMapper(mappers.WithLogger(r.Logger().WithName("httpRouteEventMapper")), mappers.WithClient(mgr.GetClient())) gatewayEventMapper := mappers.NewGatewayEventMapper(mappers.WithLogger(r.Logger().WithName("gatewayEventMapper")), mappers.WithClient(mgr.GetClient())) return ctrl.NewControllerManagedBy(mgr). @@ -234,7 +234,7 @@ func (r *RateLimitPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &gatewayapiv1.HTTPRoute{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, object client.Object) []reconcile.Request { - return httpRouteEventMapper.MapToPolicy(object, &kuadrantv1beta2.RateLimitPolicy{}) + return httpRouteEventMapper.MapToPolicy(object, schema.GroupVersionKind{Group: "kuadrant.io", Version: "kuadrant.io/v1beta2", Kind: "RateLimitPolicy"}) }), ). // Currently the purpose is to generate events when rlp references change in gateways diff --git a/pkg/library/kuadrant/referrer.go b/pkg/library/kuadrant/referrer.go index 5fce0d01c..77cc07f64 100644 --- a/pkg/library/kuadrant/referrer.go +++ b/pkg/library/kuadrant/referrer.go @@ -2,6 +2,7 @@ package kuadrant import ( "encoding/json" + "strings" "sigs.k8s.io/controller-runtime/pkg/client" @@ -33,3 +34,17 @@ func BackReferencesFromObject(obj client.Object, referrer Referrer) []client.Obj return refs } + +func DirectReferencesFromObject(obj client.Object, referrer Referrer) client.ObjectKey { + annotations := utils.ReadAnnotationsFromObject(obj) + key := referrer.DirectReferenceAnnotationName() + directRefs, found := annotations[key] + if !found { + return client.ObjectKey{} + } + + parts := strings.Split(directRefs, "/") + ref := client.ObjectKey{Namespace: parts[0], Name: parts[1]} + + return ref +} diff --git a/pkg/library/mappers/gateway.go b/pkg/library/mappers/gateway.go index 588b49968..38c918364 100644 --- a/pkg/library/mappers/gateway.go +++ b/pkg/library/mappers/gateway.go @@ -53,7 +53,7 @@ func (m *gatewayEventMapper) MapToPolicy(obj client.Object, policyGVK schema.Gro policyList.SetAPIVersion(policyGVK.Version) policyList.SetKind(policyGVK.Kind) if err := m.opts.Client.List(ctx, policyList, client.InNamespace(obj.GetNamespace())); err != nil { - logger.V(1).Error(err, fmt.Sprintf("unable to list UnstructuredList of policies, %T", policyGVK)) + logger.V(1).Info("unable to list UnstructuredList of policies, %T", policyGVK) return []reconcile.Request{} } diff --git a/pkg/library/mappers/httproute.go b/pkg/library/mappers/httproute.go index 721ae9c51..4baf30f4a 100644 --- a/pkg/library/mappers/httproute.go +++ b/pkg/library/mappers/httproute.go @@ -1,44 +1,153 @@ package mappers import ( + "context" "fmt" - + "github.com/kuadrant/kuadrant-operator/api/v1alpha1" + api "github.com/kuadrant/kuadrant-operator/api/v1beta2" + kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi" + "github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant" + "github.com/kuadrant/kuadrant-operator/pkg/library/utils" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/json" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - "github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant" ) -func NewHTTPRouteEventMapper(o ...MapperOption) EventMapper { +func NewHTTPRouteEventMapper(o ...MapperOption) EventMapperTwo { return &httpRouteEventMapper{opts: Apply(o...)} } -var _ EventMapper = &httpRouteEventMapper{} +var _ EventMapperTwo = &httpRouteEventMapper{} type httpRouteEventMapper struct { opts MapperOptions } -func (m *httpRouteEventMapper) MapToPolicy(obj client.Object, policyKind kuadrant.Referrer) []reconcile.Request { +func (m *httpRouteEventMapper) MapToPolicy(obj client.Object, policyGVK schema.GroupVersionKind) []reconcile.Request { logger := m.opts.Logger.WithValues("httproute", client.ObjectKeyFromObject(obj)) - + ctx := context.Background() + requests := make([]reconcile.Request, 0) httpRoute, ok := obj.(*gatewayapiv1.HTTPRoute) if !ok { logger.Info("cannot map httproute event to kuadrant policy", "error", fmt.Sprintf("%T is not a *gatewayapiv1beta1.HTTPRoute", obj)) return []reconcile.Request{} } - requests := make([]reconcile.Request, 0) + gatewayKeys := kuadrantgatewayapi.GetRouteAcceptedGatewayParentKeys(httpRoute) + + for _, gatewayKey := range gatewayKeys { + gateway := &gatewayapiv1.Gateway{} + err := m.opts.Client.Get(ctx, gatewayKey, gateway) + if err != nil { + logger.Info("cannot get gateway", "error", err) + continue + } + + routeList := &gatewayapiv1.HTTPRouteList{} + fields := client.MatchingFields{kuadrantgatewayapi.HTTPRouteGatewayParentField: client.ObjectKeyFromObject(gateway).String()} + if err = m.opts.Client.List(ctx, routeList, fields); err != nil { + logger.Info("cannot list httproutes", "error", err) + continue + } + policyList := &unstructured.UnstructuredList{} + policyList.SetAPIVersion(policyGVK.Version) + policyList.SetKind(policyGVK.Kind) + if err = m.opts.Client.List(ctx, policyList, client.InNamespace(obj.GetNamespace())); err != nil { + logger.V(1).Info("unable to list UnstructuredList of policies, %T", policyGVK) + continue + } - for _, policyKey := range kuadrant.BackReferencesFromObject(httpRoute, policyKind) { - logger.V(1).Info("kuadrant policy possibly affected by the httproute related event found", policyKind.Kind(), policyKey) - requests = append(requests, reconcile.Request{NamespacedName: policyKey}) + var policies []kuadrantgatewayapi.Policy + if err = policyList.EachListItem(func(obj runtime.Object) error { + objBytes, err := json.Marshal(obj) + if err != nil { + return err + } + + switch obj.GetObjectKind().GroupVersionKind().Kind { + case "AuthPolicy": + policy := &api.AuthPolicy{} + err = json.Unmarshal(objBytes, policy) + if err != nil { + return err + } + policies = append(policies, policy) + case "DNSPolicy": + policy := &v1alpha1.DNSPolicy{} + err = json.Unmarshal(objBytes, policy) + if err != nil { + return err + } + policies = append(policies, policy) + case "TLSPolicy": + policy := &v1alpha1.TLSPolicy{} + err = json.Unmarshal(objBytes, policy) + if err != nil { + return err + } + policies = append(policies, policy) + case "RateLimitPolicy": + policy := &api.RateLimitPolicy{} + err = json.Unmarshal(objBytes, policy) + if err != nil { + return err + } + policies = append(policies, policy) + default: + return fmt.Errorf("unknown policy kind: %s", obj.GetObjectKind().GroupVersionKind().Kind) + } + return nil + }); err != nil { + logger.Info("unable to list UnstructuredList of policies, %T", policyGVK) + continue + } + if len(policies) == 0 { + logger.Info("no kuadrant policy possibly affected by the gateway related event") + continue + } + topology, err := kuadrantgatewayapi.NewTopology( + kuadrantgatewayapi.WithGateways([]*gatewayapiv1.Gateway{gateway}), + kuadrantgatewayapi.WithRoutes(utils.Map(routeList.Items, ptr.To[gatewayapiv1.HTTPRoute])), + kuadrantgatewayapi.WithPolicies(policies), + kuadrantgatewayapi.WithLogger(logger), + ) + if err != nil { + logger.Info("unable to build topology for gateway", "error", err) + continue + } + index := kuadrantgatewayapi.NewTopologyIndexes(topology) + data := utils.Map(index.PoliciesFromGateway(gateway), func(p kuadrantgatewayapi.Policy) reconcile.Request { + policyKey := client.ObjectKeyFromObject(p) + logger.V(1).Info("kuadrant policy possibly affected by the gateway related event found", policyGVK.Kind, policyKey) + return reconcile.Request{NamespacedName: policyKey} + }) + requests = append(requests, data...) } - if len(requests) == 0 { - logger.V(1).Info("no kuadrant policy possibly affected by the httproute related event") + if len(requests) != 0 { + return requests } + // This block is required when a HTTProute has being deleted + var policy kuadrant.Referrer + switch policyGVK.Kind { + case "AuthPolicy": + policy = &api.AuthPolicy{} + case "DNSPolicy": + policy = &v1alpha1.DNSPolicy{} + case "TLSPolicy": + policy = &v1alpha1.TLSPolicy{} + case "RateLimitPolicy": + policy = &api.RateLimitPolicy{} + default: + return requests + } + policyKey := kuadrant.DirectReferencesFromObject(httpRoute, policy) + requests = append(requests, reconcile.Request{NamespacedName: policyKey}) return requests }