Skip to content

Commit 65b9179

Browse files
authored
Use original status when making api-server Status.Update call in adoption reconciler (#71)
Issue #, if available: aws-controllers-k8s/community#1161 Description of changes: * The Status update from AdoptionReconciler was not useful because the Create call before Status.Update was resetting status of the CustomResource. * Earlier this similar kind of problem was also present in `reconciler.go` and it was fixed by keeping an original copy of Status before making Create/Update calls * I used the same solution in AdoptionReconciler. ---------- * Discovered this issue while debugging SageMaker ModelPackage adoption test. ModelPackage uses ARN as identifier which needs to be set inside `Status.ACKResourceMetadata.ARN` field of CustomResource. * Since the Identifier was never getting correctly set, the `ReadOne` call inside `reconciler.go` was failing while reconciling the CustomResource. -------------- * Validated by running SageMaker end-to-end tests successfully. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 7037a49 commit 65b9179

File tree

2 files changed

+42
-24
lines changed

2 files changed

+42
-24
lines changed

pkg/runtime/adoption_reconciler.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,16 @@ func (r *adoptionReconciler) Sync(
224224
}, described.RuntimeObject()); err != nil {
225225
if apierrors.IsNotFound(err) {
226226
// If Adopted AWS resource was not found in k8s, create it.
227+
228+
// Before creation, Keep the copy of original described object
229+
// because after the create call, Status gets set to empty
230+
describedCopy := described.DeepCopy()
227231
if err := r.kc.Create(ctx, described.RuntimeObject()); err != nil {
228232
return r.onError(ctx, desired, err)
229233
}
230-
234+
// reset the status of described object to original value before
235+
// making the Status Update call
236+
described.SetStatus(describedCopy)
231237
if err := r.kc.Status().Update(ctx, described.RuntimeObject()); err != nil {
232238
return r.onError(ctx, desired, err)
233239
}

pkg/runtime/adoption_reconciler_test.go

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,14 @@ func mockReconciler() (acktypes.AdoptedResourceReconciler, *ctrlrtclientmock.Cli
7272
), kc, apiReader
7373
}
7474

75-
func mockDescriptorAndAWSResource() (*ackmocks.AWSResourceDescriptor, *ackmocks.AWSResource) {
75+
func mockDescriptorAndAWSResource() (*ackmocks.AWSResourceDescriptor, *ackmocks.AWSResource, *ackmocks.AWSResource) {
7676
des := &ackmocks.AWSResourceDescriptor{}
7777
emptyRuntimeObject := &ctrlrtclientmock.Object{}
7878
res := &ackmocks.AWSResource{}
79+
resDeepCopy := &ackmocks.AWSResource{}
7980
des.On("EmptyRuntimeObject").Return(emptyRuntimeObject)
8081
des.On("ResourceFromRuntimeObject", emptyRuntimeObject).Return(res)
81-
return des, res
82+
return des, res, resDeepCopy
8283
}
8384

8485
func mockManager() *ackmocks.AWSResourceManager {
@@ -91,7 +92,11 @@ func setupMockClient(kc *ctrlrtclientmock.Client, statusWriter *ctrlrtclientmock
9192
kc.On("Patch", ctx, adoptedRes, mock.AnythingOfType("*client.mergeFromPatch")).Return(nil)
9293
}
9394

94-
func setupMockAwsResource(res *ackmocks.AWSResource, adoptedRes *ackv1alpha1.AdoptedResource) {
95+
func setupMockAwsResource(
96+
res *ackmocks.AWSResource,
97+
resDeepCopy *ackmocks.AWSResource,
98+
adoptedRes *ackv1alpha1.AdoptedResource,
99+
) {
95100
res.On("SetIdentifiers", adoptedRes.Spec.AWS).Return(nil)
96101
res.On("SetObjectMeta", mock.AnythingOfType("ObjectMeta")).Run(func(args mock.Arguments) {})
97102

@@ -108,6 +113,8 @@ func setupMockAwsResource(res *ackmocks.AWSResource, adoptedRes *ackv1alpha1.Ado
108113
rmo.On("GetFinalizers").Return(make([]string, 0))
109114
rmo.On("GetOwnerReferences").Return(make([]v1.OwnerReference, 0))
110115
rmo.On("GetGenerateName").Return("")
116+
res.On("DeepCopy").Return(resDeepCopy)
117+
res.On("SetStatus", resDeepCopy).Run(func(args mock.Arguments) {})
111118
}
112119

113120
func setupMockManager(manager *ackmocks.AWSResourceManager, ctx context.Context, res *ackmocks.AWSResource) {
@@ -148,15 +155,15 @@ func TestSync_FailureInSettingIdentifiers(t *testing.T) {
148155
require := require.New(t)
149156
// Mock resource creation
150157
r, kc, apiReader := mockReconciler()
151-
descriptor, res := mockDescriptorAndAWSResource()
158+
descriptor, res, resDeepCopy := mockDescriptorAndAWSResource()
152159
manager := mockManager()
153160
adoptedRes := adoptedResource(Namespace, Name)
154161
res.On("SetIdentifiers", adoptedRes.Spec.AWS).Return(errors.New("unable to set Identifier"))
155162
ctx := context.TODO()
156163
statusWriter := &ctrlrtclientmock.StatusWriter{}
157164

158165
//Mock behavior setup
159-
setupMockAwsResource(res, adoptedRes)
166+
setupMockAwsResource(res, resDeepCopy, adoptedRes)
160167
setupMockClient(kc, statusWriter, ctx, adoptedRes)
161168

162169
// Call
@@ -176,7 +183,7 @@ func TestSync_FailureInSettingIdentifiers(t *testing.T) {
176183
Namespace: Namespace,
177184
Name: Name,
178185
}, res.RuntimeObject())
179-
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res)
186+
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res, resDeepCopy)
180187
assertManaged(false, t, ctx, kc, adoptedRes)
181188
assertAdoptedCondition("False", require, t, ctx, kc, statusWriter, adoptedRes)
182189
}
@@ -186,14 +193,14 @@ func TestSync_FailureInReadOne(t *testing.T) {
186193
require := require.New(t)
187194
// Mock resource creation
188195
r, kc, apiReader := mockReconciler()
189-
descriptor, res := mockDescriptorAndAWSResource()
196+
descriptor, res, resDeepCopy := mockDescriptorAndAWSResource()
190197
manager := mockManager()
191198
adoptedRes := adoptedResource(Namespace, Name)
192199
ctx := context.TODO()
193200
statusWriter := &ctrlrtclientmock.StatusWriter{}
194201

195202
//Mock behavior setup
196-
setupMockAwsResource(res, adoptedRes)
203+
setupMockAwsResource(res, resDeepCopy, adoptedRes)
197204
setupMockClient(kc, statusWriter, ctx, adoptedRes)
198205
manager.On("ReadOne", ctx, res).Return(res, errors.New("failed to perform ReadOne"))
199206

@@ -213,7 +220,7 @@ func TestSync_FailureInReadOne(t *testing.T) {
213220
Namespace: Namespace,
214221
Name: Name,
215222
}, res.RuntimeObject())
216-
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res)
223+
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res, resDeepCopy)
217224
assertManaged(false, t, ctx, kc, adoptedRes)
218225
assertAdoptedCondition("False", require, t, ctx, kc, statusWriter, adoptedRes)
219226
}
@@ -223,14 +230,14 @@ func TestSync_AWSResourceAlreadyExists(t *testing.T) {
223230
require := require.New(t)
224231
// Mock resource creation
225232
r, kc, apiReader := mockReconciler()
226-
descriptor, res := mockDescriptorAndAWSResource()
233+
descriptor, res, resDeepCopy := mockDescriptorAndAWSResource()
227234
manager := mockManager()
228235
adoptedRes := adoptedResource(Namespace, Name)
229236
ctx := context.TODO()
230237
statusWriter := &ctrlrtclientmock.StatusWriter{}
231238

232239
//Mock behavior setup
233-
setupMockAwsResource(res, adoptedRes)
240+
setupMockAwsResource(res, resDeepCopy, adoptedRes)
234241
setupMockClient(kc, statusWriter, ctx, adoptedRes)
235242
setupMockManager(manager, ctx, res)
236243
setupMockDescriptor(descriptor, res)
@@ -246,7 +253,7 @@ func TestSync_AWSResourceAlreadyExists(t *testing.T) {
246253
//Assertions
247254
require.Nil(err)
248255
assertAWSResourceRead(t, ctx, manager, apiReader, adoptedRes, res)
249-
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res)
256+
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res, resDeepCopy)
250257
assertManaged(true, t, ctx, kc, adoptedRes)
251258
assertAdoptedCondition("True", require, t, ctx, kc, statusWriter, adoptedRes)
252259
}
@@ -256,14 +263,14 @@ func TestSync_APIReaderUnknownError(t *testing.T) {
256263
require := require.New(t)
257264
// Mock resource creation
258265
r, kc, apiReader := mockReconciler()
259-
descriptor, res := mockDescriptorAndAWSResource()
266+
descriptor, res, resDeepCopy := mockDescriptorAndAWSResource()
260267
manager := mockManager()
261268
adoptedRes := adoptedResource(Namespace, Name)
262269
ctx := context.TODO()
263270
statusWriter := &ctrlrtclientmock.StatusWriter{}
264271

265272
//Mock behavior setup
266-
setupMockAwsResource(res, adoptedRes)
273+
setupMockAwsResource(res, resDeepCopy, adoptedRes)
267274
setupMockClient(kc, statusWriter, ctx, adoptedRes)
268275
setupMockManager(manager, ctx, res)
269276
setupMockDescriptor(descriptor, res)
@@ -280,7 +287,7 @@ func TestSync_APIReaderUnknownError(t *testing.T) {
280287
require.NotNil(err)
281288
require.Equal("unknown error", err.Error())
282289
assertAWSResourceRead(t, ctx, manager, apiReader, adoptedRes, res)
283-
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res)
290+
assertAWSResourceCreation(false, t, ctx, kc, statusWriter, res, resDeepCopy)
284291
assertManaged(false, t, ctx, kc, adoptedRes)
285292
assertAdoptedCondition("False", require, t, ctx, kc, statusWriter, adoptedRes)
286293
}
@@ -290,14 +297,14 @@ func TestSync_ErrorInResourceCreation(t *testing.T) {
290297
require := require.New(t)
291298
// Mock resource creation
292299
r, kc, apiReader := mockReconciler()
293-
descriptor, res := mockDescriptorAndAWSResource()
300+
descriptor, res, resDeepCopy := mockDescriptorAndAWSResource()
294301
manager := mockManager()
295302
adoptedRes := adoptedResource(Namespace, Name)
296303
ctx := context.TODO()
297304
statusWriter := &ctrlrtclientmock.StatusWriter{}
298305

299306
//Mock behavior setup
300-
setupMockAwsResource(res, adoptedRes)
307+
setupMockAwsResource(res, resDeepCopy, adoptedRes)
301308
setupMockClient(kc, statusWriter, ctx, adoptedRes)
302309
setupMockManager(manager, ctx, res)
303310
setupMockDescriptor(descriptor, res)
@@ -323,14 +330,14 @@ func TestSync_ErrorInStatusUpdate(t *testing.T) {
323330
require := require.New(t)
324331
// Mock resource creation
325332
r, kc, apiReader := mockReconciler()
326-
descriptor, res := mockDescriptorAndAWSResource()
333+
descriptor, res, resDeepCopy := mockDescriptorAndAWSResource()
327334
manager := mockManager()
328335
adoptedRes := adoptedResource(Namespace, Name)
329336
ctx := context.TODO()
330337
statusWriter := &ctrlrtclientmock.StatusWriter{}
331338

332339
//Mock behavior setup
333-
setupMockAwsResource(res, adoptedRes)
340+
setupMockAwsResource(res, resDeepCopy, adoptedRes)
334341
setupMockClient(kc, statusWriter, ctx, adoptedRes)
335342
setupMockManager(manager, ctx, res)
336343
setupMockDescriptor(descriptor, res)
@@ -345,7 +352,7 @@ func TestSync_ErrorInStatusUpdate(t *testing.T) {
345352
require.NotNil(err)
346353
require.Equal("status update failure", err.Error())
347354
assertAWSResourceRead(t, ctx, manager, apiReader, adoptedRes, res)
348-
assertAWSResourceCreation(true, t, ctx, kc, statusWriter, res)
355+
assertAWSResourceCreation(true, t, ctx, kc, statusWriter, res, resDeepCopy)
349356
assertManaged(false, t, ctx, kc, adoptedRes)
350357
assertAdoptedCondition("False", require, t, ctx, kc, statusWriter, adoptedRes)
351358
}
@@ -355,14 +362,14 @@ func TestSync_HappyCase(t *testing.T) {
355362
require := require.New(t)
356363
// Mock resource creation
357364
r, kc, apiReader := mockReconciler()
358-
descriptor, res := mockDescriptorAndAWSResource()
365+
descriptor, res, resDeepCopy := mockDescriptorAndAWSResource()
359366
manager := mockManager()
360367
adoptedRes := adoptedResource(Namespace, Name)
361368
ctx := context.TODO()
362369
statusWriter := &ctrlrtclientmock.StatusWriter{}
363370

364371
//Mock behavior setup
365-
setupMockAwsResource(res, adoptedRes)
372+
setupMockAwsResource(res, resDeepCopy, adoptedRes)
366373
setupMockClient(kc, statusWriter, ctx, adoptedRes)
367374
setupMockManager(manager, ctx, res)
368375
setupMockDescriptor(descriptor, res)
@@ -376,7 +383,7 @@ func TestSync_HappyCase(t *testing.T) {
376383
//Assertions
377384
require.Nil(err)
378385
assertAWSResourceRead(t, ctx, manager, apiReader, adoptedRes, res)
379-
assertAWSResourceCreation(true, t, ctx, kc, statusWriter, res)
386+
assertAWSResourceCreation(true, t, ctx, kc, statusWriter, res, resDeepCopy)
380387
assertManaged(true, t, ctx, kc, adoptedRes)
381388
assertAdoptedCondition("True", require, t, ctx, kc, statusWriter, adoptedRes)
382389
}
@@ -432,12 +439,17 @@ func assertAWSResourceCreation(
432439
kc *ctrlrtclientmock.Client,
433440
statusWriter *ctrlrtclientmock.StatusWriter,
434441
res *ackmocks.AWSResource,
442+
resDeepCopy *ackmocks.AWSResource,
435443
) {
436444
if expectedCreation {
437445
kc.AssertCalled(t, "Create", ctx, res.RuntimeObject())
446+
res.AssertCalled(t, "DeepCopy")
447+
res.AssertCalled(t, "SetStatus", resDeepCopy)
438448
statusWriter.AssertCalled(t, "Update", ctx, res.RuntimeObject())
439449
} else {
440450
kc.AssertNotCalled(t, "Create", ctx, res.RuntimeObject())
451+
res.AssertNotCalled(t, "DeepCopy")
452+
res.AssertNotCalled(t, "SetStatus", resDeepCopy)
441453
statusWriter.AssertNotCalled(t, "Update", ctx, res.RuntimeObject())
442454
}
443455
}

0 commit comments

Comments
 (0)