839 lines
32 KiB
Bash
Executable File
839 lines
32 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# GENERATED by make standalone — do not edit
|
|
#
|
|
# rigdeps.sh for profile 'default', flattened from:
|
|
# ctrl/deps.sh
|
|
# ctrl/lib/config.sh
|
|
# Edit those and run `make standalone`. Changes made here are lost, and
|
|
# `make selftest` fails while this file differs from what rig generates.
|
|
|
|
# ── from the libraries ──
|
|
declare -- CONFIG_OVERRIDABLE=$'PROFILE CLUSTER K8S_VERSION KIND_CONFIG ADDONS\n REGISTRY_MODE INGRESS_MODE DNS_MODE TILT_PORT\n SOURCE ARCH DEPS_SOURCE HTTP_PORT HTTPS_PORT\n REGISTRY_PORT MANIFESTS_DIR'
|
|
_config_restore ()
|
|
{
|
|
local line;
|
|
while IFS= read -r line; do
|
|
if [ -n "$line" ]; then
|
|
eval "export $line";
|
|
fi;
|
|
done <<< "$1";
|
|
return 0
|
|
}
|
|
default_cluster_name ()
|
|
{
|
|
local n;
|
|
n=$(basename "$(cd .. && pwd)");
|
|
n=$(echo "$n" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-');
|
|
n=$(echo "$n" | sed 's/^-*//; s/-*$//');
|
|
echo "${n:-rig}"
|
|
}
|
|
derive_port_base ()
|
|
{
|
|
local h;
|
|
h=$(printf '%s' "$1" | cksum | awk '{print $1}');
|
|
echo $((20000 + (h % 200) * 10))
|
|
}
|
|
render_kind_config ()
|
|
{
|
|
local host_workdir="${HOST_WORKDIR:-$(cd .. && pwd)}";
|
|
sed -e "s|\${CLUSTER}|${CLUSTER}|g" -e "s|\${NODE_IMAGE}|${NODE_IMAGE}|g" -e "s|\${HTTP_PORT}|${HTTP_PORT}|g" -e "s|\${HOST_WORKDIR}|${host_workdir}|g" "$KIND_CONFIG"
|
|
}
|
|
|
|
# ── configuration, frozen for profile 'default' ──
|
|
load_config() {
|
|
local k saved=""
|
|
for k in $CONFIG_OVERRIDABLE; do
|
|
if [ -n "${!k+x}" ]; then saved+="$k=$(printf '%q' "${!k}")"$'\n'; fi
|
|
done
|
|
declare -g ADDONS=""
|
|
declare -gx AIRFLOW_IMAGE="apache/airflow:2.10.4"
|
|
declare -gx CERT_MANAGER_VERSION="v1.21.1"
|
|
declare -g CLUSTER="rig"
|
|
declare -gx COMPOSE_SHA256="db1889184726840f75c4f9c001048430d4f25b3be3cb084d3ddd762bc0aed576"
|
|
declare -gx COMPOSE_URL="https://github.com/docker/compose/releases/download/v5.5.1/docker-compose-linux-x86_64"
|
|
declare -gx COMPOSE_VERSION="5.5.1"
|
|
declare -gx CTLPTL_SHA256="c63a1ec28e60bc3faf6becb76f53355c5cf5e0143dafdd27ad85db5584fa6b1e"
|
|
declare -gx CTLPTL_URL="https://github.com/tilt-dev/ctlptl/releases/download/v0.9.4/ctlptl.0.9.4.linux.x86_64.tar.gz"
|
|
declare -gx CTLPTL_VERSION="0.9.4"
|
|
declare -g DNS_MODE="hosts"
|
|
declare -g HTTPS_PORT="20311"
|
|
declare -g HTTP_PORT="20310"
|
|
declare -g INGRESS_MODE="hostport"
|
|
declare -gx JQ_SHA256="b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f"
|
|
declare -gx JQ_URL="https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-linux-amd64"
|
|
declare -gx JQ_VERSION="1.8.2"
|
|
declare -g K8S_VERSION="v1_36"
|
|
declare -g KIND_CONFIG="./k8s/kind-config.yaml.tpl"
|
|
declare -gx KIND_SHA256="50030de23cf40a18505f20426f6a8506bedf13c6e509244bd1fa9463721b0f54"
|
|
declare -gx KIND_URL="https://github.com/kubernetes-sigs/kind/releases/download/v0.32.0/kind-linux-amd64"
|
|
declare -gx KIND_VERSION="v0.32.0"
|
|
declare -g KUBECONTEXT="kind-rig"
|
|
declare -gx KUBECTL_SHA256="ebbd080e7c2e275093b55915722043257eb24004363e20acb3c4d71919f88336"
|
|
declare -gx KUBECTL_URL="https://dl.k8s.io/release/v1.36.3/bin/linux/amd64/kubectl"
|
|
declare -gx KUBECTL_VERSION="v1.36.3"
|
|
declare -g MANIFESTS_DIR="ctrl/k8s/overlays/dev"
|
|
declare -gx METALLB_VERSION="v0.16.0"
|
|
declare -gx METRICS_SERVER_VERSION="v0.9.0"
|
|
declare -g NODES="1"
|
|
declare -g NODE_IMAGE="kindest/node:v1.36.1@sha256:3489c7674813ba5d8b1a9977baea8a6e553784dab7b84759d1014dbd78f7ebd5"
|
|
declare -gx NODE_IMAGE_v1_33="kindest/node:v1.33.12@sha256:3f5c8443c620245e4d355cfe09e96a91ead32ceaa569d3f1ca9edf0cb2fe2ff4"
|
|
declare -gx NODE_IMAGE_v1_34="kindest/node:v1.34.8@sha256:02722c2dedddcfc00febf5d27fbeb9b7b2c14294c82109ff4a85d89ac9ba3256"
|
|
declare -gx NODE_IMAGE_v1_35="kindest/node:v1.35.5@sha256:ce977ae6d65918d0b58a5f8b5e940429c2ce42fa3a5619ec2bbc60b949c0ac95"
|
|
declare -gx NODE_IMAGE_v1_36="kindest/node:v1.36.1@sha256:3489c7674813ba5d8b1a9977baea8a6e553784dab7b84759d1014dbd78f7ebd5"
|
|
declare -g NODE_MB="800"
|
|
declare -gx POSTGRES_IMAGE="postgres:16-alpine"
|
|
declare -gx PROFILE="default"
|
|
declare -g PROFILE_NAME="default"
|
|
declare -gx REDIS_IMAGE="redis:7-alpine"
|
|
declare -gx REGISTRY_IMAGE="registry:2"
|
|
declare -g REGISTRY_MODE="local"
|
|
declare -g REGISTRY_PORT="20313"
|
|
declare -gx STUB_IMAGE="python:3.12-slim"
|
|
declare -g TILT_PORT="20312"
|
|
declare -gx TILT_SHA256="e9672b8a18d43501f35dcfe98465969a7db0e436b36cf0c50c7e6f8d40de5fe6"
|
|
declare -gx TILT_URL="https://github.com/tilt-dev/tilt/releases/download/v0.37.6/tilt.0.37.6.linux.x86_64.tar.gz"
|
|
declare -gx TILT_VERSION="0.37.6"
|
|
_config_restore "$saved"
|
|
}
|
|
# ── end of frozen configuration ──
|
|
|
|
# ── ctrl/deps.sh ──
|
|
# Toolchain installer: detect the host, install pinned tools into $OUT_BIN, report
|
|
# host actions it will not perform (no sudo, no apt). Usually via `make deps`.
|
|
# Usage: deps.sh [detect [all] | list | verify [core|dev] | fetch [core|dev] [--to DIR] | install [core|dev]]
|
|
# Notes: docs/notes/deps.md
|
|
|
|
set -euo pipefail
|
|
|
|
# Keep the caller's cwd so a relative --to resolves there, not against ctrl/.
|
|
INVOKED_FROM="$PWD"
|
|
cd "$(dirname "$0")"
|
|
|
|
# Pins arrive through load_config, not by sourcing versions.env, so `make
|
|
# standalone` can freeze them in.
|
|
# (sourced library inlined above)
|
|
load_config
|
|
|
|
# Resolve a possibly-relative path against the caller's original directory.
|
|
abspath() {
|
|
case "$1" in
|
|
/*) echo "$1" ;;
|
|
*) echo "$INVOKED_FROM/$1" ;;
|
|
esac
|
|
}
|
|
|
|
OUT_BIN="${OUT_BIN:-$HOME/.local/bin}"
|
|
HOST_ROOT="${HOST_ROOT:-/}"
|
|
DEPS_SOURCE="${DEPS_SOURCE:-upstream}"
|
|
DEPS_ARTIFACTORY_URL="${DEPS_ARTIFACTORY_URL:-}"
|
|
BAKED_BIN="${BAKED_BIN:-/opt/rig/bin}"
|
|
|
|
# Collected by detect(), printed by report_manual() at the very end.
|
|
MANUAL=()
|
|
|
|
# Facts print only with VERBOSE (`detect all`); problems (! and -) always print.
|
|
fact() { if [ -n "${VERBOSE:-}" ]; then echo "$@"; fi; }
|
|
|
|
# Host FILES are read through $HOST_ROOT; kernel facts are shared with the container.
|
|
# A /proc/meminfo field in MB, 0 if absent. MEMINFO overrides the source for testing.
|
|
mb_of() {
|
|
awk -v k="$1:" '$1 == k { printf "%d", $2 / 1024; found = 1 }
|
|
END { if (!found) printf "0" }' "${MEMINFO:-/proc/meminfo}"
|
|
}
|
|
|
|
host_file() {
|
|
local p="${1#/}"
|
|
if [ "$HOST_ROOT" != "/" ] && [ -e "$HOST_ROOT/$p" ]; then
|
|
echo "$HOST_ROOT/$p"
|
|
else
|
|
echo "/$p"
|
|
fi
|
|
}
|
|
|
|
# ── the tools this script itself needs ─────────────────────────────────────
|
|
|
|
arch() {
|
|
case "$(uname -m)" in
|
|
x86_64|amd64) echo amd64 ;;
|
|
aarch64|arm64) echo arm64 ;;
|
|
*) uname -m ;;
|
|
esac
|
|
}
|
|
|
|
# Pins are amd64 only: refuse elsewhere and print how to get the right checksums.
|
|
require_amd64() {
|
|
local a; a=$(arch)
|
|
[ "$a" = "amd64" ] && return 0
|
|
cat >&2 <<EOF
|
|
This machine is ${a} ($(uname -m)); every pin in this script is linux/amd64.
|
|
|
|
Nothing here would run, so it does not download. To make an ${a} version, the
|
|
URLs need the ${a} artifact and the checksums need to come from each project's
|
|
own published list — not from these values, and not from a download you did:
|
|
|
|
curl -sSL https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/checksums.txt
|
|
curl -sSL https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${a}/kubectl.sha256
|
|
curl -sSL https://github.com/tilt-dev/tilt/releases/download/v${TILT_VERSION}/checksums.txt
|
|
curl -sSL https://github.com/tilt-dev/ctlptl/releases/download/v${CTLPTL_VERSION}/checksums.txt
|
|
curl -sSL https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/sha256sum.txt
|
|
|
|
Edit the pinned block at the top of this file with what those print.
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
DL=""
|
|
pick_downloader() {
|
|
if command -v curl >/dev/null 2>&1; then DL=curl
|
|
elif command -v wget >/dev/null 2>&1; then DL=wget
|
|
else
|
|
echo "neither curl nor wget is installed, so nothing can be downloaded." >&2
|
|
echo "Install one first: $(pkg_install_cmd curl)" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
download() {
|
|
local url="$1" out="$2"
|
|
case "$DL" in
|
|
curl) curl -fsSL --retry 3 -o "$out" "$url" ;;
|
|
wget) wget -q --tries=3 -O "$out" "$url" ;;
|
|
esac
|
|
}
|
|
|
|
SHA=""
|
|
pick_sha() {
|
|
if command -v sha256sum >/dev/null 2>&1; then SHA=sha256sum
|
|
elif command -v shasum >/dev/null 2>&1; then SHA="shasum -a 256"
|
|
else
|
|
echo "no sha256sum and no shasum — downloads could not be verified." >&2
|
|
echo "Refusing to install unverified binaries." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# ── package manager, for the instructions only ─────────────────────────────
|
|
# Never runs one; names the right one so reported actions are pasteable.
|
|
|
|
pkg_install_cmd() {
|
|
local pkg="$1"
|
|
if command -v apt-get >/dev/null 2>&1; then echo "sudo apt-get update && sudo apt-get install -y $pkg"
|
|
elif command -v dnf >/dev/null 2>&1; then echo "sudo dnf install -y $pkg"
|
|
elif command -v yum >/dev/null 2>&1; then echo "sudo yum install -y $pkg"
|
|
elif command -v zypper >/dev/null 2>&1; then echo "sudo zypper install -y $pkg"
|
|
elif command -v apk >/dev/null 2>&1; then echo "sudo apk add $pkg"
|
|
else echo "install '$pkg' with this system's package manager"
|
|
fi
|
|
}
|
|
|
|
docker_pkg() {
|
|
# Debian and Ubuntu call it docker.io; the RPM distros call it docker.
|
|
if command -v apt-get >/dev/null 2>&1; then echo docker.io; else echo docker; fi
|
|
}
|
|
|
|
# ── detect ─────────────────────────────────────────────────────────────────
|
|
|
|
# Windows outside WSL (Git Bash, MSYS, Cygwin) fails confusingly; name it instead.
|
|
require_linux() {
|
|
case "$(uname -s)" in
|
|
MINGW*|MSYS*|CYGWIN*)
|
|
cat >&2 <<'EOF'
|
|
This has to run inside WSL, not Git Bash / MSYS / Cygwin.
|
|
|
|
If WSL is not installed yet, from an elevated PowerShell or Command Prompt:
|
|
|
|
wsl --install
|
|
|
|
That enables Windows features and needs a reboot, so it is not something this
|
|
script will do for you. Afterwards, open the Linux shell it installs and run
|
|
this from there.
|
|
|
|
See "Starting from plain Windows" in README.md.
|
|
EOF
|
|
exit 1 ;;
|
|
esac
|
|
}
|
|
|
|
is_wsl() { grep -qi microsoft /proc/version 2>/dev/null; }
|
|
|
|
detect() {
|
|
echo "host"
|
|
fact " kernel $(uname -r)"
|
|
|
|
local osr distro=""; osr=$(host_file /etc/os-release)
|
|
[ -r "$osr" ] && distro=$(sed -n 's/^PRETTY_NAME="\(.*\)"/\1/p' "$osr")
|
|
echo " distro ${distro:-unknown} $(arch), $(if is_wsl; then echo WSL; else echo native linux; fi)"
|
|
|
|
# In MB (whole GB rounds away too much). Facts only; check.sh judges sufficiency.
|
|
local total_mb avail_mb swap_total_mb swap_used_mb om
|
|
total_mb=$(mb_of MemTotal)
|
|
avail_mb=$(mb_of MemAvailable)
|
|
swap_total_mb=$(mb_of SwapTotal)
|
|
swap_used_mb=$(( swap_total_mb - $(mb_of SwapFree) ))
|
|
printf " memory %d MB total, %d MB available%s\n" "$total_mb" "$avail_mb" \
|
|
"$(if [ "$swap_used_mb" -gt 0 ]; then echo ", $swap_used_mb MB in swap"; fi)"
|
|
|
|
# Overcommit mode: with 1 the OOM killer settles up later, after a clean start.
|
|
om=$(cat "${OVERCOMMIT_FILE:-/proc/sys/vm/overcommit_memory}" 2>/dev/null || echo '?')
|
|
case "$om" in
|
|
0) fact " overcommit 0 heuristic — allocations are granted on a guess" ;;
|
|
1) fact " overcommit 1 always — every allocation succeeds; the OOM killer is the only limit" ;;
|
|
2) fact " overcommit 2 strict — an allocation fails honestly instead of killing later" ;;
|
|
esac
|
|
|
|
fact " install to $OUT_BIN"
|
|
detect_libc
|
|
detect_prereqs
|
|
detect_wsl
|
|
detect_filesystem
|
|
detect_docker
|
|
detect_inotify
|
|
detect_toolchain
|
|
}
|
|
|
|
detect_wsl() {
|
|
if ! is_wsl; then
|
|
return
|
|
fi
|
|
|
|
# systemd is off by default in WSL; enabling it needs a Windows-side restart.
|
|
local wc; wc=$(host_file /etc/wsl.conf)
|
|
if [ -r "$wc" ] && grep -qE '^\s*systemd\s*=\s*true' "$wc"; then
|
|
fact " systemd enabled in wsl.conf"
|
|
else
|
|
echo " ! systemd not enabled in /etc/wsl.conf"
|
|
MANUAL+=("Enable systemd — add to /etc/wsl.conf:
|
|
[boot]
|
|
systemd=true
|
|
then from a WINDOWS terminal (not this shell): wsl --shutdown")
|
|
fi
|
|
|
|
# WSL regenerates /etc/resolv.conf on every boot, which silently reverts any
|
|
# local DNS setup.
|
|
if [ -r "$wc" ] && grep -qE '^\s*generateResolvConf\s*=\s*false' "$wc"; then
|
|
fact " resolv.conf pinned (generateResolvConf=false)"
|
|
else
|
|
fact " - resolv.conf is WSL-generated; DNS_MODE=dnsmasq would be reverted on reboot"
|
|
fi
|
|
|
|
local wcfg
|
|
wcfg=$(ls "$HOST_ROOT"/mnt/c/Users/*/.wslconfig 2>/dev/null | head -1 || true)
|
|
if [ -n "$wcfg" ] && grep -qE '^\s*memory\s*=' "$wcfg"; then
|
|
fact " wslconfig memory set: $(grep -E '^\s*memory\s*=' "$wcfg" | tr -d ' ')"
|
|
else
|
|
MANUAL+=("Cap/raise the WSL VM memory — see what is set versus what booted:
|
|
make check mem
|
|
It prints the edit to make and the command to apply it.")
|
|
fi
|
|
}
|
|
|
|
# Filesystem types that deliver no inotify events (9p, drvfs, network, fuse).
|
|
# Checks the fs type, not the path.
|
|
watch_hostile_fs() {
|
|
local dir="$1" fstype
|
|
fstype=$(findmnt -no FSTYPE --target "$dir" 2>/dev/null || true)
|
|
[ -n "$fstype" ] || fstype=$(stat -f -c %T "$dir" 2>/dev/null || true)
|
|
case "$fstype" in
|
|
9p|v9fs|drvfs|cifs|smb3|nfs|nfs4|fuse.sshfs|fuseblk) echo "$fstype" ;;
|
|
*) echo "" ;;
|
|
esac
|
|
}
|
|
|
|
detect_filesystem() {
|
|
local root fstype
|
|
root=$(cd .. && pwd -P)
|
|
fstype=$(watch_hostile_fs "$root")
|
|
if [ -n "$fstype" ]; then
|
|
echo " ! this directory is on $fstype — file watching will not work"
|
|
MANUAL+=("Move this onto the local disk. Nothing watching files sees changes
|
|
on a $fstype mount, and everything else is slower:
|
|
cp -r \"$root\" ~/ && cd ~/$(basename "$root")")
|
|
else
|
|
fact " filesystem $root ($(findmnt -no FSTYPE --target "$root" 2>/dev/null || echo local))"
|
|
fi
|
|
}
|
|
|
|
# tilt needs glibc >= 2.34 (measured on Amazon Linux 2). Report the version here;
|
|
# `verify` catches the actual failure after installing.
|
|
detect_libc() {
|
|
local v=""
|
|
if command -v ldd >/dev/null 2>&1; then
|
|
v=$(ldd --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+$' || true)
|
|
fi
|
|
if [ -z "$v" ]; then
|
|
fact " libc unknown (no ldd) — 'verify' is the real test"
|
|
return 0
|
|
fi
|
|
fact " libc glibc $v"
|
|
if [ "$(printf '%s\n2.34\n' "$v" | sort -V | head -1)" != "2.34" ]; then
|
|
echo " ! older than glibc 2.34, which tilt needs. kubectl, kind, jq and"
|
|
echo " ctlptl are static or libc-only and work here; tilt will not start."
|
|
echo " Install the core tier, or run tilt from a container."
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# What this script itself needs, so `detect` answers "will install work?".
|
|
detect_prereqs() {
|
|
local missing=""
|
|
if command -v curl >/dev/null 2>&1; then fact " download curl"
|
|
elif command -v wget >/dev/null 2>&1; then fact " download wget"
|
|
else echo " ! no curl and no wget — nothing can be downloaded"; missing+=" curl"
|
|
fi
|
|
|
|
if command -v sha256sum >/dev/null 2>&1 || command -v shasum >/dev/null 2>&1; then
|
|
fact " checksums ok"
|
|
else
|
|
echo " ! no sha256sum or shasum — downloads could not be verified"
|
|
missing+=" coreutils"
|
|
fi
|
|
|
|
if command -v tar >/dev/null 2>&1 && command -v gzip >/dev/null 2>&1; then
|
|
fact " archives tar + gzip"
|
|
else
|
|
echo " ! no tar/gzip — tilt and ctlptl ship as tarballs, so the dev tier"
|
|
echo " cannot be unpacked. The core tier is two bare binaries and is fine."
|
|
missing+=" tar gzip"
|
|
fi
|
|
|
|
if [ -n "$missing" ]; then
|
|
MANUAL+=("Install what this script needs to run at all:
|
|
$(pkg_install_cmd "${missing# }")")
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
detect_docker() {
|
|
# Daemon reachability is the real question; the CLI is only how we ask.
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
if [ -S /var/run/docker.sock ]; then
|
|
echo " docker socket present (no cli in this context)"
|
|
else
|
|
echo " ! docker not found and no socket at /var/run/docker.sock"
|
|
MANUAL+=("Install Docker — the one true prerequisite, and the only thing here
|
|
that needs root:
|
|
$(pkg_install_cmd "$(docker_pkg)")
|
|
sudo systemctl enable --now docker
|
|
sudo usermod -aG docker \"\$USER\"
|
|
then log out and back in, so the new group applies to your shell.")
|
|
fi
|
|
return
|
|
fi
|
|
if docker info >/dev/null 2>&1; then
|
|
echo " docker $(docker version --format '{{.Server.Version}}' 2>/dev/null)"
|
|
local n
|
|
n=$(docker ps --filter "label=io.x-k8s.kind.cluster" --format '{{.Names}}' 2>/dev/null | wc -l)
|
|
# Must be an `if`, not `[ ] && echo`: a zero count would return 1 under set -e.
|
|
if [ "$n" -gt 0 ]; then
|
|
echo " kind $n node container(s) running — 'make cluster list'"
|
|
fi
|
|
else
|
|
echo " ! docker cli present but the daemon is unreachable"
|
|
MANUAL+=("Start Docker, or add yourself to the docker group:
|
|
sudo usermod -aG docker \"\$USER\" # then log out and back in")
|
|
fi
|
|
}
|
|
|
|
# kind and Tilt both watch large trees. WSL ships defaults (8192/128) far too low,
|
|
# and the failure mode is silent: Tilt simply stops noticing file changes.
|
|
detect_inotify() {
|
|
local w i
|
|
w=$(cat /proc/sys/fs/inotify/max_user_watches 2>/dev/null || echo 0)
|
|
i=$(cat /proc/sys/fs/inotify/max_user_instances 2>/dev/null || echo 0)
|
|
fact " inotify watches=$w instances=$i"
|
|
|
|
if [ "$w" -lt 524288 ] || [ "$i" -lt 512 ]; then
|
|
echo " ! inotify limits are low — Tilt will silently stop noticing file changes"
|
|
MANUAL+=("Raise inotify limits (needs root on the host):
|
|
echo -e 'fs.inotify.max_user_watches=524288\\nfs.inotify.max_user_instances=512' \\
|
|
| sudo tee /etc/sysctl.d/99-rig.conf
|
|
sudo sysctl --system")
|
|
fi
|
|
}
|
|
|
|
# ── fetch ──────────────────────────────────────────────────────────────────
|
|
|
|
# Resolve where a given artifact comes from, honouring DEPS_SOURCE.
|
|
resolve_url() {
|
|
local upstream="$1"
|
|
case "$DEPS_SOURCE" in
|
|
upstream) echo "$upstream" ;;
|
|
artifactory)
|
|
if [ -z "$DEPS_ARTIFACTORY_URL" ]; then
|
|
echo "DEPS_SOURCE=artifactory but DEPS_ARTIFACTORY_URL is empty" >&2
|
|
exit 1
|
|
fi
|
|
echo "${DEPS_ARTIFACTORY_URL%/}/$(basename "$upstream")"
|
|
;;
|
|
*) echo "unsupported DEPS_SOURCE '$DEPS_SOURCE' for a download" >&2; exit 1 ;;
|
|
esac
|
|
}
|
|
|
|
verify() {
|
|
local file="$1" want="$2" name="$3" got
|
|
got=$($SHA "$file" | awk '{print $1}')
|
|
if [ "$got" != "$want" ]; then
|
|
echo "checksum mismatch for $name" >&2
|
|
echo " expected $want" >&2
|
|
echo " got $got" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# fetch_bin <name> <url> <sha256> <dest-dir> — a bare binary
|
|
fetch_bin() {
|
|
local name="$1" url="$2" sha="$3" dest="$4"
|
|
local tmp="$dest/.$name.tmp"
|
|
echo " fetching $name"
|
|
download "$(resolve_url "$url")" "$tmp"
|
|
verify "$tmp" "$sha" "$name"
|
|
mv "$tmp" "$dest/$name"
|
|
chmod +x "$dest/$name"
|
|
}
|
|
|
|
# fetch_tgz <name> <url> <sha256> <dest-dir> <path-inside-archive> <strip>
|
|
# Archive layouts differ — tilt's is flat (the binary at the root, strip=0),
|
|
# others nest it a directory down — so the caller says which.
|
|
fetch_tgz() {
|
|
local name="$1" url="$2" sha="$3" dest="$4" inner="$5" strip="$6"
|
|
local tmp="$dest/.$name.tgz"
|
|
echo " fetching $name"
|
|
download "$(resolve_url "$url")" "$tmp"
|
|
verify "$tmp" "$sha" "$name"
|
|
# --no-same-owner: as root, tar would restore the archive's uid/gid.
|
|
tar -xzf "$tmp" -C "$dest" --strip-components="$strip" --no-same-owner "$inner"
|
|
rm -f "$tmp"
|
|
chmod +x "$dest/$name"
|
|
}
|
|
|
|
# The installer runs as root; hand files in a mounted dir back to the mount point's owner.
|
|
fix_ownership() {
|
|
local dir="$1"
|
|
[ -d "$dir" ] || return 0
|
|
local owner="${HOST_UID:-}:${HOST_GID:-}"
|
|
if [ "$owner" = ":" ]; then
|
|
owner=$(stat -c '%u:%g' "$dir")
|
|
fi
|
|
[ "$owner" = "0:0" ] && return 0
|
|
chown -R "$owner" "$dir" 2>/dev/null || true
|
|
}
|
|
|
|
# core: talk to a cluster someone else runs. dev: core plus tools that build clusters.
|
|
CORE_TOOLS="kubectl jq"
|
|
# No helm (nothing uses a chart). ctlptl wires in a local registry; compose is often
|
|
# missing from distro docker packages.
|
|
DEV_TOOLS="kind tilt ctlptl docker-compose"
|
|
|
|
# ── what is already on this machine ───────────────────────────────────────
|
|
# A tool already on PATH at its pinned version is left where it is.
|
|
|
|
pin_of() {
|
|
case "$1" in
|
|
kubectl) echo "$KUBECTL_VERSION" ;;
|
|
jq) echo "$JQ_VERSION" ;;
|
|
kind) echo "$KIND_VERSION" ;;
|
|
tilt) echo "$TILT_VERSION" ;;
|
|
ctlptl) echo "$CTLPTL_VERSION" ;;
|
|
docker-compose) echo "$COMPOSE_VERSION" ;;
|
|
esac
|
|
}
|
|
|
|
# The version string a binary reports (kubectl needs --client).
|
|
reported_version() {
|
|
local tool="$1" path="$2"
|
|
case "$tool" in
|
|
kubectl) "$path" version --client 2>/dev/null ;;
|
|
jq) "$path" --version 2>/dev/null ;;
|
|
*) "$path" version 2>/dev/null ;;
|
|
esac
|
|
}
|
|
|
|
# Does the binary at PATH report PIN? Whole-token match, leading v optional.
|
|
# Bash regex rather than grep, deliberately.
|
|
version_matches() {
|
|
local tool="$1" path="$2" pin="$3" out v re
|
|
out=$(reported_version "$tool" "$path") || return 1
|
|
v="${pin#v}"
|
|
v="${v//./\\.}"
|
|
re="(^|[^0-9.])v?${v}([^0-9.]|\$)"
|
|
[[ $out =~ $re ]]
|
|
}
|
|
|
|
# DEPS_ONLY narrows a fetch to the tools it names; unset means the whole tier.
|
|
# Only install() sets it.
|
|
want() { [ -z "${DEPS_ONLY:-}" ] || [[ " $DEPS_ONLY " == *" $1 "* ]]; }
|
|
|
|
# Every tool in the tier with its state, probed once and reported once. What
|
|
# still needs fetching is left in TOOLCHAIN_NEED for install() to act on.
|
|
TOOLCHAIN_NEED=""
|
|
detect_toolchain() {
|
|
local tier="${TIER:-dev}" b pin path found
|
|
TOOLCHAIN_NEED=""
|
|
local n=0
|
|
echo
|
|
fact "toolchain (pinned, tier '$tier')"
|
|
for b in $(tier_tools "$tier"); do
|
|
n=$((n + 1))
|
|
pin=$(pin_of "$b")
|
|
path=$(command -v "$b" 2>/dev/null || true)
|
|
# compose is normally a docker CLI plugin, not on PATH: ask docker instead.
|
|
if [ "$b" = docker-compose ] && [ -z "$path" ]; then
|
|
if found=$(docker compose version --short 2>/dev/null) && [ -n "$found" ]; then
|
|
if [ "${found#v}" = "${pin#v}" ]; then
|
|
fact "$(printf " %-8s %-9s %s" "$b" "$pin" "docker cli plugin")"
|
|
else
|
|
printf " ! %-8s wants %s, the docker cli plugin reports '%s'\n" \
|
|
"$b" "$pin" "$found"
|
|
TOOLCHAIN_NEED+="$b "
|
|
fi
|
|
continue
|
|
fi
|
|
fi
|
|
if [ -z "$path" ]; then
|
|
printf " - %-8s %-9s not found\n" "$b" "$pin"
|
|
TOOLCHAIN_NEED+="$b "
|
|
elif version_matches "$b" "$path" "$pin"; then
|
|
fact "$(printf " %-8s %-9s %s" "$b" "$pin" "$path")"
|
|
else
|
|
found=$(reported_version "$b" "$path" 2>/dev/null | head -1 || true)
|
|
printf " ! %-8s wants %s, %s reports '%s'\n" "$b" "$pin" "$path" "$found"
|
|
TOOLCHAIN_NEED+="$b "
|
|
fi
|
|
done
|
|
if [ -z "$TOOLCHAIN_NEED" ]; then
|
|
if [ -n "${VERBOSE:-}" ]; then echo " all $n on PATH — nothing to fetch"
|
|
else echo "toolchain all $n pinned tools on PATH (tier $tier)"; fi
|
|
else
|
|
echo "toolchain 'make deps' fetches only: ${TOOLCHAIN_NEED% }"
|
|
fi
|
|
}
|
|
|
|
fetch() {
|
|
local dest="$OUT_BIN" tier="${TIER:-dev}"
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--to) dest="$2"; shift 2 ;;
|
|
core|dev) tier="$1"; shift ;;
|
|
*) echo "unknown argument: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
dest="$(abspath "$dest")"
|
|
mkdir -p "$dest"
|
|
TIER="$tier"
|
|
|
|
if [ "$DEPS_SOURCE" = "baked" ]; then
|
|
echo "installing baked binaries from $BAKED_BIN"
|
|
cp -a "$BAKED_BIN"/. "$dest"/
|
|
fix_ownership "$dest"
|
|
return
|
|
fi
|
|
|
|
if [ -n "${DEPS_ONLY:-}" ]; then
|
|
echo "fetching ${DEPS_ONLY% } (source: $DEPS_SOURCE)"
|
|
else
|
|
echo "fetching '$tier' toolchain (source: $DEPS_SOURCE)"
|
|
fi
|
|
if want kubectl; then fetch_bin kubectl "$KUBECTL_URL" "$KUBECTL_SHA256" "$dest"; fi
|
|
if want jq; then fetch_bin jq "$JQ_URL" "$JQ_SHA256" "$dest"; fi
|
|
if [ "$tier" = "dev" ]; then
|
|
if want kind; then fetch_bin kind "$KIND_URL" "$KIND_SHA256" "$dest"; fi
|
|
if want tilt; then fetch_tgz tilt "$TILT_URL" "$TILT_SHA256" "$dest" tilt 0; fi
|
|
if want ctlptl; then fetch_tgz ctlptl "$CTLPTL_URL" "$CTLPTL_SHA256" "$dest" ctlptl 0; fi
|
|
if want docker-compose; then
|
|
fetch_bin docker-compose "$COMPOSE_URL" "$COMPOSE_SHA256" "$dest"
|
|
fi
|
|
fi
|
|
|
|
fix_ownership "$dest"
|
|
# kind writes the kubeconfig as root too; hand that back as well.
|
|
fix_ownership "${KUBE_DIR:-/out/kube}"
|
|
}
|
|
|
|
# ── install ────────────────────────────────────────────────────────────────
|
|
|
|
report_manual() {
|
|
echo
|
|
if [ ${#MANUAL[@]} -eq 0 ]; then
|
|
echo "nothing left to do by hand."
|
|
return
|
|
fi
|
|
echo "host actions this cannot perform (${#MANUAL[@]}):"
|
|
echo
|
|
local n=1
|
|
for m in "${MANUAL[@]}"; do
|
|
echo " $n. $m"
|
|
echo
|
|
n=$((n + 1))
|
|
done
|
|
}
|
|
|
|
# A verified download proves the right file, not that this machine can run it
|
|
# (old glibc breaks tilt). Run each one now.
|
|
verify_tools() {
|
|
local tier="${1:-dev}" b bin out rc broke=0
|
|
echo "checking that each one actually runs"
|
|
for b in $(tier_tools "$tier"); do
|
|
bin="$OUT_BIN/$b"
|
|
if [ ! -x "$bin" ]; then
|
|
printf ' %-14s not installed\n' "$b"
|
|
continue
|
|
fi
|
|
# Not piped into `head`: under pipefail, SIGPIPE (141) looked like failure.
|
|
rc=0
|
|
case "$b" in
|
|
kubectl) out=$("$bin" version --client 2>&1) || rc=$? ;;
|
|
jq) out=$("$bin" --version 2>&1) || rc=$? ;;
|
|
*) out=$("$bin" version 2>&1) || rc=$? ;;
|
|
esac
|
|
out=${out%%$'\n'*}
|
|
if [ "$rc" -eq 0 ]; then
|
|
printf ' %-14s %s\n' "$b" "$out"
|
|
else
|
|
printf ' ! %-12s does not run here: %s\n' "$b" "$out"
|
|
broke=1
|
|
fi
|
|
done
|
|
if [ "$broke" -eq 1 ]; then
|
|
echo
|
|
echo " A binary that downloads and verifies but will not start is almost"
|
|
echo " always this distro's libc being older than the release needs."
|
|
echo " 'detect' prints the glibc version. The core tier (kubectl + jq)"
|
|
echo " has no such dependency and will work regardless."
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
list() {
|
|
echo "pinned, linux/amd64 only:"
|
|
printf ' %-14s %s\n' kubectl "$KUBECTL_VERSION"
|
|
printf ' %-14s %s\n' jq "$JQ_VERSION"
|
|
printf ' %-14s %s\n' kind "$KIND_VERSION"
|
|
printf ' %-14s %s\n' tilt "$TILT_VERSION"
|
|
printf ' %-14s %s\n' ctlptl "$CTLPTL_VERSION"
|
|
printf ' %-14s %s\n' docker-compose "$COMPOSE_VERSION"
|
|
echo
|
|
echo " core = $CORE_TOOLS"
|
|
echo " dev = $CORE_TOOLS $DEV_TOOLS"
|
|
echo
|
|
echo "Checksums are pinned in the block at the top of this file. To bump one,"
|
|
echo "take the new checksum from the publisher's own release list — the header"
|
|
echo "comment has the exact commands."
|
|
return 0
|
|
}
|
|
|
|
tier_tools() { [ "$1" = "core" ] && echo "$CORE_TOOLS" || echo "$CORE_TOOLS $DEV_TOOLS"; }
|
|
|
|
warn_shadowing() {
|
|
local b existing shadowed="" tier="${1:-dev}"
|
|
for b in $(tier_tools "$tier"); do
|
|
[ -x "$OUT_BIN/$b" ] || continue
|
|
# Where would this resolve if OUT_BIN weren't in the way?
|
|
existing=$(PATH=$(echo "$PATH" | tr ':' '\n' | grep -vx "$OUT_BIN" | paste -sd:) \
|
|
command -v "$b" 2>/dev/null || true)
|
|
[ -n "$existing" ] || continue
|
|
[ "$existing" = "$OUT_BIN/$b" ] && continue
|
|
# The same version in both places is not a conflict: nothing changes for
|
|
# any other project whichever copy PATH happens to find first.
|
|
if version_matches "$b" "$existing" "$(pin_of "$b")"; then continue; fi
|
|
shadowed+=" $b $existing"$'\n'
|
|
done
|
|
|
|
[ -n "$shadowed" ] || return 0
|
|
|
|
case ":${PATH}:" in
|
|
*":$OUT_BIN:"*) ;;
|
|
*) return 0 ;; # not on PATH yet, so nothing is being shadowed
|
|
esac
|
|
|
|
echo
|
|
echo " ! these were already installed elsewhere and are now shadowed by $OUT_BIN:"
|
|
printf '%s' "$shadowed"
|
|
echo " Other projects on this machine will pick up the new versions."
|
|
MANUAL+=("Decide which toolchain wins. To keep the previous one, remove what
|
|
was just installed:
|
|
rm -f $(for b in $(tier_tools "$tier"); do printf '%s ' "$OUT_BIN/$b"; done)
|
|
Or install somewhere private instead:
|
|
OUT_BIN=\$PWD/def/bin make deps # then put that dir first in PATH")
|
|
}
|
|
|
|
# Link the fetched docker-compose into ~/.docker/cli-plugins so `docker compose` works.
|
|
install_compose_plugin() {
|
|
local src="$OUT_BIN/docker-compose" dir="$HOME/.docker/cli-plugins"
|
|
[ -x "$src" ] || return 0
|
|
mkdir -p "$dir"
|
|
# A real file there belongs to something else (docker-desktop, distro): don't overwrite.
|
|
if [ -e "$dir/docker-compose" ] && [ ! -L "$dir/docker-compose" ]; then
|
|
MANUAL+=("Something already installs the compose plugin at
|
|
$dir/docker-compose
|
|
To use rig's pinned build instead:
|
|
ln -sf $src $dir/docker-compose")
|
|
return 0
|
|
fi
|
|
ln -sfn "$src" "$dir/docker-compose"
|
|
echo " compose plugin -> $dir/docker-compose"
|
|
return 0
|
|
}
|
|
|
|
install() {
|
|
local tier="${1:-dev}" b
|
|
TIER="$tier"
|
|
detect
|
|
|
|
# detect_toolchain has already probed PATH. Fetch only what it found missing
|
|
# or at the wrong version; a tool already present at its pin stays where it is.
|
|
if [ -n "$TOOLCHAIN_NEED" ]; then
|
|
echo
|
|
DEPS_ONLY="$TOOLCHAIN_NEED" fetch "$tier"
|
|
echo
|
|
echo "installed to $OUT_BIN ($tier):"
|
|
for b in $TOOLCHAIN_NEED; do
|
|
if [ -x "$OUT_BIN/$b" ]; then echo " $b"; fi
|
|
done
|
|
if [ "$tier" = "core" ]; then
|
|
echo " (no kind/tilt — 'make deps dev' adds them)"
|
|
fi
|
|
# Only when compose was fetched, never at a copy rig did not install.
|
|
case " $TOOLCHAIN_NEED " in
|
|
*" docker-compose "*) install_compose_plugin ;;
|
|
esac
|
|
|
|
# PATH advice only when something actually landed in OUT_BIN.
|
|
case ":${PATH}:" in
|
|
*":$OUT_BIN:"*) ;;
|
|
*) MANUAL+=("Put the toolchain on your PATH — add to ~/.bashrc:
|
|
export PATH=\"${OUT_BIN}:\$PATH\"") ;;
|
|
esac
|
|
fi
|
|
warn_shadowing "$tier"
|
|
|
|
report_manual
|
|
}
|
|
|
|
# ── main ───────────────────────────────────────────────────────────────────
|
|
|
|
require_linux
|
|
|
|
# Shift only if there is an argument: a bare `shift` returns 1 under set -e.
|
|
cmd="${1:-install}"
|
|
[ $# -gt 0 ] && shift
|
|
|
|
# Baked mode copies binaries already in the image, so it needs no downloader.
|
|
need_downloads() {
|
|
require_amd64
|
|
if [ "$DEPS_SOURCE" != baked ]; then pick_downloader; fi
|
|
pick_sha
|
|
}
|
|
|
|
case "$cmd" in
|
|
detect) if [ "${1:-}" = all ]; then VERBOSE=1; fi; detect; report_manual ;;
|
|
list) list ;;
|
|
verify) verify_tools "${1:-dev}" ;;
|
|
fetch) need_downloads; fetch "$@" ;;
|
|
install) need_downloads; install "${1:-dev}" ;;
|
|
*) echo "usage: $0 [detect [all]|list|verify|fetch|install]" >&2
|
|
echo " install [core|dev] (default dev)" >&2
|
|
echo " fetch [core|dev] [--to DIR]" >&2
|
|
echo " OUT_BIN=<dir> overrides the install directory" >&2
|
|
exit 1 ;;
|
|
esac
|