Skip to content

Commit e625f74

Browse files
authored
Immutable fields advisory (#70)
Add an advisory condition if an immutable field is modified. ``` Conditions: Status: False Type: ACK.Terminal Status: True Type: ACK.ResourceSynced Status: False Type: ACK.Recoverable Message: Immutable Spec fields have been modified : AtRestEncryptionEnabled,TransitEncryptionEnabled Status: True Type: ACK.Advisory Description: Reinvent replication group ``` Clears the status upon fixing the field in the manifest ``` Conditions: Status: False Type: ACK.Terminal Status: True Type: ACK.ResourceSynced Status: False Type: ACK.Recoverable Description: Reinvent replication group ``` This commit allows one to specify immutable fields in `generator.yaml`. ``` TransitEncryptionEnabled: is_immutable: true AtRestEncryptionEnabled: is_immutable: true ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 858cded commit e625f74

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

pkg/generate/config/field.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ type FieldConfig struct {
155155
// IsSecret instructs the code generator that this field should be a
156156
// SecretKeyReference.
157157
IsSecret bool `json:"is_secret"`
158+
// IsImmutable instructs the code generator to add advisory conditions
159+
// if user modifies the spec field after resource was created.
160+
IsImmutable bool `json:"is_immutable"`
158161
// From instructs the code generator that the value of the field should
159162
// be retrieved from the specified operation and member path
160163
From *SourceFieldConfig `json:"from,omitempty"`

pkg/model/crd.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,32 @@ func (r *CRD) IsSecretField(path string) bool {
303303
return false
304304
}
305305

306+
// GetImmutableFieldPaths returns list of immutable field paths present in CRD
307+
func (r *CRD) GetImmutableFieldPaths() []string {
308+
fConfigs := r.cfg.ResourceFields(r.Names.Original)
309+
var immutableFields []string
310+
311+
for field, fieldConfig := range fConfigs {
312+
if fieldConfig.IsImmutable {
313+
immutableFields = append(immutableFields, field)
314+
}
315+
}
316+
317+
return immutableFields
318+
}
319+
320+
// HasImmutableFieldChanges helper function that return true if there are any immutable field changes
321+
func (r *CRD) HasImmutableFieldChanges() bool {
322+
fConfigs := r.cfg.ResourceFields(r.Names.Original)
323+
324+
for _, fieldConfig := range fConfigs {
325+
if fieldConfig.IsImmutable {
326+
return true
327+
}
328+
}
329+
return false
330+
}
331+
306332
// SetOutputCustomMethodName returns custom set output operation as *string for
307333
// given operation on custom resource, if specified in generator config
308334
func (r *CRD) SetOutputCustomMethodName(

templates/pkg/resource/sdk.go.tpl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,63 @@ func (rm *resourceManager) terminalAWSError(err error) bool {
276276
return false
277277
{{- end }}
278278
}
279+
280+
{{- if .CRD.HasImmutableFieldChanges }}
281+
// getImmutableFieldChanges returns list of immutable fields from the
282+
func (rm *resourceManager) getImmutableFieldChanges(
283+
delta *ackcompare.Delta,
284+
) []string {
285+
var fields []string;
286+
287+
{{- range $immutableField := .CRD.GetImmutableFieldPaths }}
288+
if delta.DifferentAt("{{$immutableField}}") {
289+
fields = append(fields,"{{$immutableField}}")
290+
}
291+
{{- end }}
292+
293+
return fields
294+
}
295+
296+
// handleImmutableFieldsChangedCondition validates the immutable fields and set appropriate condition
297+
func (rm *resourceManager) handleImmutableFieldsChangedCondition(
298+
r *resource,
299+
delta *ackcompare.Delta,
300+
) *resource {
301+
302+
fields := rm.getImmutableFieldChanges(delta)
303+
ko := r.ko.DeepCopy()
304+
var advisoryCondition *ackv1alpha1.Condition = nil
305+
for _, condition := range ko.Status.Conditions {
306+
if condition.Type == ackv1alpha1.ConditionTypeAdvisory {
307+
advisoryCondition = condition
308+
break
309+
}
310+
}
311+
312+
// Remove the advisory condition if issue is no longer present
313+
if len(fields) == 0 && advisoryCondition != nil{
314+
var newConditions []*ackv1alpha1.Condition
315+
for _, condition := range ko.Status.Conditions {
316+
if condition.Type != ackv1alpha1.ConditionTypeAdvisory {
317+
newConditions = append(newConditions,condition)
318+
}
319+
}
320+
ko.Status.Conditions = newConditions
321+
}
322+
323+
if len(fields) > 0 {
324+
if advisoryCondition == nil {
325+
advisoryCondition = &ackv1alpha1.Condition{
326+
Type: ackv1alpha1.ConditionTypeAdvisory,
327+
}
328+
ko.Status.Conditions = append(ko.Status.Conditions, advisoryCondition)
329+
}
330+
331+
advisoryCondition.Status = corev1.ConditionTrue
332+
message := "Immutable Spec fields have been modified : " + strings.Join(fields, ",")
333+
advisoryCondition.Message = &message
334+
}
335+
336+
return &resource{ko}
337+
}
338+
{{- end }}

templates/pkg/resource/sdk_update.go.tpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ func (rm *resourceManager) sdkUpdate(
3232
if respErr != nil {
3333
return nil, respErr
3434
}
35+
{{- if .CRD.HasImmutableFieldChanges }}
36+
desired = rm.handleImmutableFieldsChangedCondition(desired, delta)
37+
{{- end }}
3538
// Merge in the information we read from the API call above to the copy of
3639
// the original Kubernetes object we passed to the function
3740
ko := desired.ko.DeepCopy()

0 commit comments

Comments
 (0)