github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/gc_e2e_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/blang/semver/v4"
     8  	. "github.com/onsi/ginkgo/v2"
     9  	. "github.com/onsi/gomega"
    10  	. "github.com/operator-framework/operator-lifecycle-manager/test/e2e/dsl"
    11  	corev1 "k8s.io/api/core/v1"
    12  	rbacv1 "k8s.io/api/rbac/v1"
    13  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    14  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/util/rand"
    17  	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
    18  
    19  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    20  	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
    21  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
    22  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
    23  	"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
    24  )
    25  
    26  var _ = Describe("Garbage collection for dependent resources", func() {
    27  	var (
    28  		kubeClient         operatorclient.ClientInterface
    29  		operatorClient     versioned.Interface
    30  		generatedNamespace corev1.Namespace
    31  	)
    32  
    33  	BeforeEach(func() {
    34  		kubeClient = ctx.Ctx().KubeClient()
    35  		operatorClient = ctx.Ctx().OperatorClient()
    36  
    37  		namespaceName := genName("gc-e2e-")
    38  		generatedNamespace = SetupGeneratedTestNamespace(namespaceName, namespaceName)
    39  	})
    40  
    41  	AfterEach(func() {
    42  		TeardownNamespace(generatedNamespace.GetName())
    43  	})
    44  
    45  	Context("Given a ClusterRole owned by a CustomResourceDefinition", func() {
    46  		var (
    47  			crd *apiextensionsv1.CustomResourceDefinition
    48  			cr  *rbacv1.ClusterRole
    49  		)
    50  
    51  		BeforeEach(func() {
    52  			group := fmt.Sprintf("%s.com", rand.String(16))
    53  
    54  			crd = &apiextensionsv1.CustomResourceDefinition{
    55  				ObjectMeta: metav1.ObjectMeta{
    56  					Name: fmt.Sprintf("plural.%s", group),
    57  				},
    58  				Spec: apiextensionsv1.CustomResourceDefinitionSpec{
    59  					Group: group,
    60  					Scope: apiextensionsv1.ClusterScoped,
    61  					Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
    62  						{
    63  							Name:    "v1",
    64  							Served:  true,
    65  							Storage: true,
    66  							Schema: &apiextensionsv1.CustomResourceValidation{
    67  								OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{Type: "object"},
    68  							},
    69  						},
    70  					},
    71  					Names: apiextensionsv1.CustomResourceDefinitionNames{
    72  						Plural:   "plural",
    73  						Singular: "singular",
    74  						Kind:     "Kind",
    75  						ListKind: "KindList",
    76  					},
    77  				},
    78  			}
    79  
    80  			By(`Create a CustomResourceDefinition`)
    81  			var err error
    82  			Eventually(func() error {
    83  				crd, err = kubeClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), crd, metav1.CreateOptions{})
    84  				return err
    85  			}).Should(Succeed())
    86  
    87  			cr = &rbacv1.ClusterRole{
    88  				ObjectMeta: metav1.ObjectMeta{
    89  					GenerateName:    "clusterrole-",
    90  					OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(crd)},
    91  				},
    92  			}
    93  
    94  			By(`Create a ClusterRole for the crd`)
    95  			Eventually(func() error {
    96  				cr, err = kubeClient.CreateClusterRole(cr)
    97  				return err
    98  			}).Should(Succeed())
    99  		})
   100  
   101  		AfterEach(func() {
   102  
   103  			By(`Clean up cluster role`)
   104  			IgnoreError(kubeClient.DeleteClusterRole(cr.GetName(), &metav1.DeleteOptions{}))
   105  
   106  			By(`Clean up CRD`)
   107  			IgnoreError(kubeClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}))
   108  		})
   109  
   110  		When("CustomResourceDefinition is deleted", func() {
   111  
   112  			BeforeEach(func() {
   113  				By(`Delete CRD`)
   114  				Eventually(func() bool {
   115  					err := kubeClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{})
   116  					return apierrors.IsNotFound(err)
   117  				}).Should(BeTrue())
   118  			})
   119  
   120  			It("should delete the associated ClusterRole", func() {
   121  				Eventually(func() bool {
   122  					_, err := kubeClient.GetClusterRole(cr.GetName())
   123  					return apierrors.IsNotFound(err)
   124  				}).Should(BeTrue(), "get cluster role should eventually return \"not found\"")
   125  			})
   126  
   127  		})
   128  	})
   129  
   130  	Context("Given a ClusterRole owned by a APIService", func() {
   131  		var (
   132  			apiService *apiregistrationv1.APIService
   133  			cr         *rbacv1.ClusterRole
   134  		)
   135  
   136  		BeforeEach(func() {
   137  			group := rand.String(16)
   138  
   139  			apiService = &apiregistrationv1.APIService{
   140  				ObjectMeta: metav1.ObjectMeta{
   141  					Name: fmt.Sprintf("v1.%s", group),
   142  				},
   143  				Spec: apiregistrationv1.APIServiceSpec{
   144  					Group:                group,
   145  					Version:              "v1",
   146  					GroupPriorityMinimum: 1,
   147  					VersionPriority:      1,
   148  				},
   149  			}
   150  			By(`Create an API Service`)
   151  			var err error
   152  			Eventually(func() error {
   153  				apiService, err = kubeClient.CreateAPIService(apiService)
   154  				return err
   155  			}).Should(Succeed())
   156  
   157  			cr = &rbacv1.ClusterRole{
   158  				ObjectMeta: metav1.ObjectMeta{
   159  					GenerateName:    "clusterrole-",
   160  					OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(apiService)},
   161  				},
   162  			}
   163  
   164  			Eventually(func() error {
   165  				By(`Create a ClusterRole`)
   166  				cr, err = kubeClient.CreateClusterRole(cr)
   167  				return err
   168  			}).Should(Succeed())
   169  		})
   170  
   171  		AfterEach(func() {
   172  
   173  			IgnoreError(kubeClient.DeleteClusterRole(cr.GetName(), &metav1.DeleteOptions{}))
   174  
   175  			IgnoreError(kubeClient.DeleteAPIService(apiService.GetName(), &metav1.DeleteOptions{}))
   176  
   177  		})
   178  
   179  		When("APIService is deleted", func() {
   180  
   181  			BeforeEach(func() {
   182  				By(`Delete API service`)
   183  				Eventually(func() bool {
   184  					err := kubeClient.DeleteAPIService(apiService.GetName(), &metav1.DeleteOptions{})
   185  					return apierrors.IsNotFound(err)
   186  				}).Should(BeTrue())
   187  			})
   188  
   189  			It("should delete the associated ClusterRole", func() {
   190  				Eventually(func() bool {
   191  					_, err := kubeClient.GetClusterRole(cr.GetName())
   192  					return apierrors.IsNotFound(err)
   193  				}).Should(BeTrue(), "get cluster role should eventually return \"not found\"")
   194  			})
   195  
   196  		})
   197  	})
   198  
   199  	// TestOwnerReferenceGCBehavior runs a simple check on OwnerReference behavior to ensure
   200  	// a resource with multiple OwnerReferences will not be garbage collected when one of its
   201  	// owners has been deleted.
   202  	// Test Case:
   203  	//				CSV-A     CSV-B                        CSV-B
   204  	//				   \      /      --Delete CSV-A-->       |
   205  	//				   ConfigMap						 ConfigMap
   206  	Context("Given a dependent resource associated with multiple owners", func() {
   207  		var (
   208  			ownerA   v1alpha1.ClusterServiceVersion
   209  			ownerB   v1alpha1.ClusterServiceVersion
   210  			fetchedA *v1alpha1.ClusterServiceVersion
   211  			fetchedB *v1alpha1.ClusterServiceVersion
   212  
   213  			dependent *corev1.ConfigMap
   214  
   215  			propagation metav1.DeletionPropagation
   216  			options     metav1.DeleteOptions
   217  		)
   218  
   219  		BeforeEach(func() {
   220  
   221  			ownerA = newCSV("ownera", generatedNamespace.GetName(), "", semver.MustParse("0.0.0"), nil, nil, nil)
   222  			ownerB = newCSV("ownerb", generatedNamespace.GetName(), "", semver.MustParse("0.0.0"), nil, nil, nil)
   223  
   224  			By(`create all owners`)
   225  			var err error
   226  			Eventually(func() error {
   227  				fetchedA, err = operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Create(context.Background(), &ownerA, metav1.CreateOptions{})
   228  				return err
   229  			}).Should(Succeed())
   230  
   231  			Eventually(func() error {
   232  				fetchedB, err = operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Create(context.Background(), &ownerB, metav1.CreateOptions{})
   233  				return err
   234  			}).Should(Succeed())
   235  
   236  			dependent = &corev1.ConfigMap{
   237  				ObjectMeta: metav1.ObjectMeta{
   238  					Name: "dependent",
   239  				},
   240  				Data: map[string]string{},
   241  			}
   242  
   243  			By(`add owners`)
   244  			ownerutil.AddOwner(dependent, fetchedA, true, false)
   245  			ownerutil.AddOwner(dependent, fetchedB, true, false)
   246  
   247  			By(`create ConfigMap dependent`)
   248  			Eventually(func() error {
   249  				_, err = kubeClient.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Create(context.Background(), dependent, metav1.CreateOptions{})
   250  				return err
   251  			}).Should(Succeed(), "dependent could not be created")
   252  
   253  			propagation = metav1.DeletePropagationForeground
   254  			options = metav1.DeleteOptions{PropagationPolicy: &propagation}
   255  		})
   256  
   257  		When("removing one of the owner using 'Foreground' deletion policy", func() {
   258  
   259  			BeforeEach(func() {
   260  				By(`delete ownerA in the foreground (to ensure any "blocking" dependents are deleted before ownerA)`)
   261  				Eventually(func() bool {
   262  					err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Delete(context.Background(), fetchedA.GetName(), options)
   263  					return apierrors.IsNotFound(err)
   264  				}).Should(BeTrue())
   265  
   266  				By(`wait for deletion of ownerA`)
   267  				Eventually(func() bool {
   268  					_, err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Get(context.Background(), ownerA.GetName(), metav1.GetOptions{})
   269  					return apierrors.IsNotFound(err)
   270  				}).Should(BeTrue())
   271  			})
   272  
   273  			It("should not have deleted the dependent since ownerB CSV is still present", func() {
   274  				Eventually(func() error {
   275  					_, err := kubeClient.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), dependent.GetName(), metav1.GetOptions{})
   276  					return err
   277  				}).Should(Succeed(), "dependent deleted after one of the owner was deleted")
   278  				ctx.Ctx().Logf("dependent still exists after one owner was deleted")
   279  			})
   280  		})
   281  
   282  		When("removing both the owners using 'Foreground' deletion policy", func() {
   283  
   284  			BeforeEach(func() {
   285  				By(`delete ownerA in the foreground (to ensure any "blocking" dependents are deleted before ownerA)`)
   286  				Eventually(func() bool {
   287  					err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Delete(context.Background(), fetchedA.GetName(), options)
   288  					return apierrors.IsNotFound(err)
   289  				}).Should(BeTrue())
   290  
   291  				By(`wait for deletion of ownerA`)
   292  				Eventually(func() bool {
   293  					_, err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Get(context.Background(), ownerA.GetName(), metav1.GetOptions{})
   294  					return apierrors.IsNotFound(err)
   295  				}).Should(BeTrue())
   296  
   297  				By(`delete ownerB in the foreground (to ensure any "blocking" dependents are deleted before ownerB)`)
   298  				Eventually(func() bool {
   299  					err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Delete(context.Background(), fetchedB.GetName(), options)
   300  					return apierrors.IsNotFound(err)
   301  				}).Should(BeTrue())
   302  
   303  				By(`wait for deletion of ownerB`)
   304  				Eventually(func() bool {
   305  					_, err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Get(context.Background(), ownerB.GetName(), metav1.GetOptions{})
   306  					return apierrors.IsNotFound(err)
   307  				}).Should(BeTrue())
   308  			})
   309  
   310  			It("should have deleted the dependent since both the owners were deleted", func() {
   311  				Eventually(func() bool {
   312  					_, err := kubeClient.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), dependent.GetName(), metav1.GetOptions{})
   313  					return apierrors.IsNotFound(err)
   314  				}).Should(BeTrue(), "expected dependency configmap would be properly garabage collected")
   315  				ctx.Ctx().Logf("dependent successfully garbage collected after both owners were deleted")
   316  			})
   317  
   318  		})
   319  
   320  	})
   321  
   322  	When("a bundle with configmap and secret objects is installed", func() {
   323  
   324  		const (
   325  			packageName   = "busybox"
   326  			channelName   = "alpha"
   327  			subName       = "test-subscription"
   328  			secretName    = "mysecret"
   329  			configmapName = "special-config"
   330  		)
   331  
   332  		BeforeEach(func() {
   333  			const (
   334  				sourceName = "test-catalog"
   335  				imageName  = "quay.io/olmtest/single-bundle-index:objects"
   336  			)
   337  			var installPlanRef string
   338  
   339  			By(`create catalog source`)
   340  			source := &v1alpha1.CatalogSource{
   341  				TypeMeta: metav1.TypeMeta{
   342  					Kind:       v1alpha1.CatalogSourceKind,
   343  					APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   344  				},
   345  				ObjectMeta: metav1.ObjectMeta{
   346  					Name:      sourceName,
   347  					Namespace: generatedNamespace.GetName(),
   348  					Labels:    map[string]string{"olm.catalogSource": sourceName},
   349  				},
   350  				Spec: v1alpha1.CatalogSourceSpec{
   351  					SourceType: v1alpha1.SourceTypeGrpc,
   352  					Image:      imageName,
   353  					GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   354  						SecurityContextConfig: v1alpha1.Restricted,
   355  					},
   356  				},
   357  			}
   358  
   359  			Eventually(func() error {
   360  				cs, err := operatorClient.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
   361  				if err != nil {
   362  					return err
   363  				}
   364  				source = cs.DeepCopy()
   365  
   366  				return nil
   367  			}).Should(Succeed(), "could not create catalog source")
   368  
   369  			By(`Wait for the CatalogSource to be ready`)
   370  			_, err := fetchCatalogSourceOnStatus(operatorClient, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced())
   371  			Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
   372  
   373  			By(`Create a Subscription for package`)
   374  			_ = createSubscriptionForCatalog(operatorClient, source.GetNamespace(), subName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic)
   375  
   376  			By(`Wait for the Subscription to succeed`)
   377  			sub, err := fetchSubscription(operatorClient, generatedNamespace.GetName(), subName, subscriptionStateAtLatestChecker())
   378  			Expect(err).ToNot(HaveOccurred(), "could not get subscription at latest status")
   379  
   380  			installPlanRef = sub.Status.InstallPlanRef.Name
   381  
   382  			By(`Wait for the installplan to complete (5 minute timeout)`)
   383  			_, err = fetchInstallPlan(GinkgoT(), operatorClient, installPlanRef, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete))
   384  			Expect(err).ToNot(HaveOccurred(), "could not get installplan at complete phase")
   385  
   386  			ctx.Ctx().Logf("install plan %s completed", installPlanRef)
   387  
   388  			By(`confirm extra bundle objects (secret and configmap) are installed`)
   389  			Eventually(func() error {
   390  				_, err := kubeClient.GetSecret(generatedNamespace.GetName(), secretName)
   391  				return err
   392  			}).Should(Succeed(), "expected no error getting secret object associated with CSV")
   393  
   394  			Eventually(func() error {
   395  				_, err := kubeClient.GetConfigMap(generatedNamespace.GetName(), configmapName)
   396  				return err
   397  			}).Should(Succeed(), "expected no error getting configmap object associated with CSV")
   398  		})
   399  
   400  		When("the CSV is deleted", func() {
   401  
   402  			const csvName = "busybox.v2.0.0"
   403  
   404  			BeforeEach(func() {
   405  				By(`Delete subscription first`)
   406  				Eventually(func() bool {
   407  					err := operatorClient.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Delete(context.Background(), subName, metav1.DeleteOptions{})
   408  					return apierrors.IsNotFound(err)
   409  				}).Should(BeTrue())
   410  
   411  				By(`wait for deletion`)
   412  				Eventually(func() bool {
   413  					_, err := operatorClient.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
   414  					return apierrors.IsNotFound(err)
   415  				}).Should(BeTrue())
   416  
   417  				By(`Delete CSV`)
   418  				Eventually(func() bool {
   419  					err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Delete(context.Background(), csvName, metav1.DeleteOptions{})
   420  					return apierrors.IsNotFound(err)
   421  				}).Should(BeTrue())
   422  
   423  				By(`wait for deletion`)
   424  				Eventually(func() bool {
   425  					_, err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Get(context.Background(), csvName, metav1.GetOptions{})
   426  					return apierrors.IsNotFound(err)
   427  				}).Should(BeTrue())
   428  			})
   429  
   430  			It("OLM should delete the associated configmap and secret", func() {
   431  				By(`confirm extra bundle objects (secret and configmap) are no longer installed on the cluster`)
   432  				Eventually(func() bool {
   433  					_, err := kubeClient.GetSecret(generatedNamespace.GetName(), secretName)
   434  					return apierrors.IsNotFound(err)
   435  				}).Should(BeTrue())
   436  
   437  				Eventually(func() bool {
   438  					_, err := kubeClient.GetConfigMap(generatedNamespace.GetName(), configmapName)
   439  					return apierrors.IsNotFound(err)
   440  				}).Should(BeTrue())
   441  				ctx.Ctx().Logf("dependent successfully garbage collected after csv owner was deleted")
   442  			})
   443  		})
   444  	})
   445  
   446  	When("a bundle with a configmap is installed", func() {
   447  
   448  		const (
   449  			subName       = "test-subscription"
   450  			configmapName = "special-config"
   451  		)
   452  
   453  		BeforeEach(func() {
   454  			const (
   455  				packageName = "busybox"
   456  				channelName = "alpha"
   457  				sourceName  = "test-catalog"
   458  				imageName   = "quay.io/olmtest/single-bundle-index:objects-upgrade-samename"
   459  			)
   460  
   461  			var installPlanRef string
   462  			By(`create catalog source`)
   463  			source := &v1alpha1.CatalogSource{
   464  				TypeMeta: metav1.TypeMeta{
   465  					Kind:       v1alpha1.CatalogSourceKind,
   466  					APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   467  				},
   468  				ObjectMeta: metav1.ObjectMeta{
   469  					Name:      sourceName,
   470  					Namespace: generatedNamespace.GetName(),
   471  					Labels:    map[string]string{"olm.catalogSource": sourceName},
   472  				},
   473  				Spec: v1alpha1.CatalogSourceSpec{
   474  					SourceType: v1alpha1.SourceTypeGrpc,
   475  					Image:      imageName,
   476  					GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   477  						SecurityContextConfig: v1alpha1.Restricted,
   478  					},
   479  				},
   480  			}
   481  
   482  			var err error
   483  			Eventually(func() error {
   484  				source, err = operatorClient.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
   485  				return err
   486  			}).Should(Succeed(), "could not create catalog source")
   487  
   488  			By(`Wait for the CatalogSource to be ready`)
   489  			_, err = fetchCatalogSourceOnStatus(operatorClient, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced())
   490  			Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
   491  
   492  			By(`Create a Subscription for package`)
   493  			_ = createSubscriptionForCatalog(operatorClient, source.GetNamespace(), subName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic)
   494  
   495  			By(`Wait for the Subscription to succeed`)
   496  			sub, err := fetchSubscription(operatorClient, generatedNamespace.GetName(), subName, subscriptionStateAtLatestChecker())
   497  			Expect(err).ToNot(HaveOccurred(), "could not get subscription at latest status")
   498  
   499  			installPlanRef = sub.Status.InstallPlanRef.Name
   500  
   501  			By(`Wait for the installplan to complete (5 minute timeout)`)
   502  			_, err = fetchInstallPlan(GinkgoT(), operatorClient, installPlanRef, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete))
   503  			Expect(err).ToNot(HaveOccurred(), "could not get installplan at complete phase")
   504  
   505  			Eventually(func() error {
   506  				_, err := kubeClient.GetConfigMap(generatedNamespace.GetName(), configmapName)
   507  				return err
   508  			}).Should(Succeed(), "expected no error getting configmap object associated with CSV")
   509  		})
   510  
   511  		When("the subscription is updated to a later CSV with a configmap with the same name but new data", func() {
   512  
   513  			const (
   514  				upgradeChannelName = "beta"
   515  				newCSVname         = "busybox.v3.0.0"
   516  			)
   517  			var installPlanRef string
   518  
   519  			BeforeEach(func() {
   520  				Eventually(func() error {
   521  					By(`update subscription first`)
   522  					sub, err := operatorClient.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
   523  					if err != nil {
   524  						return fmt.Errorf("could not get subscription")
   525  					}
   526  					By(`update channel on sub`)
   527  					sub.Spec.Channel = upgradeChannelName
   528  					_, err = operatorClient.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Update(context.Background(), sub, metav1.UpdateOptions{})
   529  					return err
   530  				}).Should(Succeed(), "could not update subscription")
   531  
   532  				By(`Wait for the Subscription to succeed`)
   533  				sub, err := fetchSubscription(operatorClient, generatedNamespace.GetName(), subName, subscriptionStateAtLatestChecker())
   534  				Expect(err).ToNot(HaveOccurred(), "could not get subscription at latest status")
   535  
   536  				installPlanRef = sub.Status.InstallPlanRef.Name
   537  
   538  				By(`Wait for the installplan to complete (5 minute timeout)`)
   539  				_, err = fetchInstallPlan(GinkgoT(), operatorClient, installPlanRef, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete))
   540  				Expect(err).ToNot(HaveOccurred(), "could not get installplan at complete phase")
   541  
   542  				By(`Ensure the new csv is installed`)
   543  				Eventually(func() error {
   544  					_, err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Get(context.Background(), newCSVname, metav1.GetOptions{})
   545  					return err
   546  				}).Should(BeNil())
   547  			})
   548  
   549  			It("OLM should have upgraded associated configmap in place", func() {
   550  				Eventually(func() (string, error) {
   551  					cfg, err := kubeClient.GetConfigMap(generatedNamespace.GetName(), configmapName)
   552  					if err != nil {
   553  						return "", err
   554  					}
   555  					By(`check data in configmap to ensure it is the new data (configmap was updated in the newer bundle)`)
   556  					By(`new value in the configmap is "updated-very-much"`)
   557  					return cfg.Data["special.how"], nil
   558  				}).Should(Equal("updated-very-much"))
   559  				ctx.Ctx().Logf("dependent successfully updated after csv owner was updated")
   560  			})
   561  		})
   562  	})
   563  
   564  	When("a bundle with a new configmap is installed", func() {
   565  
   566  		const (
   567  			subName       = "test-subscription"
   568  			configmapName = "special-config"
   569  		)
   570  
   571  		BeforeEach(func() {
   572  			const (
   573  				packageName = "busybox"
   574  				channelName = "alpha"
   575  				sourceName  = "test-catalog"
   576  				imageName   = "quay.io/olmtest/single-bundle-index:objects-upgrade-diffname"
   577  			)
   578  
   579  			var installPlanRef string
   580  			By(`create catalog source`)
   581  			source := &v1alpha1.CatalogSource{
   582  				TypeMeta: metav1.TypeMeta{
   583  					Kind:       v1alpha1.CatalogSourceKind,
   584  					APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   585  				},
   586  				ObjectMeta: metav1.ObjectMeta{
   587  					Name:      sourceName,
   588  					Namespace: generatedNamespace.GetName(),
   589  					Labels:    map[string]string{"olm.catalogSource": sourceName},
   590  				},
   591  				Spec: v1alpha1.CatalogSourceSpec{
   592  					SourceType: v1alpha1.SourceTypeGrpc,
   593  					Image:      imageName,
   594  					GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   595  						SecurityContextConfig: v1alpha1.Restricted,
   596  					},
   597  				},
   598  			}
   599  
   600  			var err error
   601  			Eventually(func() error {
   602  				source, err = operatorClient.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
   603  				return err
   604  			}).Should(Succeed())
   605  
   606  			By(`Wait for the CatalogSource to be ready`)
   607  			_, err = fetchCatalogSourceOnStatus(operatorClient, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced())
   608  			Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
   609  
   610  			By(`Create a Subscription for package`)
   611  			_ = createSubscriptionForCatalog(operatorClient, source.GetNamespace(), subName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic)
   612  
   613  			By(`Wait for the Subscription to succeed`)
   614  			sub, err := fetchSubscription(operatorClient, generatedNamespace.GetName(), subName, subscriptionStateAtLatestChecker())
   615  			Expect(err).ToNot(HaveOccurred(), "could not get subscription at latest status")
   616  
   617  			installPlanRef = sub.Status.InstallPlanRef.Name
   618  
   619  			By(`Wait for the installplan to complete (5 minute timeout)`)
   620  			_, err = fetchInstallPlan(GinkgoT(), operatorClient, installPlanRef, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete))
   621  			Expect(err).ToNot(HaveOccurred(), "could not get installplan at complete phase")
   622  
   623  			Eventually(func() error {
   624  				_, err := kubeClient.GetConfigMap(generatedNamespace.GetName(), configmapName)
   625  				return err
   626  			}).Should(Succeed(), "expected no error getting configmap object associated with CSV")
   627  		})
   628  
   629  		When("the subscription is updated to a later CSV with a configmap with a new name", func() {
   630  
   631  			const (
   632  				upgradeChannelName    = "beta"
   633  				upgradedConfigMapName = "not-special-config"
   634  				newCSVname            = "busybox.v3.0.0"
   635  			)
   636  			var installPlanRef string
   637  
   638  			BeforeEach(func() {
   639  				Eventually(func() error {
   640  					By(`update subscription first`)
   641  					sub, err := operatorClient.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
   642  					if err != nil {
   643  						return fmt.Errorf("could not get subscription")
   644  					}
   645  					By(`update channel on sub`)
   646  					sub.Spec.Channel = upgradeChannelName
   647  					_, err = operatorClient.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Update(context.Background(), sub, metav1.UpdateOptions{})
   648  					return err
   649  				}).Should(Succeed(), "could not update subscription")
   650  
   651  				By(`Wait for the Subscription to succeed`)
   652  				sub, err := fetchSubscription(operatorClient, generatedNamespace.GetName(), subName, subscriptionStateAtLatestChecker())
   653  				Expect(err).ToNot(HaveOccurred(), "could not get subscription at latest status")
   654  
   655  				installPlanRef = sub.Status.InstallPlanRef.Name
   656  
   657  				By(`Wait for the installplan to complete (5 minute timeout)`)
   658  				_, err = fetchInstallPlan(GinkgoT(), operatorClient, installPlanRef, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete))
   659  				Expect(err).ToNot(HaveOccurred(), "could not get installplan at complete phase")
   660  
   661  				By(`Ensure the new csv is installed`)
   662  				Eventually(func() error {
   663  					_, err := operatorClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Get(context.Background(), newCSVname, metav1.GetOptions{})
   664  					return err
   665  				}).Should(BeNil())
   666  			})
   667  
   668  			It("[FLAKE] should have removed the old configmap and put the new configmap in place", func() {
   669  				By(`flake issue: https://github.com/operator-framework/operator-lifecycle-manager/issues/2626`)
   670  				Eventually(func() bool {
   671  					_, err := kubeClient.GetConfigMap(generatedNamespace.GetName(), configmapName)
   672  					return apierrors.IsNotFound(err)
   673  				}).Should(BeTrue())
   674  
   675  				Eventually(func() error {
   676  					_, err := kubeClient.GetConfigMap(generatedNamespace.GetName(), upgradedConfigMapName)
   677  					return err
   678  				}).Should(BeNil())
   679  				ctx.Ctx().Logf("dependent successfully updated after csv owner was updated")
   680  			})
   681  		})
   682  	})
   683  })