@@ -22,55 +22,51 @@ import (
2222 "github.com/aws-controllers-k8s/code-generator/pkg/model"
2323)
2424
25- // FindLateInitializedFieldsWithDelay outputs the code to create a map of fieldName to
26- // late intialization delay in seconds.
27- func FindLateInitializedFieldsWithDelay (
25+ // FindLateInitializedFieldNames outputs the code to create a sorted slice of fieldNames to
26+ // late initialize. This slice helps with short circuiting the AWSResourceManager.LateInitialize()
27+ // method if there are no fields to late initialize.
28+ //
29+ // Sample Output:
30+ // var lateInitializeFieldNames = []string{"Name"}
31+ func FindLateInitializedFieldNames (
2832 cfg * ackgenconfig.Config ,
2933 r * model.CRD ,
34+ resVarName string ,
3035 // Number of levels of indentation to use
3136 indentLevel int ,
3237) string {
33- //Sample output
34- //var lateInitializeFieldToDelaySeconds = map[string]int{"Name": 0}
3538 out := ""
3639 indent := strings .Repeat ("\t " , indentLevel )
37- fieldNameToConfig := cfg .ResourceFields (r .Names .Original )
38- if len (fieldNameToConfig ) > 0 {
39- fieldNameToDelaySeconds := make (map [string ]int )
40- sortedLateInitFieldNames := make ([]string , 0 )
41- for fName , fConfig := range fieldNameToConfig {
42- if fConfig != nil && fConfig .LateInitialize != nil {
43- fieldNameToDelaySeconds [fName ] = fConfig .LateInitialize .DelaySeconds
44- sortedLateInitFieldNames = append (sortedLateInitFieldNames , fName )
45- }
46- }
47- sort .Strings (sortedLateInitFieldNames )
48- lateInitFieldToDelayValues := ""
49- if len (sortedLateInitFieldNames ) > 0 {
50- for _ , fName := range sortedLateInitFieldNames {
51- lateInitFieldToDelayValues += fmt .Sprintf ("%q:%d," , fName , fieldNameToDelaySeconds [fName ])
52- }
53- out += fmt .Sprintf ("%svar lateInitializeFieldToDelaySeconds = map[string]int{%s}\n " , indent , lateInitFieldToDelayValues )
40+ sortedFieldNames , _ := getSortedLateInitFieldsAndConfig (cfg , r )
41+ if len (sortedFieldNames ) > 0 {
42+ out += fmt .Sprintf ("%svar %s = []string{" , indent , resVarName )
43+ for _ , fName := range sortedFieldNames {
44+ out += fmt .Sprintf ("%q," , fName )
5445 }
46+ out += "}\n "
5547 }
5648 return out
5749}
5850
59- // findLateInitializedFieldNames returns the field names which have LateInitialization configuration inside generator config
60- func findLateInitializedFieldNames (
51+ // getSortedLateInitFieldsAndConfig returns the field names in alphabetically sorted order which have LateInitialization
52+ // configuration inside generator config and also a map from fieldName to LateInitializationConfig.
53+ func getSortedLateInitFieldsAndConfig (
6154 cfg * ackgenconfig.Config ,
6255 r * model.CRD ,
63- ) []string {
64- fieldNames := make ([]string , 0 )
56+ ) ([]string , map [string ]* ackgenconfig.LateInitializeConfig ) {
6557 fieldNameToConfig := cfg .ResourceFields (r .Names .Original )
58+ fieldNameToLateInitConfig := make (map [string ]* ackgenconfig.LateInitializeConfig )
59+ sortedLateInitFieldNames := make ([]string , 0 )
6660 if len (fieldNameToConfig ) > 0 {
6761 for fName , fConfig := range fieldNameToConfig {
6862 if fConfig != nil && fConfig .LateInitialize != nil {
69- fieldNames = append (fieldNames , fName )
63+ fieldNameToLateInitConfig [fName ] = fConfig .LateInitialize
64+ sortedLateInitFieldNames = append (sortedLateInitFieldNames , fName )
7065 }
7166 }
67+ sort .Strings (sortedLateInitFieldNames )
7268 }
73- return fieldNames
69+ return sortedLateInitFieldNames , fieldNameToLateInitConfig
7470}
7571
7672// LateInitializeFromReadOne returns the gocode to set LateInitialization fields from the ReadOne output
@@ -132,13 +128,11 @@ func LateInitializeFromReadOne(
132128 indentLevel int ,
133129) string {
134130 out := ""
135- lateInitializedFieldNames := findLateInitializedFieldNames (cfg , r )
136- // sorting helps produce consistent output for unit test reliability
137- sort .Strings (lateInitializedFieldNames )
131+ lateInitializedFieldNames , _ := getSortedLateInitFieldsAndConfig (cfg , r )
138132 // TODO(vijat@): Add validation for correct field path in lateInitializedFieldNames
139133 for _ , fName := range lateInitializedFieldNames {
140134 // split the field name by period
141- // each substring represents a field. No support for '..' currently
135+ // each substring represents a field.
142136 fNameParts := strings .Split (fName , "." )
143137 // fNameIndentLevel tracks the indentation level for every new line added
144138 // This variable is incremented when building nested if blocks and decremented when closing those if blocks.
@@ -156,14 +150,14 @@ func LateInitializeFromReadOne(
156150 indent := strings .Repeat ("\t " , fNameIndentLevel )
157151 fNamePartAccesor := fmt .Sprintf ("Spec%s.%s" , fParentPath , fNamePart )
158152 if mapShapedParent {
159- fNamePartAccesor = fmt .Sprintf ("Spec%s[%s ]" , fParentPath , fNamePart )
153+ fNamePartAccesor = fmt .Sprintf ("Spec%s[%q ]" , fParentPath , fNamePart )
160154 }
161155 // Handling for all parts except last one
162156 if i != len (fNameParts )- 1 {
163157 out += fmt .Sprintf ("%sif %s.%s != nil && %s.%s != nil {\n " , indent , sourceKoVarName , fNamePartAccesor , targetKoVarName , fNamePartAccesor )
164158 // update fParentPath and fNameIndentLevel for next iteration
165159 if mapShapedParent {
166- fParentPath = fmt .Sprintf ("%s[%s ]" , fParentPath , fNamePart )
160+ fParentPath = fmt .Sprintf ("%s[%q ]" , fParentPath , fNamePart )
167161 mapShapedParent = false
168162 } else {
169163 fParentPath = fmt .Sprintf ("%s.%s" , fParentPath , fNamePart )
@@ -188,3 +182,146 @@ func LateInitializeFromReadOne(
188182 }
189183 return out
190184}
185+
186+ // CalculateRequeueDelay returns the go code which
187+ // a) checks whether all the fields are late initialized and
188+ // b) if any fields are not initialized, updates the 'delayVarNameInt' and 'incompleteInitializationVarNameBool', which
189+ // are used to requeue the requests based on the delay configured in LateInitializationConfig.
190+ //
191+ // Sample GeneratorConfig:
192+ // fields:
193+ // Name:
194+ // late_initialize: {}
195+ // ImageScanningConfiguration.ScanOnPush:
196+ // late_initialize:
197+ // min_backoff_seconds: 5
198+ // max_backoff_seconds: 15
199+ // map..subfield.x:
200+ // late_initialize:
201+ // min_backoff_seconds: 5
202+ // another.map..lastfield:
203+ // late_initialize:
204+ // min_backoff_seconds: 5
205+ // some.list:
206+ // late_initialize:
207+ // min_backoff_seconds: 10
208+ // structA.mapB..structC.valueD:
209+ // late_initialize:
210+ // min_backoff_seconds: 20
211+ //
212+ //
213+ // Sample Output:
214+ // if koWithDefaults.Spec.ImageScanningConfiguration != nil {
215+ // if koWithDefaults.Spec.ImageScanningConfiguration.ScanOnPush == nil {
216+ // delay := (&acktypes.LateInitializationRetryConfig{MinBackoffSeconds:5, MaxBackoffSeconds: 15,}).GetExponentialBackoffSeconds(numInitAttempt)
217+ // requeueDelay = int(math.Max(float64(requeueDelay), float64(delay)))
218+ // incompleteInitialization = true
219+ // }
220+ // }
221+ // if koWithDefaults.Spec.Name == nil {
222+ // delay := (&acktypes.LateInitializationRetryConfig{MinBackoffSeconds:0, MaxBackoffSeconds: 0,}).GetExponentialBackoffSeconds(numInitAttempt)
223+ // requeueDelay = int(math.Max(float64(requeueDelay), float64(delay)))
224+ // incompleteInitialization = true
225+ // }
226+ // if koWithDefaults.Spec.another != nil {
227+ // if koWithDefaults.Spec.another.map != nil {
228+ // if koWithDefaults.Spec.another.map["lastfield"] == nil {
229+ // delay := (&acktypes.LateInitializationRetryConfig{MinBackoffSeconds:5, MaxBackoffSeconds: 0,}).GetExponentialBackoffSeconds(numInitAttempt)
230+ // requeueDelay = int(math.Max(float64(requeueDelay), float64(delay)))
231+ // incompleteInitialization = true
232+ // }
233+ // }
234+ // }
235+ // if koWithDefaults.Spec.map != nil {
236+ // if koWithDefaults.Spec.map["subfield"] != nil {
237+ // if koWithDefaults.Spec.map["subfield"].x == nil {
238+ // delay := (&acktypes.LateInitializationRetryConfig{MinBackoffSeconds:5, MaxBackoffSeconds: 0,}).GetExponentialBackoffSeconds(numInitAttempt)
239+ // requeueDelay = int(math.Max(float64(requeueDelay), float64(delay)))
240+ // incompleteInitialization = true
241+ // }
242+ // }
243+ // }
244+ // if koWithDefaults.Spec.some != nil {
245+ // if koWithDefaults.Spec.some.list == nil {
246+ // delay := (&acktypes.LateInitializationRetryConfig{MinBackoffSeconds:10, MaxBackoffSeconds: 0,}).GetExponentialBackoffSeconds(numInitAttempt)
247+ // requeueDelay = int(math.Max(float64(requeueDelay), float64(delay)))
248+ // incompleteInitialization = true
249+ // }
250+ // }
251+ // if koWithDefaults.Spec.structA != nil {
252+ // if koWithDefaults.Spec.structA.mapB != nil {
253+ // if koWithDefaults.Spec.structA.mapB["structC"] != nil {
254+ // if koWithDefaults.Spec.structA.mapB["structC"].valueD == nil {
255+ // delay := (&acktypes.LateInitializationRetryConfig{MinBackoffSeconds:20, MaxBackoffSeconds: 0,}).GetExponentialBackoffSeconds(numInitAttempt)
256+ // requeueDelay = int(math.Max(float64(requeueDelay), float64(delay)))
257+ // incompleteInitialization = true
258+ // }
259+ // }
260+ // }
261+ // }
262+ func CalculateRequeueDelay (
263+ cfg * ackgenconfig.Config ,
264+ r * model.CRD ,
265+ koVarName string ,
266+ initAttemptVarName string ,
267+ delayVarNameInt string ,
268+ incompleteInitializationVarNameBool string ,
269+ // Number of levels of indentation to use
270+ indentLevel int ,
271+ ) string {
272+ out := ""
273+ sortedLateInitFieldNames , fieldNameToLateInitConfig := getSortedLateInitFieldsAndConfig (cfg , r )
274+ for _ , fName := range sortedLateInitFieldNames {
275+ // split the field name by period
276+ // each substring represents a field.
277+ fNameParts := strings .Split (fName , "." )
278+ // fNameIndentLevel tracks the indentation level for every new line added
279+ // This variable is incremented when building nested if blocks and decremented when closing those if blocks.
280+ fNameIndentLevel := indentLevel
281+ // fParentPath keeps track of parent path for any fNamePart
282+ fParentPath := ""
283+ mapShapedParent := false
284+ for i , fNamePart := range fNameParts {
285+ if fNamePart == "" {
286+ mapShapedParent = true
287+ continue
288+ }
289+ indent := strings .Repeat ("\t " , fNameIndentLevel )
290+ fNamePartAccesor := fmt .Sprintf ("Spec%s.%s" , fParentPath , fNamePart )
291+ if mapShapedParent {
292+ fNamePartAccesor = fmt .Sprintf ("Spec%s[%q]" , fParentPath , fNamePart )
293+ }
294+ // Handling for all parts except last one
295+ if i != len (fNameParts )- 1 {
296+ out += fmt .Sprintf ("%sif %s.%s != nil {\n " , indent , koVarName , fNamePartAccesor )
297+ // update fParentPath and fNameIndentLevel for next iteration
298+ if mapShapedParent {
299+ fParentPath = fmt .Sprintf ("%s[%q]" , fParentPath , fNamePart )
300+ mapShapedParent = false
301+ } else {
302+ fParentPath = fmt .Sprintf ("%s.%s" , fParentPath , fNamePart )
303+ }
304+ fNameIndentLevel = fNameIndentLevel + 1
305+ } else {
306+ // handle last part here
307+ // for last part, if the late initialized field is still nil, calculate the retry backoff using
308+ // acktypes.LateInitializationRetryConfig abstraction and set the incompleteInitialization flag to true
309+ out += fmt .Sprintf ("%sif %s.%s == nil {\n " , indent , koVarName , fNamePartAccesor )
310+ fNameIndentLevel = fNameIndentLevel + 1
311+ indent = strings .Repeat ("\t " , fNameIndentLevel )
312+ minBackoffSeconds := fieldNameToLateInitConfig [fName ].MinBackoffSeconds
313+ maxBackoffSeconds := fieldNameToLateInitConfig [fName ].MaxBackoffSeconds
314+ out += fmt .Sprintf ("%sdelay := (&acktypes.LateInitializationRetryConfig{MinBackoffSeconds:%d, MaxBackoffSeconds: %d,}).GetExponentialBackoffSeconds(%s)\n " , indent , minBackoffSeconds , maxBackoffSeconds , initAttemptVarName )
315+ out += fmt .Sprintf ("%s%s = int(math.Max(float64(%s), float64(delay)))\n " , indent , delayVarNameInt , delayVarNameInt )
316+ out += fmt .Sprintf ("%s%s = true\n " , indent , incompleteInitializationVarNameBool )
317+ }
318+ }
319+ // Close all if blocks with proper indentation
320+ fNameIndentLevel = fNameIndentLevel - 1
321+ for fNameIndentLevel >= indentLevel {
322+ out += fmt .Sprintf ("%s}\n " , strings .Repeat ("\t " , fNameIndentLevel ))
323+ fNameIndentLevel = fNameIndentLevel - 1
324+ }
325+ }
326+ return out
327+ }
0 commit comments