@@ -15,6 +15,11 @@ limitations under the License.
1515set -e
1616# set -x
1717
18+ error () { echo " ERROR: $* " >&2 ; }
19+ warn () { echo " WARN: $* " ; }
20+ success () { echo " SUCCESS: $* " ; }
21+ log () { echo " LOG: $* " ; }
22+
1823source <( curl -L https://raw.githubusercontent.com/ocp-power-automation/openshift-install-power/92996305e1a8bef69fbe613b912d5561cc753172/openshift-install-powervs 2> /dev/null | sed ' s/main "$@"//g' )
1924
2025function help {
3641 --cos-access-key string Cloud Storage access key(optional)
3742 --cos-secret-key string Cloud Storage secret key(optional)
3843 --skip-os-password Skip the root user password (optional)
44+ --rhel-sha256 string Expected SHA256 checksum for RHEL image(optional)
45+ --rhcos-sha256 string Expected SHA256 checksum for RHCOS image(optional)
3946 --help help for upload
47+ Environment Variables:
48+ DOWNLOAD_MAX_RETRIES Maximum number of retry attempts if a download fails or the checksum validation fails (default: 3)
49+ DOWNLOAD_RETRY_DELAY Delay between retries in seconds (default: 5)
50+
4051
4152EOF
4253 exit 0
@@ -62,6 +73,11 @@ PVSADM_VERSION="v0.1.11"
6273IMAGE_SIZE=" 11"
6374TARGET_DISK_SIZE=" 120"
6475
76+ # Download retry configuration
77+ DOWNLOAD_MAX_RETRIES=${DOWNLOAD_MAX_RETRIES:- " 3" }
78+ DOWNLOAD_RETRY_DELAY=${DOWNLOAD_RETRY_DELAY:- " 5" }
79+ DOWNLOAD_TIMEOUT=300
80+
6581# Default Centos image name
6682CENTOS_VM_IMAGE_NAME=' CentOS-Stream-8'
6783
@@ -612,16 +628,156 @@ function copy_image_file {
612628}
613629
614630function download_url() {
615- local url=$1
631+ local url=" $1 "
632+ local expected_sha256=" $2 "
616633 local image_name=${url##*/ }
617- rm -rf $image_name
618- retry " curl -fsSL $url -o ./$image_name "
619- if [[ $? -eq 0 ]]; then
620- # IMAGE_PATH=$(realpath ./$image_name)
621- IMAGE_PATH=./$image_name
622- DOWNLOAD_IMAGE_NAME=$image_name
634+ local retry_count=0
635+ local download_success=false
636+
637+ log " ========================================="
638+ log " Starting download: $( basename " $image_name " ) "
639+ log " Source URL: $url "
640+ log " ========================================="
641+
642+ # Validate URL before attempting download
643+ validate_url " $url "
644+
645+ # Remove any existing file
646+ rm -f " $image_name "
647+
648+ # Retry loop: focus purely on download
649+ while [ $retry_count -lt $DOWNLOAD_MAX_RETRIES ]; do
650+ if [ $retry_count -gt 0 ]; then
651+ warn " Retry attempt $retry_count of $DOWNLOAD_MAX_RETRIES "
652+ sleep $DOWNLOAD_RETRY_DELAY
653+ else
654+ log " Download attempt $(( retry_count + 1 )) of $DOWNLOAD_MAX_RETRIES "
655+ fi
656+
657+ log " Downloading $( basename " $image_name " ) ..."
658+ if curl -fLSs --retry 2 --retry-delay 2 --connect-timeout 60 \
659+ --max-time $DOWNLOAD_TIMEOUT " $url " -o " ./$image_name " 2>&1 ; then
660+ download_success=true
661+ break
662+ else
663+ local curl_exit=$?
664+ error " Download failed (curl exit code: $curl_exit )"
665+ case $curl_exit in
666+ 1) error " Could not resolve host (DNS failure)" ;;
667+ 2) error " Failed to connect to host" ;;
668+ 3) error " Partial file transfer" ;;
669+ 4) error " HTTP error (404/403/etc.)" ;;
670+ 5) error " Operation timeout" ;;
671+ 6) error " SSL connection error" ;;
672+ * ) error " See curl manual for exit code $curl_exit " ;;
673+ esac
674+ rm -f " ./$image_name "
675+ retry_count=$(( retry_count + 1 ))
676+ fi
677+ done
678+
679+ # Verify file existence and content after all download attempts
680+ if [ ! -f " ./$image_name " ] || [ ! -s " ./$image_name " ]; then
681+ error " Downloaded file is missing or empty after $DOWNLOAD_MAX_RETRIES attempts."
682+ return 1
683+ fi
684+
685+ # Perform verification once, after successful download
686+ log " Download completed — running one-time verification checks..."
687+
688+ if ! verify_file_size " ./$image_name " " $url " ; then
689+ warn " File size verification failed; please verify manually."
690+ fi
691+
692+ if ! verify_sha256 " ./$image_name " " $expected_sha256 " ; then
693+ error " Checksum verification failed; downloaded file may be corrupted."
694+ return 1
695+ fi
696+
697+ IMAGE_PATH=" ./$image_name "
698+ DOWNLOAD_IMAGE_NAME=" $image_name "
699+
700+ success " ========================================="
701+ success " ✓ Download and verification completed successfully!"
702+ success " File: $( basename " $image_name " ) "
703+ success " Location: $IMAGE_PATH "
704+ success " ========================================="
705+ return 0
706+ }
707+
708+
709+ # All retries failed
710+ if [ " $download_success " = false ]; then
711+ error " ========================================="
712+ error " ✗ Failed to download after $DOWNLOAD_MAX_RETRIES attempts"
713+ error " ========================================="
714+ error " Troubleshooting steps:"
715+ error " 1. Check your internet connection"
716+ error " 2. Verify the URL is correct and accessible:"
717+ error " $url "
718+ error " 3. Ensure special characters in URL are properly escaped"
719+ error " 4. Check if the checksum value is correct"
720+ error " 5. Try downloading manually to diagnose:"
721+ error " curl -LO \" $url \" "
722+ error " 6. Increase retry attempts: export DOWNLOAD_MAX_RETRIES=5"
723+ return 1
724+ fi
725+ }
726+
727+ # -------------------------------------------------------------------------
728+ # Verify file size matches expected size from HTTP headers
729+ # -------------------------------------------------------------------------
730+ function verify_file_size() {
731+ local file=" $1 "
732+ local url=" $2 "
733+
734+ log " Verifying file size for $( basename " $file " ) ..."
735+
736+ # Get expected size from HTTP headers
737+ local expected_size=$( curl -sI " $url " | grep -i " ^content-length:" | awk ' {print $2}' | tr -d ' \r\n' )
738+
739+ if [ -z " $expected_size " ] || [ " $expected_size " = " 0" ]; then
740+ warn " Unable to determine expected file size from server, skipping size verification"
741+ return 0
742+ fi
743+
744+ # Get actual file size
745+ local actual_size=$( stat -c%s " $file " 2> /dev/null || stat -f%z " $file " 2> /dev/null)
746+
747+ log " Expected size: $( numfmt --to=iec-i --suffix=B $expected_size 2> /dev/null || echo " $expected_size bytes" ) "
748+ log " Actual size: $( numfmt --to=iec-i --suffix=B $actual_size 2> /dev/null || echo " $actual_size bytes" ) "
749+
750+ # Allow 1% difference for potential metadata differences
751+ local size_diff=$(( expected_size - actual_size))
752+ local size_diff_abs=${size_diff# -}
753+ local threshold=$(( expected_size / 100 ))
754+
755+ if [ " $size_diff_abs " -le " $threshold " ]; then
756+ success " ✓ File size verification PASSED"
757+ return 0
623758 else
624- error " Unable to fetch the url"
759+ error " ✗ File size verification FAILED (difference: $size_diff_abs bytes)"
760+ return 1
761+ fi
762+ }
763+
764+ # -------------------------------------------------------------------------
765+ # Validate URL for common issues
766+ # -------------------------------------------------------------------------
767+ function validate_url() {
768+ local url=" $1 "
769+
770+ # Check for unescaped ampersands
771+ if [[ " $url " =~ [^\\ ]\& [^\ ] ]]; then
772+ warn " ⚠ Warning: URL contains unescaped & characters"
773+ warn " This may cause download issues. Consider escaping with \\ & or using quotes"
774+ warn " URL: $url "
775+ fi
776+
777+ # Check if URL is accessible
778+ if ! curl -sf --head " $url " > /dev/null 2>&1 ; then
779+ warn " ⚠ Warning: Unable to verify URL accessibility"
780+ warn " This might indicate network issues or incorrect URL"
625781 fi
626782}
627783
@@ -632,15 +788,15 @@ function download_image {
632788 if [[ " $1 " == " rhel" ]]; then
633789 if echo $RHEL_URL | grep -q -i ' access.cdn.redhat.com' ; then
634790 log " downloading rhel image"
635- download_url $RHEL_URL
791+ download_url " $RHEL_URL " " $RHEL_SHA256 "
636792 RHEL_IMAGE=$IMAGE_PATH
637793 RHEL_DOWNLOADED_IMAGE_NAME=$DOWNLOAD_IMAGE_NAME
638794 RHEL_NEW_IMAGE_PATH=$IMAGE_NEW_PATH
639795 COPY_RHEL_IMAGE=1
640796 fi
641797 elif [[ " $1 " == " rhcos" ]]; then
642- download_url $RHCOS_URL
643- RHCOS_IMAGE=IMAGE_PATH
798+ download_url " $RHCOS_URL " " $RHCOS_SHA256 "
799+ RHCOS_IMAGE=$ IMAGE_PATH
644800 RHCOS_DOWNLOAD_IMAGE_NAME=$DOWNLOAD_IMAGE_NAME
645801 copy_image_file $RHCOS_IMAGE $RHCOS_OBJECT_NAME
646802 RHCOS_NEW_IMAGE_PATH=$IMAGE_NEW_PATH
@@ -649,6 +805,61 @@ function download_image {
649805 warn " Unknown image"
650806 fi
651807}
808+ function calc_sha256() {
809+ local f=" $1 "
810+ if command -v sha256sum > /dev/null 2>&1 ; then
811+ sha256sum " $f " | awk ' {print $1}'
812+ elif command -v shasum > /dev/null 2>&1 ; then
813+ shasum -a 256 " $f " | awk ' {print $1}'
814+ elif command -v openssl > /dev/null 2>&1 ; then
815+ openssl dgst -sha256 " $f " | awk ' {print $NF}'
816+ else
817+ error " No SHA-256 tool available (need sha256sum, shasum, or openssl)"
818+ fi
819+ }
820+
821+
822+ function verify_sha256() {
823+ local f=" $1 "
824+ local expected=" $2 "
825+
826+ if [ -z " $expected " ]; then
827+ warn " No checksum provided for $( basename " $f " ) , skipping verification"
828+ return 0
829+ fi
830+
831+ log " Verifying SHA256 checksum for $( basename " $f " ) ..."
832+
833+ local actual
834+ actual=" $( calc_sha256 " $f " ) "
835+
836+ if [ -z " $actual " ]; then
837+ error " Failed to calculate checksum for $f "
838+ return 1
839+ fi
840+
841+ local actual_lc=$( echo " $actual " | tr ' [:upper:]' ' [:lower:]' )
842+ local expected_lc=$( echo " $expected " | tr ' [:upper:]' ' [:lower:]' )
843+
844+ log " Expected: $expected_lc "
845+ log " Actual: $actual_lc "
846+
847+ if [[ " $actual_lc " != " $expected_lc " ]]; then
848+ error " SHA-256 checksum mismatch for $( basename " $f " ) "
849+ error " Expected: $expected_lc "
850+ error " Actual: $actual_lc "
851+ error " Possible causes:"
852+ error " - Incomplete download (network interruption)"
853+ error " - Corrupted file during transfer"
854+ error " - Incorrect URL (check for unescaped special characters like &)"
855+ error " - Wrong checksum value provided"
856+ return 1
857+ fi
858+
859+ success " ✓ Checksum verification PASSED for $( basename " $f " ) "
860+ return 0
861+ }
862+
652863
653864function main {
654865 mkdir -p ./logs
@@ -702,6 +913,14 @@ function main {
702913 " --skip-os-password" )
703914 SKIP_OS_PASSWORD=" --skip-os-password"
704915 ;;
916+ " --rhel-sha256" )
917+ shift
918+ RHEL_SHA256=" $1 "
919+ ;;
920+ " --rhcos-sha256" )
921+ shift
922+ RHCOS_SHA256=" $1 "
923+ ;;
705924 " --help" )
706925 help
707926 ;;
0 commit comments