Skip to content

Commit fbacf7b

Browse files
committed
fix(rbac): implement registry rolebinding finalization for OpenShift
1 parent ec6de18 commit fbacf7b

2 files changed

Lines changed: 111 additions & 3 deletions

File tree

pkg/provision/workspace/rbac/finalize.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package rbac
1616
import (
1717
"github.com/devfile/devworkspace-operator/pkg/common"
1818
"github.com/devfile/devworkspace-operator/pkg/constants"
19+
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
1920
"github.com/devfile/devworkspace-operator/pkg/provision/sync"
2021
"sigs.k8s.io/controller-runtime/pkg/client"
2122

@@ -42,11 +43,16 @@ func FinalizeRBAC(workspace *common.DevWorkspaceWithConfig, api sync.ClusterAPI)
4243
if err := deleteRolebinding(rolebindingName, workspace.Namespace, api); err != nil {
4344
return err
4445
}
45-
return nil
46-
}
47-
if err := removeServiceAccountFromRolebinding(saName, workspace.Namespace, rolebindingName, api); err != nil {
46+
} else if err := removeServiceAccountFromRolebinding(saName, workspace.Namespace, rolebindingName, api); err != nil {
4847
return err
4948
}
49+
50+
if infrastructure.IsOpenShift() {
51+
if err := finalizeRegistryRBAC(workspace, api); err != nil {
52+
return err
53+
}
54+
}
55+
5056
return nil
5157
}
5258

@@ -74,6 +80,19 @@ func finalizeSCCRBAC(workspace *common.DevWorkspaceWithConfig, api sync.ClusterA
7480
return nil
7581
}
7682

83+
func finalizeRegistryRBAC(workspace *common.DevWorkspaceWithConfig, api sync.ClusterAPI) error {
84+
saName := common.ServiceAccountName(workspace)
85+
rolebindingName := common.RegistryImagePullerRolebindingName(workspace.Namespace)
86+
numWorkspaces, err := countNonDeletedWorkspaces(workspace.Namespace, api)
87+
if err != nil {
88+
return err
89+
}
90+
if numWorkspaces == 0 {
91+
return deleteRolebinding(rolebindingName, workspace.Namespace, api)
92+
}
93+
return removeServiceAccountFromRolebinding(saName, workspace.Namespace, rolebindingName, api)
94+
}
95+
7796
func countNonDeletedWorkspaces(namespace string, api sync.ClusterAPI) (int, error) {
7897
count := 0
7998
allWorkspaces := &dw.DevWorkspaceList{}

pkg/provision/workspace/rbac/finalize_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,85 @@ func TestFinalizeDoesNothingWhenRolebindingDoesNotExist(t *testing.T) {
133133
}
134134
}
135135

