Skip to content

Commit 07d7ea7

Browse files
authored
Bug fixes for status patching and late initialization patching (#168)
Description of changes: Commit#1 - Adds the implementation of AWSResource.DeepCopy() method. aws-controllers-k8s/runtime#48 Commit#2 - Fixes the patching bug in lateInitialize code to unblock sagemaker team - BUG: rm.LateInitialize() method was adding lateInitialized fields to the same object passed in the parameter and returning as output. ```go lateInitializedLatest, err := rm.LateInitialize(ctx, latest) rlog.Exit("rm.LateInitialize", err) // Always patch after late initialize because some fields may have been initialized while // others require a retry after some delay. // This patching does not hurt because if there is no diff then 'patchResourceMetadataAndSpec' // acts as a no-op. if ackcompare.IsNotNil(lateInitializedLatest) { patchErr := r.patchResourceMetadataAndSpec(ctx, latest, lateInitializedLatest) // Throw the patching error if reconciler is unable to patch the resource with late initializations if patchErr != nil { err = patchErr } } ``` - Since lateInitializedLatest and latest were same object, above `patchResourceMetadataAndSpec` call sees no diff and does not patch lateInitialized fields into etcd. - This PR solves the bug by adding lateInitialized fields in a copy of latest and returning that copy (without modifying latest) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 7ca284c commit 07d7ea7

File tree

8 files changed

+50
-39
lines changed

8 files changed

+50
-39
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ GO111MODULE=on
66
AWS_SERVICE=$(shell echo $(SERVICE) | tr '[:upper:]' '[:lower:]')
77

88
# Build ldflags
9-
VERSION ?= "v0.12.0"
9+
VERSION ?= "v0.13.0"
1010
GITCOMMIT=$(shell git rev-parse HEAD)
1111
BUILDDATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
1212
IMPORT_PATH=github.com/aws-controllers-k8s/code-generator

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/aws-controllers-k8s/code-generator
33
go 1.14
44

55
require (
6-
github.com/aws-controllers-k8s/runtime v0.12.0
6+
github.com/aws-controllers-k8s/runtime v0.13.0
77
github.com/aws/aws-sdk-go v1.37.10
88
github.com/dlclark/regexp2 v1.4.0
99
// pin to v0.1.1 due to release problem with v0.1.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
6767
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
6868
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
6969
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
70-
github.com/aws-controllers-k8s/runtime v0.12.0 h1:G/lCEozh4Brsv1Ojqyl9D/whpq/YvcFtDZBWXf6YIgI=
71-
github.com/aws-controllers-k8s/runtime v0.12.0/go.mod h1:kG2WM4JAmLgf67cgZV9IZUkY2DsrUzsaNbmhFMfb05c=
70+
github.com/aws-controllers-k8s/runtime v0.13.0 h1:PYiNnQejjS/1H93bolFXGIzgQZSn/gRoPSAEU6UG0ec=
71+
github.com/aws-controllers-k8s/runtime v0.13.0/go.mod h1:kG2WM4JAmLgf67cgZV9IZUkY2DsrUzsaNbmhFMfb05c=
7272
github.com/aws/aws-sdk-go v1.37.10 h1:LRwl+97B4D69Z7tz+eRUxJ1C7baBaIYhgrn5eLtua+Q=
7373
github.com/aws/aws-sdk-go v1.37.10/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
7474
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=

pkg/generate/ack/runtime_test.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,19 @@ func TestRuntimeDependency(t *testing.T) {
131131
require.Implements((*acktypes.AWSResourceIdentifiers)(nil), new(fakeIdentifiers))
132132
require.Implements((*acktypes.AWSResourceDescriptor)(nil), new(fakeDescriptor))
133133

134-
// ACK runtime 0.2.3 introduced a new logger that is now passed into the
134+
// ACK runtime v0.2.3 introduced a new logger that is now passed into the
135135
// Context and retrievable using the `pkg/runtime/log.FromContext`
136136
// function. This function returns NoopLogger if no such logger is found
137137
// in the context, but this check here is mostly to ensure that the new
138-
// function used in ACK runtime 0.2.3 and templates in code-generator
139-
// consuming 0.2.3 are properly pinned.
138+
// function used in ACK runtime v0.2.3 and templates in code-generator
139+
// consuming v0.2.3 are properly pinned.
140140
require.Implements((*acktypes.Logger)(nil), ackrtlog.FromContext(context.TODO()))
141141

142-
// ACK runtime 0.3.0 introduced a new RequeueOnSuccessSeconds method to the
142+
// ACK runtime v0.3.0 introduced a new RequeueOnSuccessSeconds method to the
143143
// resource manager factory
144144
require.Implements((*acktypes.AWSResourceManagerFactory)(nil), new(fakeRMF))
145145

146-
// ACK runtime 0.4.0 introduced a new AdditionalKeys field to the
146+
// ACK runtime v0.4.0 introduced a new AdditionalKeys field to the
147147
// AWSIdentifiers type. By simply referring to the new AdditionalKeys field
148148
// here, we have a compile-time test of the pinning of code-generator to
149149
// ACK runtime v0.4.0...
@@ -155,20 +155,24 @@ func TestRuntimeDependency(t *testing.T) {
155155
}
156156
_ = ids
157157

158-
// ACK runtime 0.6.0 modified pkg/types/AWSResourceManager.Delete signature.
158+
// ACK runtime v0.6.0 modified pkg/types/AWSResourceManager.Delete signature.
159159
require.Implements((*acktypes.AWSResourceManager)(nil), new(fakeRM))
160160

161-
// ACK runtime 0.7.0 introduced SecretNotFound error.
161+
// ACK runtime v0.7.0 introduced SecretNotFound error.
162162
require.NotNil(ackerr.SecretNotFound)
163163

164-
// ACK runtime 0.8.0 removed the unused UpdateCRStatus method from
164+
// ACK runtime v0.8.0 removed the unused UpdateCRStatus method from
165165
// AWSResourceDescriptor
166166
rdType := reflect.TypeOf((*acktypes.AWSResourceDescriptor)(nil)).Elem()
167167
_, found := rdType.MethodByName("UpdateCRStatus")
168168
require.False(found)
169169

170-
// ACK runtime 0.9.2 introduced the SetStatus method into AWSResource
170+
// ACK runtime v0.9.2 introduced the SetStatus method into AWSResource
171171
resType := reflect.TypeOf((*acktypes.AWSResource)(nil)).Elem()
172172
_, found = resType.MethodByName("SetStatus")
173173
require.True(found)
174+
175+
// ACK runtime v0.13.0 introduced the DeepCopy method into AWSResource
176+
_, found = resType.MethodByName("DeepCopy")
177+
require.True(found)
174178
}

pkg/generate/code/late_initialize.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func getSortedLateInitFieldsAndConfig(
136136
// }
137137
// }
138138
// }
139-
// return latest
139+
// return &resource{latestKo}
140140
func LateInitializeFromReadOne(
141141
cfg *ackgenconfig.Config,
142142
r *model.CRD,
@@ -151,8 +151,8 @@ func LateInitializeFromReadOne(
151151
if len(lateInitializedFieldNames) == 0 {
152152
return fmt.Sprintf("%sreturn %s", indent, targetResVarName)
153153
}
154-
out += fmt.Sprintf("%sobservedKo := rm.concreteResource(%s).ko\n", indent, sourceResVarName)
155-
out += fmt.Sprintf("%slatestKo := rm.concreteResource(%s).ko\n", indent, targetResVarName)
154+
out += fmt.Sprintf("%sobservedKo := rm.concreteResource(%s).ko.DeepCopy()\n", indent, sourceResVarName)
155+
out += fmt.Sprintf("%slatestKo := rm.concreteResource(%s).ko.DeepCopy()\n", indent, targetResVarName)
156156
// TODO(vijat@): Add validation for correct field path in lateInitializedFieldNames
157157
for _, fName := range lateInitializedFieldNames {
158158
// split the field name by period
@@ -204,7 +204,7 @@ func LateInitializeFromReadOne(
204204
fNameIndentLevel = fNameIndentLevel - 1
205205
}
206206
}
207-
out += fmt.Sprintf("%sreturn %s", indent, targetResVarName)
207+
out += fmt.Sprintf("%sreturn &resource{latestKo}", indent)
208208
return out
209209
}
210210

@@ -235,7 +235,7 @@ func LateInitializeFromReadOne(
235235
//
236236
//
237237
// Sample Output:
238-
// ko := rm.concreteResource(latest).ko
238+
// ko := rm.concreteResource(latest).ko.DeepCopy()
239239
// if ko.Spec.ImageScanningConfiguration != nil {
240240
// if ko.Spec.ImageScanningConfiguration.ScanOnPush == nil {
241241
// return true
@@ -288,7 +288,7 @@ func IncompleteLateInitialization(
288288
out += fmt.Sprintf("%sreturn false", indent)
289289
return out
290290
}
291-
out += fmt.Sprintf("%sko := rm.concreteResource(%s).ko\n", indent, resVarName)
291+
out += fmt.Sprintf("%sko := rm.concreteResource(%s).ko.DeepCopy()\n", indent, resVarName)
292292
for _, fName := range sortedLateInitFieldNames {
293293
// split the field name by period
294294
// each substring represents a field.

pkg/generate/code/late_initialize_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,15 @@ func Test_LateInitializeFromReadOne_NonNestedPath(t *testing.T) {
100100
assert.NotNil(crd.Config().ResourceFields(crd.Names.Original)["Name"].LateInitialize)
101101
assert.NotNil(crd.Config().ResourceFields(crd.Names.Original)["ImageTagMutability"].LateInitialize)
102102
expected :=
103-
` observedKo := rm.concreteResource(observed).ko
104-
latestKo := rm.concreteResource(latest).ko
103+
` observedKo := rm.concreteResource(observed).ko.DeepCopy()
104+
latestKo := rm.concreteResource(latest).ko.DeepCopy()
105105
if observedKo.Spec.ImageTagMutability != nil && latestKo.Spec.ImageTagMutability == nil {
106106
latestKo.Spec.ImageTagMutability = observedKo.Spec.ImageTagMutability
107107
}
108108
if observedKo.Spec.Name != nil && latestKo.Spec.Name == nil {
109109
latestKo.Spec.Name = observedKo.Spec.Name
110110
}
111-
return latest`
111+
return &resource{latestKo}`
112112
assert.Equal(expected, code.LateInitializeFromReadOne(crd.Config(), crd, "observed", "latest", 1))
113113
}
114114

@@ -125,8 +125,8 @@ func Test_LateInitializeFromReadOne_NestedPath(t *testing.T) {
125125
assert.NotNil(crd.Config().ResourceFields(crd.Names.Original)["Name"].LateInitialize)
126126
assert.NotNil(crd.Config().ResourceFields(crd.Names.Original)["ImageScanningConfiguration.ScanOnPush"].LateInitialize)
127127
expected :=
128-
` observedKo := rm.concreteResource(observed).ko
129-
latestKo := rm.concreteResource(latest).ko
128+
` observedKo := rm.concreteResource(observed).ko.DeepCopy()
129+
latestKo := rm.concreteResource(latest).ko.DeepCopy()
130130
if observedKo.Spec.ImageScanningConfiguration != nil && latestKo.Spec.ImageScanningConfiguration != nil {
131131
if observedKo.Spec.ImageScanningConfiguration.ScanOnPush != nil && latestKo.Spec.ImageScanningConfiguration.ScanOnPush == nil {
132132
latestKo.Spec.ImageScanningConfiguration.ScanOnPush = observedKo.Spec.ImageScanningConfiguration.ScanOnPush
@@ -163,7 +163,7 @@ func Test_LateInitializeFromReadOne_NestedPath(t *testing.T) {
163163
}
164164
}
165165
}
166-
return latest`
166+
return &resource{latestKo}`
167167
assert.Equal(expected, code.LateInitializeFromReadOne(crd.Config(), crd, "observed", "latest", 1))
168168
}
169169

@@ -193,7 +193,7 @@ func Test_IncompleteLateInitialization(t *testing.T) {
193193
assert.NotNil(crd.Config().ResourceFields(crd.Names.Original)["Name"].LateInitialize)
194194
assert.NotNil(crd.Config().ResourceFields(crd.Names.Original)["ImageScanningConfiguration.ScanOnPush"].LateInitialize)
195195
expected :=
196-
` ko := rm.concreteResource(latest).ko
196+
` ko := rm.concreteResource(latest).ko.DeepCopy()
197197
if ko.Spec.ImageScanningConfiguration != nil {
198198
if ko.Spec.ImageScanningConfiguration.ScanOnPush == nil {
199199
return true

templates/pkg/resource/manager.go.tpl

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,43 +177,44 @@ func (rm *resourceManager) LateInitialize(
177177
rlog.Debug("no late initialization required.")
178178
return latest, nil
179179
}
180+
latestCopy := latest.DeepCopy()
180181
lateInitConditionReason := ""
181182
lateInitConditionMessage := ""
182183
{{- if $hookCode := Hook .CRD "late_initialize_pre_read_one" }}
183184
{{ $hookCode }}
184185
{{- end }}
185-
observed, err := rm.ReadOne(ctx, latest)
186+
observed, err := rm.ReadOne(ctx, latestCopy)
186187
if err != nil {
187188
lateInitConditionMessage = "Unable to complete Read operation required for late initialization"
188189
lateInitConditionReason = "Late Initialization Failure"
189-
ackcondition.SetLateInitialized(latest, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason)
190-
return latest, err
190+
ackcondition.SetLateInitialized(latestCopy, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason)
191+
return latestCopy, err
191192
}
192193
{{- if $hookCode := Hook .CRD "late_initialize_post_read_one" }}
193194
{{ $hookCode }}
194195
{{- end }}
195-
latest = rm.lateInitializeFromReadOneOutput(observed, latest)
196-
incompleteInitialization := rm.incompleteLateInitialization(latest)
196+
lateInitializedRes := rm.lateInitializeFromReadOneOutput(observed, latestCopy)
197+
incompleteInitialization := rm.incompleteLateInitialization(lateInitializedRes)
197198
if incompleteInitialization {
198199
// Add the condition with LateInitialized=False
199200
lateInitConditionMessage = "Late initialization did not complete, requeuing with delay of 5 seconds"
200201
lateInitConditionReason = "Delayed Late Initialization"
201-
ackcondition.SetLateInitialized(latest, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason)
202-
return latest, ackrequeue.NeededAfter(nil, time.Duration(5)*time.Second)
202+
ackcondition.SetLateInitialized(lateInitializedRes, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason)
203+
return lateInitializedRes, ackrequeue.NeededAfter(nil, time.Duration(5)*time.Second)
203204
}
204-
// Set LateIntialized condition to True
205+
// Set LateInitialized condition to True
205206
lateInitConditionMessage = "Late initialization successful"
206207
lateInitConditionReason = "Late initialization successful"
207-
ackcondition.SetLateInitialized(latest, corev1.ConditionTrue, &lateInitConditionMessage, &lateInitConditionReason)
208-
return latest, nil
208+
ackcondition.SetLateInitialized(lateInitializedRes, corev1.ConditionTrue, &lateInitConditionMessage, &lateInitConditionReason)
209+
return lateInitializedRes, nil
209210
}
210211
211212
// incompleteLateInitialization return true if there are fields which were supposed to be
212213
// late initialized but are not. If all the fields are late initialized, false is returned
213214
func (rm *resourceManager) incompleteLateInitialization(
214-
latest acktypes.AWSResource,
215+
res acktypes.AWSResource,
215216
) bool {
216-
{{ GoCodeIncompleteLateInitialization .CRD "latest" 1 }}
217+
{{ GoCodeIncompleteLateInitialization .CRD "res" 1 }}
217218
}
218219
219220
// lateInitializeFromReadOneOutput late initializes the 'latest' resource from the 'observed'

templates/pkg/resource/resource.go.tpl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,10 @@ func (r *resource) SetStatus(desired acktypes.AWSResource) {
8181
func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error {
8282
{{- GoCodeSetResourceIdentifiers .CRD "identifier" "r.ko" 1}}
8383
return nil
84-
}
84+
}
85+
86+
// DeepCopy will return a copy of the resource
87+
func (r *resource) DeepCopy() acktypes.AWSResource {
88+
koCopy := r.ko.DeepCopy()
89+
return &resource{koCopy}
90+
}

0 commit comments

Comments
 (0)