open-cluster-management.io/governance-policy-propagator@v0.13.0/test/e2e/case9_templates_test.go (about)

     1  // Copyright (c) 2021 Red Hat, Inc.
     2  // Copyright Contributors to the Open Cluster Management project
     3  
     4  package e2e
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	. "github.com/onsi/ginkgo/v2"
    11  	. "github.com/onsi/gomega"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    14  	"k8s.io/apimachinery/pkg/types"
    15  
    16  	"open-cluster-management.io/governance-policy-propagator/test/utils"
    17  )
    18  
    19  var _ = Describe("Test policy templates", func() {
    20  	const (
    21  		case9PolicyName                   = "case9-test-policy"
    22  		case9PathPrefix                   = "../resources/case9_templates/"
    23  		case9PolicyYaml                   = case9PathPrefix + "case9-test-policy.yaml"
    24  		case9ReplicatedPolicyYamlM1       = case9PathPrefix + "case9-test-replpolicy-managed1.yaml"
    25  		case9ReplicatedPolicyYamlM1Update = case9PathPrefix + "case9-test-replpolicy-managed1-relabelled.yaml"
    26  		case9ReplicatedPolicyYamlM2       = case9PathPrefix + "case9-test-replpolicy-managed2.yaml"
    27  		case9PolicyNameEncrypted          = "case9-test-policy-encrypted"
    28  		case9PolicyYamlEncrypted          = case9PathPrefix + "case9-test-policy_encrypted.yaml"
    29  		case9PolicyYamlEncryptedRepl      = case9PathPrefix + "case9-test-replpolicy_encrypted-"
    30  		case9EncryptionSecret             = case9PathPrefix + "case9-test-encryption-secret.yaml"
    31  		case9EncryptionSecretName         = "policy-encryption-key"
    32  		case9SecretName                   = "case9-secret"
    33  		case9PolicyNameCopy               = "case9-test-policy-copy"
    34  		case9PolicyYamlCopy               = case9PathPrefix + "case9-test-policy_copy.yaml"
    35  		case9PolicyYamlCopiedRepl         = case9PathPrefix + "case9-test-replpolicy_copied-"
    36  		case9PolicyWithCSLookupName       = "case9-test-policy-cslookup"
    37  		case9PolicyWithCSLookupYaml       = case9PathPrefix + "case9-test-policy-cslookup.yaml"
    38  	)
    39  
    40  	Describe("Create policy, placement and referenced resource in ns:"+testNamespace, Ordered, func() {
    41  		It("should be created in user ns", func() {
    42  			By("Creating " + case9PolicyYaml)
    43  			utils.Kubectl("apply",
    44  				"-f", case9PolicyYaml,
    45  				"-n", testNamespace,
    46  				"--kubeconfig="+kubeconfigHub)
    47  			plc := utils.GetWithTimeout(
    48  				clientHubDynamic, gvrPolicy, case9PolicyName, testNamespace, true, defaultTimeoutSeconds,
    49  			)
    50  			Expect(plc).NotTo(BeNil())
    51  		})
    52  		It("should resolve templates and propagate to cluster ns managed1", func() {
    53  			By("Patching test-policy-plr with decision of cluster managed1")
    54  			plr := utils.GetWithTimeout(
    55  				clientHubDynamic, gvrPlacementRule, case9PolicyName+"-plr", testNamespace,
    56  				true, defaultTimeoutSeconds,
    57  			)
    58  			plr.Object["status"] = utils.GeneratePlrStatus("managed1")
    59  			_, err := clientHubDynamic.Resource(gvrPlacementRule).Namespace(testNamespace).UpdateStatus(
    60  				context.TODO(), plr, metav1.UpdateOptions{},
    61  			)
    62  			Expect(err).ToNot(HaveOccurred())
    63  			plc := utils.GetWithTimeout(
    64  				clientHubDynamic, gvrPolicy, testNamespace+"."+case9PolicyName, "managed1",
    65  				true, defaultTimeoutSeconds,
    66  			)
    67  			Expect(plc).ToNot(BeNil())
    68  
    69  			yamlPlc := utils.ParseYaml(case9ReplicatedPolicyYamlM1)
    70  			Eventually(func(g Gomega) interface{} {
    71  				replicatedPlc := utils.GetWithTimeout(
    72  					clientHubDynamic,
    73  					gvrPolicy,
    74  					testNamespace+"."+case9PolicyName,
    75  					"managed1",
    76  					true,
    77  					defaultTimeoutSeconds,
    78  				)
    79  
    80  				err := utils.RemovePolicyTemplateDBAnnotations(replicatedPlc)
    81  				g.Expect(err).ToNot(HaveOccurred())
    82  
    83  				return replicatedPlc.Object["spec"]
    84  			}, defaultTimeoutSeconds, 1).Should(utils.SemanticEqual(yamlPlc.Object["spec"]))
    85  		})
    86  		It("should update the templated value when the managed cluster labels change", func() {
    87  			By("Updating the label on managed1")
    88  			utils.Kubectl("label", "managedcluster", "managed1",
    89  				"vendor=Fake", "--overwrite", "--kubeconfig="+kubeconfigHub)
    90  
    91  			By("Verifying the policy is updated")
    92  			yamlPlc := utils.ParseYaml(case9ReplicatedPolicyYamlM1Update)
    93  			Eventually(func(g Gomega) interface{} {
    94  				replicatedPlc := utils.GetWithTimeout(
    95  					clientHubDynamic,
    96  					gvrPolicy,
    97  					testNamespace+"."+case9PolicyName,
    98  					"managed1",
    99  					true,
   100  					defaultTimeoutSeconds,
   101  				)
   102  
   103  				err := utils.RemovePolicyTemplateDBAnnotations(replicatedPlc)
   104  				g.Expect(err).ToNot(HaveOccurred())
   105  
   106  				return replicatedPlc.Object["spec"]
   107  			}, defaultTimeoutSeconds, 1).Should(utils.SemanticEqual(yamlPlc.Object["spec"]))
   108  		})
   109  		It("should resolve templates and propagate to cluster ns managed2", func() {
   110  			By("Patching test-policy-plr with decision of cluster managed2")
   111  			plr := utils.GetWithTimeout(
   112  				clientHubDynamic, gvrPlacementRule, case9PolicyName+"-plr", testNamespace,
   113  				true, defaultTimeoutSeconds,
   114  			)
   115  			plr.Object["status"] = utils.GeneratePlrStatus("managed2")
   116  			_, err := clientHubDynamic.Resource(gvrPlacementRule).Namespace(testNamespace).UpdateStatus(
   117  				context.TODO(), plr, metav1.UpdateOptions{},
   118  			)
   119  			Expect(err).ToNot(HaveOccurred())
   120  			plc := utils.GetWithTimeout(
   121  				clientHubDynamic, gvrPolicy, testNamespace+"."+case9PolicyName, "managed2",
   122  				true, defaultTimeoutSeconds,
   123  			)
   124  			Expect(plc).ToNot(BeNil())
   125  
   126  			yamlPlc := utils.ParseYaml(case9ReplicatedPolicyYamlM2)
   127  			Eventually(func(g Gomega) interface{} {
   128  				replicatedPlc := utils.GetWithTimeout(
   129  					clientHubDynamic,
   130  					gvrPolicy,
   131  					testNamespace+"."+case9PolicyName,
   132  					"managed2",
   133  					true,
   134  					defaultTimeoutSeconds,
   135  				)
   136  
   137  				err := utils.RemovePolicyTemplateDBAnnotations(replicatedPlc)
   138  				g.Expect(err).ToNot(HaveOccurred())
   139  
   140  				return replicatedPlc.Object["spec"]
   141  			}, defaultTimeoutSeconds, 1).Should(utils.SemanticEqual(yamlPlc.Object["spec"]))
   142  		})
   143  		AfterAll(func() {
   144  			utils.Kubectl("delete",
   145  				"-f", case9PolicyYaml,
   146  				"-n", testNamespace,
   147  				"--kubeconfig="+kubeconfigHub,
   148  				"--ignore-not-found")
   149  			utils.Kubectl("label", "managedcluster", "managed1",
   150  				"vendor=auto-detect", "--overwrite", "--kubeconfig="+kubeconfigHub)
   151  			opt := metav1.ListOptions{}
   152  			utils.ListWithTimeout(clientHubDynamic, gvrPolicy, opt, 0, false, defaultTimeoutSeconds)
   153  		})
   154  	})
   155  
   156  	Describe("Test encrypted policy templates", Ordered, func() {
   157  		for i := 1; i <= 2; i++ {
   158  			managedCluster := "managed" + fmt.Sprint(i)
   159  
   160  			It("should be created in user ns", func() {
   161  				By("Creating " + case9PolicyYamlEncrypted)
   162  				utils.Kubectl("apply",
   163  					"-f", case9PolicyYamlEncrypted,
   164  					"-n", testNamespace,
   165  					"--kubeconfig="+kubeconfigHub)
   166  				plc := utils.GetWithTimeout(
   167  					clientHubDynamic, gvrPolicy, case9PolicyNameEncrypted, testNamespace,
   168  					true, defaultTimeoutSeconds,
   169  				)
   170  				Expect(plc).NotTo(BeNil())
   171  			})
   172  
   173  			It("should resolve templates and propagate to cluster ns "+managedCluster, func() {
   174  				By("Initializing AES Encryption Secret")
   175  				_, err := utils.KubectlWithOutput("apply",
   176  					"-f", case9EncryptionSecret,
   177  					"-n", managedCluster,
   178  					"--kubeconfig="+kubeconfigHub)
   179  				Expect(err).ToNot(HaveOccurred())
   180  
   181  				By("Patching test-policy-plr with decision of cluster " + managedCluster)
   182  				plr := utils.GetWithTimeout(
   183  					clientHubDynamic, gvrPlacementRule, case9PolicyNameEncrypted+"-plr", testNamespace,
   184  					true, defaultTimeoutSeconds,
   185  				)
   186  				plr.Object["status"] = utils.GeneratePlrStatus(managedCluster)
   187  				_, err = clientHubDynamic.Resource(gvrPlacementRule).Namespace(testNamespace).UpdateStatus(
   188  					context.TODO(), plr, metav1.UpdateOptions{},
   189  				)
   190  				Expect(err).ToNot(HaveOccurred())
   191  
   192  				var replicatedPlc *unstructured.Unstructured
   193  				By("Waiting for encrypted values")
   194  				Eventually(func() interface{} {
   195  					replicatedPlc = utils.GetWithTimeout(
   196  						clientHubDynamic,
   197  						gvrPolicy,
   198  						testNamespace+"."+case9PolicyNameEncrypted,
   199  						managedCluster,
   200  						true,
   201  						defaultTimeoutSeconds,
   202  					)
   203  
   204  					return fmt.Sprint(replicatedPlc.Object["spec"])
   205  				}, defaultTimeoutSeconds, 1).Should(ContainSubstring("$ocm_encrypted:"))
   206  
   207  				By("Patching the initialization vector with a static value")
   208  				// Setting Initialization Vector so that the test results will be deterministic
   209  				initializationVector := "7cznVUq5SXEE4RMZNkGOrQ=="
   210  				annotations := replicatedPlc.GetAnnotations()
   211  				annotations[IVAnnotation] = initializationVector
   212  				replicatedPlc.SetAnnotations(annotations)
   213  				_, err = clientHubDynamic.Resource(gvrPolicy).Namespace(managedCluster).Update(
   214  					context.TODO(), replicatedPlc, metav1.UpdateOptions{},
   215  				)
   216  				Expect(err).ToNot(HaveOccurred())
   217  
   218  				By("Verifying the replicated policy against a snapshot")
   219  				yamlPlc := utils.ParseYaml(case9PolicyYamlEncryptedRepl + managedCluster + ".yaml")
   220  				Eventually(func(g Gomega) interface{} {
   221  					replicatedPlc = utils.GetWithTimeout(
   222  						clientHubDynamic,
   223  						gvrPolicy,
   224  						testNamespace+"."+case9PolicyNameEncrypted,
   225  						managedCluster,
   226  						true,
   227  						defaultTimeoutSeconds,
   228  					)
   229  
   230  					err := utils.RemovePolicyTemplateDBAnnotations(replicatedPlc)
   231  					g.Expect(err).ToNot(HaveOccurred())
   232  
   233  					return replicatedPlc.Object["spec"]
   234  				}, defaultTimeoutSeconds, 1).Should(utils.SemanticEqual(yamlPlc.Object["spec"]))
   235  			})
   236  
   237  			It("should reconcile when the secret referenced in the template is updated", func() {
   238  				By("Updating the secret " + case9SecretName)
   239  				newToken := "THVrZS4gSSBhbSB5b3VyIGZhdGhlci4="
   240  				patch := []byte(`{"data": {"token": "` + newToken + `"}}`)
   241  				_, err := clientHub.CoreV1().Secrets(testNamespace).Patch(
   242  					context.TODO(), case9SecretName, types.StrategicMergePatchType, patch, metav1.PatchOptions{},
   243  				)
   244  				Expect(err).ToNot(HaveOccurred())
   245  
   246  				By("Verifying the replicated policy was updated")
   247  				expected := "$ocm_encrypted:dbHPzG98PxV7RXcAx25mMGPBAUbfjJTEMyFc7kE2W7U3FW5+X31LkidHu/25ic4m"
   248  				Eventually(func() string {
   249  					replicatedPlc := utils.GetWithTimeout(
   250  						clientHubDynamic,
   251  						gvrPolicy,
   252  						testNamespace+"."+case9PolicyNameEncrypted,
   253  						managedCluster,
   254  						true,
   255  						defaultTimeoutSeconds,
   256  					)
   257  
   258  					templates, _, _ := unstructured.NestedSlice(replicatedPlc.Object, "spec", "policy-templates")
   259  					if len(templates) < 1 {
   260  						return ""
   261  					}
   262  
   263  					template, ok := templates[0].(map[string]interface{})
   264  					if !ok {
   265  						return ""
   266  					}
   267  
   268  					objectTemplates, _, _ := unstructured.NestedSlice(
   269  						template, "objectDefinition", "spec", "object-templates",
   270  					)
   271  					if len(objectTemplates) < 1 {
   272  						return ""
   273  					}
   274  
   275  					objectTemplate, ok := objectTemplates[0].(map[string]interface{})
   276  					if !ok {
   277  						return ""
   278  					}
   279  
   280  					secretValue, _, _ := unstructured.NestedString(
   281  						objectTemplate, "objectDefinition", "data", "someTopSecretThing",
   282  					)
   283  
   284  					return secretValue
   285  				}, defaultTimeoutSeconds, 1).Should(Equal(expected))
   286  			})
   287  
   288  			It("should clean up the encryption key", func() {
   289  				utils.Kubectl("delete", "secret",
   290  					case9EncryptionSecretName,
   291  					"-n", managedCluster,
   292  					"--kubeconfig="+kubeconfigHub)
   293  				utils.GetWithTimeout(
   294  					clientHubDynamic, gvrSecret, case9EncryptionSecretName, managedCluster,
   295  					false, defaultTimeoutSeconds,
   296  				)
   297  			})
   298  
   299  			It("should clean up", func() {
   300  				utils.Kubectl("delete", "-f", case9PolicyYamlEncrypted,
   301  					"-n", testNamespace, "--kubeconfig="+kubeconfigHub)
   302  				opt := metav1.ListOptions{}
   303  				utils.ListWithTimeout(clientHubDynamic, gvrPolicy, opt, 0, false, defaultTimeoutSeconds)
   304  			})
   305  		}
   306  	})
   307  
   308  	Describe("Test encrypted policy templates with secret copy", Ordered, func() {
   309  		for i := 1; i <= 2; i++ {
   310  			managedCluster := "managed" + fmt.Sprint(i)
   311  
   312  			It("should be created in user ns", func() {
   313  				By("Creating " + case9PolicyYamlCopy)
   314  				utils.Kubectl("apply",
   315  					"-f", case9PolicyYamlCopy,
   316  					"-n", testNamespace,
   317  					"--kubeconfig="+kubeconfigHub)
   318  				plc := utils.GetWithTimeout(
   319  					clientHubDynamic, gvrPolicy, case9PolicyNameCopy, testNamespace,
   320  					true, defaultTimeoutSeconds,
   321  				)
   322  				Expect(plc).NotTo(BeNil())
   323  			})
   324  
   325  			It("should resolve templates and propagate to cluster ns "+managedCluster, func() {
   326  				By("Initializing AES Encryption Secret")
   327  				_, err := utils.KubectlWithOutput("apply",
   328  					"-f", case9EncryptionSecret,
   329  					"-n", managedCluster,
   330  					"--kubeconfig="+kubeconfigHub)
   331  				Expect(err).ToNot(HaveOccurred())
   332  
   333  				By("Patching test-policy-plr with decision of cluster " + managedCluster)
   334  				plr := utils.GetWithTimeout(
   335  					clientHubDynamic, gvrPlacementRule, case9PolicyNameCopy+"-plr", testNamespace,
   336  					true, defaultTimeoutSeconds,
   337  				)
   338  				plr.Object["status"] = utils.GeneratePlrStatus(managedCluster)
   339  				_, err = clientHubDynamic.Resource(gvrPlacementRule).Namespace(testNamespace).UpdateStatus(
   340  					context.TODO(), plr, metav1.UpdateOptions{},
   341  				)
   342  				Expect(err).ToNot(HaveOccurred())
   343  
   344  				var replicatedPlc *unstructured.Unstructured
   345  				By("Waiting for encrypted values")
   346  				Eventually(func() interface{} {
   347  					replicatedPlc = utils.GetWithTimeout(
   348  						clientHubDynamic,
   349  						gvrPolicy,
   350  						testNamespace+"."+case9PolicyNameCopy,
   351  						managedCluster,
   352  						true,
   353  						defaultTimeoutSeconds,
   354  					)
   355  
   356  					return fmt.Sprint(replicatedPlc.Object["spec"])
   357  				}, defaultTimeoutSeconds, 1).Should(ContainSubstring("$ocm_encrypted:"))
   358  
   359  				By("Patching the initialization vector with a static value")
   360  				// Setting Initialization Vector so that the test results will be deterministic
   361  				initializationVector := "7cznVUq5SXEE4RMZNkGOrQ=="
   362  				annotations := replicatedPlc.GetAnnotations()
   363  				annotations[IVAnnotation] = initializationVector
   364  				replicatedPlc.SetAnnotations(annotations)
   365  				_, err = clientHubDynamic.Resource(gvrPolicy).Namespace(managedCluster).Update(
   366  					context.TODO(), replicatedPlc, metav1.UpdateOptions{},
   367  				)
   368  				Expect(err).ToNot(HaveOccurred())
   369  
   370  				By("Verifying the replicated policy against a snapshot")
   371  				yamlPlc := utils.ParseYaml(case9PolicyYamlCopiedRepl + managedCluster + ".yaml")
   372  				Eventually(func(g Gomega) interface{} {
   373  					replicatedPlc = utils.GetWithTimeout(
   374  						clientHubDynamic,
   375  						gvrPolicy,
   376  						testNamespace+"."+case9PolicyNameCopy,
   377  						managedCluster,
   378  						true,
   379  						defaultTimeoutSeconds,
   380  					)
   381  
   382  					err := utils.RemovePolicyTemplateDBAnnotations(replicatedPlc)
   383  					g.Expect(err).ToNot(HaveOccurred())
   384  
   385  					return replicatedPlc.Object["spec"]
   386  				}, defaultTimeoutSeconds, 1).Should(utils.SemanticEqual(yamlPlc.Object["spec"]))
   387  			})
   388  
   389  			It("should reconcile when the secret referenced in the template is updated", func() {
   390  				By("Updating the secret " + case9SecretName)
   391  				newToken := "THVrZS4gSSBhbSB5b3VyIGZhdGhlci4="
   392  				patch := []byte(`{"data": {"token": "` + newToken + `"}}`)
   393  				_, err := clientHub.CoreV1().Secrets(testNamespace).Patch(
   394  					context.TODO(), case9SecretName, types.StrategicMergePatchType, patch, metav1.PatchOptions{},
   395  				)
   396  				Expect(err).ToNot(HaveOccurred())
   397  
   398  				By("Verifying the replicated policy was updated")
   399  				expected := "$ocm_encrypted:dbHPzG98PxV7RXcAx25mMGPBAUbfjJTEMyFc7kE2W7U3FW5+X31LkidHu/25ic4m"
   400  				Eventually(func() string {
   401  					replicatedPlc := utils.GetWithTimeout(
   402  						clientHubDynamic,
   403  						gvrPolicy,
   404  						testNamespace+"."+case9PolicyNameCopy,
   405  						managedCluster,
   406  						true,
   407  						defaultTimeoutSeconds,
   408  					)
   409  
   410  					templates, _, _ := unstructured.NestedSlice(replicatedPlc.Object, "spec", "policy-templates")
   411  					if len(templates) < 1 {
   412  						return ""
   413  					}
   414  
   415  					template, ok := templates[0].(map[string]interface{})
   416  					if !ok {
   417  						return ""
   418  					}
   419  
   420  					objectTemplates, _, _ := unstructured.NestedSlice(
   421  						template, "objectDefinition", "spec", "object-templates",
   422  					)
   423  					if len(objectTemplates) < 1 {
   424  						return ""
   425  					}
   426  
   427  					objectTemplate, ok := objectTemplates[0].(map[string]interface{})
   428  					if !ok {
   429  						return ""
   430  					}
   431  
   432  					secretValue, _, _ := unstructured.NestedString(
   433  						objectTemplate, "objectDefinition", "data", "token",
   434  					)
   435  
   436  					return secretValue
   437  				}, defaultTimeoutSeconds, 1).Should(Equal(expected))
   438  			})
   439  
   440  			It("should clean up the encryption key", func() {
   441  				utils.Kubectl("delete", "secret",
   442  					case9EncryptionSecretName,
   443  					"-n", managedCluster,
   444  					"--kubeconfig="+kubeconfigHub)
   445  				utils.GetWithTimeout(
   446  					clientHubDynamic, gvrSecret, case9EncryptionSecretName, managedCluster,
   447  					false, defaultTimeoutSeconds,
   448  				)
   449  			})
   450  
   451  			It("should clean up", func() {
   452  				utils.Kubectl("delete", "-f", case9PolicyYamlCopy,
   453  					"-n", testNamespace,
   454  					"--kubeconfig="+kubeconfigHub)
   455  				opt := metav1.ListOptions{}
   456  				utils.ListWithTimeout(clientHubDynamic, gvrPolicy, opt, 0, false, defaultTimeoutSeconds)
   457  			})
   458  			AfterAll(func() {
   459  				utils.Kubectl("delete", "secret",
   460  					case9EncryptionSecretName,
   461  					"-n", managedCluster,
   462  					"--kubeconfig="+kubeconfigHub)
   463  				utils.Kubectl("delete", "-f", case9PolicyYamlCopy,
   464  					"-n", testNamespace,
   465  					"--kubeconfig="+kubeconfigHub)
   466  			})
   467  		}
   468  	})
   469  
   470  	Describe("Test policy templates with cluster-scoped lookup", Ordered, func() {
   471  		It("should be created in user ns", func() {
   472  			By("Creating " + case9PolicyWithCSLookupName)
   473  			utils.Kubectl("apply",
   474  				"-f", case9PolicyWithCSLookupYaml,
   475  				"-n", testNamespace,
   476  				"--kubeconfig="+kubeconfigHub)
   477  			plc := utils.GetWithTimeout(
   478  				clientHubDynamic, gvrPolicy, case9PolicyWithCSLookupName, testNamespace, true, defaultTimeoutSeconds,
   479  			)
   480  			Expect(plc).NotTo(BeNil())
   481  		})
   482  		It("should resolve templates and propagate to cluster ns managed1", func() {
   483  			By("Patching test-policy-plr with decision of cluster managed1")
   484  			plr := utils.GetWithTimeout(
   485  				clientHubDynamic, gvrPlacementRule, case9PolicyWithCSLookupName+"-plr", testNamespace,
   486  				true, defaultTimeoutSeconds,
   487  			)
   488  			plr.Object["status"] = utils.GeneratePlrStatus("managed1")
   489  			_, err := clientHubDynamic.Resource(gvrPlacementRule).Namespace(testNamespace).UpdateStatus(
   490  				context.TODO(), plr, metav1.UpdateOptions{},
   491  			)
   492  			Expect(err).ToNot(HaveOccurred())
   493  			plc := utils.GetWithTimeout(
   494  				clientHubDynamic, gvrPolicy, testNamespace+"."+case9PolicyWithCSLookupName, "managed1",
   495  				true, defaultTimeoutSeconds,
   496  			)
   497  			Expect(plc).NotTo(BeNil())
   498  
   499  			By("Verifying the replicated policy was created with the correct error annotation in the template")
   500  			tmpls, _, _ := unstructured.NestedSlice(plc.Object, "spec", "policy-templates")
   501  			Expect(tmpls).To(HaveLen(1))
   502  
   503  			tmplAnnotations, _, _ := unstructured.NestedStringMap(tmpls[0].(map[string]interface{}),
   504  				"objectDefinition", "metadata", "annotations")
   505  			Expect(tmplAnnotations).ToNot(BeEmpty())
   506  
   507  			hubTmplErrAnnotation := tmplAnnotations["policy.open-cluster-management.io/hub-templates-error"]
   508  			Expect(hubTmplErrAnnotation).To(ContainSubstring("error calling lookup"))
   509  		})
   510  		AfterAll(func() {
   511  			utils.Kubectl("delete",
   512  				"-f", case9PolicyWithCSLookupYaml,
   513  				"-n", testNamespace,
   514  				"--kubeconfig="+kubeconfigHub,
   515  				"--ignore-not-found")
   516  			opt := metav1.ListOptions{}
   517  			utils.ListWithTimeout(clientHubDynamic, gvrPolicy, opt, 0, false, defaultTimeoutSeconds)
   518  		})
   519  	})
   520  })