@@ -4,6 +4,7 @@ IFS=$'\n\t'
44
55SCRIPT_START_TS=" $( date +' %Y-%m-%dT%H:%M:%S' ) "
66SCRIPT_START_EPOCH=" $( date +%s) "
7+ MODE=inplace
78
89# In-place rollback keys
910ROLLBACK_PVC_YAML_ANN=" ${ROLLBACK_PVC_YAML_ANN:- disk.csi.azure.com/ rollback-pvc-yaml} "
@@ -16,6 +17,21 @@ declare -a CONFLICT_ISSUES
1617declare -A SC_SET
1718declare -a ROLLBACK_FAILURES
1819
20+ # Explicit zeroing (defensive; avoids 'unbound variable' under set -u even if declarations are edited later)
21+ MIG_PVCS=()
22+ PREREQ_ISSUES=()
23+ CONFLICT_ISSUES=()
24+ ROLLBACK_FAILURES=()
25+
26+ # Helper: safe length (avoids accidental nounset trip if future refactor removes a declare)
27+ safe_array_len () {
28+ # usage: safe_array_len arrayName
29+ local name=" $1 "
30+ declare -p " $name " & > /dev/null || { echo 0; return ; }
31+ # indirect expansion
32+ eval " echo \$ {#$name [@]}"
33+ }
34+
1935SCRIPT_DIR=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd) "
2036. " ${SCRIPT_DIR} /lib-premiumv2-migration-common.sh"
2137
@@ -33,39 +49,40 @@ ensure_snapshot_class # from common lib
3349ensure_no_foreign_conflicts () {
3450 info " Checking for pre-existing conflicting objects not created by this tool..."
3551 if kcmd get volumesnapshotclass " $SNAPSHOT_CLASS " > /dev/null 2>&1 ; then
36- if ! kcmd get volumesnapshotclass " $SNAPSHOT_CLASS " -o json \
37- | jq -e --arg k " $CREATED_BY_LABEL_KEY " --arg v " $MIGRATION_TOOL_ID " ' .metadata.labels[$k]==$v' > /dev/null; then
52+ if ! kcmd get volumesnapshotclass " $SNAPSHOT_CLASS " -o json | jq -e \
53+ --arg k " $CREATED_BY_LABEL_KEY " --arg v " $MIGRATION_TOOL_ID " \
54+ ' .metadata.labels[$k]==$v' > /dev/null; then
3855 CONFLICT_ISSUES+=(" VolumeSnapshotClass/$SNAPSHOT_CLASS (missing label)" )
3956 fi
4057 fi
41- for ENTRY in " ${MIG_PVCS[@]:- } " ; do
58+ for ENTRY in " ${MIG_PVCS[@]} " ; do
4259 local ns=" ${ENTRY%% |* } " pvc=" ${ENTRY##* |} "
43- local pv
60+ local pv snap
4461 pv=$( kcmd get pvc " $pvc " -n " $ns " -o jsonpath=' {.spec.volumeName}' 2> /dev/null || true)
4562 [[ -z " $pv " ]] && continue
46- local snap
4763 snap=" $( name_snapshot " $pv " ) "
4864 if kcmd get volumesnapshot " $snap " -n " $ns " > /dev/null 2>&1 ; then
49- kcmd get volumesnapshot " $snap " -n " $ns " -o json | jq -e --arg k " $CREATED_BY_LABEL_KEY " --arg v " $MIGRATION_TOOL_ID " ' .metadata.labels[$k]==$v' > /dev/null || \
50- CONFLICT_ISSUES+=(" VolumeSnapshot/$ns /$snap missing label" )
65+ kcmd get volumesnapshot " $snap " -n " $ns " -o json | jq -e \
66+ --arg k " $CREATED_BY_LABEL_KEY " --arg v " $MIGRATION_TOOL_ID " \
67+ ' .metadata.labels[$k]==$v' > /dev/null || \
68+ CONFLICT_ISSUES+=(" VolumeSnapshot/$ns /$snap missing label" )
5169 fi
5270 done
53- if (( ${ # CONFLICT_ISSUES[@]} > 0 )) ; then
54- err " Conflict check failed (${ # CONFLICT_ISSUES[@]} items)."
71+ if (( $(safe_array_len CONFLICT_ISSUES) > 0 )) ; then
72+ err " Conflict check failed ($( safe_array_len CONFLICT_ISSUES) items)."
5573 printf ' - %s\n' " ${CONFLICT_ISSUES[@]} "
5674 exit 1
5775 fi
5876 ok " No conflicting pre-existing objects detected."
5977}
6078
61- extract_event_reason () { extract_event_reason " $@ " ; } # use common lib version
62-
6379# ---------------- Discover Tagged PVCs ----------------
6480populate_pvcs
6581
6682# ---------------- Pre-Requisite Validation (mirrors dual script) ----------------
6783print_combined_validation_report_and_exit_if_needed () {
68- local prereq_count=${# PREREQ_ISSUES[@]} conflict_count=${# CONFLICT_ISSUES[@]}
84+ local prereq_count=$( safe_array_len PREREQ_ISSUES)
85+ local conflict_count=$( safe_array_len CONFLICT_ISSUES)
6986 if (( prereq_count== 0 && conflict_count== 0 )) ; then
7087 ok " Pre-req & conflict checks passed."
7188 return
@@ -138,6 +155,33 @@ create_pv2_inplace() {
138155
139156 ensure_reclaim_policy_retain " $pv_before "
140157
158+ # -------- Raw PVC backup (full YAML) prior to deletion --------
159+ if [[ " $BACKUP_ORIGINAL_PVC " == " true" ]]; then
160+ local ts backup_ns_dir backup_file tmp_file
161+ ts=" $( date +%Y%m%d-%H%M%S) "
162+ backup_ns_dir=" ${PVC_BACKUP_DIR} /${ns} "
163+ mkdir -p " $backup_ns_dir "
164+ tmp_file=" ${backup_ns_dir} /${pvc} -${ts} .yaml.tmp"
165+ backup_file=" ${backup_ns_dir} /${pvc} -${ts} .yaml"
166+ info " Backing up original PVC $ns /$pvc to $backup_file "
167+ if ! kcmd get pvc " $pvc " -n " $ns " -o yaml > " $tmp_file " 2> /dev/null; then
168+ err " Failed to fetch PVC $ns /$pvc for backup; skipping migration of this PVC."
169+ audit_add " PersistentVolumeClaim" " $pvc " " $ns " " backup-failed" " kubectl get pvc $pvc -n $ns -o yaml" " dest=$backup_file reason=kubectlError"
170+ return 1
171+ fi
172+ if [[ ! -s " $tmp_file " ]] || ! grep -q ' ^kind: *PersistentVolumeClaim' " $tmp_file " ; then
173+ err " Backup validation failed for $ns /$pvc (empty or malformed); skipping migration."
174+ rm -f " $tmp_file "
175+ audit_add " PersistentVolumeClaim" " $pvc " " $ns " " backup-invalid" " kubectl get pvc $pvc -n $ns -o yaml" " dest=$backup_file reason=validation"
176+ return 1
177+ fi
178+ mv " $tmp_file " " $backup_file "
179+ audit_add " PersistentVolumeClaim" " $pvc " " $ns " " backup" " rm -f $backup_file " " dest=$backup_file size=$( wc -c < " $backup_file " ) B"
180+ else
181+ info " BACKUP_ORIGINAL_PVC=false -> skipping raw PVC backup for $ns /$pvc "
182+ fi
183+ # ---------------------------------------------------------------
184+
141185 info " Deleting original PVC $ns /$pvc (PV retained for rollback)"
142186 kcmd delete pvc " $pvc " -n " $ns " --wait=true
143187 audit_add " PersistentVolumeClaim" " $pvc " " $ns " " delete" " N/A" " stage=replace retainedPV=${pv_before} "
@@ -158,6 +202,7 @@ metadata:
158202 namespace: ${ns}
159203 labels:
160204 ${CREATED_BY_LABEL_KEY} : ${MIGRATION_TOOL_ID}
205+ ${MIGRATION_LABEL_KEY} : "${MIGRATION_LABEL_VALUE} "
161206 annotations:
162207 ${ROLLBACK_PVC_YAML_ANN} : "${encoded_spec} "
163208 ${ROLLBACK_ORIG_PV_ANN} : "${pv_before} "
0 commit comments