Skip to content

Commit b54d96b

Browse files
openshift-merge-bot[bot]Per Goncalves da Silva
authored andcommitted
Merge pull request #670 from kevinrizza/backport-e2es
[release-4.15] OCPBUGS-25798: Fix snapshot failure, backport e2es
1 parent d8b6a26 commit b54d96b

File tree

6 files changed

+154
-102
lines changed

6 files changed

+154
-102
lines changed

staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
324324
subscription.WithAppendedReconcilers(subscription.ReconcilerFromLegacySyncHandler(op.syncSubscriptions, nil)),
325325
subscription.WithRegistryReconcilerFactory(op.reconciler),
326326
subscription.WithGlobalCatalogNamespace(op.namespace),
327+
subscription.WithSourceProvider(op.sourceInvalidator),
327328
)
328329
if err != nil {
329330
return nil, err

staging/operator-lifecycle-manager/test/e2e/csv_e2e_test.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4427,14 +4427,24 @@ var csvFailedChecker = buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseFailed
44274427
var csvAnyChecker = buildCSVConditionChecker(operatorsv1alpha1.CSVPhasePending, operatorsv1alpha1.CSVPhaseSucceeded, operatorsv1alpha1.CSVPhaseReplacing, operatorsv1alpha1.CSVPhaseDeleting, operatorsv1alpha1.CSVPhaseFailed)
44284428
var csvCopiedChecker = buildCSVReasonChecker(operatorsv1alpha1.CSVReasonCopied)
44294429

4430-
func fetchCSV(c versioned.Interface, name, namespace string, checker csvConditionChecker) (*operatorsv1alpha1.ClusterServiceVersion, error) {
4431-
var fetchedCSV *operatorsv1alpha1.ClusterServiceVersion
4432-
var err error
4433-
4434-
Eventually(func() (bool, error) {
4435-
fetchedCSV, err = c.OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(context.TODO(), name, metav1.GetOptions{})
4436-
if err != nil {
4437-
return false, err
4430+
func fetchCSV(c versioned.Interface, namespace, name string, checker csvConditionChecker) (*operatorsv1alpha1.ClusterServiceVersion, error) {
4431+
var lastPhase operatorsv1alpha1.ClusterServiceVersionPhase
4432+
var lastReason operatorsv1alpha1.ConditionReason
4433+
var lastMessage string
4434+
var lastError string
4435+
lastTime := time.Now()
4436+
var csv *operatorsv1alpha1.ClusterServiceVersion
4437+
4438+
ctx.Ctx().Logf("waiting for CSV %s/%s to reach condition", namespace, name)
4439+
err := wait.Poll(pollInterval, pollDuration, func() (bool, error) {
4440+
var err error
4441+
csv, err = c.OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(context.TODO(), name, metav1.GetOptions{})
4442+
if err != nil || csv == nil {
4443+
if lastError != err.Error() {
4444+
ctx.Ctx().Logf("error getting csv %s/%s: %v", namespace, name, err)
4445+
lastError = err.Error()
4446+
}
4447+
return false, nil
44384448
}
44394449
ctx.Ctx().Logf("%s (%s): %s", fetchedCSV.Status.Phase, fetchedCSV.Status.Reason, fetchedCSV.Status.Message)
44404450
return checker(fetchedCSV), nil

staging/operator-lifecycle-manager/test/e2e/installplan_e2e_test.go

Lines changed: 58 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"context"
66
"encoding/json"
7-
"errors"
87
"fmt"
98
"path/filepath"
109
"strconv"
@@ -32,7 +31,6 @@ import (
3231
k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
3332
"k8s.io/apimachinery/pkg/util/diff"
3433
"k8s.io/apimachinery/pkg/util/wait"
35-
"k8s.io/apimachinery/pkg/watch"
3634
"k8s.io/client-go/discovery"
3735
"k8s.io/client-go/util/retry"
3836
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -2795,11 +2793,14 @@ var _ = Describe("Install Plan", func() {
27952793
_, err := fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced)
27962794
require.NoError(GinkgoT(), err)
27972795

2796+
By("Creating a Subscription")
27982797
subscriptionName := genName("sub-nginx-")
2799-
subscriptionCleanup := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, mainCatalogSourceName, packageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
2800-
defer subscriptionCleanup()
2798+
// Subscription is explitly deleted as part of the test to avoid CSV being recreated,
2799+
// so ignore cleanup function
2800+
_ = createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, mainCatalogSourceName, packageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
28012801

2802-
subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)
2802+
By("Attempt to get Subscription")
2803+
subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
28032804
require.NoError(GinkgoT(), err)
28042805
require.NotNil(GinkgoT(), subscription)
28052806

@@ -2870,92 +2871,63 @@ var _ = Describe("Install Plan", func() {
28702871
// Should have removed every matching step
28712872
require.Equal(GinkgoT(), 0, len(expectedSteps), "Actual resource steps do not match expected: %#v", expectedSteps)
28722873

2873-
// the test from here out verifies created RBAC is removed after CSV deletion
2874-
createdClusterRoles, err := c.KubernetesInterface().RbacV1().ClusterRoles().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})
2875-
createdClusterRoleNames := map[string]struct{}{}
2876-
for _, role := range createdClusterRoles.Items {
2877-
createdClusterRoleNames[role.GetName()] = struct{}{}
2878-
GinkgoT().Logf("Monitoring cluster role %v", role.GetName())
2879-
}
2880-
2881-
createdClusterRoleBindings, err := c.KubernetesInterface().RbacV1().ClusterRoleBindings().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})
2882-
createdClusterRoleBindingNames := map[string]struct{}{}
2883-
for _, binding := range createdClusterRoleBindings.Items {
2884-
createdClusterRoleBindingNames[binding.GetName()] = struct{}{}
2885-
GinkgoT().Logf("Monitoring cluster role binding %v", binding.GetName())
2886-
}
2887-
2888-
crWatcher, err := c.KubernetesInterface().RbacV1().ClusterRoles().Watch(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})
2889-
require.NoError(GinkgoT(), err)
2890-
crbWatcher, err := c.KubernetesInterface().RbacV1().ClusterRoleBindings().Watch(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})
2891-
require.NoError(GinkgoT(), err)
2892-
2893-
done := make(chan struct{})
2894-
errExit := make(chan error)
2895-
go func() {
2896-
defer GinkgoRecover()
2897-
for {
2898-
select {
2899-
case evt, ok := <-crWatcher.ResultChan():
2900-
if !ok {
2901-
errExit <- errors.New("cr watch channel closed unexpectedly")
2902-
return
2903-
}
2904-
if evt.Type == watch.Deleted {
2905-
cr, ok := evt.Object.(*rbacv1.ClusterRole)
2906-
if !ok {
2907-
continue
2908-
}
2909-
delete(createdClusterRoleNames, cr.GetName())
2910-
if len(createdClusterRoleNames) == 0 && len(createdClusterRoleBindingNames) == 0 {
2911-
done <- struct{}{}
2912-
return
2913-
}
2914-
}
2915-
case evt, ok := <-crbWatcher.ResultChan():
2916-
if !ok {
2917-
errExit <- errors.New("crb watch channel closed unexpectedly")
2918-
return
2919-
}
2920-
if evt.Type == watch.Deleted {
2921-
crb, ok := evt.Object.(*rbacv1.ClusterRoleBinding)
2922-
if !ok {
2923-
continue
2924-
}
2925-
delete(createdClusterRoleBindingNames, crb.GetName())
2926-
if len(createdClusterRoleNames) == 0 && len(createdClusterRoleBindingNames) == 0 {
2927-
done <- struct{}{}
2928-
return
2929-
}
2930-
}
2931-
case <-time.After(pollDuration):
2932-
done <- struct{}{}
2933-
return
2874+
By(fmt.Sprintf("Explicitly deleting subscription %s/%s", generatedNamespace.GetName(), subscriptionName))
2875+
err = crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Delete(context.Background(), subscriptionName, metav1.DeleteOptions{})
2876+
By("Looking for no error OR IsNotFound error")
2877+
require.NoError(GinkgoT(), client.IgnoreNotFound(err))
2878+
2879+
By("Waiting for the Subscription to delete")
2880+
err = waitForSubscriptionToDelete(generatedNamespace.GetName(), subscriptionName, crc)
2881+
require.NoError(GinkgoT(), client.IgnoreNotFound(err))
2882+
2883+
By(fmt.Sprintf("Explicitly deleting csv %s/%s", generatedNamespace.GetName(), stableCSVName))
2884+
err = crc.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Delete(context.Background(), stableCSVName, metav1.DeleteOptions{})
2885+
By("Looking for no error OR IsNotFound error")
2886+
require.NoError(GinkgoT(), client.IgnoreNotFound(err))
2887+
By("Waiting for the CSV to delete")
2888+
err = waitForCsvToDelete(generatedNamespace.GetName(), stableCSVName, crc)
2889+
require.NoError(GinkgoT(), client.IgnoreNotFound(err))
2890+
2891+
nCrs := 0
2892+
nCrbs := 0
2893+
By("Waiting for CRBs and CRs and SAs to delete")
2894+
Eventually(func() bool {
2895+
2896+
crbs, err := c.KubernetesInterface().RbacV1().ClusterRoleBindings().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})
2897+
if err != nil {
2898+
GinkgoT().Logf("error getting crbs: %v", err)
2899+
return false
2900+
}
2901+
if n := len(crbs.Items); n != 0 {
2902+
if n != nCrbs {
2903+
GinkgoT().Logf("CRBs remaining: %v", n)
2904+
nCrbs = n
29342905
}
2906+
return false
29352907
}
2936-
}()
2937-
GinkgoT().Logf("Deleting CSV '%v' in namespace %v", stableCSVName, generatedNamespace.GetName())
2938-
require.NoError(GinkgoT(), crc.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))
2939-
select {
2940-
case <-done:
2941-
break
2942-
case err := <-errExit:
2943-
GinkgoT().Fatal(err)
2944-
}
29452908

2946-
require.Emptyf(GinkgoT(), createdClusterRoleNames, "unexpected cluster role remain: %v", createdClusterRoleNames)
2947-
require.Emptyf(GinkgoT(), createdClusterRoleBindingNames, "unexpected cluster role binding remain: %v", createdClusterRoleBindingNames)
2948-
2949-
Eventually(func() error {
2950-
_, err := c.GetServiceAccount(generatedNamespace.GetName(), serviceAccountName)
2951-
if err == nil {
2952-
return fmt.Errorf("The %v/%v ServiceAccount should have been deleted", generatedNamespace.GetName(), serviceAccountName)
2909+
crs, err := c.KubernetesInterface().RbacV1().ClusterRoles().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})
2910+
if err != nil {
2911+
GinkgoT().Logf("error getting crs: %v", err)
2912+
return false
29532913
}
2954-
if !apierrors.IsNotFound(err) {
2955-
return err
2914+
if n := len(crs.Items); n != 0 {
2915+
if n != nCrs {
2916+
GinkgoT().Logf("CRs remaining: %v", n)
2917+
nCrs = n
2918+
}
2919+
return false
29562920
}
2957-
return nil
2958-
}, timeout, interval).Should(BeNil())
2921+
2922+
_, err = c.KubernetesInterface().CoreV1().ServiceAccounts(generatedNamespace.GetName()).Get(context.Background(), serviceAccountName, metav1.GetOptions{})
2923+
if client.IgnoreNotFound(err) != nil {
2924+
GinkgoT().Logf("error getting sa %s/%s: %v", generatedNamespace.GetName(), serviceAccountName, err)
2925+
return false
2926+
}
2927+
2928+
return true
2929+
}, pollDuration*2, pollInterval).Should(BeTrue())
2930+
By("Cleaning up the test")
29592931
})
29602932

29612933
It("CRD validation", func() {

staging/operator-lifecycle-manager/test/e2e/subscription_e2e_test.go

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import (
3636
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
3737
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
3838
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/projection"
39-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/comparison"
4039
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
4140
"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
4241
registryapi "github.com/operator-framework/operator-registry/pkg/api"
@@ -315,7 +314,7 @@ var _ = Describe("Subscription", func() {
315314
subscriptionCleanup, _ := createSubscription(GinkgoT(), crc, generatedNamespace.GetName(), "manual-subscription", testPackageName, stableChannel, operatorsv1alpha1.ApprovalManual)
316315
defer subscriptionCleanup()
317316

318-
subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), "manual-subscription", subscriptionStateUpgradePendingChecker)
317+
subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), "manual-subscription", subscriptionHasCondition(operatorsv1alpha1.SubscriptionInstallPlanPending, corev1.ConditionTrue, string(operatorsv1alpha1.InstallPlanPhaseRequiresApproval), ""))
319318
require.NoError(GinkgoT(), err)
320319
require.NotNil(GinkgoT(), subscription)
321320

@@ -1035,6 +1034,7 @@ var _ = Describe("Subscription", func() {
10351034
// - Wait for sub to have status condition SubscriptionInstallPlanMissing true
10361035
// - Ensure original non-InstallPlan status conditions remain after InstallPlan transitions
10371036
It("can reconcile InstallPlan status", func() {
1037+
By(`TestSubscriptionInstallPlanStatus ensures that a Subscription has the appropriate status conditions for possible referenced InstallPlan states.`)
10381038
c := newKubeClient()
10391039
crc := newCRClient()
10401040

@@ -1081,6 +1081,7 @@ var _ = Describe("Subscription", func() {
10811081
ref := sub.Status.InstallPlanRef
10821082
Expect(ref).ToNot(BeNil())
10831083

1084+
By(`Get the InstallPlan`)
10841085
plan := &operatorsv1alpha1.InstallPlan{}
10851086
plan.SetNamespace(ref.Namespace)
10861087
plan.SetName(ref.Name)
@@ -1192,16 +1193,13 @@ var _ = Describe("Subscription", func() {
11921193
Expect(err).ToNot(HaveOccurred())
11931194
Expect(sub).ToNot(BeNil())
11941195

1195-
// Ensure original non-InstallPlan status conditions remain after InstallPlan transitions
1196-
hashEqual := comparison.NewHashEqualitor()
1196+
By(`Ensure InstallPlan-related status conditions match what we're expecting`)
11971197
for _, cond := range conds {
11981198
switch condType := cond.Type; condType {
11991199
case operatorsv1alpha1.SubscriptionInstallPlanPending, operatorsv1alpha1.SubscriptionInstallPlanFailed:
12001200
require.FailNowf(GinkgoT(), "failed", "subscription contains unexpected installplan condition: %v", cond)
12011201
case operatorsv1alpha1.SubscriptionInstallPlanMissing:
12021202
require.Equal(GinkgoT(), operatorsv1alpha1.ReferencedInstallPlanNotFound, cond.Reason)
1203-
default:
1204-
require.True(GinkgoT(), hashEqual(cond, sub.Status.GetCondition(condType)), "non-installplan status condition changed")
12051203
}
12061204
}
12071205
})
@@ -2825,14 +2823,53 @@ func subscriptionHasCurrentCSV(currentCSV string) subscriptionStateChecker {
28252823
}
28262824

28272825
func subscriptionHasCondition(condType operatorsv1alpha1.SubscriptionConditionType, status corev1.ConditionStatus, reason, message string) subscriptionStateChecker {
2826+
var lastCond operatorsv1alpha1.SubscriptionCondition
2827+
lastTime := time.Now()
2828+
// if status/reason/message meet expectations, then subscription state is considered met/true
2829+
// IFF this is the result of a recent change of status/reason/message
2830+
// else, cache the current status/reason/message for next loop/comparison
28282831
return func(subscription *operatorsv1alpha1.Subscription) bool {
28292832
cond := subscription.Status.GetCondition(condType)
28302833
if cond.Status == status && cond.Reason == reason && cond.Message == message {
2831-
fmt.Printf("subscription condition met %v\n", cond)
2834+
if lastCond.Status != cond.Status && lastCond.Reason != cond.Reason && lastCond.Message == cond.Message {
2835+
GinkgoT().Logf("waited %s subscription condition met %v\n", time.Since(lastTime), cond)
2836+
lastTime = time.Now()
2837+
lastCond = cond
2838+
}
28322839
return true
28332840
}
28342841

2835-
fmt.Printf("subscription condition not met: %v\n", cond)
2842+
if lastCond.Status != cond.Status && lastCond.Reason != cond.Reason && lastCond.Message == cond.Message {
2843+
GinkgoT().Logf("waited %s subscription condition not met: %v\n", time.Since(lastTime), cond)
2844+
lastTime = time.Now()
2845+
lastCond = cond
2846+
}
2847+
return false
2848+
}
2849+
}
2850+
2851+
func subscriptionDoesNotHaveCondition(condType operatorsv1alpha1.SubscriptionConditionType) subscriptionStateChecker {
2852+
var lastStatus corev1.ConditionStatus
2853+
lastTime := time.Now()
2854+
// if status meets expectations, then subscription state is considered met/true
2855+
// IFF this is the result of a recent change of status
2856+
// else, cache the current status for next loop/comparison
2857+
return func(subscription *operatorsv1alpha1.Subscription) bool {
2858+
cond := subscription.Status.GetCondition(condType)
2859+
if cond.Status == corev1.ConditionUnknown {
2860+
if cond.Status != lastStatus {
2861+
GinkgoT().Logf("waited %s subscription condition not found\n", time.Since(lastTime))
2862+
lastStatus = cond.Status
2863+
lastTime = time.Now()
2864+
}
2865+
return true
2866+
}
2867+
2868+
if cond.Status != lastStatus {
2869+
GinkgoT().Logf("waited %s subscription condition found: %v\n", time.Since(lastTime), cond)
2870+
lastStatus = cond.Status
2871+
lastTime = time.Now()
2872+
}
28362873
return false
28372874
}
28382875
}
@@ -2943,6 +2980,35 @@ func createSubscriptionForCatalogWithSpec(t GinkgoTInterface, crc versioned.Inte
29432980
return buildSubscriptionCleanupFunc(crc, subscription)
29442981
}
29452982

2983+
func waitForSubscriptionToDelete(namespace, name string, c versioned.Interface) error {
2984+
var lastState operatorsv1alpha1.SubscriptionState
2985+
var lastReason operatorsv1alpha1.ConditionReason
2986+
lastTime := time.Now()
2987+
2988+
ctx.Ctx().Logf("waiting for subscription %s/%s to delete", namespace, name)
2989+
err := wait.Poll(pollInterval, pollDuration, func() (bool, error) {
2990+
sub, err := c.OperatorsV1alpha1().Subscriptions(namespace).Get(context.TODO(), name, metav1.GetOptions{})
2991+
if apierrors.IsNotFound(err) {
2992+
ctx.Ctx().Logf("subscription %s/%s deleted", namespace, name)
2993+
return true, nil
2994+
}
2995+
if err != nil {
2996+
ctx.Ctx().Logf("error getting subscription %s/%s: %v", namespace, name, err)
2997+
}
2998+
if sub != nil {
2999+
state, reason := sub.Status.State, sub.Status.Reason
3000+
if state != lastState || reason != lastReason {
3001+
ctx.Ctx().Logf("waited %s for subscription %s/%s status: %s (%s)", time.Since(lastTime), namespace, name, state, reason)
3002+
lastState, lastReason = state, reason
3003+
lastTime = time.Now()
3004+
}
3005+
}
3006+
return false, nil
3007+
})
3008+
3009+
return err
3010+
}
3011+
29463012
func checkDeploymentHasPodConfigNodeSelector(t GinkgoTInterface, client operatorclient.ClientInterface, csv *operatorsv1alpha1.ClusterServiceVersion, nodeSelector map[string]string) error {
29473013
resolver := install.StrategyResolver{}
29483014

staging/operator-lifecycle-manager/test/e2e/util.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,8 +632,10 @@ func createInternalCatalogSource(
632632
ctx.Ctx().Logf("Catalog source %s created", name)
633633

634634
cleanupInternalCatalogSource := func() {
635+
ctx.Ctx().Logf("Cleaning catalog source %s", name)
635636
configMapCleanup()
636637
buildCatalogSourceCleanupFunc(c, crc, namespace, catalogSource)()
638+
ctx.Ctx().Logf("Done cleaning catalog source %s", name)
637639
}
638640
return catalogSource, cleanupInternalCatalogSource
639641
}

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)