Skip to content

Commit cd6b3a6

Browse files
committed
refactor applyObject method to reduce complexity
Signed-off-by: Atif Ali <[email protected]>
1 parent 5bedc33 commit cd6b3a6

File tree

1 file changed

+77
-60
lines changed

1 file changed

+77
-60
lines changed

pkg/sync/sync_context.go

Lines changed: 77 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,80 @@ func formatFieldChange(field string, currentVal, desiredVal any) string {
11101110
field, formatValue(currentVal), formatValue(desiredVal))
11111111
}
11121112

1113+
// validateStatefulSetUpdate checks for changes to immutable fields in a StatefulSet
1114+
// Returns the formatted error message and true if immutable fields were changed
1115+
func (sc *syncContext) validateStatefulSetUpdate(current, desired *unstructured.Unstructured) (string, bool) {
1116+
currentSpec, _, _ := unstructured.NestedMap(current.Object, "spec")
1117+
desiredSpec, _, _ := unstructured.NestedMap(desired.Object, "spec")
1118+
1119+
changes := getImmutableFieldChanges(currentSpec, desiredSpec)
1120+
if len(changes) == 0 {
1121+
return "", false
1122+
}
1123+
1124+
sort.Strings(changes)
1125+
message := fmt.Sprintf("attempting to change immutable fields:\n%s\n\nForbidden: updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden",
1126+
strings.Join(changes, "\n"))
1127+
return message, true
1128+
}
1129+
1130+
// getImmutableFieldChanges compares specs and returns a list of changes to immutable fields
1131+
func getImmutableFieldChanges(currentSpec, desiredSpec map[string]any) []string {
1132+
mutableFields := map[string]bool{
1133+
"replicas": true, "ordinals": true, "template": true,
1134+
"updateStrategy": true, "persistentVolumeClaimRetentionPolicy": true,
1135+
"minReadySeconds": true,
1136+
}
1137+
1138+
var changes []string
1139+
for k, desiredVal := range desiredSpec {
1140+
if mutableFields[k] {
1141+
continue
1142+
}
1143+
1144+
currentVal, exists := currentSpec[k]
1145+
if !exists {
1146+
changes = append(changes, formatFieldChange(k, nil, desiredVal))
1147+
continue
1148+
}
1149+
1150+
if !reflect.DeepEqual(currentVal, desiredVal) {
1151+
if k == "volumeClaimTemplates" {
1152+
changes = append(changes, formatVolumeClaimChanges(currentVal, desiredVal)...)
1153+
} else {
1154+
changes = append(changes, formatFieldChange(k, currentVal, desiredVal))
1155+
}
1156+
}
1157+
}
1158+
return changes
1159+
}
1160+
1161+
// formatVolumeClaimChanges handles the special case of formatting changes to volumeClaimTemplates
1162+
func formatVolumeClaimChanges(currentVal, desiredVal any) []string {
1163+
currentTemplates := currentVal.([]any)
1164+
desiredTemplates := desiredVal.([]any)
1165+
1166+
if len(currentTemplates) != len(desiredTemplates) {
1167+
return []string{formatFieldChange("volumeClaimTemplates", currentVal, desiredVal)}
1168+
}
1169+
1170+
var changes []string
1171+
for i := range desiredTemplates {
1172+
desiredTemplate := desiredTemplates[i].(map[string]any)
1173+
currentTemplate := currentTemplates[i].(map[string]any)
1174+
1175+
name := desiredTemplate["metadata"].(map[string]any)["name"].(string)
1176+
desiredStorage := getTemplateStorage(desiredTemplate)
1177+
currentStorage := getTemplateStorage(currentTemplate)
1178+
1179+
if currentStorage != desiredStorage {
1180+
changes = append(changes, fmt.Sprintf(" - volumeClaimTemplates.%s:\n from: %q\n to: %q",
1181+
name, currentStorage, desiredStorage))
1182+
}
1183+
}
1184+
return changes
1185+
}
1186+
11131187
func (sc *syncContext) applyObject(t *syncTask, dryRun, validate bool) (common.ResultCode, string) {
11141188
dryRunStrategy := cmdutil.DryRunNone
11151189
if dryRun {
@@ -1150,67 +1224,9 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, validate bool) (common.R
11501224
message, err = sc.resourceOps.ApplyResource(context.TODO(), t.targetObj, dryRunStrategy, force, validate, serverSideApply, sc.serverSideApplyManager)
11511225
}
11521226
if err != nil {
1153-
// Check if this is a StatefulSet immutable field error
11541227
if strings.Contains(err.Error(), "updates to statefulset spec for fields other than") {
1155-
current := t.liveObj
1156-
desired := t.targetObj
1157-
1158-
if current != nil && desired != nil {
1159-
currentSpec, _, _ := unstructured.NestedMap(current.Object, "spec")
1160-
desiredSpec, _, _ := unstructured.NestedMap(desired.Object, "spec")
1161-
1162-
mutableFields := map[string]bool{
1163-
"replicas": true,
1164-
"ordinals": true,
1165-
"template": true,
1166-
"updateStrategy": true,
1167-
"persistentVolumeClaimRetentionPolicy": true,
1168-
"minReadySeconds": true,
1169-
}
1170-
1171-
var changes []string
1172-
for k, desiredVal := range desiredSpec {
1173-
if !mutableFields[k] {
1174-
currentVal, exists := currentSpec[k]
1175-
if !exists {
1176-
changes = append(changes, formatFieldChange(k, nil, desiredVal))
1177-
} else if !reflect.DeepEqual(currentVal, desiredVal) {
1178-
if k == "volumeClaimTemplates" {
1179-
// Handle volumeClaimTemplates specially
1180-
currentTemplates := currentVal.([]any)
1181-
desiredTemplates := desiredVal.([]any)
1182-
1183-
// If template count differs or we're adding/removing templates,
1184-
// use the standard array format
1185-
if len(currentTemplates) != len(desiredTemplates) {
1186-
changes = append(changes, formatFieldChange(k, currentVal, desiredVal))
1187-
} else {
1188-
// Compare each template
1189-
for i, desired := range desiredTemplates {
1190-
current := currentTemplates[i]
1191-
desiredTemplate := desired.(map[string]any)
1192-
currentTemplate := current.(map[string]any)
1193-
1194-
name := desiredTemplate["metadata"].(map[string]any)["name"].(string)
1195-
desiredStorage := getTemplateStorage(desiredTemplate)
1196-
currentStorage := getTemplateStorage(currentTemplate)
1197-
1198-
if currentStorage != desiredStorage {
1199-
changes = append(changes, fmt.Sprintf(" - volumeClaimTemplates.%s:\n from: %q\n to: %q",
1200-
name, currentStorage, desiredStorage))
1201-
}
1202-
}
1203-
}
1204-
} else {
1205-
changes = append(changes, formatFieldChange(k, currentVal, desiredVal))
1206-
}
1207-
}
1208-
}
1209-
}
1210-
if len(changes) > 0 {
1211-
sort.Strings(changes)
1212-
message := fmt.Sprintf("attempting to change immutable fields:\n%s\n\nForbidden: updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden",
1213-
strings.Join(changes, "\n"))
1228+
if t.liveObj != nil && t.targetObj != nil {
1229+
if message, hasChanges := sc.validateStatefulSetUpdate(t.liveObj, t.targetObj); hasChanges {
12141230
return common.ResultCodeSyncFailed, message
12151231
}
12161232
}
@@ -1223,6 +1239,7 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, validate bool) (common.R
12231239
sc.log.Error(err, fmt.Sprintf("failed to ensure that CRD %s is ready", crdName))
12241240
}
12251241
}
1242+
12261243
return common.ResultCodeSynced, message
12271244
}
12281245

0 commit comments

Comments
 (0)