Skip to content

Commit 8f55426

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

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
@@ -1226,6 +1226,80 @@ func formatFieldChange(field string, currentVal, desiredVal any) string {
12261226
field, formatValue(currentVal), formatValue(desiredVal))
12271227
}
12281228

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

0 commit comments

Comments
 (0)