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 })