Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

scripts/local/test-luci-deployment.sh

Raw
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
tmp="$(mktemp -d "${repo_root}/.luci-deployment-test.XXXXXX")"
trap 'rm -rf "${tmp}"' EXIT

fail() {
  echo "test failed: $*" >&2
  exit 1
}

rendered_quadlet="${tmp}/quadlets"
mkdir "${rendered_quadlet}"
cp "${repo_root}/modules/klops/generated/luci.container" "${rendered_quadlet}/luci.container"
QUADLET_UNIT_DIRS="${rendered_quadlet}" /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun

context="${tmp}/context"
mkdir -p "${context}"
git init -q "${context}"
git -C "${context}" config user.email test@example.invalid
git -C "${context}" config user.name Test
printf 'FROM scratch\n' >"${context}/Containerfile"
git -C "${context}" add Containerfile
git -C "${context}" commit -qm initial
printf 'package main\n' >"${context}/untracked.go"
if output="$("${repo_root}/scripts/local/build-remote-image.sh" example.invalid 22 ci localhost/test:latest "${context}" 2>&1)"; then
  fail "untracked build context was accepted"
fi
[[ "${output}" == *"image context has untracked files"* && "${output}" == *"untracked.go"* ]] || fail "untracked build-context rejection was incomplete"

if LUCI_IMAGE=localhost/other:latest "${repo_root}/scripts/local/load-luci-image.sh" example.invalid 22 >/dev/null 2>&1; then
  fail "LUCI_IMAGE override was accepted"
fi

key_program="$(sed -n "/^awk '\$/,/^' \/root\/\.ssh\/authorized_keys/p" "${repo_root}/scripts/remote/setup-ci-user.sh" | sed '1d;$d')"
source_key='from="192.0.2.0/24",expiry-time="20300101",restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITest test@example.invalid'
[[ "$(printf '%s\n' "${source_key}" | awk "${key_program}")" == "${source_key}" ]] || fail "authorized-key restrictions were not preserved"

