Per operator: the firewalld rule (VM->host:3000 for the Flux<-Gitea source) must be IaC, not a manual command (R-ENV-01, R-PLAT-04). - infra/ansible/playbooks/host-prereqs.yml: idempotent firewalld open on the libvirt zone (localhost play, become; no extra collection). - bootstrap/gitea/bootstrap-gitea.sh: now fully automates admin + public repo + push (was manual instructions) — idempotent; creds stay 0600 on host, never committed. Applying host-prereqs still needs host sudo (I lack non-interactive sudo); with passwordless become a destroy/rebuild is fully unattended. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
83 lines
4.3 KiB
Bash
Executable File
83 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# bootstrap/gitea/bootstrap-gitea.sh — run a SINGLE Gitea container on the HOST (outside the
|
|
# cluster) as the Phase-1 GitOps bootstrap source. While the cluster is built, Flux (in-cluster)
|
|
# reconciles this repo from the host Gitea over the k3s-dev NAT gateway. This is the "local
|
|
# bootstrap source" that migrates to in-cluster Gitea in Phase 6 (docs/05 "Source-of-truth",
|
|
# CONTRIBUTING.md).
|
|
#
|
|
# Run by the OPERATOR at bring-up — NOT during author-only, NOT by any agent.
|
|
set -euo pipefail
|
|
ENGINE="${ENGINE:-podman}"
|
|
GITEA_IMAGE="${GITEA_IMAGE:-docker.io/gitea/gitea:1.22}" # pinned; bump if needed
|
|
NAME="${GITEA_NAME:-home-k3s-bootstrap-gitea}"
|
|
HTTP_PORT="${GITEA_HTTP_PORT:-3000}"
|
|
SSH_PORT="${GITEA_SSH_PORT:-2222}"
|
|
# Use a NAMED podman volume, not a host bind-mount: rootless podman maps the container's uid to a
|
|
# subuid that can't write a host-owned dir, so Gitea fails to create /data/gitea/conf/app.ini.
|
|
DATA_VOLUME="${GITEA_DATA_VOLUME:-gitea-bootstrap-data}"
|
|
DATA_DIR="podman volume ${DATA_VOLUME}" # display label only (data lives in the named volume)
|
|
# The cluster VMs reach the host on the k3s-dev NAT gateway (10.73.0.1). ROOT_URL must match so
|
|
# clone/redirect URLs are correct from inside the cluster.
|
|
# NOTE: firewalld's `libvirt` zone (owns virbrN) blocks VM->host:3000 by default. Open it once:
|
|
# sudo firewall-cmd --zone=libvirt --add-port=3000/tcp --permanent && sudo firewall-cmd --reload
|
|
HOST_NAT_IP="${HOST_NAT_IP:-10.73.0.1}"
|
|
ORG="${GITEA_ORG:-Home-Network}"
|
|
REPO="${GITEA_REPO:-home-network-k3s-impl}"
|
|
|
|
"$ENGINE" volume create "${DATA_VOLUME}" >/dev/null 2>&1 || true
|
|
if "$ENGINE" container exists "$NAME" 2>/dev/null; then
|
|
echo "Gitea container '${NAME}' already exists — starting it."
|
|
"$ENGINE" start "$NAME" >/dev/null
|
|
else
|
|
"$ENGINE" run -d --name "$NAME" --restart unless-stopped \
|
|
-p "${HTTP_PORT}:3000" -p "${SSH_PORT}:22" \
|
|
-v "${DATA_VOLUME}:/data" \
|
|
-e GITEA__server__ROOT_URL="http://${HOST_NAT_IP}:${HTTP_PORT}/" \
|
|
-e GITEA__server__SSH_PORT="${SSH_PORT}" \
|
|
-e GITEA__security__INSTALL_LOCK=true \
|
|
"$GITEA_IMAGE"
|
|
fi
|
|
|
|
# --- automated, idempotent setup (admin + PUBLIC repo + push) — NO manual steps (R-ENV-01) ---
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
ADMIN_CRED_FILE="${GITEA_ADMIN_CRED_FILE:-${HOME}/.config/home-k3s-bootstrap-gitea-admin.txt}"
|
|
BASE="http://localhost:${HTTP_PORT}" # host-side mgmt via localhost; the cluster uses HOST_NAT_IP
|
|
|
|
echo "Waiting for Gitea to be healthy..."
|
|
curl -fsS --retry 40 --retry-delay 2 --retry-all-errors "${BASE}/api/healthz" >/dev/null
|
|
|
|
# Admin credentials: generated once, stored 0600 on the host (never committed, never printed).
|
|
if [ ! -s "$ADMIN_CRED_FILE" ]; then
|
|
mkdir -p "$(dirname "$ADMIN_CRED_FILE")"
|
|
printf 'user=gitops\npass=%s\n' "$(openssl rand -base64 18 | tr -dc 'A-Za-z0-9' | head -c 22)" > "$ADMIN_CRED_FILE"
|
|
chmod 600 "$ADMIN_CRED_FILE"
|
|
fi
|
|
ADMIN_USER="$(sed -n 's/^user=//p' "$ADMIN_CRED_FILE")"
|
|
ADMIN_PW="$(sed -n 's/^pass=//p' "$ADMIN_CRED_FILE")"
|
|
|
|
# Create the admin user if missing (idempotent).
|
|
if ! "$ENGINE" exec -u git "$NAME" gitea admin user list 2>/dev/null | grep -qw "$ADMIN_USER"; then
|
|
"$ENGINE" exec -u git "$NAME" gitea admin user create \
|
|
--username "$ADMIN_USER" --password "$ADMIN_PW" --email "${ADMIN_USER}@home.local" \
|
|
--admin --must-change-password=false
|
|
fi
|
|
|
|
# Org + PUBLIC repo (idempotent — 'already exists' is fine; public => Flux needs no git creds).
|
|
curl -fsS -u "${ADMIN_USER}:${ADMIN_PW}" -H 'Content-Type: application/json' -X POST \
|
|
"${BASE}/api/v1/orgs" -d "{\"username\":\"${ORG}\"}" -o /dev/null 2>/dev/null || true
|
|
curl -fsS -u "${ADMIN_USER}:${ADMIN_PW}" -H 'Content-Type: application/json' -X POST \
|
|
"${BASE}/api/v1/orgs/${ORG}/repos" -d "{\"name\":\"${REPO}\",\"private\":false}" -o /dev/null 2>/dev/null || true
|
|
|
|
# Push current HEAD -> the bootstrap source's main.
|
|
git -C "$REPO_ROOT" push "http://${ADMIN_USER}:${ADMIN_PW}@localhost:${HTTP_PORT}/${ORG}/${REPO}.git" HEAD:main
|
|
|
|
cat <<EOF
|
|
|
|
Gitea bootstrap source ready (${DATA_DIR}).
|
|
UI (host): ${BASE}/${ORG}/${REPO}
|
|
Flux GitRepository (cluster): http://${HOST_NAT_IP}:${HTTP_PORT}/${ORG}/${REPO}.git (clusters/dev)
|
|
Admin creds (host, 0600): ${ADMIN_CRED_FILE}
|
|
Prerequisite: firewalld must allow VM->host:${HTTP_PORT} —
|
|
ansible-playbook infra/ansible/playbooks/host-prereqs.yml --ask-become-pass
|
|
EOF
|