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

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/blang/semver/v4"
     7  	. "github.com/onsi/ginkgo/v2"
     8  	. "github.com/onsi/gomega"
     9  	"github.com/stretchr/testify/require"
    10  	corev1 "k8s.io/api/core/v1"
    11  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    12  
    13  	"k8s.io/apimachinery/pkg/api/meta"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  
    16  	operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
    17  	operatorsv2 "github.com/operator-framework/api/pkg/operators/v2"
    18  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
    19  	"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
    20  )
    21  
    22  var _ = Describe("Operator Condition", func() {
    23  	var (
    24  		generatedNamespace corev1.Namespace
    25  	)
    26  
    27  	BeforeEach(func() {
    28  		generatedNamespace = SetupGeneratedTestNamespace(genName("operator-conditions-e2e-"))
    29  	})
    30  
    31  	AfterEach(func() {
    32  		TeardownNamespace(generatedNamespace.GetName())
    33  	})
    34  
    35  	It("[FLAKE] OperatorCondition Upgradeable type and overrides", func() {
    36  		By("This test proves that an operator can upgrade successfully when" +
    37  			" Upgrade condition type is set in OperatorCondition spec. Plus, an operator" +
    38  			" chooses not to use OperatorCondition, the upgrade process will proceed as" +
    39  			" expected. The overrides spec in OperatorCondition can be used to override" +
    40  			" the conditions spec. The overrides spec will remain in place until" +
    41  			" they are unset.")
    42  		c := ctx.Ctx().KubeClient()
    43  		crc := ctx.Ctx().OperatorClient()
    44  
    45  		By(`Create a catalog for csvA, csvB, and csvD`)
    46  		pkgA := genName("a-")
    47  		pkgB := genName("b-")
    48  		pkgD := genName("d-")
    49  		pkgAStable := pkgA + "-stable"
    50  		pkgBStable := pkgB + "-stable"
    51  		pkgDStable := pkgD + "-stable"
    52  		stableChannel := "stable"
    53  		strategyA := newNginxInstallStrategy(pkgAStable, nil, nil)
    54  		strategyB := newNginxInstallStrategy(pkgBStable, nil, nil)
    55  		strategyD := newNginxInstallStrategy(pkgDStable, nil, nil)
    56  		crd := newCRD(genName(pkgA))
    57  		csvA := newCSV(pkgAStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, &strategyA)
    58  		csvB := newCSV(pkgBStable, generatedNamespace.GetName(), pkgAStable, semver.MustParse("0.2.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, &strategyB)
    59  		csvD := newCSV(pkgDStable, generatedNamespace.GetName(), pkgBStable, semver.MustParse("0.3.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, &strategyD)
    60  
    61  		By(`Create the initial catalogsources`)
    62  		manifests := []registry.PackageManifest{
    63  			{
    64  				PackageName: pkgA,
    65  				Channels: []registry.PackageChannel{
    66  					{Name: stableChannel, CurrentCSVName: pkgAStable},
    67  				},
    68  				DefaultChannelName: stableChannel,
    69  			},
    70  		}
    71  
    72  		catalog := genName("catalog-")
    73  		_, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalog, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csvA})
    74  		defer cleanupCatalogSource()
    75  		_, err := fetchCatalogSourceOnStatus(crc, catalog, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
    76  		subName := genName("sub-")
    77  		cleanupSub := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subName, catalog, pkgA, stableChannel, pkgAStable, operatorsv1alpha1.ApprovalAutomatic)
    78  		defer cleanupSub()
    79  
    80  		By(`Await csvA's success`)
    81  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvA.GetName(), csvSucceededChecker)
    82  		require.NoError(GinkgoT(), err)
    83  
    84  		By(`Get the OperatorCondition for csvA and report that it is not upgradeable`)
    85  		var cond *operatorsv2.OperatorCondition
    86  		upgradeableFalseCondition := metav1.Condition{
    87  			Type:               operatorsv2.Upgradeable,
    88  			Status:             metav1.ConditionFalse,
    89  			Reason:             "test",
    90  			Message:            "test",
    91  			LastTransitionTime: metav1.Now(),
    92  		}
    93  
    94  		var currentGen int64
    95  		Eventually(func() error {
    96  			cond, err := crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).Get(context.TODO(), csvA.GetName(), metav1.GetOptions{})
    97  			if err != nil {
    98  				return err
    99  			}
   100  			currentGen = cond.ObjectMeta.GetGeneration()
   101  			upgradeableFalseCondition.ObservedGeneration = currentGen
   102  			meta.SetStatusCondition(&cond.Spec.Conditions, upgradeableFalseCondition)
   103  			_, err = crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).Update(context.TODO(), cond, metav1.UpdateOptions{})
   104  			return err
   105  		}, pollInterval, pollDuration).Should(Succeed())
   106  
   107  		By(`Update the catalogsources`)
   108  		manifests = []registry.PackageManifest{
   109  			{
   110  				PackageName: pkgA,
   111  				Channels: []registry.PackageChannel{
   112  					{Name: stableChannel, CurrentCSVName: pkgBStable},
   113  				},
   114  				DefaultChannelName: stableChannel,
   115  			},
   116  		}
   117  		updateInternalCatalog(GinkgoT(), c, crc, catalog, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB}, manifests)
   118  
   119  		By(`Attempt to get the catalog source before creating install plan(s)`)
   120  		_, err = fetchCatalogSourceOnStatus(crc, catalog, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   121  		require.NoError(GinkgoT(), err)
   122  
   123  		By(`csvB will be in Pending phase due to csvA reports Upgradeable=False condition`)
   124  		fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvB.GetName(), buildCSVReasonChecker(operatorsv1alpha1.CSVReasonOperatorConditionNotUpgradeable))
   125  		require.NoError(GinkgoT(), err)
   126  		require.Equal(GinkgoT(), fetchedCSV.Status.Phase, operatorsv1alpha1.CSVPhasePending)
   127  
   128  		By(`Get the OperatorCondition for csvA and report that it is upgradeable, unblocking csvB`)
   129  		upgradeableTrueCondition := metav1.Condition{
   130  			Type:               operatorsv2.Upgradeable,
   131  			Status:             metav1.ConditionTrue,
   132  			Reason:             "test",
   133  			Message:            "test",
   134  			LastTransitionTime: metav1.Now(),
   135  		}
   136  		Eventually(func() error {
   137  			cond, err = crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).Get(context.TODO(), csvA.GetName(), metav1.GetOptions{})
   138  			if err != nil || currentGen == cond.ObjectMeta.GetGeneration() {
   139  				return err
   140  			}
   141  			currentGen = cond.ObjectMeta.GetGeneration()
   142  			upgradeableTrueCondition.ObservedGeneration = cond.ObjectMeta.GetGeneration()
   143  			meta.SetStatusCondition(&cond.Spec.Conditions, upgradeableTrueCondition)
   144  			_, err = crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).Update(context.TODO(), cond, metav1.UpdateOptions{})
   145  			return err
   146  		}, pollInterval, pollDuration).Should(Succeed())
   147  
   148  		By(`Await csvB's success`)
   149  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvB.GetName(), csvSucceededChecker)
   150  		require.NoError(GinkgoT(), err)
   151  
   152  		By(`Get the OperatorCondition for csvB and purposedly change ObservedGeneration`)
   153  		By(`to cause mismatch generation situation`)
   154  		Eventually(func() error {
   155  			cond, err = crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).Get(context.TODO(), csvB.GetName(), metav1.GetOptions{})
   156  			if err != nil || currentGen == cond.ObjectMeta.GetGeneration() {
   157  				return err
   158  			}
   159  			currentGen = cond.ObjectMeta.GetGeneration()
   160  			upgradeableTrueCondition.ObservedGeneration = currentGen + 1
   161  			meta.SetStatusCondition(&cond.Status.Conditions, upgradeableTrueCondition)
   162  			_, err = crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).UpdateStatus(context.TODO(), cond, metav1.UpdateOptions{})
   163  			return err
   164  		}, pollInterval, pollDuration).Should(Succeed())
   165  
   166  		By(`Update the catalogsources`)
   167  		manifests = []registry.PackageManifest{
   168  			{
   169  				PackageName: pkgA,
   170  				Channels: []registry.PackageChannel{
   171  					{Name: stableChannel, CurrentCSVName: pkgDStable},
   172  				},
   173  				DefaultChannelName: stableChannel,
   174  			},
   175  		}
   176  
   177  		updateInternalCatalog(GinkgoT(), c, crc, catalog, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csvA, csvB, csvD}, manifests)
   178  		By(`Attempt to get the catalog source before creating install plan(s)`)
   179  		_, err = fetchCatalogSourceOnStatus(crc, catalog, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   180  		require.NoError(GinkgoT(), err)
   181  
   182  		By(`CSVD will be in Pending status due to overrides in csvB's condition`)
   183  		fetchedCSV, err = fetchCSV(crc, generatedNamespace.GetName(), csvD.GetName(), buildCSVReasonChecker(operatorsv1alpha1.CSVReasonOperatorConditionNotUpgradeable))
   184  		require.NoError(GinkgoT(), err)
   185  		require.Equal(GinkgoT(), fetchedCSV.Status.Phase, operatorsv1alpha1.CSVPhasePending)
   186  
   187  		By(`Get the OperatorCondition for csvB and override the upgradeable false condition`)
   188  		Eventually(func() error {
   189  			cond, err = crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).Get(context.TODO(), csvB.GetName(), metav1.GetOptions{})
   190  			if err != nil {
   191  				return err
   192  			}
   193  			meta.SetStatusCondition(&cond.Spec.Overrides, upgradeableTrueCondition)
   194  			By(`Update the condition`)
   195  			_, err = crc.OperatorsV2().OperatorConditions(generatedNamespace.GetName()).Update(context.TODO(), cond, metav1.UpdateOptions{})
   196  			return err
   197  		}, pollInterval, pollDuration).Should(Succeed())
   198  		require.NoError(GinkgoT(), err)
   199  
   200  		require.NoError(GinkgoT(), err)
   201  		_, err = fetchCSV(crc, generatedNamespace.GetName(), csvD.GetName(), csvSucceededChecker)
   202  		require.NoError(GinkgoT(), err)
   203  	})
   204  })