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

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"path/filepath"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
    14  	packageserverclientset "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/client/clientset/versioned"
    15  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    16  	k8serror "k8s.io/apimachinery/pkg/api/errors"
    17  	"sigs.k8s.io/controller-runtime/pkg/client"
    18  
    19  	"github.com/blang/semver/v4"
    20  	. "github.com/onsi/ginkgo/v2"
    21  	. "github.com/onsi/gomega"
    22  	appsv1 "k8s.io/api/apps/v1"
    23  	corev1 "k8s.io/api/core/v1"
    24  	"k8s.io/apimachinery/pkg/api/meta"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/labels"
    27  	"k8s.io/apimachinery/pkg/types"
    28  
    29  	"github.com/operator-framework/api/pkg/lib/version"
    30  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    31  	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
    32  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalogtemplate"
    33  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
    34  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/catalogsource"
    35  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
    36  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
    37  	"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
    38  )
    39  
    40  const (
    41  	openshiftregistryFQDN = "image-registry.openshift-image-registry.svc:5000"
    42  	catsrcImage           = "docker://quay.io/olmtest/catsrc-update-test:"
    43  	badCSVDir             = "bad-csv"
    44  )
    45  
    46  var _ = Describe("Starting CatalogSource e2e tests", func() {
    47  	var (
    48  		generatedNamespace  corev1.Namespace
    49  		c                   operatorclient.ClientInterface
    50  		crc                 versioned.Interface
    51  		packageserverClient *packageserverclientset.Clientset
    52  	)
    53  
    54  	BeforeEach(func() {
    55  		// In OCP, PSA labels for any namespace created that is not prefixed with "openshift-" is overridden to enforce
    56  		// PSA restricted. This test namespace needs to prefixed with openshift- so that baseline/privileged enforcement
    57  		// for the PSA specific tests are not overridden,
    58  		// Change it only after https://github.com/operator-framework/operator-lifecycle-manager/issues/2859 is closed.
    59  		namespaceName := genName("openshift-catsrc-e2e-")
    60  		og := operatorsv1.OperatorGroup{
    61  			ObjectMeta: metav1.ObjectMeta{
    62  				Name:      fmt.Sprintf("%s-operatorgroup", namespaceName),
    63  				Namespace: namespaceName,
    64  			},
    65  		}
    66  		generatedNamespace = SetupGeneratedTestNamespaceWithOperatorGroup(namespaceName, og)
    67  		c = ctx.Ctx().KubeClient()
    68  		crc = ctx.Ctx().OperatorClient()
    69  		packageserverClient = packageserverclientset.NewForConfigOrDie(ctx.Ctx().RESTConfig())
    70  	})
    71  
    72  	AfterEach(func() {
    73  		TeardownNamespace(generatedNamespace.GetName())
    74  	})
    75  
    76  	It("loading between restarts", func() {
    77  		By("create a simple catalogsource")
    78  		packageName := genName("nginx")
    79  		stableChannel := "stable"
    80  		packageStable := packageName + "-stable"
    81  		manifests := []registry.PackageManifest{
    82  			{
    83  				PackageName: packageName,
    84  				Channels: []registry.PackageChannel{
    85  					{Name: stableChannel, CurrentCSVName: packageStable},
    86  				},
    87  				DefaultChannelName: stableChannel,
    88  			},
    89  		}
    90  
    91  		crd := newCRD(genName("ins-"))
    92  		csv := newCSV(packageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
    93  
    94  		defer func() {
    95  			Eventually(func() error {
    96  				return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{})
    97  			}).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue())))
    98  			Eventually(func() error {
    99  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv))
   100  			}).Should(Succeed())
   101  		}()
   102  
   103  		catalogSourceName := genName("mock-ocs-")
   104  		_, cleanupSource := createInternalCatalogSource(c, crc, catalogSourceName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csv})
   105  		defer cleanupSource()
   106  
   107  		By("ensure the mock catalog exists and has been synced by the catalog operator")
   108  		catalogSource, err := fetchCatalogSourceOnStatus(crc, catalogSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   109  		Expect(err).ShouldNot(HaveOccurred())
   110  
   111  		By("get catalog operator deployment")
   112  		deployment, err := getOperatorDeployment(c, operatorNamespace, labels.Set{"app": "catalog-operator"})
   113  		Expect(err).ShouldNot(HaveOccurred())
   114  		Expect(deployment).ToNot(BeNil(), "Could not find catalog operator deployment")
   115  
   116  		By("rescale catalog operator")
   117  		By("Rescaling catalog operator...")
   118  		err = rescaleDeployment(c, deployment)
   119  		Expect(err).ShouldNot(HaveOccurred(), "Could not rescale catalog operator")
   120  		By("Catalog operator rescaled")
   121  
   122  		By("check for last synced update to catalogsource")
   123  		By("Checking for catalogsource lastSync updates")
   124  		_, err = fetchCatalogSourceOnStatus(crc, catalogSourceName, generatedNamespace.GetName(), func(cs *v1alpha1.CatalogSource) bool {
   125  			before := catalogSource.Status.GRPCConnectionState
   126  			after := cs.Status.GRPCConnectionState
   127  			if after != nil && after.LastConnectTime.After(before.LastConnectTime.Time) {
   128  				ctx.Ctx().Logf("lastSync updated: %s -> %s", before.LastConnectTime, after.LastConnectTime)
   129  				return true
   130  			}
   131  			return false
   132  		})
   133  		Expect(err).ShouldNot(HaveOccurred(), "Catalog source changed after rescale")
   134  		By("Catalog source successfully loaded after rescale")
   135  	})
   136  
   137  	It("global update triggers subscription sync", func() {
   138  		mainPackageName := genName("nginx-")
   139  
   140  		mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)
   141  		mainPackageReplacement := fmt.Sprintf("%s-replacement", mainPackageStable)
   142  
   143  		stableChannel := "stable"
   144  
   145  		mainCRD := newCRD(genName("ins-"))
   146  		mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, nil)
   147  		replacementCSV := newCSV(mainPackageReplacement, generatedNamespace.GetName(), mainPackageStable, semver.MustParse("0.2.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, nil)
   148  
   149  		defer func() {
   150  			Eventually(func() error {
   151  				return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), mainCRD.GetName(), metav1.DeleteOptions{})
   152  			}).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue())))
   153  			Eventually(func() error {
   154  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV))
   155  			}).Should(Succeed())
   156  			Eventually(func() error {
   157  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &replacementCSV))
   158  			}).Should(Succeed())
   159  		}()
   160  
   161  		mainCatalogName := genName("mock-ocs-main-")
   162  
   163  		By("Create separate manifests for each CatalogSource")
   164  		mainManifests := []registry.PackageManifest{
   165  			{
   166  				PackageName: mainPackageName,
   167  				Channels: []registry.PackageChannel{
   168  					{Name: stableChannel, CurrentCSVName: mainPackageStable},
   169  				},
   170  				DefaultChannelName: stableChannel,
   171  			},
   172  		}
   173  
   174  		By("Create the initial catalog source")
   175  		cs, cleanup := createInternalCatalogSource(c, crc, mainCatalogName, globalCatalogNamespace, mainManifests, []apiextensionsv1.CustomResourceDefinition{mainCRD}, []v1alpha1.ClusterServiceVersion{mainCSV})
   176  		defer cleanup()
   177  
   178  		By("Attempt to get the catalog source before creating install plan")
   179  		_, err := fetchCatalogSourceOnStatus(crc, cs.GetName(), cs.GetNamespace(), catalogSourceRegistryPodSynced())
   180  		Expect(err).ToNot(HaveOccurred())
   181  
   182  		subscriptionSpec := &v1alpha1.SubscriptionSpec{
   183  			CatalogSource:          cs.GetName(),
   184  			CatalogSourceNamespace: cs.GetNamespace(),
   185  			Package:                mainPackageName,
   186  			Channel:                stableChannel,
   187  			StartingCSV:            mainCSV.GetName(),
   188  			InstallPlanApproval:    v1alpha1.ApprovalManual,
   189  		}
   190  
   191  		By("Create Subscription")
   192  		subscriptionName := genName("sub-")
   193  		createSubscriptionForCatalogWithSpec(GinkgoT(), crc, generatedNamespace.GetName(), subscriptionName, subscriptionSpec)
   194  
   195  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
   196  		Expect(err).ShouldNot(HaveOccurred())
   197  		Expect(subscription).ToNot(BeNil())
   198  
   199  		installPlanName := subscription.Status.Install.Name
   200  		requiresApprovalChecker := buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseRequiresApproval)
   201  
   202  		Eventually(func() error {
   203  			fetchedInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, installPlanName, generatedNamespace.GetName(), requiresApprovalChecker)
   204  			if err != nil {
   205  				return err
   206  			}
   207  
   208  			fetchedInstallPlan.Spec.Approved = true
   209  			_, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Update(context.Background(), fetchedInstallPlan, metav1.UpdateOptions{})
   210  			return err
   211  		}).Should(Succeed())
   212  
   213  		_, err = fetchCSV(crc, generatedNamespace.GetName(), mainCSV.GetName(), csvSucceededChecker)
   214  		Expect(err).ShouldNot(HaveOccurred())
   215  
   216  		By("Update manifest")
   217  		mainManifests = []registry.PackageManifest{
   218  			{
   219  				PackageName: mainPackageName,
   220  				Channels: []registry.PackageChannel{
   221  					{Name: stableChannel, CurrentCSVName: replacementCSV.GetName()},
   222  				},
   223  				DefaultChannelName: stableChannel,
   224  			},
   225  		}
   226  
   227  		By("Update catalog configmap")
   228  		updateInternalCatalog(GinkgoT(), c, crc, cs.GetName(), cs.GetNamespace(), []apiextensionsv1.CustomResourceDefinition{mainCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, replacementCSV}, mainManifests)
   229  
   230  		By("Get updated catalogsource")
   231  		fetchedUpdatedCatalog, err := fetchCatalogSourceOnStatus(crc, cs.GetName(), cs.GetNamespace(), catalogSourceRegistryPodSynced())
   232  		Expect(err).ShouldNot(HaveOccurred())
   233  
   234  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateUpgradePendingChecker())
   235  		Expect(err).ShouldNot(HaveOccurred())
   236  		Expect(subscription).ShouldNot(BeNil())
   237  
   238  		By("Ensure the timing")
   239  		catalogConnState := fetchedUpdatedCatalog.Status.GRPCConnectionState
   240  		subUpdatedTime := subscription.Status.LastUpdated
   241  		Expect(subUpdatedTime.Time).Should(BeTemporally("<", catalogConnState.LastConnectTime.Add(60*time.Second)))
   242  	})
   243  
   244  	It("config map update triggers registry pod rollout", func() {
   245  
   246  		mainPackageName := genName("nginx-")
   247  		dependentPackageName := genName("nginxdep-")
   248  
   249  		mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)
   250  		dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName)
   251  
   252  		stableChannel := "stable"
   253  
   254  		dependentCRD := newCRD(genName("ins-"))
   255  		mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil)
   256  		dependentCSV := newCSV(dependentPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil, nil)
   257  
   258  		defer func() {
   259  			Eventually(func() error {
   260  				return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{})
   261  			}).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue())))
   262  			Eventually(func() error {
   263  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV))
   264  			}).Should(Succeed())
   265  			Eventually(func() error {
   266  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &dependentCSV))
   267  			}).Should(Succeed())
   268  		}()
   269  
   270  		mainCatalogName := genName("mock-ocs-main-")
   271  
   272  		By("Create separate manifests for each CatalogSource")
   273  		mainManifests := []registry.PackageManifest{
   274  			{
   275  				PackageName: mainPackageName,
   276  				Channels: []registry.PackageChannel{
   277  					{Name: stableChannel, CurrentCSVName: mainPackageStable},
   278  				},
   279  				DefaultChannelName: stableChannel,
   280  			},
   281  		}
   282  
   283  		dependentManifests := []registry.PackageManifest{
   284  			{
   285  				PackageName: dependentPackageName,
   286  				Channels: []registry.PackageChannel{
   287  					{Name: stableChannel, CurrentCSVName: dependentPackageStable},
   288  				},
   289  				DefaultChannelName: stableChannel,
   290  			},
   291  		}
   292  
   293  		By("Create the initial catalogsource")
   294  		createInternalCatalogSource(c, crc, mainCatalogName, generatedNamespace.GetName(), mainManifests, nil, []v1alpha1.ClusterServiceVersion{mainCSV})
   295  
   296  		By("Attempt to get the catalog source before creating install plan")
   297  		fetchedInitialCatalog, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   298  		Expect(err).ShouldNot(HaveOccurred())
   299  
   300  		By("Get initial configmap")
   301  		configMap, err := c.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), fetchedInitialCatalog.Spec.ConfigMap, metav1.GetOptions{})
   302  		Expect(err).ShouldNot(HaveOccurred())
   303  
   304  		By("Check pod created")
   305  		initialPods, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{LabelSelector: "olm.configMapResourceVersion=" + configMap.ResourceVersion})
   306  		Expect(err).ShouldNot(HaveOccurred())
   307  		Expect(initialPods.Items).To(HaveLen(1))
   308  
   309  		By("Update catalog configmap")
   310  		updateInternalCatalog(GinkgoT(), c, crc, mainCatalogName, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, dependentCSV}, append(mainManifests, dependentManifests...))
   311  
   312  		fetchedUpdatedCatalog, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, generatedNamespace.GetName(), func(catalog *v1alpha1.CatalogSource) bool {
   313  			before := fetchedInitialCatalog.Status.ConfigMapResource
   314  			after := catalog.Status.ConfigMapResource
   315  			if after != nil && before.LastUpdateTime.Before(&after.LastUpdateTime) &&
   316  				after.ResourceVersion != before.ResourceVersion {
   317  				ctx.Ctx().Logf("catalog updated")
   318  				return true
   319  			}
   320  			ctx.Ctx().Logf("waiting for catalog pod to be available")
   321  			return false
   322  		})
   323  		Expect(err).ShouldNot(HaveOccurred())
   324  
   325  		var updatedConfigMap *corev1.ConfigMap
   326  		Eventually(func() (types.UID, error) {
   327  			var err error
   328  			By("Get updated configmap")
   329  			updatedConfigMap, err = c.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), fetchedInitialCatalog.Spec.ConfigMap, metav1.GetOptions{})
   330  			if err != nil {
   331  				return "", err
   332  			}
   333  			if len(updatedConfigMap.ObjectMeta.OwnerReferences) == 0 {
   334  				return "", nil
   335  			}
   336  			return updatedConfigMap.ObjectMeta.OwnerReferences[0].UID, nil
   337  		}).Should(Equal(fetchedUpdatedCatalog.ObjectMeta.UID))
   338  
   339  		Expect(configMap.ResourceVersion).ShouldNot(Equal(updatedConfigMap.ResourceVersion))
   340  		Expect(fetchedInitialCatalog.Status.ConfigMapResource.ResourceVersion).ShouldNot(Equal(fetchedUpdatedCatalog.Status.ConfigMapResource.ResourceVersion))
   341  		Expect(fetchedUpdatedCatalog.Status.ConfigMapResource.ResourceVersion).Should(Equal(updatedConfigMap.GetResourceVersion()))
   342  
   343  		By("Await 1 CatalogSource registry pod matching the updated labels")
   344  		singlePod := podCount(1)
   345  		selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": mainCatalogName, "olm.configMapResourceVersion": updatedConfigMap.GetResourceVersion()})
   346  		podList, err := awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), singlePod)
   347  		Expect(err).ShouldNot(HaveOccurred())
   348  		Expect(podList.Items).To(HaveLen(1), "expected pod list not of length 1")
   349  
   350  		By("Await 1 CatalogSource registry pod matching the updated labels")
   351  		selector = labels.SelectorFromSet(map[string]string{"olm.catalogSource": mainCatalogName})
   352  		podList, err = awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), singlePod)
   353  		Expect(err).ShouldNot(HaveOccurred())
   354  		Expect(podList.Items).To(HaveLen(1), "expected pod list not of length 1")
   355  
   356  		By("Create Subscription")
   357  		subscriptionName := genName("sub-")
   358  		createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, fetchedUpdatedCatalog.GetName(), mainPackageName, stableChannel, "", v1alpha1.ApprovalAutomatic)
   359  
   360  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
   361  		Expect(err).ShouldNot(HaveOccurred())
   362  		Expect(subscription).ShouldNot(BeNil())
   363  		_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(v1alpha1.CSVPhaseSucceeded))
   364  		Expect(err).ShouldNot(HaveOccurred())
   365  
   366  		ipList, err := crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{})
   367  		ipCount := 0
   368  		for _, ip := range ipList.Items {
   369  			if ownerutil.IsOwnedBy(&ip, subscription) {
   370  				ipCount += 1
   371  			}
   372  		}
   373  		Expect(err).ShouldNot(HaveOccurred())
   374  	})
   375  
   376  	It("config map replace triggers registry pod rollout", func() {
   377  
   378  		mainPackageName := genName("nginx-")
   379  		dependentPackageName := genName("nginxdep-")
   380  
   381  		mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)
   382  
   383  		dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName)
   384  
   385  		stableChannel := "stable"
   386  
   387  		dependentCRD := newCRD(genName("ins-"))
   388  		mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil)
   389  		dependentCSV := newCSV(dependentPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil, nil)
   390  
   391  		defer func() {
   392  			Eventually(func() error {
   393  				return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{})
   394  			}).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue())))
   395  			Eventually(func() error {
   396  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV))
   397  			}).Should(Succeed())
   398  			Eventually(func() error {
   399  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &dependentCSV))
   400  			}).Should(Succeed())
   401  		}()
   402  
   403  		mainCatalogName := genName("mock-ocs-main-")
   404  
   405  		By("Create separate manifests for each CatalogSource")
   406  		mainManifests := []registry.PackageManifest{
   407  			{
   408  				PackageName: mainPackageName,
   409  				Channels: []registry.PackageChannel{
   410  					{Name: stableChannel, CurrentCSVName: mainPackageStable},
   411  				},
   412  				DefaultChannelName: stableChannel,
   413  			},
   414  		}
   415  
   416  		dependentManifests := []registry.PackageManifest{
   417  			{
   418  				PackageName: dependentPackageName,
   419  				Channels: []registry.PackageChannel{
   420  					{Name: stableChannel, CurrentCSVName: dependentPackageStable},
   421  				},
   422  				DefaultChannelName: stableChannel,
   423  			},
   424  		}
   425  
   426  		By("Create the initial catalogsource")
   427  		_, cleanupSource := createInternalCatalogSource(c, crc, mainCatalogName, generatedNamespace.GetName(), mainManifests, nil, []v1alpha1.ClusterServiceVersion{mainCSV})
   428  
   429  		By("Attempt to get the catalog source before creating install plan")
   430  		fetchedInitialCatalog, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   431  		Expect(err).ShouldNot(HaveOccurred())
   432  		By("Get initial configmap")
   433  		configMap, err := c.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), fetchedInitialCatalog.Spec.ConfigMap, metav1.GetOptions{})
   434  		Expect(err).ShouldNot(HaveOccurred())
   435  
   436  		By("Check pod created")
   437  		initialPods, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{LabelSelector: "olm.configMapResourceVersion=" + configMap.ResourceVersion})
   438  		Expect(err).ShouldNot(HaveOccurred())
   439  		Expect(initialPods.Items).To(HaveLen(1))
   440  
   441  		By("delete the first catalog")
   442  		cleanupSource()
   443  
   444  		By("create a catalog with the same name")
   445  		createInternalCatalogSource(c, crc, mainCatalogName, generatedNamespace.GetName(), append(mainManifests, dependentManifests...), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, dependentCSV})
   446  
   447  		By("Create Subscription")
   448  		subscriptionName := genName("sub-")
   449  		createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", v1alpha1.ApprovalAutomatic)
   450  
   451  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
   452  		Expect(err).ShouldNot(HaveOccurred())
   453  		Expect(subscription).ToNot(BeNil())
   454  		_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(v1alpha1.CSVPhaseSucceeded))
   455  		Expect(err).ShouldNot(HaveOccurred())
   456  	})
   457  
   458  	It("gRPC address catalog source", func() {
   459  
   460  		By("Create an internal (configmap) CatalogSource with stable and dependency csv")
   461  		By("Create an internal (configmap) replacement CatalogSource with a stable, stable-replacement, and dependency csv")
   462  		By("Copy both configmap-server pods to the test namespace")
   463  		By("Delete both CatalogSources")
   464  		By("Create an \"address\" CatalogSource with a Spec.Address field set to the stable copied pod's PodIP")
   465  		By("Create a Subscription to the stable package")
   466  		By("Wait for the stable Subscription to be Successful")
   467  		By("Wait for the stable CSV to be Successful")
   468  		By("Update the \"address\" CatalogSources's Spec.Address field with the PodIP of the replacement copied pod's PodIP")
   469  		By("Wait for the replacement CSV to be Successful")
   470  
   471  		mainPackageName := genName("nginx-")
   472  		dependentPackageName := genName("nginxdep-")
   473  
   474  		mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)
   475  		mainPackageReplacement := fmt.Sprintf("%s-replacement", mainPackageStable)
   476  		dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName)
   477  
   478  		stableChannel := "stable"
   479  
   480  		dependentCRD := newCRD(genName("ins-"))
   481  		mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil)
   482  		replacementCSV := newCSV(mainPackageReplacement, generatedNamespace.GetName(), mainPackageStable, semver.MustParse("0.2.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil)
   483  		dependentCSV := newCSV(dependentPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil, nil)
   484  
   485  		defer func() {
   486  			Eventually(func() error {
   487  				return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{})
   488  			}).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue())))
   489  			Eventually(func() error {
   490  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV))
   491  			}).Should(Succeed())
   492  			Eventually(func() error {
   493  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &dependentCSV))
   494  			}).Should(Succeed())
   495  			Eventually(func() error {
   496  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &replacementCSV))
   497  			}).Should(Succeed())
   498  		}()
   499  
   500  		mainSourceName := genName("mock-ocs-main-")
   501  		replacementSourceName := genName("mock-ocs-main-with-replacement-")
   502  
   503  		By("Create separate manifests for each CatalogSource")
   504  		mainManifests := []registry.PackageManifest{
   505  			{
   506  				PackageName: mainPackageName,
   507  				Channels: []registry.PackageChannel{
   508  					{Name: stableChannel, CurrentCSVName: mainPackageStable},
   509  				},
   510  				DefaultChannelName: stableChannel,
   511  			},
   512  		}
   513  
   514  		replacementManifests := []registry.PackageManifest{
   515  			{
   516  				PackageName: mainPackageName,
   517  				Channels: []registry.PackageChannel{
   518  					{Name: stableChannel, CurrentCSVName: mainPackageReplacement},
   519  				},
   520  				DefaultChannelName: stableChannel,
   521  			},
   522  		}
   523  
   524  		dependentManifests := []registry.PackageManifest{
   525  			{
   526  				PackageName: dependentPackageName,
   527  				Channels: []registry.PackageChannel{
   528  					{Name: stableChannel, CurrentCSVName: dependentPackageStable},
   529  				},
   530  				DefaultChannelName: stableChannel,
   531  			},
   532  		}
   533  
   534  		By("Create ConfigMap CatalogSources")
   535  		createInternalCatalogSource(c, crc, mainSourceName, generatedNamespace.GetName(), append(mainManifests, dependentManifests...), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, dependentCSV})
   536  		createInternalCatalogSource(c, crc, replacementSourceName, generatedNamespace.GetName(), append(replacementManifests, dependentManifests...), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{replacementCSV, mainCSV, dependentCSV})
   537  
   538  		By("Wait for ConfigMap CatalogSources to be ready")
   539  		mainSource, err := fetchCatalogSourceOnStatus(crc, mainSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   540  		Expect(err).ShouldNot(HaveOccurred())
   541  		replacementSource, err := fetchCatalogSourceOnStatus(crc, replacementSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   542  		Expect(err).ShouldNot(HaveOccurred())
   543  
   544  		By("Replicate catalog pods with no OwnerReferences")
   545  		mainCopy := replicateCatalogPod(c, mainSource)
   546  		mainCopy = awaitPod(GinkgoT(), c, mainCopy.GetNamespace(), mainCopy.GetName(), hasPodIP)
   547  		replacementCopy := replicateCatalogPod(c, replacementSource)
   548  		replacementCopy = awaitPod(GinkgoT(), c, replacementCopy.GetNamespace(), replacementCopy.GetName(), hasPodIP)
   549  
   550  		addressSourceName := genName("address-catalog-")
   551  
   552  		By("Create a CatalogSource pointing to the grpc pod")
   553  		addressSource := &v1alpha1.CatalogSource{
   554  			TypeMeta: metav1.TypeMeta{
   555  				Kind:       v1alpha1.CatalogSourceKind,
   556  				APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   557  			},
   558  			ObjectMeta: metav1.ObjectMeta{
   559  				Name:      addressSourceName,
   560  				Namespace: generatedNamespace.GetName(),
   561  			},
   562  			Spec: v1alpha1.CatalogSourceSpec{
   563  				SourceType: v1alpha1.SourceTypeGrpc,
   564  				Address:    net.JoinHostPort(mainCopy.Status.PodIP, "50051"),
   565  				GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   566  					SecurityContextConfig: v1alpha1.Restricted,
   567  				},
   568  			},
   569  		}
   570  
   571  		addressSource, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Create(context.Background(), addressSource, metav1.CreateOptions{})
   572  		Expect(err).ShouldNot(HaveOccurred())
   573  		defer func() {
   574  			err := crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Delete(context.Background(), addressSourceName, metav1.DeleteOptions{})
   575  			Expect(err).ShouldNot(HaveOccurred())
   576  		}()
   577  
   578  		By("Wait for the CatalogSource to be ready")
   579  		_, err = fetchCatalogSourceOnStatus(crc, addressSource.GetName(), addressSource.GetNamespace(), catalogSourceRegistryPodSynced())
   580  		Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
   581  
   582  		By("Delete CatalogSources")
   583  		err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Delete(context.Background(), mainSourceName, metav1.DeleteOptions{})
   584  		Expect(err).ShouldNot(HaveOccurred())
   585  		err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Delete(context.Background(), replacementSourceName, metav1.DeleteOptions{})
   586  		Expect(err).ShouldNot(HaveOccurred())
   587  
   588  		By("Create Subscription")
   589  		subscriptionName := genName("sub-")
   590  		cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, addressSourceName, mainPackageName, stableChannel, "", v1alpha1.ApprovalAutomatic)
   591  		defer cleanupSubscription()
   592  
   593  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
   594  		Expect(err).ShouldNot(HaveOccurred())
   595  		Expect(subscription).ShouldNot(BeNil())
   596  		_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, csvSucceededChecker)
   597  		Expect(err).ShouldNot(HaveOccurred())
   598  
   599  		By("Update the catalog's address to point at the other registry pod's cluster ip")
   600  		Eventually(func() error {
   601  			addressSource, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Get(context.Background(), addressSourceName, metav1.GetOptions{})
   602  			if err != nil {
   603  				return err
   604  			}
   605  
   606  			addressSource.Spec.Address = net.JoinHostPort(replacementCopy.Status.PodIP, "50051")
   607  			_, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Update(context.Background(), addressSource, metav1.UpdateOptions{})
   608  			return err
   609  		}).Should(Succeed())
   610  
   611  		By("Wait for the replacement CSV to be installed")
   612  		_, err = fetchCSV(crc, generatedNamespace.GetName(), replacementCSV.GetName(), csvSucceededChecker)
   613  		Expect(err).ShouldNot(HaveOccurred())
   614  	})
   615  
   616  	It("delete internal registry pod triggers recreation", func() {
   617  
   618  		By("Create internal CatalogSource containing csv in package")
   619  		By("Wait for a registry pod to be created")
   620  		By("Delete the registry pod")
   621  		By("Wait for a new registry pod to be created")
   622  
   623  		By("Create internal CatalogSource containing csv in package")
   624  		packageName := genName("nginx-")
   625  		packageStable := fmt.Sprintf("%s-stable", packageName)
   626  		stableChannel := "stable"
   627  		sourceName := genName("catalog-")
   628  		crd := newCRD(genName("ins-"))
   629  		csv := newCSV(packageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   630  		manifests := []registry.PackageManifest{
   631  			{
   632  				PackageName: packageName,
   633  				Channels: []registry.PackageChannel{
   634  					{Name: stableChannel, CurrentCSVName: packageStable},
   635  				},
   636  				DefaultChannelName: stableChannel,
   637  			},
   638  		}
   639  
   640  		defer func() {
   641  			Eventually(func() error {
   642  				return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{})
   643  			}).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue())))
   644  			Eventually(func() error {
   645  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv))
   646  			}).Should(Succeed())
   647  		}()
   648  
   649  		_, cleanupSource := createInternalCatalogSource(c, crc, sourceName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csv})
   650  		defer cleanupSource()
   651  
   652  		By("Wait for a new registry pod to be created")
   653  		selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": sourceName})
   654  		singlePod := podCount(1)
   655  		registryPods, err := awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), singlePod)
   656  		Expect(err).ShouldNot(HaveOccurred(), "error awaiting registry pod")
   657  		Expect(registryPods).ToNot(BeNil(), "nil registry pods")
   658  		Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found")
   659  
   660  		By("Store the UID for later comparison")
   661  		uid := registryPods.Items[0].GetUID()
   662  
   663  		By("Delete the registry pod")
   664  		Eventually(func() error {
   665  			backgroundDeletion := metav1.DeletePropagationBackground
   666  			return c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{PropagationPolicy: &backgroundDeletion}, metav1.ListOptions{LabelSelector: selector.String()})
   667  		}).Should(Succeed())
   668  
   669  		By("Wait for a new registry pod to be created")
   670  		notUID := func(pods *corev1.PodList) bool {
   671  			uids := make([]string, 0)
   672  			for _, pod := range pods.Items {
   673  				uids = append(uids, string(pod.GetUID()))
   674  				if pod.GetUID() == uid {
   675  					ctx.Ctx().Logf("waiting for %v not to contain %s", uids, uid)
   676  					return false
   677  				}
   678  			}
   679  			ctx.Ctx().Logf("waiting for %v to not be empty and not contain %s", uids, uid)
   680  			return len(pods.Items) > 0
   681  		}
   682  		registryPods, err = awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), unionPodsCheck(singlePod, notUID))
   683  		Expect(err).ShouldNot(HaveOccurred(), "error waiting for replacement registry pod")
   684  		Expect(registryPods).ToNot(BeNil(), "nil replacement registry pods")
   685  		Expect(registryPods.Items).To(HaveLen(1), "unexpected number of replacement registry pods found")
   686  	})
   687  
   688  	It("delete gRPC registry pod triggers recreation", func() {
   689  
   690  		By("Create gRPC CatalogSource using an external registry image (community-operators)")
   691  		By("Wait for a registry pod to be created")
   692  		By("Delete the registry pod")
   693  		By("Wait for a new registry pod to be created")
   694  
   695  		By("Create gRPC CatalogSource using an external registry image (community-operators)")
   696  		source := &v1alpha1.CatalogSource{
   697  			TypeMeta: metav1.TypeMeta{
   698  				Kind:       v1alpha1.CatalogSourceKind,
   699  				APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   700  			},
   701  			ObjectMeta: metav1.ObjectMeta{
   702  				Name:      genName("catalog-"),
   703  				Namespace: generatedNamespace.GetName(),
   704  			},
   705  			Spec: v1alpha1.CatalogSourceSpec{
   706  				SourceType: v1alpha1.SourceTypeGrpc,
   707  				Image:      communityOperatorsImage,
   708  				GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   709  					SecurityContextConfig: v1alpha1.Restricted,
   710  				},
   711  			},
   712  		}
   713  
   714  		source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
   715  		Expect(err).ShouldNot(HaveOccurred())
   716  
   717  		By("Wait for a new registry pod to be created")
   718  		selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": source.GetName()})
   719  		singlePod := podCount(1)
   720  		registryPods, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), singlePod)
   721  		Expect(err).ShouldNot(HaveOccurred(), "error awaiting registry pod")
   722  		Expect(registryPods).ToNot(BeNil(), "nil registry pods")
   723  		Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found")
   724  
   725  		By("Store the UID for later comparison")
   726  		uid := registryPods.Items[0].GetUID()
   727  
   728  		By("Delete the registry pod")
   729  		Eventually(func() error {
   730  			backgroundDeletion := metav1.DeletePropagationBackground
   731  			return c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{PropagationPolicy: &backgroundDeletion}, metav1.ListOptions{LabelSelector: selector.String()})
   732  		}).Should(Succeed())
   733  
   734  		By("Wait for a new registry pod to be created")
   735  		notUID := func(pods *corev1.PodList) bool {
   736  			uids := make([]string, 0)
   737  			for _, pod := range pods.Items {
   738  				uids = append(uids, string(pod.GetUID()))
   739  				if pod.GetUID() == uid {
   740  					ctx.Ctx().Logf("waiting for %v not to contain %s", uids, uid)
   741  					return false
   742  				}
   743  			}
   744  			ctx.Ctx().Logf("waiting for %v to not be empty and not contain %s", uids, uid)
   745  			return len(pods.Items) > 0
   746  		}
   747  		registryPods, err = awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), unionPodsCheck(singlePod, notUID))
   748  		Expect(err).ShouldNot(HaveOccurred(), "error waiting for replacement registry pod")
   749  		Expect(registryPods).ShouldNot(BeNil(), "nil replacement registry pods")
   750  		Expect(registryPods.Items).To(HaveLen(1), "unexpected number of replacement registry pods found")
   751  	})
   752  
   753  	It("configure gRPC registry pod to extract content", func() {
   754  
   755  		By("Create gRPC CatalogSource using an external registry image (community-operators)")
   756  		source := &v1alpha1.CatalogSource{
   757  			TypeMeta: metav1.TypeMeta{
   758  				Kind:       v1alpha1.CatalogSourceKind,
   759  				APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   760  			},
   761  			ObjectMeta: metav1.ObjectMeta{
   762  				Name:      genName("catalog-"),
   763  				Namespace: generatedNamespace.GetName(),
   764  			},
   765  			Spec: v1alpha1.CatalogSourceSpec{
   766  				SourceType: v1alpha1.SourceTypeGrpc,
   767  				Image:      communityOperatorsImage,
   768  				GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   769  					SecurityContextConfig: v1alpha1.Restricted,
   770  					ExtractContent: &v1alpha1.ExtractContentConfig{
   771  						CacheDir:   "/tmp/cache",
   772  						CatalogDir: "/configs",
   773  					},
   774  				},
   775  			},
   776  		}
   777  
   778  		source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
   779  		Expect(err).ShouldNot(HaveOccurred())
   780  
   781  		By("Wait for the CatalogSource to be ready")
   782  		source, err = fetchCatalogSourceOnStatus(crc, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced())
   783  		Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
   784  
   785  		By("the gRPC endpoints are not exposed from the pod, and there's no simple way to get at them -")
   786  		By("the index images don't contain `grpcurl`, port-forwarding is a mess, etc. let's use the")
   787  		By("package-server as a proxy for a functional catalog")
   788  		By("Waiting for packages from the catalog to show up in the Kubernetes API")
   789  		Eventually(func() error {
   790  			manifests, err := packageserverClient.OperatorsV1().PackageManifests("default").List(context.Background(), metav1.ListOptions{})
   791  			if err != nil {
   792  				return err
   793  			}
   794  			if len(manifests.Items) == 0 {
   795  				return errors.New("did not find any PackageManifests")
   796  			}
   797  			return nil
   798  		}).Should(Succeed())
   799  	})
   800  
   801  	It("image update", func() {
   802  		if ok, err := inKind(c); ok && err == nil {
   803  			Skip("This spec fails when run using KIND cluster. See https://github.com/operator-framework/operator-lifecycle-manager/issues/2420 for more details")
   804  		} else if err != nil {
   805  			Skip("Could not determine whether running in a kind cluster. Skipping.")
   806  		}
   807  		By("Create an image based catalog source from public Quay image")
   808  		By("Use a unique tag as identifier")
   809  		By("See https://quay.io/repository/olmtest/catsrc-update-test?namespace=olmtest for registry")
   810  		By("Push an updated version of the image with the same identifier")
   811  		By("Confirm catalog source polling feature is working as expected: a newer version of the catalog source pod comes up")
   812  		By("etcd operator updated from 0.9.0 to 0.9.2-clusterwide")
   813  		By("Subscription should detect the latest version of the operator in the new catalog source and pull it")
   814  
   815  		By("create internal registry for purposes of pushing/pulling IF running e2e test locally")
   816  		By("registry is insecure and for purposes of this test only")
   817  
   818  		local, err := Local(c)
   819  		Expect(err).NotTo(HaveOccurred(), "cannot determine if test running locally or on CI: %s", err)
   820  
   821  		By("Create an image based catalog source from public Quay image using a unique tag as identifier")
   822  		var registryURL string
   823  		var registryAuthSecretName string
   824  		if local {
   825  			By("Creating a local registry to use")
   826  			registryURL, err = createDockerRegistry(c, generatedNamespace.GetName())
   827  			Expect(err).NotTo(HaveOccurred(), "error creating container registry: %s", err)
   828  			defer deleteDockerRegistry(c, generatedNamespace.GetName())
   829  
   830  			By("ensure registry pod with local URL " + registryURL + " is ready before attempting port-forwarding")
   831  			_ = awaitPod(GinkgoT(), c, generatedNamespace.GetName(), registryName, podReady)
   832  
   833  			By("By port-forwarding to the registry")
   834  			err = registryPortForward(generatedNamespace.GetName())
   835  			Expect(err).NotTo(HaveOccurred(), "port-forwarding local registry: %s", err)
   836  		} else {
   837  			registryURL = fmt.Sprintf("%s/%s", openshiftregistryFQDN, generatedNamespace.GetName())
   838  			By("Using the OpenShift registry at " + registryURL)
   839  			registryAuthSecretName, err = getRegistryAuthSecretName(c, generatedNamespace.GetName())
   840  			Expect(err).NotTo(HaveOccurred(), "error getting openshift registry authentication: %s", err)
   841  		}
   842  
   843  		By("testImage is the name of the image used throughout the test - the image overwritten by skopeo")
   844  		By("the tag is generated randomly and appended to the end of the testImage")
   845  		testImage := fmt.Sprint("docker://", registryURL, "/catsrc-update", ":")
   846  		tag := genName("x")
   847  		By("Generating a target test image name " + testImage + " with tag " + tag)
   848  
   849  		By("copying old catalog image into test-specific tag in internal docker registry")
   850  		if local {
   851  			By("executing out to a local skopeo client")
   852  			_, err := skopeoLocalCopy(testImage, tag, catsrcImage, "old")
   853  			Expect(err).NotTo(HaveOccurred(), "error copying old registry file: %s", err)
   854  		} else {
   855  			By("creating a skopoeo Pod to do the copying")
   856  			skopeoArgs := skopeoCopyCmd(testImage, tag, catsrcImage, "old", registryAuthSecretName)
   857  			err = createSkopeoPod(c, skopeoArgs, generatedNamespace.GetName(), registryAuthSecretName)
   858  			Expect(err).NotTo(HaveOccurred(), "error creating skopeo pod: %s", err)
   859  
   860  			By("waiting for the skopeo pod to exit successfully")
   861  			awaitPod(GinkgoT(), c, generatedNamespace.GetName(), skopeo, func(pod *corev1.Pod) bool {
   862  				return pod.Status.Phase == corev1.PodSucceeded
   863  			})
   864  
   865  			By("removing the skopeo pod")
   866  			err = deleteSkopeoPod(c, generatedNamespace.GetName())
   867  			Expect(err).NotTo(HaveOccurred(), "error deleting skopeo pod: %s", err)
   868  		}
   869  
   870  		By("setting catalog source")
   871  		sourceName := genName("catalog-")
   872  		packageName := "busybox"
   873  		channelName := "alpha"
   874  
   875  		By("Create gRPC CatalogSource using an external registry image and poll interval")
   876  		var image string
   877  		image = testImage[9:] // strip off docker://
   878  		image = fmt.Sprint(image, tag)
   879  
   880  		source := &v1alpha1.CatalogSource{
   881  			TypeMeta: metav1.TypeMeta{
   882  				Kind:       v1alpha1.CatalogSourceKind,
   883  				APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   884  			},
   885  			ObjectMeta: metav1.ObjectMeta{
   886  				Name:      sourceName,
   887  				Namespace: generatedNamespace.GetName(),
   888  				Labels:    map[string]string{"olm.catalogSource": sourceName},
   889  			},
   890  			Spec: v1alpha1.CatalogSourceSpec{
   891  				SourceType: v1alpha1.SourceTypeGrpc,
   892  				Image:      image,
   893  				GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   894  					SecurityContextConfig: v1alpha1.Restricted,
   895  				},
   896  				UpdateStrategy: &v1alpha1.UpdateStrategy{
   897  					RegistryPoll: &v1alpha1.RegistryPoll{
   898  						// Using RawInterval rather than Interval due to this issue:
   899  						// https://github.com/operator-framework/operator-lifecycle-manager/issues/2621
   900  						RawInterval: "1m0s",
   901  					},
   902  				},
   903  			},
   904  		}
   905  
   906  		source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
   907  		Expect(err).ShouldNot(HaveOccurred())
   908  		defer func() {
   909  			err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Delete(context.Background(), source.GetName(), metav1.DeleteOptions{})
   910  			Expect(err).ShouldNot(HaveOccurred())
   911  		}()
   912  
   913  		By("wait for new catalog source pod to be created")
   914  		By("Wait for a new registry pod to be created")
   915  		selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": source.GetName()})
   916  		singlePod := podCount(1)
   917  		registryPods, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), singlePod)
   918  		Expect(err).ToNot(HaveOccurred(), "error awaiting registry pod")
   919  		Expect(registryPods).ShouldNot(BeNil(), "nil registry pods")
   920  		Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found")
   921  
   922  		By("Create a Subscription for package")
   923  		subscriptionName := genName("sub-")
   924  		cleanupSubscription := createSubscriptionForCatalog(crc, source.GetNamespace(), subscriptionName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic)
   925  		defer cleanupSubscription()
   926  
   927  		By("Wait for the Subscription to succeed")
   928  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
   929  		Expect(err).ShouldNot(HaveOccurred())
   930  		Expect(subscription).ShouldNot(BeNil())
   931  
   932  		By("Wait for csv to succeed")
   933  		_, err = fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker)
   934  		Expect(err).ShouldNot(HaveOccurred())
   935  
   936  		registryCheckFunc := func(podList *corev1.PodList) bool {
   937  			if len(podList.Items) > 1 {
   938  				return false
   939  			}
   940  			return podList.Items[0].Status.ContainerStatuses[0].ImageID != ""
   941  		}
   942  		By("get old catalog source pod")
   943  		registryPod, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), registryCheckFunc)
   944  		By("Updateing image on registry to trigger a newly updated version of the catalog source pod to be deployed after some time")
   945  		if local {
   946  			By("executing out to a local skopeo client")
   947  			_, err := skopeoLocalCopy(testImage, tag, catsrcImage, "new")
   948  			Expect(err).NotTo(HaveOccurred(), "error copying new registry file: %s", err)
   949  		} else {
   950  			By("creating a skopoeo Pod to do the copying")
   951  			skopeoArgs := skopeoCopyCmd(testImage, tag, catsrcImage, "new", registryAuthSecretName)
   952  			err = createSkopeoPod(c, skopeoArgs, generatedNamespace.GetName(), registryAuthSecretName)
   953  			Expect(err).NotTo(HaveOccurred(), "error creating skopeo pod: %s", err)
   954  
   955  			By("waiting for the skopeo pod to exit successfully")
   956  			awaitPod(GinkgoT(), c, generatedNamespace.GetName(), skopeo, func(pod *corev1.Pod) bool {
   957  				return pod.Status.Phase == corev1.PodSucceeded
   958  			})
   959  
   960  			By("removing the skopeo pod")
   961  			err = deleteSkopeoPod(c, generatedNamespace.GetName())
   962  			Expect(err).NotTo(HaveOccurred(), "error deleting skopeo pod: %s", err)
   963  		}
   964  
   965  		By("update catalog source with annotation (to kick resync)")
   966  		Eventually(func() error {
   967  			source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{})
   968  			if err != nil {
   969  				return nil
   970  			}
   971  
   972  			source.Annotations = make(map[string]string)
   973  			source.Annotations["testKey"] = "testValue"
   974  			_, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{})
   975  			return err
   976  		}).Should(Succeed())
   977  
   978  		By("ensure new registry pod container image is as we expect")
   979  		podCheckFunc := func(podList *corev1.PodList) bool {
   980  			ctx.Ctx().Logf("pod list length %d\n", len(podList.Items))
   981  			for _, pod := range podList.Items {
   982  				ctx.Ctx().Logf("pod list name %v\n", pod.Name)
   983  			}
   984  
   985  			for _, pod := range podList.Items {
   986  				ctx.Ctx().Logf("old image id %s\n new image id %s\n", registryPod.Items[0].Status.ContainerStatuses[0].ImageID,
   987  					pod.Status.ContainerStatuses[0].ImageID)
   988  				if pod.Status.ContainerStatuses[0].ImageID != registryPod.Items[0].Status.ContainerStatuses[0].ImageID {
   989  					return true
   990  				}
   991  			}
   992  			By("update catalog source with annotation (to kick resync)")
   993  			Eventually(func() error {
   994  				source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{})
   995  				if err != nil {
   996  					return err
   997  				}
   998  
   999  				if source.Annotations == nil {
  1000  					source.Annotations = make(map[string]string)
  1001  				}
  1002  
  1003  				source.Annotations["testKey"] = genName("newValue")
  1004  				_, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{})
  1005  				return err
  1006  			}).Should(Succeed())
  1007  
  1008  			return false
  1009  		}
  1010  		By("await new catalog source and ensure old one was deleted")
  1011  		registryPods, err = awaitPodsWithInterval(GinkgoT(), c, source.GetNamespace(), selector.String(), 30*time.Second, 10*time.Minute, podCheckFunc)
  1012  		Expect(err).ShouldNot(HaveOccurred(), "error awaiting registry pod")
  1013  		Expect(registryPods).ShouldNot(BeNil(), "nil registry pods")
  1014  		Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found")
  1015  
  1016  		By("update catalog source with annotation (to kick resync)")
  1017  		Eventually(func() error {
  1018  			source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{})
  1019  			if err != nil {
  1020  				return err
  1021  			}
  1022  
  1023  			source.Annotations["testKey"] = "newValue"
  1024  			_, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{})
  1025  			return err
  1026  		}).Should(Succeed())
  1027  
  1028  		subChecker := func(sub *v1alpha1.Subscription) bool {
  1029  			return sub.Status.InstalledCSV == "busybox.v2.0.0"
  1030  		}
  1031  		By("Wait for the Subscription to succeed")
  1032  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subChecker)
  1033  		Expect(err).ShouldNot(HaveOccurred())
  1034  		Expect(subscription).ShouldNot(BeNil())
  1035  
  1036  		By("Wait for csv to succeed")
  1037  		csv, err := fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker)
  1038  		Expect(err).ShouldNot(HaveOccurred())
  1039  
  1040  		By("check version of running csv to ensure the latest version (0.9.2) was installed onto the cluster")
  1041  		v := csv.Spec.Version
  1042  		busyboxVersion := semver.Version{
  1043  			Major: 2,
  1044  			Minor: 0,
  1045  			Patch: 0,
  1046  		}
  1047  
  1048  		Expect(v).Should(Equal(version.OperatorVersion{Version: busyboxVersion}), "latest version of operator not installed: catalog source update failed")
  1049  	})
  1050  
  1051  	It("Dependency has correct replaces field", func() {
  1052  		By("Create a CatalogSource that contains the busybox v1 and busybox-dependency v1 images")
  1053  		By("Create a Subscription for busybox v1, which has a dependency on busybox-dependency v1.")
  1054  		By("Wait for the busybox and busybox2 Subscriptions to succeed")
  1055  		By("Wait for the CSVs to succeed")
  1056  		By("Update the catalog to point to an image that contains the busybox v2 and busybox-dependency v2 images.")
  1057  		By("Wait for the new Subscriptions to succeed and check if they include the new CSVs")
  1058  		By("Wait for the CSVs to succeed and confirm that the have the correct Spec.Replaces fields.")
  1059  
  1060  		sourceName := genName("catalog-")
  1061  		packageName := "busybox"
  1062  		channelName := "alpha"
  1063  
  1064  		catSrcImage := "quay.io/olmtest/busybox-dependencies-index"
  1065  
  1066  		By("creating gRPC CatalogSource")
  1067  		source := &v1alpha1.CatalogSource{
  1068  			TypeMeta: metav1.TypeMeta{
  1069  				Kind:       v1alpha1.CatalogSourceKind,
  1070  				APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
  1071  			},
  1072  			ObjectMeta: metav1.ObjectMeta{
  1073  				Name:      sourceName,
  1074  				Namespace: generatedNamespace.GetName(),
  1075  			},
  1076  			Spec: v1alpha1.CatalogSourceSpec{
  1077  				SourceType: v1alpha1.SourceTypeGrpc,
  1078  				Image:      catSrcImage + ":1.0.0-with-ListBundles-method",
  1079  				GrpcPodConfig: &v1alpha1.GrpcPodConfig{
  1080  					SecurityContextConfig: v1alpha1.Restricted,
  1081  				},
  1082  			},
  1083  		}
  1084  		source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
  1085  		Expect(err).ShouldNot(HaveOccurred())
  1086  		defer func() {
  1087  			err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Delete(context.Background(), source.GetName(), metav1.DeleteOptions{})
  1088  			Expect(err).ShouldNot(HaveOccurred())
  1089  		}()
  1090  
  1091  		By("waiting for the CatalogSource to be ready")
  1092  		_, err = fetchCatalogSourceOnStatus(crc, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced())
  1093  		Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
  1094  
  1095  		By("creating a Subscription for busybox")
  1096  		subscriptionName := genName("sub-")
  1097  		cleanupSubscription := createSubscriptionForCatalog(crc, source.GetNamespace(), subscriptionName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic)
  1098  		defer cleanupSubscription()
  1099  
  1100  		By("waiting for the Subscription to succeed")
  1101  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
  1102  		Expect(err).ShouldNot(HaveOccurred())
  1103  		Expect(subscription).ShouldNot(BeNil())
  1104  		Expect(subscription.Status.InstalledCSV).To(Equal("busybox.v1.0.0"))
  1105  
  1106  		By("confirming that a subscription was created for busybox-dependency")
  1107  		subscriptionList, err := crc.OperatorsV1alpha1().Subscriptions(source.GetNamespace()).List(context.Background(), metav1.ListOptions{})
  1108  		Expect(err).ShouldNot(HaveOccurred())
  1109  		dependencySubscriptionName := ""
  1110  		for _, sub := range subscriptionList.Items {
  1111  			if strings.HasPrefix(sub.GetName(), "busybox-dependency") {
  1112  				dependencySubscriptionName = sub.GetName()
  1113  			}
  1114  		}
  1115  		Expect(dependencySubscriptionName).ToNot(BeEmpty())
  1116  
  1117  		By("waiting for the Subscription to succeed")
  1118  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), dependencySubscriptionName, subscriptionStateAtLatestChecker())
  1119  		Expect(err).ShouldNot(HaveOccurred())
  1120  		Expect(subscription).ShouldNot(BeNil())
  1121  		Expect(subscription.Status.InstalledCSV).To(Equal("busybox-dependency.v1.0.0"))
  1122  
  1123  		By("updating the catalog image")
  1124  		Eventually(func() error {
  1125  			existingSource, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), sourceName, metav1.GetOptions{})
  1126  			if err != nil {
  1127  				return err
  1128  			}
  1129  			existingSource.Spec.Image = catSrcImage + ":2.0.0-with-ListBundles-method"
  1130  
  1131  			source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), existingSource, metav1.UpdateOptions{})
  1132  			return err
  1133  		}).Should(Succeed())
  1134  
  1135  		By("waiting for the CatalogSource to be ready")
  1136  		_, err = fetchCatalogSourceOnStatus(crc, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced())
  1137  		Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
  1138  
  1139  		By("waiting for the busybox v2 Subscription to succeed")
  1140  		subChecker := func(sub *v1alpha1.Subscription) bool {
  1141  			return sub.Status.InstalledCSV == "busybox.v2.0.0"
  1142  		}
  1143  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subChecker)
  1144  		Expect(err).ShouldNot(HaveOccurred())
  1145  		Expect(subscription).ShouldNot(BeNil())
  1146  
  1147  		By("waiting for busybox v2 csv to succeed and check the replaces field")
  1148  		csv, err := fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker)
  1149  		Expect(err).ShouldNot(HaveOccurred())
  1150  		Expect(csv.Spec.Replaces).To(Equal("busybox.v1.0.0"))
  1151  
  1152  		By("waiting for the busybox-dependency v2 Subscription to succeed")
  1153  		subChecker = func(sub *v1alpha1.Subscription) bool {
  1154  			return sub.Status.InstalledCSV == "busybox-dependency.v2.0.0"
  1155  		}
  1156  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), dependencySubscriptionName, subChecker)
  1157  		Expect(err).ShouldNot(HaveOccurred())
  1158  		Expect(subscription).ShouldNot(BeNil())
  1159  
  1160  		By("waiting for busybox-dependency v2 csv to succeed and check the replaces field")
  1161  		csv, err = fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker)
  1162  		Expect(err).ShouldNot(HaveOccurred())
  1163  		Expect(csv.Spec.Replaces).To(Equal("busybox-dependency.v1.0.0"))
  1164  	})
  1165  	When("A catalogSource is created with correct polling interval", func() {
  1166  		var source *v1alpha1.CatalogSource
  1167  		singlePod := podCount(1)
  1168  		sourceName := genName("catalog-")
  1169  
  1170  		BeforeEach(func() {
  1171  			source = &v1alpha1.CatalogSource{
  1172  				TypeMeta: metav1.TypeMeta{
  1173  					Kind:       v1alpha1.CatalogSourceKind,
  1174  					APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
  1175  				},
  1176  				ObjectMeta: metav1.ObjectMeta{
  1177  					Name:      sourceName,
  1178  					Namespace: generatedNamespace.GetName(),
  1179  					Labels:    map[string]string{"olm.catalogSource": sourceName},
  1180  				},
  1181  				Spec: v1alpha1.CatalogSourceSpec{
  1182  					SourceType: v1alpha1.SourceTypeGrpc,
  1183  					Image:      "quay.io/olmtest/catsrc-update-test:new",
  1184  					GrpcPodConfig: &v1alpha1.GrpcPodConfig{
  1185  						SecurityContextConfig: v1alpha1.Restricted,
  1186  					},
  1187  					UpdateStrategy: &v1alpha1.UpdateStrategy{
  1188  						RegistryPoll: &v1alpha1.RegistryPoll{
  1189  							RawInterval: "45s",
  1190  						},
  1191  					},
  1192  				},
  1193  			}
  1194  
  1195  			source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
  1196  			Expect(err).ToNot(HaveOccurred())
  1197  
  1198  			By("wait for new catalog source pod to be created and report ready")
  1199  			selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": source.GetName()})
  1200  
  1201  			catalogPods, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), singlePod)
  1202  			Expect(err).ToNot(HaveOccurred())
  1203  			Expect(catalogPods).ToNot(BeNil())
  1204  
  1205  			Eventually(func() (bool, error) {
  1206  				podList, err := c.KubernetesInterface().CoreV1().Pods(source.GetNamespace()).List(context.Background(), metav1.ListOptions{LabelSelector: selector.String()})
  1207  				if err != nil {
  1208  					return false, err
  1209  				}
  1210  
  1211  				for _, p := range podList.Items {
  1212  					if podReady(&p) {
  1213  						return true, nil
  1214  					}
  1215  					return false, nil
  1216  				}
  1217  
  1218  				return false, nil
  1219  			}).Should(BeTrue())
  1220  		})
  1221  
  1222  		It("registry polls on the correct interval", func() {
  1223  			By("Wait roughly the polling interval for update pod to show up")
  1224  			updateSelector := labels.SelectorFromSet(map[string]string{"catalogsource.operators.coreos.com/update": source.GetName()})
  1225  			updatePods, err := awaitPodsWithInterval(GinkgoT(), c, source.GetNamespace(), updateSelector.String(), 5*time.Second, 2*time.Minute, singlePod)
  1226  			Expect(err).ToNot(HaveOccurred())
  1227  			Expect(updatePods).ToNot(BeNil())
  1228  			Expect(updatePods.Items).To(HaveLen(1))
  1229  
  1230  			By("No update to image: update pod should be deleted quickly")
  1231  			noPod := podCount(0)
  1232  			updatePods, err = awaitPodsWithInterval(GinkgoT(), c, source.GetNamespace(), updateSelector.String(), 1*time.Second, 30*time.Second, noPod)
  1233  			Expect(err).ToNot(HaveOccurred())
  1234  			Expect(updatePods.Items).To(HaveLen(0))
  1235  		})
  1236  
  1237  	})
  1238  
  1239  	When("A catalogSource is created with incorrect polling interval", func() {
  1240  		var (
  1241  			source     *v1alpha1.CatalogSource
  1242  			sourceName string
  1243  		)
  1244  
  1245  		const (
  1246  			incorrectInterval = "45mError.code"
  1247  			correctInterval   = "45m"
  1248  		)
  1249  
  1250  		BeforeEach(func() {
  1251  			sourceName = genName("catalog-")
  1252  			source = &v1alpha1.CatalogSource{
  1253  				TypeMeta: metav1.TypeMeta{
  1254  					Kind:       v1alpha1.CatalogSourceKind,
  1255  					APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
  1256  				},
  1257  				ObjectMeta: metav1.ObjectMeta{
  1258  					Name:      sourceName,
  1259  					Namespace: generatedNamespace.GetName(),
  1260  					Labels:    map[string]string{"olm.catalogSource": sourceName},
  1261  				},
  1262  				Spec: v1alpha1.CatalogSourceSpec{
  1263  					SourceType: v1alpha1.SourceTypeGrpc,
  1264  					Image:      "quay.io/olmtest/catsrc-update-test:new",
  1265  					GrpcPodConfig: &v1alpha1.GrpcPodConfig{
  1266  						SecurityContextConfig: v1alpha1.Restricted,
  1267  					},
  1268  					UpdateStrategy: &v1alpha1.UpdateStrategy{
  1269  						RegistryPoll: &v1alpha1.RegistryPoll{
  1270  							RawInterval: incorrectInterval,
  1271  						},
  1272  					},
  1273  				},
  1274  			}
  1275  
  1276  			_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
  1277  			Expect(err).ToNot(HaveOccurred())
  1278  
  1279  		})
  1280  		AfterEach(func() {
  1281  			err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Delete(context.Background(), source.GetName(), metav1.DeleteOptions{})
  1282  			Expect(err).ToNot(HaveOccurred())
  1283  		})
  1284  		It("the catalogsource status communicates that a default interval time is being used instead", func() {
  1285  			Eventually(func() bool {
  1286  				catsrc, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{})
  1287  				Expect(err).ToNot(HaveOccurred())
  1288  				if catsrc.Status.Reason == v1alpha1.CatalogSourceIntervalInvalidError {
  1289  					if catsrc.Status.Message == "error parsing spec.updateStrategy.registryPoll.interval. Using the default value of 15m0s instead. Error: time: unknown unit \"mError\" in duration \"45mError.code\"" {
  1290  						return true
  1291  					}
  1292  				}
  1293  				return false
  1294  			}).Should(BeTrue())
  1295  		})
  1296  		When("the catalogsource is updated with a valid polling interval", func() {
  1297  
  1298  			BeforeEach(func() {
  1299  				Eventually(func() error {
  1300  					catsrc, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{})
  1301  					if err != nil {
  1302  						return err
  1303  					}
  1304  					catsrc.Spec.UpdateStrategy.RegistryPoll.RawInterval = correctInterval
  1305  					_, err = crc.OperatorsV1alpha1().CatalogSources(catsrc.GetNamespace()).Update(context.Background(), catsrc, metav1.UpdateOptions{})
  1306  					return err
  1307  				}).Should(Succeed())
  1308  			})
  1309  
  1310  			It("the catalogsource spec shows the updated polling interval, and the error message in the status is cleared", func() {
  1311  				Eventually(func() error {
  1312  					catsrc, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{})
  1313  					if err != nil {
  1314  						return err
  1315  					}
  1316  					expectedTime, err := time.ParseDuration(correctInterval)
  1317  					if err != nil {
  1318  						return err
  1319  					}
  1320  					if catsrc.Status.Reason != "" || (catsrc.Spec.UpdateStrategy.Interval != &metav1.Duration{Duration: expectedTime}) {
  1321  						return err
  1322  					}
  1323  					return nil
  1324  				}).Should(Succeed())
  1325  			})
  1326  		})
  1327  	})
  1328  
  1329  	It("adding catalog template adjusts image used", func() {
  1330  		By("This test attempts to create a catalog source, and update it with a template annotation")
  1331  		By("and ensure that the image gets changed according to what's in the template as well as")
  1332  		By("check the status conditions are updated accordingly")
  1333  		sourceName := genName("catalog-")
  1334  		source := &v1alpha1.CatalogSource{
  1335  			TypeMeta: metav1.TypeMeta{
  1336  				Kind:       v1alpha1.CatalogSourceKind,
  1337  				APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
  1338  			},
  1339  			ObjectMeta: metav1.ObjectMeta{
  1340  				Name:      sourceName,
  1341  				Namespace: generatedNamespace.GetName(),
  1342  				Labels:    map[string]string{"olm.catalogSource": sourceName},
  1343  			},
  1344  			Spec: v1alpha1.CatalogSourceSpec{
  1345  				SourceType: v1alpha1.SourceTypeGrpc,
  1346  				Image:      "quay.io/olmtest/catsrc-update-test:old",
  1347  				GrpcPodConfig: &v1alpha1.GrpcPodConfig{
  1348  					SecurityContextConfig: v1alpha1.Restricted,
  1349  				},
  1350  			},
  1351  		}
  1352  
  1353  		By("creating a catalog source")
  1354  
  1355  		var err error
  1356  		Eventually(func() error {
  1357  			source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
  1358  			return err
  1359  		}).Should(Succeed())
  1360  
  1361  		By("updating the catalog source with template annotation")
  1362  
  1363  		Eventually(func() error {
  1364  			source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{})
  1365  			if err != nil {
  1366  				return err
  1367  			}
  1368  			By("create an annotation using the kube templates")
  1369  			source.SetAnnotations(map[string]string{
  1370  				catalogsource.CatalogImageTemplateAnnotation: fmt.Sprintf("quay.io/olmtest/catsrc-update-test:%s.%s.%s", catalogsource.TemplKubeMajorV, catalogsource.TemplKubeMinorV, catalogsource.TemplKubePatchV),
  1371  			})
  1372  
  1373  			By("Update the catalog image")
  1374  			_, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{})
  1375  			return err
  1376  		}).Should(Succeed())
  1377  
  1378  		By("wait for status condition to show up")
  1379  		Eventually(func() (bool, error) {
  1380  			source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), sourceName, metav1.GetOptions{})
  1381  			if err != nil {
  1382  				return false, err
  1383  			}
  1384  			By("if the conditions array has the entry we know things got updated")
  1385  			condition := meta.FindStatusCondition(source.Status.Conditions, catalogtemplate.StatusTypeTemplatesHaveResolved)
  1386  			if condition != nil {
  1387  				return true, nil
  1388  			}
  1389  
  1390  			return false, nil
  1391  		}).Should(BeTrue())
  1392  
  1393  		By("source should be the latest we got from the eventually block")
  1394  		Expect(source.Status.Conditions).ToNot(BeNil())
  1395  
  1396  		templatesResolvedCondition := meta.FindStatusCondition(source.Status.Conditions, catalogtemplate.StatusTypeTemplatesHaveResolved)
  1397  		if Expect(templatesResolvedCondition).ToNot(BeNil()) {
  1398  			Expect(templatesResolvedCondition.Reason).To(BeIdenticalTo(catalogtemplate.ReasonAllTemplatesResolved))
  1399  			Expect(templatesResolvedCondition.Status).To(BeIdenticalTo(metav1.ConditionTrue))
  1400  		}
  1401  		resolvedImageCondition := meta.FindStatusCondition(source.Status.Conditions, catalogtemplate.StatusTypeResolvedImage)
  1402  		if Expect(resolvedImageCondition).ToNot(BeNil()) {
  1403  			Expect(resolvedImageCondition.Reason).To(BeIdenticalTo(catalogtemplate.ReasonAllTemplatesResolved))
  1404  			Expect(resolvedImageCondition.Status).To(BeIdenticalTo(metav1.ConditionTrue))
  1405  
  1406  			By("if we can, try to determine the server version so we can check the resulting image")
  1407  			if serverVersion, err := crc.Discovery().ServerVersion(); err != nil {
  1408  				if serverGitVersion, err := semver.Parse(serverVersion.GitVersion); err != nil {
  1409  					expectedImage := fmt.Sprintf("quay.io/olmtest/catsrc-update-test:%s.%s.%s", serverVersion.Major, serverVersion.Minor, strconv.FormatUint(serverGitVersion.Patch, 10))
  1410  					Expect(resolvedImageCondition.Message).To(BeIdenticalTo(expectedImage))
  1411  				}
  1412  			}
  1413  		}
  1414  	})
  1415  
  1416  	When("A CatalogSource is created with an operator that has a CSV with missing metadata.ApiVersion", func() {
  1417  		var (
  1418  			magicCatalog      *MagicCatalog
  1419  			catalogSourceName string
  1420  			subscription      *v1alpha1.Subscription
  1421  			c                 client.Client
  1422  		)
  1423  
  1424  		BeforeEach(func() {
  1425  			c = ctx.Ctx().Client()
  1426  
  1427  			provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, badCSVDir, "bad-csv.yaml"))
  1428  			Expect(err).To(BeNil())
  1429  
  1430  			catalogSourceName = genName("cat-bad-csv")
  1431  			magicCatalog = NewMagicCatalog(c, generatedNamespace.GetName(), catalogSourceName, provider)
  1432  			Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil())
  1433  
  1434  		})
  1435  
  1436  		AfterEach(func() {
  1437  			TeardownNamespace(generatedNamespace.GetName())
  1438  		})
  1439  
  1440  		When("A Subscription is created catalogSource built with the malformed CSV", func() {
  1441  
  1442  			BeforeEach(func() {
  1443  				subscription = &v1alpha1.Subscription{
  1444  					ObjectMeta: metav1.ObjectMeta{
  1445  						Name:      fmt.Sprintf("%s-sub", catalogSourceName),
  1446  						Namespace: generatedNamespace.GetName(),
  1447  					},
  1448  					Spec: &v1alpha1.SubscriptionSpec{
  1449  						CatalogSource:          catalogSourceName,
  1450  						CatalogSourceNamespace: generatedNamespace.GetName(),
  1451  						Channel:                "stable",
  1452  						Package:                "test-package",
  1453  					},
  1454  				}
  1455  				Expect(c.Create(context.Background(), subscription)).To(BeNil())
  1456  			})
  1457  
  1458  			It("fails with a BundleUnpackFailed error condition, and a message that highlights the missing field in the CSV", func() {
  1459  				Eventually(func(g Gomega) string {
  1460  					fetchedSubscription, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subscription.GetName(), metav1.GetOptions{})
  1461  					g.Expect(err).NotTo(HaveOccurred())
  1462  
  1463  					By("expect the message that API missing")
  1464  					failingCondition := fetchedSubscription.Status.GetCondition(v1alpha1.SubscriptionBundleUnpackFailed)
  1465  					return failingCondition.Message
  1466  				}).Should(ContainSubstring("missing APIVersion"))
  1467  			})
  1468  		})
  1469  	})
  1470  	When("The namespace is labled as Pod Security Admission policy enforce:restricted", func() {
  1471  		BeforeEach(func() {
  1472  			var err error
  1473  			testNS := &corev1.Namespace{}
  1474  			Eventually(func() error {
  1475  				testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), generatedNamespace.GetName(), metav1.GetOptions{})
  1476  				if err != nil {
  1477  					return err
  1478  				}
  1479  				return nil
  1480  			}).Should(BeNil())
  1481  
  1482  			testNS.ObjectMeta.Labels = map[string]string{
  1483  				"pod-security.kubernetes.io/enforce":         "restricted",
  1484  				"pod-security.kubernetes.io/enforce-version": "latest",
  1485  			}
  1486  
  1487  			Eventually(func() error {
  1488  				_, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{})
  1489  				if err != nil {
  1490  					return err
  1491  				}
  1492  				return nil
  1493  			}).Should(BeNil())
  1494  		})
  1495  		When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created with spec.GrpcPodConfig.SecurityContextConfig set to restricted", func() {
  1496  			var sourceName string
  1497  			BeforeEach(func() {
  1498  				sourceName = genName("catalog-")
  1499  				source := &v1alpha1.CatalogSource{
  1500  					TypeMeta: metav1.TypeMeta{
  1501  						Kind:       v1alpha1.CatalogSourceKind,
  1502  						APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
  1503  					},
  1504  					ObjectMeta: metav1.ObjectMeta{
  1505  						Name:      sourceName,
  1506  						Namespace: generatedNamespace.GetName(),
  1507  						Labels:    map[string]string{"olm.catalogSource": sourceName},
  1508  					},
  1509  					Spec: v1alpha1.CatalogSourceSpec{
  1510  						SourceType: v1alpha1.SourceTypeGrpc,
  1511  						Image:      "quay.io/olmtest/old-opm-catsrc:v1.21.0",
  1512  						GrpcPodConfig: &v1alpha1.GrpcPodConfig{
  1513  							SecurityContextConfig: v1alpha1.Restricted,
  1514  						},
  1515  					},
  1516  				}
  1517  
  1518  				Eventually(func() error {
  1519  					_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
  1520  					return err
  1521  				}).Should(Succeed())
  1522  			})
  1523  			It("The registry pod fails to become come up because of lack of permission", func() {
  1524  				Eventually(func() (bool, error) {
  1525  					podList, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.TODO(), metav1.ListOptions{})
  1526  					if err != nil {
  1527  						return false, err
  1528  					}
  1529  					for _, pod := range podList.Items {
  1530  						if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName {
  1531  							if pod.Status.ContainerStatuses != nil && pod.Status.ContainerStatuses[0].State.Terminated != nil {
  1532  								return true, nil
  1533  							}
  1534  						}
  1535  					}
  1536  					return false, nil
  1537  				}).Should(BeTrue())
  1538  			})
  1539  		})
  1540  	})
  1541  	When("The namespace is labled as Pod Security Admission policy enforce:baseline", func() {
  1542  		BeforeEach(func() {
  1543  			var err error
  1544  			testNS := &corev1.Namespace{}
  1545  			Eventually(func() error {
  1546  				testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), generatedNamespace.GetName(), metav1.GetOptions{})
  1547  				if err != nil {
  1548  					return err
  1549  				}
  1550  				return nil
  1551  			}).Should(BeNil())
  1552  
  1553  			testNS.ObjectMeta.Labels = map[string]string{
  1554  				"pod-security.kubernetes.io/enforce":         "baseline",
  1555  				"pod-security.kubernetes.io/enforce-version": "latest",
  1556  			}
  1557  
  1558  			Eventually(func() error {
  1559  				_, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{})
  1560  				if err != nil {
  1561  					return err
  1562  				}
  1563  				return nil
  1564  			}).Should(BeNil())
  1565  		})
  1566  		When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created with spec.GrpcPodConfig.SecurityContextConfig set to legacy", func() {
  1567  			var sourceName string
  1568  			BeforeEach(func() {
  1569  				sourceName = genName("catalog-")
  1570  				source := &v1alpha1.CatalogSource{
  1571  					TypeMeta: metav1.TypeMeta{
  1572  						Kind:       v1alpha1.CatalogSourceKind,
  1573  						APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
  1574  					},
  1575  					ObjectMeta: metav1.ObjectMeta{
  1576  						Name:      sourceName,
  1577  						Namespace: generatedNamespace.GetName(),
  1578  						Labels:    map[string]string{"olm.catalogSource": sourceName},
  1579  					},
  1580  					Spec: v1alpha1.CatalogSourceSpec{
  1581  						SourceType: v1alpha1.SourceTypeGrpc,
  1582  						Image:      "quay.io/olmtest/old-opm-catsrc:v1.21.0",
  1583  						GrpcPodConfig: &v1alpha1.GrpcPodConfig{
  1584  							SecurityContextConfig: v1alpha1.Legacy,
  1585  						},
  1586  					},
  1587  				}
  1588  
  1589  				Eventually(func() error {
  1590  					_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
  1591  					return err
  1592  				}).Should(Succeed())
  1593  			})
  1594  			It("The registry pod comes up successfully", func() {
  1595  				Eventually(func() (bool, error) {
  1596  					podList, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.TODO(), metav1.ListOptions{})
  1597  					if err != nil {
  1598  						return false, err
  1599  					}
  1600  					for _, pod := range podList.Items {
  1601  						if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName {
  1602  							if pod.Status.ContainerStatuses != nil {
  1603  								if *pod.Status.ContainerStatuses[0].Started == true {
  1604  									return true, nil
  1605  								}
  1606  							}
  1607  						}
  1608  					}
  1609  					return false, nil
  1610  				}).Should(BeTrue())
  1611  			})
  1612  		})
  1613  	})
  1614  })
  1615  
  1616  func getOperatorDeployment(c operatorclient.ClientInterface, namespace string, operatorLabels labels.Set) (*appsv1.Deployment, error) {
  1617  	deployments, err := c.ListDeploymentsWithLabels(namespace, operatorLabels)
  1618  	if err != nil || deployments == nil || len(deployments.Items) != 1 {
  1619  		return nil, fmt.Errorf("Error getting single operator deployment for label: %v", operatorLabels)
  1620  	}
  1621  	return &deployments.Items[0], nil
  1622  }
  1623  
  1624  func rescaleDeployment(c operatorclient.ClientInterface, deployment *appsv1.Deployment) error {
  1625  	// scale down
  1626  	var replicas int32 = 0
  1627  	deployment.Spec.Replicas = &replicas
  1628  	deployment, updated, err := c.UpdateDeployment(deployment)
  1629  
  1630  	if err != nil || updated == false || deployment == nil {
  1631  		return fmt.Errorf("Failed to scale down deployment")
  1632  	}
  1633  
  1634  	waitForScaleup := func() (bool, error) {
  1635  		fetchedDeployment, err := c.GetDeployment(deployment.GetNamespace(), deployment.GetName())
  1636  		if err != nil {
  1637  			return true, err
  1638  		}
  1639  		if fetchedDeployment.Status.Replicas == replicas {
  1640  			return true, nil
  1641  		}
  1642  
  1643  		return false, nil
  1644  	}
  1645  
  1646  	// wait for deployment to scale down
  1647  	Eventually(waitForScaleup, 5*time.Minute, 1*time.Second).Should(BeTrue())
  1648  
  1649  	// scale up
  1650  	replicas = 1
  1651  	deployment.Spec.Replicas = &replicas
  1652  	deployment, updated, err = c.UpdateDeployment(deployment)
  1653  	if err != nil || updated == false || deployment == nil {
  1654  		return fmt.Errorf("Failed to scale up deployment")
  1655  	}
  1656  
  1657  	// wait for deployment to scale up
  1658  	Eventually(waitForScaleup, 5*time.Minute, 1*time.Second).Should(BeTrue())
  1659  
  1660  	return err
  1661  }
  1662  
  1663  func replicateCatalogPod(c operatorclient.ClientInterface, catalog *v1alpha1.CatalogSource) *corev1.Pod {
  1664  	initialPods, err := c.KubernetesInterface().CoreV1().Pods(catalog.GetNamespace()).List(context.Background(), metav1.ListOptions{LabelSelector: "olm.catalogSource=" + catalog.GetName()})
  1665  	Expect(err).ToNot(HaveOccurred())
  1666  	Expect(initialPods.Items).To(HaveLen(1))
  1667  
  1668  	pod := initialPods.Items[0]
  1669  	copied := &corev1.Pod{
  1670  		ObjectMeta: metav1.ObjectMeta{
  1671  			Namespace: catalog.GetNamespace(),
  1672  			Name:      catalog.GetName() + "-copy",
  1673  		},
  1674  		Spec: pod.Spec,
  1675  	}
  1676  
  1677  	copied, err = c.KubernetesInterface().CoreV1().Pods(catalog.GetNamespace()).Create(context.Background(), copied, metav1.CreateOptions{})
  1678  	Expect(err).ToNot(HaveOccurred())
  1679  
  1680  	return copied
  1681  }