Skip to content

Commit a92a591

Browse files
committed
Unit testing
1 parent 0fa927b commit a92a591

File tree

4 files changed

+338
-3
lines changed

4 files changed

+338
-3
lines changed

api/v1/mdb/mongodbbuilder.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,22 @@ func (b *MongoDBBuilder) SetSecurityTLSEnabled() *MongoDBBuilder {
146146
return b
147147
}
148148

149+
func (b *MongoDBBuilder) SetRoles(roles []MongoDBRole) *MongoDBBuilder {
150+
if b.mdb.Spec.Security == nil {
151+
b.mdb.Spec.Security = &Security{}
152+
}
153+
b.mdb.Spec.Security.Roles = roles
154+
return b
155+
}
156+
157+
func (b *MongoDBBuilder) SetRoleRefs(roleRefs []MongoDBRoleRef) *MongoDBBuilder {
158+
if b.mdb.Spec.Security == nil {
159+
b.mdb.Spec.Security = &Security{}
160+
}
161+
b.mdb.Spec.Security.RoleRefs = roleRefs
162+
return b
163+
}
164+
149165
func (b *MongoDBBuilder) SetLabels(labels map[string]string) *MongoDBBuilder {
150166
b.mdb.Labels = labels
151167
return b
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package operator
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"k8s.io/apimachinery/pkg/types"
9+
"sigs.k8s.io/controller-runtime/pkg/client"
10+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
11+
12+
apiErrors "k8s.io/apimachinery/pkg/api/errors"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
15+
"github.com/mongodb/mongodb-kubernetes/api/v1/mdb"
16+
"github.com/mongodb/mongodb-kubernetes/api/v1/mdbmulti"
17+
rolev1 "github.com/mongodb/mongodb-kubernetes/api/v1/role"
18+
"github.com/mongodb/mongodb-kubernetes/controllers/operator/mock"
19+
"github.com/mongodb/mongodb-kubernetes/controllers/operator/workflow"
20+
"github.com/mongodb/mongodb-kubernetes/pkg/util"
21+
)
22+
23+
func TestReconcileClusterMongoDBRole(t *testing.T) {
24+
ctx := context.Background()
25+
role := DefaultClusterMongoDBRoleBuilder().Build()
26+
reconciler, _ := defaultRoleReconciler(ctx, role)
27+
28+
actual, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Name: role.Name}})
29+
if err != nil {
30+
return
31+
}
32+
33+
expected, _ := workflow.OK().ReconcileResult()
34+
35+
assert.Nil(t, err, "there should be no error on successful reconciliation")
36+
assert.Equal(t, expected, actual, "there should be a successful reconciliation if the password is a valid reference")
37+
}
38+
39+
func TestEnsureFinalizer(t *testing.T) {
40+
ctx := context.Background()
41+
role := DefaultClusterMongoDBRoleBuilder().Build()
42+
reconciler, fakeClient := defaultRoleReconciler(ctx, role)
43+
44+
_, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Name: role.Name}})
45+
assert.NoError(t, err)
46+
47+
err = fakeClient.Get(ctx, types.NamespacedName{Name: role.Name}, role)
48+
assert.NoError(t, err)
49+
50+
assert.Contains(t, role.GetFinalizers(), util.RoleFinalizer, "the finalizer should be present")
51+
}
52+
53+
func TestRoleIsRemovedWhenNoReferences(t *testing.T) {
54+
ctx := context.Background()
55+
role := DefaultClusterMongoDBRoleBuilder().Build()
56+
reconciler, fakeClient := defaultRoleReconciler(ctx, role)
57+
58+
_, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Name: role.Name}})
59+
assert.NoError(t, err)
60+
61+
err = fakeClient.Delete(ctx, role)
62+
assert.NoError(t, err)
63+
64+
newResult, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Name: role.Name}})
65+
66+
newExpected, _ := workflow.OK().ReconcileResult()
67+
68+
assert.Nil(t, err, "there should be no error on successful reconciliation")
69+
assert.Equal(t, newExpected, newResult, "there should be a successful reconciliation if the password is a valid reference")
70+
71+
err = fakeClient.Get(ctx, types.NamespacedName{Name: role.Name}, role)
72+
assert.True(t, apiErrors.IsNotFound(err), "the role should not exist")
73+
}
74+
75+
func TestRoleIsNotRemovedWhenReferenced(t *testing.T) {
76+
ctx := context.Background()
77+
role := DefaultClusterMongoDBRoleBuilder().Build()
78+
79+
roleRefs := []mdb.MongoDBRoleRef{
80+
{
81+
Name: role.Name,
82+
Kind: util.ClusterMongoDBRoleKind,
83+
},
84+
}
85+
cases := []struct {
86+
name string
87+
resource client.Object
88+
}{
89+
{
90+
name: "Replicaset",
91+
resource: mdb.NewDefaultReplicaSetBuilder().SetRoleRefs(roleRefs).Build(),
92+
},
93+
{
94+
name: "Standalone",
95+
resource: mdb.NewStandaloneBuilder().SetRoleRefs(roleRefs).Build(),
96+
},
97+
{
98+
name: "Sharded cluster",
99+
resource: mdb.NewDefaultShardedClusterBuilder().SetRoleRefs(roleRefs).Build(),
100+
},
101+
{
102+
name: "Multi cluster sharded",
103+
resource: mdb.NewDefaultMultiShardedClusterBuilder().SetRoleRefs(roleRefs).Build(),
104+
},
105+
{
106+
name: "Multi cluster replicaset",
107+
resource: mdbmulti.DefaultMultiReplicaSetBuilder().SetRoleRefs(roleRefs).Build(),
108+
},
109+
}
110+
111+
for _, c := range cases {
112+
t.Run(c.name, func(t *testing.T) {
113+
role = DefaultClusterMongoDBRoleBuilder().Build()
114+
reconciler, fakeClient := defaultRoleReconciler(ctx, role, c.resource)
115+
116+
// Add finalizer
117+
_, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Name: role.Name}})
118+
assert.NoError(t, err)
119+
120+
// Delete resource, should fail since it is still referenced
121+
err = fakeClient.Delete(ctx, role)
122+
assert.NoError(t, err)
123+
124+
// Should not remove the finalizer
125+
_, _ = reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Name: role.Name}})
126+
127+
err = fakeClient.Get(ctx, types.NamespacedName{Name: role.Name}, role)
128+
assert.NoError(t, err, "the role should still exist")
129+
assert.NotEmpty(t, role.GetFinalizers(), "the finalizer should still be present")
130+
})
131+
}
132+
}
133+
134+
func defaultRoleReconciler(ctx context.Context, objects ...client.Object) (*ClusterMongoDBRoleReconciler, client.Client) {
135+
kubeClient := mock.NewEmptyFakeClientBuilder().
136+
WithObjects(objects...).
137+
WithIndex(&mdb.MongoDB{}, ClusterMongoDBRoleIndexForMdb, findRolesForMongoDB).
138+
WithIndex(&mdbmulti.MongoDBMultiCluster{}, ClusterMongoDBRoleIndexForMdbMulti, findRolesForMongoDBMultiCluster).
139+
Build()
140+
141+
return newClusterMongoDBRoleReconciler(ctx, kubeClient), kubeClient
142+
}
143+
144+
type ClusterMongoDBRoleBuilder struct {
145+
name string
146+
finalizers []string
147+
annotations map[string]string
148+
mongoDBRole mdb.MongoDBRole
149+
}
150+
151+
func DefaultClusterMongoDBRoleBuilder() *ClusterMongoDBRoleBuilder {
152+
return &ClusterMongoDBRoleBuilder{
153+
name: "default-role",
154+
finalizers: []string{},
155+
mongoDBRole: mdb.MongoDBRole{
156+
Role: "default-role",
157+
AuthenticationRestrictions: nil,
158+
Db: "admin",
159+
Privileges: nil,
160+
Roles: []mdb.InheritedRole{
161+
{
162+
Role: "readWrite",
163+
Db: "admin",
164+
},
165+
},
166+
},
167+
annotations: map[string]string{},
168+
}
169+
}
170+
171+
func (b *ClusterMongoDBRoleBuilder) SetName(name string) *ClusterMongoDBRoleBuilder {
172+
b.name = name
173+
return b
174+
}
175+
176+
func (b *ClusterMongoDBRoleBuilder) AddFinalizer(finalizer string) *ClusterMongoDBRoleBuilder {
177+
b.finalizers = append(b.finalizers, finalizer)
178+
return b
179+
}
180+
181+
func (b *ClusterMongoDBRoleBuilder) SetMongoDBRole(role mdb.MongoDBRole) *ClusterMongoDBRoleBuilder {
182+
b.mongoDBRole = role
183+
return b
184+
}
185+
186+
func (b *ClusterMongoDBRoleBuilder) AddAnnotation(key, value string) *ClusterMongoDBRoleBuilder {
187+
b.annotations[key] = value
188+
return b
189+
}
190+
191+
func (b *ClusterMongoDBRoleBuilder) Build() *rolev1.ClusterMongoDBRole {
192+
return &rolev1.ClusterMongoDBRole{
193+
ObjectMeta: metav1.ObjectMeta{
194+
Name: b.name,
195+
Finalizers: b.finalizers,
196+
Annotations: b.annotations,
197+
},
198+
Spec: rolev1.ClusterMongoDBRoleSpec{
199+
MongoDBRole: b.mongoDBRole,
200+
},
201+
}
202+
}

