Skip to content

Commit 9c35918

Browse files
authored
Merge pull request #95 from mbaijal/infinite-requeue-2
Provide an option to configure a resource to requeue infinitely (Issue #826)
2 parents 13160d8 + cb1bbe6 commit 9c35918

File tree

7 files changed

+157
-5
lines changed

7 files changed

+157
-5
lines changed

pkg/generate/config/resource.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ type ResourceConfig struct {
5656
// very little consistency to the APIs that we can use to instruct the code
5757
// generator :(
5858
UpdateOperation *UpdateOperationConfig `json:"update_operation,omitempty"`
59+
// Reconcile describes options for controlling the reconciliation
60+
// logic for a particular resource.
61+
Reconcile *ReconcileConfig `json:"reconcile,omitempty"`
5962
// UpdateConditionsCustomMethodName provides the name of the custom method on the
6063
// `resourceManager` struct that will set Conditions on a `resource` struct
6164
// depending on the status of the resource.
@@ -275,6 +278,21 @@ type PrintConfig struct {
275278
OrderBy string `json:"order_by"`
276279
}
277280

281+
// ReconcileConfig describes options for controlling the reconciliation
282+
// logic for a particular resource.
283+
type ReconcileConfig struct {
284+
// RequeueOnSuccessSeconds indicates the number of seconds after which to requeue a
285+
// resource that has been successfully reconciled (i.e. ConditionTypeResourceSynced=true)
286+
// This is useful for resources that are long-lived and may have observable status fields
287+
// change over time that would be useful to refresh those field values for users.
288+
// This field is optional and the default behaviour of the ACK runtime is to not requeue
289+
// resources that have been successfully reconciled. Note that all ACK controllers will
290+
// *flush and resync their watch caches* every 10 hours by default, which will end up
291+
// causing ACK controllers to refresh the status views of all watched resources, but this
292+
// behaviour is expensive and may be turned off in future ACK runtime options.
293+
RequeueOnSuccessSeconds int `json:"requeue_on_success_seconds,omitempty"`
294+
}
295+
278296
// ResourceConfig returns the ResourceConfig for a given named resource
279297
func (c *Config) ResourceConfig(name string) (*ResourceConfig, bool) {
280298
rc, ok := c.Resources[name]

pkg/generate/sagemaker_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,89 @@ func TestSageMaker_Error_Suffix_Message(t *testing.T) {
140140
// Validation Exception has suffix ModelPackageGroup arn:aws:sagemaker:/ does not exist
141141
assert.Equal("&& strings.HasSuffix(awsErr.Message(), \"does not exist.\") ", code.CheckExceptionMessage(crd.Config(), crd, 404))
142142
}
143+
144+
func TestSageMaker_RequeueOnSuccessSeconds(t *testing.T) {
145+
assert := assert.New(t)
146+
require := require.New(t)
147+
148+
g := testutil.NewGeneratorForService(t, "sagemaker")
149+
150+
crds, err := g.GetCRDs()
151+
require.Nil(err)
152+
153+
crd := getCRDByName("Endpoint", crds)
154+
require.NotNil(crd)
155+
156+
// The CreateEndpoint has the following definition:
157+
//
158+
// "CreateEndpoint":{
159+
// "name":"CreateEndpoint",
160+
// "http":{
161+
// "method":"POST",
162+
// "requestUri":"/"
163+
// },
164+
// "input":{"shape":"CreateEndpointInput"},
165+
// "output":{"shape":"CreateEndpointOutput"},
166+
// "errors":[
167+
// {"shape":"ResourceLimitExceeded"}
168+
// ]
169+
// }
170+
//
171+
// Where the CreateEndpointOutput shape looks like this:
172+
//
173+
// "CreateEndpointOutput":{
174+
// "type":"structure",
175+
// "required":["EndpointArn"],
176+
// "members":{
177+
// "EndpointArn":{"shape":"EndpointArn"}
178+
// }
179+
// }
180+
//
181+
// So, we expect that crd.ReconcileRequeuOnSuccessSeconds() returns the requeue
182+
// duration specified in the config file
183+
assert.Equal(10, crd.ReconcileRequeuOnSuccessSeconds())
184+
}
185+
186+
func TestSageMaker_RequeueOnSuccessSeconds_Default(t *testing.T) {
187+
assert := assert.New(t)
188+
require := require.New(t)
189+
190+
g := testutil.NewGeneratorForService(t, "sagemaker")
191+
192+
crds, err := g.GetCRDs()
193+
require.Nil(err)
194+
195+
crd := getCRDByName("DataQualityJobDefinition", crds)
196+
require.NotNil(crd)
197+
198+
// The CreateDataQualityJobDefinition has the following definition:
199+
//
200+
// "CreateDataQualityJobDefinition":{
201+
// "name":"CreateDataQualityJobDefinition",
202+
// "http":{
203+
// "method":"POST",
204+
// "requestUri":"/"
205+
// },
206+
// "input":{"shape":"CreateDataQualityJobDefinitionRequest"},
207+
// "output":{"shape":"CreateDataQualityJobDefinitionResponse"},
208+
// "errors":[
209+
// {"shape":"ResourceLimitExceeded"},
210+
// {"shape":"ResourceInUse"}
211+
// ]
212+
// }
213+
//
214+
// Where the CreateDataQualityJobDefinitionResponse shape looks like this:
215+
//
216+
// "CreateDataQualityJobDefinitionResponse":{
217+
// "type":"structure",
218+
// "required":["JobDefinitionArn"],
219+
// "members":{
220+
// "JobDefinitionArn":{"shape":"MonitoringJobDefinitionArn"}
221+
// }
222+
// }
223+
//
224+
// So, we expect that crd.ReconcileRequeuOnSuccessSeconds() returns the default
225+
// requeue duration of 0 because it is not specified in the config file
226+
assert.Equal(0, crd.ReconcileRequeuOnSuccessSeconds())
227+
228+
}

pkg/generate/testdata/models/apis/sagemaker/0000-00-00/generator.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ resources:
1919
404:
2020
code: ValidationException
2121
message_suffix: does not exist.
22+
Endpoint:
23+
reconcile:
24+
requeue_on_success_seconds: 10
2225
ignore:
2326
resource_names:
2427
- Algorithm
@@ -35,7 +38,7 @@ ignore:
3538
- Domain
3639
- EdgePackagingJob
3740
- EndpointConfig
38-
- Endpoint
41+
# - Endpoint
3942
- Experiment
4043
- FeatureGroup
4144
- FlowDefinition

pkg/model/crd.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,24 @@ func (r *CRD) PrintAgeColumn() bool {
556556
return r.cfg.GetResourcePrintAddAgeColumn(r.Names.Camel)
557557
}
558558

559+
// ReconcileRequeuOnSuccessSeconds returns the duration after which to requeue
560+
// the custom resource as int, if specified in generator config.
561+
func (r *CRD) ReconcileRequeuOnSuccessSeconds() int {
562+
if r.cfg == nil {
563+
return 0
564+
}
565+
resGenConfig, found := r.cfg.Resources[r.Names.Original]
566+
if !found {
567+
return 0
568+
}
569+
reconcile := resGenConfig.Reconcile
570+
if reconcile != nil {
571+
return reconcile.RequeueOnSuccessSeconds
572+
}
573+
// handles the default case
574+
return 0
575+
}
576+
559577
// CustomUpdateMethodName returns the name of the custom resourceManager method
560578
// for updating the resource state, if any has been specified in the generator
561579
// config

templates/pkg/resource/manager.go.tpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ func (rm *resourceManager) onError(
184184
r *resource,
185185
err error,
186186
) (acktypes.AWSResource, error) {
187-
r1, updated := rm.updateConditions(r, err)
187+
r1, updated := rm.updateConditions(r, false, err)
188188
if !updated {
189189
return r, err
190190
}
@@ -204,7 +204,7 @@ func (rm *resourceManager) onError(
204204
func (rm *resourceManager) onSuccess(
205205
r *resource,
206206
) (acktypes.AWSResource, error) {
207-
r1, updated := rm.updateConditions(r, nil)
207+
r1, updated := rm.updateConditions(r, true, nil)
208208
if !updated {
209209
return r, nil
210210
}

templates/pkg/resource/manager_factory.go.tpl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ func (f *resourceManagerFactory) IsAdoptable() bool {
6666
return {{ .CRD.IsAdoptable }}
6767
}
6868

69+
// RequeueOnSuccessSeconds returns true if the resource should be requeued after specified seconds
70+
// Default is false which means resource will not be requeued after success.
71+
func (f *resourceManagerFactory) RequeueOnSuccessSeconds() int {
72+
{{- if $reconcileRequeuOnSuccessSeconds := .CRD.ReconcileRequeuOnSuccessSeconds }}
73+
return {{ $reconcileRequeuOnSuccessSeconds }}
74+
{{- else }}
75+
return 0
76+
{{- end }}
77+
}
78+
6979
func newResourceManagerFactory() *resourceManagerFactory {
7080
return &resourceManagerFactory{
7181
rmCache: map[string]*resourceManager{},

templates/pkg/resource/sdk.go.tpl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ func (rm *resourceManager) setStatusDefaults (
189189
// else it returns nil, false
190190
func (rm *resourceManager) updateConditions (
191191
r *resource,
192+
onSuccess bool,
192193
err error,
193194
) (*resource, bool) {
194195
ko := r.ko.DeepCopy()
@@ -197,13 +198,17 @@ func (rm *resourceManager) updateConditions (
197198
// Terminal condition
198199
var terminalCondition *ackv1alpha1.Condition = nil
199200
var recoverableCondition *ackv1alpha1.Condition = nil
201+
var syncCondition *ackv1alpha1.Condition = nil
200202
for _, condition := range ko.Status.Conditions {
201203
if condition.Type == ackv1alpha1.ConditionTypeTerminal {
202204
terminalCondition = condition
203205
}
204206
if condition.Type == ackv1alpha1.ConditionTypeRecoverable {
205207
recoverableCondition = condition
206208
}
209+
if condition.Type == ackv1alpha1.ConditionTypeResourceSynced {
210+
syncCondition = condition
211+
}
207212
}
208213

209214
if rm.terminalAWSError(err) {
@@ -245,15 +250,27 @@ func (rm *resourceManager) updateConditions (
245250
}
246251
}
247252

253+
{{- if $reconcileRequeuOnSuccessSeconds := .CRD.ReconcileRequeuOnSuccessSeconds }}
254+
if syncCondition == nil && onSuccess {
255+
syncCondition = &ackv1alpha1.Condition{
256+
Type: ackv1alpha1.ConditionTypeResourceSynced,
257+
Status: corev1.ConditionTrue,
258+
}
259+
ko.Status.Conditions = append(ko.Status.Conditions, syncCondition)
260+
}
261+
{{- else }}
262+
// Required to avoid the "declared but not used" error in the default case
263+
_ = syncCondition
264+
{{- end }}
248265

249266
{{- if $updateConditionsCustomMethodName := .CRD.UpdateConditionsCustomMethodName }}
250267
// custom update conditions
251268
customUpdate := rm.{{ $updateConditionsCustomMethodName }}(ko, r, err)
252-
if terminalCondition != nil || recoverableCondition != nil || customUpdate {
269+
if terminalCondition != nil || recoverableCondition != nil || syncCondition != nil || customUpdate {
253270
return &resource{ko}, true // updated
254271
}
255272
{{- else }}
256-
if terminalCondition != nil || recoverableCondition != nil {
273+
if terminalCondition != nil || recoverableCondition != nil || syncCondition != nil {
257274
return &resource{ko}, true // updated
258275
}
259276
{{- end }}

0 commit comments

Comments
 (0)