@cryptotaxi247 / netdata / commits / 0ec096dd6

Improve error handling in netdata-updater when fetching files. (#22422)

* Improve error handling in netdata-updater when fetching files. This adds code to properly differentiate between: - Failure to connect to the remote host. - TLS errors (such as failed certificate validation). - Inability to actually download the file due to a 4xx or 5xx error code. - Everything else. That, in turn, lets us give more accurate error information when we fail to download a file. This also adds support for systems with GNU wget2 installed that do not include a `wget` compatibility link or a copy of `curl` (these systems should be rare, but a number of distros we support do technically allow for such a setup). * Address review and further differentiate protocol errors. * Further fixes. * Yet more fixes. * Fix and clean up handling of latest tag check. * Fix logic checking for curl/wget. * Yet more fixes. * Restructure temporary directory handling to avoid repeated variable assignment. * Fix CI testing of kickstart and updater scripts. * Address issues indicated in review. * Fix handling of CURL output when checking redirects in kickstart script. It’s only broken in circumstances we end up using for testing, not the general case for most users, but it’s still breaking our CI jobs involving running the script. * Fix logic for updater self-update.

Austin S. Hemmelgarn committed Jun 1, 2026 at 09:35 UTC 0ec096dd6b7394040c45cc6e16e06b73f54b8692
3 files changed +150 -80
.github/workflows/build.yml
+6 -3
@@ -649,8 +649,9 @@ jobs:
649 id: prepare
650 if: needs.file-check.outputs.run == 'true'
651 run: |
652 - mkdir -p download/latest
652 + mkdir -p download/latest latest
653 mv artifacts/* download/latest
654 + cp -a download/latest/* latest
655 ls -al download/latest
656 - name: Verify that artifacts work with installer
657 id: verify
@@ -718,8 +719,9 @@ jobs:
719 id: prepare
720 if: needs.file-check.outputs.run == 'true'
721 run: |
721 - mkdir -p download/latest
722 + mkdir -p download/latest latest
723 mv artifacts/* download/latest
724 + cp -a download/latest/* latest
725 ls -al download/latest
726 - name: Verify that artifacts work with installer
727 id: verify
@@ -787,8 +789,9 @@ jobs:
789 id: prepare
790 if: needs.file-check.outputs.run == 'true'
791 run: |
790 - mkdir -p download/latest
792 + mkdir -p download/latest latest
793 mv artifacts/* download/latest
794 + cp -a download/latest/* latest
795 ls -al download/latest
796 - name: Run Updater Check
797 id: check
packaging/installer/kickstart.sh
+2 -2
@@ -729,7 +729,7 @@ get_redirect() {
729 if [ -n "${CURL}" ]; then
730 checked=1
731
732 - if run sh -c "${CURL} ${url} -s -L -I -o /dev/null -w '%{url_effective}' | grep -Eo '[^/]+$'"; then
732 + if run sh -c "${CURL} ${url} -s -L -I -o /dev/null -w '%{url_effective}' | grep -Eo '[^/]+/?$' | grep -Eo '^[^/]+'"; then
733 succeeded=1
734 fi
735 fi
@@ -738,7 +738,7 @@ get_redirect() {
738 if command -v wget > /dev/null 2>&1; then
739 checked=1
740
741 - if run sh -c "wget -S -O /dev/null ${url} 2>&1 | grep -m 1 Location | grep -Eo '[^/]+$'"; then
741 + if run sh -c "wget -S -O /dev/null ${url} 2>&1 | grep -m 1 Location | grep -Eo '[^/]+/?$' | grep -Eo '^[^/]+'"; then
742 succeeded=1
743 fi
744 fi
packaging/installer/netdata-updater.sh
+142 -75
@@ -22,7 +22,7 @@
22 # - TMPDIR (set to a usable temporary directory)
23 # - NETDATA_NIGHTLIES_BASEURL (set the base url for downloading the dist tarball)
24
25 -# Next unused error code: U001F
25 +# Next unused error code: U0029
26
27 set -e
28
@@ -563,41 +563,52 @@ _cannot_use_tmpdir() {
563 }
564
565 create_exec_tmp_directory() {
566 - if [ -n "${NETDATA_TMPDIR_PATH}" ]; then
567 - echo "${NETDATA_TMPDIR_PATH}"
568 - return
569 - fi
566 + if [ -z "${ndtmpdir}" ]; then
567 + if [ -n "${NETDATA_TMPDIR_PATH}" ]; then
568 + ndtmpdir="${NETDATA_TMPDIR_PATH}"
569 + else
570 + root_dir=""
571 +
572 + if [ -n "${NETDATA_TMPDIR}" ] && ! _cannot_use_tmpdir "${NETDATA_TMPDIR}"; then
573 + root_dir="${NETDATA_TMPDIR}"
574 + elif [ -n "${TMPDIR}" ] && ! _cannot_use_tmpdir "${TMPDIR}"; then
575 + root_dir="${TMPDIR}"
576 + elif ! _cannot_use_tmpdir /tmp; then
577 + root_dir="/tmp"
578 + elif ! _cannot_use_tmpdir "${PWD}"; then
579 + root_dir="${PWD}"
580 + else
581 + fatal "Unable to find a usable temporary directory. Please set \$TMPDIR to a path that is both writable and allows execution of files and try again." U0003
582 + fi
583
571 - root_dir=""
584 + TMPDIR="${root_dir}"
585
573 - if [ -n "${NETDATA_TMPDIR}" ] && ! _cannot_use_tmpdir "${NETDATA_TMPDIR}"; then
574 - root_dir="${NETDATA_TMPDIR}"
575 - elif [ -n "${TMPDIR}" ] && ! _cannot_use_tmpdir "${TMPDIR}"; then
576 - root_dir="${TMPDIR}"
577 - elif ! _cannot_use_tmpdir /tmp; then
578 - root_dir="/tmp"
579 - elif ! _cannot_use_tmpdir "${PWD}"; then
580 - root_dir="${PWD}"
581 - else
582 - fatal "Unable to find a usable temporary directory. Please set \$TMPDIR to a path that is both writable and allows execution of files and try again." U0003
586 + ndtmpdir="$(mktemp -d -p "${root_dir}" -t netdata-updater-XXXXXXXXXX)"
587 + fi
588 fi
589
585 - TMPDIR="${root_dir}"
586 -
587 - mktemp -d -p "${root_dir}" -t netdata-updater-XXXXXXXXXX
590 + info "Putting temporary files in ${ndtmpdir}"
591 }
592
593 check_for_curl() {
594 if [ -z "${curl}" ]; then
592 - curl="$(PATH="${PATH}:/opt/netdata/bin" command -v curl 2>/dev/null && true)"
595 + curl="$(PATH="${PATH}:/opt/netdata/bin" command -v curl 2>/dev/null || true)"
596 + fi
597 +}
598 +
599 +check_for_wget() {
600 + if [ -z "${wget}" ]; then
601 + wget="$(command -v wget 2>/dev/null || true)"
602 + fi
603 +
604 + if [ -z "${wget}" ]; then
605 + wget="$(command -v wget2 2>/dev/null || true)"
606 fi
607 }
608
609 _safe_download() {
610 url="${1}"
611 dest="${2}"
599 - succeeded=0
600 - checked=0
612
613 if echo "${url}" | grep -Eq "^file:///"; then
614 cp "${url#file://}" "${dest}" || return 1
@@ -605,36 +616,75 @@ _safe_download() {
616 fi
617
618 check_for_curl
619 + check_for_wget
620 + create_exec_tmp_directory
621 + dl_log="${ndtmpdir}/download.log"
622 + rm -f "${dl_log}"
623
624 if [ -n "${curl}" ]; then
610 - checked=1
611 -
612 - if "${curl}" -fsSL --connect-timeout 10 --retry 3 "${url}" > "${dest}"; then
613 - succeeded=1
614 - elif [ "${dest}" != "/dev/null" ]; then
615 - rm -f "${dest}"
616 - fi
617 - fi
625 + set +e
626 + "${curl}" --fail --location --write-out "%{http_code}" --connect-timeout 10 --retry 3 "${url}" --output "${dest}" > "${dl_log}"
627 + result="$?"
628 + set -e
629
619 - if [ "${succeeded}" -eq 0 ]; then
620 - if command -v wget > /dev/null 2>&1; then
621 - checked=1
630 + case "${result}" in
631 + 0) return 0 ;;
632 + 22|78)
633 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
634 + case "$(tail -n 1 "${dl_log}")" in
635 + 404) return 1 ;;
636 + 4*) return 5 ;;
637 + 5*) return 6 ;;
638 + *) return 4 ;;
639 + esac
640 + ;;
641 + 5|6|7)
642 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
643 + return 2
644 + ;;
645 + 35|60|83)
646 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
647 + return 3
648 + ;;
649 + *)
650 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
651 + return 4
652 + ;;
653 + esac
654 + elif [ -n "${wget}" ]; then
655 + set +e
656 + "${wget}" -T 15 -S -o "${dl_log}" -O "${dest}" "${url}"
657 + result="$?"
658 + set -e
659
623 - if wget -T 15 -O - "${url}" > "${dest}"; then
624 - succeeded=1
625 - elif [ "${dest}" != "/dev/null" ]; then
626 - rm -f "${dest}"
627 - fi
628 - fi
660 + case "${result}" in
661 + 0) return 0 ;;
662 + 8)
663 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
664 +
665 + case "$(grep "HTTP/" "${dl_log}" | tail -n 1 | awk '{ print $2 }')" in
666 + 404) return 1 ;;
667 + 4*) return 5 ;;
668 + 5*) return 6 ;;
669 + *) return 4 ;;
670 + esac
671 + ;;
672 + 4)
673 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
674 + return 2
675 + ;;
676 + 5)
677 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
678 + return 3
679 + ;;
680 + *)
681 + [ "${dest}" != "/dev/null" ] && rm -f "${dest}"
682 + return 4
683 + ;;
684 + esac
685 fi
686
631 - if [ "${succeeded}" -eq 1 ]; then
632 - return 0
633 - elif [ "${checked}" -eq 1 ]; then
634 - return 1
635 - else
636 - return 255
637 - fi
687 + return 255
688 }
689
690 download() {
@@ -646,49 +696,57 @@ download() {
696 ret=$?
697 set -e
698
649 - if [ ${ret} -eq 0 ]; then
650 - return 0
651 - elif [ ${ret} -eq 255 ]; then
652 - fatal "I need curl or wget to proceed, but neither is available on this system." U0004
653 - else
654 - fatal "Cannot download ${url}" U0005
655 - fi
699 + case "${ret}" in
700 + 0) return 0 ;;
701 + 1) fatal "File ${url} not found on remote server" U0022 ;;
702 + 2) fatal "Unable to connect to remote host to download ${url}" U0023 ;;
703 + 3) fatal "TLS error connecting to remote host to download ${url}" U0024 ;;
704 + 5) fatal "Client error when trying to download ${url}" U0027 ;;
705 + 6) fatal "Internal server error when trying to download ${url}" U0028 ;;
706 + 255) fatal "I need curl or wget to proceed, but neither is available on this system." U0004 ;;
707 + *) fatal "Cannot download ${url}" U0005 ;;
708 + esac
709 }
710
711 get_netdata_latest_tag() {
712 url="${1}/latest"
713
714 check_for_curl
715 + check_for_wget
716 +
717 + if [ -z "${curl}" ] && [ -z "${wget}" ]; then
718 + fatal "I need curl or wget to proceed, but neither of them are available on this system." U0006
719 + fi
720
721 if [ -n "${curl}" ]; then
664 - tag=$("${curl}" "${url}" -s -L -I -o /dev/null -w '%{url_effective}')
722 + tag=$("${curl}" "${url}" -s -L -I -o /dev/null -w '%{url_effective}' || true)
723 fi
724
725 if [ -z "${tag}" ]; then
668 - if command -v wget >/dev/null 2>&1; then
669 - tag=$(wget -S -O /dev/null "${url}" 2>&1 | grep Location)
726 + if [ -n "${wget}" ]; then
727 + tag=$("${wget}" -S -O /dev/null "${url}" 2>&1 | grep Location || true)
728 fi
729 fi
730
731 if [ -z "${tag}" ]; then
674 - fatal "I need curl or wget to proceed, but neither of them are available on this system." U0006
732 + tag='latest'
733 fi
734
735 tag="$(echo "${tag}" | grep -Eom 1 '[^/]*/?$')"
736
737 # Fallback case for simpler local testing.
738 if echo "${tag}" | grep -Eq 'latest/?$'; then
681 - if _safe_download "${url}/latest-version.txt" ./ndupdate-version.txt; then
682 - tag="$(cat ./ndupdate-version.txt)"
739 + set +e
740 + _safe_download "${url}/latest-version.txt" ./ndupdate-version.txt
741 + result="$?"
742 + set -e
743
684 - if grep -q 'Not Found' ./ndupdate-version.txt; then
685 - tag="latest"
686 - fi
744 + case "${result}" in
745 + 0) tag="$(cat ./ndupdate-version.txt)" ;;
746 + *) tag='latest' ;;
747 + esac
748
688 - rm -f ./ndupdate-version.txt
689 - else
690 - tag="latest"
691 - fi
749 + rm -f ./ndupdate-version.txt
750 fi
751
752 echo "${tag}"
@@ -697,7 +755,7 @@ get_netdata_latest_tag() {
755 newer_commit_date() {
756 info "Checking if a newer version of the updater script is available."
757
700 - ndtmpdir="$(create_exec_tmp_directory)"
758 + create_exec_tmp_directory
759 commit_check_file="${ndtmpdir}/latest-commit.json"
760 commit_check_url="https://api.github.com/repos/netdata/netdata/commits?path=packaging%2Finstaller%2Fnetdata-updater.sh&page=1&per_page=1"
761 python_version_check="
@@ -712,7 +770,11 @@ else:
770 print(data[0]['commit']['committer']['date'] if isinstance(data, list) and data else '')
771 "
772
715 - _safe_download "${commit_check_url}" "${commit_check_file}"
773 + if ! _safe_download "${commit_check_url}" "${commit_check_file}"; then
774 + warning "Failed to check for an updated updater script, skipping self-update check."
775 + rm -f "${commit_check_file}" 2>/dev/null || true
776 + return 1
777 + fi
778
779 if command -v jq > /dev/null 2>&1; then
780 commit_date="$(jq '.[0].commit.committer.date' 2>/dev/null < "${commit_check_file}" | tr -d '"')"
@@ -723,7 +785,7 @@ else:
785 fi
786
787 if [ -z "${NETDATA_TMPDIR_PATH}" ]; then
726 - rm -rf "${ndtmpdir}" >&3 2>&3
788 + rm -f "${commit_check_file}" 2>/dev/null || true
789 fi
790
791 if [ -z "${commit_date}" ] ; then
@@ -751,8 +813,8 @@ self_update() {
813 if [ -z "${NETDATA_NO_UPDATER_SELF_UPDATE}" ] && newer_commit_date; then
814 info "Downloading newest version of updater script."
815
754 - ndtmpdir=$(create_exec_tmp_directory)
755 - cd "$ndtmpdir" || exit 1
816 + create_exec_tmp_directory
817 + cd "${ndtmpdir}" || exit 1
818
819 if _safe_download "https://raw.githubusercontent.com/netdata/netdata/master/packaging/installer/netdata-updater.sh" ./netdata-updater.sh; then
820 chmod +x ./netdata-updater.sh || exit 1
@@ -902,11 +964,11 @@ set_tarball_urls() {
964 export NETDATA_TARBALL_URL="file://${path}/${filename}"
965 export NETDATA_TARBALL_CHECKSUM_URL="file://${path}/sha256sums.txt"
966 elif [ "$1" = "stable" ]; then
905 - latest="$(get_netdata_latest_tag "${NETDATA_STABLE_BASE_URL}")"
967 + latest="$(get_latest_tag)"
968 export NETDATA_TARBALL_URL="${NETDATA_STABLE_BASE_URL}/download/$latest/${filename}"
969 export NETDATA_TARBALL_CHECKSUM_URL="${NETDATA_STABLE_BASE_URL}/download/$latest/sha256sums.txt"
970 else
909 - tag="$(get_netdata_latest_tag "${NETDATA_NIGHTLY_BASE_URL}")"
971 + tag="$(get_latest_tag)"
972 export NETDATA_TARBALL_URL="${NETDATA_NIGHTLY_BASE_URL}/download/${tag}/${filename}"
973 export NETDATA_TARBALL_CHECKSUM_URL="${NETDATA_NIGHTLY_BASE_URL}/download/${tag}/sha256sums.txt"
974 fi
@@ -916,7 +978,7 @@ update_build() {
978 [ -z "${logfile}" ] && info "Running on a terminal - (this script also supports running headless from crontab)"
979
980 RUN_INSTALLER=0
919 - ndtmpdir=$(create_exec_tmp_directory)
981 + create_exec_tmp_directory
982 cd "$ndtmpdir" || fatal "Failed to change current working directory to ${ndtmpdir}" U0016
983
984 install_build_dependencies
@@ -1015,7 +1077,7 @@ update_build() {
1077 }
1078
1079 update_static() {
1018 - ndtmpdir="$(create_exec_tmp_directory)"
1080 + create_exec_tmp_directory
1081 PREVDIR="$(pwd)"
1082
1083 info "Entering ${ndtmpdir}"
@@ -1227,6 +1289,11 @@ update_binpkg() {
1289 error ""
1290 fatal "Unable to update due to native packages no longer being published for this platform" U001E
1291 ;;
1292 + 2) fatal "Failed to connect to Netdata package repositories. This is most likely a result of networking problems with this system." U001F ;;
1293 + 3) fatal "TLS error when trying to connect to Netdata package repositories." U0020 ;;
1294 + 4) fatal "Unknown error when trying to connect to Netdata package repositories." U0021 ;;
1295 + 5) fatal "Client error when trying to connect to Netdata package repositories." U0025 ;;
1296 + 6) fatal "Internal server error when trying to connect to Netdata package repositories." U0026 ;;
1297 255) warning "Unable to check whether native packages are being published, wget or curl is required." ;;
1298 esac
1299 fi