Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion scripts/install-k8s.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2611,13 +2611,30 @@ function Test-Preflight {
$backendHost = (Get-BackendUrl) -replace '^https?://','' -replace '/$',''
$criticals = @(
@{ label = "Docker Hub (registry-1.docker.io)"; url = "https://registry-1.docker.io/v2/" },
# auth.docker.io is Docker Hub's token endpoint: a network that allows
# registry-1 but blocks the token host fails only at in-cluster pull time (#416).
@{ label = "Docker Hub auth (auth.docker.io)"; url = "https://auth.docker.io/token" },
@{ label = "GitHub Container Registry (ghcr.io)"; url = "https://ghcr.io/" },
@{ label = "tracebloc API ($backendHost)"; url = "https://$backendHost/" },
# The chart repo is probed at its index.yaml, strictly: the site ROOT 404s by
# design (so "any response = reachable" proves nothing), while the index must
# actually exist for `helm repo add` to succeed (#385).
@{ label = "tracebloc Helm charts (tracebloc.github.io)"; url = "$TRACEBLOC_HELM_REPO_URL/index.yaml"; strict = $true }
)
# Download hosts Step 1 fetches from — promoted to HARD (#416): a blocked one
# used to pass preflight then fail the install ~30s later. Added only when the
# fetch will actually happen (tool/app absent; a present tool is never
# re-downloaded). k3d release assets 302 to objects.githubusercontent.com, so it
# is probed explicitly. Kept in lockstep with preflight.sh (drift: check-drift.sh).
if (-not (Test-Path "$env:ProgramFiles\Docker\Docker\Docker Desktop.exe")) {
$criticals += @{ label = "Docker Desktop (desktop.docker.com)"; url = "https://desktop.docker.com/" }
}
if (-not (Has "kubectl")) { $criticals += @{ label = "kubectl (dl.k8s.io)"; url = "https://dl.k8s.io/" } }
if (-not (Has "helm")) { $criticals += @{ label = "Helm (get.helm.sh)"; url = "https://get.helm.sh/" } }
if (-not (Has "k3d")) {
$criticals += @{ label = "k3d download (github.com)"; url = "https://github.com/" }
$criticals += @{ label = "k3d assets (objects.githubusercontent.com)"; url = "https://objects.githubusercontent.com/" }
}
$tlsSeen = $false; $cfail = 0
foreach ($c in $criticals) {
$status = Test-PfUrl $c.url -RequireSuccess:([bool]$c.strict)
Expand All @@ -2630,7 +2647,7 @@ function Test-Preflight {
}
}
if ($tlsSeen) { Hint "A TLS/certificate error usually means a break-and-inspect (TLS-inspecting) proxy whose corporate CA isn't trusted here - see the proxy notes." }
if ($cfail -gt 0){ Hint "Allow HTTPS (443) egress to: registry-1.docker.io, ghcr.io, $backendHost, tracebloc.github.io - or configure your corporate proxy." }
if ($cfail -gt 0){ Hint "Allow HTTPS (443) egress to the host(s) named above - the always-needed set is registry-1.docker.io, auth.docker.io, ghcr.io, $backendHost, tracebloc.github.io, plus any tool-download host listed (desktop.docker.com / dl.k8s.io / get.helm.sh / github.com / objects.githubusercontent.com) - or configure your corporate proxy." }

if ($hardFail -gt 0) {
Write-Host ""
Expand Down
90 changes: 70 additions & 20 deletions scripts/lib/preflight.sh
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ _pf_host_ncpu() {
# Available (free) RAM right now, KB — Linux only (for the busy-shared-VM warn).
_pf_avail_mem_kb() { awk '/^MemAvailable:/ {print $2}' /proc/meminfo 2>/dev/null; }

# True on a macOS box with a real GUI login session — mirrors setup-macos.sh's
# _has_gui_session (the branch that installs Docker Desktop from desktop.docker.com
# vs. the headless branch that installs colima/docker via brew). /dev/console is
# owned by the GUI user; on headless Macs (EC2/CI) it's "root" or empty. Used to
# decide whether a missing docker means a brew install (→ formulae.brew.sh needed).
_pf_has_gui_session() {
local u; u="$(stat -f '%Su' /dev/console 2>/dev/null || echo '')"
[[ -n "$u" && "$u" != "root" ]]
}

# Selectors: prefer the runtime view, fall back to the host. The checks (and the
# bats numeric test) call these names; they always emit exactly one integer.
_pf_total_mem_kb() { local v; v="$(_pf_runtime_mem_kb)"; [[ -n "$v" ]] && { echo "$v"; return 0; }; _pf_host_mem_kb; }
Expand Down Expand Up @@ -392,12 +402,63 @@ _pf_connectivity() {
# Entries are "label|url" with an optional third "|strict" field.
local criticals=(
"Docker Hub (registry-1.docker.io)|https://registry-1.docker.io/v2/"
# auth.docker.io is Docker Hub's token endpoint: a network that allows
# registry-1 but blocks the token host fails only at in-cluster pull time (#416).
"Docker Hub auth (auth.docker.io)|https://auth.docker.io/token"
"GitHub Container Registry (ghcr.io)|https://ghcr.io/"
"tracebloc API (${backend_host})|https://${backend_host}/"
# The chart repo is probed at its index.yaml, strictly (third field): the site
# ROOT 404s by design, while the index must exist for `helm repo add` (#385).
"tracebloc Helm charts (tracebloc.github.io)|https://tracebloc.github.io/client/index.yaml|strict"
)
# Tool-binary download hosts — promoted to HARD (#416): a blocked one used to
# pass preflight then fail the install ~30s later. Only added when the fetch will
# actually happen (tool absent; a present tool is never re-downloaded). These are
# UNAMBIGUOUS: the installer always fetches kubectl/k3d/helm from exactly these
# hosts. Release assets 302 to objects.githubusercontent.com and _pf_probe_url
# does not follow redirects, so github.com passing proves nothing about the asset
# host — probe it explicitly. Lockstep with install-k8s.ps1 (drift: check-drift.sh).
#
# The Docker-ENGINE install host is deliberately NOT hard: it's path/distro/
# environment-dependent (Debian→get.docker.com, RHEL clones→download.docker.com,
# Amazon/Arch/SUSE→distro repos, macOS GUI→desktop.docker.com, headless→Colima via
# brew/ghcr.io). preflight can't cheaply know which, so hard-probing a fixed host
# would abort supported paths that never touch it (Bugbot). It goes in `soft`
# (warn-only) below. On Windows Docker Desktop is the sole path, so install-k8s.ps1
# keeps desktop.docker.com hard there.
local soft=()
if [[ "$OS" == "Linux" ]]; then
if ! has k3d; then criticals+=("k3d download (github.com)|https://github.com/" \
"k3d assets (objects.githubusercontent.com)|https://objects.githubusercontent.com/"); fi
if ! has kubectl; then criticals+=("kubectl (dl.k8s.io)|https://dl.k8s.io/"); fi
if ! has helm; then criticals+=("Helm (get.helm.sh)|https://get.helm.sh/"); fi
if ! has docker; then soft+=("Docker install (get.docker.com)|https://get.docker.com/" \
"Docker packages (download.docker.com)|https://download.docker.com/"); fi
elif [[ "$OS" == "Darwin" ]]; then
# macOS install paths, all keyed on what will actually be fetched (#416):
# - Homebrew install (when brew absent): the script from raw.githubusercontent.com,
# then a git-clone of Homebrew/brew + core from github.com — both hard.
# - kubectl/k3d/helm ALWAYS install via `brew install` -> formula metadata from
# formulae.brew.sh (bottles are ghcr.io, probed above; metadata host is separate
# and is hit even when brew is already present) -> hard.
# - docker is path-dependent: a GUI Mac installs Docker Desktop from
# desktop.docker.com (hard — the actual path); a HEADLESS Mac installs
# colima/docker via brew, which needs formulae.brew.sh instead. _pf_has_gui_session
# (mirrors setup-macos.sh) picks the branch, so each host is probed only on the
# path that fetches it — no false-fail on the path that doesn't (Bugbot r3/r4/r5).
if ! has brew; then criticals+=("Homebrew install (raw.githubusercontent.com)|https://raw.githubusercontent.com/" \
"Homebrew clone (github.com)|https://github.com/"); fi
local _brew_will_run=""
if ! has kubectl || ! has k3d || ! has helm; then _brew_will_run=1; fi
if ! has docker; then
if _pf_has_gui_session; then
criticals+=("Docker Desktop (desktop.docker.com)|https://desktop.docker.com/") # GUI: the actual Docker path
else
_brew_will_run=1 # headless: colima/docker via brew
fi
fi
Comment thread
shujaatTracebloc marked this conversation as resolved.
[[ -n "$_brew_will_run" ]] && criticals+=("Homebrew formulae (formulae.brew.sh)|https://formulae.brew.sh/")
fi
# Probe each critical host in the FOREGROUND (so PF_HARD_FAIL updates in THIS
# shell — a backgrounded spinner subshell couldn't propagate it), advancing a
# spinner frame before each blocking probe. No sleep: the network probe itself
Expand Down Expand Up @@ -434,31 +495,20 @@ _pf_connectivity() {
done
fi

# Tool-download hosts: only relevant on Linux when the tool isn't present. Warn-only.
if [[ "$OS" == "Linux" ]]; then
local conds=()
if ! has docker; then conds+=("Docker install (get.docker.com)|https://get.docker.com/"); fi
if ! has k3d; then conds+=("k3d download (github.com)|https://github.com/"); fi
if ! has kubectl; then conds+=("kubectl (dl.k8s.io)|https://dl.k8s.io/"); fi
if ! has helm; then conds+=("Helm (get.helm.sh)|https://get.helm.sh/"); fi
# ${conds[@]+...} guard: expanding an empty array under `set -u` errors on
# bash 3.2 (macOS). This expands to nothing when no tools are missing.
for c in ${conds[@]+"${conds[@]}"}; do
label="${c%%|*}"; url="${c#*|}"
status="$(_pf_probe_url "$url")"
if [[ "$status" == "ok" ]]; then
_pf_ok "${label} reachable"
else
warn "${label} unreachable (${status}) — needed only to install that tool."
fi
done
fi
# Path-dependent Docker-engine hosts: WARN only (never hard) so a blocked host on
# a path that won't use it doesn't abort a supported install (Bugbot). The
# ${soft[@]+…} guard keeps `set -u` happy with an empty array on bash 3.2 (macOS).
for c in ${soft[@]+"${soft[@]}"}; do
label="${c%%|*}"; url="${c#*|}"
status="$(_pf_probe_url "$url")"
[[ "$status" == "ok" ]] || warn "${label} unreachable (${status}) — only needed if the installer fetches Docker from that host; other install paths don't."
done

Comment thread
shujaatTracebloc marked this conversation as resolved.
if [[ "$tls_seen" -eq 1 ]]; then
hint "A TLS/certificate error usually means a break-and-inspect (TLS-inspecting) proxy whose corporate CA isn't trusted here — see the proxy notes."
fi
if [[ "$cfail" -gt 0 ]]; then
hint "Allow HTTPS (443) egress to: registry-1.docker.io, ghcr.io, ${backend_host}, tracebloc.github.io — or set HTTP_PROXY if you use a corporate proxy."
hint "Allow HTTPS (443) egress to the host(s) named above — the always-needed set is registry-1.docker.io, auth.docker.io, ghcr.io, ${backend_host}, tracebloc.github.io, plus any tool-download host listed (dl.k8s.io / get.helm.sh / github.com / objects.githubusercontent.com) — or set HTTP_PROXY if you use a corporate proxy."
fi
return 0
}
Expand Down
4 changes: 2 additions & 2 deletions scripts/manifest.sha256
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
c29c4fafe6691bfbfb6f90d761aa0650d79d0a5962a599926284d9e3e6a10006 scripts/install-k8s.sh
944e980e796852f152601e929ea8391f7208b104eadbb34de7ab47841e77d7a2 scripts/lib/common.sh
de18e2c38363e6296fd9e9ed89748cc87c0949bde019383b8ee11b7ee9bdb212 scripts/lib/preflight.sh
5ecbc741b61bfdbbdae14123d12328d786aa1a7d4e16bc3c31ce256eb4a01933 scripts/lib/preflight.sh
19be2771df0e1a41b4fa9678e1cf6a77492304f66f73cf705a2ae42b1dac2ba3 scripts/lib/detect-gpu.sh
7977ef12a16fc6aee1c2824cae13bd1450f7fe1c1345323292e4a14decbfe83f scripts/lib/gpu-nvidia.sh
b569eec2d8ffb9673da287a2a59d249a7dbc7236c98ab6a5062136bcc69a942c scripts/lib/gpu-amd.sh
Expand All @@ -15,4 +15,4 @@ e2ea63d844e6649f1d3aaae9fd4733845a1a39df37d68abbaeda00330f9e1c7e scripts/lib/as
b6b903a97872925ad2c0189292a81b7ebf8f0c6d4e93ac7b1e103e15afd5df68 scripts/lib/probe.sh
cf095d9a92d6f4099ad19c8783d908a5952c7cbe7851043c90279724f7767dff scripts/lib/summary.sh
77e03332ebfab1ef759c6148a57afcf479c02c5dc6cc7b0e0e680f58e20cd364 scripts/lib/diagnose.sh
9e4183c5601a7ba99382c4e4cf300a6b4a042a5ea4d9f558e2ab775e2dfe47d7 scripts/install-k8s.ps1
1a23655ce08c4db959347dd815c093a8d5dfc867a4f61264e0729528c028de93 scripts/install-k8s.ps1
53 changes: 53 additions & 0 deletions scripts/tests/check-drift.bats
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,56 @@ YAML
command() { if [[ "${2:-}" == helm ]]; then return 1; fi; builtin command "$@"; }
_drift=0; _drift_workload_names >/dev/null 2>&1; [ "$_drift" -eq 0 ]
}

# ── Check 4: preflight download-host parity (#416) ───────────────────────────
# The check extracts hosts from PROBE ENTRIES only — bash "label|https://host/…"
# and ps1 @{ label = "…"; url = "https://host/…" } (label REQUIRED on the ps1 line,
# so an unrelated $url = "https://…" download line can't count). Fixtures write
# real probe entries; an explicit array iterates regardless of the runner's IFS.
@test "preflight hosts: both installers probe the shared set as URLs -> no drift (#416)" {
local shared=(registry-1.docker.io auth.docker.io ghcr.io dl.k8s.io get.helm.sh github.com objects.githubusercontent.com desktop.docker.com) h
for h in "${shared[@]}"; do
printf ' "L (%s)|https://%s/"\n' "$h" "$h" >> "$ROOT/scripts/lib/preflight.sh"
printf ' @{ label = "L (%s)"; url = "https://%s/" }\n' "$h" "$h" >> "$ROOT/scripts/install-k8s.ps1"
done
_drift=0; _drift_preflight_hosts >/dev/null; [ "$_drift" -eq 0 ]
}

@test "preflight hosts: a probe entry missing from ps1 -> drift (#416)" {
local shared=(registry-1.docker.io auth.docker.io ghcr.io dl.k8s.io get.helm.sh github.com objects.githubusercontent.com desktop.docker.com) h
for h in "${shared[@]}"; do
printf ' "L (%s)|https://%s/"\n' "$h" "$h" >> "$ROOT/scripts/lib/preflight.sh"
[[ "$h" == "dl.k8s.io" ]] || printf ' @{ label = "L (%s)"; url = "https://%s/" }\n' "$h" "$h" >> "$ROOT/scripts/install-k8s.ps1"
done
_drift=0; _drift_preflight_hosts >/dev/null 2>&1; [ "$_drift" -ge 1 ]
}

@test "preflight hosts: a host present only in a COMMENT/hint is NOT counted -> drift (reviewer #416)" {
# The whole-file grep this replaced would pass here; the URL-extracting check
# must still flag dl.k8s.io because it's no longer in a probe entry on the ps1 side.
local shared=(registry-1.docker.io auth.docker.io ghcr.io dl.k8s.io get.helm.sh github.com objects.githubusercontent.com desktop.docker.com) h
for h in "${shared[@]}"; do
printf ' "L (%s)|https://%s/"\n' "$h" "$h" >> "$ROOT/scripts/lib/preflight.sh"
if [[ "$h" == "dl.k8s.io" ]]; then
printf ' # kubectl comes from dl.k8s.io\n Hint "allow HTTPS egress to dl.k8s.io"\n' >> "$ROOT/scripts/install-k8s.ps1"
else
printf ' @{ label = "L (%s)"; url = "https://%s/" }\n' "$h" "$h" >> "$ROOT/scripts/install-k8s.ps1"
fi
done
_drift=0; _drift_preflight_hosts >/dev/null 2>&1; [ "$_drift" -ge 1 ]
}

@test "preflight hosts: an unrelated \$url= download line does NOT mask a deleted probe -> drift (Bugbot #416)" {
# github.com's PROBE entry is removed, but a winget-style \$url = "https://github.com/…"
# download line remains — the label-scoped extractor must NOT count it as a probe.
local shared=(registry-1.docker.io auth.docker.io ghcr.io dl.k8s.io get.helm.sh github.com objects.githubusercontent.com desktop.docker.com) h
for h in "${shared[@]}"; do
printf ' "L (%s)|https://%s/"\n' "$h" "$h" >> "$ROOT/scripts/lib/preflight.sh"
if [[ "$h" == "github.com" ]]; then
printf ' $url = "https://github.com/microsoft/winget-cli/releases/latest/download/x.msixbundle"\n' >> "$ROOT/scripts/install-k8s.ps1"
else
printf ' @{ label = "L (%s)"; url = "https://%s/" }\n' "$h" "$h" >> "$ROOT/scripts/install-k8s.ps1"
fi
done
_drift=0; _drift_preflight_hosts >/dev/null 2>&1; [ "$_drift" -ge 1 ]
}
43 changes: 43 additions & 0 deletions scripts/tests/check-drift.sh
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,55 @@ _drift_cli_contract() {
fi
}

# ── Check 4: preflight probes the same download hosts on both installers ─────
# preflight.sh (Linux/macOS) and install-k8s.ps1 (Windows) each probe the hosts
# the install fetches from (#416). A host added to one installer but not the other
# silently reopens the "green preflight, blocked download 30s later" gap on
# whichever OS was missed.
#
# We extract only hosts that appear in an actual PROBE URL — preflight.sh entries
# are "label|https://host/…", install-k8s.ps1 entries are url = "https://host/…" —
# NOT a whole-file grep. A whole-file grep also matches the same hosts inside
# comments and the egress-hint strings, so it would pass even if a real probe line
# were deleted (reviewer: it couldn't detect the drift it exists to catch).
#
# The shared-core set is every host BOTH probe as a URL literal on the default
# path. OS-specific hosts (get.docker.com / download.docker.com on Linux,
# raw.githubusercontent.com / formulae.brew.sh on macOS) are excluded, as is
# tracebloc.github.io (install-k8s.ps1 probes it via the $TRACEBLOC_HELM_REPO_URL
# variable, not a literal — Check 1 already pins the backend/chart host map).
# Extract ONLY the hosts in a preflight probe entry, not any URL in the file:
# - preflight.sh entries are "label|https://host/…" (the pipe is unique to them)
# - install-k8s.ps1 entries are @{ label = "…"; url = "https://host/…" } — REQUIRE
# the `label =` on the same line so unrelated `$url = "https://…"` assignments
# (e.g. the winget download) don't count and mask a deleted probe (Bugbot).
_drift_probed_hosts_sh() { grep -oE '\|https://[a-zA-Z0-9.-]+' "$1" 2>/dev/null | sed 's#.*//##' | sort -u; }
_drift_probed_hosts_ps1() { grep -oE 'label *=.*url *= *"https://[a-zA-Z0-9.-]+' "$1" 2>/dev/null | sed 's#.*https://##' | sort -u; }
_drift_preflight_hosts() {
echo "▸ Preflight download-host parity (probed URLs in preflight.sh · install-k8s.ps1)"
local shared=(
registry-1.docker.io auth.docker.io ghcr.io
dl.k8s.io get.helm.sh github.com objects.githubusercontent.com
desktop.docker.com
)
local before=$_drift h sh_hosts ps_hosts
sh_hosts="$(_drift_probed_hosts_sh "$DRIFT_ROOT/scripts/lib/preflight.sh")"
ps_hosts="$(_drift_probed_hosts_ps1 "$DRIFT_ROOT/scripts/install-k8s.ps1")"
for h in "${shared[@]}"; do
grep -qxF -- "$h" <<<"$sh_hosts" || _note "preflight.sh: '$h' is not in any probe URL — both installers must probe it (#416)"
grep -qxF -- "$h" <<<"$ps_hosts" || _note "install-k8s.ps1: '$h' is not in any probe URL — both installers must probe it (#416)"
done
if [[ "$_drift" -eq "$before" ]]; then _ok "both installers probe the shared download-host set (extracted from probe URLs)"; fi
return 0
}

main() {
set -uo pipefail
echo "── source-of-truth drift checks ─────────────────────────────"
_drift_backend_hosts
_drift_workload_names
_drift_cli_contract
_drift_preflight_hosts
echo "─────────────────────────────────────────────────────────────"
if [[ "$_drift" -gt 0 ]]; then
echo "DRIFT: $_drift divergence(s) above. Update both sides (or the contract in check-drift.sh) and re-run." >&2
Expand Down
Loading
Loading