Skip to content
This repository was archived by the owner on Oct 6, 2025. It is now read-only.

Commit cfb403e

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

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
@@ -1235,6 +1235,80 @@ func formatFieldChange(field string, currentVal, desiredVal any) string {
12351235
field, formatValue(currentVal), formatValue(desiredVal))
12361236
}
12371237

1238+
// validateStatefulSetUpdate checks for changes to immutable fields in a StatefulSet
1239+
// Returns the formatted error message and true if immutable fields were changed
1240+
func (sc *syncContext) validateStatefulSetUpdate(current, desired *unstructured.Unstructured) (string, bool) {
1241+
currentSpec, _, _ := unstructured.NestedMap(current.Object, "spec")
1242+
desiredSpec, _, _ := unstructured.NestedMap(desired.Object, "spec")
1243+
1244+
changes := getImmutableFieldChanges(currentSpec, desiredSpec)
1245+
if len(changes) == 0 {
1246+
return "", false
1247+
}
1248+
1249+
sort.Strings(changes)
1250+
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",
1251+
strings.Join(changes, "\n"))
1252+
return message, true
1253+
}
1254+
1255+
// getImmutableFieldChanges compares specs and returns a list of changes to immutable fields
1256+
func getImmutableFieldChanges(currentSpec, desiredSpec map[string]any) []string {
1257+
mutableFields := map[string]bool{
1258+
"replicas": true, "ordinals": true, "template": true,
1259+
"updateStrategy": true, "persistentVolumeClaimRetentionPolicy": true,
1260+
"minReadySeconds": true,
1261+
}
1262+
1263+
var changes []string
1264+
for k, desiredVal := range desiredSpec {
1265+
if mutableFields[k] {
1266+
continue
1267+
}
1268+
1269+
currentVal, exists := currentSpec[k]
1270+
if !exists {
1271+
changes = append(changes, formatFieldChange(k, nil, desiredVal))
1272+
continue
1273+
}
1274+
1275+
if !reflect.DeepEqual(currentVal, desiredVal) {
1276+
if k == "volumeClaimTemplates" {
1277+
changes = append(changes, formatVolumeClaimChanges(currentVal, desiredVal)...)
1278+
} else {
1279+
changes = append(changes, formatFieldChange(k, currentVal, desiredVal))
1280+
}
1281+
}
1282+
}
1283+
return changes
1284+
}
1285+
1286+
// formatVolumeClaimChanges handles the special case of formatting changes to volumeClaimTemplates
1287+
func formatVolumeClaimChanges(currentVal, desiredVal any) []string {
1288+
currentTemplates := currentVal.([]any)
1289+
desiredTemplates := desiredVal.([]any)
1290+
1291+
if len(currentTemplates) != len(desiredTemplates) {
1292+
return []string{formatFieldChange("volumeClaimTemplates", currentVal, desiredVal)}
1293+
}
1294+
1295+
var changes []string
1296+
for i := range desiredTemplates {
1297+
desiredTemplate := desiredTemplates[i].(map[string]any)
1298+
currentTemplate := currentTemplates[i].(map[string]any)
1299+
1300+
name := desiredTemplate["metadata"].(map[string]any)["name"].(string)
1301+
desiredStorage := getTemplateStorage(desiredTemplate)
1302+
currentStorage := getTemplateStorage(currentTemplate)
1303+
1304+
if currentStorage != desiredStorage {
1305+
changes = append(changes, fmt.Sprintf(" - volumeClaimTemplates.%s:\n from: %q\n to: %q",
1306+
name, currentStorage, desiredStorage))
1307+
}
1308+
}
1309+
return changes
1310+
}
1311+
12381312
func (sc *syncContext) applyObject(t *syncTask, dryRun, validate bool) (common.ResultCode, string) {
12391313
dryRunStrategy := cmdutil.DryRunNone
12401314
if dryRun {
@@ -1286,67 +1360,9 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, validate bool) (common.R
12861360
message, err = sc.resourceOps.ApplyResource(context.TODO(), t.targetObj, dryRunStrategy, force, validate, serverSideApply, sc.serverSideApplyManager)
12871361
}
12881362
if err != nil {
1289-
// Check if this is a StatefulSet immutable field error
12901363
if strings.Contains(err.Error(), "updates to statefulset spec for fields other than") {
1291-
current := t.liveObj
1292-
desired := t.targetObj
1293-
1294-
if current != nil && desired != nil {
1295-
currentSpec, _, _ := unstructured.NestedMap(current.Object, "spec")
1296-
desiredSpec, _, _ := unstructured.NestedMap(desired.Object, "spec")
1297-
1298-
mutableFields := map[string]bool{
1299-
"replicas": true,
1300-
"ordinals": true,
1301-
"template": true,
1302-
"updateStrategy": true,
1303-
"persistentVolumeClaimRetentionPolicy": true,
1304-
"minReadySeconds": true,
1305-
}
1306-
1307-
var changes []string
1308-
for k, desiredVal := range desiredSpec {
1309-
if !mutableFields[k] {
1310-
currentVal, exists := currentSpec[k]
1311-
if !exists {
1312-
changes = append(changes, formatFieldChange(k, nil, desiredVal))
1313-
} else if !reflect.DeepEqual(currentVal, desiredVal) {
1314-
if k == "volumeClaimTemplates" {
1315-
// Handle volumeClaimTemplates specially
1316-
currentTemplates := currentVal.([]any)
1317-
desiredTemplates := desiredVal.([]any)
1318-
1319-
// If template count differs or we're adding/removing templates,
1320-
// use the standard array format
1321-
if len(currentTemplates) != len(desiredTemplates) {
1322-
changes = append(changes, formatFieldChange(k, currentVal, desiredVal))
1323-
} else {
1324-
// Compare each template
1325-
for i, desired := range desiredTemplates {
1326-
current := currentTemplates[i]
1327-
desiredTemplate := desired.(map[string]any)
1328-
currentTemplate := current.(map[string]any)
1329-
1330-
name := desiredTemplate["metadata"].(map[string]any)["name"].(string)
1331-
desiredStorage := getTemplateStorage(desiredTemplate)
1332-
currentStorage := getTemplateStorage(currentTemplate)
1333-
1334-
if currentStorage != desiredStorage {
1335-
changes = append(changes, fmt.Sprintf(" - volumeClaimTemplates.%s:\n from: %q\n to: %q",
1336-
name, currentStorage, desiredStorage))
1337-
}
1338-
}
1339-
}
1340-
} else {
1341-
changes = append(changes, formatFieldChange(k, currentVal, desiredVal))
1342-
}
1343-
}
1344-
}
1345-
}
1346-
if len(changes) > 0 {
1347-
sort.Strings(changes)
1348-
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",
1349-
strings.Join(changes, "\n"))
1364+
if t.liveObj != nil && t.targetObj != nil {
1365+
if message, hasChanges := sc.validateStatefulSetUpdate(t.liveObj, t.targetObj); hasChanges {
13501366
return common.ResultCodeSyncFailed, message
13511367
}
13521368
}
@@ -1359,6 +1375,7 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, validate bool) (common.R
13591375
sc.log.Error(err, fmt.Sprintf("failed to ensure that CRD %s is ready", crdName))
13601376
}
13611377
}
1378+
13621379
return common.ResultCodeSynced, message
13631380
}
13641381

0 commit comments

Comments
 (0)