Skip to content

Commit 2760276

Browse files
Merge pull request #1039 from jpeeler/bug1749036
Bug 1749036: fix install behavior both during and post-install permission changes
2 parents 201c8aa + 858e619 commit 2760276

File tree

8 files changed

+345
-29
lines changed

8 files changed

+345
-29
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ const (
231231
CSVReasonRequirementsMet ConditionReason = "AllRequirementsMet"
232232
CSVReasonOwnerConflict ConditionReason = "OwnerConflict"
233233
CSVReasonComponentFailed ConditionReason = "InstallComponentFailed"
234+
CSVReasonComponentFailedNoRetry ConditionReason = "InstallComponentFailedNoRetry"
234235
CSVReasonInvalidStrategy ConditionReason = "InvalidInstallStrategy"
235236
CSVReasonWaiting ConditionReason = "InstallWaiting"
236237
CSVReasonInstallSuccessful ConditionReason = "InstallSucceeded"
@@ -251,6 +252,7 @@ const (
251252
CSVReasonTooManyOperatorGroups ConditionReason = "TooManyOperatorGroups"
252253
CSVReasonInterOperatorGroupOwnerConflict ConditionReason = "InterOperatorGroupOwnerConflict"
253254
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs ConditionReason = "CannotModifyStaticOperatorGroupProvidedAPIs"
255+
CSVReasonDetectedClusterChange ConditionReason = "DetectedClusterChange"
254256
)
255257

256258
// Conditions appear in the status as a record of state transitions on the ClusterServiceVersion

pkg/controller/install/deployment.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
appsv1 "k8s.io/api/apps/v1"
88
rbac "k8s.io/api/rbac/v1"
99
"k8s.io/apimachinery/pkg/api/equality"
10+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
1011
"k8s.io/apimachinery/pkg/util/diff"
1112

1213
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
@@ -160,6 +161,9 @@ func (i *StrategyDeploymentInstaller) Install(s Strategy) error {
160161
}
161162

162163
if err := i.installDeployments(strategy.DeploymentSpecs); err != nil {
164+
if k8serrors.IsForbidden(err) {
165+
return StrategyError{Reason: StrategyErrInsufficientPermissions, Message: fmt.Sprintf("install strategy failed: %s", err)}
166+
}
163167
return err
164168
}
165169

pkg/controller/install/errors.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ const (
1111
StrategyErrReasonUnknown = "Unknown"
1212
StrategyErrBadPatch = "PatchUnsuccessful"
1313
StrategyErrDeploymentUpdated = "DeploymentUpdated"
14+
StrategyErrInsufficientPermissions = "InsufficentPermissions"
1415
)
1516

1617
// unrecoverableErrors are the set of errors that mean we can't recover an install strategy
1718
var unrecoverableErrors = map[string]struct{}{
18-
StrategyErrReasonInvalidStrategy: {},
19-
StrategyErrReasonTimeout: {},
20-
StrategyErrBadPatch: {},
19+
StrategyErrReasonInvalidStrategy: {},
20+
StrategyErrReasonTimeout: {},
21+
StrategyErrBadPatch: {},
22+
StrategyErrInsufficientPermissions: {},
2123
}
2224

2325
// StrategyError is used to represent error types for install strategies

pkg/controller/operators/catalog/installplan_sync.go

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import (
77

88
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
99
"k8s.io/apimachinery/pkg/labels"
10-
"k8s.io/apimachinery/pkg/runtime"
1110
utilerrors "k8s.io/apimachinery/pkg/util/errors"
1211

12+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/scoped"
1313
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
14-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1514
)
1615

1716
// When a user adds permission to a ServiceAccount by creating or updating
@@ -26,7 +25,7 @@ func (o *Operator) triggerInstallPlanRetry(obj interface{}) (syncError error) {
2625
return
2726
}
2827

29-
related, _ := isObjectRBACRelated(obj)
28+
related, _ := scoped.IsObjectRBACRelated(obj)
3029
if !related {
3130
return
3231
}
@@ -75,26 +74,3 @@ func (o *Operator) triggerInstallPlanRetry(obj interface{}) (syncError error) {
7574
syncError = utilerrors.NewAggregate(errs)
7675
return
7776
}
78-
79-
func isObjectRBACRelated(obj interface{}) (related bool, object runtime.Object) {
80-
object, ok := obj.(runtime.Object)
81-
if !ok {
82-
return
83-
}
84-
85-
if err := ownerutil.InferGroupVersionKind(object); err != nil {
86-
return
87-
}
88-
89-
kind := object.GetObjectKind().GroupVersionKind().Kind
90-
switch kind {
91-
case roleKind:
92-
fallthrough
93-
case roleBindingKind:
94-
fallthrough
95-
case serviceAccountKind:
96-
related = true
97-
}
98-
99-
return
100-
}

pkg/controller/operators/olm/operator.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,29 @@ func (a *Operator) syncObject(obj interface{}) (syncError error) {
679679
a.requeueCSVsByLabelSet(logger, labelSets...)
680680
}
681681

682+
// Requeue CSVs that have the reason of `CSVReasonComponentFailedNoRetry` in the case of an RBAC change
683+
var errs []error
684+
related, _ := scoped.IsObjectRBACRelated(metaObj)
685+
if related {
686+
csvList := a.csvSet(metaObj.GetNamespace(), v1alpha1.CSVPhaseFailed)
687+
for _, csv := range csvList {
688+
if csv.Status.Reason != v1alpha1.CSVReasonComponentFailedNoRetry {
689+
continue
690+
}
691+
csv.SetPhase(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonDetectedClusterChange, "Cluster resources changed state", a.now())
692+
_, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).UpdateStatus(csv)
693+
if err != nil {
694+
errs = append(errs, err)
695+
continue
696+
}
697+
if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil {
698+
errs = append(errs, err)
699+
}
700+
logger.Debug("Requeuing CSV due to detected RBAC change")
701+
}
702+
}
703+
704+
syncError = utilerrors.NewAggregate(errs)
682705
return nil
683706
}
684707

