Skip to content

Commit 934ba68

Browse files
authored
Initial changes for late initializing resource fields. (#37)
Issue: aws-controllers-k8s/community#812 Changes: * Introduce new condition ConditionTypeLateInitialized * Update reconciler to invoke `AWSResourceManager.LateInitialize()` method ------------------ Corresponding Code generator PR: aws-controllers-k8s/code-generator#138 Please do not merge until code generator PR is merged ------------------ Testing * Unit tests successful ``` ➜ runtime git:(li-mlp) ✗ make test building mocks for pkg/types ... ok. building mocks for k8s.io/apimachinery/pkg/apis/meta/v1 ... ok. building mocks for k8s.io/apimachinery/runtime ... ok. building mocks for k8s.io/apimachinery/runtime/schema ... ok. building mocks for sigs.k8s.io/controller-runtime/pkg/client ... ok. go test ./... ? github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1 [no test files] ? github.com/aws-controllers-k8s/runtime/mocks/apimachinery/pkg/apis/meta/v1 [no test files] ? github.com/aws-controllers-k8s/runtime/mocks/apimachinery/pkg/runtime [no test files] ? github.com/aws-controllers-k8s/runtime/mocks/apimachinery/pkg/runtime/schema [no test files] ? github.com/aws-controllers-k8s/runtime/mocks/controller-runtime/pkg/client [no test files] ? github.com/aws-controllers-k8s/runtime/mocks/pkg/types [no test files] ok github.com/aws-controllers-k8s/runtime/pkg/compare (cached) ok github.com/aws-controllers-k8s/runtime/pkg/condition (cached) ? github.com/aws-controllers-k8s/runtime/pkg/config [no test files] ? github.com/aws-controllers-k8s/runtime/pkg/errors [no test files] ? github.com/aws-controllers-k8s/runtime/pkg/metrics [no test files] ok github.com/aws-controllers-k8s/runtime/pkg/requeue (cached) ok github.com/aws-controllers-k8s/runtime/pkg/runtime 1.287s ok github.com/aws-controllers-k8s/runtime/pkg/runtime/cache (cached) ? github.com/aws-controllers-k8s/runtime/pkg/runtime/log [no test files] ? github.com/aws-controllers-k8s/runtime/pkg/types [no test files] ok github.com/aws-controllers-k8s/runtime/pkg/util (cached) ? github.com/aws-controllers-k8s/runtime/pkg/webhook [no test files] ``` * local-kind-e2e-test logs ``` │ │ 2021-07-26T19:38:08.701Z DEBUG ackrt > r.Sync {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "kind": "Repository" │ │ 2021-07-26T19:38:08.701Z DEBUG ackrt >> rm.ReadOne {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": f │ │ 2021-07-26T19:38:08.701Z DEBUG ackrt >>> rm.sdkFind {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": │ │ 2021-07-26T19:38:09.641Z DEBUG ackrt <<< rm.sdkFind {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": │ │ 2021-07-26T19:38:09.641Z DEBUG ackrt << rm.ReadOne {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": f │ │ 2021-07-26T19:38:09.641Z DEBUG ackrt >> r.setResourceManaged {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_a │ │ 2021-07-26T19:38:09.641Z DEBUG ackrt >>> kc.Patch (metadata + spec) {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2" │ │ 2021-07-26T19:38:09.694Z DEBUG ackrt <<< kc.Patch (metadata + spec) {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2" │ │ 2021-07-26T19:38:09.694Z DEBUG ackrt marked resource as managed {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "i │ │ 2021-07-26T19:38:09.694Z DEBUG ackrt << r.setResourceManaged {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_a │ │ 2021-07-26T19:38:09.694Z DEBUG ackrt >> rm.Create {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": fa │ │ 2021-07-26T19:38:09.694Z DEBUG ackrt >>> rm.sdkCreate {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted" │ │ 2021-07-26T19:38:09.725Z DEBUG ackrt <<< rm.sdkCreate {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted" │ │ 2021-07-26T19:38:09.725Z DEBUG ackrt << rm.Create {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": fa │ │ 2021-07-26T19:38:09.725Z INFO ackrt created new resource {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopt │ │ 2021-07-26T19:38:09.725Z DEBUG ackrt >> rm.LateInitialize {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adop │ │ 2021-07-26T19:38:09.725Z INFO ackrt calculated late initialization delay is 0 seconds {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "re │ │ 2021-07-26T19:38:09.725Z DEBUG ackrt >>> rm.sdkFind {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": │ │ 2021-07-26T19:38:09.748Z DEBUG ackrt <<< rm.sdkFind {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": │ │ 2021-07-26T19:38:09.748Z DEBUG ackrt << rm.LateInitialize {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adop │ │ 2021-07-26T19:38:09.748Z DEBUG ackrt >> r.patchResourceMetadataAndSpec {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west │ │ 2021-07-26T19:38:09.748Z DEBUG ackrt >>> kc.Patch (metadata + spec) {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2" │ │ 2021-07-26T19:38:09.758Z DEBUG ackrt <<< kc.Patch (metadata + spec) {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2" │ │ 2021-07-26T19:38:09.759Z DEBUG ackrt patched resource metadata and spec {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-wes │ │ 2021-07-26T19:38:09.759Z DEBUG ackrt << r.patchResourceMetadataAndSpec {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west │ │ 2021-07-26T19:38:09.759Z DEBUG ackrt >> r.patchResourceStatus {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_ │ │ 2021-07-26T19:38:09.759Z DEBUG ackrt >>> kc.Patch (status) {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_ado │ │ 2021-07-26T19:38:09.794Z DEBUG ackrt <<< kc.Patch (status) {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_ado │ │ 2021-07-26T19:38:09.795Z DEBUG ackrt patched resource status {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_a │ │ 2021-07-26T19:38:09.795Z DEBUG ackrt << r.patchResourceStatus {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_ │ │ 2021-07-26T19:38:09.795Z DEBUG ackrt < r.Sync {"kind": "Repository", "namespace": "default", "name": "ecr-repository-wy6n6wjm8", "generation": 1, "account": "309117047740", "role": "", "region": "us-west-2", "is_adopted": false, │ │ 2021-07-26T19:38:09.795Z DEBUG controller-runtime.controller Successfully Reconciled {"controller": "repository", "request": "default/ecr-repository-wy6n6wjm8"} ```
1 parent c5631b6 commit 934ba68

File tree

9 files changed

+214
-4
lines changed

9 files changed

+214
-4
lines changed

apis/core/v1alpha1/conditions.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ const (
4545
// Examples include
4646
// - Modifying an immutable field after it was created
4747
ConditionTypeAdvisory ConditionType = "ACK.Advisory"
48+
// ConditionTypeLateInitialized indicates whether the late initialization
49+
// of fields is completed or is in progress.
50+
// The absence of this condition indicates there is no late initalization
51+
// needed for the k8s resource.
52+
// "True" status indicates that the resource fields have been late initialized
53+
// "False" status indicates that the resource fields are in process of being late initialized.
54+
ConditionTypeLateInitialized ConditionType = "ACK.LateInitialized"
4855
)
4956

5057
// Condition is the common struct used by all CRDs managed by ACK service

mocks/pkg/types/aws_resource.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mocks/pkg/types/aws_resource_manager.go

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/condition/condition.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ func Terminal(subject acktypes.ConditionManager) *ackv1alpha1.Condition {
3535
return FirstOfType(subject, ackv1alpha1.ConditionTypeTerminal)
3636
}
3737

38+
// LateInitialized returns the Condition in the resource's Conditions collection that
39+
// is of type ConditionTypeLateInitialized. If no such condition is found, returns
40+
// nil.
41+
func LateInitialized(subject acktypes.ConditionManager) *ackv1alpha1.Condition {
42+
return FirstOfType(subject, ackv1alpha1.ConditionTypeLateInitialized)
43+
}
44+
3845
// FirstOfType returns the first Condition in the resource's Conditions
3946
// collection of the supplied type. If no such condition is found, returns nil.
4047
func FirstOfType(
@@ -111,3 +118,35 @@ func SetTerminal(
111118
c.Reason = reason
112119
subject.ReplaceConditions(allConds)
113120
}
121+
122+
// SetLateInitialized sets the resource's Condition of type ConditionTypeLateInitialized to
123+
// the supplied status, optional message and reason.
124+
func SetLateInitialized(
125+
subject acktypes.ConditionManager,
126+
status corev1.ConditionStatus,
127+
message *string,
128+
reason *string,
129+
) {
130+
allConds := subject.Conditions()
131+
var c *ackv1alpha1.Condition
132+
if c = LateInitialized(subject); c == nil {
133+
c = &ackv1alpha1.Condition{
134+
Type: ackv1alpha1.ConditionTypeLateInitialized,
135+
}
136+
allConds = append(allConds, c)
137+
}
138+
now := metav1.Now()
139+
c.LastTransitionTime = &now
140+
c.Status = status
141+
c.Message = message
142+
c.Reason = reason
143+
subject.ReplaceConditions(allConds)
144+
}
145+
146+
// LateInitializationInProgress return true if ConditionTypeLateInitialized has "False" status
147+
// False status means that resource has LateInitializationConfig but has not been completely
148+
// late initialized yet.
149+
func LateInitializationInProgress(subject acktypes.ConditionManager) bool {
150+
c := LateInitialized(subject)
151+
return c != nil && c.Status == corev1.ConditionFalse
152+
}

pkg/requeue/requeue.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,16 @@ type RequeueNeeded struct {
5353
}
5454

5555
func (e *RequeueNeeded) Error() string {
56-
if e.err == nil {
56+
if e == nil || e.err == nil {
5757
return ""
5858
}
5959
return e.err.Error()
6060
}
6161

6262
func (e *RequeueNeeded) Unwrap() error {
63+
if e == nil {
64+
return nil
65+
}
6366
return e.err
6467
}
6568

@@ -78,17 +81,23 @@ type RequeueNeededAfter struct {
7881
}
7982

8083
func (e *RequeueNeededAfter) Error() string {
81-
if e.err == nil {
84+
if e == nil || e.err == nil {
8285
return ""
8386
}
8487
return e.err.Error()
8588
}
8689

8790
func (e *RequeueNeededAfter) Duration() time.Duration {
91+
if e == nil {
92+
return time.Duration(0)*time.Second
93+
}
8894
return e.duration
8995
}
9096

9197
func (e *RequeueNeededAfter) Unwrap() error {
98+
if e == nil {
99+
return nil
100+
}
92101
return e.err
93102
}
94103

pkg/requeue/requeue_test.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ import (
1717
"testing"
1818
"time"
1919

20+
"github.com/aws-controllers-k8s/runtime/pkg/requeue"
2021
"github.com/pkg/errors"
2122
"github.com/stretchr/testify/assert"
22-
23-
"github.com/aws-controllers-k8s/runtime/pkg/requeue"
2423
)
2524

2625
func TestRequeueNeeded(t *testing.T) {
@@ -109,3 +108,15 @@ func TestRequeueNeededAfter(t *testing.T) {
109108
})
110109
}
111110
}
111+
112+
func TestRequeueNeededAfter_Nil(t *testing.T) {
113+
assert := assert.New(t)
114+
var nilRequeueNeededAfter *requeue.RequeueNeededAfter
115+
assert.Empty(nilRequeueNeededAfter.Error())
116+
assert.Nil(nilRequeueNeededAfter.Unwrap())
117+
assert.Equal("0s", nilRequeueNeededAfter.Duration().String())
118+
119+
var nilRequeueNeeded *requeue.RequeueNeeded
120+
assert.Empty(nilRequeueNeeded.Error())
121+
assert.Nil(nilRequeueNeeded.Unwrap())
122+
}

pkg/runtime/reconciler.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ func (r *resourceReconciler) Sync(
221221
return latest, err
222222
}
223223
}
224+
// Attempt to late initialize the resource. If there are no fields to
225+
// late initialize, this operation will be a no-op.
226+
if latest, err = r.lateInitializeResource(ctx, rm, latest); err != nil {
227+
return latest, err
228+
}
224229
return r.handleRequeues(ctx, latest)
225230
}
226231

