@@ -14,6 +14,9 @@ ok() { echo "$(ts) [OK] $*"; }
1414declare -A SC_SET
1515declare -g -A SC_CACHE
1616declare -g -A PVC_SC_CACHE
17+ declare -g -A PVCS_MIGRATION_INPROGRESS_MARKED_CACHE
18+ declare -A _UNIQUE_ZONES=()
19+ declare -g -A SNAPSHOTS_ARRAY
1720
1821# ----------- Common configurations -----------
1922MIG_SUFFIX=" ${MIG_SUFFIX:- csi} "
@@ -29,6 +32,7 @@ POLL_INTERVAL="${POLL_INTERVAL:-120}"
2932WAIT_FOR_WORKLOAD=" ${WAIT_FOR_WORKLOAD:- true} "
3033MIGRATION_FORCE_INPROGRESS_AFTER_MINUTES=" ${MIGRATION_FORCE_INPROGRESS_AFTER_MINUTES:- 3} "
3134ONE_SC_FOR_MULTIPLE_ZONES=" ${ONE_SC_FOR_MULTIPLE_ZONES:- true} "
35+ SINGLE_ZONE_USE_GENERIC_PV2_SC=" ${SINGLE_ZONE_USE_GENERIC_PV2_SC:- true} "
3236
3337# In-place rollback keys
3438ROLLBACK_PVC_YAML_ANN=" ${ROLLBACK_PVC_YAML_ANN:- disk.csi.azure.com/ rollback-pvc-yaml} "
@@ -64,7 +68,14 @@ MIGRATION_INPROGRESS_LABEL_KEY="${MIGRATION_INPROGRESS_LABEL_KEY:-disk.csi.azure
6468MIGRATION_INPROGRESS_LABEL_VALUE=" ${MIGRATION_INPROGRESS_LABEL_VALUE:- true} "
6569ZONE_SC_ANNOTATION_KEY=" ${ZONE_SC_ANNOTATION_KEY:- disk.csi.azure.com/ migration-sourcesc} "
6670
71+ # ------------ State ----------
6772LAST_RUN_WITHOUT_ERREXIT_RC=0
73+ GENERIC_PV2_SC_MODE=0
74+
75+ is_direct_exec () {
76+ # realpath + -ef handles symlinks and differing relative paths
77+ [[ " $( realpath " ${BASH_SOURCE[0]} " ) " -ef " $( realpath " $0 " ) " ]]
78+ }
6879
6980audit_add () {
7081 [[ " $AUDIT_ENABLE " != " true" ]] && return 0
@@ -268,20 +279,49 @@ ensure_reclaim_policy_retain() {
268279 fi
269280}
270281
282+ mark_source_in_progress () {
283+ local pvc_ns=" $1 " pvc=" $2 "
284+
285+ if [[ -n ${PVCS_MIGRATION_INPROGRESS_MARKED_CACHE["$pvc_ns/$pvc"]:- } ]]; then
286+ return 0
287+ fi
288+
289+ kcmd label pvc " $pvc " -n " $pvc_ns " " ${MIGRATION_INPROGRESS_LABEL_KEY} =${MIGRATION_INPROGRESS_LABEL_VALUE} " --overwrite > /dev/null
290+
291+ if [[ $? -eq 0 ]]; then
292+ PVCS_MIGRATION_INPROGRESS_MARKED_CACHE[" $pvc_ns /$pvc " ]=1
293+ fi
294+
295+ audit_add " PersistentVolumeClaim" " $pvc " " $pvc_ns " " label" \
296+ " kubectl label pvc $pvc -n $pvc_ns ${MIGRATION_INPROGRESS_LABEL_KEY} -" \
297+ " in-progress=${MIGRATION_INPROGRESS_LABEL_VALUE} "
298+
299+ }
300+
271301mark_source_done () {
272302 local pvc_ns=" $1 " pvc=" $2 "
273- kcmd label pvc " $pvc " -n " $pvc_ns " " ${MIGRATION_DONE_LABEL_KEY} =${MIGRATION_DONE_LABEL_VALUE} " --overwrite
303+ kcmd label pvc " $pvc " -n " $pvc_ns " " ${MIGRATION_DONE_LABEL_KEY} =${MIGRATION_DONE_LABEL_VALUE} " --overwrite > /dev/null
274304 audit_add " PersistentVolumeClaim" " $pvc " " $pvc_ns " " label" \
275305 " kubectl label pvc $pvc -n $pvc_ns ${MIGRATION_DONE_LABEL_KEY} -" \
276306 " done=${MIGRATION_DONE_LABEL_VALUE} "
307+
308+ kcmd label pvc " $pvc " -n " $pvc_ns " " ${MIGRATION_INPROGRESS_LABEL_KEY} -" --overwrite > /dev/null
309+ audit_add " PersistentVolumeClaim" " $pvc " " $pvc_ns " " label-remove" \
310+ " kubectl label pvc $pvc -n $pvc_ns ${MIGRATION_INPROGRESS_LABEL_KEY} -" \
311+ " remove in-progress label"
277312}
278313
279- remove_migration_done_label () {
314+ mark_source_notdone () {
280315 local pvc_ns=" $1 " pvc=" $2 "
281- kcmd label pvc " $pvc " -n " $pvc_ns " " ${MIGRATION_DONE_LABEL_KEY} -" --overwrite
316+ kcmd label pvc " $pvc " -n " $pvc_ns " " ${MIGRATION_DONE_LABEL_KEY} =${MIGRATION_DONE_LABEL_VALUE_FALSE} " --overwrite > /dev/null
317+ audit_add " PersistentVolumeClaim" " $pvc " " $pvc_ns " " label" \
318+ " kubectl label pvc $pvc -n $pvc_ns ${MIGRATION_DONE_LABEL_KEY} -" \
319+ " done=${MIGRATION_DONE_LABEL_VALUE_FALSE} "
320+
321+ kcmd label pvc " $pvc " -n " $pvc_ns " " ${MIGRATION_INPROGRESS_LABEL_KEY} -" --overwrite > /dev/null
282322 audit_add " PersistentVolumeClaim" " $pvc " " $pvc_ns " " label-remove" \
283- " kubectl label pvc $pvc -n $pvc_ns ${MIGRATION_DONE_LABEL_KEY} = " \
284- " remove done label"
323+ " kubectl label pvc $pvc -n $pvc_ns ${MIGRATION_INPROGRESS_LABEL_KEY} - " \
324+ " remove in-progress label"
285325}
286326
287327wait_pvc_bound () {
@@ -417,11 +457,31 @@ backup_pvc() {
417457 fi
418458}
419459
460+ is_pvc_created_by_migration_tool () {
461+ local pvc=" $1 " ns=" $2 "
462+ createdby=$( kcmd get pvc " $pvc " -n " $ns " -o go-template=" {{ index .metadata.labels \" ${CREATED_BY_LABEL_KEY} \" }}" 2> /dev/null || true)
463+ if [[ $createdby == " $MIGRATION_TOOL_ID " ]]; then
464+ echo " true"
465+ else
466+ echo " false"
467+ fi
468+ }
469+
470+ is_pvc_in_migration () {
471+ local pvc=" $1 " ns=" $2 "
472+ inprog=$( kcmd get pvc " $pvc " -n " $ns " -o go-template=" {{ index .metadata.labels \" ${MIGRATION_INPROGRESS_LABEL_KEY} \" }}" 2> /dev/null || true)
473+ if [[ $inprog == " true" ]]; then
474+ echo " true"
475+ else
476+ echo " false"
477+ fi
478+ }
479+
420480get_srcsc_of_sc () {
421481 local sci=" $1 "
422482
423- # if one sc for multiple zones is false, return the original sc name
424- # else if one sc for multiple zones are used, use the zonal annotation based sc name
483+ # if one sc for multiple zones are used, use the incoming storage class which is intermediate based on zone
484+ # else if one sc for multiple zones is false, return the sc name and not source sc [which means one storage class per zone & dont need naming in storage class]
425485 if [[ " $ONE_SC_FOR_MULTIPLE_ZONES " == " true" ]]; then
426486 printf ' %s' " $sci "
427487 return 0
@@ -1030,6 +1090,40 @@ extract_event_reason() {
10301090 end'
10311091}
10321092
1093+ detect_generic_pv2_mode () {
1094+ if [[ " ${# MIG_PVCS[@]} " -gt 0 ]]; then
1095+ for pvc_entry in " ${MIG_PVCS[@]} " ; do
1096+ local _ns=" ${pvc_entry%% |* } "
1097+ local _name=" ${pvc_entry##* |} "
1098+ local _pv=" $( kcmd get pvc " $_name " -n " $_ns " -o jsonpath=' {.spec.volumeName}' 2> /dev/null || true) "
1099+ [[ -z " $_pv " ]] && continue
1100+ local _zone=" $( extract_zone_from_pv_nodeaffinity " $_pv " || true) "
1101+ if [[ -n " $_zone " ]]; then
1102+ _UNIQUE_ZONES[" $_zone " ]=1
1103+ fi
1104+
1105+ is_created_by_migrator=$( is_pvc_in_migration " $_name " " $_ns " )
1106+ if [[ $is_created_by_migrator == " true" ]]; then
1107+ info " skipping detect_generic_pv2_mode as the migration have already kicked in"
1108+ return
1109+ fi
1110+
1111+ is_migration=$( is_pvc_created_by_migration_tool " $_name " " $_ns " )
1112+ if [[ $is_migration == " true" ]]; then
1113+ info " skipping detect_generic_pv2_mode as there are pvcs created by migration tool"
1114+ return
1115+ fi
1116+ done
1117+ fi
1118+ local _unique_zone_count=" ${# _UNIQUE_ZONES[@]} "
1119+ if [[ " $_unique_zone_count " == " 1" && " $SINGLE_ZONE_USE_GENERIC_PV2_SC " == " true" ]]; then
1120+ info " Single zone detected across all PVCs; will use generic pv2 naming (<origSC>-pv2) instead of zone suffix."
1121+ GENERIC_PV2_SC_MODE=1
1122+ else
1123+ GENERIC_PV2_SC_MODE=0
1124+ fi
1125+ }
1126+
10331127populate_pvcs () {
10341128 if [[ -z " $NAMESPACE " ]]; then
10351129 mapfile -t MIG_PVCS < <( kcmd get pvc --all-namespaces -l " $MIGRATION_LABEL " -o jsonpath=' {range .items[*]}{.metadata.namespace}{"|"}{.metadata.name}{"\n"}{end}' )
@@ -1085,7 +1179,8 @@ create_unique_storage_classes() {
10851179 [[ -n " $sc " ]] && SC_SET[" $sc " ]=1
10861180 done
10871181 SOURCE_SCS=(" ${! SC_SET[@]} " )
1088- info " Source StorageClasses: ${SOURCE_SCS[*]} "
1182+ info " Source StorageClasses:"
1183+ printf ' %s\n' " ${SOURCE_SCS[@]} "
10891184 create_variants_for_sources " ${SOURCE_SCS[@]} "
10901185}
10911186
@@ -1275,20 +1370,11 @@ EOF
12751370 audit_add " VolumeSnapshot" " $snap " " $ns " " create" " kubectl delete volumesnapshot $snap -n $ns " " $extra "
12761371 fi
12771372
1278- # Wait up to 3 minutes (36 * 5s) – same as dual
1279- for _ in {1..36}; do
1280- local ready
1281- ready=$( kcmd get volumesnapshot " $snap " -n " $ns " -o jsonpath=' {.status.readyToUse}' 2> /dev/null || true)
1282- [[ " $ready " == " true" ]] && { ok " Snapshot $ns /$snap ready" ; return 0; }
1283- sleep 5
1284- done
1285- audit_add " VolumeSnapshot" " $snap " " $ns " " not-ready" " N/A" " sourcePVC=$source_pvc recreate=$recreated reason=timeout"
1286- warn " Snapshot $ns /$snap not ready after timeout"
1287- return 1
1373+ SNAPSHOTS_ARRAY+=(" $ns |$snap |$source_pvc " )
12881374}
12891375
12901376# --- Snapshot routines ---
1291- ensure_snapshot () {
1377+ create_snapshot () {
12921378 local snap=$1 source_pvc=" $2 " ns=" $3 " pv=" $4 "
12931379 set +e
12941380 ensure_volume_snapshot " $ns " " $source_pvc " " $snap "
@@ -1297,6 +1383,33 @@ ensure_snapshot() {
12971383 return $rc
12981384}
12991385
1386+ wait_for_snapshots_ready () {
1387+ info " Waiting for all snapshots to be ready..."
1388+ SOURCE_SNAPSHOTS=(" ${! SNAPSHOTS_ARRAY[@]} " )
1389+ info " Source Snapshots:"
1390+ printf ' %s\n' " ${SOURCE_SNAPSHOTS[@]} "
1391+
1392+ for ns_snap in " ${SOURCE_SNAPSHOTS[@]} " ; do
1393+ IFS=' |' read -r ns snap source_pvc <<< " $ns_snap"
1394+ local ready
1395+
1396+ info " Waiting for snapshot $ns /$snap to be ready..."
1397+ # Wait up to 3 minutes (36 * 5s) – same as dual
1398+ for _ in {1..36}; do
1399+ ready=$( kcmd get volumesnapshot " $snap " -n " $ns " -o jsonpath=' {.status.readyToUse}' 2> /dev/null || true)
1400+ [[ " $ready " == " true" ]] && { break ; }
1401+ sleep 5
1402+ done
1403+ if [[ " $ready " == " true" ]]; then
1404+ ok " Snapshot $ns /$snap ready" ;
1405+ continue
1406+ fi
1407+ audit_add " VolumeSnapshot" " $snap " " $ns " " not-ready" " N/A" " sourcePVC=$source_pvc reason=timeout"
1408+ warn " Snapshot $ns /$snap not ready after timeout"
1409+ return 1
1410+ done
1411+ }
1412+
13001413print_migration_cleanup_report () {
13011414 local mode=" ${MODE:- dual} "
13021415 local success_header_printed=false
0 commit comments