136+
func TestShouldRemoveWorkspaceSAFromRegistryRolebindingWhenDeletedOnOpenShift(t *testing.T) {
137+
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
138+
testdw := getTestDevWorkspace("test-devworkspace")
139+
testdw2 := getTestDevWorkspace("test-devworkspace2")
140+
testdw.DeletionTimestamp = &metav1.Time{Time: time.Now()}
141+
testdw.DevWorkspace.ObjectMeta.Finalizers = []string{"test-finalizer"}
142+
testdwSAName := common.ServiceAccountName(testdw)
143+
testdw2SAName := common.ServiceAccountName(testdw2)
144+
registryRolebinding := newRegistryImagePullerRolebinding(
145+
rbacv1.Subject{
146+
Kind: rbacv1.ServiceAccountKind,
147+
Name: testdwSAName,
148+
Namespace: testNamespace,
149+
},
150+
rbacv1.Subject{
151+
Kind: rbacv1.ServiceAccountKind,
152+
Name: testdw2SAName,
153+
Namespace: testNamespace,
154+
})
155+
api := getTestClusterAPI(t, testdw.DevWorkspace, testdw2.DevWorkspace, registryRolebinding)
156+
retryErr := &dwerrors.RetryError{}
157+
err := FinalizeRBAC(testdw, api)
158+
if assert.Error(t, err, "Should return error to indicate registry rolebinding updated") {
159+
assert.ErrorAs(t, err, &retryErr, "Error should be RetryError")
160+
}
161+
err = FinalizeRBAC(testdw, api)
162+
assert.NoError(t, err, "Should not return error once registry rolebinding is in sync")
163+
164+
actualRolebinding := &rbacv1.RoleBinding{}
165+
err = api.Client.Get(api.Ctx, types.NamespacedName{
166+
Name: common.RegistryImagePullerRolebindingName(testNamespace),
167+
Namespace: testNamespace,
168+
}, actualRolebinding)
169+
assert.NoError(t, err, "Unexpected test error getting registry rolebinding")
170+
assert.False(t, testHasSubject(testdwSAName, testNamespace, actualRolebinding), "Should remove deleted workspace SA from registry rolebinding subjects")
171+
assert.True(t, testHasSubject(testdw2SAName, testNamespace, actualRolebinding), "Should leave other workspace SA in registry rolebinding subjects")
172+
}
173+
174+
func TestDeletesRegistryRolebindingWhenLastWorkspaceIsDeletedOnOpenShift(t *testing.T) {
175+
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
176+
testdw := getTestDevWorkspace("test-devworkspace")
177+
testdw.DeletionTimestamp = &metav1.Time{Time: time.Now()}
178+
testdw.DevWorkspace.ObjectMeta.Finalizers = []string{"test-finalizer"}
179+
registryRolebinding := newRegistryImagePullerRolebinding(rbacv1.Subject{
180+
Kind: rbacv1.ServiceAccountKind,
181+
Name: common.ServiceAccountName(testdw),
182+
Namespace: testNamespace,
183+
})
184+
api := getTestClusterAPI(t, testdw.DevWorkspace, registryRolebinding)
185+
retryErr := &dwerrors.RetryError{}
186+
err := FinalizeRBAC(testdw, api)
187+
if assert.Error(t, err, "Should return error to indicate registry rolebinding deleted") {
188+
assert.ErrorAs(t, err, &retryErr, "Error should be RetryError")
189+
assert.Regexp(t, fmt.Sprintf("deleted rolebinding .* in namespace %s", testNamespace), err.Error())
190+
}
191+
err = FinalizeRBAC(testdw, api)
192+
assert.NoError(t, err, "Should not return error once registry rolebinding is deleted")
193+
194+
actualRolebinding := &rbacv1.RoleBinding{}
195+
err = api.Client.Get(api.Ctx, types.NamespacedName{
196+
Name: common.RegistryImagePullerRolebindingName(testNamespace),
197+
Namespace: testNamespace,
198+
}, actualRolebinding)
199+
if assert.Error(t, err, "Expect error when getting deleted registry rolebinding") {
200+
assert.True(t, k8sErrors.IsNotFound(err), "Error should have IsNotFound type")
201+
}
202+
}
203+
204+
func TestFinalizeDoesNothingWhenRegistryRolebindingDoesNotExistOnOpenShift(t *testing.T) {
205+
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
206+
testdw := getTestDevWorkspace("test-devworkspace")
207+
testdw2 := getTestDevWorkspace("test-devworkspace2")
208+
testdw.DeletionTimestamp = &metav1.Time{Time: time.Now()}
209+
testdw.DevWorkspace.ObjectMeta.Finalizers = []string{"test-finalizer"}
210+
api := getTestClusterAPI(t, testdw.DevWorkspace, testdw2.DevWorkspace)
211+
err := FinalizeRBAC(testdw, api)
212+
assert.NoError(t, err, "Should not return error when registry rolebinding does not exist")
213+
}
214+
136215
func TestDeletesSCCRoleAndRolebindingWhenLastWorkspaceIsDeleted(t *testing.T) {
137216
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
138217
testdw := getTestDevWorkspaceWithAttributes(t, "test-devworkspace", constants.WorkspaceSCCAttribute, testSCCName)
@@ -237,3 +316,13 @@ func TestFinalizeDoesNothingWhenSCCRolebindingDoesNotExist(t *testing.T) {
237316
assert.True(t, k8sErrors.IsNotFound(err), "Error should have IsNotFound type")
238317
}
239318
}
319+
320+
func newRegistryImagePullerRolebinding(subjects ...rbacv1.Subject) *rbacv1.RoleBinding {
321+
rolebinding := generateDefaultRolebinding(
322+
common.RegistryImagePullerRolebindingName(testNamespace),
323+
testNamespace,
324+
common.RegistryImagePullerRoleName(),
325+
constants.RbacClusterRoleKind)
326+
rolebinding.Subjects = append(rolebinding.Subjects, subjects...)
327+
return rolebinding
328+
}

0 commit comments

Comments
 (0)