controllers/operator/common_controller_test.go

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,121 @@ func TestReadSubjectNoCertificate(t *testing.T) {
265265
assertSubjectFromFileFails(t, "testdata/certificates/just_key")
266266
}
267267

268+
func TestFailWhenRoleAndRoleRefsAreConfigured(t *testing.T) {
269+
ctx := context.Background()
270+
customRole := mdbv1.MongoDBRole{
271+
Role: "foo",
272+
AuthenticationRestrictions: []mdbv1.AuthenticationRestriction{},
273+
Db: "admin",
274+
Roles: []mdbv1.InheritedRole{{
275+
Db: "admin",
276+
Role: "readWriteAnyDatabase",
277+
}},
278+
}
279+
roleResource := DefaultClusterMongoDBRoleBuilder().Build()
280+
roleRef := mdbv1.MongoDBRoleRef{
281+
Name: roleResource.Name,
282+
Kind: util.ClusterMongoDBRoleKind,
283+
}
284+
assert.Nil(t, customRole.Privileges)
285+
rs := mdbv1.NewDefaultReplicaSetBuilder().SetRoles([]mdbv1.MongoDBRole{customRole}).SetRoleRefs([]mdbv1.MongoDBRoleRef{roleRef}).Build()
286+
287+
kubeClient, omConnectionFactory := mock.NewDefaultFakeClient()
288+
controller := NewReconcileCommonController(ctx, kubeClient)
289+
mockOm, _ := prepareConnection(ctx, controller, omConnectionFactory.GetConnectionFunc, t)
290+
291+
result := controller.ensureRoles(ctx, rs.Spec.Security.Roles, rs.Spec.Security.RoleRefs, mockOm, kube.ObjectKeyFromApiObject(rs), &zap.SugaredLogger{})
292+
assert.False(t, result.IsOK())
293+
assert.Equal(t, status.PhaseFailed, result.Phase())
294+
295+
ac, err := mockOm.ReadAutomationConfig()
296+
assert.NoError(t, err)
297+
roles, ok := ac.Deployment["roles"].([]mdbv1.MongoDBRole)
298+
assert.False(t, ok)
299+
assert.Empty(t, roles)
300+
}
301+
302+
func TestRoleRefsAreAdded(t *testing.T) {
303+
ctx := context.Background()
304+
roleResource := DefaultClusterMongoDBRoleBuilder().Build()
305+
roleRefs := []mdbv1.MongoDBRoleRef{
306+
{
307+
Name: roleResource.Name,
308+
Kind: util.ClusterMongoDBRoleKind,
309+
},
310+
}
311+
rs := mdbv1.NewDefaultReplicaSetBuilder().SetRoleRefs(roleRefs).Build()
312+
313+
kubeClient, omConnectionFactory := mock.NewDefaultFakeClient()
314+
controller := NewReconcileCommonController(ctx, kubeClient)
315+
mockOm, _ := prepareConnection(ctx, controller, omConnectionFactory.GetConnectionFunc, t)
316+
317+
_ = kubeClient.Create(ctx, roleResource)
318+
319+
controller.ensureRoles(ctx, rs.Spec.Security.Roles, rs.Spec.Security.RoleRefs, mockOm, kube.ObjectKeyFromApiObject(rs), &zap.SugaredLogger{})
320+
321+
ac, err := mockOm.ReadAutomationConfig()
322+
assert.NoError(t, err)
323+
roles, ok := ac.Deployment["roles"].([]mdbv1.MongoDBRole)
324+
assert.True(t, ok)
325+
assert.NotNil(t, roles[0].Privileges)
326+
assert.Len(t, roles, 1)
327+
}
328+
329+
func TestErrorWhenRoleDoesNotExist(t *testing.T) {
330+
ctx := context.Background()
331+
roleResource := mdbv1.NewDefaultReplicaSetBuilder().Build()
332+
roleRefs := []mdbv1.MongoDBRoleRef{
333+
{
334+
Name: roleResource.Name,
335+
Kind: "WrongMongoDBRoleReference",
336+
},
337+
}
338+
rs := mdbv1.NewDefaultReplicaSetBuilder().SetRoleRefs(roleRefs).Build()
339+
340+
kubeClient, omConnectionFactory := mock.NewDefaultFakeClient()
341+
controller := NewReconcileCommonController(ctx, kubeClient)
342+
mockOm, _ := prepareConnection(ctx, controller, omConnectionFactory.GetConnectionFunc, t)
343+
344+
_ = kubeClient.Create(ctx, roleResource)
345+
346+
result := controller.ensureRoles(ctx, rs.Spec.Security.Roles, rs.Spec.Security.RoleRefs, mockOm, kube.ObjectKeyFromApiObject(rs), &zap.SugaredLogger{})
347+
assert.False(t, result.IsOK())
348+
assert.Equal(t, status.PhaseFailed, result.Phase())
349+
350+
ac, err := mockOm.ReadAutomationConfig()
351+
assert.NoError(t, err)
352+
roles, ok := ac.Deployment["roles"].([]mdbv1.MongoDBRole)
353+
assert.False(t, ok)
354+
assert.Empty(t, roles)
355+
}
356+
357+
func TestErrorWhenRoleRefIsWrong(t *testing.T) {
358+
ctx := context.Background()
359+
roleResource := DefaultClusterMongoDBRoleBuilder().Build()
360+
roleRefs := []mdbv1.MongoDBRoleRef{
361+
{
362+
Name: roleResource.Name,
363+
Kind: util.ClusterMongoDBRoleKind,
364+
},
365+
}
366+
rs := mdbv1.NewDefaultReplicaSetBuilder().SetRoleRefs(roleRefs).Build()
367+
368+
kubeClient, omConnectionFactory := mock.NewDefaultFakeClient()
369+
controller := NewReconcileCommonController(ctx, kubeClient)
370+
mockOm, _ := prepareConnection(ctx, controller, omConnectionFactory.GetConnectionFunc, t)
371+
372+
result := controller.ensureRoles(ctx, rs.Spec.Security.Roles, rs.Spec.Security.RoleRefs, mockOm, kube.ObjectKeyFromApiObject(rs), &zap.SugaredLogger{})
373+
assert.False(t, result.IsOK())
374+
assert.Equal(t, status.PhaseFailed, result.Phase())
375+
376+
ac, err := mockOm.ReadAutomationConfig()
377+
assert.NoError(t, err)
378+
roles, ok := ac.Deployment["roles"].([]mdbv1.MongoDBRole)
379+
assert.False(t, ok)
380+
assert.Empty(t, roles)
381+
}
382+
268383
func TestDontSendNilPrivileges(t *testing.T) {
269384
ctx := context.Background()
270385
customRole := mdbv1.MongoDBRole{
@@ -281,7 +396,7 @@ func TestDontSendNilPrivileges(t *testing.T) {
281396
kubeClient, omConnectionFactory := mock.NewDefaultFakeClient()
282397
controller := NewReconcileCommonController(ctx, kubeClient)
283398
mockOm, _ := prepareConnection(ctx, controller, omConnectionFactory.GetConnectionFunc, t)
284-
ensureRoles(rs.Spec.Security.Roles, mockOm, &zap.SugaredLogger{})
399+
controller.ensureRoles(ctx, rs.Spec.Security.Roles, rs.Spec.Security.RoleRefs, mockOm, kube.ObjectKeyFromApiObject(rs), &zap.SugaredLogger{})
285400
ac, err := mockOm.ReadAutomationConfig()
286401
assert.NoError(t, err)
287402
roles, ok := ac.Deployment["roles"].([]mdbv1.MongoDBRole)

controllers/operator/mongodbuser_controller_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1313

1414
corev1 "k8s.io/api/core/v1"
15+
apiErrors "k8s.io/apimachinery/pkg/api/errors"
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617

1718
mdbv1 "github.com/mongodb/mongodb-kubernetes/api/v1/mdb"
@@ -402,7 +403,7 @@ func TestFinalizerIsAdded_WhenUserIsCreated(t *testing.T) {
402403

403404
_ = client.Get(ctx, kube.ObjectKey(user.Namespace, user.Name), user)
404405

405-
assert.Contains(t, user.GetFinalizers(), util.Finalizer)
406+
assert.Contains(t, user.GetFinalizers(), util.UserFinalizer)
406407
}
407408

408409
func TestUserReconciler_SavesConnectionStringForMultiShardedCluster(t *testing.T) {
@@ -497,7 +498,8 @@ func TestFinalizerIsRemoved_WhenUserIsDeleted(t *testing.T) {
497498
assert.Nil(t, err, "there should be no error on successful reconciliation")
498499
assert.Equal(t, newExpected, newResult, "there should be a successful reconciliation if the password is a valid reference")
499500

500-
assert.Empty(t, user.GetFinalizers())
501+
err = client.Get(ctx, kube.ObjectKey(user.Namespace, user.Name), user)
502+
assert.True(t, apiErrors.IsNotFound(err), "the user should not exist")
501503
}
502504

503505
// BuildAuthenticationEnabledReplicaSet returns a AutomationConfig after creating a Replica Set with a set of

0 commit comments

Comments
 (0)