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

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/blang/semver/v4"
    15  	"github.com/ghodss/yaml"
    16  	. "github.com/onsi/ginkgo/v2"
    17  	. "github.com/onsi/gomega"
    18  	configv1 "github.com/openshift/api/config/v1"
    19  	configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
    20  	"github.com/stretchr/testify/require"
    21  	appsv1 "k8s.io/api/apps/v1"
    22  	corev1 "k8s.io/api/core/v1"
    23  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    24  	"k8s.io/apimachinery/pkg/api/equality"
    25  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/types"
    29  	"k8s.io/apimachinery/pkg/util/wait"
    30  	"k8s.io/client-go/discovery"
    31  	"sigs.k8s.io/controller-runtime/pkg/client"
    32  
    33  	"github.com/operator-framework/api/pkg/lib/version"
    34  	operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
    35  	operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
    36  	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
    37  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
    38  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
    39  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/projection"
    40  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
    41  	"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
    42  	registryapi "github.com/operator-framework/operator-registry/pkg/api"
    43  )
    44  
    45  func Step(level int, text string, callbacks ...func()) {
    46  	By(strings.Repeat(" ", level*2)+text, callbacks...)
    47  }
    48  
    49  const (
    50  	timeout  = time.Second * 20
    51  	interval = time.Millisecond * 100
    52  )
    53  
    54  const (
    55  	subscriptionTestDataBaseDir = "subscription/"
    56  )
    57  
    58  var _ = Describe("Subscription", func() {
    59  	var (
    60  		generatedNamespace corev1.Namespace
    61  		operatorGroup      operatorsv1.OperatorGroup
    62  		c                  operatorclient.ClientInterface
    63  		crc                versioned.Interface
    64  	)
    65  
    66  	BeforeEach(func() {
    67  		c = ctx.Ctx().KubeClient()
    68  		crc = ctx.Ctx().OperatorClient()
    69  
    70  		nsName := genName("subscription-e2e-")
    71  		operatorGroup = operatorsv1.OperatorGroup{
    72  			ObjectMeta: metav1.ObjectMeta{
    73  				Name:      fmt.Sprintf("%s-operatorgroup", nsName),
    74  				Namespace: nsName,
    75  			},
    76  		}
    77  		generatedNamespace = SetupGeneratedTestNamespaceWithOperatorGroup(nsName, operatorGroup)
    78  	})
    79  
    80  	AfterEach(func() {
    81  		TeardownNamespace(generatedNamespace.GetName())
    82  	})
    83  
    84  	When("an entry in the middle of a channel does not provide a required GVK", func() {
    85  		var (
    86  			teardown func()
    87  		)
    88  
    89  		BeforeEach(func() {
    90  			teardown = func() {}
    91  			packages := []registry.PackageManifest{
    92  				{
    93  					PackageName: "dependency",
    94  					Channels: []registry.PackageChannel{
    95  						{Name: "channel-dependency", CurrentCSVName: "csv-dependency-3"},
    96  					},
    97  					DefaultChannelName: "channel-dependency",
    98  				},
    99  				{
   100  					PackageName: "root",
   101  					Channels: []registry.PackageChannel{
   102  						{Name: "channel-root", CurrentCSVName: "csv-root"},
   103  					},
   104  					DefaultChannelName: "channel-root",
   105  				},
   106  			}
   107  
   108  			crds := []apiextensionsv1.CustomResourceDefinition{newCRD(genName("crd-"))}
   109  			csvs := []operatorsv1alpha1.ClusterServiceVersion{
   110  				newCSV("csv-dependency-1", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), crds, nil, nil),
   111  				newCSV("csv-dependency-2", generatedNamespace.GetName(), "csv-dependency-1", semver.MustParse("2.0.0"), nil, nil, nil),
   112  				newCSV("csv-dependency-3", generatedNamespace.GetName(), "csv-dependency-2", semver.MustParse("3.0.0"), crds, nil, nil),
   113  				newCSV("csv-root", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), nil, crds, nil),
   114  			}
   115  
   116  			_, teardown = createInternalCatalogSource(ctx.Ctx().KubeClient(), ctx.Ctx().OperatorClient(), "test-catalog", generatedNamespace.GetName(), packages, crds, csvs)
   117  			_, err := fetchCatalogSourceOnStatus(ctx.Ctx().OperatorClient(), "test-catalog", generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   118  			Expect(err).NotTo(HaveOccurred())
   119  
   120  			createSubscriptionForCatalog(ctx.Ctx().OperatorClient(), generatedNamespace.GetName(), "test-subscription", "test-catalog", "root", "channel-root", "", operatorsv1alpha1.ApprovalAutomatic)
   121  		})
   122  
   123  		AfterEach(func() {
   124  			teardown()
   125  		})
   126  
   127  		It("should create a Subscription for the latest entry providing the required GVK", func() {
   128  			Eventually(func() ([]operatorsv1alpha1.Subscription, error) {
   129  				var list operatorsv1alpha1.SubscriptionList
   130  				if err := ctx.Ctx().Client().List(context.Background(), &list); err != nil {
   131  					return nil, err
   132  				}
   133  				return list.Items, nil
   134  			}).Should(ContainElement(WithTransform(
   135  				func(in operatorsv1alpha1.Subscription) operatorsv1alpha1.SubscriptionSpec {
   136  					return operatorsv1alpha1.SubscriptionSpec{
   137  						CatalogSource:          in.Spec.CatalogSource,
   138  						CatalogSourceNamespace: in.Spec.CatalogSourceNamespace,
   139  						Package:                in.Spec.Package,
   140  						Channel:                in.Spec.Channel,
   141  						StartingCSV:            in.Spec.StartingCSV,
   142  					}
   143  				},
   144  				Equal(operatorsv1alpha1.SubscriptionSpec{
   145  					CatalogSource:          "test-catalog",
   146  					CatalogSourceNamespace: generatedNamespace.GetName(),
   147  					Package:                "dependency",
   148  					Channel:                "channel-dependency",
   149  					StartingCSV:            "csv-dependency-3",
   150  				}),
   151  			)))
   152  		})
   153  	})
   154  
   155  	It("creation if not installed", func() {
   156  		By(` I. Creating a new subscription`)
   157  		By(`    A. If package is not installed, creating a subscription should install latest version`)
   158  
   159  		defer func() {
   160  			if env := os.Getenv("SKIP_CLEANUP"); env != "" {
   161  				fmt.Printf("Skipping cleanup of subscriptions in namespace %s\n", generatedNamespace.GetName())
   162  				return
   163  			}
   164  			require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))
   165  		}()
   166  
   167  		By("creating a catalog")
   168  		require.NoError(GinkgoT(), initCatalog(GinkgoT(), generatedNamespace.GetName(), c, crc))
   169  
   170  		By(fmt.Sprintf("creating a subscription: %s/%s", generatedNamespace.GetName(), testSubscriptionName))
   171  		cleanup, _ := createSubscription(GinkgoT(), crc, generatedNamespace.GetName(), testSubscriptionName, testPackageName, betaChannel, operatorsv1alpha1.ApprovalAutomatic)
   172  
   173  		defer cleanup()
   174  
   175  		By("waiting for the subscription to have a current CSV and be at latest")
   176  		var currentCSV string
   177  		Eventually(func() bool {
   178  			fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), testSubscriptionName, metav1.GetOptions{})
   179  			if err != nil {
   180  				return false
   181  			}
   182  			if fetched != nil {
   183  				currentCSV = fetched.Status.CurrentCSV
   184  				return fetched.Status.State == operatorsv1alpha1.SubscriptionStateAtLatest
   185  			}
   186  			return false
   187  		}, 5*time.Minute, 10*time.Second).Should(BeTrue())
   188  
   189  		csv, err := fetchCSV(crc, generatedNamespace.GetName(), currentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
   190  		require.NoError(GinkgoT(), err)
   191  
   192  		By(`Check for the olm.package property as a proxy for`)
   193  		By(`verifying that the annotation value is reasonable.`)
   194  		Expect(
   195  			projection.PropertyListFromPropertiesAnnotation(csv.GetAnnotations()["operatorframework.io/properties"]),
   196  		).To(ContainElement(
   197  			&registryapi.Property{Type: "olm.package", Value: `{"packageName":"myapp","version":"0.1.1"}`},
   198  		))
   199  	})
   200  
   201  	It("creation using existing CSV", func() {
   202  		By(`  I. Creating a new subscription`)
   203  		By(`     B. If package is already installed, creating a subscription should upgrade it to the latest version`)
   204  
   205  		defer func() {
   206  			require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))
   207  		}()
   208  		require.NoError(GinkgoT(), initCatalog(GinkgoT(), generatedNamespace.GetName(), c, crc))
   209  
   210  		By(`Will be cleaned up by the upgrade process`)
   211  		_, err := createCSV(c, crc, stableCSV, generatedNamespace.GetName(), false, false)
   212  		require.NoError(GinkgoT(), err)
   213  
   214  		subscriptionCleanup, _ := createSubscription(GinkgoT(), crc, generatedNamespace.GetName(), testSubscriptionName, testPackageName, alphaChannel, operatorsv1alpha1.ApprovalAutomatic)
   215  		defer subscriptionCleanup()
   216  
   217  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), testSubscriptionName, subscriptionStateAtLatestChecker())
   218  		require.NoError(GinkgoT(), err)
   219  		require.NotNil(GinkgoT(), subscription)
   220  		_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
   221  		require.NoError(GinkgoT(), err)
   222  	})
   223  	It("skip range", func() {
   224  
   225  		crdPlural := genName("ins")
   226  		crdName := crdPlural + ".cluster.com"
   227  		crd := apiextensionsv1.CustomResourceDefinition{
   228  			ObjectMeta: metav1.ObjectMeta{
   229  				Name: crdName,
   230  			},
   231  			Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   232  				Group: "cluster.com",
   233  				Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   234  					{
   235  						Name:    "v1alpha1",
   236  						Served:  true,
   237  						Storage: true,
   238  						Schema: &apiextensionsv1.CustomResourceValidation{
   239  							OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
   240  								Type:        "object",
   241  								Description: "my crd schema",
   242  							},
   243  						},
   244  					},
   245  				},
   246  				Names: apiextensionsv1.CustomResourceDefinitionNames{
   247  					Plural:   crdPlural,
   248  					Singular: crdPlural,
   249  					Kind:     crdPlural,
   250  					ListKind: "list" + crdPlural,
   251  				},
   252  				Scope: apiextensionsv1.NamespaceScoped,
   253  			},
   254  		}
   255  
   256  		mainPackageName := genName("nginx-")
   257  		mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)
   258  		updatedPackageStable := fmt.Sprintf("%s-updated", mainPackageName)
   259  		stableChannel := "stable"
   260  		mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0-1556661347"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   261  		updatedCSV := newCSV(updatedPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0-1556661832"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   262  		updatedCSV.SetAnnotations(map[string]string{"olm.skipRange": ">=0.1.0-1556661347 <0.1.0-1556661832"})
   263  
   264  		c := newKubeClient()
   265  		crc := newCRClient()
   266  		defer func() {
   267  			require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))
   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  		updatedManifests := []registry.PackageManifest{
   283  			{
   284  				PackageName: mainPackageName,
   285  				Channels: []registry.PackageChannel{
   286  					{Name: stableChannel, CurrentCSVName: updatedPackageStable},
   287  				},
   288  				DefaultChannelName: stableChannel,
   289  			},
   290  		}
   291  
   292  		By(`Create catalog source`)
   293  		_, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, generatedNamespace.GetName(), mainManifests, []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})
   294  		defer cleanupMainCatalogSource()
   295  		By(`Attempt to get the catalog source before creating subscription`)
   296  		_, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   297  		require.NoError(GinkgoT(), err)
   298  
   299  		By(`Create a subscription`)
   300  		subscriptionName := genName("sub-nginx-")
   301  		subscriptionCleanup := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
   302  		defer subscriptionCleanup()
   303  
   304  		By(`Wait for csv to install`)
   305  		firstCSV, err := fetchCSV(crc, generatedNamespace.GetName(), mainCSV.GetName(), csvSucceededChecker)
   306  		require.NoError(GinkgoT(), err)
   307  
   308  		By(`Update catalog with a new csv in the channel with a skip range`)
   309  		updateInternalCatalog(GinkgoT(), c, crc, mainCatalogName, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{updatedCSV}, updatedManifests)
   310  
   311  		By(`Wait for csv to update`)
   312  		finalCSV, err := fetchCSV(crc, generatedNamespace.GetName(), updatedCSV.GetName(), csvSucceededChecker)
   313  		require.NoError(GinkgoT(), err)
   314  
   315  		By(`Ensure we set the replacement field based on the registry data`)
   316  		require.Equal(GinkgoT(), firstCSV.GetName(), finalCSV.Spec.Replaces)
   317  	})
   318  
   319  	It("creation manual approval", func() {
   320  		By(`If installPlanApproval is set to manual, the installplans created should be created with approval: manual`)
   321  
   322  		defer func() {
   323  			require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))
   324  		}()
   325  		require.NoError(GinkgoT(), initCatalog(GinkgoT(), generatedNamespace.GetName(), c, crc))
   326  
   327  		subscriptionCleanup, _ := createSubscription(GinkgoT(), crc, generatedNamespace.GetName(), "manual-subscription", testPackageName, stableChannel, operatorsv1alpha1.ApprovalManual)
   328  		defer subscriptionCleanup()
   329  
   330  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), "manual-subscription", subscriptionHasCondition(operatorsv1alpha1.SubscriptionInstallPlanPending, corev1.ConditionTrue, string(operatorsv1alpha1.InstallPlanPhaseRequiresApproval), ""))
   331  		require.NoError(GinkgoT(), err)
   332  		require.NotNil(GinkgoT(), subscription)
   333  
   334  		installPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, subscription.Status.Install.Name, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseRequiresApproval))
   335  		require.NoError(GinkgoT(), err)
   336  		require.NotNil(GinkgoT(), installPlan)
   337  
   338  		require.Equal(GinkgoT(), operatorsv1alpha1.ApprovalManual, installPlan.Spec.Approval)
   339  		require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseRequiresApproval, installPlan.Status.Phase)
   340  
   341  		By(`Delete the current installplan`)
   342  		err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Delete(context.Background(), installPlan.Name, metav1.DeleteOptions{})
   343  		require.NoError(GinkgoT(), err)
   344  
   345  		var ipName string
   346  		Eventually(func() bool {
   347  			fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), "manual-subscription", metav1.GetOptions{})
   348  			if err != nil {
   349  				return false
   350  			}
   351  			if fetched.Status.Install != nil {
   352  				ipName = fetched.Status.Install.Name
   353  				return fetched.Status.Install.Name != installPlan.Name
   354  			}
   355  			return false
   356  		}, 5*time.Minute, 10*time.Second).Should(BeTrue())
   357  
   358  		By(`Fetch new installplan`)
   359  		newInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, ipName, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseRequiresApproval))
   360  		require.NoError(GinkgoT(), err)
   361  		require.NotNil(GinkgoT(), newInstallPlan)
   362  
   363  		require.NotEqual(GinkgoT(), installPlan.Name, newInstallPlan.Name, "expected new installplan recreated")
   364  		require.Equal(GinkgoT(), operatorsv1alpha1.ApprovalManual, newInstallPlan.Spec.Approval)
   365  		require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseRequiresApproval, newInstallPlan.Status.Phase)
   366  
   367  		By(`Set the InstallPlan's approved to True`)
   368  		Eventually(Apply(newInstallPlan, func(p *operatorsv1alpha1.InstallPlan) error {
   369  
   370  			p.Spec.Approved = true
   371  			return nil
   372  		})).Should(Succeed())
   373  
   374  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), "manual-subscription", subscriptionStateAtLatestChecker())
   375  		require.NoError(GinkgoT(), err)
   376  		require.NotNil(GinkgoT(), subscription)
   377  
   378  		_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
   379  		require.NoError(GinkgoT(), err)
   380  	})
   381  
   382  	It("with starting CSV", func() {
   383  
   384  		crdPlural := genName("ins")
   385  		crdName := crdPlural + ".cluster.com"
   386  
   387  		crd := apiextensionsv1.CustomResourceDefinition{
   388  			ObjectMeta: metav1.ObjectMeta{
   389  				Name: crdName,
   390  			},
   391  			Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   392  				Group: "cluster.com",
   393  				Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   394  					{
   395  						Name:    "v1alpha1",
   396  						Served:  true,
   397  						Storage: true,
   398  						Schema: &apiextensionsv1.CustomResourceValidation{
   399  							OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
   400  								Type:        "object",
   401  								Description: "my crd schema",
   402  							},
   403  						},
   404  					},
   405  				},
   406  				Names: apiextensionsv1.CustomResourceDefinitionNames{
   407  					Plural:   crdPlural,
   408  					Singular: crdPlural,
   409  					Kind:     crdPlural,
   410  					ListKind: "list" + crdPlural,
   411  				},
   412  				Scope: apiextensionsv1.NamespaceScoped,
   413  			},
   414  		}
   415  
   416  		By(`Create CSV`)
   417  		packageName := genName("nginx-")
   418  		stableChannel := "stable"
   419  
   420  		csvA := newCSV("nginx-a", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   421  		csvB := newCSV("nginx-b", generatedNamespace.GetName(), "nginx-a", semver.MustParse("0.2.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   422  
   423  		By(`Create PackageManifests`)
   424  		manifests := []registry.PackageManifest{
   425  			{
   426  				PackageName: packageName,
   427  				Channels: []registry.PackageChannel{
   428  					{Name: stableChannel, CurrentCSVName: csvB.GetName()},
   429  				},
   430  				DefaultChannelName: stableChannel,
   431  			},
   432  		}
   433  
   434  		By(`Create the CatalogSource`)
   435  		catalogSourceName := genName("mock-nginx-")
   436  		_, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalogSourceName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB})
   437  		defer cleanupCatalogSource()
   438  
   439  		By(`Attempt to get the catalog source before creating install plan`)
   440  		_, err := fetchCatalogSourceOnStatus(crc, catalogSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   441  		require.NoError(GinkgoT(), err)
   442  
   443  		subscriptionName := genName("sub-nginx-")
   444  		cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, catalogSourceName, packageName, stableChannel, csvA.GetName(), operatorsv1alpha1.ApprovalManual)
   445  		defer cleanupSubscription()
   446  
   447  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
   448  		require.NoError(GinkgoT(), err)
   449  		require.NotNil(GinkgoT(), subscription)
   450  
   451  		installPlanName := subscription.Status.Install.Name
   452  
   453  		By(`Wait for InstallPlan to be status: Complete before checking resource presence`)
   454  		requiresApprovalChecker := buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseRequiresApproval)
   455  		fetchedInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, installPlanName, generatedNamespace.GetName(), requiresApprovalChecker)
   456  		require.NoError(GinkgoT(), err)
   457  
   458  		By(`Ensure that only 1 installplan was created`)
   459  		ips, err := crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{})
   460  		require.NoError(GinkgoT(), err)
   461  		require.Len(GinkgoT(), ips.Items, 1)
   462  
   463  		By(`Ensure that csvA and its crd are found in the plan`)
   464  		csvFound := false
   465  		crdFound := false
   466  		for _, s := range fetchedInstallPlan.Status.Plan {
   467  			require.Equal(GinkgoT(), csvA.GetName(), s.Resolving, "unexpected resolution found")
   468  			require.Equal(GinkgoT(), operatorsv1alpha1.StepStatusUnknown, s.Status, "status should be unknown")
   469  			require.Equal(GinkgoT(), catalogSourceName, s.Resource.CatalogSource, "incorrect catalogsource on step resource")
   470  			switch kind := s.Resource.Kind; kind {
   471  			case operatorsv1alpha1.ClusterServiceVersionKind:
   472  				require.Equal(GinkgoT(), csvA.GetName(), s.Resource.Name, "unexpected csv found")
   473  				csvFound = true
   474  			case "CustomResourceDefinition":
   475  				require.Equal(GinkgoT(), crdName, s.Resource.Name, "unexpected crd found")
   476  				crdFound = true
   477  			default:
   478  				GinkgoT().Fatalf("unexpected resource kind found in installplan: %s", kind)
   479  			}
   480  		}
   481  		require.True(GinkgoT(), csvFound, "expected csv not found in installplan")
   482  		require.True(GinkgoT(), crdFound, "expected crd not found in installplan")
   483  
   484  		By(`Ensure that csvB is not found in the plan`)
   485  		csvFound = false
   486  		for _, s := range fetchedInstallPlan.Status.Plan {
   487  			require.Equal(GinkgoT(), csvA.GetName(), s.Resolving, "unexpected resolution found")
   488  			require.Equal(GinkgoT(), operatorsv1alpha1.StepStatusUnknown, s.Status, "status should be unknown")
   489  			require.Equal(GinkgoT(), catalogSourceName, s.Resource.CatalogSource, "incorrect catalogsource on step resource")
   490  			switch kind := s.Resource.Kind; kind {
   491  			case operatorsv1alpha1.ClusterServiceVersionKind:
   492  				if s.Resource.Name == csvB.GetName() {
   493  					csvFound = true
   494  				}
   495  			}
   496  		}
   497  		require.False(GinkgoT(), csvFound, "expected csv not found in installplan")
   498  
   499  		By(`Approve the installplan and wait for csvA to be installed`)
   500  		fetchedInstallPlan.Spec.Approved = true
   501  		_, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Update(context.Background(), fetchedInstallPlan, metav1.UpdateOptions{})
   502  		require.NoError(GinkgoT(), err)
   503  
   504  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvA.GetName(), csvSucceededChecker)
   505  		require.NoError(GinkgoT(), err)
   506  
   507  		By(`Wait for the subscription to begin upgrading to csvB`)
   508  		By(`The upgrade changes the installplanref on the subscription`)
   509  		Eventually(func() (bool, error) {
   510  			subscription, err = crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subscriptionName, metav1.GetOptions{})
   511  			return subscription != nil && subscription.Status.InstallPlanRef.Name != fetchedInstallPlan.GetName() && subscription.Status.State == operatorsv1alpha1.SubscriptionStateUpgradePending, err
   512  		}, 5*time.Minute, 1*time.Second).Should(BeTrue(), "expected new installplan for upgraded csv")
   513  
   514  		upgradeInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, subscription.Status.InstallPlanRef.Name, generatedNamespace.GetName(), requiresApprovalChecker)
   515  		require.NoError(GinkgoT(), err)
   516  
   517  		By(`Approve the upgrade installplan and wait for`)
   518  		upgradeInstallPlan.Spec.Approved = true
   519  		_, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Update(context.Background(), upgradeInstallPlan, metav1.UpdateOptions{})
   520  		require.NoError(GinkgoT(), err)
   521  
   522  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvB.GetName(), csvSucceededChecker)
   523  		require.NoError(GinkgoT(), err)
   524  
   525  		By(`Ensure that 2 installplans were created`)
   526  		ips, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{})
   527  		require.NoError(GinkgoT(), err)
   528  		require.Len(GinkgoT(), ips.Items, 2)
   529  	})
   530  
   531  	It("[FLAKE] updates multiple intermediates", func() {
   532  		By(`issue: https://github.com/operator-framework/operator-lifecycle-manager/issues/2635`)
   533  
   534  		crd := newCRD("ins")
   535  
   536  		By(`Create CSV`)
   537  		packageName := genName("nginx-")
   538  		stableChannel := "stable"
   539  
   540  		csvA := newCSV("nginx-a", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   541  		csvB := newCSV("nginx-b", generatedNamespace.GetName(), "nginx-a", semver.MustParse("0.2.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   542  		csvC := newCSV("nginx-c", generatedNamespace.GetName(), "nginx-b", semver.MustParse("0.3.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
   543  
   544  		By(`Create PackageManifests`)
   545  		manifests := []registry.PackageManifest{
   546  			{
   547  				PackageName: packageName,
   548  				Channels: []registry.PackageChannel{
   549  					{Name: stableChannel, CurrentCSVName: csvA.GetName()},
   550  				},
   551  				DefaultChannelName: stableChannel,
   552  			},
   553  		}
   554  
   555  		By(`Create the CatalogSource with just one version`)
   556  		catalogSourceName := genName("mock-nginx-")
   557  		_, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalogSourceName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csvA})
   558  		defer cleanupCatalogSource()
   559  
   560  		By(`Attempt to get the catalog source before creating install plan`)
   561  		_, err := fetchCatalogSourceOnStatus(crc, catalogSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   562  		require.NoError(GinkgoT(), err)
   563  
   564  		subscriptionName := genName("sub-nginx-")
   565  		cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, catalogSourceName, packageName, stableChannel, csvA.GetName(), operatorsv1alpha1.ApprovalAutomatic)
   566  		defer cleanupSubscription()
   567  
   568  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
   569  		require.NoError(GinkgoT(), err)
   570  		require.NotNil(GinkgoT(), subscription)
   571  
   572  		By(`Wait for csvA to be installed`)
   573  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvA.GetName(), csvSucceededChecker)
   574  		require.NoError(GinkgoT(), err)
   575  
   576  		By(`Set up async watches that will fail the test if csvB doesn't get created in between csvA and csvC`)
   577  		var wg sync.WaitGroup
   578  		go func(t GinkgoTInterface) {
   579  			defer GinkgoRecover()
   580  			wg.Add(1)
   581  			defer wg.Done()
   582  			_, err := fetchCSV(crc, generatedNamespace.GetName(), csvB.GetName(), csvReplacingChecker)
   583  			require.NoError(GinkgoT(), err)
   584  		}(GinkgoT())
   585  		By(`Update the catalog to include multiple updates`)
   586  		packages := []registry.PackageManifest{
   587  			{
   588  				PackageName: packageName,
   589  				Channels: []registry.PackageChannel{
   590  					{Name: stableChannel, CurrentCSVName: csvC.GetName()},
   591  				},
   592  				DefaultChannelName: stableChannel,
   593  			},
   594  		}
   595  
   596  		updateInternalCatalog(GinkgoT(), c, crc, catalogSourceName, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB, csvC}, packages)
   597  
   598  		By(`wait for checks on intermediate csvs to succeed`)
   599  		wg.Wait()
   600  
   601  		By(`Wait for csvC to be installed`)
   602  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvC.GetName(), csvSucceededChecker)
   603  		require.NoError(GinkgoT(), err)
   604  
   605  		By(`Should eventually GC the CSVs`)
   606  		err = waitForCsvToDelete(generatedNamespace.GetName(), csvA.Name, crc)
   607  		Expect(err).ShouldNot(HaveOccurred())
   608  
   609  		err = waitForCsvToDelete(generatedNamespace.GetName(), csvB.Name, crc)
   610  		Expect(err).ShouldNot(HaveOccurred())
   611  
   612  		By(`TODO: check installplans, subscription status, etc`)
   613  	})
   614  
   615  	It("updates existing install plan", func() {
   616  		By(`TestSubscriptionUpdatesExistingInstallPlan ensures that an existing InstallPlan has the appropriate approval requirement from Subscription.`)
   617  
   618  		Skip("ToDo: This test was skipped before ginkgo conversion")
   619  
   620  		By(`Create CSV`)
   621  		packageName := genName("nginx-")
   622  		stableChannel := "stable"
   623  
   624  		csvA := newCSV("nginx-a", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, nil, nil)
   625  		csvB := newCSV("nginx-b", generatedNamespace.GetName(), "nginx-a", semver.MustParse("0.2.0"), nil, nil, nil)
   626  
   627  		By(`Create PackageManifests`)
   628  		manifests := []registry.PackageManifest{
   629  			{
   630  				PackageName: packageName,
   631  				Channels: []registry.PackageChannel{
   632  					{Name: stableChannel, CurrentCSVName: csvB.GetName()},
   633  				},
   634  				DefaultChannelName: stableChannel,
   635  			},
   636  		}
   637  
   638  		By(`Create the CatalogSource with just one version`)
   639  		catalogSourceName := genName("mock-nginx-")
   640  		_, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalogSourceName, generatedNamespace.GetName(), manifests, nil, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB})
   641  		defer cleanupCatalogSource()
   642  
   643  		By(`Attempt to get the catalog source before creating install plan`)
   644  		_, err := fetchCatalogSourceOnStatus(crc, catalogSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   645  		require.NoError(GinkgoT(), err)
   646  
   647  		By(`Create a subscription to just get an InstallPlan for csvB`)
   648  		subscriptionName := genName("sub-nginx-")
   649  		createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, catalogSourceName, packageName, stableChannel, csvB.GetName(), operatorsv1alpha1.ApprovalAutomatic)
   650  
   651  		By(`Wait for csvB to be installed`)
   652  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvB.GetName(), csvSucceededChecker)
   653  		require.NoError(GinkgoT(), err)
   654  
   655  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
   656  		fetchedInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, subscription.Status.InstallPlanRef.Name, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))
   657  
   658  		By(`Delete this subscription`)
   659  		err = crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).DeleteCollection(context.Background(), *metav1.NewDeleteOptions(0), metav1.ListOptions{})
   660  		require.NoError(GinkgoT(), err)
   661  		By(`Delete orphaned csvB`)
   662  		require.NoError(GinkgoT(), crc.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Delete(context.Background(), csvB.GetName(), metav1.DeleteOptions{}))
   663  
   664  		By(`Create an InstallPlan for csvB`)
   665  		ip := &operatorsv1alpha1.InstallPlan{
   666  			ObjectMeta: metav1.ObjectMeta{
   667  				GenerateName: "install-",
   668  				Namespace:    generatedNamespace.GetName(),
   669  			},
   670  			Spec: operatorsv1alpha1.InstallPlanSpec{
   671  				ClusterServiceVersionNames: []string{csvB.GetName()},
   672  				Approval:                   operatorsv1alpha1.ApprovalAutomatic,
   673  				Approved:                   false,
   674  			},
   675  		}
   676  		ip2, err := crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Create(context.Background(), ip, metav1.CreateOptions{})
   677  		require.NoError(GinkgoT(), err)
   678  
   679  		ip2.Status = operatorsv1alpha1.InstallPlanStatus{
   680  			Plan:           fetchedInstallPlan.Status.Plan,
   681  			CatalogSources: []string{catalogSourceName},
   682  		}
   683  
   684  		_, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).UpdateStatus(context.Background(), ip2, metav1.UpdateOptions{})
   685  		require.NoError(GinkgoT(), err)
   686  
   687  		subscriptionName = genName("sub-nginx-")
   688  		cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, catalogSourceName, packageName, stableChannel, csvA.GetName(), operatorsv1alpha1.ApprovalManual)
   689  		defer cleanupSubscription()
   690  
   691  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
   692  		require.NoError(GinkgoT(), err)
   693  		require.NotNil(GinkgoT(), subscription)
   694  
   695  		installPlanName := subscription.Status.Install.Name
   696  
   697  		By(`Wait for InstallPlan to be status: Complete before checking resource presence`)
   698  		requiresApprovalChecker := buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseRequiresApproval)
   699  		fetchedInstallPlan, err = fetchInstallPlanWithNamespace(GinkgoT(), crc, installPlanName, generatedNamespace.GetName(), requiresApprovalChecker)
   700  		require.NoError(GinkgoT(), err)
   701  
   702  		By(`Approve the installplan and wait for csvA to be installed`)
   703  		fetchedInstallPlan.Spec.Approved = true
   704  		_, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Update(context.Background(), fetchedInstallPlan, metav1.UpdateOptions{})
   705  		require.NoError(GinkgoT(), err)
   706  
   707  		By(`Wait for csvA to be installed`)
   708  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvA.GetName(), csvSucceededChecker)
   709  		require.NoError(GinkgoT(), err)
   710  
   711  		By(`Wait for the subscription to begin upgrading to csvB`)
   712  		subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateUpgradePendingChecker())
   713  		require.NoError(GinkgoT(), err)
   714  
   715  		By(`Fetch existing csvB installPlan`)
   716  		fetchedInstallPlan, err = fetchInstallPlanWithNamespace(GinkgoT(), crc, subscription.Status.InstallPlanRef.Name, generatedNamespace.GetName(), requiresApprovalChecker)
   717  		require.NoError(GinkgoT(), err)
   718  		require.Equal(GinkgoT(), ip2.GetName(), subscription.Status.InstallPlanRef.Name, "expected new installplan is the same with pre-exising one")
   719  
   720  		By(`Approve the installplan and wait for csvB to be installed`)
   721  		fetchedInstallPlan.Spec.Approved = true
   722  		_, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Update(context.Background(), fetchedInstallPlan, metav1.UpdateOptions{})
   723  		require.NoError(GinkgoT(), err)
   724  
   725  		By(`Wait for csvB to be installed`)
   726  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvB.GetName(), csvSucceededChecker)
   727  		require.NoError(GinkgoT(), err)
   728  	})
   729  
   730  	Describe("puppeting CatalogSource health status", func() {
   731  		var (
   732  			getOpts    metav1.GetOptions
   733  			deleteOpts *metav1.DeleteOptions
   734  		)
   735  
   736  		BeforeEach(func() {
   737  			getOpts = metav1.GetOptions{}
   738  			deleteOpts = &metav1.DeleteOptions{}
   739  		})
   740  
   741  		AfterEach(func() {
   742  			err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{})
   743  			Expect(err).NotTo(HaveOccurred())
   744  		})
   745  
   746  		When("missing target catalog", func() {
   747  			It("should surface the missing catalog", func() {
   748  				By(`TestSubscriptionStatusMissingTargetCatalogSource ensures that a Subscription has the appropriate status condition when`)
   749  				By(`its target catalog is missing.`)
   750  				By(`			BySteps:`)
   751  				By(`1. Generate an initial CatalogSource in the target namespace`)
   752  				By(`2. Generate Subscription, "sub", targetting non-existent CatalogSource, "missing"`)
   753  				By(`3. Wait for sub status to show SubscriptionCatalogSourcesUnhealthy with status True, reason CatalogSourcesUpdated, and appropriate missing message`)
   754  				By(`4. Update sub's spec to target the "mysubscription"`)
   755  				By(`5. Wait for sub's status to show SubscriptionCatalogSourcesUnhealthy with status False, reason AllCatalogSourcesHealthy, and reason "all available catalogsources are healthy"`)
   756  				By(`6. Wait for sub to succeed`)
   757  				err := initCatalog(GinkgoT(), generatedNamespace.GetName(), c, crc)
   758  				Expect(err).NotTo(HaveOccurred())
   759  
   760  				missingName := "missing"
   761  				cleanup := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), testSubscriptionName, missingName, testPackageName, betaChannel, "", operatorsv1alpha1.ApprovalAutomatic)
   762  				defer cleanup()
   763  
   764  				By("detecting its absence")
   765  				sub, err := fetchSubscription(crc, generatedNamespace.GetName(), testSubscriptionName, subscriptionHasCondition(operatorsv1alpha1.SubscriptionCatalogSourcesUnhealthy, corev1.ConditionTrue, operatorsv1alpha1.UnhealthyCatalogSourceFound, fmt.Sprintf("targeted catalogsource %s/%s missing", generatedNamespace.GetName(), missingName)))
   766  				Expect(err).NotTo(HaveOccurred())
   767  				Expect(sub).ToNot(BeNil())
   768  
   769  				By("updating the subscription to target an existing catsrc")
   770  				Eventually(func() error {
   771  					sub, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), testSubscriptionName, metav1.GetOptions{})
   772  					if err != nil {
   773  						return err
   774  					}
   775  					if sub == nil {
   776  						return fmt.Errorf("subscription is nil")
   777  					}
   778  					sub.Spec.CatalogSource = catalogSourceName
   779  					_, err = crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Update(context.Background(), sub, metav1.UpdateOptions{})
   780  					return err
   781  				}).Should(Succeed())
   782  
   783  				By(`Wait for SubscriptionCatalogSourcesUnhealthy to be false`)
   784  				By("detecting a new existing target")
   785  				_, err = fetchSubscription(crc, generatedNamespace.GetName(), testSubscriptionName, subscriptionHasCondition(operatorsv1alpha1.SubscriptionCatalogSourcesUnhealthy, corev1.ConditionFalse, operatorsv1alpha1.AllCatalogSourcesHealthy, "all available catalogsources are healthy"))
   786  				Expect(err).NotTo(HaveOccurred())
   787  
   788  				By(`Wait for success`)
   789  				_, err = fetchSubscription(crc, generatedNamespace.GetName(), testSubscriptionName, subscriptionStateAtLatestChecker())
   790  				Expect(err).NotTo(HaveOccurred())
   791  			})
   792  		})
   793  
   794  		When("the target catalog's sourceType", func() {
   795  			Context("is unknown", func() {
   796  				It("should surface catalog health", func() {
   797  					cs := &operatorsv1alpha1.CatalogSource{
   798  						TypeMeta: metav1.TypeMeta{
   799  							Kind:       operatorsv1alpha1.CatalogSourceKind,
   800  							APIVersion: operatorsv1alpha1.CatalogSourceCRDAPIVersion,
   801  						},
   802  						ObjectMeta: metav1.ObjectMeta{
   803  							Name: "cs",
   804  						},
   805  						Spec: operatorsv1alpha1.CatalogSourceSpec{
   806  							SourceType: "goose",
   807  							GrpcPodConfig: &operatorsv1alpha1.GrpcPodConfig{
   808  								SecurityContextConfig: operatorsv1alpha1.Restricted,
   809  							},
   810  						},
   811  					}
   812  
   813  					var err error
   814  					cs, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Create(context.Background(), cs, metav1.CreateOptions{})
   815  					defer func() {
   816  						err = crc.OperatorsV1alpha1().CatalogSources(cs.GetNamespace()).Delete(context.Background(), cs.GetName(), *deleteOpts)
   817  						Expect(err).ToNot(HaveOccurred())
   818  					}()
   819  
   820  					subName := genName("sub-")
   821  					cleanup := createSubscriptionForCatalog(
   822  						crc,
   823  						cs.GetNamespace(),
   824  						subName,
   825  						cs.GetName(),
   826  						testPackageName,
   827  						betaChannel,
   828  						"",
   829  						operatorsv1alpha1.ApprovalManual,
   830  					)
   831  					defer cleanup()
   832  
   833  					var sub *operatorsv1alpha1.Subscription
   834  					sub, err = fetchSubscription(
   835  						crc,
   836  						cs.GetNamespace(),
   837  						subName,
   838  						subscriptionHasCondition(
   839  							operatorsv1alpha1.SubscriptionCatalogSourcesUnhealthy,
   840  							corev1.ConditionTrue,
   841  							operatorsv1alpha1.UnhealthyCatalogSourceFound,
   842  							fmt.Sprintf("targeted catalogsource %s/%s unhealthy", cs.GetNamespace(), cs.GetName()),
   843  						),
   844  					)
   845  					Expect(err).NotTo(HaveOccurred())
   846  					Expect(sub).ToNot(BeNil())
   847  
   848  					By(`Get the latest CatalogSource`)
   849  					cs, err = crc.OperatorsV1alpha1().CatalogSources(cs.GetNamespace()).Get(context.Background(), cs.GetName(), getOpts)
   850  					Expect(err).NotTo(HaveOccurred())
   851  					Expect(cs).ToNot(BeNil())
   852  				})
   853  			})
   854  
   855  			Context("is grpc and its spec is missing the address and image fields", func() {
   856  				It("should surface catalog health", func() {
   857  					By(`Create a CatalogSource pointing to the grpc pod`)
   858  					cs := &operatorsv1alpha1.CatalogSource{
   859  						TypeMeta: metav1.TypeMeta{
   860  							Kind:       operatorsv1alpha1.CatalogSourceKind,
   861  							APIVersion: operatorsv1alpha1.CatalogSourceCRDAPIVersion,
   862  						},
   863  						ObjectMeta: metav1.ObjectMeta{
   864  							Name:      genName("cs-"),
   865  							Namespace: generatedNamespace.GetName(),
   866  						},
   867  						Spec: operatorsv1alpha1.CatalogSourceSpec{
   868  							SourceType: operatorsv1alpha1.SourceTypeGrpc,
   869  							GrpcPodConfig: &operatorsv1alpha1.GrpcPodConfig{
   870  								SecurityContextConfig: operatorsv1alpha1.Restricted,
   871  							},
   872  						},
   873  					}
   874  
   875  					var err error
   876  					cs, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Create(context.Background(), cs, metav1.CreateOptions{})
   877  					defer func() {
   878  						err = crc.OperatorsV1alpha1().CatalogSources(cs.GetNamespace()).Delete(context.Background(), cs.GetName(), *deleteOpts)
   879  						Expect(err).ToNot(HaveOccurred())
   880  					}()
   881  
   882  					By(`Wait for the CatalogSource status to be updated to reflect its invalid spec`)
   883  					_, err = fetchCatalogSourceOnStatus(crc, cs.GetName(), cs.GetNamespace(), catalogSourceInvalidSpec)
   884  					Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
   885  
   886  					subName := genName("sub-")
   887  					cleanup := createSubscriptionForCatalog(
   888  						crc,
   889  						cs.GetNamespace(),
   890  						subName,
   891  						cs.GetName(),
   892  						testPackageName,
   893  						betaChannel,
   894  						"",
   895  						operatorsv1alpha1.ApprovalManual,
   896  					)
   897  					defer cleanup()
   898  
   899  					var sub *operatorsv1alpha1.Subscription
   900  					sub, err = fetchSubscription(
   901  						crc,
   902  						cs.GetNamespace(),
   903  						subName,
   904  						subscriptionHasCondition(
   905  							operatorsv1alpha1.SubscriptionCatalogSourcesUnhealthy,
   906  							corev1.ConditionTrue,
   907  							operatorsv1alpha1.UnhealthyCatalogSourceFound,
   908  							fmt.Sprintf("targeted catalogsource %s/%s unhealthy", cs.GetNamespace(), cs.GetName()),
   909  						),
   910  					)
   911  					Expect(err).NotTo(HaveOccurred())
   912  					Expect(sub).ToNot(BeNil())
   913  				})
   914  			})
   915  
   916  			Context("is internal and its spec is missing the configmap reference", func() {
   917  				It("should surface catalog health", func() {
   918  					cs := &operatorsv1alpha1.CatalogSource{
   919  						TypeMeta: metav1.TypeMeta{
   920  							Kind:       operatorsv1alpha1.CatalogSourceKind,
   921  							APIVersion: operatorsv1alpha1.CatalogSourceCRDAPIVersion,
   922  						},
   923  						ObjectMeta: metav1.ObjectMeta{
   924  							Name:      genName("cs-"),
   925  							Namespace: generatedNamespace.GetName(),
   926  						},
   927  						Spec: operatorsv1alpha1.CatalogSourceSpec{
   928  							SourceType: operatorsv1alpha1.SourceTypeInternal,
   929  							GrpcPodConfig: &operatorsv1alpha1.GrpcPodConfig{
   930  								SecurityContextConfig: operatorsv1alpha1.Restricted,
   931  							},
   932  						},
   933  					}
   934  
   935  					var err error
   936  					cs, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Create(context.Background(), cs, metav1.CreateOptions{})
   937  					defer func() {
   938  						err = crc.OperatorsV1alpha1().CatalogSources(cs.GetNamespace()).Delete(context.Background(), cs.GetName(), *deleteOpts)
   939  						Expect(err).ToNot(HaveOccurred())
   940  					}()
   941  
   942  					subName := genName("sub-")
   943  					cleanup := createSubscriptionForCatalog(
   944  						crc,
   945  						cs.GetNamespace(),
   946  						subName,
   947  						cs.GetName(),
   948  						testPackageName,
   949  						betaChannel,
   950  						"",
   951  						operatorsv1alpha1.ApprovalManual,
   952  					)
   953  					defer cleanup()
   954  
   955  					var sub *operatorsv1alpha1.Subscription
   956  					sub, err = fetchSubscription(
   957  						crc,
   958  						cs.GetNamespace(),
   959  						subName,
   960  						subscriptionHasCondition(
   961  							operatorsv1alpha1.SubscriptionCatalogSourcesUnhealthy,
   962  							corev1.ConditionTrue,
   963  							operatorsv1alpha1.UnhealthyCatalogSourceFound,
   964  							fmt.Sprintf("targeted catalogsource %s/%s unhealthy", cs.GetNamespace(), cs.GetName()),
   965  						),
   966  					)
   967  					Expect(err).NotTo(HaveOccurred())
   968  					Expect(sub).ToNot(BeNil())
   969  				})
   970  			})
   971  
   972  			Context("is configmap and its spec is missing the configmap reference", func() {
   973  				It("should surface catalog health", func() {
   974  					cs := &operatorsv1alpha1.CatalogSource{
   975  						TypeMeta: metav1.TypeMeta{
   976  							Kind:       operatorsv1alpha1.CatalogSourceKind,
   977  							APIVersion: operatorsv1alpha1.CatalogSourceCRDAPIVersion,
   978  						},
   979  						ObjectMeta: metav1.ObjectMeta{
   980  							Name:      genName("cs-"),
   981  							Namespace: generatedNamespace.GetName(),
   982  						},
   983  						Spec: operatorsv1alpha1.CatalogSourceSpec{
   984  							SourceType: operatorsv1alpha1.SourceTypeInternal,
   985  							GrpcPodConfig: &operatorsv1alpha1.GrpcPodConfig{
   986  								SecurityContextConfig: operatorsv1alpha1.Restricted,
   987  							},
   988  						},
   989  					}
   990  
   991  					var err error
   992  					cs, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Create(context.Background(), cs, metav1.CreateOptions{})
   993  					defer func() {
   994  						err = crc.OperatorsV1alpha1().CatalogSources(cs.GetNamespace()).Delete(context.Background(), cs.GetName(), *deleteOpts)
   995  						Expect(err).ToNot(HaveOccurred())
   996  					}()
   997  
   998  					subName := genName("sub-")
   999  					cleanup := createSubscriptionForCatalog(
  1000  						crc,
  1001  						cs.GetNamespace(),
  1002  						subName,
  1003  						cs.GetName(),
  1004  						testPackageName,
  1005  						betaChannel,
  1006  						"",
  1007  						operatorsv1alpha1.ApprovalAutomatic,
  1008  					)
  1009  					defer cleanup()
  1010  
  1011  					var sub *operatorsv1alpha1.Subscription
  1012  					sub, err = fetchSubscription(
  1013  						crc,
  1014  						cs.GetNamespace(),
  1015  						subName,
  1016  						subscriptionHasCondition(
  1017  							operatorsv1alpha1.SubscriptionCatalogSourcesUnhealthy,
  1018  							corev1.ConditionTrue,
  1019  							operatorsv1alpha1.UnhealthyCatalogSourceFound,
  1020  							fmt.Sprintf("targeted catalogsource %s/%s unhealthy", cs.GetNamespace(), cs.GetName()),
  1021  						),
  1022  					)
  1023  					Expect(err).NotTo(HaveOccurred())
  1024  					Expect(sub).ToNot(BeNil())
  1025  				})
  1026  			})
  1027  		})
  1028  
  1029  	})
  1030  
  1031  	It("can reconcile InstallPlan status", func() {
  1032  		By(`TestSubscriptionInstallPlanStatus ensures that a Subscription has the appropriate status conditions for possible referenced InstallPlan states.`)
  1033  		c := newKubeClient()
  1034  		crc := newCRClient()
  1035  
  1036  		By(`Create CatalogSource, cs, in ns`)
  1037  		pkgName := genName("pkg-")
  1038  		channelName := genName("channel-")
  1039  		crd := newCRD(pkgName)
  1040  		csv := newCSV(pkgName, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  1041  		manifests := []registry.PackageManifest{
  1042  			{
  1043  				PackageName: pkgName,
  1044  				Channels: []registry.PackageChannel{
  1045  					{Name: channelName, CurrentCSVName: csv.GetName()},
  1046  				},
  1047  				DefaultChannelName: channelName,
  1048  			},
  1049  		}
  1050  		catalogName := genName("catalog-")
  1051  		_, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalogName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csv})
  1052  		defer cleanupCatalogSource()
  1053  		_, err := fetchCatalogSourceOnStatus(crc, catalogName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1054  		Expect(err).ToNot(HaveOccurred())
  1055  
  1056  		By(`Create Subscription to a package of cs in ns, sub`)
  1057  		subName := genName("sub-")
  1058  		defer createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subName, catalogName, pkgName, channelName, pkgName, operatorsv1alpha1.ApprovalAutomatic)()
  1059  
  1060  		By(`Wait for the package from sub to install successfully with no remaining InstallPlan status conditions`)
  1061  		checker := subscriptionStateAtLatestChecker()
  1062  		sub, err := fetchSubscription(crc, generatedNamespace.GetName(), subName, func(s *operatorsv1alpha1.Subscription) bool {
  1063  			for _, cond := range s.Status.Conditions {
  1064  				switch cond.Type {
  1065  				case operatorsv1alpha1.SubscriptionInstallPlanMissing, operatorsv1alpha1.SubscriptionInstallPlanPending, operatorsv1alpha1.SubscriptionInstallPlanFailed:
  1066  					return false
  1067  				}
  1068  			}
  1069  			return checker(s)
  1070  		})
  1071  		Expect(err).ToNot(HaveOccurred())
  1072  		Expect(sub).ToNot(BeNil())
  1073  
  1074  		By(`Store conditions for later comparision`)
  1075  		conds := sub.Status.Conditions
  1076  
  1077  		ref := sub.Status.InstallPlanRef
  1078  		Expect(ref).ToNot(BeNil())
  1079  
  1080  		By(`Get the InstallPlan`)
  1081  		plan := &operatorsv1alpha1.InstallPlan{}
  1082  		plan.SetNamespace(ref.Namespace)
  1083  		plan.SetName(ref.Name)
  1084  
  1085  		By(`Set the InstallPlan's approval mode to Manual`)
  1086  		Eventually(Apply(plan, func(p *operatorsv1alpha1.InstallPlan) error {
  1087  			p.Spec.Approval = operatorsv1alpha1.ApprovalManual
  1088  
  1089  			p.Spec.Approved = false
  1090  			return nil
  1091  		})).Should(Succeed())
  1092  
  1093  		By(`Set the InstallPlan's phase to None`)
  1094  		Eventually(Apply(plan, func(p *operatorsv1alpha1.InstallPlan) error {
  1095  			p.Status.Phase = operatorsv1alpha1.InstallPlanPhaseNone
  1096  			return nil
  1097  		})).Should(Succeed())
  1098  
  1099  		By(`Wait for sub to have status condition SubscriptionInstallPlanPending true and reason InstallPlanNotYetReconciled`)
  1100  		sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, func(s *operatorsv1alpha1.Subscription) bool {
  1101  			cond := s.Status.GetCondition(operatorsv1alpha1.SubscriptionInstallPlanPending)
  1102  			return cond.Status == corev1.ConditionTrue && cond.Reason == operatorsv1alpha1.InstallPlanNotYetReconciled
  1103  		})
  1104  		Expect(err).ToNot(HaveOccurred())
  1105  
  1106  		By(`Set the phase to InstallPlanPhaseRequiresApproval`)
  1107  		Eventually(Apply(plan, func(p *operatorsv1alpha1.InstallPlan) error {
  1108  			p.Status.Phase = operatorsv1alpha1.InstallPlanPhaseRequiresApproval
  1109  			return nil
  1110  		})).Should(Succeed())
  1111  
  1112  		By(`Wait for sub to have status condition SubscriptionInstallPlanPending true and reason RequiresApproval`)
  1113  		sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, func(s *operatorsv1alpha1.Subscription) bool {
  1114  			cond := s.Status.GetCondition(operatorsv1alpha1.SubscriptionInstallPlanPending)
  1115  			return cond.Status == corev1.ConditionTrue && cond.Reason == string(operatorsv1alpha1.InstallPlanPhaseRequiresApproval)
  1116  		})
  1117  		Expect(err).ToNot(HaveOccurred())
  1118  
  1119  		By(`Set the phase to InstallPlanPhaseInstalling`)
  1120  		Eventually(Apply(plan, func(p *operatorsv1alpha1.InstallPlan) error {
  1121  			p.Status.Phase = operatorsv1alpha1.InstallPlanPhaseInstalling
  1122  			return nil
  1123  		})).Should(Succeed())
  1124  
  1125  		By(`Wait for sub to have status condition SubscriptionInstallPlanPending true and reason Installing`)
  1126  		sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, func(s *operatorsv1alpha1.Subscription) bool {
  1127  			cond := s.Status.GetCondition(operatorsv1alpha1.SubscriptionInstallPlanPending)
  1128  			isConditionPresent := cond.Status == corev1.ConditionTrue && cond.Reason == string(operatorsv1alpha1.InstallPlanPhaseInstalling)
  1129  
  1130  			if isConditionPresent {
  1131  				return true
  1132  			}
  1133  
  1134  			// Sometimes the transition from installing to complete can be so quick that the test does not capture
  1135  			// the condition in the subscription before it is removed. To mitigate this, we check if the installplan
  1136  			// has transitioned to complete and exit out the fetch subscription loop if so.
  1137  			// This is a mitigation. We should probably fix this test appropriately.
  1138  			// issue: https://github.com/operator-framework/operator-lifecycle-manager/issues/2667
  1139  			ip, err := crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Get(context.TODO(), plan.Name, metav1.GetOptions{})
  1140  			if err != nil {
  1141  				// retry on failure
  1142  				return false
  1143  			}
  1144  			isInstallPlanComplete := ip.Status.Phase == operatorsv1alpha1.InstallPlanPhaseComplete
  1145  
  1146  			return isInstallPlanComplete
  1147  		})
  1148  		Expect(err).ToNot(HaveOccurred())
  1149  
  1150  		By(`Set the phase to InstallPlanPhaseFailed and remove all status conditions`)
  1151  		Eventually(Apply(plan, func(p *operatorsv1alpha1.InstallPlan) error {
  1152  			p.Status.Phase = operatorsv1alpha1.InstallPlanPhaseFailed
  1153  			p.Status.Conditions = nil
  1154  			return nil
  1155  		})).Should(Succeed())
  1156  
  1157  		By(`Wait for sub to have status condition SubscriptionInstallPlanFailed true and reason InstallPlanFailed`)
  1158  		sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, func(s *operatorsv1alpha1.Subscription) bool {
  1159  			cond := s.Status.GetCondition(operatorsv1alpha1.SubscriptionInstallPlanFailed)
  1160  			return cond.Status == corev1.ConditionTrue && cond.Reason == operatorsv1alpha1.InstallPlanFailed
  1161  		})
  1162  		Expect(err).ToNot(HaveOccurred())
  1163  
  1164  		By(`Set status condition of type Installed to false with reason InstallComponentFailed`)
  1165  		Eventually(Apply(plan, func(p *operatorsv1alpha1.InstallPlan) error {
  1166  			p.Status.Phase = operatorsv1alpha1.InstallPlanPhaseFailed
  1167  			failedCond := p.Status.GetCondition(operatorsv1alpha1.InstallPlanInstalled)
  1168  			failedCond.Status = corev1.ConditionFalse
  1169  			failedCond.Reason = operatorsv1alpha1.InstallPlanReasonComponentFailed
  1170  			p.Status.SetCondition(failedCond)
  1171  			return nil
  1172  		})).Should(Succeed())
  1173  
  1174  		By(`Wait for sub to have status condition SubscriptionInstallPlanFailed true and reason InstallComponentFailed`)
  1175  		sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, func(s *operatorsv1alpha1.Subscription) bool {
  1176  			cond := s.Status.GetCondition(operatorsv1alpha1.SubscriptionInstallPlanFailed)
  1177  			return cond.Status == corev1.ConditionTrue && cond.Reason == string(operatorsv1alpha1.InstallPlanReasonComponentFailed)
  1178  		})
  1179  		Expect(err).ToNot(HaveOccurred())
  1180  
  1181  		By(`Delete the referenced InstallPlan`)
  1182  		Eventually(func() error {
  1183  			return crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Delete(context.Background(), ref.Name, metav1.DeleteOptions{})
  1184  		}).Should(Succeed())
  1185  
  1186  		By(`Wait for sub to have status condition SubscriptionInstallPlanMissing true`)
  1187  		sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, func(s *operatorsv1alpha1.Subscription) bool {
  1188  			return s.Status.GetCondition(operatorsv1alpha1.SubscriptionInstallPlanMissing).Status == corev1.ConditionTrue
  1189  		})
  1190  		Expect(err).ToNot(HaveOccurred())
  1191  		Expect(sub).ToNot(BeNil())
  1192  
  1193  		By(`Ensure InstallPlan-related status conditions match what we're expecting`)
  1194  		for _, cond := range conds {
  1195  			switch condType := cond.Type; condType {
  1196  			case operatorsv1alpha1.SubscriptionInstallPlanPending, operatorsv1alpha1.SubscriptionInstallPlanFailed:
  1197  				require.FailNowf(GinkgoT(), "failed", "subscription contains unexpected installplan condition: %v", cond)
  1198  			case operatorsv1alpha1.SubscriptionInstallPlanMissing:
  1199  				require.Equal(GinkgoT(), operatorsv1alpha1.ReferencedInstallPlanNotFound, cond.Reason)
  1200  			}
  1201  		}
  1202  	})
  1203  
  1204  	It("creation with pod config", func() {
  1205  
  1206  		newConfigClient := func(t GinkgoTInterface) configv1client.ConfigV1Interface {
  1207  			client, err := configv1client.NewForConfig(ctx.Ctx().RESTConfig())
  1208  			require.NoError(GinkgoT(), err)
  1209  
  1210  			return client
  1211  		}
  1212  
  1213  		proxyEnvVarFunc := func(t GinkgoTInterface, client configv1client.ConfigV1Interface) []corev1.EnvVar {
  1214  			if discovery.ServerSupportsVersion(ctx.Ctx().KubeClient().KubernetesInterface().Discovery(), configv1.GroupVersion) != nil {
  1215  				return nil
  1216  			}
  1217  
  1218  			proxy, getErr := client.Proxies().Get(context.Background(), "cluster", metav1.GetOptions{})
  1219  			if apierrors.IsNotFound(getErr) {
  1220  				return nil
  1221  			}
  1222  			require.NoError(GinkgoT(), getErr)
  1223  			require.NotNil(GinkgoT(), proxy)
  1224  
  1225  			proxyEnv := []corev1.EnvVar{}
  1226  
  1227  			if proxy.Status.HTTPProxy != "" {
  1228  				proxyEnv = append(proxyEnv, corev1.EnvVar{
  1229  					Name:  "HTTP_PROXY",
  1230  					Value: proxy.Status.HTTPProxy,
  1231  				})
  1232  			}
  1233  
  1234  			if proxy.Status.HTTPSProxy != "" {
  1235  				proxyEnv = append(proxyEnv, corev1.EnvVar{
  1236  					Name:  "HTTPS_PROXY",
  1237  					Value: proxy.Status.HTTPSProxy,
  1238  				})
  1239  			}
  1240  
  1241  			if proxy.Status.NoProxy != "" {
  1242  				proxyEnv = append(proxyEnv, corev1.EnvVar{
  1243  					Name:  "NO_PROXY",
  1244  					Value: proxy.Status.NoProxy,
  1245  				})
  1246  			}
  1247  
  1248  			return proxyEnv
  1249  		}
  1250  
  1251  		kubeClient := newKubeClient()
  1252  		crClient := newCRClient()
  1253  		config := newConfigClient(GinkgoT())
  1254  
  1255  		By(`Create a ConfigMap that is mounted to the operator via the subscription`)
  1256  		testConfigMapName := genName("test-configmap-")
  1257  		testConfigMap := &corev1.ConfigMap{
  1258  			ObjectMeta: metav1.ObjectMeta{
  1259  				Name: testConfigMapName,
  1260  			},
  1261  		}
  1262  
  1263  		_, err := kubeClient.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Create(context.Background(), testConfigMap, metav1.CreateOptions{})
  1264  		require.NoError(GinkgoT(), err)
  1265  		defer func() {
  1266  			err := kubeClient.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Delete(context.Background(), testConfigMap.Name, metav1.DeleteOptions{})
  1267  			require.NoError(GinkgoT(), err)
  1268  		}()
  1269  
  1270  		By(`Configure the Subscription.`)
  1271  
  1272  		podEnv := []corev1.EnvVar{
  1273  			{
  1274  				Name:  "MY_ENV_VARIABLE1",
  1275  				Value: "value1",
  1276  			},
  1277  			{
  1278  				Name:  "MY_ENV_VARIABLE2",
  1279  				Value: "value2",
  1280  			},
  1281  		}
  1282  		testVolumeName := genName("test-volume-")
  1283  		podVolumes := []corev1.Volume{
  1284  			{
  1285  				Name: testVolumeName,
  1286  				VolumeSource: corev1.VolumeSource{
  1287  					ConfigMap: &corev1.ConfigMapVolumeSource{
  1288  						LocalObjectReference: corev1.LocalObjectReference{
  1289  							Name: testConfigMapName,
  1290  						},
  1291  					},
  1292  				},
  1293  			},
  1294  		}
  1295  
  1296  		podVolumeMounts := []corev1.VolumeMount{
  1297  			{Name: testVolumeName, MountPath: "/test"},
  1298  		}
  1299  
  1300  		podTolerations := []corev1.Toleration{
  1301  			{
  1302  				Key:      "my-toleration-key",
  1303  				Value:    "my-toleration-value",
  1304  				Effect:   corev1.TaintEffectNoSchedule,
  1305  				Operator: corev1.TolerationOpEqual,
  1306  			},
  1307  		}
  1308  		podResources := &corev1.ResourceRequirements{
  1309  			Limits: corev1.ResourceList{
  1310  				corev1.ResourceCPU: resource.MustParse("100m"),
  1311  			},
  1312  			Requests: corev1.ResourceList{
  1313  				corev1.ResourceCPU:    resource.MustParse("100m"),
  1314  				corev1.ResourceMemory: resource.MustParse("128Mi"),
  1315  			},
  1316  		}
  1317  
  1318  		podConfig := &operatorsv1alpha1.SubscriptionConfig{
  1319  			Env:          podEnv,
  1320  			Volumes:      podVolumes,
  1321  			VolumeMounts: podVolumeMounts,
  1322  			Tolerations:  podTolerations,
  1323  			Resources:    podResources,
  1324  		}
  1325  
  1326  		permissions := deploymentPermissions()
  1327  		catsrc, subSpec, catsrcCleanup := newCatalogSource(GinkgoT(), kubeClient, crClient, "podconfig", generatedNamespace.GetName(), permissions)
  1328  		defer catsrcCleanup()
  1329  
  1330  		By(`Ensure that the catalog source is resolved before we create a subscription.`)
  1331  		_, err = fetchCatalogSourceOnStatus(crClient, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1332  		require.NoError(GinkgoT(), err)
  1333  
  1334  		subscriptionName := genName("podconfig-sub-")
  1335  		subSpec.Config = podConfig
  1336  		cleanupSubscription := createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subSpec)
  1337  		defer cleanupSubscription()
  1338  
  1339  		subscription, err := fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
  1340  		require.NoError(GinkgoT(), err)
  1341  		require.NotNil(GinkgoT(), subscription)
  1342  
  1343  		proxyEnv := proxyEnvVarFunc(GinkgoT(), config)
  1344  		expected := podEnv
  1345  		expected = append(expected, proxyEnv...)
  1346  
  1347  		Eventually(func() error {
  1348  			csv, err := fetchCSV(crClient, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
  1349  			if err != nil {
  1350  				return err
  1351  			}
  1352  
  1353  			return checkDeploymentWithPodConfiguration(kubeClient, csv, podConfig.Env, podConfig.Volumes, podConfig.VolumeMounts, podConfig.Tolerations, podConfig.Resources)
  1354  		}).Should(Succeed())
  1355  	})
  1356  
  1357  	It("creation with nodeSelector config", func() {
  1358  		kubeClient := newKubeClient()
  1359  		crClient := newCRClient()
  1360  
  1361  		By(`Create a ConfigMap that is mounted to the operator via the subscription`)
  1362  		testConfigMapName := genName("test-configmap-")
  1363  		testConfigMap := &corev1.ConfigMap{
  1364  			ObjectMeta: metav1.ObjectMeta{
  1365  				Name: testConfigMapName,
  1366  			},
  1367  		}
  1368  
  1369  		_, err := kubeClient.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Create(context.Background(), testConfigMap, metav1.CreateOptions{})
  1370  		require.NoError(GinkgoT(), err)
  1371  		defer func() {
  1372  			err := kubeClient.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Delete(context.Background(), testConfigMap.Name, metav1.DeleteOptions{})
  1373  			require.NoError(GinkgoT(), err)
  1374  		}()
  1375  
  1376  		By(`Configure the Subscription.`)
  1377  		podNodeSelector := map[string]string{
  1378  			"foo": "bar",
  1379  		}
  1380  
  1381  		podConfig := &operatorsv1alpha1.SubscriptionConfig{
  1382  			NodeSelector: podNodeSelector,
  1383  		}
  1384  
  1385  		permissions := deploymentPermissions()
  1386  		catsrc, subSpec, catsrcCleanup := newCatalogSource(GinkgoT(), kubeClient, crClient, "podconfig", generatedNamespace.GetName(), permissions)
  1387  		defer catsrcCleanup()
  1388  
  1389  		By(`Ensure that the catalog source is resolved before we create a subscription.`)
  1390  		_, err = fetchCatalogSourceOnStatus(crClient, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1391  		require.NoError(GinkgoT(), err)
  1392  
  1393  		subscriptionName := genName("podconfig-sub-")
  1394  		subSpec.Config = podConfig
  1395  		cleanupSubscription := createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subSpec)
  1396  		defer cleanupSubscription()
  1397  
  1398  		subscription, err := fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
  1399  		require.NoError(GinkgoT(), err)
  1400  		require.NotNil(GinkgoT(), subscription)
  1401  
  1402  		csv, err := fetchCSV(crClient, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseInstalling, operatorsv1alpha1.CSVPhaseSucceeded))
  1403  		require.NoError(GinkgoT(), err)
  1404  
  1405  		Eventually(func() error {
  1406  			return checkDeploymentHasPodConfigNodeSelector(GinkgoT(), kubeClient, csv, podNodeSelector)
  1407  		}, timeout, interval).Should(Succeed())
  1408  
  1409  	})
  1410  
  1411  	It("[FLAKE] creation with dependencies", func() {
  1412  
  1413  		kubeClient := newKubeClient()
  1414  		crClient := newCRClient()
  1415  
  1416  		permissions := deploymentPermissions()
  1417  
  1418  		catsrc, subSpec, catsrcCleanup := newCatalogSourceWithDependencies(GinkgoT(), kubeClient, crClient, "podconfig", generatedNamespace.GetName(), permissions)
  1419  		defer catsrcCleanup()
  1420  
  1421  		By(`Ensure that the catalog source is resolved before we create a subscription.`)
  1422  		_, err := fetchCatalogSourceOnStatus(crClient, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1423  		require.NoError(GinkgoT(), err)
  1424  
  1425  		By(`Create duplicates of the CatalogSource`)
  1426  		for i := 0; i < 10; i++ {
  1427  			duplicateCatsrc, _, duplicateCatSrcCleanup := newCatalogSourceWithDependencies(GinkgoT(), kubeClient, crClient, "podconfig", generatedNamespace.GetName(), permissions)
  1428  			defer duplicateCatSrcCleanup()
  1429  
  1430  			By(`Ensure that the catalog source is resolved before we create a subscription.`)
  1431  			_, err = fetchCatalogSourceOnStatus(crClient, duplicateCatsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1432  			require.NoError(GinkgoT(), err)
  1433  		}
  1434  
  1435  		By(`Create a subscription that has a dependency`)
  1436  		subscriptionName := genName("podconfig-sub-")
  1437  		cleanupSubscription := createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subSpec)
  1438  		defer cleanupSubscription()
  1439  
  1440  		subscription, err := fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
  1441  		require.NoError(GinkgoT(), err)
  1442  		require.NotNil(GinkgoT(), subscription)
  1443  
  1444  		By(`Check that a single catalog source was used to resolve the InstallPlan`)
  1445  		installPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crClient, subscription.Status.InstallPlanRef.Name, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))
  1446  		require.NoError(GinkgoT(), err)
  1447  		require.Len(GinkgoT(), installPlan.Status.CatalogSources, 1)
  1448  	})
  1449  
  1450  	It("creation with dependencies required and provided in different versions of an operator in the same package", func() {
  1451  		By(`	PARITY: this test covers the same scenario as the TestSolveOperators_PackageCannotSelfSatisfy unit test`)
  1452  		kubeClient := ctx.Ctx().KubeClient()
  1453  		crClient := ctx.Ctx().OperatorClient()
  1454  
  1455  		crd := newCRD(genName("ins"))
  1456  		crd2 := newCRD(genName("ins"))
  1457  
  1458  		By(`csvs for catalogsource 1`)
  1459  		csvs1 := make([]operatorsv1alpha1.ClusterServiceVersion, 0)
  1460  
  1461  		By(`csvs for catalogsource 2`)
  1462  		csvs2 := make([]operatorsv1alpha1.ClusterServiceVersion, 0)
  1463  
  1464  		testPackage := registry.PackageManifest{PackageName: "test-package"}
  1465  		By("Package A", func() {
  1466  			Step(1, "Default Channel: Stable", func() {
  1467  				testPackage.DefaultChannelName = stableChannel
  1468  			})
  1469  
  1470  			Step(1, "Channel Stable", func() {
  1471  				Step(2, "Operator A (Requires CRD, CRD 2)", func() {
  1472  					csvA := newCSV("csv-a", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{crd, crd2}, nil)
  1473  					testPackage.
  1474  						Channels = append(testPackage.
  1475  						Channels, registry.PackageChannel{Name: stableChannel, CurrentCSVName: csvA.GetName()})
  1476  					csvs1 = append(csvs1, csvA)
  1477  				})
  1478  			})
  1479  
  1480  			Step(1, "Channel Alpha", func() {
  1481  				Step(2, "Operator ABC (Provides: CRD, CRD 2)", func() {
  1482  					csvABC := newCSV("csv-abc", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd, crd2}, nil, nil)
  1483  					testPackage.
  1484  						Channels = append(testPackage.
  1485  						Channels, registry.PackageChannel{Name: alphaChannel, CurrentCSVName: csvABC.GetName()})
  1486  					csvs1 = append(csvs1, csvABC)
  1487  				})
  1488  			})
  1489  		})
  1490  
  1491  		anotherPackage := registry.PackageManifest{PackageName: "another-package"}
  1492  		By("Package B", func() {
  1493  			Step(1, "Default Channel: Stable", func() {
  1494  				anotherPackage.DefaultChannelName = stableChannel
  1495  			})
  1496  
  1497  			Step(1, "Channel Stable", func() {
  1498  				Step(2, "Operator B (Provides: CRD)", func() {
  1499  					csvB := newCSV("csv-b", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  1500  					anotherPackage.Channels = append(anotherPackage.Channels, registry.PackageChannel{Name: stableChannel, CurrentCSVName: csvB.GetName()})
  1501  					csvs1 = append(csvs1, csvB)
  1502  				})
  1503  			})
  1504  
  1505  			Step(1, "Channel Alpha", func() {
  1506  				Step(2, "Operator D (Provides: CRD)", func() {
  1507  					csvD := newCSV("csv-d", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  1508  					anotherPackage.Channels = append(anotherPackage.Channels, registry.PackageChannel{Name: alphaChannel, CurrentCSVName: csvD.GetName()})
  1509  					csvs1 = append(csvs1, csvD)
  1510  				})
  1511  			})
  1512  		})
  1513  
  1514  		packageBInCatsrc2 := registry.PackageManifest{PackageName: "another-package"}
  1515  		By("Package B", func() {
  1516  			Step(1, "Default Channel: Stable", func() {
  1517  				packageBInCatsrc2.DefaultChannelName = stableChannel
  1518  			})
  1519  
  1520  			Step(1, "Channel Stable", func() {
  1521  				Step(2, "Operator C (Provides: CRD 2)", func() {
  1522  					csvC := newCSV("csv-c", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd2}, nil, nil)
  1523  					packageBInCatsrc2.Channels = append(packageBInCatsrc2.Channels, registry.PackageChannel{Name: stableChannel, CurrentCSVName: csvC.GetName()})
  1524  					csvs2 = append(csvs2, csvC)
  1525  				})
  1526  			})
  1527  		})
  1528  
  1529  		packageC := registry.PackageManifest{PackageName: "PackageC"}
  1530  		By("Package C", func() {
  1531  			Step(1, "Default Channel: Stable", func() {
  1532  				packageC.DefaultChannelName = stableChannel
  1533  			})
  1534  
  1535  			Step(1, "Channel Stable", func() {
  1536  				Step(2, "Operator E (Provides: CRD 2)", func() {
  1537  					csvE := newCSV("csv-e", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd2}, nil, nil)
  1538  					packageC.Channels = append(packageC.Channels, registry.PackageChannel{Name: stable, CurrentCSVName: csvE.GetName()})
  1539  					csvs2 = append(csvs2, csvE)
  1540  				})
  1541  			})
  1542  		})
  1543  
  1544  		By(`create catalogsources`)
  1545  		var catsrc, catsrc2 *operatorsv1alpha1.CatalogSource
  1546  		var cleanup cleanupFunc
  1547  		By("creating catalogsources", func() {
  1548  			var c1, c2 cleanupFunc
  1549  			catsrc, c1 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), generatedNamespace.GetName(), []registry.PackageManifest{testPackage, anotherPackage}, []apiextensionsv1.CustomResourceDefinition{crd, crd2}, csvs1)
  1550  			catsrc2, c2 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc2"), generatedNamespace.GetName(), []registry.PackageManifest{packageBInCatsrc2, packageC}, []apiextensionsv1.CustomResourceDefinition{crd, crd2}, csvs2)
  1551  			cleanup = func() {
  1552  				c1()
  1553  				c2()
  1554  			}
  1555  		})
  1556  		defer cleanup()
  1557  
  1558  		By("waiting for catalogsources to be ready", func() {
  1559  			_, err := fetchCatalogSourceOnStatus(crClient, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1560  			require.NoError(GinkgoT(), err)
  1561  			_, err = fetchCatalogSourceOnStatus(crClient, catsrc2.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1562  			require.NoError(GinkgoT(), err)
  1563  		})
  1564  
  1565  		By(`Create a subscription for test-package in catsrc`)
  1566  		subscriptionSpec := &operatorsv1alpha1.SubscriptionSpec{
  1567  			CatalogSource:          catsrc.GetName(),
  1568  			CatalogSourceNamespace: catsrc.GetNamespace(),
  1569  			Package:                testPackage.PackageName,
  1570  			Channel:                stableChannel,
  1571  			InstallPlanApproval:    operatorsv1alpha1.ApprovalAutomatic,
  1572  		}
  1573  		subscriptionName := genName("sub-")
  1574  		cleanupSubscription := createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subscriptionSpec)
  1575  		defer cleanupSubscription()
  1576  
  1577  		subscription, err := fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
  1578  		require.NoError(GinkgoT(), err)
  1579  		require.NotNil(GinkgoT(), subscription)
  1580  
  1581  		By(`ensure correct CSVs were picked`)
  1582  		var got []string
  1583  		Eventually(func() []string {
  1584  			ip, err := crClient.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Get(context.Background(), subscription.Status.InstallPlanRef.Name, metav1.GetOptions{})
  1585  			if err != nil {
  1586  				return nil
  1587  			}
  1588  			got = ip.Spec.ClusterServiceVersionNames
  1589  			return got
  1590  		}).ShouldNot(BeNil())
  1591  		require.ElementsMatch(GinkgoT(), []string{"csv-a", "csv-b", "csv-e"}, got)
  1592  	})
  1593  
  1594  	Context("to an operator with dependencies from different CatalogSources with priorities", func() {
  1595  		var (
  1596  			kubeClient                                    operatorclient.ClientInterface
  1597  			crClient                                      versioned.Interface
  1598  			crd                                           apiextensionsv1.CustomResourceDefinition
  1599  			packageMain, packageDepRight, packageDepWrong registry.PackageManifest
  1600  			csvsMain, csvsRight, csvsWrong                []operatorsv1alpha1.ClusterServiceVersion
  1601  			catsrcMain, catsrcDepRight, catsrcDepWrong    *operatorsv1alpha1.CatalogSource
  1602  			cleanup, cleanupSubscription                  cleanupFunc
  1603  		)
  1604  		const (
  1605  			mainCSVName  = "csv-main"
  1606  			rightCSVName = "csv-right"
  1607  			wrongCSVName = "csv-wrong"
  1608  		)
  1609  
  1610  		BeforeEach(func() {
  1611  			kubeClient = ctx.Ctx().KubeClient()
  1612  			crClient = ctx.Ctx().OperatorClient()
  1613  			crd = newCRD(genName("ins"))
  1614  
  1615  			packageMain = registry.PackageManifest{PackageName: genName("PkgMain-")}
  1616  			csv := newCSV(mainCSVName, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil,
  1617  				[]apiextensionsv1.CustomResourceDefinition{crd}, nil)
  1618  			packageMain.DefaultChannelName = stableChannel
  1619  			packageMain.Channels = append(packageMain.Channels, registry.PackageChannel{Name: stableChannel, CurrentCSVName: csv.GetName()})
  1620  
  1621  			csvsMain = []operatorsv1alpha1.ClusterServiceVersion{csv}
  1622  			csvsRight = []operatorsv1alpha1.ClusterServiceVersion{}
  1623  			csvsWrong = []operatorsv1alpha1.ClusterServiceVersion{}
  1624  		})
  1625  
  1626  		Context("creating CatalogSources providing the same dependency with different names", func() {
  1627  			var catsrcCleanup1, catsrcCleanup2, catsrcCleanup3 cleanupFunc
  1628  
  1629  			BeforeEach(func() {
  1630  				packageDepRight = registry.PackageManifest{PackageName: "PackageDependent"}
  1631  				csv := newCSV(rightCSVName, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"),
  1632  					[]apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  1633  				packageDepRight.DefaultChannelName = alphaChannel
  1634  				packageDepRight.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1635  				csvsRight = append(csvsRight, csv)
  1636  
  1637  				csv.Name = wrongCSVName
  1638  				packageDepWrong = packageDepRight
  1639  				packageDepWrong.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1640  				csvsWrong = append(csvsWrong, csv)
  1641  
  1642  				catsrcMain, catsrcCleanup1 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), generatedNamespace.GetName(),
  1643  					[]registry.PackageManifest{packageMain}, nil, csvsMain)
  1644  
  1645  				catsrcDepRight, catsrcCleanup2 = createInternalCatalogSource(kubeClient, crClient, "catsrc1", generatedNamespace.GetName(),
  1646  					[]registry.PackageManifest{packageDepRight}, []apiextensionsv1.CustomResourceDefinition{crd}, csvsRight)
  1647  
  1648  				catsrcDepWrong, catsrcCleanup3 = createInternalCatalogSource(kubeClient, crClient, "catsrc2", generatedNamespace.GetName(),
  1649  					[]registry.PackageManifest{packageDepWrong}, []apiextensionsv1.CustomResourceDefinition{crd}, csvsWrong)
  1650  
  1651  				_, err := fetchCatalogSourceOnStatus(crClient, catsrcMain.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1652  				Expect(err).ToNot(HaveOccurred())
  1653  				_, err = fetchCatalogSourceOnStatus(crClient, catsrcDepRight.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1654  				Expect(err).ToNot(HaveOccurred())
  1655  				_, err = fetchCatalogSourceOnStatus(crClient, catsrcDepWrong.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1656  				Expect(err).ToNot(HaveOccurred())
  1657  			})
  1658  
  1659  			AfterEach(func() {
  1660  				if catsrcCleanup1 != nil {
  1661  					catsrcCleanup1()
  1662  				}
  1663  				if catsrcCleanup2 != nil {
  1664  					catsrcCleanup2()
  1665  				}
  1666  				if catsrcCleanup3 != nil {
  1667  					catsrcCleanup3()
  1668  				}
  1669  			})
  1670  
  1671  			When("creating subscription for the main package", func() {
  1672  				var subscription *operatorsv1alpha1.Subscription
  1673  
  1674  				BeforeEach(func() {
  1675  					By(`Create a subscription for test-package in catsrc`)
  1676  					subscriptionSpec := &operatorsv1alpha1.SubscriptionSpec{
  1677  						CatalogSource:          catsrcMain.GetName(),
  1678  						CatalogSourceNamespace: catsrcMain.GetNamespace(),
  1679  						Package:                packageMain.PackageName,
  1680  						Channel:                stableChannel,
  1681  						InstallPlanApproval:    operatorsv1alpha1.ApprovalAutomatic,
  1682  					}
  1683  					subscriptionName := genName("sub-")
  1684  					cleanupSubscription = createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subscriptionSpec)
  1685  
  1686  					var err error
  1687  					subscription, err = fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
  1688  					Expect(err).ToNot(HaveOccurred())
  1689  					Expect(subscription).ToNot(BeNil())
  1690  
  1691  					_, err = fetchCSV(crClient, generatedNamespace.GetName(), mainCSVName, csvSucceededChecker)
  1692  					Expect(err).ToNot(HaveOccurred())
  1693  
  1694  				})
  1695  
  1696  				AfterEach(func() {
  1697  					if cleanupSubscription != nil {
  1698  						cleanupSubscription()
  1699  					}
  1700  					if cleanup != nil {
  1701  						cleanup()
  1702  					}
  1703  				})
  1704  
  1705  				It("[FLAKE] choose the dependency from the right CatalogSource based on lexicographical name ordering of catalogs", func() {
  1706  					By(`ensure correct CSVs were picked`)
  1707  					Eventually(func() ([]string, error) {
  1708  						ip, err := crClient.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Get(context.Background(), subscription.Status.InstallPlanRef.Name, metav1.GetOptions{})
  1709  						if err != nil {
  1710  							return nil, err
  1711  						}
  1712  						return ip.Spec.ClusterServiceVersionNames, nil
  1713  					}).Should(ConsistOf(mainCSVName, rightCSVName))
  1714  				})
  1715  			})
  1716  		})
  1717  
  1718  		Context("creating the main and an arbitrary CatalogSources providing the same dependency", func() {
  1719  			var catsrcCleanup1, catsrcCleanup2 cleanupFunc
  1720  
  1721  			BeforeEach(func() {
  1722  
  1723  				packageDepRight = registry.PackageManifest{PackageName: "PackageDependent"}
  1724  				csv := newCSV(rightCSVName, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"),
  1725  					[]apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  1726  				packageDepRight.DefaultChannelName = alphaChannel
  1727  				packageDepRight.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1728  				csvsMain = append(csvsMain, csv)
  1729  
  1730  				csv.Name = wrongCSVName
  1731  				packageDepWrong = packageDepRight
  1732  				packageDepWrong.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1733  				csvsWrong = append(csvsWrong, csv)
  1734  
  1735  				catsrcMain, catsrcCleanup1 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), generatedNamespace.GetName(),
  1736  					[]registry.PackageManifest{packageDepRight, packageMain}, []apiextensionsv1.CustomResourceDefinition{crd}, csvsMain)
  1737  
  1738  				catsrcDepWrong, catsrcCleanup2 = createInternalCatalogSourceWithPriority(kubeClient, crClient,
  1739  					genName("catsrc"), generatedNamespace.GetName(), []registry.PackageManifest{packageDepWrong}, []apiextensionsv1.CustomResourceDefinition{crd},
  1740  					csvsWrong, 100)
  1741  
  1742  				By(`waiting for catalogsources to be ready`)
  1743  				_, err := fetchCatalogSourceOnStatus(crClient, catsrcMain.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1744  				Expect(err).ToNot(HaveOccurred())
  1745  				_, err = fetchCatalogSourceOnStatus(crClient, catsrcDepWrong.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1746  				Expect(err).ToNot(HaveOccurred())
  1747  			})
  1748  
  1749  			AfterEach(func() {
  1750  				if catsrcCleanup1 != nil {
  1751  					catsrcCleanup1()
  1752  				}
  1753  				if catsrcCleanup2 != nil {
  1754  					catsrcCleanup2()
  1755  				}
  1756  
  1757  			})
  1758  
  1759  			When("creating subscription for the main package", func() {
  1760  				var subscription *operatorsv1alpha1.Subscription
  1761  
  1762  				BeforeEach(func() {
  1763  					By(`Create a subscription for test-package in catsrc`)
  1764  					subscriptionSpec := &operatorsv1alpha1.SubscriptionSpec{
  1765  						CatalogSource:          catsrcMain.GetName(),
  1766  						CatalogSourceNamespace: catsrcMain.GetNamespace(),
  1767  						Package:                packageMain.PackageName,
  1768  						Channel:                stableChannel,
  1769  						InstallPlanApproval:    operatorsv1alpha1.ApprovalAutomatic,
  1770  					}
  1771  					subscriptionName := genName("sub-")
  1772  					cleanupSubscription = createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subscriptionSpec)
  1773  
  1774  					var err error
  1775  					subscription, err = fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
  1776  					Expect(err).ToNot(HaveOccurred())
  1777  					Expect(subscription).ToNot(BeNil())
  1778  
  1779  					_, err = fetchCSV(crClient, generatedNamespace.GetName(), mainCSVName, csvSucceededChecker)
  1780  					Expect(err).ToNot(HaveOccurred())
  1781  
  1782  				})
  1783  
  1784  				AfterEach(func() {
  1785  					if cleanupSubscription != nil {
  1786  						cleanupSubscription()
  1787  					}
  1788  					if cleanup != nil {
  1789  						cleanup()
  1790  					}
  1791  				})
  1792  
  1793  				It("choose the dependent package from the same catsrc as the installing operator", func() {
  1794  					By(`ensure correct CSVs were picked`)
  1795  					Eventually(func() ([]string, error) {
  1796  						ip, err := crClient.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Get(context.Background(), subscription.Status.InstallPlanRef.Name, metav1.GetOptions{})
  1797  						if err != nil {
  1798  							return nil, err
  1799  						}
  1800  						return ip.Spec.ClusterServiceVersionNames, nil
  1801  					}).Should(ConsistOf(mainCSVName, rightCSVName))
  1802  				})
  1803  			})
  1804  		})
  1805  
  1806  		Context("creating CatalogSources providing the same dependency with different priority value", func() {
  1807  			var catsrcCleanup1, catsrcCleanup2, catsrcCleanup3 cleanupFunc
  1808  
  1809  			BeforeEach(func() {
  1810  				packageDepRight = registry.PackageManifest{PackageName: "PackageDependent"}
  1811  				csv := newCSV(rightCSVName, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"),
  1812  					[]apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  1813  				packageDepRight.DefaultChannelName = alphaChannel
  1814  				packageDepRight.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1815  				csvsRight = append(csvsRight, csv)
  1816  
  1817  				csv.Name = wrongCSVName
  1818  				packageDepWrong = packageDepRight
  1819  				packageDepWrong.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1820  				csvsWrong = append(csvsWrong, csv)
  1821  
  1822  				catsrcMain, catsrcCleanup1 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), generatedNamespace.GetName(),
  1823  					[]registry.PackageManifest{packageMain}, nil, csvsMain)
  1824  
  1825  				catsrcDepRight, catsrcCleanup2 = createInternalCatalogSourceWithPriority(kubeClient, crClient,
  1826  					genName("catsrc"), generatedNamespace.GetName(), []registry.PackageManifest{packageDepRight}, []apiextensionsv1.CustomResourceDefinition{crd},
  1827  					csvsRight, 100)
  1828  
  1829  				catsrcDepWrong, catsrcCleanup3 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), generatedNamespace.GetName(),
  1830  					[]registry.PackageManifest{packageDepWrong}, []apiextensionsv1.CustomResourceDefinition{crd}, csvsWrong)
  1831  
  1832  				By(`waiting for catalogsources to be ready`)
  1833  				_, err := fetchCatalogSourceOnStatus(crClient, catsrcMain.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1834  				Expect(err).ToNot(HaveOccurred())
  1835  				_, err = fetchCatalogSourceOnStatus(crClient, catsrcDepRight.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1836  				Expect(err).ToNot(HaveOccurred())
  1837  				_, err = fetchCatalogSourceOnStatus(crClient, catsrcDepWrong.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1838  				Expect(err).ToNot(HaveOccurred())
  1839  			})
  1840  
  1841  			AfterEach(func() {
  1842  				if catsrcCleanup1 != nil {
  1843  					catsrcCleanup1()
  1844  				}
  1845  				if catsrcCleanup2 != nil {
  1846  					catsrcCleanup2()
  1847  				}
  1848  				if catsrcCleanup3 != nil {
  1849  					catsrcCleanup3()
  1850  				}
  1851  			})
  1852  
  1853  			When("creating subscription for the main package", func() {
  1854  				var subscription *operatorsv1alpha1.Subscription
  1855  
  1856  				BeforeEach(func() {
  1857  					By(`Create a subscription for test-package in catsrc`)
  1858  					subscriptionSpec := &operatorsv1alpha1.SubscriptionSpec{
  1859  						CatalogSource:          catsrcMain.GetName(),
  1860  						CatalogSourceNamespace: catsrcMain.GetNamespace(),
  1861  						Package:                packageMain.PackageName,
  1862  						Channel:                stableChannel,
  1863  						InstallPlanApproval:    operatorsv1alpha1.ApprovalAutomatic,
  1864  					}
  1865  					subscriptionName := genName("sub-")
  1866  					cleanupSubscription = createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subscriptionSpec)
  1867  
  1868  					var err error
  1869  					subscription, err = fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
  1870  					Expect(err).ToNot(HaveOccurred())
  1871  					Expect(subscription).ToNot(BeNil())
  1872  
  1873  					_, err = fetchCSV(crClient, generatedNamespace.GetName(), mainCSVName, csvSucceededChecker)
  1874  					Expect(err).ToNot(HaveOccurred())
  1875  
  1876  				})
  1877  
  1878  				AfterEach(func() {
  1879  					if cleanupSubscription != nil {
  1880  						cleanupSubscription()
  1881  					}
  1882  					if cleanup != nil {
  1883  						cleanup()
  1884  					}
  1885  				})
  1886  
  1887  				It("choose the dependent package from the catsrc with higher priority", func() {
  1888  					By(`ensure correct CSVs were picked`)
  1889  					Eventually(func() ([]string, error) {
  1890  						ip, err := crClient.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Get(context.Background(), subscription.Status.InstallPlanRef.Name, metav1.GetOptions{})
  1891  						if err != nil {
  1892  							return nil, err
  1893  						}
  1894  						return ip.Spec.ClusterServiceVersionNames, nil
  1895  					}).Should(ConsistOf(mainCSVName, rightCSVName))
  1896  				})
  1897  			})
  1898  		})
  1899  
  1900  		Context("creating CatalogSources providing the same dependency under test and global namespaces", func() {
  1901  			var catsrcCleanup1, catsrcCleanup2, catsrcCleanup3 cleanupFunc
  1902  
  1903  			BeforeEach(func() {
  1904  
  1905  				packageDepRight = registry.PackageManifest{PackageName: "PackageDependent"}
  1906  				csv := newCSV(rightCSVName, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"),
  1907  					[]apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  1908  				packageDepRight.DefaultChannelName = alphaChannel
  1909  				packageDepRight.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1910  				csvsRight = append(csvsRight, csv)
  1911  
  1912  				csv.Name = wrongCSVName
  1913  				packageDepWrong = packageDepRight
  1914  				packageDepWrong.Channels = []registry.PackageChannel{{Name: alphaChannel, CurrentCSVName: csv.GetName()}}
  1915  				csvsWrong = append(csvsWrong, csv)
  1916  
  1917  				catsrcMain, catsrcCleanup1 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), generatedNamespace.GetName(),
  1918  					[]registry.PackageManifest{packageMain}, nil, csvsMain)
  1919  
  1920  				catsrcDepRight, catsrcCleanup2 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), generatedNamespace.GetName(),
  1921  					[]registry.PackageManifest{packageDepRight}, []apiextensionsv1.CustomResourceDefinition{crd}, csvsRight)
  1922  
  1923  				catsrcDepWrong, catsrcCleanup3 = createInternalCatalogSource(kubeClient, crClient, genName("catsrc"), operatorNamespace,
  1924  					[]registry.PackageManifest{packageDepWrong}, []apiextensionsv1.CustomResourceDefinition{crd}, csvsWrong)
  1925  
  1926  				By(`waiting for catalogsources to be ready`)
  1927  				_, err := fetchCatalogSourceOnStatus(crClient, catsrcMain.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1928  				Expect(err).ToNot(HaveOccurred())
  1929  				_, err = fetchCatalogSourceOnStatus(crClient, catsrcDepRight.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  1930  				Expect(err).ToNot(HaveOccurred())
  1931  				_, err = fetchCatalogSourceOnStatus(crClient, catsrcDepWrong.GetName(), operatorNamespace, catalogSourceRegistryPodSynced())
  1932  				Expect(err).ToNot(HaveOccurred())
  1933  			})
  1934  
  1935  			AfterEach(func() {
  1936  				if catsrcCleanup1 != nil {
  1937  					catsrcCleanup1()
  1938  				}
  1939  				if catsrcCleanup2 != nil {
  1940  					catsrcCleanup2()
  1941  				}
  1942  				if catsrcCleanup3 != nil {
  1943  					catsrcCleanup3()
  1944  				}
  1945  			})
  1946  
  1947  			When("creating subscription for the main package", func() {
  1948  				var subscription *operatorsv1alpha1.Subscription
  1949  
  1950  				BeforeEach(func() {
  1951  					By(`Create a subscription for test-package in catsrc`)
  1952  					subscriptionSpec := &operatorsv1alpha1.SubscriptionSpec{
  1953  						CatalogSource:          catsrcMain.GetName(),
  1954  						CatalogSourceNamespace: catsrcMain.GetNamespace(),
  1955  						Package:                packageMain.PackageName,
  1956  						Channel:                stableChannel,
  1957  						InstallPlanApproval:    operatorsv1alpha1.ApprovalAutomatic,
  1958  					}
  1959  					subscriptionName := genName("sub-")
  1960  					cleanupSubscription = createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subscriptionSpec)
  1961  
  1962  					var err error
  1963  					subscription, err = fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
  1964  					Expect(err).ToNot(HaveOccurred())
  1965  					Expect(subscription).ToNot(BeNil())
  1966  
  1967  					_, err = fetchCSV(crClient, generatedNamespace.GetName(), mainCSVName, csvSucceededChecker)
  1968  					Expect(err).ToNot(HaveOccurred())
  1969  
  1970  				})
  1971  
  1972  				AfterEach(func() {
  1973  					if cleanupSubscription != nil {
  1974  						cleanupSubscription()
  1975  					}
  1976  					if cleanup != nil {
  1977  						cleanup()
  1978  					}
  1979  				})
  1980  
  1981  				It("choose the dependent package from the catsrc in the same namespace as the installing operator", func() {
  1982  					By(`ensure correct CSVs were picked`)
  1983  					Eventually(func() ([]string, error) {
  1984  						ip, err := crClient.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Get(context.Background(), subscription.Status.InstallPlanRef.Name, metav1.GetOptions{})
  1985  						if err != nil {
  1986  							return nil, err
  1987  						}
  1988  						return ip.Spec.ClusterServiceVersionNames, nil
  1989  					}).Should(ConsistOf(mainCSVName, rightCSVName))
  1990  				})
  1991  			})
  1992  		})
  1993  	})
  1994  
  1995  	It("creation in case of transferring providedAPIs", func() {
  1996  		By(`csvA owns CRD1 & csvB owns CRD2 and requires CRD1`)
  1997  		By(`Create subscription for csvB lead to installation of csvB and csvA`)
  1998  		By(`Update catsrc to upgrade csvA to csvNewA which now requires CRD1`)
  1999  		By(`csvNewA can't be installed due to no other operators provide CRD1 for it`)
  2000  		By(`(Note: OLM can't pick csvA as dependency for csvNewA as it is from the same`)
  2001  		By(`same package)`)
  2002  		By(`Update catsrc again to upgrade csvB to csvNewB which now owns both CRD1 and`)
  2003  		By(`CRD2.`)
  2004  		By(`Now csvNewA and csvNewB are installed successfully as csvNewB provides CRD1`)
  2005  		By(`that csvNewA requires`)
  2006  		By(`	PARITY: this test covers the same scenario as the TestSolveOperators_TransferApiOwnership unit test`)
  2007  		kubeClient := ctx.Ctx().KubeClient()
  2008  		crClient := ctx.Ctx().OperatorClient()
  2009  
  2010  		crd := newCRD(genName("ins"))
  2011  		crd2 := newCRD(genName("ins"))
  2012  
  2013  		By(`Create CSV`)
  2014  		packageName1 := genName("apackage")
  2015  		packageName2 := genName("bpackage")
  2016  
  2017  		By(`csvA provides CRD`)
  2018  		csvA := newCSV("nginx-a", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  2019  		By(`csvB provides CRD2 and requires CRD`)
  2020  		csvB := newCSV("nginx-b", generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd2}, []apiextensionsv1.CustomResourceDefinition{crd}, nil)
  2021  		By(`New csvA requires CRD (transfer CRD ownership to the new csvB)`)
  2022  		csvNewA := newCSV("nginx-new-a", generatedNamespace.GetName(), "nginx-a", semver.MustParse("0.2.0"), nil, []apiextensionsv1.CustomResourceDefinition{crd}, nil)
  2023  		By(`New csvB provides CRD and CRD2`)
  2024  		csvNewB := newCSV("nginx-new-b", generatedNamespace.GetName(), "nginx-b", semver.MustParse("0.2.0"), []apiextensionsv1.CustomResourceDefinition{crd, crd2}, nil, nil)
  2025  
  2026  		By(`constraints not satisfiable:`)
  2027  		By(`apackagert6cq requires at least one of catsrcc6xgr/operators/stable/nginx-new-a,`)
  2028  		By(`apackagert6cq is mandatory,`)
  2029  		By(`pkgunique/apackagert6cq permits at most 1 of catsrcc6xgr/operators/stable/nginx-new-a, catsrcc6xgr/operators/stable/nginx-a,`)
  2030  		By(`catsrcc6xgr/operators/stable/nginx-new-a requires at least one of catsrcc6xgr/operators/stable/nginx-a`)
  2031  
  2032  		By(`Create PackageManifests 1`)
  2033  		By(`Contain csvA, ABC and B`)
  2034  		manifests := []registry.PackageManifest{
  2035  			{
  2036  				PackageName: packageName1,
  2037  				Channels: []registry.PackageChannel{
  2038  					{Name: stableChannel, CurrentCSVName: csvA.GetName()},
  2039  				},
  2040  				DefaultChannelName: stableChannel,
  2041  			},
  2042  			{
  2043  				PackageName: packageName2,
  2044  				Channels: []registry.PackageChannel{
  2045  					{Name: stableChannel, CurrentCSVName: csvB.GetName()},
  2046  				},
  2047  				DefaultChannelName: stableChannel,
  2048  			},
  2049  		}
  2050  
  2051  		catalogSourceName := genName("catsrc")
  2052  		catsrc, cleanup := createInternalCatalogSource(kubeClient, crClient, catalogSourceName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd, crd2}, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB})
  2053  		defer cleanup()
  2054  
  2055  		By(`Ensure that the catalog source is resolved before we create a subscription.`)
  2056  		_, err := fetchCatalogSourceOnStatus(crClient, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  2057  		require.NoError(GinkgoT(), err)
  2058  
  2059  		subscriptionSpec := &operatorsv1alpha1.SubscriptionSpec{
  2060  			CatalogSource:          catsrc.GetName(),
  2061  			CatalogSourceNamespace: catsrc.GetNamespace(),
  2062  			Package:                packageName2,
  2063  			Channel:                stableChannel,
  2064  			StartingCSV:            csvB.GetName(),
  2065  			InstallPlanApproval:    operatorsv1alpha1.ApprovalAutomatic,
  2066  		}
  2067  
  2068  		By(`Create a subscription that has a dependency`)
  2069  		subscriptionName := genName("sub-")
  2070  		cleanupSubscription := createSubscriptionForCatalogWithSpec(GinkgoT(), crClient, generatedNamespace.GetName(), subscriptionName, subscriptionSpec)
  2071  		defer cleanupSubscription()
  2072  
  2073  		subscription, err := fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker())
  2074  		require.NoError(GinkgoT(), err)
  2075  		require.NotNil(GinkgoT(), subscription)
  2076  
  2077  		By(`Check that a single catalog source was used to resolve the InstallPlan`)
  2078  		_, err = fetchInstallPlanWithNamespace(GinkgoT(), crClient, subscription.Status.InstallPlanRef.Name, generatedNamespace.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))
  2079  		require.NoError(GinkgoT(), err)
  2080  		By(`Fetch CSVs A and B`)
  2081  		_, err = fetchCSV(crClient, generatedNamespace.GetName(), csvA.Name, csvSucceededChecker)
  2082  		require.NoError(GinkgoT(), err)
  2083  		_, err = fetchCSV(crClient, generatedNamespace.GetName(), csvB.Name, csvSucceededChecker)
  2084  		require.NoError(GinkgoT(), err)
  2085  
  2086  		By(`Update PackageManifest`)
  2087  		manifests = []registry.PackageManifest{
  2088  			{
  2089  				PackageName: packageName1,
  2090  				Channels: []registry.PackageChannel{
  2091  					{Name: stableChannel, CurrentCSVName: csvNewA.GetName()},
  2092  				},
  2093  				DefaultChannelName: stableChannel,
  2094  			},
  2095  			{
  2096  				PackageName: packageName2,
  2097  				Channels: []registry.PackageChannel{
  2098  					{Name: stableChannel, CurrentCSVName: csvB.GetName()},
  2099  				},
  2100  				DefaultChannelName: stableChannel,
  2101  			},
  2102  		}
  2103  		updateInternalCatalog(GinkgoT(), kubeClient, crClient, catalogSourceName, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{crd, crd2}, []operatorsv1alpha1.ClusterServiceVersion{csvNewA, csvA, csvB}, manifests)
  2104  		csvAsub := strings.Join([]string{packageName1, stableChannel, catalogSourceName, generatedNamespace.GetName()}, "-")
  2105  		_, err = fetchSubscription(crClient, generatedNamespace.GetName(), csvAsub, subscriptionStateAtLatestChecker())
  2106  		require.NoError(GinkgoT(), err)
  2107  		By(`Ensure csvNewA is not installed`)
  2108  		_, err = crClient.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Get(context.Background(), csvNewA.Name, metav1.GetOptions{})
  2109  		require.Error(GinkgoT(), err)
  2110  		By(`Ensure csvA still exists`)
  2111  		_, err = fetchCSV(crClient, generatedNamespace.GetName(), csvA.Name, csvSucceededChecker)
  2112  		require.NoError(GinkgoT(), err)
  2113  
  2114  		By(`Update packagemanifest again`)
  2115  		manifests = []registry.PackageManifest{
  2116  			{
  2117  				PackageName: packageName1,
  2118  				Channels: []registry.PackageChannel{
  2119  					{Name: stableChannel, CurrentCSVName: csvNewA.GetName()},
  2120  				},
  2121  				DefaultChannelName: stableChannel,
  2122  			},
  2123  			{
  2124  				PackageName: packageName2,
  2125  				Channels: []registry.PackageChannel{
  2126  					{Name: stableChannel, CurrentCSVName: csvNewB.GetName()},
  2127  				},
  2128  				DefaultChannelName: stableChannel,
  2129  			},
  2130  		}
  2131  		updateInternalCatalog(GinkgoT(), kubeClient, crClient, catalogSourceName, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{crd, crd2}, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB, csvNewA, csvNewB}, manifests)
  2132  
  2133  		_, err = fetchSubscription(crClient, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(subscription.Status.InstallPlanRef.Name))
  2134  		require.NoError(GinkgoT(), err)
  2135  		By(`Ensure csvNewA is installed`)
  2136  		_, err = fetchCSV(crClient, generatedNamespace.GetName(), csvNewA.Name, csvSucceededChecker)
  2137  		require.NoError(GinkgoT(), err)
  2138  		By(`Ensure csvNewB is installed`)
  2139  		_, err = fetchCSV(crClient, generatedNamespace.GetName(), csvNewB.Name, csvSucceededChecker)
  2140  		require.NoError(GinkgoT(), err)
  2141  	})
  2142  
  2143  	When("A subscription is created for an operator that requires an API that is not available", func() {
  2144  		var (
  2145  			c          operatorclient.ClientInterface
  2146  			crc        versioned.Interface
  2147  			teardown   func()
  2148  			cleanup    func()
  2149  			packages   []registry.PackageManifest
  2150  			crd        = newCRD(genName("foo-"))
  2151  			csvA       operatorsv1alpha1.ClusterServiceVersion
  2152  			csvB       operatorsv1alpha1.ClusterServiceVersion
  2153  			subName    = genName("test-subscription-")
  2154  			catSrcName = genName("test-catalog-")
  2155  		)
  2156  
  2157  		BeforeEach(func() {
  2158  			c = newKubeClient()
  2159  			crc = newCRClient()
  2160  
  2161  			packages = []registry.PackageManifest{
  2162  				{
  2163  					PackageName: "test-package",
  2164  					Channels: []registry.PackageChannel{
  2165  						{Name: "alpha", CurrentCSVName: "csvA"},
  2166  					},
  2167  					DefaultChannelName: "alpha",
  2168  				},
  2169  			}
  2170  			csvA = newCSV("csvA", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), nil, []apiextensionsv1.CustomResourceDefinition{crd}, nil)
  2171  
  2172  			_, teardown = createInternalCatalogSource(c, ctx.Ctx().OperatorClient(), catSrcName, generatedNamespace.GetName(), packages, nil, []operatorsv1alpha1.ClusterServiceVersion{csvA})
  2173  
  2174  			By(`Ensure that the catalog source is resolved before we create a subscription.`)
  2175  			_, err := fetchCatalogSourceOnStatus(crc, catSrcName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
  2176  			require.NoError(GinkgoT(), err)
  2177  
  2178  			cleanup = createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subName, catSrcName, "test-package", "alpha", "", operatorsv1alpha1.ApprovalAutomatic)
  2179  		})
  2180  
  2181  		AfterEach(func() {
  2182  			cleanup()
  2183  			teardown()
  2184  		})
  2185  
  2186  		It("the subscription has a condition in it's status that indicates the resolution error", func() {
  2187  			Eventually(func() (corev1.ConditionStatus, error) {
  2188  				sub, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
  2189  				if err != nil {
  2190  					return corev1.ConditionUnknown, err
  2191  				}
  2192  				return sub.Status.GetCondition(operatorsv1alpha1.SubscriptionResolutionFailed).Status, nil
  2193  			}).Should(Equal(corev1.ConditionTrue))
  2194  		})
  2195  
  2196  		When("the required API is made available", func() {
  2197  
  2198  			BeforeEach(func() {
  2199  				newPkg := registry.PackageManifest{
  2200  					PackageName: "another-package",
  2201  					Channels: []registry.PackageChannel{
  2202  						{Name: "alpha", CurrentCSVName: "csvB"},
  2203  					},
  2204  					DefaultChannelName: "alpha",
  2205  				}
  2206  				packages = append(packages, newPkg)
  2207  
  2208  				csvB = newCSV("csvB", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
  2209  
  2210  				updateInternalCatalog(GinkgoT(), c, crc, catSrcName, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB}, packages)
  2211  			})
  2212  
  2213  			It("the ResolutionFailed condition previously set in its status that indicated the resolution error is cleared off", func() {
  2214  				Eventually(func() (corev1.ConditionStatus, error) {
  2215  					sub, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
  2216  					if err != nil {
  2217  						return corev1.ConditionFalse, err
  2218  					}
  2219  					return sub.Status.GetCondition(operatorsv1alpha1.SubscriptionResolutionFailed).Status, nil
  2220  				}).Should(Equal(corev1.ConditionUnknown))
  2221  			})
  2222  		})
  2223  	})
  2224  
  2225  	When("an unannotated ClusterServiceVersion exists with an associated Subscription", func() {
  2226  		var (
  2227  			teardown func()
  2228  		)
  2229  
  2230  		BeforeEach(func() {
  2231  			teardown = func() {}
  2232  
  2233  			packages := []registry.PackageManifest{
  2234  				{
  2235  					PackageName: "package",
  2236  					Channels: []registry.PackageChannel{
  2237  						{Name: "channel-x", CurrentCSVName: "csv-x"},
  2238  						{Name: "channel-y", CurrentCSVName: "csv-y"},
  2239  					},
  2240  					DefaultChannelName: "channel-x",
  2241  				},
  2242  			}
  2243  
  2244  			x := newCSV("csv-x", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), nil, nil, nil)
  2245  			y := newCSV("csv-y", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), nil, nil, nil)
  2246  
  2247  			_, teardown = createInternalCatalogSource(ctx.Ctx().KubeClient(), ctx.Ctx().OperatorClient(), "test-catalog", generatedNamespace.GetName(), packages, nil, []operatorsv1alpha1.ClusterServiceVersion{x, y})
  2248  
  2249  			createSubscriptionForCatalog(ctx.Ctx().OperatorClient(), generatedNamespace.GetName(), "test-subscription-x", "test-catalog", "package", "channel-x", "", operatorsv1alpha1.ApprovalAutomatic)
  2250  
  2251  			Eventually(func() error {
  2252  				var unannotated operatorsv1alpha1.ClusterServiceVersion
  2253  				if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKey{Namespace: generatedNamespace.GetName(), Name: "csv-x"}, &unannotated); err != nil {
  2254  					return err
  2255  				}
  2256  				if _, ok := unannotated.Annotations["operatorframework.io/properties"]; !ok {
  2257  					return nil
  2258  				}
  2259  				delete(unannotated.Annotations, "operatorframework.io/properties")
  2260  				return ctx.Ctx().Client().Update(context.Background(), &unannotated)
  2261  			}).Should(Succeed())
  2262  		})
  2263  
  2264  		AfterEach(func() {
  2265  			teardown()
  2266  		})
  2267  
  2268  		It("uses inferred properties to prevent a duplicate installation from the same package ", func() {
  2269  			createSubscriptionForCatalog(ctx.Ctx().OperatorClient(), generatedNamespace.GetName(), "test-subscription-y", "test-catalog", "package", "channel-y", "", operatorsv1alpha1.ApprovalAutomatic)
  2270  
  2271  			Consistently(func() error {
  2272  				var no operatorsv1alpha1.ClusterServiceVersion
  2273  				return ctx.Ctx().Client().Get(context.Background(), client.ObjectKey{Namespace: generatedNamespace.GetName(), Name: "csv-y"}, &no)
  2274  			}).ShouldNot(Succeed())
  2275  		})
  2276  	})
  2277  
  2278  	When("there exists a Subscription to an operator having dependency candidates in both default and nondefault channels", func() {
  2279  		var (
  2280  			teardown func()
  2281  		)
  2282  
  2283  		BeforeEach(func() {
  2284  			teardown = func() {}
  2285  
  2286  			packages := []registry.PackageManifest{
  2287  				{
  2288  					PackageName: "dependency",
  2289  					Channels: []registry.PackageChannel{
  2290  						{Name: "default", CurrentCSVName: "csv-dependency"},
  2291  						{Name: "nondefault", CurrentCSVName: "csv-dependency"},
  2292  					},
  2293  					DefaultChannelName: "default",
  2294  				},
  2295  				{
  2296  					PackageName: "root",
  2297  					Channels: []registry.PackageChannel{
  2298  						{Name: "unimportant", CurrentCSVName: "csv-root"},
  2299  					},
  2300  					DefaultChannelName: "unimportant",
  2301  				},
  2302  			}
  2303  
  2304  			crds := []apiextensionsv1.CustomResourceDefinition{newCRD(genName("crd-"))}
  2305  			csvs := []operatorsv1alpha1.ClusterServiceVersion{
  2306  				newCSV("csv-dependency", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), crds, nil, nil),
  2307  				newCSV("csv-root", generatedNamespace.GetName(), "", semver.MustParse("1.0.0"), nil, crds, nil),
  2308  			}
  2309  
  2310  			_, teardown = createInternalCatalogSource(ctx.Ctx().KubeClient(), ctx.Ctx().OperatorClient(), "test-catalog", generatedNamespace.GetName(), packages, crds, csvs)
  2311  
  2312  			createSubscriptionForCatalog(ctx.Ctx().OperatorClient(), generatedNamespace.GetName(), "test-subscription", "test-catalog", "root", "unimportant", "", operatorsv1alpha1.ApprovalAutomatic)
  2313  		})
  2314  
  2315  		AfterEach(func() {
  2316  			teardown()
  2317  		})
  2318  
  2319  		It("should create a Subscription using the candidate's default channel", func() {
  2320  			Eventually(func() ([]operatorsv1alpha1.Subscription, error) {
  2321  				var list operatorsv1alpha1.SubscriptionList
  2322  				if err := ctx.Ctx().Client().List(context.Background(), &list); err != nil {
  2323  					return nil, err
  2324  				}
  2325  				return list.Items, nil
  2326  			}).Should(ContainElement(WithTransform(
  2327  				func(in operatorsv1alpha1.Subscription) operatorsv1alpha1.SubscriptionSpec {
  2328  					return operatorsv1alpha1.SubscriptionSpec{
  2329  						CatalogSource:          in.Spec.CatalogSource,
  2330  						CatalogSourceNamespace: in.Spec.CatalogSourceNamespace,
  2331  						Package:                in.Spec.Package,
  2332  						Channel:                in.Spec.Channel,
  2333  					}
  2334  				},
  2335  				Equal(operatorsv1alpha1.SubscriptionSpec{
  2336  					CatalogSource:          "test-catalog",
  2337  					CatalogSourceNamespace: generatedNamespace.GetName(),
  2338  					Package:                "dependency",
  2339  					Channel:                "default",
  2340  				}),
  2341  			)))
  2342  		})
  2343  	})
  2344  
  2345  	It("unpacks bundle image", func() {
  2346  		catsrc := &operatorsv1alpha1.CatalogSource{
  2347  			ObjectMeta: metav1.ObjectMeta{
  2348  				Name:      genName("kiali-"),
  2349  				Namespace: generatedNamespace.GetName(),
  2350  				Labels:    map[string]string{"olm.catalogSource": "kaili-catalog"},
  2351  			},
  2352  			Spec: operatorsv1alpha1.CatalogSourceSpec{
  2353  				Image:      "quay.io/operator-framework/ci-index:latest",
  2354  				SourceType: operatorsv1alpha1.SourceTypeGrpc,
  2355  				GrpcPodConfig: &operatorsv1alpha1.GrpcPodConfig{
  2356  					SecurityContextConfig: operatorsv1alpha1.Restricted,
  2357  				},
  2358  			},
  2359  		}
  2360  		catsrc, err := crc.OperatorsV1alpha1().CatalogSources(catsrc.GetNamespace()).Create(context.Background(), catsrc, metav1.CreateOptions{})
  2361  		require.NoError(GinkgoT(), err)
  2362  		defer func() {
  2363  			Eventually(func() error {
  2364  				return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), catsrc))
  2365  			}).Should(Succeed())
  2366  		}()
  2367  
  2368  		By("waiting for the CatalogSource to be ready")
  2369  		catsrc, err = fetchCatalogSourceOnStatus(crc, catsrc.GetName(), catsrc.GetNamespace(), catalogSourceRegistryPodSynced())
  2370  		require.NoError(GinkgoT(), err)
  2371  
  2372  		By("generating a Subscription")
  2373  		subName := genName("kiali-")
  2374  		cleanUpSubscriptionFn := createSubscriptionForCatalog(crc, catsrc.GetNamespace(), subName, catsrc.GetName(), "kiali", stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
  2375  		defer cleanUpSubscriptionFn()
  2376  
  2377  		By("waiting for the InstallPlan to get created for the subscription")
  2378  		sub, err := fetchSubscription(crc, catsrc.GetNamespace(), subName, subscriptionHasInstallPlanChecker())
  2379  		require.NoError(GinkgoT(), err)
  2380  
  2381  		By("waiting for the expected InstallPlan's execution to either fail or succeed")
  2382  		ipName := sub.Status.InstallPlanRef.Name
  2383  		ip, err := waitForInstallPlan(crc, ipName, sub.GetNamespace(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseFailed, operatorsv1alpha1.InstallPlanPhaseComplete))
  2384  		require.NoError(GinkgoT(), err)
  2385  		require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, ip.Status.Phase, "InstallPlan not complete")
  2386  
  2387  		By("ensuring the InstallPlan contains the steps resolved from the bundle image")
  2388  		operatorName := "kiali-operator"
  2389  		expectedSteps := map[registry.ResourceKey]struct{}{
  2390  			{Name: operatorName, Kind: "ClusterServiceVersion"}:                                  {},
  2391  			{Name: "kialis.kiali.io", Kind: "CustomResourceDefinition"}:                          {},
  2392  			{Name: "monitoringdashboards.monitoring.kiali.io", Kind: "CustomResourceDefinition"}: {},
  2393  			{Name: operatorName, Kind: "ServiceAccount"}:                                         {},
  2394  			{Name: operatorName, Kind: "ClusterRole"}:                                            {},
  2395  			{Name: operatorName, Kind: "ClusterRoleBinding"}:                                     {},
  2396  		}
  2397  		require.Lenf(GinkgoT(), ip.Status.Plan, len(expectedSteps), "number of expected steps does not match installed: %v", ip.Status.Plan)
  2398  
  2399  		for _, step := range ip.Status.Plan {
  2400  			key := registry.ResourceKey{
  2401  				Name: step.Resource.Name,
  2402  				Kind: step.Resource.Kind,
  2403  			}
  2404  			for expected := range expectedSteps {
  2405  				if strings.HasPrefix(key.Name, expected.Name) && key.Kind == expected.Kind {
  2406  					delete(expectedSteps, expected)
  2407  				}
  2408  			}
  2409  		}
  2410  		require.Lenf(GinkgoT(), expectedSteps, 0, "Actual resource steps do not match expected: %#v", expectedSteps)
  2411  	})
  2412  
  2413  	When("unpacking bundle", func() {
  2414  		var (
  2415  			magicCatalog      *MagicCatalog
  2416  			catalogSourceName string
  2417  			subName           string
  2418  		)
  2419  
  2420  		BeforeEach(func() {
  2421  			By("deploying the testing catalog")
  2422  			provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.1.0.yaml"))
  2423  			Expect(err).To(BeNil())
  2424  			catalogSourceName = fmt.Sprintf("%s-catsrc", generatedNamespace.GetName())
  2425  			magicCatalog = NewMagicCatalog(ctx.Ctx().Client(), generatedNamespace.GetName(), catalogSourceName, provider)
  2426  			Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil())
  2427  
  2428  			By("creating the testing subscription")
  2429  			subName = fmt.Sprintf("%s-test-package-sub", generatedNamespace.GetName())
  2430  			createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subName, catalogSourceName, "test-package", stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
  2431  
  2432  			By("waiting until the subscription has an IP reference")
  2433  			subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasInstallPlanChecker())
  2434  			Expect(err).Should(BeNil())
  2435  
  2436  			By("waiting for the v0.1.0 CSV to report a succeeded phase")
  2437  			_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
  2438  			Expect(err).ShouldNot(HaveOccurred())
  2439  		})
  2440  
  2441  		It("should not report unpacking progress or errors after successfull unpacking", func() {
  2442  			By("verifying that the subscription is not reporting unpacking progress")
  2443  			Eventually(
  2444  				func() (corev1.ConditionStatus, error) {
  2445  					fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
  2446  					if err != nil {
  2447  						return "", err
  2448  					}
  2449  					cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpacking)
  2450  					return cond.Status, nil
  2451  				},
  2452  				5*time.Minute,
  2453  				interval,
  2454  			).Should(Equal(corev1.ConditionUnknown))
  2455  
  2456  			By("verifying that the subscription is not reporting unpacking errors")
  2457  			Eventually(
  2458  				func() (corev1.ConditionStatus, error) {
  2459  					fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
  2460  					if err != nil {
  2461  						return "", err
  2462  					}
  2463  					cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpackFailed)
  2464  					return cond.Status, nil
  2465  				},
  2466  				5*time.Minute,
  2467  				interval,
  2468  			).Should(Equal(corev1.ConditionUnknown))
  2469  		})
  2470  
  2471  		Context("with bundle which OLM will fail to unpack", func() {
  2472  			BeforeEach(func() {
  2473  				By("patching the OperatorGroup to reduce the bundle unpacking timeout")
  2474  				ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
  2475  				addBundleUnpackTimeoutOGAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "1s")
  2476  
  2477  				By("updating the catalog with a broken v0.2.0 bundle image")
  2478  				brokenProvider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.2.0-non-existent-tag.yaml"))
  2479  				Expect(err).To(BeNil())
  2480  				err = magicCatalog.UpdateCatalog(context.Background(), brokenProvider)
  2481  				Expect(err).To(BeNil())
  2482  			})
  2483  
  2484  			It("should expose a condition indicating failure to unpack", func() {
  2485  				By("verifying that the subscription is reporting bundle unpack failure condition")
  2486  				Eventually(
  2487  					func() (string, error) {
  2488  						fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
  2489  						if err != nil {
  2490  							return "", err
  2491  						}
  2492  						cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpackFailed)
  2493  						if cond.Status != corev1.ConditionTrue || cond.Reason != "BundleUnpackFailed" {
  2494  							return "", fmt.Errorf("%s condition not found", operatorsv1alpha1.SubscriptionBundleUnpackFailed)
  2495  						}
  2496  
  2497  						return cond.Message, nil
  2498  					},
  2499  					5*time.Minute,
  2500  					interval,
  2501  				).Should(ContainSubstring("bundle unpacking failed. Reason: DeadlineExceeded"))
  2502  
  2503  				By("waiting for the subscription to maintain the example-operator.v0.1.0 status.currentCSV")
  2504  				Consistently(subscriptionCurrentCSVGetter(crc, generatedNamespace.GetName(), subName)).Should(Equal("example-operator.v0.1.0"))
  2505  			})
  2506  
  2507  			It("should be able to recover when catalog gets updated with a fixed version", func() {
  2508  				By("patching the OperatorGroup to reduce the bundle unpacking timeout")
  2509  				ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
  2510  				addBundleUnpackTimeoutOGAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "5m")
  2511  
  2512  				By("updating the catalog with a fixed v0.2.0 bundle image")
  2513  				brokenProvider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.2.0.yaml"))
  2514  				Expect(err).To(BeNil())
  2515  				err = magicCatalog.UpdateCatalog(context.Background(), brokenProvider)
  2516  				Expect(err).To(BeNil())
  2517  
  2518  				By("waiting for the subscription to have v0.2.0 installed")
  2519  				_, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasCurrentCSV("example-operator.v0.2.0"))
  2520  				Expect(err).Should(BeNil())
  2521  			})
  2522  
  2523  			It("should report deprecation conditions when package, channel, and bundle are referenced in an olm.deprecations object", func() {
  2524  				By("patching the OperatorGroup to reduce the bundle unpacking timeout")
  2525  				ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
  2526  				addBundleUnpackTimeoutOGAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "5m")
  2527  
  2528  				By("updating the catalog with a fixed v0.2.0 bundle image marked deprecated")
  2529  				provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.2.0-deprecations.yaml"))
  2530  				Expect(err).To(BeNil())
  2531  				err = magicCatalog.UpdateCatalog(context.Background(), provider)
  2532  				Expect(err).To(BeNil())
  2533  
  2534  				By("waiting for the subscription to have v0.2.0 installed with a Bundle Deprecated condition")
  2535  				sub, err := fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasCondition(
  2536  					operatorsv1alpha1.SubscriptionBundleDeprecated,
  2537  					corev1.ConditionTrue,
  2538  					"",
  2539  					"olm.bundle/example-operator.v0.2.0: bundle \"example-operator.v0.2.0\" has been deprecated. Please switch to a different one."))
  2540  				Expect(err).Should(BeNil())
  2541  
  2542  				By("checking for the deprecated conditions")
  2543  				By(`Operator is deprecated at all three levels in the catalog`)
  2544  				packageCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionPackageDeprecated)
  2545  				Expect(packageCondition.Status).To(Equal(corev1.ConditionTrue))
  2546  				channelCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionChannelDeprecated)
  2547  				Expect(channelCondition.Status).To(Equal(corev1.ConditionTrue))
  2548  				bundleCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleDeprecated)
  2549  				Expect(bundleCondition.Status).To(Equal(corev1.ConditionTrue))
  2550  
  2551  				By("verifying that a roll-up condition is present containing all deprecation conditions")
  2552  				By(`Roll-up condition should be present and contain deprecation messages from all three levels`)
  2553  				rollUpCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionDeprecated)
  2554  				Expect(rollUpCondition.Status).To(Equal(corev1.ConditionTrue))
  2555  				Expect(rollUpCondition.Message).To(ContainSubstring(packageCondition.Message))
  2556  				Expect(rollUpCondition.Message).To(ContainSubstring(channelCondition.Message))
  2557  				Expect(rollUpCondition.Message).To(ContainSubstring(bundleCondition.Message))
  2558  
  2559  				By("updating the catalog with a fixed v0.3.0 bundle image no longer marked deprecated")
  2560  				provider, err = NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.3.0.yaml"))
  2561  				Expect(err).To(BeNil())
  2562  				err = magicCatalog.UpdateCatalog(context.Background(), provider)
  2563  				Expect(err).To(BeNil())
  2564  
  2565  				By("waiting for the subscription to have v0.3.0 installed with no deprecation message present")
  2566  				_, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionDoesNotHaveCondition(operatorsv1alpha1.SubscriptionDeprecated))
  2567  				Expect(err).Should(BeNil())
  2568  			})
  2569  
  2570  			It("[FLAKE] should report only package and channel deprecation conditions when bundle is no longer deprecated", func() {
  2571  				By("patching the OperatorGroup to reduce the bundle unpacking timeout")
  2572  				ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
  2573  				addBundleUnpackTimeoutOGAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "5m")
  2574  
  2575  				By("updating the catalog with a fixed v0.2.0 bundle image marked deprecated at all levels")
  2576  				provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.2.0-deprecations.yaml"))
  2577  				Expect(err).To(BeNil())
  2578  				err = magicCatalog.UpdateCatalog(context.Background(), provider)
  2579  				Expect(err).To(BeNil())
  2580  
  2581  				By("waiting for the subscription to have v0.2.0 installed with a Bundle Deprecated condition")
  2582  				sub, err := fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasCondition(
  2583  					operatorsv1alpha1.SubscriptionBundleDeprecated,
  2584  					corev1.ConditionTrue,
  2585  					"",
  2586  					"olm.bundle/example-operator.v0.2.0: bundle \"example-operator.v0.2.0\" has been deprecated. Please switch to a different one."))
  2587  				Expect(err).Should(BeNil())
  2588  
  2589  				By("checking for the bundle deprecated condition")
  2590  				bundleCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleDeprecated)
  2591  				Expect(bundleCondition.Status).To(Equal(corev1.ConditionTrue))
  2592  				bundleDeprecatedMessage := bundleCondition.Message
  2593  
  2594  				By("updating the catalog with a fixed v0.3.0 bundle marked partially deprecated")
  2595  				provider, err = NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.3.0-deprecations.yaml"))
  2596  				Expect(err).To(BeNil())
  2597  				err = magicCatalog.UpdateCatalog(context.Background(), provider)
  2598  				Expect(err).To(BeNil())
  2599  
  2600  				By("waiting for the subscription to switch to v0.3.0")
  2601  				sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasCurrentCSV("example-operator.v0.3.0"))
  2602  				Expect(err).Should(BeNil())
  2603  
  2604  				By("waiting for the subscription to have be at latest known")
  2605  				sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionStateAtLatestChecker())
  2606  				Expect(err).Should(BeNil())
  2607  
  2608  				By("waiting for the install plan pending to go away")
  2609  				sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName,
  2610  					subscriptionHasCondition(
  2611  						operatorsv1alpha1.SubscriptionInstallPlanPending,
  2612  						corev1.ConditionUnknown,
  2613  						"",
  2614  						"",
  2615  					),
  2616  				)
  2617  				Expect(err).Should(BeNil())
  2618  
  2619  				By("waiting for the subscription to have v0.3.0 installed without a bundle deprecated condition")
  2620  				sub, err = fetchSubscription(crc, generatedNamespace.GetName(), subName,
  2621  					subscriptionHasCondition(
  2622  						operatorsv1alpha1.SubscriptionBundleDeprecated,
  2623  						corev1.ConditionUnknown,
  2624  						"",
  2625  						"",
  2626  					),
  2627  				)
  2628  				Expect(err).Should(BeNil())
  2629  
  2630  				By("checking for the deprecated conditions")
  2631  				By(`Operator is deprecated at only Package and Channel levels`)
  2632  				packageCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionPackageDeprecated)
  2633  				Expect(packageCondition.Status).To(Equal(corev1.ConditionTrue))
  2634  				channelCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionChannelDeprecated)
  2635  				Expect(channelCondition.Status).To(Equal(corev1.ConditionTrue))
  2636  
  2637  				By("verifying that a roll-up condition is present not containing bundle deprecation condition")
  2638  				By(`Roll-up condition should be present and contain deprecation messages from Package and Channel levels`)
  2639  				rollUpCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionDeprecated)
  2640  				Expect(rollUpCondition.Status).To(Equal(corev1.ConditionTrue))
  2641  				Expect(rollUpCondition.Message).To(ContainSubstring(packageCondition.Message))
  2642  				Expect(rollUpCondition.Message).To(ContainSubstring(channelCondition.Message))
  2643  				Expect(rollUpCondition.Message).ToNot(ContainSubstring(bundleDeprecatedMessage))
  2644  			})
  2645  
  2646  			It("should report deprecated status when catalog is updated to deprecate an installed bundle", func() {
  2647  				By("patching the OperatorGroup to reduce the bundle unpacking timeout")
  2648  				ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
  2649  				addBundleUnpackTimeoutOGAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "5m")
  2650  
  2651  				By("updating the catalog with a fixed v0.2.0 bundle not marked deprecated")
  2652  				provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.2.0.yaml"))
  2653  				Expect(err).To(BeNil())
  2654  				err = magicCatalog.UpdateCatalog(context.Background(), provider)
  2655  				Expect(err).To(BeNil())
  2656  
  2657  				By("waiting for the subscription to have v0.2.0 installed")
  2658  				sub, err := fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasCurrentCSV("example-operator.v0.2.0"))
  2659  				Expect(err).Should(BeNil())
  2660  
  2661  				By("the subscription should not be marked deprecated")
  2662  				rollupCondition := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionDeprecated)
  2663  				Expect(rollupCondition.Status).To(Equal(corev1.ConditionUnknown))
  2664  
  2665  				By("updating the catalog with a fixed v0.2.0 bundle image marked deprecated at all levels")
  2666  				provider, err = NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.2.0-deprecations.yaml"))
  2667  				Expect(err).To(BeNil())
  2668  				err = magicCatalog.UpdateCatalog(context.Background(), provider)
  2669  				Expect(err).To(BeNil())
  2670  
  2671  				By("checking for the bundle deprecated condition")
  2672  				Eventually(func() (bool, error) {
  2673  					sub, err := fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasCurrentCSV("example-operator.v0.2.0"))
  2674  					if err != nil {
  2675  						return false, err
  2676  					}
  2677  					cond := sub.Status.GetCondition(operatorsv1alpha1.SubscriptionDeprecated)
  2678  					if cond.Status != corev1.ConditionTrue {
  2679  						return false, fmt.Errorf("%s condition not found", operatorsv1alpha1.SubscriptionDeprecated)
  2680  					}
  2681  
  2682  					return true, nil
  2683  				}, 2*time.Minute, time.Second*10).Should(BeTrue())
  2684  			})
  2685  		})
  2686  	})
  2687  	When("bundle unpack retries are enabled", func() {
  2688  		It("should retry failing unpack jobs", func() {
  2689  			if ok, err := inKind(c); ok && err == nil {
  2690  				Skip("This spec fails when run using KIND cluster. See https://github.com/operator-framework/operator-lifecycle-manager/issues/2420 for more details")
  2691  			} else if err != nil {
  2692  				Skip("Could not determine whether running in a kind cluster. Skipping.")
  2693  			}
  2694  			By("Ensuring a registry to host bundle images")
  2695  			local, err := Local(c)
  2696  			Expect(err).NotTo(HaveOccurred(), "cannot determine if test running locally or on CI: %s", err)
  2697  
  2698  			var registryURL string
  2699  			var copyImage func(dst, dstTag, src, srcTag string) error
  2700  			if local {
  2701  				registryURL, err = createDockerRegistry(c, generatedNamespace.GetName())
  2702  				Expect(err).NotTo(HaveOccurred(), "error creating container registry: %s", err)
  2703  				defer deleteDockerRegistry(c, generatedNamespace.GetName())
  2704  
  2705  				By(`ensure registry pod is ready before attempting port-forwarding`)
  2706  				_ = awaitPod(GinkgoT(), c, generatedNamespace.GetName(), registryName, podReady)
  2707  
  2708  				err = registryPortForward(generatedNamespace.GetName())
  2709  				Expect(err).NotTo(HaveOccurred(), "port-forwarding local registry: %s", err)
  2710  				copyImage = func(dst, dstTag, src, srcTag string) error {
  2711  					if !strings.HasPrefix(src, "docker://") {
  2712  						src = fmt.Sprintf("docker://%s", src)
  2713  					}
  2714  					if !strings.HasPrefix(dst, "docker://") {
  2715  						dst = fmt.Sprintf("docker://%s", dst)
  2716  					}
  2717  					_, err := skopeoLocalCopy(dst, dstTag, src, srcTag)
  2718  					return err
  2719  				}
  2720  			} else {
  2721  				registryURL = fmt.Sprintf("%s/%s", openshiftregistryFQDN, generatedNamespace.GetName())
  2722  				registryAuthSecretName, err := getRegistryAuthSecretName(c, generatedNamespace.GetName())
  2723  				Expect(err).NotTo(HaveOccurred(), "error getting openshift registry authentication: %s", err)
  2724  				copyImage = func(dst, dstTag, src, srcTag string) error {
  2725  					if !strings.HasPrefix(src, "docker://") {
  2726  						src = fmt.Sprintf("docker://%s", src)
  2727  					}
  2728  					if !strings.HasPrefix(dst, "docker://") {
  2729  						dst = fmt.Sprintf("docker://%s", dst)
  2730  					}
  2731  					skopeoArgs := skopeoCopyCmd(dst, dstTag, src, srcTag, registryAuthSecretName)
  2732  					err = createSkopeoPod(c, skopeoArgs, generatedNamespace.GetName(), registryAuthSecretName)
  2733  					if err != nil {
  2734  						return fmt.Errorf("error creating skopeo pod: %v", err)
  2735  					}
  2736  
  2737  					By(`wait for skopeo pod to exit successfully`)
  2738  					awaitPod(GinkgoT(), c, generatedNamespace.GetName(), skopeo, func(pod *corev1.Pod) bool {
  2739  						ctx.Ctx().Logf("skopeo pod status: %s (waiting for: %s)", pod.Status.Phase, corev1.PodSucceeded)
  2740  						return pod.Status.Phase == corev1.PodSucceeded
  2741  					})
  2742  
  2743  					if err := deleteSkopeoPod(c, generatedNamespace.GetName()); err != nil {
  2744  						return fmt.Errorf("error deleting skopeo pod: %s", err)
  2745  					}
  2746  					return nil
  2747  				}
  2748  			}
  2749  
  2750  			By(`The remote image to be copied onto the local registry`)
  2751  			srcImage := "quay.io/olmtest/example-operator-bundle:"
  2752  			srcTag := "0.1.0"
  2753  
  2754  			By(`on-cluster image ref`)
  2755  			bundleImage := registryURL + "/unpack-retry-bundle:"
  2756  			bundleTag := genName("x")
  2757  
  2758  			unpackRetryCatalog := fmt.Sprintf(`
  2759  schema: olm.package
  2760  name: unpack-retry-package
  2761  defaultChannel: stable
  2762  ---
  2763  schema: olm.channel
  2764  package: unpack-retry-package
  2765  name: stable
  2766  entries:
  2767    - name: example-operator.v0.1.0
  2768  ---
  2769  schema: olm.bundle
  2770  name: example-operator.v0.1.0
  2771  package: unpack-retry-package
  2772  image: %s%s
  2773  properties:
  2774    - type: olm.package
  2775      value:
  2776        packageName: unpack-retry-package
  2777        version: 1.0.0
  2778  `, bundleImage, bundleTag)
  2779  
  2780  			By("creating a catalog referencing a non-existent bundle image")
  2781  			unpackRetryProvider, err := NewRawFileBasedCatalogProvider(unpackRetryCatalog)
  2782  			Expect(err).ToNot(HaveOccurred())
  2783  			catalogSourceName := fmt.Sprintf("%s-catsrc", generatedNamespace.GetName())
  2784  			magicCatalog := NewMagicCatalog(ctx.Ctx().Client(), generatedNamespace.GetName(), catalogSourceName, unpackRetryProvider)
  2785  			Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil())
  2786  
  2787  			By("patching the OperatorGroup to reduce the bundle unpacking timeout")
  2788  			ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
  2789  			addBundleUnpackTimeoutOGAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "1s")
  2790  
  2791  			By("creating a subscription for the missing bundle")
  2792  			unpackRetrySubName := fmt.Sprintf("%s-unpack-retry-package-sub", generatedNamespace.GetName())
  2793  			createSubscriptionForCatalog(crc, generatedNamespace.GetName(), unpackRetrySubName, catalogSourceName, "unpack-retry-package", stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
  2794  
  2795  			By("waiting for bundle unpack to fail")
  2796  			Eventually(
  2797  				func() error {
  2798  					fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), unpackRetrySubName, metav1.GetOptions{})
  2799  					if err != nil {
  2800  						return err
  2801  					}
  2802  					if cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpackFailed); cond.Status != corev1.ConditionTrue || cond.Reason != "BundleUnpackFailed" {
  2803  						return fmt.Errorf("%s condition not found", operatorsv1alpha1.SubscriptionBundleUnpackFailed)
  2804  					}
  2805  					return nil
  2806  				},
  2807  				5*time.Minute,
  2808  				interval,
  2809  			).Should(Succeed())
  2810  
  2811  			By("pushing missing bundle image")
  2812  			Expect(copyImage(bundleImage, bundleTag, srcImage, srcTag)).To(Succeed())
  2813  
  2814  			By("patching the OperatorGroup to increase the bundle unpacking timeout")
  2815  			addBundleUnpackTimeoutOGAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "") // revert to default unpack timeout
  2816  
  2817  			By("patching operator group to enable unpack retries")
  2818  			setBundleUnpackRetryMinimumIntervalAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "1s")
  2819  
  2820  			By("waiting until the subscription has an IP reference")
  2821  			subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), unpackRetrySubName, subscriptionHasInstallPlanChecker())
  2822  			Expect(err).Should(BeNil())
  2823  
  2824  			By("waiting for the v0.1.0 CSV to report a succeeded phase")
  2825  			_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
  2826  			Expect(err).ShouldNot(HaveOccurred())
  2827  
  2828  			By("checking if old unpack conditions on subscription are removed")
  2829  			Eventually(func() error {
  2830  				fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), unpackRetrySubName, metav1.GetOptions{})
  2831  				if err != nil {
  2832  					return err
  2833  				}
  2834  				if cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpacking); cond.Status != corev1.ConditionUnknown {
  2835  					return fmt.Errorf("subscription condition %s has unexpected value %s, expected %s", operatorsv1alpha1.SubscriptionBundleUnpacking, cond.Status, corev1.ConditionFalse)
  2836  				}
  2837  				if cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpackFailed); cond.Status != corev1.ConditionUnknown {
  2838  					return fmt.Errorf("unexpected condition %s on subscription", operatorsv1alpha1.SubscriptionBundleUnpackFailed)
  2839  				}
  2840  				return nil
  2841  			}).Should(Succeed())
  2842  		})
  2843  
  2844  		It("should not retry successful unpack jobs", func() {
  2845  			By("deploying the testing catalog")
  2846  			provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, subscriptionTestDataBaseDir, "example-operator.v0.1.0.yaml"))
  2847  			Expect(err).To(BeNil())
  2848  			catalogSourceName := fmt.Sprintf("%s-catsrc", generatedNamespace.GetName())
  2849  			magicCatalog := NewMagicCatalog(ctx.Ctx().Client(), generatedNamespace.GetName(), catalogSourceName, provider)
  2850  			Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil())
  2851  
  2852  			By("creating the testing subscription")
  2853  			subName := fmt.Sprintf("%s-test-package-sub", generatedNamespace.GetName())
  2854  			createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subName, catalogSourceName, "test-package", stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
  2855  
  2856  			By("waiting until the subscription has an IP reference")
  2857  			subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasInstallPlanChecker())
  2858  			Expect(err).Should(BeNil())
  2859  
  2860  			By("waiting for the v0.1.0 CSV to report a succeeded phase")
  2861  			_, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
  2862  			Expect(err).ShouldNot(HaveOccurred())
  2863  
  2864  			By("waiting for the subscription bundle unpack conditions to be scrubbed")
  2865  			// This step removes flakes from this test where the conditions on the subscription haven't been
  2866  			// updated by the time the Consistently block executed a couple of steps below to ensure that the unpack
  2867  			// job has not been retried
  2868  			Eventually(func() error {
  2869  				fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
  2870  				if err != nil {
  2871  					return err
  2872  				}
  2873  				if cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpacking); cond.Status == corev1.ConditionTrue {
  2874  					return fmt.Errorf("unexpected condition status for %s on subscription %s", operatorsv1alpha1.SubscriptionBundleUnpacking, subName)
  2875  				}
  2876  				if cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpackFailed); cond.Status == corev1.ConditionTrue {
  2877  					return fmt.Errorf("unexpected condition status for %s on subscription %s", operatorsv1alpha1.SubscriptionBundleUnpackFailed, subName)
  2878  				}
  2879  				return nil
  2880  			}).Should(Succeed())
  2881  
  2882  			By("patching operator group to enable unpack retries")
  2883  			ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
  2884  			setBundleUnpackRetryMinimumIntervalAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "1s")
  2885  
  2886  			By("Ensuring successful bundle unpack jobs are not retried")
  2887  			Consistently(func() error {
  2888  				fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
  2889  				if err != nil {
  2890  					return err
  2891  				}
  2892  				if cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpacking); cond.Status == corev1.ConditionTrue {
  2893  					return fmt.Errorf("unexpected condition status for %s on subscription %s", operatorsv1alpha1.SubscriptionBundleUnpacking, subName)
  2894  				}
  2895  				if cond := fetched.Status.GetCondition(operatorsv1alpha1.SubscriptionBundleUnpackFailed); cond.Status == corev1.ConditionTrue {
  2896  					return fmt.Errorf("unexpected condition status for %s on subscription %s", operatorsv1alpha1.SubscriptionBundleUnpackFailed, subName)
  2897  				}
  2898  				return nil
  2899  			}).Should(Succeed())
  2900  		})
  2901  	})
  2902  })
  2903  
  2904  const (
  2905  	catalogSourceName    = "mock-ocs"
  2906  	catalogConfigMapName = "mock-ocs"
  2907  	testSubscriptionName = "mysubscription"
  2908  	testPackageName      = "myapp"
  2909  
  2910  	stableChannel = "stable"
  2911  	betaChannel   = "beta"
  2912  	alphaChannel  = "alpha"
  2913  
  2914  	outdated = "myapp-outdated"
  2915  	stable   = "myapp-stable"
  2916  	alpha    = "myapp-alpha"
  2917  	beta     = "myapp-beta"
  2918  )
  2919  
  2920  var (
  2921  	dummyManifest = []registry.PackageManifest{{
  2922  		PackageName: testPackageName,
  2923  		Channels: []registry.PackageChannel{
  2924  			{Name: stableChannel, CurrentCSVName: stable},
  2925  			{Name: betaChannel, CurrentCSVName: beta},
  2926  			{Name: alphaChannel, CurrentCSVName: alpha},
  2927  		},
  2928  		DefaultChannelName: stableChannel,
  2929  	}}
  2930  	csvType = metav1.TypeMeta{
  2931  		Kind:       operatorsv1alpha1.ClusterServiceVersionKind,
  2932  		APIVersion: operatorsv1alpha1.GroupVersion,
  2933  	}
  2934  
  2935  	strategy = operatorsv1alpha1.StrategyDetailsDeployment{
  2936  		DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{
  2937  			{
  2938  				Name: genName("dep-"),
  2939  				Spec: newNginxDeployment(genName("nginx-")),
  2940  			},
  2941  		},
  2942  	}
  2943  	strategyRaw, _  = json.Marshal(strategy)
  2944  	installStrategy = operatorsv1alpha1.NamedInstallStrategy{
  2945  		StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment,
  2946  		StrategySpec: strategy,
  2947  	}
  2948  	outdatedCSV = operatorsv1alpha1.ClusterServiceVersion{
  2949  		TypeMeta: csvType,
  2950  		ObjectMeta: metav1.ObjectMeta{
  2951  			Name: outdated,
  2952  		},
  2953  		Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
  2954  			Replaces:       "",
  2955  			Version:        version.OperatorVersion{Version: semver.MustParse("0.1.0")},
  2956  			MinKubeVersion: "0.0.0",
  2957  			InstallModes: []operatorsv1alpha1.InstallMode{
  2958  				{
  2959  					Type:      operatorsv1alpha1.InstallModeTypeOwnNamespace,
  2960  					Supported: true,
  2961  				},
  2962  				{
  2963  					Type:      operatorsv1alpha1.InstallModeTypeSingleNamespace,
  2964  					Supported: true,
  2965  				},
  2966  				{
  2967  					Type:      operatorsv1alpha1.InstallModeTypeMultiNamespace,
  2968  					Supported: true,
  2969  				},
  2970  				{
  2971  					Type:      operatorsv1alpha1.InstallModeTypeAllNamespaces,
  2972  					Supported: true,
  2973  				},
  2974  			},
  2975  			InstallStrategy: installStrategy,
  2976  		},
  2977  	}
  2978  	stableCSV = operatorsv1alpha1.ClusterServiceVersion{
  2979  		TypeMeta: csvType,
  2980  		ObjectMeta: metav1.ObjectMeta{
  2981  			Name: stable,
  2982  		},
  2983  		Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
  2984  			Replaces:       outdated,
  2985  			Version:        version.OperatorVersion{Version: semver.MustParse("0.2.0")},
  2986  			MinKubeVersion: "0.0.0",
  2987  			InstallModes: []operatorsv1alpha1.InstallMode{
  2988  				{
  2989  					Type:      operatorsv1alpha1.InstallModeTypeOwnNamespace,
  2990  					Supported: true,
  2991  				},
  2992  				{
  2993  					Type:      operatorsv1alpha1.InstallModeTypeSingleNamespace,
  2994  					Supported: true,
  2995  				},
  2996  				{
  2997  					Type:      operatorsv1alpha1.InstallModeTypeMultiNamespace,
  2998  					Supported: true,
  2999  				},
  3000  				{
  3001  					Type:      operatorsv1alpha1.InstallModeTypeAllNamespaces,
  3002  					Supported: true,
  3003  				},
  3004  			},
  3005  			InstallStrategy: installStrategy,
  3006  		},
  3007  	}
  3008  	betaCSV = operatorsv1alpha1.ClusterServiceVersion{
  3009  		TypeMeta: csvType,
  3010  		ObjectMeta: metav1.ObjectMeta{
  3011  			Name: beta,
  3012  		},
  3013  		Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
  3014  			Replaces: stable,
  3015  			Version:  version.OperatorVersion{Version: semver.MustParse("0.1.1")},
  3016  			InstallModes: []operatorsv1alpha1.InstallMode{
  3017  				{
  3018  					Type:      operatorsv1alpha1.InstallModeTypeOwnNamespace,
  3019  					Supported: true,
  3020  				},
  3021  				{
  3022  					Type:      operatorsv1alpha1.InstallModeTypeSingleNamespace,
  3023  					Supported: true,
  3024  				},
  3025  				{
  3026  					Type:      operatorsv1alpha1.InstallModeTypeMultiNamespace,
  3027  					Supported: true,
  3028  				},
  3029  				{
  3030  					Type:      operatorsv1alpha1.InstallModeTypeAllNamespaces,
  3031  					Supported: true,
  3032  				},
  3033  			},
  3034  			InstallStrategy: installStrategy,
  3035  		},
  3036  	}
  3037  	alphaCSV = operatorsv1alpha1.ClusterServiceVersion{
  3038  		TypeMeta: csvType,
  3039  		ObjectMeta: metav1.ObjectMeta{
  3040  			Name: alpha,
  3041  		},
  3042  		Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
  3043  			Replaces: beta,
  3044  			Version:  version.OperatorVersion{Version: semver.MustParse("0.3.0")},
  3045  			InstallModes: []operatorsv1alpha1.InstallMode{
  3046  				{
  3047  					Type:      operatorsv1alpha1.InstallModeTypeOwnNamespace,
  3048  					Supported: true,
  3049  				},
  3050  				{
  3051  					Type:      operatorsv1alpha1.InstallModeTypeSingleNamespace,
  3052  					Supported: true,
  3053  				},
  3054  				{
  3055  					Type:      operatorsv1alpha1.InstallModeTypeMultiNamespace,
  3056  					Supported: true,
  3057  				},
  3058  				{
  3059  					Type:      operatorsv1alpha1.InstallModeTypeAllNamespaces,
  3060  					Supported: true,
  3061  				},
  3062  			},
  3063  			InstallStrategy: installStrategy,
  3064  		},
  3065  	}
  3066  	csvList = []operatorsv1alpha1.ClusterServiceVersion{outdatedCSV, stableCSV, betaCSV, alphaCSV}
  3067  
  3068  	strategyNew = operatorsv1alpha1.StrategyDetailsDeployment{
  3069  		DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{
  3070  			{
  3071  				Name: genName("dep-"),
  3072  				Spec: appsv1.DeploymentSpec{
  3073  					Selector: &metav1.LabelSelector{
  3074  						MatchLabels: map[string]string{"app": "nginx"},
  3075  					},
  3076  					Replicas: &singleInstance,
  3077  					Template: corev1.PodTemplateSpec{
  3078  						ObjectMeta: metav1.ObjectMeta{
  3079  							Labels: map[string]string{"app": "nginx"},
  3080  						},
  3081  						Spec: corev1.PodSpec{Containers: []corev1.Container{
  3082  							{
  3083  								Name:            genName("nginx"),
  3084  								Image:           *dummyImage,
  3085  								Ports:           []corev1.ContainerPort{{ContainerPort: 80}},
  3086  								ImagePullPolicy: corev1.PullIfNotPresent,
  3087  							},
  3088  						}},
  3089  					},
  3090  				},
  3091  			},
  3092  		},
  3093  	}
  3094  
  3095  	dummyCatalogConfigMap = &corev1.ConfigMap{
  3096  		ObjectMeta: metav1.ObjectMeta{
  3097  			Name: catalogConfigMapName,
  3098  		},
  3099  		Data: map[string]string{},
  3100  	}
  3101  
  3102  	dummyCatalogSource = operatorsv1alpha1.CatalogSource{
  3103  		TypeMeta: metav1.TypeMeta{
  3104  			Kind:       operatorsv1alpha1.CatalogSourceKind,
  3105  			APIVersion: operatorsv1alpha1.CatalogSourceCRDAPIVersion,
  3106  		},
  3107  		ObjectMeta: metav1.ObjectMeta{
  3108  			Name: catalogSourceName,
  3109  		},
  3110  		Spec: operatorsv1alpha1.CatalogSourceSpec{
  3111  			SourceType: "internal",
  3112  			ConfigMap:  catalogConfigMapName,
  3113  			GrpcPodConfig: &operatorsv1alpha1.GrpcPodConfig{
  3114  				SecurityContextConfig: operatorsv1alpha1.Restricted,
  3115  			},
  3116  		},
  3117  	}
  3118  )
  3119  
  3120  func init() {
  3121  	for i := 0; i < len(csvList); i++ {
  3122  		csvList[i].Spec.InstallStrategy.StrategySpec = strategyNew
  3123  	}
  3124  
  3125  	manifestsRaw, err := yaml.Marshal(dummyManifest)
  3126  	if err != nil {
  3127  		panic(err)
  3128  	}
  3129  	dummyCatalogConfigMap.Data[registry.ConfigMapPackageName] = string(manifestsRaw)
  3130  	csvsRaw, err := yaml.Marshal(csvList)
  3131  	if err != nil {
  3132  		panic(err)
  3133  	}
  3134  	dummyCatalogConfigMap.Data[registry.ConfigMapCSVName] = string(csvsRaw)
  3135  	dummyCatalogConfigMap.Data[registry.ConfigMapCRDName] = ""
  3136  }
  3137  
  3138  func initCatalog(t GinkgoTInterface, namespace string, c operatorclient.ClientInterface, crc versioned.Interface) error {
  3139  	dummyCatalogConfigMap.SetNamespace(namespace)
  3140  	if _, err := c.KubernetesInterface().CoreV1().ConfigMaps(namespace).Create(context.Background(), dummyCatalogConfigMap, metav1.CreateOptions{}); err != nil {
  3141  		if apierrors.IsAlreadyExists(err) {
  3142  			return fmt.Errorf("E2E bug detected: %v", err)
  3143  		}
  3144  		return err
  3145  	}
  3146  	t.Logf("created configmap %s/%s", dummyCatalogConfigMap.Namespace, dummyCatalogConfigMap.Name)
  3147  
  3148  	dummyCatalogSource.SetNamespace(namespace)
  3149  	if _, err := crc.OperatorsV1alpha1().CatalogSources(namespace).Create(context.Background(), &dummyCatalogSource, metav1.CreateOptions{}); err != nil {
  3150  		if apierrors.IsAlreadyExists(err) {
  3151  			return fmt.Errorf("E2E bug detected: %v", err)
  3152  		}
  3153  		return err
  3154  	}
  3155  	t.Logf("created catalog source %s/%s", dummyCatalogSource.Namespace, dummyCatalogSource.Name)
  3156  
  3157  	fetched, err := fetchCatalogSourceOnStatus(crc, dummyCatalogSource.GetName(), dummyCatalogSource.GetNamespace(), catalogSourceRegistryPodSynced())
  3158  	require.NoError(t, err)
  3159  	require.NotNil(t, fetched)
  3160  
  3161  	return nil
  3162  }
  3163  
  3164  type subscriptionStateChecker func(subscription *operatorsv1alpha1.Subscription) bool
  3165  
  3166  func subscriptionStateUpgradeAvailableChecker() func(subscription *operatorsv1alpha1.Subscription) bool {
  3167  	var lastState operatorsv1alpha1.SubscriptionState
  3168  	lastTime := time.Now()
  3169  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3170  		if subscription.Status.State != lastState {
  3171  			ctx.Ctx().Logf("waiting %s for subscription %s/%s to have state %s: has state %s", time.Since(lastTime), subscription.Namespace, subscription.Name, operatorsv1alpha1.SubscriptionStateUpgradeAvailable, subscription.Status.State)
  3172  			lastState = subscription.Status.State
  3173  			lastTime = time.Now()
  3174  		}
  3175  		return subscription.Status.State == operatorsv1alpha1.SubscriptionStateUpgradeAvailable
  3176  	}
  3177  }
  3178  
  3179  func subscriptionStateUpgradePendingChecker() func(subscription *operatorsv1alpha1.Subscription) bool {
  3180  	var lastState operatorsv1alpha1.SubscriptionState
  3181  	lastTime := time.Now()
  3182  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3183  		if subscription.Status.State != lastState {
  3184  			ctx.Ctx().Logf("waiting %s for subscription %s/%s to have state %s: has state %s", time.Since(lastTime), subscription.Namespace, subscription.Name, operatorsv1alpha1.SubscriptionStateUpgradePending, subscription.Status.State)
  3185  			lastState = subscription.Status.State
  3186  			lastTime = time.Now()
  3187  		}
  3188  		return subscription.Status.State == operatorsv1alpha1.SubscriptionStateUpgradePending
  3189  	}
  3190  }
  3191  
  3192  func subscriptionStateAtLatestChecker() func(subscription *operatorsv1alpha1.Subscription) bool {
  3193  	var lastState operatorsv1alpha1.SubscriptionState
  3194  	lastTime := time.Now()
  3195  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3196  		if subscription.Status.State != lastState {
  3197  			ctx.Ctx().Logf("waiting %s for subscription %s/%s to have state %s: has state %s", time.Since(lastTime), subscription.Namespace, subscription.Name, operatorsv1alpha1.SubscriptionStateAtLatest, subscription.Status.State)
  3198  			lastState = subscription.Status.State
  3199  			lastTime = time.Now()
  3200  		}
  3201  		return subscription.Status.State == operatorsv1alpha1.SubscriptionStateAtLatest
  3202  	}
  3203  }
  3204  
  3205  func subscriptionHasInstallPlanChecker() func(subscription *operatorsv1alpha1.Subscription) bool {
  3206  	var lastState operatorsv1alpha1.SubscriptionState
  3207  	lastTime := time.Now()
  3208  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3209  		if subscription.Status.State != lastState {
  3210  			ctx.Ctx().Logf("waiting %s for subscription %s/%s to have installplan ref: has ref %#v", time.Since(lastTime), subscription.Namespace, subscription.Name, subscription.Status.InstallPlanRef)
  3211  			lastState = subscription.Status.State
  3212  			lastTime = time.Now()
  3213  		}
  3214  		return subscription.Status.InstallPlanRef != nil
  3215  	}
  3216  }
  3217  
  3218  func subscriptionHasInstallPlanDifferentChecker(currentInstallPlanName string) subscriptionStateChecker {
  3219  	checker := subscriptionHasInstallPlanChecker()
  3220  	var lastState operatorsv1alpha1.SubscriptionState
  3221  	lastTime := time.Now()
  3222  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3223  		if subscription.Status.State != lastState {
  3224  			ctx.Ctx().Logf("waiting %s for subscription %s/%s to have installplan different from %s: has ref %#v", time.Since(lastTime), subscription.Namespace, subscription.Name, currentInstallPlanName, subscription.Status.InstallPlanRef)
  3225  			lastState = subscription.Status.State
  3226  			lastTime = time.Now()
  3227  		}
  3228  		return checker(subscription) && subscription.Status.InstallPlanRef.Name != currentInstallPlanName
  3229  	}
  3230  }
  3231  
  3232  func subscriptionHasCurrentCSV(currentCSV string) subscriptionStateChecker {
  3233  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3234  		return subscription.Status.CurrentCSV == currentCSV
  3235  	}
  3236  }
  3237  
  3238  func subscriptionHasCondition(condType operatorsv1alpha1.SubscriptionConditionType, status corev1.ConditionStatus, reason, message string) subscriptionStateChecker {
  3239  	var lastCond operatorsv1alpha1.SubscriptionCondition
  3240  	lastTime := time.Now()
  3241  	// if status/reason/message meet expectations, then subscription state is considered met/true
  3242  	// IFF this is the result of a recent change of status/reason/message
  3243  	// else, cache the current status/reason/message for next loop/comparison
  3244  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3245  		cond := subscription.Status.GetCondition(condType)
  3246  		if cond.Status == status && cond.Reason == reason && cond.Message == message {
  3247  			if lastCond.Status != cond.Status && lastCond.Reason != cond.Reason && lastCond.Message == cond.Message {
  3248  				GinkgoT().Logf("waited %s subscription condition met %v\n", time.Since(lastTime), cond)
  3249  				lastTime = time.Now()
  3250  				lastCond = cond
  3251  			}
  3252  			return true
  3253  		}
  3254  
  3255  		if lastCond.Status != cond.Status && lastCond.Reason != cond.Reason && lastCond.Message == cond.Message {
  3256  			GinkgoT().Logf("waited %s subscription condition not met: %v\n", time.Since(lastTime), cond)
  3257  			lastTime = time.Now()
  3258  			lastCond = cond
  3259  		}
  3260  		return false
  3261  	}
  3262  }
  3263  
  3264  func subscriptionDoesNotHaveCondition(condType operatorsv1alpha1.SubscriptionConditionType) subscriptionStateChecker {
  3265  	var lastStatus corev1.ConditionStatus
  3266  	lastTime := time.Now()
  3267  	// if status meets expectations, then subscription state is considered met/true
  3268  	// IFF this is the result of a recent change of status
  3269  	// else, cache the current status for next loop/comparison
  3270  	return func(subscription *operatorsv1alpha1.Subscription) bool {
  3271  		cond := subscription.Status.GetCondition(condType)
  3272  		if cond.Status == corev1.ConditionUnknown {
  3273  			if cond.Status != lastStatus {
  3274  				GinkgoT().Logf("waited %s subscription condition not found\n", time.Since(lastTime))
  3275  				lastStatus = cond.Status
  3276  				lastTime = time.Now()
  3277  			}
  3278  			return true
  3279  		}
  3280  
  3281  		if cond.Status != lastStatus {
  3282  			GinkgoT().Logf("waited %s subscription condition found: %v\n", time.Since(lastTime), cond)
  3283  			lastStatus = cond.Status
  3284  			lastTime = time.Now()
  3285  		}
  3286  		return false
  3287  	}
  3288  }
  3289  
  3290  func fetchSubscription(crc versioned.Interface, namespace, name string, checker subscriptionStateChecker) (*operatorsv1alpha1.Subscription, error) {
  3291  	var fetchedSubscription *operatorsv1alpha1.Subscription
  3292  
  3293  	log := func(s string) {
  3294  		ctx.Ctx().Logf("%s: %s", time.Now().Format("15:04:05.9999"), s)
  3295  	}
  3296  
  3297  	var lastState operatorsv1alpha1.SubscriptionState
  3298  	var lastCSV string
  3299  	var lastInstallPlanRef *corev1.ObjectReference
  3300  
  3301  	err := wait.Poll(pollInterval, pollDuration, func() (bool, error) {
  3302  		var err error
  3303  		fetchedSubscription, err = crc.OperatorsV1alpha1().Subscriptions(namespace).Get(context.Background(), name, metav1.GetOptions{})
  3304  		if err != nil || fetchedSubscription == nil {
  3305  			log(fmt.Sprintf("error getting subscription %s/%s: %v", namespace, name, err))
  3306  			return false, nil
  3307  		}
  3308  		thisState, thisCSV, thisInstallPlanRef := fetchedSubscription.Status.State, fetchedSubscription.Status.CurrentCSV, fetchedSubscription.Status.InstallPlanRef
  3309  		if thisState != lastState || thisCSV != lastCSV || !equality.Semantic.DeepEqual(thisInstallPlanRef, lastInstallPlanRef) {
  3310  			lastState, lastCSV, lastInstallPlanRef = thisState, thisCSV, thisInstallPlanRef
  3311  			log(fmt.Sprintf("subscription %s/%s state: %s (csv %s): installPlanRef: %#v", namespace, name, thisState, thisCSV, thisInstallPlanRef))
  3312  			log(fmt.Sprintf("subscription %s/%s state: %s (csv %s): status: %#v", namespace, name, thisState, thisCSV, fetchedSubscription.Status))
  3313  		}
  3314  		return checker(fetchedSubscription), nil
  3315  	})
  3316  	if err != nil {
  3317  		log(fmt.Sprintf("subscription %s/%s never got correct status: %#v", namespace, name, fetchedSubscription.Status))
  3318  		log(fmt.Sprintf("subscription %s/%s spec: %#v", namespace, name, fetchedSubscription.Spec))
  3319  		return nil, err
  3320  	}
  3321  	return fetchedSubscription, nil
  3322  }
  3323  
  3324  func buildSubscriptionCleanupFunc(crc versioned.Interface, subscription *operatorsv1alpha1.Subscription) cleanupFunc {
  3325  	return func() {
  3326  		if env := os.Getenv("SKIP_CLEANUP"); env != "" {
  3327  			fmt.Printf("Skipping cleanup of install plan for subscription %s/%s...\n", subscription.GetNamespace(), subscription.GetName())
  3328  			return
  3329  		}
  3330  
  3331  		if installPlanRef := subscription.Status.InstallPlanRef; installPlanRef != nil {
  3332  			installPlan, err := crc.OperatorsV1alpha1().InstallPlans(subscription.GetNamespace()).Get(context.Background(), installPlanRef.Name, metav1.GetOptions{})
  3333  			if err == nil {
  3334  				buildInstallPlanCleanupFunc(crc, subscription.GetNamespace(), installPlan)()
  3335  			} else {
  3336  				ctx.Ctx().Logf("Could not get installplan %s while building subscription %s's cleanup function", installPlan.GetName(), subscription.GetName())
  3337  			}
  3338  		}
  3339  
  3340  		err := crc.OperatorsV1alpha1().Subscriptions(subscription.GetNamespace()).Delete(context.Background(), subscription.GetName(), metav1.DeleteOptions{})
  3341  		Expect(err).NotTo(HaveOccurred())
  3342  	}
  3343  }
  3344  
  3345  func createSubscription(t GinkgoTInterface, crc versioned.Interface, namespace, name, packageName, channel string, approval operatorsv1alpha1.Approval) (cleanupFunc, *operatorsv1alpha1.Subscription) {
  3346  	subscription := &operatorsv1alpha1.Subscription{
  3347  		TypeMeta: metav1.TypeMeta{
  3348  			Kind:       operatorsv1alpha1.SubscriptionKind,
  3349  			APIVersion: operatorsv1alpha1.SubscriptionCRDAPIVersion,
  3350  		},
  3351  		ObjectMeta: metav1.ObjectMeta{
  3352  			Namespace: namespace,
  3353  			Name:      name,
  3354  		},
  3355  		Spec: &operatorsv1alpha1.SubscriptionSpec{
  3356  			CatalogSource:          catalogSourceName,
  3357  			CatalogSourceNamespace: namespace,
  3358  			Package:                packageName,
  3359  			Channel:                channel,
  3360  			InstallPlanApproval:    approval,
  3361  		},
  3362  	}
  3363  
  3364  	subscription, err := crc.OperatorsV1alpha1().Subscriptions(namespace).Create(context.Background(), subscription, metav1.CreateOptions{})
  3365  	Expect(err).ToNot(HaveOccurred())
  3366  	t.Logf("created subscription %s/%s", subscription.Namespace, subscription.Name)
  3367  	return buildSubscriptionCleanupFunc(crc, subscription), subscription
  3368  }
  3369  
  3370  func createSubscriptionForCatalog(crc versioned.Interface, namespace, name, catalog, packageName, channel, startingCSV string, approval operatorsv1alpha1.Approval) cleanupFunc {
  3371  	subscription := &operatorsv1alpha1.Subscription{
  3372  		TypeMeta: metav1.TypeMeta{
  3373  			Kind:       operatorsv1alpha1.SubscriptionKind,
  3374  			APIVersion: operatorsv1alpha1.SubscriptionCRDAPIVersion,
  3375  		},
  3376  		ObjectMeta: metav1.ObjectMeta{
  3377  			Namespace: namespace,
  3378  			Name:      name,
  3379  		},
  3380  		Spec: &operatorsv1alpha1.SubscriptionSpec{
  3381  			CatalogSource:          catalog,
  3382  			CatalogSourceNamespace: namespace,
  3383  			Package:                packageName,
  3384  			Channel:                channel,
  3385  			StartingCSV:            startingCSV,
  3386  			InstallPlanApproval:    approval,
  3387  		},
  3388  	}
  3389  
  3390  	subscription, err := crc.OperatorsV1alpha1().Subscriptions(namespace).Create(context.Background(), subscription, metav1.CreateOptions{})
  3391  	Expect(err).NotTo(HaveOccurred())
  3392  	return buildSubscriptionCleanupFunc(crc, subscription)
  3393  }
  3394  
  3395  func createSubscriptionForCatalogWithSpec(t GinkgoTInterface, crc versioned.Interface, namespace, name string, spec *operatorsv1alpha1.SubscriptionSpec) cleanupFunc {
  3396  	subscription := &operatorsv1alpha1.Subscription{
  3397  		TypeMeta: metav1.TypeMeta{
  3398  			Kind:       operatorsv1alpha1.SubscriptionKind,
  3399  			APIVersion: operatorsv1alpha1.SubscriptionCRDAPIVersion,
  3400  		},
  3401  		ObjectMeta: metav1.ObjectMeta{
  3402  			Namespace: namespace,
  3403  			Name:      name,
  3404  		},
  3405  		Spec: spec,
  3406  	}
  3407  
  3408  	subscription, err := crc.OperatorsV1alpha1().Subscriptions(namespace).Create(context.Background(), subscription, metav1.CreateOptions{})
  3409  	require.NoError(t, err)
  3410  	return buildSubscriptionCleanupFunc(crc, subscription)
  3411  }
  3412  
  3413  func waitForSubscriptionToDelete(namespace, name string, c versioned.Interface) error {
  3414  	var lastState operatorsv1alpha1.SubscriptionState
  3415  	var lastReason operatorsv1alpha1.ConditionReason
  3416  	lastTime := time.Now()
  3417  
  3418  	ctx.Ctx().Logf("waiting for subscription %s/%s to delete", namespace, name)
  3419  	err := wait.Poll(pollInterval, pollDuration, func() (bool, error) {
  3420  		sub, err := c.OperatorsV1alpha1().Subscriptions(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3421  		if apierrors.IsNotFound(err) {
  3422  			ctx.Ctx().Logf("subscription %s/%s deleted", namespace, name)
  3423  			return true, nil
  3424  		}
  3425  		if err != nil {
  3426  			ctx.Ctx().Logf("error getting subscription %s/%s: %v", namespace, name, err)
  3427  		}
  3428  		if sub != nil {
  3429  			state, reason := sub.Status.State, sub.Status.Reason
  3430  			if state != lastState || reason != lastReason {
  3431  				ctx.Ctx().Logf("waited %s for subscription %s/%s status: %s (%s)", time.Since(lastTime), namespace, name, state, reason)
  3432  				lastState, lastReason = state, reason
  3433  				lastTime = time.Now()
  3434  			}
  3435  		}
  3436  		return false, nil
  3437  	})
  3438  
  3439  	return err
  3440  }
  3441  
  3442  func checkDeploymentHasPodConfigNodeSelector(t GinkgoTInterface, client operatorclient.ClientInterface, csv *operatorsv1alpha1.ClusterServiceVersion, nodeSelector map[string]string) error {
  3443  
  3444  	resolver := install.StrategyResolver{}
  3445  
  3446  	strategy, err := resolver.UnmarshalStrategy(csv.Spec.InstallStrategy)
  3447  	if err != nil {
  3448  		return err
  3449  	}
  3450  
  3451  	strategyDetailsDeployment, ok := strategy.(*operatorsv1alpha1.StrategyDetailsDeployment)
  3452  	require.Truef(t, ok, "could not cast install strategy as type %T", strategyDetailsDeployment)
  3453  
  3454  	for _, deploymentSpec := range strategyDetailsDeployment.DeploymentSpecs {
  3455  		deployment, err := client.KubernetesInterface().AppsV1().Deployments(csv.GetNamespace()).Get(context.Background(), deploymentSpec.Name, metav1.GetOptions{})
  3456  		if err != nil {
  3457  			return err
  3458  		}
  3459  
  3460  		isEqual := reflect.DeepEqual(nodeSelector, deployment.Spec.Template.Spec.NodeSelector)
  3461  		if !isEqual {
  3462  			err = fmt.Errorf("actual nodeSelector=%v does not match expected nodeSelector=%v", deploymentSpec.Spec.Template.Spec.NodeSelector, nodeSelector)
  3463  		}
  3464  	}
  3465  	return nil
  3466  }
  3467  
  3468  func checkDeploymentWithPodConfiguration(client operatorclient.ClientInterface, csv *operatorsv1alpha1.ClusterServiceVersion, envVar []corev1.EnvVar, volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, tolerations []corev1.Toleration, resources *corev1.ResourceRequirements) error {
  3469  	resolver := install.StrategyResolver{}
  3470  
  3471  	strategy, err := resolver.UnmarshalStrategy(csv.Spec.InstallStrategy)
  3472  	Expect(err).NotTo(HaveOccurred())
  3473  
  3474  	strategyDetailsDeployment, ok := strategy.(*operatorsv1alpha1.StrategyDetailsDeployment)
  3475  	Expect(ok).To(BeTrue(), "could not cast install strategy as type %T", strategyDetailsDeployment)
  3476  
  3477  	findEnvVar := func(envVar []corev1.EnvVar, name string) (foundEnvVar *corev1.EnvVar, found bool) {
  3478  		for i := range envVar {
  3479  			if name == envVar[i].Name {
  3480  				found = true
  3481  				foundEnvVar = &envVar[i]
  3482  
  3483  				break
  3484  			}
  3485  		}
  3486  
  3487  		return
  3488  	}
  3489  
  3490  	findVolumeMount := func(volumeMounts []corev1.VolumeMount, name string) (foundVolumeMount *corev1.VolumeMount, found bool) {
  3491  		for i := range volumeMounts {
  3492  			if name == volumeMounts[i].Name {
  3493  				found = true
  3494  				foundVolumeMount = &volumeMounts[i]
  3495  
  3496  				break
  3497  			}
  3498  		}
  3499  
  3500  		return
  3501  	}
  3502  
  3503  	findVolume := func(volumes []corev1.Volume, name string) (foundVolume *corev1.Volume, found bool) {
  3504  		for i := range volumes {
  3505  			if name == volumes[i].Name {
  3506  				found = true
  3507  				foundVolume = &volumes[i]
  3508  
  3509  				break
  3510  			}
  3511  		}
  3512  
  3513  		return
  3514  	}
  3515  
  3516  	findTolerations := func(tolerations []corev1.Toleration, toleration corev1.Toleration) (foundToleration *corev1.Toleration, found bool) {
  3517  		for i := range tolerations {
  3518  			if reflect.DeepEqual(toleration, tolerations[i]) {
  3519  				found = true
  3520  				foundToleration = &toleration
  3521  
  3522  				break
  3523  			}
  3524  		}
  3525  
  3526  		return
  3527  	}
  3528  
  3529  	findResources := func(existingResource *corev1.ResourceRequirements, podResource *corev1.ResourceRequirements) (foundResource *corev1.ResourceRequirements, found bool) {
  3530  		if reflect.DeepEqual(existingResource, podResource) {
  3531  			found = true
  3532  			foundResource = podResource
  3533  		}
  3534  
  3535  		return
  3536  	}
  3537  
  3538  	check := func(container *corev1.Container) error {
  3539  		for _, e := range envVar {
  3540  			existing, found := findEnvVar(container.Env, e.Name)
  3541  			if !found || existing == nil {
  3542  				return fmt.Errorf("env variable name=%s not injected", e.Name)
  3543  			}
  3544  			Expect(e.Value).Should(Equal(existing.Value), "env variable value does not match %s=%s", e.Name, e.Value)
  3545  		}
  3546  
  3547  		for _, v := range volumeMounts {
  3548  			existing, found := findVolumeMount(container.VolumeMounts, v.Name)
  3549  			if !found || existing == nil {
  3550  				return fmt.Errorf("VolumeMount name=%s not injected", v.Name)
  3551  			}
  3552  			Expect(v.MountPath).Should(Equal(existing.MountPath), "VolumeMount MountPath does not match %s=%s", v.Name, v.MountPath)
  3553  		}
  3554  
  3555  		existing, found := findResources(&container.Resources, resources)
  3556  		if !found || existing == nil {
  3557  			return fmt.Errorf("Resources not injected. Resource=%v", resources)
  3558  		}
  3559  		Expect(existing).Should(Equal(resources), "Resource=%v does not match expected Resource=%v", existing, resources)
  3560  		return nil
  3561  	}
  3562  
  3563  	for _, deploymentSpec := range strategyDetailsDeployment.DeploymentSpecs {
  3564  		deployment, err := client.KubernetesInterface().AppsV1().Deployments(csv.GetNamespace()).Get(context.Background(), deploymentSpec.Name, metav1.GetOptions{})
  3565  		Expect(err).NotTo(HaveOccurred())
  3566  		for _, v := range volumes {
  3567  			existing, found := findVolume(deployment.Spec.Template.Spec.Volumes, v.Name)
  3568  			if !found || existing == nil {
  3569  				return fmt.Errorf("Volume name=%s not injected", v.Name)
  3570  			}
  3571  			Expect(v.ConfigMap.LocalObjectReference.Name).Should(Equal(existing.ConfigMap.LocalObjectReference.Name), "volume ConfigMap Names does not match %s=%s", v.Name, v.ConfigMap.LocalObjectReference.Name)
  3572  		}
  3573  
  3574  		for _, toleration := range tolerations {
  3575  			existing, found := findTolerations(deployment.Spec.Template.Spec.Tolerations, toleration)
  3576  			if !found || existing == nil {
  3577  				return fmt.Errorf("Toleration not injected. Toleration=%v", toleration)
  3578  			}
  3579  			Expect(*existing).Should(Equal(toleration), "Toleration=%v does not match expected Toleration=%v", existing, toleration)
  3580  		}
  3581  
  3582  		for i := range deployment.Spec.Template.Spec.Containers {
  3583  			err = check(&deployment.Spec.Template.Spec.Containers[i])
  3584  			if err != nil {
  3585  				return err
  3586  			}
  3587  		}
  3588  	}
  3589  	return nil
  3590  }
  3591  
  3592  func updateInternalCatalog(t GinkgoTInterface, c operatorclient.ClientInterface, crc versioned.Interface, catalogSourceName, namespace string, crds []apiextensionsv1.CustomResourceDefinition, csvs []operatorsv1alpha1.ClusterServiceVersion, packages []registry.PackageManifest) {
  3593  	fetchedInitialCatalog, err := fetchCatalogSourceOnStatus(crc, catalogSourceName, namespace, catalogSourceRegistryPodSynced())
  3594  	require.NoError(t, err)
  3595  
  3596  	// Get initial configmap
  3597  	configMap, err := c.KubernetesInterface().CoreV1().ConfigMaps(namespace).Get(context.Background(), fetchedInitialCatalog.Spec.ConfigMap, metav1.GetOptions{})
  3598  	require.NoError(t, err)
  3599  
  3600  	// Update package to point to new csv
  3601  	manifestsRaw, err := yaml.Marshal(packages)
  3602  	require.NoError(t, err)
  3603  	configMap.Data[registry.ConfigMapPackageName] = string(manifestsRaw)
  3604  
  3605  	// Update raw CRDs
  3606  	var crdsRaw []byte
  3607  	crdStrings := []string{}
  3608  	for _, crd := range crds {
  3609  		crdStrings = append(crdStrings, serializeCRD(crd))
  3610  	}
  3611  	crdsRaw, err = yaml.Marshal(crdStrings)
  3612  	require.NoError(t, err)
  3613  	configMap.Data[registry.ConfigMapCRDName] = strings.Replace(string(crdsRaw), "- |\n  ", "- ", -1)
  3614  
  3615  	// Update raw CSVs
  3616  	csvsRaw, err := yaml.Marshal(csvs)
  3617  	require.NoError(t, err)
  3618  	configMap.Data[registry.ConfigMapCSVName] = string(csvsRaw)
  3619  
  3620  	// Update configmap
  3621  	_, err = c.KubernetesInterface().CoreV1().ConfigMaps(namespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
  3622  	require.NoError(t, err)
  3623  
  3624  	// wait for catalog to update
  3625  	var lastState string
  3626  	lastTime := time.Now()
  3627  	_, err = fetchCatalogSourceOnStatus(crc, catalogSourceName, namespace, func(catalog *operatorsv1alpha1.CatalogSource) bool {
  3628  		before := fetchedInitialCatalog.Status.ConfigMapResource
  3629  		after := catalog.Status.ConfigMapResource
  3630  		if after != nil && after.LastUpdateTime.After(before.LastUpdateTime.Time) && after.ResourceVersion != before.ResourceVersion &&
  3631  			catalog.Status.GRPCConnectionState.LastConnectTime.After(after.LastUpdateTime.Time) && catalog.Status.GRPCConnectionState.LastObservedState == "READY" {
  3632  			fmt.Println("catalog updated")
  3633  			return true
  3634  		}
  3635  		if catalog.Status.GRPCConnectionState.LastObservedState != lastState {
  3636  			fmt.Printf("waited %s for catalog pod %v to be available (after catalog update) - %s\n", time.Since(lastTime), catalog.GetName(), catalog.Status.GRPCConnectionState.LastObservedState)
  3637  			lastState = catalog.Status.GRPCConnectionState.LastObservedState
  3638  			lastTime = time.Now()
  3639  		}
  3640  		return false
  3641  	})
  3642  	require.NoError(t, err)
  3643  }
  3644  
  3645  func subscriptionCurrentCSVGetter(crclient versioned.Interface, namespace, subName string) func() string {
  3646  	return func() string {
  3647  		subscription, err := crclient.OperatorsV1alpha1().Subscriptions(namespace).Get(context.Background(), subName, metav1.GetOptions{})
  3648  		if err != nil || subscription == nil {
  3649  			return ""
  3650  		}
  3651  		return subscription.Status.CurrentCSV
  3652  	}
  3653  }