Skip to content

Commit 6c59b78

Browse files
authored
requeue independent resource to reconcile after 15 minutes (#1854)
1 parent f501d63 commit 6c59b78

File tree

3 files changed

+183
-2
lines changed

3 files changed

+183
-2
lines changed

pkg/controller/atlasdatabaseuser/atlasdatabaseuser_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ func (r *AtlasDatabaseUserReconciler) ready(ctx *workflow.Context, atlasDatabase
202202
EnsureStatusOption(status.AtlasDatabaseUserNameOption(atlasDatabaseUser.Spec.Username)).
203203
EnsureStatusOption(status.AtlasDatabaseUserPasswordVersion(passwordVersion))
204204

205+
if atlasDatabaseUser.Spec.ExternalProjectRef != nil {
206+
return workflow.Requeue(workflow.StandaloneResourceRequeuePeriod).ReconcileResult()
207+
}
208+
205209
return workflow.OK().ReconcileResult()
206210
}
207211

pkg/controller/atlasdatabaseuser/atlasdatabaseuser_controller_test.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import (
55
"errors"
66
"reflect"
77
"testing"
8+
"time"
89

10+
"github.com/google/go-cmp/cmp"
11+
"github.com/google/go-cmp/cmp/cmpopts"
912
"github.com/stretchr/testify/assert"
1013
"github.com/stretchr/testify/mock"
1114
"go.mongodb.org/atlas-sdk/v20231115008/admin"
@@ -283,6 +286,172 @@ func TestTerminate(t *testing.T) {
283286
}
284287
}
285288

289+
func TestReady(t *testing.T) {
290+
tests := map[string]struct {
291+
dbUser *akov2.AtlasDatabaseUser
292+
passwordVersion string
293+
interceptors interceptor.Funcs
294+
295+
expectedResult ctrl.Result
296+
expectedConditions []api.Condition
297+
}{
298+
"fail to set finalizer": {
299+
dbUser: &akov2.AtlasDatabaseUser{
300+
ObjectMeta: metav1.ObjectMeta{
301+
Name: "user1",
302+
Namespace: "default",
303+
},
304+
Spec: akov2.AtlasDatabaseUserSpec{
305+
Project: &common.ResourceRefNamespaced{
306+
Name: "my-project",
307+
Namespace: "default",
308+
},
309+
Username: "user1",
310+
PasswordSecret: &common.ResourceRef{
311+
Name: "user-pass",
312+
},
313+
DatabaseName: "admin",
314+
},
315+
},
316+
passwordVersion: "1",
317+
interceptors: interceptor.Funcs{
318+
Patch: func(ctx context.Context, client client.WithWatch, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
319+
return errors.New("failed to set finalizer")
320+
},
321+
},
322+
expectedResult: workflow.Terminate(workflow.AtlasFinalizerNotSet, "").ReconcileResult(),
323+
expectedConditions: []api.Condition{
324+
api.FalseCondition(api.DatabaseUserReadyType).
325+
WithReason(string(workflow.AtlasFinalizerNotSet)).
326+
WithMessageRegexp("failed to set finalizer"),
327+
},
328+
},
329+
"fail to set last applied config": {
330+
dbUser: &akov2.AtlasDatabaseUser{
331+
ObjectMeta: metav1.ObjectMeta{
332+
Name: "user1",
333+
Namespace: "default",
334+
},
335+
Spec: akov2.AtlasDatabaseUserSpec{
336+
Project: &common.ResourceRefNamespaced{
337+
Name: "my-project",
338+
Namespace: "default",
339+
},
340+
Username: "user1",
341+
PasswordSecret: &common.ResourceRef{
342+
Name: "user-pass",
343+
},
344+
DatabaseName: "admin",
345+
},
346+
},
347+
passwordVersion: "1",
348+
interceptors: interceptor.Funcs{
349+
Patch: func(ctx context.Context, client client.WithWatch, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
350+
if patch.Type() == types.JSONPatchType {
351+
return nil
352+
}
353+
354+
return errors.New("failed to set last applied config")
355+
},
356+
},
357+
expectedResult: workflow.Terminate(workflow.Internal, "").ReconcileResult(),
358+
expectedConditions: []api.Condition{
359+
api.FalseCondition(api.DatabaseUserReadyType).
360+
WithReason(string(workflow.Internal)).
361+
WithMessageRegexp("failed to set last applied config"),
362+
},
363+
},
364+
"don't requeue when it's a linked resource": {
365+
dbUser: &akov2.AtlasDatabaseUser{
366+
ObjectMeta: metav1.ObjectMeta{
367+
Name: "user1",
368+
Namespace: "default",
369+
},
370+
Spec: akov2.AtlasDatabaseUserSpec{
371+
Project: &common.ResourceRefNamespaced{
372+
Name: "my-project",
373+
Namespace: "default",
374+
},
375+
Username: "user1",
376+
PasswordSecret: &common.ResourceRef{
377+
Name: "user-pass",
378+
},
379+
DatabaseName: "admin",
380+
},
381+
},
382+
passwordVersion: "1",
383+
expectedResult: workflow.OK().ReconcileResult(),
384+
expectedConditions: []api.Condition{
385+
api.TrueCondition(api.ReadyType),
386+
api.TrueCondition(api.DatabaseUserReadyType),
387+
},
388+
},
389+
"don't requeue when it's a standalone resource": {
390+
dbUser: &akov2.AtlasDatabaseUser{
391+
ObjectMeta: metav1.ObjectMeta{
392+
Name: "user1",
393+
Namespace: "default",
394+
},
395+
Spec: akov2.AtlasDatabaseUserSpec{
396+
ExternalProjectRef: &akov2.ExternalProjectReference{
397+
ID: "project-id",
398+
},
399+
LocalCredentialHolder: api.LocalCredentialHolder{
400+
ConnectionSecret: &api.LocalObjectReference{
401+
Name: "user-creds",
402+
},
403+
},
404+
Username: "user1",
405+
PasswordSecret: &common.ResourceRef{
406+
Name: "user-pass",
407+
},
408+
DatabaseName: "admin",
409+
},
410+
},
411+
passwordVersion: "1",
412+
expectedResult: workflow.Requeue(15 * time.Minute).ReconcileResult(),
413+
expectedConditions: []api.Condition{
414+
api.TrueCondition(api.ReadyType),
415+
api.TrueCondition(api.DatabaseUserReadyType),
416+
},
417+
},
418+
}
419+
420+
for name, tt := range tests {
421+
t.Run(name, func(t *testing.T) {
422+
testScheme := runtime.NewScheme()
423+
assert.NoError(t, akov2.AddToScheme(testScheme))
424+
assert.NoError(t, corev1.AddToScheme(testScheme))
425+
k8sClient := fake.NewClientBuilder().
426+
WithScheme(testScheme).
427+
WithObjects(tt.dbUser).
428+
WithStatusSubresource(tt.dbUser).
429+
WithInterceptorFuncs(tt.interceptors).
430+
Build()
431+
432+
logger := zaptest.NewLogger(t).Sugar()
433+
c := &AtlasDatabaseUserReconciler{
434+
Client: k8sClient,
435+
Log: logger,
436+
}
437+
ctx := &workflow.Context{
438+
Context: context.Background(),
439+
Log: logger,
440+
}
441+
442+
assert.Equal(t, tt.expectedResult, c.ready(ctx, tt.dbUser, tt.passwordVersion))
443+
assert.True(
444+
t,
445+
cmp.Equal(
446+
tt.expectedConditions,
447+
ctx.Conditions(),
448+
cmpopts.IgnoreFields(api.Condition{}, "LastTransitionTime"),
449+
),
450+
)
451+
})
452+
}
453+
}
454+
286455
func TestFindAtlasDatabaseUserForSecret(t *testing.T) {
287456
for _, tc := range []struct {
288457
name string

pkg/controller/workflow/result.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
)
88

99
const (
10-
DefaultRetry = time.Second * 10
11-
DefaultTimeout = time.Minute * 20
10+
DefaultRetry = time.Second * 10
11+
StandaloneResourceRequeuePeriod = time.Minute * 15
12+
DefaultTimeout = time.Minute * 20
1213
)
1314

1415
type Result struct {
@@ -30,6 +31,13 @@ func OK() Result {
3031
}
3132
}
3233

34+
func Requeue(period time.Duration) Result {
35+
return Result{
36+
terminated: false,
37+
requeueAfter: period,
38+
}
39+
}
40+
3341
// Terminate indicates that the reconciliation logic cannot proceed and needs to be finished (and possibly requeued).
3442
// This is not an expected termination of the reconciliation process so 'warning' flag is set to 'true'.
3543
// 'reason' and 'message' indicate the error state and are supposed to be reflected in the `conditions` for the

0 commit comments

Comments
 (0)