@@ -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,164 @@ 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 -rf " $image_name "
647+
648+ # Retry loop
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+ # Download with timeout and progress
658+ log " Downloading $( basename " $image_name " ) ..."
659+ if curl -fLSs --retry 2 --retry-delay 2 --connect-timeout 60 --max-time $DOWNLOAD_TIMEOUT " $url " -o " ./$image_name " 2>&1 ; then
660+
661+ # Verify file exists and has content
662+ if [ ! -f " ./$image_name " ] || [ ! -s " ./$image_name " ]; then
663+ error " Downloaded file is missing or empty"
664+ retry_count=$(( retry_count + 1 ))
665+ continue
666+ fi
667+
668+ log " Download completed, verifying integrity..."
669+
670+ # Verify file size
671+ if ! verify_file_size " ./$image_name " " $url " ; then
672+ warn " File size verification failed, retrying..."
673+ rm -f " ./$image_name "
674+ retry_count=$(( retry_count + 1 ))
675+ continue
676+ fi
677+
678+ # Verify SHA256 checksum
679+ if ! verify_sha256 " ./$image_name " " $expected_sha256 " ; then
680+ error " Checksum verification failed, retrying..."
681+ rm -f " ./$image_name "
682+ retry_count=$(( retry_count + 1 ))
683+ continue
684+ fi
685+
686+ # All verifications passed
687+ IMAGE_PATH=" ./$image_name "
688+ DOWNLOAD_IMAGE_NAME=" $image_name "
689+ download_success=true
690+
691+ success " ========================================="
692+ success " ✓ Download and verification completed successfully!"
693+ success " File: $( basename " $image_name " ) "
694+ success " Location: $IMAGE_PATH "
695+ success " ========================================="
696+ return 0
697+ else
698+ local curl_exit=$?
699+ error " Download failed with curl error code: $curl_exit "
700+
701+ # Provide specific error messages for common curl exit codes
702+ case $curl_exit in
703+ 1) error " Reason: Could not resolve host (DNS failure)" ;;
704+ 2) error " Reason: Failed to connect to host" ;;
705+ 3) error " Reason: Partial file transfer" ;;
706+ 4) error " Reason: HTTP error (404, 403, etc.)" ;;
707+ 5) error " Reason: Operation timeout" ;;
708+ 6) error " Reason: SSL connection error" ;;
709+ * ) error " Reason: See curl manual for error code $curl_exit " ;;
710+ esac
711+
712+ rm -f " ./$image_name "
713+ retry_count=$(( retry_count + 1 ))
714+ fi
715+ done
716+
717+ # All retries failed
718+ if [ " $download_success " = false ]; then
719+ error " ========================================="
720+ error " ✗ Failed to download after $DOWNLOAD_MAX_RETRIES attempts"
721+ error " ========================================="
722+ error " Troubleshooting steps:"
723+ error " 1. Check your internet connection"
724+ error " 2. Verify the URL is correct and accessible:"
725+ error " $url "
726+ error " 3. Ensure special characters in URL are properly escaped"
727+ error " 4. Check if the checksum value is correct"
728+ error " 5. Try downloading manually to diagnose:"
729+ error " curl -LO \" $url \" "
730+ error " 6. Increase retry attempts: export DOWNLOAD_MAX_RETRIES=5"
731+ return 1
732+ fi
733+ }
734+
735+ # -------------------------------------------------------------------------
736+ # Verify file size matches expected size from HTTP headers
737+ # -------------------------------------------------------------------------
738+ function verify_file_size() {
739+ local file=" $1 "
740+ local url=" $2 "
741+
742+ log " Verifying file size for $( basename " $file " ) ..."
743+
744+ # Get expected size from HTTP headers
745+ local expected_size=$( curl -sI " $url " | grep -i " ^content-length:" | awk ' {print $2}' | tr -d ' \r\n' )
746+
747+ if [ -z " $expected_size " ] || [ " $expected_size " = " 0" ]; then
748+ warn " Unable to determine expected file size from server, skipping size verification"
749+ return 0
750+ fi
751+
752+ # Get actual file size
753+ local actual_size=$( stat -c%s " $file " 2> /dev/null || stat -f%z " $file " 2> /dev/null)
754+
755+ log " Expected size: $( numfmt --to=iec-i --suffix=B $expected_size 2> /dev/null || echo " $expected_size bytes" ) "
756+ log " Actual size: $( numfmt --to=iec-i --suffix=B $actual_size 2> /dev/null || echo " $actual_size bytes" ) "
757+
758+ # Allow 1% difference for potential metadata differences
759+ local size_diff=$(( expected_size - actual_size))
760+ local size_diff_abs=${size_diff# -}
761+ local threshold=$(( expected_size / 100 ))
762+
763+ if [ " $size_diff_abs " -le " $threshold " ]; then
764+ success " ✓ File size verification PASSED"
765+ return 0
623766 else
624- error " Unable to fetch the url"
767+ error " ✗ File size verification FAILED (difference: $size_diff_abs bytes)"
768+ return 1
769+ fi
770+ }
771+
772+ # -------------------------------------------------------------------------
773+ # Validate URL for common issues
774+ # -------------------------------------------------------------------------
775+ function validate_url() {
776+ local url=" $1 "
777+
778+ # Check for unescaped ampersands
779+ if [[ " $url " =~ [^\\ ]\& [^\ ] ]]; then
780+ warn " ⚠ Warning: URL contains unescaped & characters"
781+ warn " This may cause download issues. Consider escaping with \\ & or using quotes"
782+ warn " URL: $url "
783+ fi
784+
785+ # Check if URL is accessible
786+ if ! curl -sf --head " $url " > /dev/null 2>&1 ; then
787+ warn " ⚠ Warning: Unable to verify URL accessibility"
788+ warn " This might indicate network issues or incorrect URL"
625789 fi
626790}
627791
@@ -632,15 +796,15 @@ function download_image {
632796 if [[ " $1 " == " rhel" ]]; then
633797 if echo $RHEL_URL | grep -q -i ' access.cdn.redhat.com' ; then
634798 log " downloading rhel image"
635- download_url $RHEL_URL
799+ download_url " $RHEL_URL " " $RHEL_SHA256 "
636800 RHEL_IMAGE=$IMAGE_PATH
637801 RHEL_DOWNLOADED_IMAGE_NAME=$DOWNLOAD_IMAGE_NAME
638802 RHEL_NEW_IMAGE_PATH=$IMAGE_NEW_PATH
639803 COPY_RHEL_IMAGE=1
640804 fi
641805 elif [[ " $1 " == " rhcos" ]]; then
642- download_url $RHCOS_URL
643- RHCOS_IMAGE=IMAGE_PATH
806+ download_url " $RHCOS_URL " " $RHCOS_SHA256 "
807+ RHCOS_IMAGE=$ IMAGE_PATH
644808 RHCOS_DOWNLOAD_IMAGE_NAME=$DOWNLOAD_IMAGE_NAME
645809 copy_image_file $RHCOS_IMAGE $RHCOS_OBJECT_NAME
646810 RHCOS_NEW_IMAGE_PATH=$IMAGE_NEW_PATH
@@ -649,6 +813,61 @@ function download_image {
649813 warn " Unknown image"
650814 fi
651815}
816+ function calc_sha256() {
817+ local f=" $1 "
818+ if command -v sha256sum > /dev/null 2>&1 ; then
819+ sha256sum " $f " | awk ' {print $1}'
820+ elif command -v shasum > /dev/null 2>&1 ; then
821+ shasum -a 256 " $f " | awk ' {print $1}'
822+ elif command -v openssl > /dev/null 2>&1 ; then
823+ openssl dgst -sha256 " $f " | awk ' {print $NF}'
824+ else
825+ error " No SHA-256 tool available (need sha256sum, shasum, or openssl)"
826+ fi
827+ }
828+
829+
830+ function verify_sha256() {
831+ local f=" $1 "
832+ local expected=" $2 "
833+
834+ if [ -z " $expected " ]; then
835+ warn " No checksum provided for $( basename " $f " ) , skipping verification"
836+ return 0
837+ fi
838+
839+ log " Verifying SHA256 checksum for $( basename " $f " ) ..."
840+
841+ local actual
842+ actual=" $( calc_sha256 " $f " ) "
843+
844+ if [ -z " $actual " ]; then
845+ error " Failed to calculate checksum for $f "
846+ return 1
847+ fi
848+
849+ local actual_lc=$( echo " $actual " | tr ' [:upper:]' ' [:lower:]' )
850+ local expected_lc=$( echo " $expected " | tr ' [:upper:]' ' [:lower:]' )
851+
852+ log " Expected: $expected_lc "
853+ log " Actual: $actual_lc "
854+
855+ if [[ " $actual_lc " != " $expected_lc " ]]; then
856+ error " SHA-256 checksum mismatch for $( basename " $f " ) "
857+ error " Expected: $expected_lc "
858+ error " Actual: $actual_lc "
859+ error " Possible causes:"
860+ error " - Incomplete download (network interruption)"
861+ error " - Corrupted file during transfer"
862+ error " - Incorrect URL (check for unescaped special characters like &)"
863+ error " - Wrong checksum value provided"
864+ return 1
865+ fi
866+
867+ success " ✓ Checksum verification PASSED for $( basename " $f " ) "
868+ return 0
869+ }
870+
652871
653872function main {
654873 mkdir -p ./logs
@@ -702,6 +921,14 @@ function main {
702921 " --skip-os-password" )
703922 SKIP_OS_PASSWORD=" --skip-os-password"
704923 ;;
924+ " --rhel-sha256" )
925+ shift
926+ RHEL_SHA256=" $1 "
927+ ;;
928+ " --rhcos-sha256" )
929+ shift
930+ RHCOS_SHA256=" $1 "
931+ ;;
705932 " --help" )
706933 help
707934 ;;
0 commit comments