@@ -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
@@ -500,7 +560,7 @@ create_csi_pv_pvc() {
500560 audit_add " PersistentVolumeClaim" " $csi_pvc " " $ns " " delete" " N/A" " inplace=true reason=preexisting"
501561 return 1
502562 fi
503- audit_add " PersistentVolumeClaim" " $csi_pvc " " $ns " " delete" " N/A " " inplace=true reason=preexisting "
563+ audit_add " PersistentVolumeClaim" " $csi_pvc " " $ns " " delete" " kubectl create -f <(echo \" $encoded_spec \" | base64 --decode) " " inplace=true reason=replace "
504564
505565 if ! kcmd patch pv " $pv " -p ' {"spec":{"claimRef":null}}' > /dev/null 2>&1 ; then
506566 warn " Clear claimRef failed for PV $pv "
@@ -637,7 +697,7 @@ create_pvc_from_snapshot() {
637697 audit_add " PersistentVolumeClaim" " $pvc " " $ns " " delete-failed" " N/A" " inplace=true reason=preexisting"
638698 return 1
639699 fi
640- audit_add " PersistentVolumeClaim" " $pvc " " $ns " " delete" " N/A " " inplace=true reason=preexisting"
700+ audit_add " PersistentVolumeClaim" " $pvc " " $ns " " delete" " kubectl create -f <(echo \" $encoded_spec \" | base64 --decode) " " inplace=true reason=preexisting"
641701
642702 if ! kcmd patch pv " $pv " -p ' {"spec":{"claimRef":null}}' > /dev/null 2>&1 ; then
643703 warn " Clear claimRef failed for PV $pv "
678738 audit_add " PersistentVolumeClaim" " $destpvc " " $ns " " create-failed" " N/A" " inplace=$inplace sc=$sc reason=applyFailure"
679739 return 1
680740 else
741+ audit_add " PersistentVolumeClaim" " $destpvc " " $ns " " create" " kubectl delete pvc $destpvc -n $ns " " inplace=$inplace sc=${sc} snapshot=${snapshot} "
681742 audit_add " PersistentVolumeClaim" " $destpvc " " $ns " " create" " kubectl delete pvc $destpvc -n $ns " " sc=${sc} snapshot=${snapshot} "
682- if $inplace ; then
683- # remove ROLLBACK_PVC_YAML_ANN annotation from pvc
684- kcmd annotate pvc " $destpvc " -n " $ns " " ${ROLLBACK_PVC_YAML_ANN} -" --overwrite > /dev/null 2>&1 || true
685- kcmd annotate pvc " $destpvc " -n " $ns " " ${ROLLBACK_ORIG_PV_ANN} -" --overwrite > /dev/null 2>&1 || true
686- fi
687743 fi
688744
689745 if wait_pvc_bound " $ns " " $destpvc " " $BIND_TIMEOUT_SECONDS " ; then
@@ -1030,6 +1086,40 @@ extract_event_reason() {
10301086 end'
10311087}
10321088
1089+ detect_generic_pv2_mode () {
1090+ if [[ " ${# MIG_PVCS[@]} " -gt 0 ]]; then
1091+ for pvc_entry in " ${MIG_PVCS[@]} " ; do
1092+ local _ns=" ${pvc_entry%% |* } "
1093+ local _name=" ${pvc_entry##* |} "
1094+ local _pv=" $( kcmd get pvc " $_name " -n " $_ns " -o jsonpath=' {.spec.volumeName}' 2> /dev/null || true) "
1095+ [[ -z " $_pv " ]] && continue
1096+ local _zone=" $( extract_zone_from_pv_nodeaffinity " $_pv " || true) "
1097+ if [[ -n " $_zone " ]]; then
1098+ _UNIQUE_ZONES[" $_zone " ]=1
1099+ fi
1100+
1101+ is_created_by_migrator=$( is_pvc_in_migration " $_name " " $_ns " )
1102+ if [[ $is_created_by_migrator == " true" ]]; then
1103+ info " skipping detect_generic_pv2_mode as the migration have already kicked in"
1104+ return
1105+ fi
1106+
1107+ is_migration=$( is_pvc_created_by_migration_tool " $_name " " $_ns " )
1108+ if [[ $is_migration == " true" ]]; then
1109+ info " skipping detect_generic_pv2_mode as there are pvcs created by migration tool"
1110+ return
1111+ fi
1112+ done
1113+ fi
1114+ local _unique_zone_count=" ${# _UNIQUE_ZONES[@]} "
1115+ if [[ " $_unique_zone_count " == " 1" && " $SINGLE_ZONE_USE_GENERIC_PV2_SC " == " true" ]]; then
1116+ info " Single zone detected across all PVCs; will use generic pv2 naming (<origSC>-pv2) instead of zone suffix."
1117+ GENERIC_PV2_SC_MODE=1
1118+ else
1119+ GENERIC_PV2_SC_MODE=0
1120+ fi
1121+ }
1122+
10331123populate_pvcs () {
10341124 if [[ -z " $NAMESPACE " ]]; then
10351125 mapfile -t MIG_PVCS < <( kcmd get pvc --all-namespaces -l " $MIGRATION_LABEL " -o jsonpath=' {range .items[*]}{.metadata.namespace}{"|"}{.metadata.name}{"\n"}{end}' )
@@ -1085,7 +1175,8 @@ create_unique_storage_classes() {
10851175 [[ -n " $sc " ]] && SC_SET[" $sc " ]=1
10861176 done
10871177 SOURCE_SCS=(" ${! SC_SET[@]} " )
1088- info " Source StorageClasses: ${SOURCE_SCS[*]} "
1178+ info " Source StorageClasses:"
1179+ printf ' %s\n' " ${SOURCE_SCS[@]} "
10891180 create_variants_for_sources " ${SOURCE_SCS[@]} "
10901181}
10911182
@@ -1275,20 +1366,11 @@ EOF
12751366 audit_add " VolumeSnapshot" " $snap " " $ns " " create" " kubectl delete volumesnapshot $snap -n $ns " " $extra "
12761367 fi
12771368
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
1369+ SNAPSHOTS_ARRAY+=(" $ns |$snap |$source_pvc " )
12881370}
12891371
12901372# --- Snapshot routines ---
1291- ensure_snapshot () {
1373+ create_snapshot () {
12921374 local snap=$1 source_pvc=" $2 " ns=" $3 " pv=" $4 "
12931375 set +e
12941376 ensure_volume_snapshot " $ns " " $source_pvc " " $snap "
@@ -1297,6 +1379,33 @@ ensure_snapshot() {
12971379 return $rc
12981380}
12991381
1382+ wait_for_snapshots_ready () {
1383+ info " Waiting for all snapshots to be ready..."
1384+ SOURCE_SNAPSHOTS=(" ${! SNAPSHOTS_ARRAY[@]} " )
1385+ info " Source Snapshots:"
1386+ printf ' %s\n' " ${SOURCE_SNAPSHOTS[@]} "
1387+
1388+ for ns_snap in " ${SOURCE_SNAPSHOTS[@]} " ; do
1389+ IFS=' |' read -r ns snap source_pvc <<< " $ns_snap"
1390+ local ready
1391+
1392+ info " Waiting for snapshot $ns /$snap to be ready..."
1393+ # Wait up to 3 minutes (36 * 5s) – same as dual
1394+ for _ in {1..36}; do
1395+ ready=$( kcmd get volumesnapshot " $snap " -n " $ns " -o jsonpath=' {.status.readyToUse}' 2> /dev/null || true)
1396+ [[ " $ready " == " true" ]] && { break ; }
1397+ sleep 5
1398+ done
1399+ if [[ " $ready " == " true" ]]; then
1400+ ok " Snapshot $ns /$snap ready" ;
1401+ continue
1402+ fi
1403+ audit_add " VolumeSnapshot" " $snap " " $ns " " not-ready" " N/A" " sourcePVC=$source_pvc reason=timeout"
1404+ warn " Snapshot $ns /$snap not ready after timeout"
1405+ return 1
1406+ done
1407+ }
1408+
13001409print_migration_cleanup_report () {
13011410 local mode=" ${MODE:- dual} "
13021411 local success_header_printed=false
0 commit comments