Skip to content

Commit ed1bb53

Browse files
IvanHuntersmyasnikovdaniil
authored andcommitted
fix(platform): migrate ephemeralStorage to diskSize via pre-upgrade hook
Add pre-upgrade migration 41 that walks all kuberneteses.apps.cozystack.io Application CRs and renames nodeGroups[*].ephemeralStorage to diskSize, preserving the user's value. Without this migration, clusters upgraded after PR #2454 either fail to reconcile (hard fail blocks all Flux operations) or silently lose the user's disk size setting (default 20Gi replaces whatever was configured). The migration is idempotent and best-effort: a failed patch is logged and leaves the version stamp at 41 for retry on next upgrade. Update the chart-side guard error message to direct operators to the migration job logs when the field appears post-upgrade (regression detector). Bump migrations.targetVersion 41 -> 42. Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
1 parent 9c87996 commit ed1bb53

5 files changed

Lines changed: 119 additions & 6 deletions

File tree

‎packages/apps/kubernetes/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ See the reference for components utilized in this service:
8585

8686
## Breaking Changes
8787

88-
- **`ephemeralStorage` renamed to `diskSize`**: The `nodeGroups[name].ephemeralStorage` field has been renamed to `nodeGroups[name].diskSize` to better reflect its purpose (persistent disk for kubelet and containerd data). There is no backward-compatibility fallback; users MUST update their configurations to use `diskSize` instead of `ephemeralStorage`. If `ephemeralStorage` is still present in values, Helm template rendering will fail with an error directing you to use `diskSize`. When upgrading the CRD directly (bypassing Helm), the unrecognized field is silently dropped and kubelet storage reverts to the default 20Gi. Existing VMs will be automatically rolling-updated via CAPI when the new values are applied. State persists across same-VM reboots (virt-launcher restart, guest reboot, node failure); VM replacement by CAPI (e.g. nodeGroup field change, MachineHealthCheck remediation) provisions a fresh PVC.
88+
- **`ephemeralStorage` renamed to `diskSize`** (v1.4): The `nodeGroups[name].ephemeralStorage` field has been renamed to `nodeGroups[name].diskSize` to better reflect its purpose (persistent disk for kubelet and containerd data). Existing clusters are migrated transparently by platform migration 41 during the pre-upgrade hook — no manual action is required. Newly written values should use `diskSize`. Existing VMs will be automatically rolling-updated via CAPI when the new values are applied. State persists across same-VM reboots (virt-launcher restart, guest reboot, node failure); VM replacement by CAPI (e.g. nodeGroup field change, MachineHealthCheck remediation) provisions a fresh PVC.
8989

9090
## Parameters
9191

‎packages/apps/kubernetes/templates/cluster.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ spec:
3939
cdi.kubevirt.io/storage.usePopulator: "false"
4040
spec:
4141
{{- if hasKey .group "ephemeralStorage" }}
42-
{{- fail (printf "nodeGroup %q: ephemeralStorage is no longer supported. Rename it to diskSize. See README.md for migration instructions." .groupName) }}
42+
{{- fail (printf "nodeGroup %q: ephemeralStorage is no longer supported and should have been automatically migrated to diskSize by platform migration 41. If you see this error after upgrading, the migration did not run — check the cozystack-migration-hook Job logs in cozy-system." .groupName) }}
4343
{{- end }}
4444
source:
4545
blank: {}

‎packages/apps/kubernetes/tests/cluster_test.yaml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ tests:
351351
nodeGroups.md0.ephemeralStorage: 50Gi
352352
asserts:
353353
- failedTemplate:
354-
errorMessage: 'nodeGroup "md0": ephemeralStorage is no longer supported. Rename it to diskSize. See README.md for migration instructions.'
354+
errorMessage: 'nodeGroup "md0": ephemeralStorage is no longer supported and should have been automatically migrated to diskSize by platform migration 41. If you see this error after upgrading, the migration did not run — check the cozystack-migration-hook Job logs in cozy-system.'
355355

356356
- it: fails when ephemeralStorage is set to empty string
357357
release:
@@ -361,7 +361,7 @@ tests:
361361
nodeGroups.md0.ephemeralStorage: ""
362362
asserts:
363363
- failedTemplate:
364-
errorMessage: 'nodeGroup "md0": ephemeralStorage is no longer supported. Rename it to diskSize. See README.md for migration instructions.'
364+
errorMessage: 'nodeGroup "md0": ephemeralStorage is no longer supported and should have been automatically migrated to diskSize by platform migration 41. If you see this error after upgrading, the migration did not run — check the cozystack-migration-hook Job logs in cozy-system.'
365365

366366
- it: fails when both ephemeralStorage and diskSize are set
367367
release:
@@ -372,7 +372,7 @@ tests:
372372
nodeGroups.md0.diskSize: 50Gi
373373
asserts:
374374
- failedTemplate:
375-
errorMessage: 'nodeGroup "md0": ephemeralStorage is no longer supported. Rename it to diskSize. See README.md for migration instructions.'
375+
errorMessage: 'nodeGroup "md0": ephemeralStorage is no longer supported and should have been automatically migrated to diskSize by platform migration 41. If you see this error after upgrading, the migration did not run — check the cozystack-migration-hook Job logs in cozy-system.'
376376