mkdir -p "${tmp}/bin" "${tmp}/data/ci/ssh" "${tmp}/run/sshd"
cat >"${tmp}/bin/install" <<'SCRIPT'
#!/usr/bin/env bash
exit 0
SCRIPT
cat >"${tmp}/bin/sshd" <<'SCRIPT'
#!/usr/bin/env bash
for ((i = 1; i <= $#; i++)); do
  if [[ "${!i}" == -f ]]; then
    next=$((i + 1))
    cp "${!next}" "${TEST_SSHD_CONFIG}"
  fi
done
[[ " $* " == *" -t "* ]] && exit 0
[[ " $* " == *" -D "* ]] && exit 0
exit 1
SCRIPT
cat >"${tmp}/bin/luci" <<'SCRIPT'
#!/usr/bin/env bash
printf '%s\n' "$*" >>"${TEST_LUCI_LOG}"
[[ "$1" == env-check ]] && exit 0
[[ "$1" == serve ]] && sleep 30
SCRIPT
chmod 0755 "${tmp}/bin/"*
sed \
  -e "s|/usr/local/bin/luci|${tmp}/bin/luci|g" \
  -e "s|/usr/sbin/sshd|${tmp}/bin/sshd|g" \
  -e "s|/data/ci|${tmp}/data/ci|g" \
  -e "s|/run/luci-sshd.conf|${tmp}/run/luci-sshd.conf|g" \
  -e "s|/run/luci-ssh-command|${tmp}/run/luci-ssh-command|g" \
  -e "s|/run/sshd|${tmp}/run/sshd|g" \
  "${repo_root}/services/luci/entrypoint.sh" >"${tmp}/entrypoint.sh"
chmod 0755 "${tmp}/entrypoint.sh"
PATH="${tmp}/bin:${PATH}" \
  TEST_SSHD_CONFIG="${tmp}/sshd_config" \
  TEST_LUCI_LOG="${tmp}/luci.log" \
  LUCI_DATA_DIR="${tmp}/data/ci" \
  LUCI_REPO_ROOTS=/repos \
  LUCI_HTTP_ADDR=0.0.0.0:8080 \
  LUCI_BASE_URL=https://deployment.invalid \
  LUCI_INBOX_DIR="${tmp}/data/ci/inbox" \
  LUCI_PODMAN_SOCKET=unix:///podman.sock \
  LUCI_SSH_ADDR=0.0.0.0:2223 \
  LUCI_SSH_AUTHORIZED_KEYS="${tmp}/data/ci/authorized_keys" \
  LUCI_THEME_CSS="${tmp}/data/ci/nugu.css" \
  LUCI_DEFAULT_TIMEOUT=41m \
  LUCI_DEFAULT_MEMORY=3g \
  LUCI_DEFAULT_CPUS=4 \
  LUCI_CACHE_MAX_VERSIONS=5 \
  LUCI_CACHE_MAX_BYTES=3GiB \
  LUCI_LOG_LEVEL=debug \
  LUCI_PUBLISH_REGISTRY_AUTH_FILE=/run/secrets/zot-auth.json \
  LUCI_PUBLISH_PKG_ROOT=/data/pkg \
  LUCI_PUBLISH_SITE_ROOT=/data/sites \
  "${tmp}/entrypoint.sh"
sshd -t -f "${tmp}/sshd_config"
grep -qx 'SetEnv LUCI_BASE_URL="https://deployment.invalid"' "${tmp}/sshd_config" || fail "runtime base URL was not forwarded to SSH"
grep -qx 'SetEnv LUCI_DEFAULT_TIMEOUT="41m"' "${tmp}/sshd_config" || fail "runtime timeout was not forwarded to SSH"
for name in LUCI_DATA_DIR LUCI_REPO_ROOTS LUCI_HTTP_ADDR LUCI_BASE_URL LUCI_INBOX_DIR LUCI_PODMAN_SOCKET LUCI_SSH_ADDR LUCI_SSH_AUTHORIZED_KEYS LUCI_THEME_CSS LUCI_DEFAULT_TIMEOUT LUCI_DEFAULT_MEMORY LUCI_DEFAULT_CPUS LUCI_CACHE_MAX_VERSIONS LUCI_CACHE_MAX_BYTES LUCI_LOG_LEVEL LUCI_PUBLISH_REGISTRY_AUTH_FILE LUCI_PUBLISH_PKG_ROOT LUCI_PUBLISH_SITE_ROOT; do
  grep -q "^SetEnv ${name}=" "${tmp}/sshd_config" || fail "${name} was not forwarded to SSH"
done
grep -qx 'env-check' "${tmp}/luci.log" || fail "entrypoint did not validate Luci configuration"
grep -qx 'serve' "${tmp}/luci.log" || fail "entrypoint did not start Luci"
grep -qx 'export LUCI_REPO_ROOTS=/repos' "${tmp}/run/luci-ssh-command" || fail "forced SSH command did not receive runtime environment"
grep -qx "exec ${tmp}/bin/luci ssh" "${tmp}/run/luci-ssh-command" || fail "forced SSH command did not dispatch Luci"

cat >"${tmp}/bin/id" <<'SCRIPT'
#!/usr/bin/env bash
printf '1000\n'
SCRIPT
cat >"${tmp}/bin/runuser" <<'SCRIPT'
#!/usr/bin/env bash
for ((i = 1; i <= $#; i++)); do
  if [[ "${!i}" == -lc ]]; then
    next=$((i + 1))
    exec bash -c "${!next}"
  fi
done
exit 1
SCRIPT
cat >"${tmp}/bin/podman" <<'SCRIPT'
#!/usr/bin/env bash
printf '%s\n' "$*" >>"${TEST_PODMAN_LOG}"
case "$1 $2" in
  'image exists') exit 0 ;;
  'port luci') printf '0.0.0.0:8080\n'; exit 0 ;;
esac
[[ "$1" == tag ]] && exit 0
exit 1
SCRIPT
cat >"${tmp}/bin/systemctl" <<'SCRIPT'
#!/usr/bin/env bash
if [[ "${TEST_SYSTEMCTL_FAIL_RESTART:-}" == 1 && ( " $* " == *" restart "* || " $* " == *" start "* ) ]]; then
  count=0
  [[ -f "${TEST_SYSTEMCTL_STATE}" ]] && count="$(<"${TEST_SYSTEMCTL_STATE}")"
  count=$((count + 1))
  printf '%s\n' "${count}" >"${TEST_SYSTEMCTL_STATE}"
  (( count <= 2 )) && exit 1
fi
exit 0
SCRIPT
cat >"${tmp}/bin/curl" <<'SCRIPT'
#!/usr/bin/env bash
exit 1
SCRIPT
cat >"${tmp}/bin/sleep" <<'SCRIPT'
#!/usr/bin/env bash
exit 0
SCRIPT
chmod 0755 "${tmp}/bin/"*
mkdir -p "${tmp}/home" "${tmp}/data/ci"
sed \
  -e "s|home_dir=\"/home/\\\${user}\"|home_dir=\"${tmp}/home\"|" \
  -e "s|/data/ci|${tmp}/data/ci|g" \
  "${repo_root}/scripts/remote/start-luci.sh" >"${tmp}/start-luci.sh"
chmod 0755 "${tmp}/start-luci.sh"
PATH="${tmp}/bin:${PATH}" TEST_PODMAN_LOG="${tmp}/podman.log" "${tmp}/start-luci.sh" backup
grep -qx 'tag localhost/bugabinga-luci:latest localhost/bugabinga-luci:rollback' "${tmp}/podman.log" || fail "previous image was not retained for rollback"
mkdir -p "${tmp}/home/.config/containers/systemd"
: >"${tmp}/home/.config/containers/systemd/luci.container"
: >"${tmp}/data/ci/authorized_keys"
: >"${tmp}/podman.log"
if PATH="${tmp}/bin:${PATH}" TEST_PODMAN_LOG="${tmp}/podman.log" "${tmp}/start-luci.sh" deploy; then
  fail "unhealthy Luci candidate was accepted"
fi
grep -qx 'tag localhost/bugabinga-luci:rollback localhost/bugabinga-luci:latest' "${tmp}/podman.log" || fail "unhealthy candidate was not rolled back"
printf 'concurrent-key\n' >"${tmp}/data/ci/authorized_keys"
: >"${tmp}/podman.log"
if PATH="${tmp}/bin:${PATH}" TEST_PODMAN_LOG="${tmp}/podman.log" TEST_SYSTEMCTL_FAIL_RESTART=1 TEST_SYSTEMCTL_STATE="${tmp}/systemctl-state" "${tmp}/start-luci.sh" deploy; then
  fail "restart-failed Luci candidate was accepted"
fi
grep -qx 'tag localhost/bugabinga-luci:rollback localhost/bugabinga-luci:latest' "${tmp}/podman.log" || fail "restart-failed candidate was not rolled back"
grep -qx 'concurrent-key' "${tmp}/data/ci/authorized_keys" || fail "temporary health key truncated existing keys"
[[ "$(wc -l <"${tmp}/data/ci/authorized_keys")" -eq 1 ]] || fail "temporary health key was not removed"

printf 'ok: Luci deployment checks\n'