diff --git a/controllers/helper_test.go b/controllers/helper_test.go index a2004f70b..54f6d3db1 100644 --- a/controllers/helper_test.go +++ b/controllers/helper_test.go @@ -391,6 +391,99 @@ func testRLPIsAccepted(rlpKey client.ObjectKey) func() bool { } } +func testRLPIsNotAccepted(rlpKey client.ObjectKey) func() bool { + return func() bool { + existingRLP := &kuadrantv1beta2.RateLimitPolicy{} + err := k8sClient.Get(context.Background(), rlpKey, existingRLP) + if err != nil { + logf.Log.V(1).Info("ratelimitpolicy not read", "rlp", rlpKey, "error", err) + return false + } + if meta.IsStatusConditionTrue(existingRLP.Status.Conditions, string(gatewayapiv1alpha2.PolicyConditionAccepted)) { + logf.Log.V(1).Info("ratelimitpolicy still accepted", "rlp", rlpKey) + return false + } + + return true + } +} + +func testHTTPRouteWithoutDirectBackReference(routeKey client.ObjectKey, annotationName string) func() bool { + return testNetworkResourceWithoutDirectBackReference(routeKey, &gatewayapiv1.HTTPRoute{}, annotationName) +} + +func testGatewayWithoutDirectBackReference(gwKey client.ObjectKey, annotationName string) func() bool { + return testNetworkResourceWithoutDirectBackReference(gwKey, &gatewayapiv1.Gateway{}, annotationName) +} + +func testNetworkResourceWithoutDirectBackReference(objKey client.ObjectKey, obj client.Object, annotationName string) func() bool { + return func() bool { + err := k8sClient.Get(context.Background(), objKey, obj) + if err != nil { + logf.Log.V(1).Info("object not read", "object", objKey, + "kind", obj.GetObjectKind().GroupVersionKind(), "error", err) + return false + } + + _, ok := obj.GetAnnotations()[annotationName] + if ok { + logf.Log.V(1).Info("object sill has the direct ref annotation", + "object", objKey, "kind", obj.GetObjectKind().GroupVersionKind()) + return false + } + + return true + } +} + +func testHTTPRouteHasDirectBackReference(routeKey client.ObjectKey, annotationName, annotationVal string) func() bool { + return testNetworkResourceHasDirectBackReference(routeKey, &gatewayapiv1.HTTPRoute{}, annotationName, annotationVal) +} + +func testGatewayHasDirectBackReference(gwKey client.ObjectKey, annotationName, annotationVal string) func() bool { + return testNetworkResourceHasDirectBackReference(gwKey, &gatewayapiv1.Gateway{}, annotationName, annotationVal) +} + +func testNetworkResourceHasDirectBackReference(objKey client.ObjectKey, obj client.Object, annotationName, annotationVal string) func() bool { + return func() bool { + err := k8sClient.Get(context.Background(), objKey, obj) + if err != nil { + logf.Log.V(1).Info("object not read", "object", objKey, + "kind", obj.GetObjectKind().GroupVersionKind(), "error", err) + return false + } + + val, ok := obj.GetAnnotations()[annotationName] + if !ok { + logf.Log.V(1).Info("object does not have the direct ref annotation", + "object", objKey, "kind", obj.GetObjectKind().GroupVersionKind()) + return false + } + + if val != annotationVal { + logf.Log.V(1).Info("object direct ref annotation value does not match", + "object", objKey, "kind", obj.GetObjectKind().GroupVersionKind(), + "val", val) + return false + } + + return true + } +} + +func testObjectDoesNotExist(obj client.Object) func() bool { + return func() bool { + err := testClient().Get(context.Background(), client.ObjectKeyFromObject(obj), obj) + if err != nil && apierrors.IsNotFound(err) { + return true + } + + logf.Log.V(1).Info("object not deleted", "object", client.ObjectKeyFromObject(obj), + "kind", obj.GetObjectKind().GroupVersionKind()) + return false + } +} + // DNS func testBuildManagedZone(name, ns, domainName string) *kuadrantdnsv1alpha1.ManagedZone { diff --git a/controllers/ratelimitpolicy_controller_test.go b/controllers/ratelimitpolicy_controller_test.go index d83d48811..cbe924d25 100644 --- a/controllers/ratelimitpolicy_controller_test.go +++ b/controllers/ratelimitpolicy_controller_test.go @@ -394,6 +394,425 @@ var _ = Describe("RateLimitPolicy controller", func() { time.Minute, 5*time.Second).Should(BeTrue()) }) }) + + Context("When RLP switches target from one HTTPRoute to another HTTPRoute", func() { + var ( + routeAName = "route-a" + routeBName = "route-b" + ) + + It("direct references are updated", func() { + // Initial state + // Route A + // RLP A -> Route A + + // Switch target to another route + // Route A + // Route B + // RLP A -> Route B + + // create httproute A + httpRouteA := testBuildBasicHttpRoute(routeAName, gwName, testNamespace, []string{"*.a.example.com"}) + err := k8sClient.Create(context.Background(), httpRouteA) + Expect(err).ToNot(HaveOccurred()) + Eventually(testRouteIsAccepted(client.ObjectKeyFromObject(httpRouteA)), time.Minute, 5*time.Second).Should(BeTrue()) + + // create ratelimitpolicy + rlp := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) { + policy.Spec.TargetRef.Kind = "HTTPRoute" + policy.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeAName) + }) + err = k8sClient.Create(context.Background(), rlp) + Expect(err).ToNot(HaveOccurred()) + + // Check RLP status is available + rlpKey := client.ObjectKeyFromObject(rlp) + Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute A direct back reference + routeAKey := client.ObjectKey{Name: routeAName, Namespace: testNamespace} + Eventually( + testHTTPRouteHasDirectBackReference( + routeAKey, rlp.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlp).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Proceed with the update: + // From RLP A -> Route A + // To RLP A -> Route B + + // create httproute B + httpRouteB := testBuildBasicHttpRoute(routeBName, gwName, testNamespace, []string{"*.b.example.com"}) + err = k8sClient.Create(context.Background(), httpRouteB) + Expect(err).ToNot(HaveOccurred()) + Eventually(testRouteIsAccepted(client.ObjectKeyFromObject(httpRouteB)), time.Minute, 5*time.Second).Should(BeTrue()) + + rlpUpdated := &kuadrantv1beta2.RateLimitPolicy{} + err = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(rlp), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + rlpUpdated.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeBName) + err = k8sClient.Update(context.Background(), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + // Check RLP status is available + Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute A direct back reference is gone + Eventually( + testHTTPRouteWithoutDirectBackReference(routeAKey, rlp.DirectReferenceAnnotationName()), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute B direct back reference + routeBKey := client.ObjectKey{Name: routeBName, Namespace: testNamespace} + Eventually( + testHTTPRouteHasDirectBackReference( + routeBKey, rlp.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlp).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + }) + }) + + Context("When RLP switches target from one Gateway to another Gateway", func() { + var ( + gwAName = "gw-a" + gwBName = "gw-b" + ) + + It("direct references are updated", func() { + // Initial state + // Gw A + // RLP A -> Gw A + + // Switch target to another gw + // Gw A + // Gw B + // RLP A -> Gw B + + // create Gw A + gatewayA := testBuildBasicGateway(gwAName, testNamespace) + err := k8sClient.Create(context.Background(), gatewayA) + Expect(err).ToNot(HaveOccurred()) + Eventually(testGatewayIsReady(gatewayA), 30*time.Second, 5*time.Second).Should(BeTrue()) + + // create ratelimitpolicy + rlp := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) { + policy.Spec.TargetRef.Kind = "Gateway" + policy.Spec.TargetRef.Name = gatewayapiv1.ObjectName(gwAName) + }) + err = k8sClient.Create(context.Background(), rlp) + Expect(err).ToNot(HaveOccurred()) + + // Check RLP status is available + rlpKey := client.ObjectKeyFromObject(rlp) + Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check Gateway direct back reference + gwAKey := client.ObjectKey{Name: gwAName, Namespace: testNamespace} + Eventually( + testGatewayHasDirectBackReference( + gwAKey, rlp.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlp).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Proceed with the update: + // From RLP A -> Gw A + // To RLP A -> Gw B + + // create Gw B + gatewayB := testBuildBasicGateway(gwBName, testNamespace) + err = k8sClient.Create(context.Background(), gatewayB) + Expect(err).ToNot(HaveOccurred()) + Eventually(testGatewayIsReady(gatewayB), 30*time.Second, 5*time.Second).Should(BeTrue()) + + rlpUpdated := &kuadrantv1beta2.RateLimitPolicy{} + err = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(rlp), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + rlpUpdated.Spec.TargetRef.Name = gatewayapiv1.ObjectName(gwBName) + err = k8sClient.Update(context.Background(), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + // Check RLP status is available + Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check Gw A direct back reference is gone + Eventually( + testGatewayWithoutDirectBackReference(gwAKey, rlp.DirectReferenceAnnotationName()), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Check Gateway B direct back reference + gwBKey := client.ObjectKey{Name: gwBName, Namespace: testNamespace} + Eventually( + testGatewayHasDirectBackReference( + gwBKey, rlp.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlp).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + }) + }) + + Context("When RLP switches target from one HTTPRoute to another taken HTTPRoute", func() { + var ( + routeAName = "route-a" + routeBName = "route-b" + rlpAName = "rlp-a" + rlpBName = "rlp-b" + ) + + It("direct references are updated", func() { + // Initial state + // Route A + // Route B + // RLP A -> Route A + // RLP B -> Route B + + // Switch target to another route + // Route A + // Route B + // RLP A -> Route B + // RLP B -> Route B + + // create httproute A + httpRouteA := testBuildBasicHttpRoute(routeAName, gwName, testNamespace, []string{"*.a.example.com"}) + err := k8sClient.Create(context.Background(), httpRouteA) + Expect(err).ToNot(HaveOccurred()) + Eventually(testRouteIsAccepted(client.ObjectKeyFromObject(httpRouteA)), time.Minute, 5*time.Second).Should(BeTrue()) + + // create httproute B + httpRouteB := testBuildBasicHttpRoute(routeBName, gwName, testNamespace, []string{"*.b.example.com"}) + err = k8sClient.Create(context.Background(), httpRouteB) + Expect(err).ToNot(HaveOccurred()) + Eventually(testRouteIsAccepted(client.ObjectKeyFromObject(httpRouteB)), time.Minute, 5*time.Second).Should(BeTrue()) + + // create rlpA + rlpA := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) { + policy.ObjectMeta.Name = rlpAName + policy.Spec.TargetRef.Kind = "HTTPRoute" + policy.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeAName) + }) + err = k8sClient.Create(context.Background(), rlpA) + Expect(err).ToNot(HaveOccurred()) + + rlpAKey := client.ObjectKeyFromObject(rlpA) + Eventually(testRLPIsAccepted(rlpAKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // create rlpB + rlpB := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) { + policy.ObjectMeta.Name = rlpBName + policy.Spec.TargetRef.Kind = "HTTPRoute" + policy.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeBName) + }) + err = k8sClient.Create(context.Background(), rlpB) + Expect(err).ToNot(HaveOccurred()) + + // Check RLP status is available + rlpBKey := client.ObjectKeyFromObject(rlpB) + Eventually(testRLPIsAccepted(rlpBKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute A direct back reference + routeAKey := client.ObjectKey{Name: routeAName, Namespace: testNamespace} + Eventually( + testHTTPRouteHasDirectBackReference( + routeAKey, rlpA.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlpA).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute B direct back reference + routeBKey := client.ObjectKey{Name: routeBName, Namespace: testNamespace} + Eventually( + testHTTPRouteHasDirectBackReference( + routeBKey, rlpB.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlpB).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Proceed with the update: + // From RLP A -> Route A + // To RLP A -> Route B (already taken) + + rlpUpdated := &kuadrantv1beta2.RateLimitPolicy{} + err = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(rlpA), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + rlpUpdated.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeBName) + err = k8sClient.Update(context.Background(), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + // Check RLP status is available + Eventually(testRLPIsNotAccepted(rlpAKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute A direct back reference is gone + Eventually( + testHTTPRouteWithoutDirectBackReference(routeAKey, rlpA.DirectReferenceAnnotationName()), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute B direct back reference + Eventually( + testHTTPRouteHasDirectBackReference( + routeBKey, rlpB.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlpB).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + }) + }) + + Context("When target is deleted", func() { + var ( + routeName = "route-a" + ) + + It("policy status reports error", func() { + // Initial state + // Route A + // RLP A -> Route A + + // Delete route + // RLP A + + // create httproute A + httpRoute := testBuildBasicHttpRoute(routeName, gwName, testNamespace, []string{"*.example.com"}) + err := k8sClient.Create(context.Background(), httpRoute) + Expect(err).ToNot(HaveOccurred()) + Eventually(testRouteIsAccepted(client.ObjectKeyFromObject(httpRoute)), time.Minute, 5*time.Second).Should(BeTrue()) + + // create rlp + rlp := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) { + policy.Spec.TargetRef.Kind = "HTTPRoute" + policy.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeName) + }) + err = k8sClient.Create(context.Background(), rlp) + Expect(err).ToNot(HaveOccurred()) + + rlpKey := client.ObjectKeyFromObject(rlp) + Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute A direct back reference + routeKey := client.ObjectKey{Name: routeName, Namespace: testNamespace} + Eventually( + testHTTPRouteHasDirectBackReference( + routeKey, rlp.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlp).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Proceed with the update: + // Delete Route A + err = k8sClient.Delete(context.Background(), httpRoute) + Expect(err).ToNot(HaveOccurred()) + Eventually(testObjectDoesNotExist(httpRoute), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check RLP status is available + Eventually(testRLPIsNotAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue()) + }) + }) + + Context("When RLP targets already taken HTTPRoute and the route is being released", func() { + var ( + routeAName = "route-a" + routeBName = "route-b" + rlpAName = "rlp-a" + rlpBName = "rlp-b" + ) + + It("direct references are updated and RLP status is ready", func() { + // Initial state + // Route A + // RLP A -> Route A + + // New RLP targets already taken route + // Route A + // RLP A -> Route A + // RLP B -> Route A (already taken) + + // already taken route is being released by owner policy + // Route A + // Route B + // RLP A -> Route B + // RLP B -> Route A + + // create httproute A + httpRouteA := testBuildBasicHttpRoute(routeAName, gwName, testNamespace, []string{"*.a.example.com"}) + err := k8sClient.Create(context.Background(), httpRouteA) + Expect(err).ToNot(HaveOccurred()) + Eventually(testRouteIsAccepted(client.ObjectKeyFromObject(httpRouteA)), time.Minute, 5*time.Second).Should(BeTrue()) + + // create rlpA + rlpA := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) { + policy.ObjectMeta.Name = rlpAName + policy.Spec.TargetRef.Kind = "HTTPRoute" + policy.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeAName) + }) + err = k8sClient.Create(context.Background(), rlpA) + Expect(err).ToNot(HaveOccurred()) + + rlpAKey := client.ObjectKeyFromObject(rlpA) + Eventually(testRLPIsAccepted(rlpAKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Proceed with the update: + // new RLP B -> Route A (already taken) + + // create rlpB + rlpB := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) { + policy.ObjectMeta.Name = rlpBName + policy.Spec.TargetRef.Kind = "HTTPRoute" + policy.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeAName) + }) + err = k8sClient.Create(context.Background(), rlpB) + Expect(err).ToNot(HaveOccurred()) + + // Check RLP status is not available + rlpBKey := client.ObjectKeyFromObject(rlpB) + Eventually(testRLPIsNotAccepted(rlpBKey), time.Minute, 5*time.Second).Should(BeTrue()) + + // Check HTTPRoute A direct back reference to RLP A + routeAKey := client.ObjectKey{Name: routeAName, Namespace: testNamespace} + Eventually( + testHTTPRouteHasDirectBackReference( + routeAKey, rlpA.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlpA).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + // Proceed with the update: + // new Route B + // RLP A -> Route B + // RLP A was the older owner of route A, and wiil be the new owner of route B + + // create httproute B + httpRouteB := testBuildBasicHttpRoute(routeBName, gwName, testNamespace, []string{"*.b.example.com"}) + err = k8sClient.Create(context.Background(), httpRouteB) + Expect(err).ToNot(HaveOccurred()) + Eventually(testRouteIsAccepted(client.ObjectKeyFromObject(httpRouteB)), time.Minute, 5*time.Second).Should(BeTrue()) + + // RLP A -> Route B + rlpUpdated := &kuadrantv1beta2.RateLimitPolicy{} + err = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(rlpA), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + rlpUpdated.Spec.TargetRef.Name = gatewayapiv1.ObjectName(routeBName) + err = k8sClient.Update(context.Background(), rlpUpdated) + Expect(err).ToNot(HaveOccurred()) + + // Check HTTPRoute A direct back reference to RLP B + Eventually( + testHTTPRouteHasDirectBackReference( + routeAKey, rlpB.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlpB).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + Eventually(testRLPIsAccepted(rlpBKey), time.Minute, 5*time.Second).Should(BeTrue()) + + routeBKey := client.ObjectKey{Name: routeBName, Namespace: testNamespace} + // Check HTTPRoute B direct back reference to RLP A + Eventually( + testHTTPRouteHasDirectBackReference( + routeBKey, rlpA.DirectReferenceAnnotationName(), + client.ObjectKeyFromObject(rlpA).String(), + ), + time.Minute, 5*time.Second).Should(BeTrue()) + + Eventually(testRLPIsAccepted(rlpAKey), time.Minute, 5*time.Second).Should(BeTrue()) + }) + }) }) var _ = Describe("RateLimitPolicy CEL Validations", func() { diff --git a/pkg/library/reconcilers/target_ref_reconciler.go b/pkg/library/reconcilers/target_ref_reconciler.go index e09283b4f..29ca96111 100644 --- a/pkg/library/reconcilers/target_ref_reconciler.go +++ b/pkg/library/reconcilers/target_ref_reconciler.go @@ -92,7 +92,7 @@ func (r *TargetRefReconciler) TargetedGatewayKeys(_ context.Context, targetNetwo } } -// ReconcileTargetBackReference adds policy key in annotations of the target object +// ReconcileTargetBackReference reconciles policy key in annotations of the target object func (r *TargetRefReconciler) ReconcileTargetBackReference(ctx context.Context, p kuadrant.Policy, targetNetworkObject client.Object, annotationName string) error { logger, _ := logr.FromContext(ctx) @@ -100,14 +100,63 @@ func (r *TargetRefReconciler) ReconcileTargetBackReference(ctx context.Context, targetNetworkObjectKey := client.ObjectKeyFromObject(targetNetworkObject) targetNetworkObjectKind := targetNetworkObject.GetObjectKind().GroupVersionKind() - // Reconcile the back reference: + // Step 1 Build list of network objects in the same namespace as the policy + // Step 2 Remove the direct back reference annotation to the current policy from any network object not being currently referenced + // Step 3 Check direct back ref annotation from the current target network object + // Step 3.1 if it does not exit -> create it + // Step 3.2 if it already exits and the reference is the current policy -> nothing to do + // Step 3.3 if it already exits and the reference is not the current policy -> return err + + // Step 1 + gwList := &gatewayapiv1.GatewayList{} + err := r.Client.List(ctx, gwList, client.InNamespace(p.GetNamespace())) + logger.V(1).Info("ReconcileTargetBackReference: list gateways", "#Gateways", len(gwList.Items), "err", err) + if err != nil { + return err + } + + routeList := &gatewayapiv1.HTTPRouteList{} + err = r.Client.List(ctx, routeList, client.InNamespace(p.GetNamespace())) + logger.V(1).Info("ReconcileTargetBackReference: list httproutes", "#HTTPRoutes", len(routeList.Items), "err", err) + if err != nil { + return err + } + + networkObjectList := utils.Map(gwList.Items, func(g gatewayapiv1.Gateway) client.Object { return &g }) + networkObjectList = append(networkObjectList, utils.Map(routeList.Items, func(g gatewayapiv1.HTTPRoute) client.Object { return &g })...) + // remove currently targeted network resource from the list + networkObjectList = utils.Filter(networkObjectList, func(obj client.Object) bool { + return targetNetworkObjectKey != client.ObjectKeyFromObject(obj) + }) + + // Step 2 + for _, networkObject := range networkObjectList { + annotations := networkObject.GetAnnotations() + if val, ok := annotations[annotationName]; ok && val == policyKey.String() { + delete(annotations, annotationName) + networkObject.SetAnnotations(annotations) + err := r.Client.Update(ctx, networkObject) + logger.V(1).Info("ReconcileTargetBackReference: update network resource", + "kind", networkObject.GetObjectKind().GroupVersionKind(), + "name", client.ObjectKeyFromObject(networkObject), "err", err) + if err != nil { + return err + } + } + } + + // Step 3 objAnnotations := utils.ReadAnnotationsFromObject(targetNetworkObject) if val, ok := objAnnotations[annotationName]; ok { if val != policyKey.String() { + // Step 3.3 return kuadrant.NewErrConflict(p.Kind(), val, fmt.Errorf("the %s target %s is already referenced by policy %s", targetNetworkObjectKind, targetNetworkObjectKey, val)) } + // Step 3.2 + // NO OP } else { + // Step 3.1 objAnnotations[annotationName] = policyKey.String() targetNetworkObject.SetAnnotations(objAnnotations) err := r.Client.Update(ctx, targetNetworkObject)