sigs.k8s.io/cluster-api@v1.7.1/exp/addons/internal/controllers/clusterresourceset_controller_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"crypto/sha1" //nolint: gosec
    21  	"fmt"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	. "github.com/onsi/gomega"
    27  	"github.com/pkg/errors"
    28  	corev1 "k8s.io/api/core/v1"
    29  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"sigs.k8s.io/controller-runtime/pkg/client"
    32  	"sigs.k8s.io/yaml"
    33  
    34  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    35  	addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1"
    36  	"sigs.k8s.io/cluster-api/internal/test/envtest"
    37  	"sigs.k8s.io/cluster-api/util"
    38  	"sigs.k8s.io/cluster-api/util/conditions"
    39  )
    40  
    41  const (
    42  	timeout = time.Second * 15
    43  )
    44  
    45  func TestClusterResourceSetReconciler(t *testing.T) {
    46  	var (
    47  		clusterResourceSetName      string
    48  		testCluster                 *clusterv1.Cluster
    49  		clusterName                 string
    50  		labels                      map[string]string
    51  		configmapName               = "test-configmap"
    52  		secretName                  = "test-secret"
    53  		namespacePrefix             = "test-cluster-resource-set"
    54  		resourceConfigMap1Name      = "resource-configmap"
    55  		resourceConfigMap2Name      = "resource-configmap-2"
    56  		resourceConfigMapsNamespace = "default"
    57  	)
    58  
    59  	setup := func(t *testing.T, g *WithT) *corev1.Namespace {
    60  		t.Helper()
    61  
    62  		clusterResourceSetName = fmt.Sprintf("clusterresourceset-%s", util.RandomString(6))
    63  		labels = map[string]string{clusterResourceSetName: "bar"}
    64  
    65  		ns, err := env.CreateNamespace(ctx, namespacePrefix)
    66  		g.Expect(err).ToNot(HaveOccurred())
    67  
    68  		clusterName = fmt.Sprintf("cluster-%s", util.RandomString(6))
    69  		testCluster = &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: clusterName, Namespace: ns.Name}}
    70  
    71  		t.Log("Creating the Cluster")
    72  		g.Expect(env.Create(ctx, testCluster)).To(Succeed())
    73  		t.Log("Creating the remote Cluster kubeconfig")
    74  		g.Expect(env.CreateKubeconfigSecret(ctx, testCluster)).To(Succeed())
    75  
    76  		resourceConfigMap1Content := fmt.Sprintf(`metadata:
    77   name: %s
    78   namespace: %s
    79  kind: ConfigMap
    80  apiVersion: v1`, resourceConfigMap1Name, resourceConfigMapsNamespace)
    81  
    82  		testConfigmap := &corev1.ConfigMap{
    83  			ObjectMeta: metav1.ObjectMeta{
    84  				Name:      configmapName,
    85  				Namespace: ns.Name,
    86  			},
    87  			Data: map[string]string{
    88  				"cm": resourceConfigMap1Content,
    89  			},
    90  		}
    91  
    92  		resourceConfigMap2Content := fmt.Sprintf(`metadata:
    93  kind: ConfigMap
    94  apiVersion: v1
    95  metadata:
    96   name: %s
    97   namespace: %s`, resourceConfigMap2Name, resourceConfigMapsNamespace)
    98  		testSecret := &corev1.Secret{
    99  			ObjectMeta: metav1.ObjectMeta{
   100  				Name:      secretName,
   101  				Namespace: ns.Name,
   102  			},
   103  			Type: "addons.cluster.x-k8s.io/resource-set",
   104  			StringData: map[string]string{
   105  				"cm": resourceConfigMap2Content,
   106  			},
   107  		}
   108  		t.Log("Creating a Secret and a ConfigMap with ConfigMap in their data field")
   109  		g.Expect(env.Create(ctx, testConfigmap)).To(Succeed())
   110  		g.Expect(env.Create(ctx, testSecret)).To(Succeed())
   111  
   112  		return ns
   113  	}
   114  
   115  	teardown := func(t *testing.T, g *WithT, ns *corev1.Namespace) {
   116  		t.Helper()
   117  
   118  		t.Log("Deleting the Kubeconfigsecret")
   119  		secret := &corev1.Secret{
   120  			ObjectMeta: metav1.ObjectMeta{
   121  				Name:      clusterName + "-kubeconfig",
   122  				Namespace: ns.Name,
   123  			},
   124  		}
   125  		g.Expect(env.Delete(ctx, secret)).To(Succeed())
   126  
   127  		clusterResourceSetInstance := &addonsv1.ClusterResourceSet{
   128  			ObjectMeta: metav1.ObjectMeta{
   129  				Name:      clusterResourceSetName,
   130  				Namespace: ns.Name,
   131  			},
   132  		}
   133  
   134  		err := env.Get(ctx, client.ObjectKey{Namespace: clusterResourceSetInstance.Namespace, Name: clusterResourceSetInstance.Name}, clusterResourceSetInstance)
   135  		if err == nil {
   136  			g.Expect(env.Delete(ctx, clusterResourceSetInstance)).To(Succeed())
   137  		}
   138  
   139  		g.Eventually(func() bool {
   140  			crsKey := client.ObjectKey{
   141  				Namespace: clusterResourceSetInstance.Namespace,
   142  				Name:      clusterResourceSetInstance.Name,
   143  			}
   144  			crs := &addonsv1.ClusterResourceSet{}
   145  			err := env.Get(ctx, crsKey, crs)
   146  			return err != nil
   147  		}, timeout).Should(BeTrue())
   148  
   149  		g.Expect(env.Delete(ctx, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{
   150  			Name:      configmapName,
   151  			Namespace: ns.Name,
   152  		}})).To(Succeed())
   153  		g.Expect(env.Delete(ctx, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{
   154  			Name:      secretName,
   155  			Namespace: ns.Name,
   156  		}})).To(Succeed())
   157  
   158  		cm1 := &corev1.ConfigMap{
   159  			ObjectMeta: metav1.ObjectMeta{
   160  				Name:      resourceConfigMap1Name,
   161  				Namespace: resourceConfigMapsNamespace,
   162  			},
   163  		}
   164  		if err = env.Get(ctx, client.ObjectKeyFromObject(cm1), cm1); err == nil {
   165  			g.Expect(env.Delete(ctx, cm1)).To(Succeed())
   166  		}
   167  		cm2 := &corev1.ConfigMap{
   168  			ObjectMeta: metav1.ObjectMeta{
   169  				Name:      resourceConfigMap2Name,
   170  				Namespace: resourceConfigMapsNamespace,
   171  			},
   172  		}
   173  		if err = env.Get(ctx, client.ObjectKeyFromObject(cm2), cm2); err == nil {
   174  			g.Expect(env.Delete(ctx, cm2)).To(Succeed())
   175  		}
   176  
   177  		g.Expect(env.Delete(ctx, ns)).To(Succeed())
   178  
   179  		clusterKey := client.ObjectKey{Namespace: testCluster.Namespace, Name: testCluster.Name}
   180  		if err = env.Get(ctx, clusterKey, testCluster); err == nil {
   181  			g.Expect(env.Delete(ctx, testCluster)).To(Succeed())
   182  		}
   183  	}
   184  
   185  	t.Run("Should reconcile a ClusterResourceSet with multiple resources when a cluster with matching label exists", func(t *testing.T) {
   186  		g := NewWithT(t)
   187  		ns := setup(t, g)
   188  		defer teardown(t, g, ns)
   189  
   190  		t.Log("Updating the cluster with labels")
   191  		testCluster.SetLabels(labels)
   192  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   193  
   194  		t.Log("Creating a ClusterResourceSet instance that has same labels as selector")
   195  		clusterResourceSetInstance := &addonsv1.ClusterResourceSet{
   196  			ObjectMeta: metav1.ObjectMeta{
   197  				Name:      clusterResourceSetName,
   198  				Namespace: ns.Name,
   199  			},
   200  			Spec: addonsv1.ClusterResourceSetSpec{
   201  				ClusterSelector: metav1.LabelSelector{
   202  					MatchLabels: labels,
   203  				},
   204  				Resources: []addonsv1.ResourceRef{{Name: configmapName, Kind: "ConfigMap"}, {Name: secretName, Kind: "Secret"}},
   205  			},
   206  		}
   207  		// Create the ClusterResourceSet.
   208  		g.Expect(env.Create(ctx, clusterResourceSetInstance)).To(Succeed())
   209  
   210  		t.Log("Verifying ClusterResourceSetBinding is created with cluster owner reference")
   211  		g.Eventually(clusterResourceSetBindingReady(env, testCluster), timeout).Should(BeTrue())
   212  		t.Log("Deleting the Cluster")
   213  		g.Expect(env.Delete(ctx, testCluster)).To(Succeed())
   214  	})
   215  
   216  	t.Run("Should reconcile a cluster when its labels are changed to match a ClusterResourceSet's selector", func(t *testing.T) {
   217  		g := NewWithT(t)
   218  		ns := setup(t, g)
   219  		defer teardown(t, g, ns)
   220  
   221  		clusterResourceSetInstance := &addonsv1.ClusterResourceSet{
   222  			ObjectMeta: metav1.ObjectMeta{
   223  				Name:      clusterResourceSetName,
   224  				Namespace: ns.Name,
   225  			},
   226  			Spec: addonsv1.ClusterResourceSetSpec{
   227  				ClusterSelector: metav1.LabelSelector{
   228  					MatchLabels: labels,
   229  				},
   230  			},
   231  		}
   232  		// Create the ClusterResourceSet.
   233  		g.Expect(env.Create(ctx, clusterResourceSetInstance)).To(Succeed())
   234  
   235  		testCluster.SetLabels(labels)
   236  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   237  
   238  		t.Log("Verifying ClusterResourceSetBinding is created with cluster name")
   239  		g.Eventually(func() bool {
   240  			binding := &addonsv1.ClusterResourceSetBinding{}
   241  			clusterResourceSetBindingKey := client.ObjectKey{
   242  				Namespace: testCluster.Namespace,
   243  				Name:      testCluster.Name,
   244  			}
   245  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   246  			if err != nil {
   247  				return false
   248  			}
   249  
   250  			return binding.Spec.ClusterName == testCluster.Name
   251  		}, timeout).Should(BeTrue())
   252  
   253  		// Wait until ClusterResourceSetBinding is created for the Cluster
   254  		clusterResourceSetBindingKey := client.ObjectKey{
   255  			Namespace: testCluster.Namespace,
   256  			Name:      testCluster.Name,
   257  		}
   258  		g.Eventually(func() bool {
   259  			binding := &addonsv1.ClusterResourceSetBinding{}
   260  
   261  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   262  			return err == nil
   263  		}, timeout).Should(BeTrue())
   264  
   265  		t.Log("Verifying ClusterResourceSetBinding is deleted when its cluster owner reference is deleted")
   266  		g.Expect(env.Delete(ctx, testCluster)).To(Succeed())
   267  
   268  		g.Eventually(func() bool {
   269  			binding := &addonsv1.ClusterResourceSetBinding{}
   270  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   271  			return apierrors.IsNotFound(err)
   272  		}, timeout).Should(BeTrue())
   273  	})
   274  
   275  	t.Run("Should reconcile a ClusterResourceSet when a ConfigMap resource is created that is part of ClusterResourceSet resources", func(t *testing.T) {
   276  		g := NewWithT(t)
   277  		ns := setup(t, g)
   278  		defer teardown(t, g, ns)
   279  
   280  		newCMName := fmt.Sprintf("test-configmap-%s", util.RandomString(6))
   281  
   282  		crsInstance := &addonsv1.ClusterResourceSet{
   283  			ObjectMeta: metav1.ObjectMeta{
   284  				Name:      clusterResourceSetName,
   285  				Namespace: ns.Name,
   286  			},
   287  			Spec: addonsv1.ClusterResourceSetSpec{
   288  				ClusterSelector: metav1.LabelSelector{
   289  					MatchLabels: labels,
   290  				},
   291  				Resources: []addonsv1.ResourceRef{{Name: newCMName, Kind: "ConfigMap"}},
   292  			},
   293  		}
   294  		// Create the ClusterResourceSet.
   295  		g.Expect(env.Create(ctx, crsInstance)).To(Succeed())
   296  
   297  		testCluster.SetLabels(labels)
   298  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   299  
   300  		t.Log("Verifying ClusterResourceSetBinding is created with cluster owner reference")
   301  		// Wait until ClusterResourceSetBinding is created for the Cluster
   302  		clusterResourceSetBindingKey := client.ObjectKey{
   303  			Namespace: testCluster.Namespace,
   304  			Name:      testCluster.Name,
   305  		}
   306  		g.Eventually(func() bool {
   307  			binding := &addonsv1.ClusterResourceSetBinding{}
   308  
   309  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   310  			return err == nil
   311  		}, timeout).Should(BeTrue())
   312  
   313  		// Initially ConfigMap is missing, so no resources in the binding.
   314  		g.Eventually(func() bool {
   315  			binding := &addonsv1.ClusterResourceSetBinding{}
   316  
   317  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   318  			if err == nil {
   319  				if len(binding.Spec.Bindings) > 0 && len(binding.Spec.Bindings[0].Resources) == 0 {
   320  					return true
   321  				}
   322  			}
   323  			return false
   324  		}, timeout).Should(BeTrue())
   325  
   326  		// Must sleep here to make sure resource is created after the previous reconcile.
   327  		// If the resource is created in between, predicates are not used as intended in this test.
   328  		time.Sleep(2 * time.Second)
   329  
   330  		newConfigmap := &corev1.ConfigMap{
   331  			ObjectMeta: metav1.ObjectMeta{
   332  				Name:      newCMName,
   333  				Namespace: ns.Name,
   334  			},
   335  			Data: map[string]string{},
   336  		}
   337  		g.Expect(env.Create(ctx, newConfigmap)).To(Succeed())
   338  		defer func() {
   339  			g.Expect(env.Delete(ctx, newConfigmap)).To(Succeed())
   340  		}()
   341  
   342  		cmKey := client.ObjectKey{
   343  			Namespace: ns.Name,
   344  			Name:      newCMName,
   345  		}
   346  		g.Eventually(func() bool {
   347  			m := &corev1.ConfigMap{}
   348  			err := env.Get(ctx, cmKey, m)
   349  			return err == nil
   350  		}, timeout).Should(BeTrue())
   351  
   352  		// When the ConfigMap resource is created, CRS should get reconciled immediately.
   353  		g.Eventually(func() error {
   354  			binding := &addonsv1.ClusterResourceSetBinding{}
   355  			if err := env.Get(ctx, clusterResourceSetBindingKey, binding); err != nil {
   356  				return err
   357  			}
   358  			if len(binding.Spec.Bindings[0].Resources) > 0 && binding.Spec.Bindings[0].Resources[0].Name == newCMName {
   359  				return nil
   360  			}
   361  			return errors.Errorf("ClusterResourceSet binding does not have any resources matching %q: %v", newCMName, binding.Spec.Bindings)
   362  		}, timeout).Should(Succeed())
   363  
   364  		t.Log("Verifying ClusterResourceSetBinding is deleted when its cluster owner reference is deleted")
   365  		g.Expect(env.Delete(ctx, testCluster)).To(Succeed())
   366  
   367  		g.Eventually(func() bool {
   368  			binding := &addonsv1.ClusterResourceSetBinding{}
   369  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   370  			return apierrors.IsNotFound(err)
   371  		}, timeout).Should(BeTrue())
   372  	})
   373  
   374  	t.Run("Should reconcile a ClusterResourceSet when a Secret resource is created that is part of ClusterResourceSet resources", func(t *testing.T) {
   375  		g := NewWithT(t)
   376  		ns := setup(t, g)
   377  		defer teardown(t, g, ns)
   378  
   379  		newSecretName := fmt.Sprintf("test-secret-%s", util.RandomString(6))
   380  
   381  		crsInstance := &addonsv1.ClusterResourceSet{
   382  			ObjectMeta: metav1.ObjectMeta{
   383  				Name:      clusterResourceSetName,
   384  				Namespace: ns.Name,
   385  			},
   386  			Spec: addonsv1.ClusterResourceSetSpec{
   387  				ClusterSelector: metav1.LabelSelector{
   388  					MatchLabels: labels,
   389  				},
   390  				Resources: []addonsv1.ResourceRef{{Name: newSecretName, Kind: "Secret"}},
   391  			},
   392  		}
   393  		// Create the ClusterResourceSet.
   394  		g.Expect(env.Create(ctx, crsInstance)).To(Succeed())
   395  
   396  		testCluster.SetLabels(labels)
   397  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   398  
   399  		// Must sleep here to make sure resource is created after the previous reconcile.
   400  		// If the resource is created in between, predicates are not used as intended in this test.
   401  		time.Sleep(2 * time.Second)
   402  
   403  		t.Log("Verifying ClusterResourceSetBinding is created with cluster owner reference")
   404  		// Wait until ClusterResourceSetBinding is created for the Cluster
   405  		clusterResourceSetBindingKey := client.ObjectKey{
   406  			Namespace: testCluster.Namespace,
   407  			Name:      testCluster.Name,
   408  		}
   409  		g.Eventually(func() bool {
   410  			binding := &addonsv1.ClusterResourceSetBinding{}
   411  
   412  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   413  			return err == nil
   414  		}, timeout).Should(BeTrue())
   415  
   416  		// Initially Secret is missing, so no resources in the binding.
   417  		g.Eventually(func() bool {
   418  			binding := &addonsv1.ClusterResourceSetBinding{}
   419  
   420  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   421  			if err == nil {
   422  				if len(binding.Spec.Bindings) > 0 && len(binding.Spec.Bindings[0].Resources) == 0 {
   423  					return true
   424  				}
   425  			}
   426  			return false
   427  		}, timeout).Should(BeTrue())
   428  
   429  		newSecret := &corev1.Secret{
   430  			ObjectMeta: metav1.ObjectMeta{
   431  				Name:      newSecretName,
   432  				Namespace: ns.Name,
   433  			},
   434  			Type: addonsv1.ClusterResourceSetSecretType,
   435  			Data: map[string][]byte{},
   436  		}
   437  		g.Expect(env.Create(ctx, newSecret)).To(Succeed())
   438  		defer func() {
   439  			g.Expect(env.Delete(ctx, newSecret)).To(Succeed())
   440  		}()
   441  
   442  		cmKey := client.ObjectKey{
   443  			Namespace: ns.Name,
   444  			Name:      newSecretName,
   445  		}
   446  		g.Eventually(func() bool {
   447  			m := &corev1.Secret{}
   448  			err := env.Get(ctx, cmKey, m)
   449  			return err == nil
   450  		}, timeout).Should(BeTrue())
   451  
   452  		// When the Secret resource is created, CRS should get reconciled immediately.
   453  		g.Eventually(func() error {
   454  			binding := &addonsv1.ClusterResourceSetBinding{}
   455  			if err := env.Get(ctx, clusterResourceSetBindingKey, binding); err != nil {
   456  				return err
   457  			}
   458  			if len(binding.Spec.Bindings[0].Resources) > 0 && binding.Spec.Bindings[0].Resources[0].Name == newSecretName {
   459  				return nil
   460  			}
   461  			return errors.Errorf("ClusterResourceSet binding does not have any resources matching %q: %v", newSecretName, binding.Spec.Bindings)
   462  		}, timeout).Should(Succeed())
   463  
   464  		t.Log("Verifying ClusterResourceSetBinding is deleted when its cluster owner reference is deleted")
   465  		g.Expect(env.Delete(ctx, testCluster)).To(Succeed())
   466  
   467  		g.Eventually(func() bool {
   468  			binding := &addonsv1.ClusterResourceSetBinding{}
   469  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   470  			return apierrors.IsNotFound(err)
   471  		}, timeout).Should(BeTrue())
   472  	})
   473  
   474  	t.Run("Should delete ClusterResourceSet from the bindings list when ClusterResourceSet is deleted", func(t *testing.T) {
   475  		g := NewWithT(t)
   476  		ns := setup(t, g)
   477  		defer teardown(t, g, ns)
   478  
   479  		t.Log("Updating the cluster with labels")
   480  		testCluster.SetLabels(labels)
   481  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   482  
   483  		t.Log("Creating a ClusterResourceSet instance that has same labels as selector")
   484  		clusterResourceSetInstance2 := &addonsv1.ClusterResourceSet{
   485  			ObjectMeta: metav1.ObjectMeta{
   486  				Name:      clusterResourceSetName,
   487  				Namespace: ns.Name,
   488  			},
   489  			Spec: addonsv1.ClusterResourceSetSpec{
   490  				ClusterSelector: metav1.LabelSelector{
   491  					MatchLabels: labels,
   492  				},
   493  				Resources: []addonsv1.ResourceRef{{Name: configmapName, Kind: "ConfigMap"}},
   494  			},
   495  		}
   496  		// Create the ClusterResourceSet.
   497  		g.Expect(env.Create(ctx, clusterResourceSetInstance2)).To(Succeed())
   498  
   499  		t.Log("Creating a second ClusterResourceSet instance that has same labels as selector")
   500  		clusterResourceSetInstance3 := &addonsv1.ClusterResourceSet{
   501  			ObjectMeta: metav1.ObjectMeta{
   502  				Name:      "test-clusterresourceset2",
   503  				Namespace: ns.Name,
   504  			},
   505  			Spec: addonsv1.ClusterResourceSetSpec{
   506  				ClusterSelector: metav1.LabelSelector{
   507  					MatchLabels: labels,
   508  				},
   509  				Resources: []addonsv1.ResourceRef{{Name: configmapName, Kind: "ConfigMap"}, {Name: secretName, Kind: "Secret"}},
   510  			},
   511  		}
   512  		// Create the ClusterResourceSet.
   513  		g.Expect(env.Create(ctx, clusterResourceSetInstance3)).To(Succeed())
   514  
   515  		t.Log("Verifying ClusterResourceSetBinding is created with 2 ClusterResourceSets")
   516  		g.Eventually(func() bool {
   517  			binding := &addonsv1.ClusterResourceSetBinding{}
   518  			clusterResourceSetBindingKey := client.ObjectKey{
   519  				Namespace: testCluster.Namespace,
   520  				Name:      testCluster.Name,
   521  			}
   522  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   523  			if err != nil {
   524  				return false
   525  			}
   526  			return len(binding.Spec.Bindings) == 2 && len(binding.OwnerReferences) == 2
   527  		}, timeout).Should(BeTrue(), "Expected 2 ClusterResourceSets and 2 OwnerReferences")
   528  
   529  		t.Log("Verifying deleted CRS is deleted from ClusterResourceSetBinding")
   530  		// Delete one of the CRS instances and wait until it is removed from the binding list.
   531  		g.Expect(env.Delete(ctx, clusterResourceSetInstance2)).To(Succeed())
   532  		g.Eventually(func() bool {
   533  			binding := &addonsv1.ClusterResourceSetBinding{}
   534  			clusterResourceSetBindingKey := client.ObjectKey{
   535  				Namespace: testCluster.Namespace,
   536  				Name:      testCluster.Name,
   537  			}
   538  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   539  			if err != nil {
   540  				return false
   541  			}
   542  			return len(binding.Spec.Bindings) == 1 && len(binding.OwnerReferences) == 1
   543  		}, timeout).Should(BeTrue(), "ClusterResourceSetBinding should have 1 ClusterResourceSet and 1 OwnerReferences")
   544  
   545  		t.Log("Verifying ClusterResourceSetBinding is deleted after deleting all matching CRS objects")
   546  		// Delete one of the CRS instances and wait until it is removed from the binding list.
   547  		g.Expect(env.Delete(ctx, clusterResourceSetInstance3)).To(Succeed())
   548  		g.Eventually(func() bool {
   549  			binding := &addonsv1.ClusterResourceSetBinding{}
   550  			clusterResourceSetBindingKey := client.ObjectKey{
   551  				Namespace: testCluster.Namespace,
   552  				Name:      testCluster.Name,
   553  			}
   554  			return env.Get(ctx, clusterResourceSetBindingKey, binding) != nil
   555  		}, timeout).Should(BeTrue())
   556  
   557  		t.Log("Deleting the Cluster")
   558  		g.Expect(env.Delete(ctx, testCluster)).To(Succeed())
   559  	})
   560  
   561  	t.Run("Should add finalizer after reconcile", func(t *testing.T) {
   562  		g := NewWithT(t)
   563  		ns := setup(t, g)
   564  		defer teardown(t, g, ns)
   565  
   566  		dt := metav1.Now()
   567  		clusterResourceSetInstance := &addonsv1.ClusterResourceSet{
   568  			ObjectMeta: metav1.ObjectMeta{
   569  				Name:              clusterResourceSetName,
   570  				Namespace:         ns.Name,
   571  				Finalizers:        []string{addonsv1.ClusterResourceSetFinalizer},
   572  				DeletionTimestamp: &dt,
   573  			},
   574  			Spec: addonsv1.ClusterResourceSetSpec{
   575  				ClusterSelector: metav1.LabelSelector{
   576  					MatchLabels: labels,
   577  				},
   578  			},
   579  		}
   580  		// Create the ClusterResourceSet.
   581  		g.Expect(env.Create(ctx, clusterResourceSetInstance)).To(Succeed())
   582  		g.Eventually(func() bool {
   583  			crsKey := client.ObjectKey{
   584  				Namespace: clusterResourceSetInstance.Namespace,
   585  				Name:      clusterResourceSetInstance.Name,
   586  			}
   587  			crs := &addonsv1.ClusterResourceSet{}
   588  
   589  			err := env.Get(ctx, crsKey, crs)
   590  			if err == nil {
   591  				return len(crs.Finalizers) > 0
   592  			}
   593  			return false
   594  		}, timeout).Should(BeTrue())
   595  	})
   596  
   597  	t.Run("Should reconcile a ClusterResourceSet with Reconcile strategy after the resources have already been created", func(t *testing.T) {
   598  		g := NewWithT(t)
   599  		ns := setup(t, g)
   600  		defer teardown(t, g, ns)
   601  
   602  		t.Log("Updating the cluster with labels")
   603  		testCluster.SetLabels(labels)
   604  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   605  
   606  		t.Log("Creating a ClusterResourceSet instance that has same labels as selector")
   607  		clusterResourceSet := &addonsv1.ClusterResourceSet{
   608  			ObjectMeta: metav1.ObjectMeta{
   609  				Name:      clusterResourceSetName,
   610  				Namespace: ns.Name,
   611  			},
   612  			Spec: addonsv1.ClusterResourceSetSpec{
   613  				Strategy: string(addonsv1.ClusterResourceSetStrategyReconcile),
   614  				ClusterSelector: metav1.LabelSelector{
   615  					MatchLabels: labels,
   616  				},
   617  				Resources: []addonsv1.ResourceRef{{Name: configmapName, Kind: "ConfigMap"}, {Name: secretName, Kind: "Secret"}},
   618  			},
   619  		}
   620  
   621  		g.Expect(env.Create(ctx, clusterResourceSet)).To(Succeed())
   622  
   623  		t.Log("Verifying ClusterResourceSetBinding is created with cluster owner reference")
   624  		clusterResourceSetBindingKey := client.ObjectKey{
   625  			Namespace: testCluster.Namespace,
   626  			Name:      testCluster.Name,
   627  		}
   628  		g.Eventually(clusterResourceSetBindingReady(env, testCluster), timeout).Should(BeTrue())
   629  
   630  		binding := &addonsv1.ClusterResourceSetBinding{}
   631  		err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   632  		g.Expect(err).ToNot(HaveOccurred())
   633  		resourceHashes := map[string]string{}
   634  		for _, r := range binding.Spec.Bindings[0].Resources {
   635  			resourceHashes[r.Name] = r.Hash
   636  		}
   637  
   638  		t.Log("Verifying resource ConfigMap 1 has been created")
   639  		resourceConfigMap1Key := client.ObjectKey{
   640  			Namespace: resourceConfigMapsNamespace,
   641  			Name:      resourceConfigMap1Name,
   642  		}
   643  		g.Eventually(func() error {
   644  			cm := &corev1.ConfigMap{}
   645  			return env.Get(ctx, resourceConfigMap1Key, cm)
   646  		}, timeout).Should(Succeed())
   647  
   648  		t.Log("Verifying resource ConfigMap 2 has been created")
   649  		resourceConfigMap2Key := client.ObjectKey{
   650  			Namespace: resourceConfigMapsNamespace,
   651  			Name:      resourceConfigMap2Name,
   652  		}
   653  		g.Eventually(func() error {
   654  			cm := &corev1.ConfigMap{}
   655  			return env.Get(ctx, resourceConfigMap2Key, cm)
   656  		}, timeout).Should(Succeed())
   657  
   658  		resourceConfigMap1 := configMap(
   659  			resourceConfigMap1Name,
   660  			resourceConfigMapsNamespace,
   661  			map[string]string{
   662  				"my_new_config": "some_value",
   663  			},
   664  		)
   665  
   666  		resourceConfigMap1Content, err := yaml.Marshal(resourceConfigMap1)
   667  		g.Expect(err).ToNot(HaveOccurred())
   668  
   669  		testConfigmap := configMap(
   670  			configmapName,
   671  			ns.Name,
   672  			map[string]string{
   673  				"cm": string(resourceConfigMap1Content),
   674  			},
   675  		)
   676  
   677  		resourceConfigMap2 := configMap(
   678  			resourceConfigMap2Name,
   679  			resourceConfigMapsNamespace,
   680  			map[string]string{
   681  				"my_new_secret_config": "some_secret_value",
   682  			},
   683  		)
   684  
   685  		resourceConfigMap2Content, err := yaml.Marshal(resourceConfigMap2)
   686  		g.Expect(err).ToNot(HaveOccurred())
   687  
   688  		testSecret := &corev1.Secret{
   689  			ObjectMeta: metav1.ObjectMeta{
   690  				Name:      secretName,
   691  				Namespace: ns.Name,
   692  			},
   693  			Type: "addons.cluster.x-k8s.io/resource-set",
   694  			StringData: map[string]string{
   695  				"cm": string(resourceConfigMap2Content),
   696  			},
   697  		}
   698  		t.Log("Updating the Secret and a ConfigMap with updated ConfigMaps in their data field")
   699  		g.Expect(env.Update(ctx, testConfigmap)).To(Succeed())
   700  		g.Expect(env.Update(ctx, testSecret)).To(Succeed())
   701  
   702  		t.Log("Verifying ClusterResourceSetBinding has been updated with new hashes")
   703  		g.Eventually(func() error {
   704  			binding := &addonsv1.ClusterResourceSetBinding{}
   705  			if err := env.Get(ctx, clusterResourceSetBindingKey, binding); err != nil {
   706  				return err
   707  			}
   708  
   709  			for _, r := range binding.Spec.Bindings[0].Resources {
   710  				if resourceHashes[r.Name] == r.Hash {
   711  					return errors.Errorf("resource binding for %s hasn't been updated with new hash", r.Name)
   712  				}
   713  			}
   714  
   715  			return nil
   716  		}, timeout).Should(Succeed())
   717  
   718  		t.Log("Checking resource ConfigMap 1 has been updated")
   719  		g.Eventually(configMapHasBeenUpdated(env, resourceConfigMap1Key, resourceConfigMap1), timeout).Should(Succeed())
   720  
   721  		t.Log("Checking resource ConfigMap 2 has been updated")
   722  		g.Eventually(configMapHasBeenUpdated(env, resourceConfigMap2Key, resourceConfigMap2), timeout).Should(Succeed())
   723  	})
   724  
   725  	t.Run("Should reconcile a ClusterResourceSet with ApplyOnce strategy even when one of the resources already exist", func(t *testing.T) {
   726  		g := NewWithT(t)
   727  		ns := setup(t, g)
   728  		defer teardown(t, g, ns)
   729  
   730  		t.Log("Updating the cluster with labels")
   731  		testCluster.SetLabels(labels)
   732  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   733  
   734  		t.Log("Creating resource CM before creating CRS")
   735  		// This CM is defined in the data of "configmapName", which is included in the
   736  		// CRS we will create in this test
   737  		resourceConfigMap1 := configMap(
   738  			resourceConfigMap1Name,
   739  			resourceConfigMapsNamespace,
   740  			map[string]string{
   741  				"created": "before CRS",
   742  			},
   743  		)
   744  		g.Expect(env.Create(ctx, resourceConfigMap1)).To(Succeed())
   745  
   746  		t.Log("Creating a ClusterResourceSet instance that has same labels as selector")
   747  		clusterResourceSet := &addonsv1.ClusterResourceSet{
   748  			ObjectMeta: metav1.ObjectMeta{
   749  				Name:      clusterResourceSetName,
   750  				Namespace: ns.Name,
   751  			},
   752  			Spec: addonsv1.ClusterResourceSetSpec{
   753  				Strategy: string(addonsv1.ClusterResourceSetStrategyApplyOnce),
   754  				ClusterSelector: metav1.LabelSelector{
   755  					MatchLabels: labels,
   756  				},
   757  				Resources: []addonsv1.ResourceRef{{Name: configmapName, Kind: "ConfigMap"}, {Name: secretName, Kind: "Secret"}},
   758  			},
   759  		}
   760  
   761  		g.Expect(env.Create(ctx, clusterResourceSet)).To(Succeed())
   762  
   763  		t.Log("Checking resource ConfigMap 1 hasn't been updated")
   764  		resourceConfigMap1Key := client.ObjectKey{
   765  			Namespace: resourceConfigMapsNamespace,
   766  			Name:      resourceConfigMap1Name,
   767  		}
   768  		g.Eventually(configMapHasBeenUpdated(env, resourceConfigMap1Key, resourceConfigMap1), timeout).Should(Succeed())
   769  
   770  		t.Log("Verifying resource ConfigMap 2 has been created")
   771  		resourceConfigMap2Key := client.ObjectKey{
   772  			Namespace: resourceConfigMapsNamespace,
   773  			Name:      resourceConfigMap2Name,
   774  		}
   775  		g.Eventually(func() error {
   776  			cm := &corev1.ConfigMap{}
   777  			return env.Get(ctx, resourceConfigMap2Key, cm)
   778  		}, timeout).Should(Succeed())
   779  	})
   780  
   781  	t.Run("Should reconcile a ClusterResourceSet with ApplyOnce strategy even when there is an error, after the error has been corrected", func(t *testing.T) {
   782  		// To trigger an error in the middle of the reconciliation, we'll define an object in a namespace that doesn't yet exist.
   783  		// We'll expect the CRS to reconcile all other objects except that one and bubble up the error.
   784  		// Once that happens, we'll go ahead and create the namespace. Then we'll expect the CRS to, eventually, create that remaining object.
   785  
   786  		g := NewWithT(t)
   787  		ns := setup(t, g)
   788  		defer teardown(t, g, ns)
   789  
   790  		t.Log("Updating the cluster with labels")
   791  		testCluster.SetLabels(labels)
   792  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   793  
   794  		t.Log("Updating the test config map with the missing namespace resource")
   795  		missingNamespace := randomNamespaceForTest(t)
   796  
   797  		resourceConfigMap1 := configMap(
   798  			resourceConfigMap1Name,
   799  			resourceConfigMapsNamespace,
   800  			map[string]string{
   801  				"my_new_config": "some_value",
   802  			},
   803  		)
   804  
   805  		resourceConfigMap1Content, err := yaml.Marshal(resourceConfigMap1)
   806  		g.Expect(err).ToNot(HaveOccurred())
   807  
   808  		resourceConfigMapWithMissingNamespace := configMap(
   809  			"cm-missing-namespace",
   810  			missingNamespace,
   811  			map[string]string{
   812  				"my_new_config": "this is all new",
   813  			},
   814  		)
   815  
   816  		resourceConfigMapMissingNamespaceContent, err := yaml.Marshal(resourceConfigMapWithMissingNamespace)
   817  		g.Expect(err).ToNot(HaveOccurred())
   818  
   819  		testConfigmap := configMap(
   820  			configmapName,
   821  			ns.Name,
   822  			map[string]string{
   823  				"cm":             string(resourceConfigMap1Content),
   824  				"problematic_cm": string(resourceConfigMapMissingNamespaceContent),
   825  			},
   826  		)
   827  
   828  		g.Expect(env.Update(ctx, testConfigmap)).To(Succeed())
   829  
   830  		t.Log("Creating a ClusterResourceSet instance that has same labels as selector")
   831  		clusterResourceSet := &addonsv1.ClusterResourceSet{
   832  			ObjectMeta: metav1.ObjectMeta{
   833  				Name:      clusterResourceSetName,
   834  				Namespace: ns.Name,
   835  			},
   836  			Spec: addonsv1.ClusterResourceSetSpec{
   837  				Strategy: string(addonsv1.ClusterResourceSetStrategyApplyOnce),
   838  				ClusterSelector: metav1.LabelSelector{
   839  					MatchLabels: labels,
   840  				},
   841  				Resources: []addonsv1.ResourceRef{{Name: testConfigmap.Name, Kind: "ConfigMap"}, {Name: secretName, Kind: "Secret"}},
   842  			},
   843  		}
   844  
   845  		g.Expect(env.Create(ctx, clusterResourceSet)).To(Succeed())
   846  
   847  		t.Log("Verifying resource ConfigMap 1 has been created")
   848  		resourceConfigMap1Key := client.ObjectKeyFromObject(resourceConfigMap1)
   849  		g.Eventually(configMapHasBeenUpdated(env, resourceConfigMap1Key, resourceConfigMap1), timeout).Should(Succeed())
   850  
   851  		t.Log("Verifying resource ConfigMap 2 has been created")
   852  		resourceConfigMap2Key := client.ObjectKey{
   853  			Namespace: resourceConfigMapsNamespace,
   854  			Name:      resourceConfigMap2Name,
   855  		}
   856  		g.Eventually(func() error {
   857  			cm := &corev1.ConfigMap{}
   858  			return env.Get(ctx, resourceConfigMap2Key, cm)
   859  		}, timeout).Should(Succeed())
   860  
   861  		t.Log("Verifying CRS Binding failed marked the resource as not applied")
   862  		g.Eventually(func(g Gomega) {
   863  			clusterResourceSetBindingKey := client.ObjectKey{
   864  				Namespace: testCluster.Namespace,
   865  				Name:      testCluster.Name,
   866  			}
   867  			binding := &addonsv1.ClusterResourceSetBinding{}
   868  			g.Expect(env.Get(ctx, clusterResourceSetBindingKey, binding)).To(Succeed())
   869  
   870  			g.Expect(binding.Spec.Bindings).To(HaveLen(1))
   871  			g.Expect(binding.Spec.Bindings[0].Resources).To(HaveLen(2))
   872  
   873  			for _, r := range binding.Spec.Bindings[0].Resources {
   874  				switch r.ResourceRef.Name {
   875  				case testConfigmap.Name:
   876  					g.Expect(r.Applied).To(BeFalse(), "test-configmap should be not applied bc of missing namespace")
   877  				case secretName:
   878  					g.Expect(r.Applied).To(BeTrue(), "test-secret should be applied")
   879  				}
   880  			}
   881  		}, timeout).Should(Succeed())
   882  
   883  		t.Log("Verifying CRS has a false ResourcesApplied condition")
   884  		g.Eventually(func(g Gomega) {
   885  			clusterResourceSetKey := client.ObjectKeyFromObject(clusterResourceSet)
   886  			crs := &addonsv1.ClusterResourceSet{}
   887  			g.Expect(env.Get(ctx, clusterResourceSetKey, crs)).To(Succeed())
   888  
   889  			appliedCondition := conditions.Get(crs, addonsv1.ResourcesAppliedCondition)
   890  			g.Expect(appliedCondition).NotTo(BeNil())
   891  			g.Expect(appliedCondition.Status).To(Equal(corev1.ConditionFalse))
   892  			g.Expect(appliedCondition.Reason).To(Equal(addonsv1.ApplyFailedReason))
   893  			g.Expect(appliedCondition.Message).To(ContainSubstring("creating object /v1, Kind=ConfigMap %s/cm-missing-namespace", missingNamespace))
   894  		}, timeout).Should(Succeed())
   895  
   896  		t.Log("Creating missing namespace")
   897  		missingNs := &corev1.Namespace{
   898  			ObjectMeta: metav1.ObjectMeta{
   899  				Name: missingNamespace,
   900  			},
   901  		}
   902  		g.Expect(env.Create(ctx, missingNs)).To(Succeed())
   903  
   904  		t.Log("Verifying CRS Binding has all resources applied")
   905  		g.Eventually(clusterResourceSetBindingReady(env, testCluster), timeout).Should(BeTrue())
   906  
   907  		t.Log("Verifying resource ConfigMap with previously missing namespace has been created")
   908  		g.Eventually(configMapHasBeenUpdated(env, client.ObjectKeyFromObject(resourceConfigMapWithMissingNamespace), resourceConfigMapWithMissingNamespace), timeout).Should(Succeed())
   909  
   910  		g.Expect(env.Delete(ctx, resourceConfigMapWithMissingNamespace)).To(Succeed())
   911  		g.Expect(env.Delete(ctx, missingNs)).To(Succeed())
   912  	})
   913  
   914  	t.Run("Should only create ClusterResourceSetBinding after the remote cluster's Kubernetes API Server Service has been created", func(t *testing.T) {
   915  		g := NewWithT(t)
   916  		ns := setup(t, g)
   917  		defer teardown(t, g, ns)
   918  
   919  		fakeService := &corev1.Service{
   920  			TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: "v1"},
   921  			ObjectMeta: metav1.ObjectMeta{
   922  				Name:      "fake",
   923  				Namespace: metav1.NamespaceDefault,
   924  			},
   925  			Spec: corev1.ServiceSpec{
   926  				Ports: []corev1.ServicePort{
   927  					{
   928  						Name: "https",
   929  						Port: 443,
   930  					},
   931  				},
   932  				Type: "ClusterIP",
   933  			},
   934  		}
   935  
   936  		kubernetesAPIServerService := &corev1.Service{}
   937  		t.Log("Verifying Kubernetes API Server Service has been created")
   938  		g.Expect(env.Get(ctx, client.ObjectKey{Name: "kubernetes", Namespace: metav1.NamespaceDefault}, kubernetesAPIServerService)).To(Succeed())
   939  
   940  		fakeService.Spec.ClusterIP = kubernetesAPIServerService.Spec.ClusterIP
   941  
   942  		t.Log("Let Kubernetes API Server Service fail to create by occupying its IP")
   943  		g.Eventually(func() error {
   944  			err := env.Delete(ctx, kubernetesAPIServerService)
   945  			if err != nil {
   946  				return err
   947  			}
   948  			err = env.Create(ctx, fakeService)
   949  			if err != nil {
   950  				return err
   951  			}
   952  			return nil
   953  		}, timeout).Should(Succeed())
   954  		g.Expect(apierrors.IsNotFound(env.Get(ctx, client.ObjectKeyFromObject(kubernetesAPIServerService), &corev1.Service{}))).To(BeTrue())
   955  
   956  		clusterResourceSetInstance := &addonsv1.ClusterResourceSet{
   957  			ObjectMeta: metav1.ObjectMeta{
   958  				Name:      clusterResourceSetName,
   959  				Namespace: ns.Name,
   960  			},
   961  			Spec: addonsv1.ClusterResourceSetSpec{
   962  				ClusterSelector: metav1.LabelSelector{
   963  					MatchLabels: labels,
   964  				},
   965  			},
   966  		}
   967  		// Create the ClusterResourceSet.
   968  		g.Expect(env.Create(ctx, clusterResourceSetInstance)).To(Succeed())
   969  
   970  		testCluster.SetLabels(labels)
   971  		g.Expect(env.Update(ctx, testCluster)).To(Succeed())
   972  
   973  		// ClusterResourceSetBinding for the Cluster is not created because the Kubernetes API Server Service doesn't exist.
   974  		clusterResourceSetBindingKey := client.ObjectKey{Namespace: testCluster.Namespace, Name: testCluster.Name}
   975  		g.Consistently(func() bool {
   976  			binding := &addonsv1.ClusterResourceSetBinding{}
   977  
   978  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   979  			return apierrors.IsNotFound(err)
   980  		}, timeout).Should(BeTrue())
   981  
   982  		t.Log("Create Kubernetes API Server Service")
   983  		g.Expect(env.Delete(ctx, fakeService)).Should(Succeed())
   984  		kubernetesAPIServerService.ResourceVersion = ""
   985  		g.Expect(env.Create(ctx, kubernetesAPIServerService)).Should(Succeed())
   986  
   987  		// Label the CRS to trigger reconciliation.
   988  		labels["new"] = ""
   989  		clusterResourceSetInstance.SetLabels(labels)
   990  		g.Expect(env.Patch(ctx, clusterResourceSetInstance, client.MergeFrom(clusterResourceSetInstance.DeepCopy()))).To(Succeed())
   991  
   992  		// Wait until ClusterResourceSetBinding is created for the Cluster
   993  		g.Eventually(func() bool {
   994  			binding := &addonsv1.ClusterResourceSetBinding{}
   995  			err := env.Get(ctx, clusterResourceSetBindingKey, binding)
   996  			return err == nil
   997  		}, timeout).Should(BeTrue())
   998  	})
   999  }
  1000  
  1001  func clusterResourceSetBindingReady(env *envtest.Environment, cluster *clusterv1.Cluster) func() bool {
  1002  	return func() bool {
  1003  		clusterResourceSetBindingKey := client.ObjectKey{
  1004  			Namespace: cluster.Namespace,
  1005  			Name:      cluster.Name,
  1006  		}
  1007  		binding := &addonsv1.ClusterResourceSetBinding{}
  1008  		err := env.Get(ctx, clusterResourceSetBindingKey, binding)
  1009  		if err != nil {
  1010  			return false
  1011  		}
  1012  
  1013  		if len(binding.Spec.Bindings) != 1 {
  1014  			return false
  1015  		}
  1016  		if len(binding.Spec.Bindings[0].Resources) != 2 {
  1017  			return false
  1018  		}
  1019  
  1020  		if !binding.Spec.Bindings[0].Resources[0].Applied || !binding.Spec.Bindings[0].Resources[1].Applied {
  1021  			return false
  1022  		}
  1023  
  1024  		return binding.Spec.ClusterName == cluster.Name
  1025  	}
  1026  }
  1027  
  1028  func configMapHasBeenUpdated(env *envtest.Environment, key client.ObjectKey, newState *corev1.ConfigMap) func() error {
  1029  	return func() error {
  1030  		cm := &corev1.ConfigMap{}
  1031  		if err := env.Get(ctx, key, cm); err != nil {
  1032  			return err
  1033  		}
  1034  
  1035  		if !reflect.DeepEqual(cm.Data, newState.Data) {
  1036  			return errors.Errorf("configMap %s hasn't been updated yet", key.Name)
  1037  		}
  1038  
  1039  		return nil
  1040  	}
  1041  }
  1042  
  1043  func configMap(name, namespace string, data map[string]string) *corev1.ConfigMap {
  1044  	return &corev1.ConfigMap{
  1045  		TypeMeta: metav1.TypeMeta{
  1046  			APIVersion: "v1",
  1047  			Kind:       "ConfigMap",
  1048  		},
  1049  		ObjectMeta: metav1.ObjectMeta{
  1050  			Name:      name,
  1051  			Namespace: namespace,
  1052  		},
  1053  		Data: data,
  1054  	}
  1055  }
  1056  
  1057  func randomNamespaceForTest(tb testing.TB) string {
  1058  	tb.Helper()
  1059  	// This is just to get a short form of the test name
  1060  	// sha1 is totally fine
  1061  	h := sha1.New() //nolint: gosec
  1062  	h.Write([]byte(tb.Name()))
  1063  	testNameHash := fmt.Sprintf("%x", h.Sum(nil))
  1064  	return "ns-" + testNameHash[:7] + "-" + util.RandomString(6)
  1065  }