Skip to content

Commit 299547d

Browse files
committed
Add tests for role/rolebinding provisioning
Signed-off-by: Angel Misevski <[email protected]>
1 parent 11ae3e4 commit 299547d

File tree

5 files changed

+847
-0
lines changed

5 files changed

+847
-0
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Copyright (c) 2019-2022 Red Hat, Inc.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package rbac
15+
16+
import (
17+
"context"
18+
"fmt"
19+
"testing"
20+
21+
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
22+
"github.com/devfile/api/v2/pkg/attributes"
23+
"github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
24+
"github.com/devfile/devworkspace-operator/pkg/common"
25+
"github.com/devfile/devworkspace-operator/pkg/constants"
26+
"github.com/devfile/devworkspace-operator/pkg/provision/sync"
27+
testlog "github.com/go-logr/logr/testing"
28+
"github.com/stretchr/testify/assert"
29+
rbacv1 "k8s.io/api/rbac/v1"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
"k8s.io/apimachinery/pkg/runtime"
32+
"k8s.io/apimachinery/pkg/types"
33+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
34+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
35+
"sigs.k8s.io/controller-runtime/pkg/client"
36+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
37+
)
38+
39+
const (
40+
testNamespace = "test-namespace"
41+
testSCCName = "test-scc"
42+
)
43+
44+
var (
45+
scheme = runtime.NewScheme()
46+
)
47+
48+
func init() {
49+
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
50+
utilruntime.Must(v1alpha1.AddToScheme(scheme))
51+
utilruntime.Must(dw.AddToScheme(scheme))
52+
}
53+
54+
var (
55+
oldRole = &rbacv1.Role{
56+
ObjectMeta: metav1.ObjectMeta{
57+
Name: common.OldWorkspaceRoleName(),
58+
Namespace: testNamespace,
59+
},
60+
Rules: []rbacv1.PolicyRule{},
61+
}
62+
oldRolebinding = &rbacv1.RoleBinding{
63+
ObjectMeta: metav1.ObjectMeta{
64+
Name: common.OldWorkspaceRolebindingName(),
65+
Namespace: testNamespace,
66+
},
67+
}
68+
newRole = &rbacv1.Role{
69+
ObjectMeta: metav1.ObjectMeta{
70+
Name: common.WorkspaceRoleName(),
71+
Namespace: testNamespace,
72+
},
73+
Rules: []rbacv1.PolicyRule{},
74+
}
75+
newRolebinding = &rbacv1.RoleBinding{
76+
ObjectMeta: metav1.ObjectMeta{
77+
Name: common.WorkspaceRolebindingName(),
78+
Namespace: testNamespace,
79+
},
80+
RoleRef: rbacv1.RoleRef{
81+
Kind: "Role",
82+
Name: common.WorkspaceRoleName(),
83+
},
84+
}
85+
newSCCRole = &rbacv1.Role{
86+
ObjectMeta: metav1.ObjectMeta{
87+
Name: common.WorkspaceSCCRoleName(testSCCName),
88+
Namespace: testNamespace,
89+
},
90+
Rules: []rbacv1.PolicyRule{},
91+
}
92+
newSCCRolebinding = &rbacv1.RoleBinding{
93+
ObjectMeta: metav1.ObjectMeta{
94+
Name: common.WorkspaceSCCRolebindingName(testSCCName),
95+
Namespace: testNamespace,
96+
},
97+
RoleRef: rbacv1.RoleRef{
98+
Kind: "Role",
99+
Name: common.WorkspaceSCCRoleName(testSCCName),
100+
},
101+
}
102+
)
103+
104+
func TestSyncRBAC(t *testing.T) {
105+
testdw1 := getTestDevWorkspaceWithAttributes(t, "test-devworkspace", constants.WorkspaceSCCAttribute, testSCCName)
106+
testdw2 := getTestDevWorkspaceWithAttributes(t, "test-devworkspace2", constants.WorkspaceSCCAttribute, testSCCName)
107+
testdw1SAName := common.ServiceAccountName(testdw1.Status.DevWorkspaceId)
108+
testdw2SAName := common.ServiceAccountName(testdw2.Status.DevWorkspaceId)
109+
api := getTestClusterAPI(t, testdw1.DevWorkspace, testdw2.DevWorkspace, oldRole, oldRolebinding)
110+
// Keep calling SyncRBAC until error returned is nil, to account for multiple steps
111+
iterCount := 0
112+
maxIters := 30
113+
retryErr := &RetryError{}
114+
for err := SyncRBAC(testdw1, api); err != nil; err = SyncRBAC(testdw1, api) {
115+
iterCount += 1
116+
if err == nil {
117+
break
118+
}
119+
if !assert.ErrorAs(t, err, &retryErr, "Unexpected error from SyncRBAC: %s", err) {
120+
return
121+
}
122+
if !assert.LessOrEqual(t, iterCount, maxIters, fmt.Sprintf("SyncRBAC did not sync everything within %d iterations", maxIters)) {
123+
return
124+
}
125+
}
126+
for err := SyncRBAC(testdw2, api); err != nil; err = SyncRBAC(testdw2, api) {
127+
iterCount += 1
128+
if err == nil {
129+
break
130+
}
131+
if !assert.ErrorAs(t, err, &retryErr, "Unexpected error from SyncRBAC: %s", err) {
132+
return
133+
}
134+
if !assert.LessOrEqual(t, iterCount, maxIters, fmt.Sprintf("SyncRBAC did not sync everything within %d iterations", maxIters)) {
135+
return
136+
}
137+
}
138+
actualRole := &rbacv1.Role{}
139+
err := api.Client.Get(api.Ctx, types.NamespacedName{
140+
Name: common.WorkspaceRoleName(),
141+
Namespace: testNamespace,
142+
}, actualRole)
143+
assert.NoError(t, err, "Role should be created")
144+
145+
actualSCCRole := &rbacv1.Role{}
146+
err = api.Client.Get(api.Ctx, types.NamespacedName{
147+
Name: common.WorkspaceSCCRoleName(testSCCName),
148+
Namespace: testNamespace,
149+
}, actualSCCRole)
150+
assert.NoError(t, err, "SCC Role should be created")
151+
152+
actualRolebinding := &rbacv1.RoleBinding{}
153+
err = api.Client.Get(api.Ctx, types.NamespacedName{
154+
Name: common.WorkspaceRolebindingName(),
155+
Namespace: testNamespace,
156+
}, actualRolebinding)
157+
assert.NoError(t, err, "Role should be created")
158+
assert.True(t, testHasSubject(testdw1SAName, testNamespace, actualRolebinding), "Should have testdw1 SA as subject")
159+
assert.True(t, testHasSubject(testdw2SAName, testNamespace, actualRolebinding), "Should have testdw2 SA as subject")
160+
161+
actualSCCRolebinding := &rbacv1.RoleBinding{}
162+
err = api.Client.Get(api.Ctx, types.NamespacedName{
163+
Name: common.WorkspaceSCCRolebindingName(testSCCName),
164+
Namespace: testNamespace,
165+
}, actualSCCRolebinding)
166+
assert.NoError(t, err, "SCC Rolebindind should be created")
167+
assert.True(t, testHasSubject(testdw1SAName, testNamespace, actualSCCRolebinding), "Should have testdw1 SA as subject")
168+
assert.True(t, testHasSubject(testdw2SAName, testNamespace, actualSCCRolebinding), "Should have testdw2 SA as subject")
169+
}
170+
171+
func getTestClusterAPI(t *testing.T, initialObjects ...client.Object) sync.ClusterAPI {
172+
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initialObjects...).Build()
173+
return sync.ClusterAPI{
174+
Ctx: context.Background(),
175+
Client: fakeClient,
176+
Scheme: scheme,
177+
Logger: testlog.TestLogger{T: t},
178+
}
179+
}
180+
181+
func getTestDevWorkspace(id string) *common.DevWorkspaceWithConfig {
182+
return &common.DevWorkspaceWithConfig{
183+
DevWorkspace: &dw.DevWorkspace{
184+
ObjectMeta: metav1.ObjectMeta{
185+
Name: id,
186+
Namespace: testNamespace,
187+
},
188+
189+
Status: dw.DevWorkspaceStatus{
190+
DevWorkspaceId: id,
191+
},
192+
},
193+
}
194+
}
195+
196+
func getTestDevWorkspaceWithAttributes(t *testing.T, id string, keysAndValues ...string) *common.DevWorkspaceWithConfig {
197+
attr := attributes.Attributes{}
198+
if len(keysAndValues)%2 != 0 {
199+
t.Fatalf("Invalid keysAndValues for getTestDevWorkspaceWithAttributes")
200+
}
201+
for i := 0; i < len(keysAndValues); i += 2 {
202+
attr.PutString(keysAndValues[i], keysAndValues[i+1])
203+
}
204+
return &common.DevWorkspaceWithConfig{
205+
DevWorkspace: &dw.DevWorkspace{
206+
ObjectMeta: metav1.ObjectMeta{
207+
Name: id,
208+
Namespace: testNamespace,
209+
},
210+
Spec: dw.DevWorkspaceSpec{
211+
Template: dw.DevWorkspaceTemplateSpec{
212+
DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{
213+
Attributes: attr,
214+
},
215+
},
216+
},
217+
Status: dw.DevWorkspaceStatus{
218+
DevWorkspaceId: id,
219+
},
220+
},
221+
}
222+
}

0 commit comments

Comments
 (0)