377377
###############################################
378378
# KubeadmConfigTemplate — files block #
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/bin/bash
2+
# Migration 41 --> 42
3+
# Rename nodeGroups[*].ephemeralStorage to nodeGroups[*].diskSize in every
4+
# kuberneteses.apps.cozystack.io Application CR.
5+
#
6+
# PR #2454 renamed the field in the kubernetes app chart with no fallback.
7+
# Without this migration, upgraded clusters either fail to reconcile (hard fail
8+
# behavior) or silently lose their disk size (silent-drop behavior).
9+
#
10+
# Idempotent: re-running on already-migrated values is a no-op
11+
# (has("ephemeralStorage") is false on the post-migration shape).
12+
# Best-effort: a failed patch is logged and leaves the version stamp at 41 so
13+
# the migration retries on the next platform upgrade.
14+
15+
set -euo pipefail
16+
17+
DRY_RUN="${MIGRATION_DRY_RUN:-0}"
18+
FAILURES=0
19+
PATCHED=0
20+
SKIPPED=0
21+
22+
# Emit ephemeralStorage: null rather than dropping the key, because JSON merge
23+
# patch only deletes a field when an explicit null is present in the patch
24+
# document. A patch that simply omits a key leaves the existing key intact.
25+
JQ_TRANSFORM='
26+
walk(
27+
if type == "object" and has("nodeGroups") and (.nodeGroups | type) == "object" then
28+
.nodeGroups |= with_entries(
29+
.value |= (
30+
if type == "object" and has("ephemeralStorage") then
31+
(if has("diskSize") and (.diskSize // "") != "" then .
32+
else .diskSize = .ephemeralStorage end)
33+
| .ephemeralStorage = null
34+
else . end
35+
)
36+
)
37+
else . end
38+
)
39+
'
40+
41+
patch_object() {
42+
local kind="$1" ns="$2" name="$3" selector="$4"
43+
local obj current new patch
44+
45+
obj=$(kubectl get "$kind" --namespace "$ns" "$name" --output json 2>/dev/null || true)
46+
if [ -z "$obj" ]; then SKIPPED=$((SKIPPED + 1)); return 0; fi
47+
48+
current=$(printf '%s' "$obj" | jq -c "$selector // null")
49+
if [ "$current" = "null" ]; then SKIPPED=$((SKIPPED + 1)); return 0; fi
50+
51+
new=$(printf '%s' "$current" | jq -c "$JQ_TRANSFORM")
52+
if [ "$current" = "$new" ]; then SKIPPED=$((SKIPPED + 1)); return 0; fi
53+
54+
patch=$(jq --null-input --argjson val "$new" --arg path "${selector#.}" '
55+
def to_path: split(".");
56+
{} | setpath($path | to_path; $val)
57+
')
58+
59+
if [ "$DRY_RUN" = "1" ]; then
60+
echo "[dry-run] would patch $kind $ns/$name ($selector)"
61+
diff <(printf '%s' "$current" | jq --sort-keys .) \
62+
<(printf '%s' "$new" | jq --sort-keys .) || true
63+
PATCHED=$((PATCHED + 1))
64+
return 0
65+
fi
66+
67+
local kubectl_err
68+
if kubectl_err=$(printf '%s' "$patch" | kubectl patch "$kind" --namespace "$ns" "$name" \
69+
--type merge --patch-file /dev/stdin 2>&1 >/dev/null); then
70+
echo "Patched $kind $ns/$name ($selector)"
71+
PATCHED=$((PATCHED + 1))
72+
else
73+
echo "WARN: failed to patch $kind $ns/$name ($selector): ${kubectl_err:-unknown error}" >&2
74+
FAILURES=$((FAILURES + 1))
75+
fi
76+
}
77+
78+
list_objects() {
79+
local kind="$1" out
80+
if ! out=$(kubectl get "$kind" --all-namespaces \
81+
--output 'jsonpath={range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}' \
82+
2>&1); then
83+
case "$out" in
84+
*"the server doesn't have a resource type"*|*"no matches for kind"*|*"NotFound"*) return 0 ;;
85+
esac
86+
echo "ERROR: kubectl get $kind failed: $out" >&2
87+
return 1
88+
fi
89+
printf '%s' "$out"
90+
}
91+
92+
echo "==> Migrating kuberneteses.apps.cozystack.io"
93+
items=$(list_objects kuberneteses.apps.cozystack.io) || exit 1
94+
while IFS=$'\t' read -r ns name; do
95+
[ -z "$ns" ] && continue
96+
patch_object kuberneteses.apps.cozystack.io "$ns" "$name" .spec
97+
done <<<"$items"
98+
99+
echo "==> Summary: patched=$PATCHED skipped=$SKIPPED failures=$FAILURES" | tee /dev/stderr
100+
101+
if [ "$DRY_RUN" = "1" ]; then
102+
echo "Dry-run complete; version stamp not advanced"
103+
exit 0
104+
fi
105+
106+
if [ "$FAILURES" -gt 0 ]; then
107+
echo "WARN: $FAILURES patch(es) failed; leaving CURRENT_VERSION at 41 for retry on next upgrade."
108+
exit 0
109+
fi
110+
111+
kubectl create configmap --namespace cozy-system cozystack-version \
112+
--from-literal version=42 --dry-run=client --output yaml \
113+
| kubectl apply --filename -

‎packages/core/platform/values.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ sourceRef:
66
migrations:
77
enabled: false
88
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.4.0-rc.2@sha256:17390197a9b11d76a8dd1b24c6d1512adb7d3226b28b5bcba8551b14b1ec4be0
9-
targetVersion: 41
9+
targetVersion: 42
1010
# Bundle deployment configuration
1111
bundles:
1212
system:

0 commit comments

Comments
 (0)