Skip to content

Commit 9febd60

Browse files
Merge pull request #761 from ecordell/multiple-upgrades
Subscription steps through multiple upgrades
2 parents 4d6bb6c + 837c5fe commit 9febd60

26 files changed

+538
-273
lines changed

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ setup-bare: clean e2e.namespace
7171
. ./scripts/install_bare.sh $(shell cat ./e2e.namespace) test/e2e/resources
7272

7373
e2e:
74-
go test -v -failfast -timeout 50m ./test/e2e/... -namespace=openshift-operators -kubeconfig=${KUBECONFIG} -olmNamespace=openshift-operator-lifecycle-manager
74+
go test -v -failfast -timeout 70m ./test/e2e/... -namespace=openshift-operators -kubeconfig=${KUBECONFIG} -olmNamespace=openshift-operator-lifecycle-manager
7575

7676
e2e-local:
7777
. ./scripts/build_local.sh
@@ -98,7 +98,8 @@ container:
9898

9999
clean-e2e:
100100
kubectl delete crds --all
101-
kubectl delete apiservices.apiregistration.k8s.io v1alpha1.packages.apps.redhat.com
101+
kubectl delete apiservices.apiregistration.k8s.io v1alpha1.packages.apps.redhat.com || true
102+
for i in {1..40}; do kubectl delete namespace "ns-$i" || true; done
102103
kubectl delete -f test/e2e/resources/0000_50_olm_00-namespace.yaml
103104

104105
clean:

go.sum

Lines changed: 2 additions & 71 deletions
Large diffs are not rendered by default.

pkg/api/apis/operators/v1alpha1/clusterserviceversion.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ var uncopiableReasons = map[ConditionReason]struct{}{
3030
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs: {},
3131
}
3232