@@ -338,6 +343,43 @@ func (r *resourceReconciler) updateResource(
338343
return latest, nil
339344
}
340345

346+
// lateInitializeResource calls AWSResourceManager.LateInitialize() method and
347+
// returns the AWSResource with late initialized fields.
348+
//
349+
// When the late initialization is delayed for an AWSResource, an error is returned
350+
// with specific requeue delay to attempt lateInitialization again.
351+
//
352+
// This method also adds an annotation to K8s CR, indicating the number of
353+
// late initialization attempts to correctly calculate exponential backoff delay
354+
//
355+
// This method also adds Condition to CR's status indicating status of late initialization.
356+
func (r *resourceReconciler) lateInitializeResource(
357+
ctx context.Context,
358+
rm acktypes.AWSResourceManager,
359+
latest acktypes.AWSResource,
360+
) (acktypes.AWSResource, error) {
361+
var err error
362+
rlog := ackrtlog.FromContext(ctx)
363+
exit := rlog.Trace("r.lateInitializeResource")
364+
defer exit(err)
365+
366+
rlog.Enter("rm.LateInitialize")
367+
lateInitializedLatest, err := rm.LateInitialize(ctx, latest)
368+
rlog.Exit("rm.LateInitialize", err)
369+
// Always patch after late initialize because some fields may have been initialized while
370+
// others require a retry after some delay.
371+
// This patching does not hurt because if there is no diff then 'patchResourceMetadataAndSpec'
372+
// acts as a no-op.
373+
if ackcompare.IsNotNil(lateInitializedLatest) {
374+
patchErr := r.patchResourceMetadataAndSpec(ctx, latest, lateInitializedLatest)
375+
// Throw the patching error if reconciler is unable to patch the resource with late initializations
376+
if patchErr != nil {
377+
err = patchErr
378+
}
379+
}
380+
return lateInitializedLatest, err
381+
}
382+
341383
// patchResourceMetadataAndSpec patches the custom resource in the Kubernetes API to match the
342384
// supplied latest resource's metadata and spec.
343385
func (r *resourceReconciler) patchResourceMetadataAndSpec(

pkg/runtime/reconciler_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ package runtime_test
1515

1616
import (
1717
"context"
18+
"errors"
1819
"testing"
20+
"time"
1921

22+
"github.com/stretchr/testify/assert"
2023
"github.com/stretchr/testify/require"
2124
"go.uber.org/zap/zapcore"
2225
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -29,6 +32,7 @@ import (
2932
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
3033
ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config"
3134
ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics"
35+
"github.com/aws-controllers-k8s/runtime/pkg/requeue"
3236
ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime"
3337
ackrtcache "github.com/aws-controllers-k8s/runtime/pkg/runtime/cache"
3438
acktypes "github.com/aws-controllers-k8s/runtime/pkg/types"
@@ -152,6 +156,9 @@ func TestReconcilerUpdate(t *testing.T) {
152156
).Once()
153157
rd.On("Delta", desired, latest).Return(ackcompare.NewDelta())
154158

159+
rm.On("LateInitialize", ctx, latest).Return(latest, nil)
160+
rd.On("Delta", latest, latest).Return(ackcompare.NewDelta())
161+
155162
r, kc := reconcilerMocks(rmf)
156163

157164
kc.On("Patch", ctx, latestRTObj, client.MergeFrom(desiredRTObj)).Return(nil)
@@ -170,6 +177,7 @@ func TestReconcilerUpdate(t *testing.T) {
170177
kc.AssertNotCalled(t, "Patch", ctx, latestRTObj, client.MergeFrom(desiredRTObj))
171178
// Only the HandleReconcilerError wrapper function ever calls patchResourceStatus
172179
kc.AssertNotCalled(t, "Status")
180+
rm.AssertCalled(t, "LateInitialize", ctx, latest)
173181
}
174182

175183
func TestReconcilerUpdate_PatchMetadataAndSpec_DiffInMetadata(t *testing.T) {
@@ -206,6 +214,8 @@ func TestReconcilerUpdate_PatchMetadataAndSpec_DiffInMetadata(t *testing.T) {
206214
rm.On("Update", ctx, desired, latest, delta).Return(
207215
latest, nil,
208216
)
217+
rm.On("LateInitialize", ctx, latest).Return(latest, nil)
218+
rd.On("Delta", latest, latest).Return(ackcompare.NewDelta())
209219

210220
r, kc := reconcilerMocks(rmf)
211221

@@ -219,6 +229,7 @@ func TestReconcilerUpdate_PatchMetadataAndSpec_DiffInMetadata(t *testing.T) {
219229
kc.AssertCalled(t, "Patch", ctx, latestRTObj, client.MergeFrom(desiredRTObj))
220230
// Only the HandleReconcilerError wrapper function ever calls patchResourceStatus
221231
kc.AssertNotCalled(t, "Status")
232+
rm.AssertCalled(t, "LateInitialize", ctx, latest)
222233
}
223234

224235
func TestReconcilerUpdate_PatchMetadataAndSpec_DiffInSpec(t *testing.T) {
@@ -252,6 +263,8 @@ func TestReconcilerUpdate_PatchMetadataAndSpec_DiffInSpec(t *testing.T) {
252263
rm.On("Update", ctx, desired, latest, delta).Return(
253264
latest, nil,
254265
)
266+
rm.On("LateInitialize", ctx, latest).Return(latest, nil)
267+
rd.On("Delta", latest, latest).Return(ackcompare.NewDelta())
255268

256269
r, kc := reconcilerMocks(rmf)
257270

@@ -265,6 +278,7 @@ func TestReconcilerUpdate_PatchMetadataAndSpec_DiffInSpec(t *testing.T) {
265278
kc.AssertCalled(t, "Patch", ctx, latestRTObj, client.MergeFrom(desiredRTObj))
266279
// Only the HandleReconcilerError wrapper function ever calls patchResourceStatus
267280
kc.AssertNotCalled(t, "Status")
281+
rm.AssertCalled(t, "LateInitialize", ctx, latest)
268282
}
269283

270284
func TestReconcilerHandleReconcilerError_PatchStatus_Latest(t *testing.T) {
@@ -326,3 +340,56 @@ func TestReconcilerHandleReconcilerError_NoPatchStatus_NoLatest(t *testing.T) {
326340
// patch the spec/metadata...
327341
kc.AssertNotCalled(t, "Patch")
328342
}
343+
344+
func TestReconcilerUpdate_ErrorInLateInitialization(t *testing.T) {
345+
require := require.New(t)
346+
assert := assert.New(t)
347+
348+
ctx := context.TODO()
349+
arn := ackv1alpha1.AWSResourceName("mybook-arn")
350+
351+
delta := ackcompare.NewDelta()
352+
delta.Add("Spec.A", "val1", "val2")
353+
354+
desired, desiredRTObj, _ := resourceMocks()
355+
356+
ids := &ackmocks.AWSResourceIdentifiers{}
357+
ids.On("ARN").Return(&arn)
358+
359+
latest, latestRTObj, _ := resourceMocks()
360+
latest.On("Identifiers").Return(ids)
361+
latest.On("Conditions").Return([]*ackv1alpha1.Condition{})
362+
363+
rm := &ackmocks.AWSResourceManager{}
364+
rm.On("ReadOne", ctx, desired).Return(
365+
latest, nil,
366+
)
367+
rm.On("Update", ctx, desired, latest, delta).Return(
368+
latest, nil,
369+
)
370+
371+
rmf, rd := managerFactoryMocks(desired, latest, delta)
372+
rd.On("Delta", desired, latest).Return(
373+
delta,
374+
).Once()
375+
rd.On("Delta", desired, latest).Return(ackcompare.NewDelta())
376+
377+
requeueError := requeue.NeededAfter(errors.New("error from late initialization"), time.Duration(0)*time.Second)
378+
rm.On("LateInitialize", ctx, latest).Return(latest, requeueError)
379+
rd.On("Delta", latest, latest).Return(ackcompare.NewDelta())
380+
381+
r, kc := reconcilerMocks(rmf)
382+
383+
kc.On("Patch", ctx, latestRTObj, client.MergeFrom(desiredRTObj)).Return(nil)
384+
385+
_, err := r.Sync(ctx, rm, desired)
386+
// Assert the error from late initialization
387+
require.NotNil(err)
388+
assert.Equal(requeueError, err)
389+
rm.AssertCalled(t, "ReadOne", ctx, desired)
390+
rd.AssertCalled(t, "Delta", desired, latest)
391+
rm.AssertCalled(t, "Update", ctx, desired, latest, delta)
392+
// No difference in desired, latest metadata and spec
393+
kc.AssertNotCalled(t, "Patch", ctx, latestRTObj, client.MergeFrom(desiredRTObj))
394+
rm.AssertCalled(t, "LateInitialize", ctx, latest)
395+
}

pkg/types/aws_resource_manager.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ type AWSResourceManager interface {
6767
// GetAttributes operations but all we have (for new CRs at least) is a
6868
// name for the resource
6969
ARNFromName(string) string
70+
// LateInitialize returns an AWS Resource after setting the late initialized
71+
// fields from the ReadOne call. This method will initialize the optional fields
72+
// which were not provided by the k8s user but were defaulted by the AWS service.
73+
// If there are no such fields to be initialized, the returned object is identical to
74+
// object passed in the parameter.
75+
// This method also adds/updates the ConditionTypeLateInitialized for the AWSResource.
76+
LateInitialize(context.Context, AWSResource) (AWSResource, error)
7077
}
7178

7279
// AWSResourceManagerFactory returns an AWSResourceManager that can be used to

0 commit comments

Comments
 (0)