Skip to content

Commit b81e7ce

Browse files
committed
Allow non-CSV-owned ServiceAccounts to satisfy CSV requirements.
The existing check, intended to prevent a race condition that could occur during CSV upgrades, marks a ServiceAccount as NotPresent when it has at least one owner but is not owned by the current CSV. It's expected for the ServiceAccount "default" not to be have an ownerreference to a CSV, so the check can be relaxed to only consider owners of kind ClusterServiceVersion. This also combines two ServiceAccount checks that were independently added to address the same race condition into a single check. Signed-off-by: Ben Luddy <[email protected]>
1 parent 3787874 commit b81e7ce

File tree

2 files changed

+86
-14
lines changed

2 files changed

+86
-14
lines changed

pkg/controller/operators/olm/requirements.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,7 @@ func (a *Operator) permissionStatus(strategyDetailsDeployment *v1alpha1.Strategy
273273
if ownerutil.IsOwnedByKind(sa, v1alpha1.ClusterServiceVersionKind) && !ownerutil.IsOwnedBy(sa, csv) {
274274
met = false
275275
status.Status = v1alpha1.RequirementStatusReasonNotPresent
276-
status.Message = "Service account is stale"
277-
statusesSet[saName] = status
278-
continue
279-
}
280-
281-
// Check if the ServiceAccount is owned by CSV
282-
if len(sa.GetOwnerReferences()) != 0 && !ownerutil.IsOwnedBy(sa, csv) {
283-
met = false
284-
status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied
285-
status.Message = "Service account is not owned by this ClusterServiceVersion"
276+
status.Message = "Service account is owned by another ClusterServiceVersion"
286277
statusesSet[saName] = status
287278
continue
288279
}

pkg/controller/operators/olm/requirements_test.go

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"k8s.io/apimachinery/pkg/types"
1515

1616
"github.com/operator-framework/api/pkg/operators/v1alpha1"
17+
"github.com/stretchr/testify/assert"
1718
)
1819

1920
func TestRequirementAndPermissionStatus(t *testing.T) {
@@ -827,6 +828,85 @@ func TestRequirementAndPermissionStatus(t *testing.T) {
827828
},
828829
expectedError: nil,
829830
},
831+
{
832+
description: "RequirementMet/ServiceAccountOwnedByNonCSV",
833+
csv: csvWithUID(csv("csv",
834+
namespace,
835+
"0.0.0",
836+
"",
837+
installStrategy(
838+
"csv-dep",
839+
[]v1alpha1.StrategyDeploymentPermissions{
840+
{
841+
ServiceAccountName: "sa",
842+
},
843+
},
844+
nil,
845+
),
846+
nil,
847+
nil,
848+
v1alpha1.CSVPhasePending,
849+
), types.UID("csv-uid")),
850+
existingObjs: []runtime.Object{
851+
&corev1.ServiceAccount{
852+
ObjectMeta: metav1.ObjectMeta{
853+
Name: "sa",
854+
Namespace: namespace,
855+
UID: types.UID("sa"),
856+
OwnerReferences: []metav1.OwnerReference{
857+
{
858+
Kind: v1alpha1.SubscriptionKind, // arbitrary non-CSV kind
859+
UID: "non-csv",
860+
},
861+
},
862+
},
863+
},
864+
&rbacv1.Role{
865+
ObjectMeta: metav1.ObjectMeta{
866+
Name: "role",
867+
Namespace: namespace,
868+
},
869+
},
870+
&rbacv1.RoleBinding{
871+
ObjectMeta: metav1.ObjectMeta{
872+
Name: "roleBinding",
873+
Namespace: namespace,
874+
},
875+
Subjects: []rbacv1.Subject{
876+
{
877+
Kind: "ServiceAccount",
878+
Name: "sa",
879+
Namespace: namespace,
880+
},
881+
},
882+
RoleRef: rbacv1.RoleRef{
883+
APIGroup: "rbac.authorization.k8s.io",
884+
Kind: "Role",
885+
Name: "role",
886+
},
887+
},
888+
},
889+
existingExtObjs: nil,
890+
met: true,
891+
expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
892+
{"", "v1", "ServiceAccount", "sa"}: {
893+
Group: "",
894+
Version: "v1",
895+
Kind: "ServiceAccount",
896+
Name: "sa",
897+
Status: v1alpha1.RequirementStatusReasonPresent,
898+
Dependents: []v1alpha1.DependentStatus{},
899+
},
900+
{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv"}: {
901+
Group: "operators.coreos.com",
902+
Version: "v1alpha1",
903+
Kind: "ClusterServiceVersion",
904+
Name: "csv",
905+
Status: v1alpha1.RequirementStatusReasonPresent,
906+
},
907+
},
908+
expectedError: nil,
909+
},
830910
}
831911

832912
for _, test := range tests {
@@ -843,7 +923,8 @@ func TestRequirementAndPermissionStatus(t *testing.T) {
843923
require.Error(t, err)
844924
require.EqualError(t, test.expectedError, err.Error())
845925
}
846-
require.Equal(t, test.met, met)
926+
assert := assert.New(t)
927+
assert.Equal(test.met, met)
847928

848929
for _, status := range statuses {
849930
key := gvkn{
@@ -854,14 +935,14 @@ func TestRequirementAndPermissionStatus(t *testing.T) {
854935
}
855936

856937
expected, ok := test.expectedRequirementStatuses[key]
857-
require.True(t, ok, fmt.Sprintf("permission requirement status %+v found but not expected", key))
858-
require.Len(t, status.Dependents, len(expected.Dependents), "number of dependents is not what was expected")
938+
assert.True(ok, fmt.Sprintf("permission requirement status %+v found but not expected", key))
939+
assert.Len(status.Dependents, len(expected.Dependents), "number of dependents is not what was expected")
859940

860941
// Delete the requirement status to mark as found
861942
delete(test.expectedRequirementStatuses, key)
862943
}
863944

864-
require.Len(t, test.expectedRequirementStatuses, 0, "not all expected permission requirement statuses were found")
945+
assert.Len(test.expectedRequirementStatuses, 0, "not all expected permission requirement statuses were found")
865946
})
866947
}
867948
}

0 commit comments

Comments
 (0)