99 lines
3.7 KiB
Bash
Executable File
99 lines
3.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Project NOMAD Offline Artifact Bundle Verifier
|
|
|
|
###################################################################################################################################################################################################
|
|
#
|
|
# Inspects and verifies an offline artifact bundle without installing anything.
|
|
#
|
|
# Useful before transferring a bundle to removable media, and on the target
|
|
# before running the installer. Runs entirely offline.
|
|
#
|
|
# Usage:
|
|
# ./verify_offline_bundle.sh [/path/to/bundle]
|
|
#
|
|
###################################################################################################################################################################################################
|
|
|
|
set -Eeuo pipefail
|
|
|
|
RESET='\033[0m'
|
|
RED='\033[1;31m'
|
|
GREEN='\033[1;32m'
|
|
|
|
die() {
|
|
echo -e "${RED}#${RESET} $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
BUNDLE="${1:-.}"
|
|
BUNDLE="$(cd -- "${BUNDLE}" 2>/dev/null && pwd)" || die "Bundle directory not found: ${1:-.}"
|
|
|
|
command -v sha256sum > /dev/null 2>&1 || die "sha256sum is required."
|
|
|
|
manifest_get() {
|
|
local key="$1"
|
|
awk -F= -v wanted="${key}" '$1 == wanted { sub(/^[^=]*=/, "", $0); print; exit }' "${BUNDLE}/manifest"
|
|
}
|
|
|
|
# Same required set the installer enforces, so a bundle that passes here will
|
|
# not fail validation on the target for a missing file.
|
|
required_files=(
|
|
manifest
|
|
SHA256SUMS
|
|
install_nomad.sh
|
|
packages/apt/Packages
|
|
packages/apt/Packages.gz
|
|
images/core-images.txt
|
|
images/core-images.tar
|
|
payload/nomad/management_compose.yaml
|
|
payload/nomad/compose.artifact.yml
|
|
payload/nomad/start_nomad.sh
|
|
payload/nomad/stop_nomad.sh
|
|
payload/nomad/update_nomad.sh
|
|
)
|
|
|
|
for required_file in "${required_files[@]}"; do
|
|
[[ -f "${BUNDLE}/${required_file}" ]] || die "Bundle is incomplete. Missing: ${required_file}"
|
|
done
|
|
|
|
echo "Verifying checksums..."
|
|
(cd "${BUNDLE}" && sha256sum -c SHA256SUMS > /dev/null) ||
|
|
die "Checksum verification failed — the bundle is corrupt or incomplete."
|
|
|
|
# ! -name '._*' skips macOS AppleDouble sidecars left on FAT/exFAT media.
|
|
deb_count="$(find "${BUNDLE}/packages/apt" -maxdepth 1 -name '*.deb' ! -name '._*' | wc -l | tr -d ' ')"
|
|
[[ "${deb_count}" -gt 0 ]] || die "Bundle contains no .deb packages."
|
|
|
|
image_count="$(grep -c '[^[:space:]]' "${BUNDLE}/images/core-images.txt" || true)"
|
|
[[ "${image_count}" -gt 0 ]] || die "Bundle lists no management images."
|
|
|
|
# Every service in the bundled compose file must appear in the pull-never
|
|
# override, or that service would still try to reach a registry on the target.
|
|
missing_overrides=''
|
|
while IFS= read -r service; do
|
|
grep -qE "^[[:space:]]+${service}:[[:space:]]*$" "${BUNDLE}/payload/nomad/compose.artifact.yml" ||
|
|
missing_overrides="${missing_overrides} ${service}"
|
|
done < <(awk '
|
|
/^services:[[:space:]]*$/ { in_services = 1; next }
|
|
/^[^[:space:]#]/ { in_services = 0 }
|
|
in_services && /^ [A-Za-z0-9._-]+:[[:space:]]*$/ {
|
|
gsub(/^ |:[[:space:]]*$/, "", $0)
|
|
print
|
|
}
|
|
' "${BUNDLE}/payload/nomad/management_compose.yaml")
|
|
|
|
[[ -z "${missing_overrides}" ]] ||
|
|
die "compose.artifact.yml is missing pull_policy overrides for:${missing_overrides}"
|
|
|
|
echo ''
|
|
printf 'Bundle format : %s\n' "$(manifest_get BUNDLE_FORMAT_VERSION)"
|
|
printf 'NOMAD commit : %s\n' "$(manifest_get NOMAD_COMMIT)"
|
|
printf 'Target : %s %s (%s)\n' \
|
|
"$(manifest_get TARGET_OS)" "$(manifest_get TARGET_VERSION)" "$(manifest_get TARGET_ARCH)"
|
|
printf 'NVIDIA toolkit: %s\n' "$(manifest_get WITH_NVIDIA_TOOLKIT)"
|
|
printf 'Created (UTC) : %s\n' "$(manifest_get CREATED_AT_UTC)"
|
|
printf 'Packages : %s .deb files\n' "${deb_count}"
|
|
printf 'Images : %s\n' "${image_count}"
|
|
echo ''
|
|
echo -e "${GREEN}#${RESET} Bundle verified. Checksums confirm transfer integrity, not publisher identity."
|