@@ -1096,6 +1119,12 @@ func (a *Operator) operatorGroupForCSV(csv *v1alpha1.ClusterServiceVersion, logg
10961119
if targetNamespaceList, err := a.getOperatorGroupTargets(operatorGroup); err == nil && len(targetNamespaceList) == 0 {
10971120
csv.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonNoTargetNamespaces, "no targetNamespaces are matched operatorgroups namespace selection", now, a.recorder)
10981121
}
1122+
logger.Debug("CSV not in operatorgroup, requeuing operator group")
1123+
// this requeue helps when an operator group has not annotated a CSV due to a permissions error
1124+
// but the permissions issue has now been resolved
1125+
if err := a.ogQueueSet.Requeue(operatorGroup.GetNamespace(), operatorGroup.GetName()); err != nil {
1126+
return nil, err
1127+
}
10991128
return nil, nil
11001129
}
11011130
logger.Info("csv in operatorgroup")
@@ -1119,6 +1148,12 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
11191148
"phase": in.Status.Phase,
11201149
})
11211150

1151+
if in.Status.Reason == v1alpha1.CSVReasonComponentFailedNoRetry {
1152+
// will change phase out of failed in the event of an intentional requeue
1153+
logger.Debugf("skipping sync for CSV in failed-no-retry state")
1154+
return
1155+
}
1156+
11221157
out = in.DeepCopy()
11231158
now := a.now()
11241159

@@ -1308,6 +1343,11 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
13081343
}
13091344

13101345
if syncError = installer.Install(strategy); syncError != nil {
1346+
if install.IsErrorUnrecoverable(syncError) {
1347+
logger.Infof("Setting CSV reason to failed without retry: %v", syncError)
1348+
out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonComponentFailedNoRetry, fmt.Sprintf("install strategy failed: %s", syncError), now, a.recorder)
1349+
return
1350+
}
13111351
out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonComponentFailed, fmt.Sprintf("install strategy failed: %s", syncError), now, a.recorder)
13121352
return
13131353
}

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,34 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error {
5353
"namespace": op.GetNamespace(),
5454
})
5555

56+
previousRef := op.Status.ServiceAccountRef.DeepCopy()
5657
op, err := a.serviceAccountSyncer.SyncOperatorGroup(op)
5758
if err != nil {
5859
logger.Errorf("error updating service account - %v", err)
5960
return err
6061
}
62+
if op.Status.ServiceAccountRef != previousRef {
63+
crdList, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().List(labels.Everything())
64+
if err != nil {
65+
return err
66+
}
67+
for _, csv := range crdList {
68+
if group, ok := csv.GetAnnotations()[v1.OperatorGroupAnnotationKey]; !ok || group != op.GetName() {
69+
continue
70+
}
71+
if csv.Status.Reason == v1alpha1.CSVReasonComponentFailedNoRetry {
72+
csv.SetPhase(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonDetectedClusterChange, "Cluster resources changed state", a.now())
73+
_, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).UpdateStatus(csv)
74+
if err != nil {
75+
return err
76+
}
77+
if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil {
78+
return err
79+
}
80+
logger.Debug("Requeuing CSV due to detected service account change")
81+
}
82+
}
83+
}
6184

6285
targetNamespaces, err := a.updateNamespaceList(op)
6386
if err != nil {

pkg/lib/scoped/util.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ package scoped
22

33
import (
44
v1 "k8s.io/api/core/v1"
5+
rbacv1 "k8s.io/api/rbac/v1"
6+
"k8s.io/apimachinery/pkg/runtime"
7+
8+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
9+
)
10+
11+
const (
12+
roleKind = "Role"
13+
roleBindingKind = "RoleBinding"
514
)
615

716
// IsServiceAccountToken returns true if the secret is a valid api token for the service account
@@ -24,3 +33,26 @@ func IsServiceAccountToken(secret *v1.Secret, sa *v1.ServiceAccount) bool {
2433

2534
return true
2635
}
36+
37+
func IsObjectRBACRelated(obj interface{}) (related bool, object runtime.Object) {
38+
object, ok := obj.(runtime.Object)
39+
if !ok {
40+
return
41+
}
42+
43+
if err := ownerutil.InferGroupVersionKind(object); err != nil {
44+
return
45+
}
46+
47+
kind := object.GetObjectKind().GroupVersionKind().Kind
48+
switch kind {
49+
case roleKind:
50+
fallthrough
51+
case roleBindingKind:
52+
fallthrough
53+
case rbacv1.ServiceAccountKind:
54+
related = true
55+
}
56+
57+
return
58+
}

0 commit comments

Comments
 (0)