33+
// safeToAnnotateOperatorGroupReasons are the set of reasons that it's safe to attempt to update the operatorgroup
34+
// annotations
35+
var safeToAnnotateOperatorGroupReasons = map[ConditionReason]struct{}{
36+
CSVReasonOwnerConflict: {},
37+
CSVReasonInstallSuccessful: {},
38+
CSVReasonInvalidInstallModes: {},
39+
CSVReasonNoTargetNamespaces: {},
40+
CSVReasonUnsupportedOperatorGroup: {},
41+
CSVReasonNoOperatorGroup: {},
42+
CSVReasonTooManyOperatorGroups: {},
43+
CSVReasonInterOperatorGroupOwnerConflict: {},
44+
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs: {},
45+
}
46+
3347
// SetPhaseWithEventIfChanged emits a Kubernetes event with details of a phase change and sets the current phase if phase, reason, or message would changed
3448
func (c *ClusterServiceVersion) SetPhaseWithEventIfChanged(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now metav1.Time, recorder record.EventRecorder) {
3549
if c.Status.Phase == phase && c.Status.Reason == reason && c.Status.Message == message {
@@ -120,6 +134,11 @@ func (c *ClusterServiceVersion) IsUncopiable() bool {
120134
return ok
121135
}
122136

137+
func (c *ClusterServiceVersion) IsSafeToUpdateOperatorGroupAnnotations() bool {
138+
_, ok := safeToAnnotateOperatorGroupReasons[c.Status.Reason]
139+
return ok
140+
}
141+
123142
// NewInstallModeSet returns an InstallModeSet instantiated from the given list of InstallModes.
124143
// If the given list is not a set, an error is returned.
125144
func NewInstallModeSet(modes []InstallMode) (InstallModeSet, error) {

pkg/controller/install/deployment.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ func (i *StrategyDeploymentInstaller) installDeployments(deps []StrategyDeployme
7575
dep.Spec.Template.SetAnnotations(annotations)
7676

7777
ownerutil.AddNonBlockingOwner(dep, i.owner)
78-
ownerutil.AddOwnerLabels(dep, i.owner)
78+
if err := ownerutil.AddOwnerLabels(dep, i.owner); err != nil {
79+
return err
80+
}
7981
if _, err := i.strategyClient.CreateOrUpdateDeployment(dep); err != nil {
8082
return err
8183
}

pkg/controller/operators/catalog/operator.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ func NewOperator(kubeconfigPath string, logger *logrus.Logger, wakeupInterval ti
8989
// Create an informer for each watched namespace.
9090
ipSharedIndexInformers := []cache.SharedIndexInformer{}
9191
subSharedIndexInformers := []cache.SharedIndexInformer{}
92+
csvSharedIndexInformers := []cache.SharedIndexInformer{}
9293
for _, namespace := range watchedNamespaces {
9394
nsInformerFactory := externalversions.NewSharedInformerFactoryWithOptions(crClient, wakeupInterval, externalversions.WithNamespace(namespace))
9495
ipSharedIndexInformers = append(ipSharedIndexInformers, nsInformerFactory.Operators().V1alpha1().InstallPlans().Informer())
9596
subSharedIndexInformers = append(subSharedIndexInformers, nsInformerFactory.Operators().V1alpha1().Subscriptions().Informer())
97+
csvSharedIndexInformers = append(csvSharedIndexInformers, nsInformerFactory.Operators().V1alpha1().ClusterServiceVersions().Informer())
9698

9799
// resolver needs subscription and csv listers
98100
lister.OperatorsV1alpha1().RegisterSubscriptionLister(namespace, nsInformerFactory.Operators().V1alpha1().Subscriptions().Lister())
@@ -230,6 +232,11 @@ func NewOperator(kubeconfigPath string, logger *logrus.Logger, wakeupInterval ti
230232
op.lister.CoreV1().RegisterNamespaceLister(namespaceInformer.Lister())
231233
op.namespaceResolveQueue = resolvingNamespaceQueue
232234

235+
// Register CSV informers to fill cache
236+
for _, informer := range csvSharedIndexInformers {
237+
op.RegisterInformer(informer)
238+
}
239+
233240
return op, nil
234241
}
235242

@@ -381,7 +388,7 @@ func (o *Operator) syncCatalogSources(obj interface{}) (syncError error) {
381388

382389
return nil
383390
}
384-
391+
385392
logger.Debug("catsrc configmap state good, checking registry pod")
386393
}
387394

@@ -391,7 +398,6 @@ func (o *Operator) syncCatalogSources(obj interface{}) (syncError error) {
391398
return fmt.Errorf("no reconciler for source type %s", catsrc.Spec.SourceType)
392399
}
393400

394-
395401
// if registry pod hasn't been created or hasn't been updated since the last configmap update, recreate it
396402
if catsrc.Status.RegistryServiceStatus == nil || catsrc.Status.RegistryServiceStatus.CreatedAt.Before(&catsrc.Status.LastSync) {
397403
logger.Debug("registry server scheduled recheck")
@@ -538,7 +544,7 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
538544

539545
subs, err := o.lister.OperatorsV1alpha1().SubscriptionLister().Subscriptions(namespace).List(labels.Everything())
540546
if err != nil {
541-
logger.WithError(err).Debug("couldn't list subscriptions")
547+
logger.WithError(err).Debug("couldn't list subscriptions")
542548
return err
543549
}
544550

@@ -745,7 +751,7 @@ func (o *Operator) ensureSubscriptionCSVState(logger *logrus.Entry, sub *v1alpha
745751
} else {
746752
out.Status.State = v1alpha1.SubscriptionStateAtLatest
747753
}
748-
754+
749755
out.Status.InstalledCSV = sub.Status.CurrentCSV
750756
}
751757

pkg/controller/operators/olm/apiservices.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (a *Operator) checkAPIServiceResources(csv *v1alpha1.ClusterServiceVersion,
9393

9494
// Check if the APIService is adoptable
9595
if !ownerutil.AdoptableLabels(apiService.GetLabels(), true, owners...) {
96+
logger.WithFields(log.Fields{"obj": "apiService", "labels": apiService.GetLabels()}).Debug("adoption failed")
9697
err := olmerrors.NewUnadoptableError("", apiServiceName)
9798
logger.WithError(err).Warn("found unadoptable apiservice")
9899
errs = append(errs, err)
@@ -554,7 +555,10 @@ func (a *Operator) installAPIServiceRequirements(desc v1alpha1.APIServiceDescrip
554555
if err == nil {
555556
// Check if the only owners are this CSV or in this CSV's replacement chain.
556557
if ownerutil.AdoptableLabels(existingAuthDelegatorClusterRoleBinding.GetLabels(), true, csv) {
557-
ownerutil.AddOwnerLabels(authDelegatorClusterRoleBinding, csv)
558+
logger.WithFields(log.Fields{"obj": "authDelegatorCRB", "labels": existingAuthDelegatorClusterRoleBinding.GetLabels()}).Debug("adopting")
559+
if err := ownerutil.AddOwnerLabels(authDelegatorClusterRoleBinding, csv); err != nil {
560+
return nil, err
561+
}
558562
}
559563

560564
// Attempt an update.
@@ -564,7 +568,9 @@ func (a *Operator) installAPIServiceRequirements(desc v1alpha1.APIServiceDescrip
564568
}
565569
} else if k8serrors.IsNotFound(err) {
566570
// Create the role.
567-
ownerutil.AddOwnerLabels(authDelegatorClusterRoleBinding, csv)
571+
if err := ownerutil.AddOwnerLabels(authDelegatorClusterRoleBinding, csv); err != nil {
572+
return nil, err
573+
}
568574
_, err = a.OpClient.CreateClusterRoleBinding(authDelegatorClusterRoleBinding)
569575
if err != nil {
570576
log.Warnf("could not create auth delegator clusterrolebinding %s", authDelegatorClusterRoleBinding.GetName())
@@ -597,7 +603,10 @@ func (a *Operator) installAPIServiceRequirements(desc v1alpha1.APIServiceDescrip
597603
if err == nil {
598604
// Check if the only owners are this CSV or in this CSV's replacement chain.
599605
if ownerutil.AdoptableLabels(existingAuthReaderRoleBinding.GetLabels(), true, csv) {
600-
ownerutil.AddOwnerLabels(authReaderRoleBinding, csv)
606+
logger.WithFields(log.Fields{"obj": "existingAuthReaderRB", "labels": existingAuthReaderRoleBinding.GetLabels()}).Debug("adopting")
607+
if err := ownerutil.AddOwnerLabels(authReaderRoleBinding, csv); err != nil {
608+
return nil, err
609+
}
601610
}
602611
// Attempt an update.
603612
if _, err := a.OpClient.UpdateRoleBinding(authReaderRoleBinding); err != nil {
@@ -606,7 +615,9 @@ func (a *Operator) installAPIServiceRequirements(desc v1alpha1.APIServiceDescrip
606615
}
607616
} else if k8serrors.IsNotFound(err) {
608617
// Create the role.
609-
ownerutil.AddOwnerLabels(authReaderRoleBinding, csv)
618+
if err := ownerutil.AddOwnerLabels(authReaderRoleBinding, csv); err != nil {
619+
return nil, err
620+
}
610621
_, err = a.OpClient.CreateRoleBinding(authReaderRoleBinding)
611622
if err != nil {
612623
log.Warnf("could not create auth reader role binding %s", authReaderRoleBinding.GetName())
@@ -706,12 +717,15 @@ func (a *Operator) installAPIServiceRequirements(desc v1alpha1.APIServiceDescrip
706717

707718
// check if the APIService is adoptable
708719
if !ownerutil.AdoptableLabels(apiService.GetLabels(), true, owners...) {
720+
logger.WithFields(log.Fields{"obj": "apiService", "labels": apiService.GetLabels()}).Debug("adoption failed")
709721
return nil, fmt.Errorf("pre-existing APIService %s is not adoptable", apiServiceName)
710722
}
711723
}
712724

713725
// Add the CSV as an owner
714-
ownerutil.AddOwnerLabels(apiService, csv)
726+
if err := ownerutil.AddOwnerLabels(apiService, csv); err != nil {
727+
return nil, err
728+
}
715729

716730
// update the ServiceReference
717731
apiService.Spec.Service = &apiregistrationv1.ServiceReference{

pkg/controller/operators/olm/operator.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type Operator struct {
5858
lister operatorlister.OperatorLister
5959
recorder record.EventRecorder
6060
copyQueueIndexer *queueinformer.QueueIndexer
61+
gcQueueIndexer *queueinformer.QueueIndexer
6162
}
6263

6364
func NewOperator(logger *logrus.Logger, crClient versioned.Interface, opClient operatorclient.ClientInterface, strategyResolver install.StrategyResolverInterface, wakeupInterval time.Duration, namespaces []string) (*Operator, error) {
@@ -261,6 +262,12 @@ func NewOperator(logger *logrus.Logger, crClient versioned.Interface, opClient o
261262
op.RegisterQueueIndexer(csvQueueIndexer)
262263
op.copyQueueIndexer = csvQueueIndexer
263264

265+
// Register separate queue for gcing csvs
266+
csvGCQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "csvGC")
267+
csvGCQueueIndexer := queueinformer.NewQueueIndexer(csvGCQueue, csvIndexes, op.syncGcCsv, "csvGC", logger, metrics.NewMetricsNil())
268+
op.RegisterQueueIndexer(csvGCQueueIndexer)
269+
op.gcQueueIndexer = csvGCQueueIndexer
270+
264271
// Set up watch on deployments
265272
depHandlers := &cache.ResourceEventHandlerFuncs{
266273
// TODO: pass closure that forgets queue item after calling custom deletion handler.
@@ -435,7 +442,7 @@ func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) {
435442
for _, namespace := range namespaces {
436443
if namespace != operatorNamespace {
437444
logger.WithField("targetNamespace", namespace).Debug("requeueing child csv for deletion")
438-
a.csvQueueSet.Requeue(clusterServiceVersion.GetName(), namespace)
445+
a.gcQueueIndexer.Add(fmt.Sprintf("%s/%s", namespace, clusterServiceVersion.GetName()))
439446
}
440447
}
441448

@@ -478,34 +485,35 @@ func (a *Operator) removeDanglingChildCSVs(csv *v1alpha1.ClusterServiceVersion)
478485
operatorNamespace, ok := csv.Annotations[v1alpha2.OperatorGroupNamespaceAnnotationKey]
479486
if !ok {
480487
logger.Debug("missing operator namespace annotation on copied CSV")
481-
return a.deleteChild(csv)
488+
return a.deleteChild(csv, logger)
482489
}
483490

484491
logger = logger.WithField("parentNamespace", operatorNamespace)
485492
parent, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(operatorNamespace).Get(csv.GetName())
486493
if k8serrors.IsNotFound(err) || k8serrors.IsGone(err) || parent == nil {
487494
logger.Debug("deleting copied CSV since parent is missing")
488-
return a.deleteChild(csv)
495+
return a.deleteChild(csv, logger)
489496
}
490497

491498
if parent.Status.Phase == v1alpha1.CSVPhaseFailed && parent.Status.Reason == v1alpha1.CSVReasonInterOperatorGroupOwnerConflict {
492499
logger.Debug("deleting copied CSV since parent has intersecting operatorgroup conflict")
493-
return a.deleteChild(csv)
500+
return a.deleteChild(csv, logger)
494501
}
495502

496503
if annotations := parent.GetAnnotations(); annotations != nil {
497504
if !resolver.NewNamespaceSetFromString(annotations[v1alpha2.OperatorGroupTargetsAnnotationKey]).Contains(csv.GetNamespace()) {
498505
logger.WithField("parentTargets", annotations[v1alpha2.OperatorGroupTargetsAnnotationKey]).
499506
Debug("deleting copied CSV since parent no longer lists this as a target namespace")
500-
return a.deleteChild(csv)
507+
return a.deleteChild(csv, logger)
501508
}
502509
}
503510

504511
return nil
505512
}
506513

507-
func (a *Operator) deleteChild(csv *v1alpha1.ClusterServiceVersion) error {
508-
return a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Delete(csv.GetName(), &metav1.DeleteOptions{})
514+
func (a *Operator) deleteChild(csv *v1alpha1.ClusterServiceVersion, logger *logrus.Entry) error {
515+
logger.Debug("gcing csv")
516+
return a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Delete(csv.GetName(), metav1.NewDeleteOptions(0))
509517
}
510518

511519
// syncClusterServiceVersion is the method that gets called when we see a CSV event in the cluster
@@ -609,6 +617,19 @@ func (a *Operator) syncCopyCSV(obj interface{}) (syncError error) {
609617
return
610618
}
611619

620+
func (a *Operator) syncGcCsv(obj interface{}) (syncError error) {
621+
clusterServiceVersion, ok := obj.(*v1alpha1.ClusterServiceVersion)
622+
if !ok {
623+
a.Log.Debugf("wrong type: %#v", obj)
624+
return fmt.Errorf("casting ClusterServiceVersion failed")
625+
}
626+
if clusterServiceVersion.IsCopied() {
627+
syncError = a.removeDanglingChildCSVs(clusterServiceVersion)
628+
return
629+
}
630+
return
631+
}
632+
612633
// operatorGroupFromAnnotations returns the OperatorGroup for the CSV only if the CSV is active one in the group
613634
func (a *Operator) operatorGroupFromAnnotations(logger *logrus.Entry, csv *v1alpha1.ClusterServiceVersion) *v1alpha2.OperatorGroup {
614635
annotations := csv.GetAnnotations()
@@ -712,8 +733,8 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
712733
now := timeNow()
713734

714735
if out.IsCopied() {
715-
logger.Debug("skipping copied csv transition")
716-
syncError = a.removeDanglingChildCSVs(out)
736+
logger.Debug("skipping copied csv transition, schedule for gc check")
737+
a.gcQueueIndexer.Enqueue(out)
717738
return
718739
}
719740

@@ -732,7 +753,6 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
732753
return
733754
}
734755

735-
logger.Info("updated annotations to match current operatorgroup")
736756
if err := a.ensureDeploymentAnnotations(logger, out); err != nil {
737757
return nil, err
738758
}
@@ -1324,6 +1344,10 @@ func (a *Operator) cleanupCSVDeployments(logger *logrus.Entry, csv *v1alpha1.Clu
13241344
}
13251345

13261346
func (a *Operator) ensureDeploymentAnnotations(logger *logrus.Entry, csv *v1alpha1.ClusterServiceVersion) error {
1347+
if !csv.IsSafeToUpdateOperatorGroupAnnotations() {
1348+
return nil
1349+
}
1350+
13271351
// Get csv operatorgroup annotations
13281352
annotations := a.copyOperatorGroupAnnotations(&csv.ObjectMeta)
13291353

@@ -1355,7 +1379,6 @@ func (a *Operator) ensureDeploymentAnnotations(logger *logrus.Entry, csv *v1alph
13551379
for _, d := range existingDeployments {
13561380
existingMap[d.GetName()] = d
13571381
}
1358-
13591382
updateErrs := []error{}
13601383
for _, dep := range existingMap {
13611384
if dep.Spec.Template.Annotations == nil {
@@ -1368,5 +1391,7 @@ func (a *Operator) ensureDeploymentAnnotations(logger *logrus.Entry, csv *v1alph
13681391
updateErrs = append(updateErrs, err)
13691392
}
13701393
}
1394+
logger.Info("updated annotations to match current operatorgroup")
1395+
13711396
return utilerrors.NewAggregate(updateErrs)
13721397
}

pkg/controller/operators/olm/operator_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2987,7 +2987,7 @@ func TestSyncOperatorGroups(t *testing.T) {
29872987
ObjectMeta: metav1.ObjectMeta{
29882988
Name: "csv-role",
29892989
Namespace: operatorNamespace,
2990-
Labels: ownerutil.OwnerLabel(operatorCSV),
2990+
Labels: ownerutil.OwnerLabel(operatorCSV, v1alpha1.ClusterServiceVersionKind),
29912991
OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(operatorCSV)},
29922992
},
29932993
Rules: permissions[0].Rules,
@@ -3001,7 +3001,7 @@ func TestSyncOperatorGroups(t *testing.T) {
30013001
ObjectMeta: metav1.ObjectMeta{
30023002
Name: "csv-rolebinding",
30033003
Namespace: operatorNamespace,
3004-
Labels: ownerutil.OwnerLabel(operatorCSV),
3004+
Labels: ownerutil.OwnerLabel(operatorCSV, v1alpha1.ClusterServiceVersionKind),
30053005
OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(operatorCSV)},
30063006
},
30073007
Subjects: []rbacv1.Subject{

0 commit comments

Comments
 (0)