#!/usr/bin/env bash # # Smoke tests for the top-level server installer. set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" INSTALL_SCRIPT="${ROOT_DIR}/install.sh" if [[ ! -f "${INSTALL_SCRIPT}" ]]; then echo "install.sh not found at ${INSTALL_SCRIPT}" >&2 exit 1 fi failures=0 assert_success() { local desc="$1" shift if "$@"; then echo "[PASS] ${desc}" return 0 else echo "[FAIL] ${desc}" >&2 ((failures++)) return 1 fi } load_installer() { # shellcheck disable=SC1090 source "${INSTALL_SCRIPT}" trap - EXIT } test_infer_release_from_archive_name_supports_prerelease() { ( load_installer local version version="$(infer_release_from_archive_name "/tmp/pulse-v5.1.27-rc.1-linux-arm64.tar.gz")" [[ "${version}" == "v5.1.27-rc.1" ]] ) } test_ensure_update_disk_headroom_fails_when_tmp_and_install_share_full_filesystem() { ( load_installer UPDATE_MIN_TEMP_FREE_BYTES=$((100 * 1024)) UPDATE_MIN_INSTALL_FREE_BYTES=$((80 * 1024)) print_error() { :; } print_info() { :; } print_warn() { :; } df() { if [[ "$1" == "-Pk" ]]; then case "$2" in /tmp|/opt/pulse) printf 'Filesystem 1024-blocks Used Available Capacity Mounted on\n' printf '/dev/shared 1000 0 150 0%% /\n' return 0 ;; esac fi command df "$@" } if ensure_update_disk_headroom /tmp /opt/pulse; then echo "ensure_update_disk_headroom unexpectedly passed on a shared full filesystem" >&2 return 1 fi ) } test_ensure_update_disk_headroom_accepts_separate_filesystems_with_sufficient_space() { ( load_installer UPDATE_MIN_TEMP_FREE_BYTES=$((100 * 1024)) UPDATE_MIN_INSTALL_FREE_BYTES=$((80 * 1024)) print_error() { :; } print_info() { :; } print_warn() { :; } df() { if [[ "$1" == "-Pk" ]]; then case "$2" in /tmp) printf 'Filesystem 1024-blocks Used Available Capacity Mounted on\n' printf '/dev/tmp 1000 0 120 0%% /tmp\n' return 0 ;; /opt/pulse) printf 'Filesystem 1024-blocks Used Available Capacity Mounted on\n' printf '/dev/root 1000 0 90 0%% /\n' return 0 ;; esac fi command df "$@" } ensure_update_disk_headroom /tmp /opt/pulse ) } test_download_pulse_installs_from_local_archive_without_network() { ( load_installer local tmpdir archive_root archive_path tmpdir="$(mktemp -d)" archive_root="${tmpdir}/archive" archive_path="${tmpdir}/pulse-v5.1.99-linux-amd64.tar.gz" mkdir -p "${archive_root}/bin" cat > "${archive_root}/bin/pulse" <<'EOF' #!/usr/bin/env bash echo "v5.1.99" EOF chmod +x "${archive_root}/bin/pulse" printf 'v5.1.99\n' > "${archive_root}/VERSION" tar -czf "${archive_path}" -C "${archive_root}" . INSTALL_DIR="${tmpdir}/opt/pulse" CONFIG_DIR="${tmpdir}/etc/pulse" BUILD_FROM_SOURCE=false SKIP_DOWNLOAD=false ARCHIVE_OVERRIDE="${archive_path}" FORCE_VERSION="" FORCE_CHANNEL="" UPDATE_CHANNEL="stable" LATEST_RELEASE="" STOPPED_PULSE_SERVICE="" mkdir -p "${INSTALL_DIR}/bin" "${CONFIG_DIR}" detect_service_name() { echo "pulse"; } stop_pulse_service_for_update() { return 0; } restore_selinux_contexts() { :; } install_additional_agent_binaries() { return 0; } deploy_agent_scripts() { return 0; } validate_pulse_binary_architecture() { return 0; } chown() { :; } ln() { :; } curl() { echo "unexpected curl call" >&2; return 99; } wget() { echo "unexpected wget call" >&2; return 99; } download_pulse [[ -x "${INSTALL_DIR}/bin/pulse" ]] [[ "$("${INSTALL_DIR}/bin/pulse" --version)" == "v5.1.99" ]] [[ "${LATEST_RELEASE}" == "v5.1.99" ]] ) } test_prefetch_pulse_archive_for_container_sets_output_var() { ( load_installer local archive_path="" LATEST_RELEASE="v5.1.42" resolve_target_release() { :; } download_release_archive() { printf 'test archive\n' > "$3" return 0 } uname() { echo "x86_64"; } prefetch_pulse_archive_for_container archive_path [[ "${archive_path}" == /tmp/pulse-v5.1.42-amd64-lxc-*.tar.gz ]] [[ -f "${archive_path}" ]] rm -f "${archive_path}" ) } test_install_pulse_archive_rejects_mismatched_arch_without_replacing_existing_binary() { ( load_installer local tmpdir archive_root archive_path tmpdir="$(mktemp -d)" archive_root="${tmpdir}/archive" archive_path="${tmpdir}/pulse-v5.1.99-linux-arm64.tar.gz" mkdir -p "${archive_root}/bin" cat > "${archive_root}/bin/pulse" <<'EOF' #!/usr/bin/env bash echo "v5.1.99" EOF chmod +x "${archive_root}/bin/pulse" tar -czf "${archive_path}" -C "${archive_root}" . INSTALL_DIR="${tmpdir}/opt/pulse" mkdir -p "${INSTALL_DIR}/bin" cat > "${INSTALL_DIR}/bin/pulse" <<'EOF' #!/usr/bin/env bash echo "v5.1.10" EOF chmod +x "${INSTALL_DIR}/bin/pulse" validate_pulse_binary_architecture() { return 1; } if install_pulse_archive "${archive_path}" "v5.1.99"; then echo "install_pulse_archive unexpectedly succeeded on mismatched archive" >&2 rm -rf "${tmpdir}" return 1 fi [[ "$("${INSTALL_DIR}/bin/pulse" --version)" == "v5.1.10" ]] [[ ! -e "${INSTALL_DIR}/bin/pulse.old" ]] rm -rf "${tmpdir}" ) } test_parse_args_rejects_archive_with_source() { local tmpdir output_file tmpdir="$(mktemp -d)" output_file="${tmpdir}/output.txt" if bash "${INSTALL_SCRIPT}" --source --archive /tmp/pulse.tar.gz >"${output_file}" 2>&1; then echo "installer unexpectedly accepted --archive with --source" >&2 rm -rf "${tmpdir}" return 1 fi if ! grep -q -- "--archive cannot be used with --source" "${output_file}"; then echo "expected archive/source validation message" >&2 cat "${output_file}" >&2 rm -rf "${tmpdir}" return 1 fi rm -rf "${tmpdir}" return 0 } test_installer_runs_when_streamed_over_stdin() { local tmpdir output_file tmpdir="$(mktemp -d)" output_file="${tmpdir}/output.txt" if ! cat "${INSTALL_SCRIPT}" | bash -s -- --help >"${output_file}" 2>&1; then echo "installer failed when streamed to bash" >&2 cat "${output_file}" >&2 rm -rf "${tmpdir}" return 1 fi if grep -q "BASH_SOURCE\\[0\\]: unbound variable" "${output_file}"; then echo "installer still hit BASH_SOURCE unbound variable when streamed to bash" >&2 cat "${output_file}" >&2 rm -rf "${tmpdir}" return 1 fi if ! grep -q "Usage: install.sh \\[OPTIONS\\]" "${output_file}"; then echo "expected streamed installer help to show install.sh usage" >&2 cat "${output_file}" >&2 rm -rf "${tmpdir}" return 1 fi rm -rf "${tmpdir}" return 0 } test_install_additional_agent_binaries_copies_local_binaries_without_network() { ( load_installer local tmpdir source_dir tmpdir="$(mktemp -d)" source_dir="${tmpdir}/source" INSTALL_DIR="${tmpdir}/opt/pulse" mkdir -p "${source_dir}/bin" "${INSTALL_DIR}/bin" printf 'host-agent\n' > "${source_dir}/bin/pulse-host-agent-linux-arm64" printf 'unified-agent\n' > "${source_dir}/bin/pulse-agent-linux-arm64" chmod +x "${source_dir}/bin/pulse-host-agent-linux-arm64" "${source_dir}/bin/pulse-agent-linux-arm64" local curl_calls=0 local wget_calls=0 chown() { :; } curl() { curl_calls=$((curl_calls + 1)); return 99; } wget() { wget_calls=$((wget_calls + 1)); return 99; } install_additional_agent_binaries "v5.1.99" "${source_dir}" [[ -x "${INSTALL_DIR}/bin/pulse-host-agent-linux-arm64" ]] [[ -x "${INSTALL_DIR}/bin/pulse-agent-linux-arm64" ]] [[ "${curl_calls}" -eq 0 ]] [[ "${wget_calls}" -eq 0 ]] rm -rf "${tmpdir}" ) } test_install_additional_agent_binaries_skips_network_when_local_extras_are_missing() { ( load_installer local tmpdir source_dir tmpdir="$(mktemp -d)" source_dir="${tmpdir}/source" INSTALL_DIR="${tmpdir}/opt/pulse" mkdir -p "${source_dir}/bin" "${INSTALL_DIR}/bin" local curl_calls=0 local wget_calls=0 chown() { :; } curl() { curl_calls=$((curl_calls + 1)); return 99; } wget() { wget_calls=$((wget_calls + 1)); return 99; } install_additional_agent_binaries "v5.1.99" "${source_dir}" [[ "${curl_calls}" -eq 0 ]] [[ "${wget_calls}" -eq 0 ]] rm -rf "${tmpdir}" ) } main() { assert_success "infer_release_from_archive_name parses prerelease tarballs" test_infer_release_from_archive_name_supports_prerelease assert_success "update disk preflight fails on shared low-space filesystems" test_ensure_update_disk_headroom_fails_when_tmp_and_install_share_full_filesystem assert_success "update disk preflight passes on separate filesystems with enough headroom" test_ensure_update_disk_headroom_accepts_separate_filesystems_with_sufficient_space assert_success "download_pulse installs from local archive without network" test_download_pulse_installs_from_local_archive_without_network assert_success "prefetch helper writes archive path via output variable" test_prefetch_pulse_archive_for_container_sets_output_var assert_success "wrong-arch archives fail before replacing the installed binary" test_install_pulse_archive_rejects_mismatched_arch_without_replacing_existing_binary assert_success "parse_args rejects archive with source builds" test_parse_args_rejects_archive_with_source assert_success "installer supports curl-pipe execution via bash stdin" test_installer_runs_when_streamed_over_stdin assert_success "install_additional_agent_binaries copies local extras without network" test_install_additional_agent_binaries_copies_local_binaries_without_network assert_success "install_additional_agent_binaries skips network when extras are missing" test_install_additional_agent_binaries_skips_network_when_local_extras_are_missing if (( failures > 0 )); then echo "Total failures: ${failures}" >&2 return 1 fi echo "All server installer smoke tests passed." } main "$@"