diff --git a/k8s-bootstrap/CLAUDE.md b/k8s-bootstrap/CLAUDE.md index df6e0c7..19fd66e 100644 --- a/k8s-bootstrap/CLAUDE.md +++ b/k8s-bootstrap/CLAUDE.md @@ -5,7 +5,7 @@ Bootstraps a k3s cluster on the 5 Proxmox VMs created by `proxmox-infra`. Starts ## How it works 1. Starts all 5 VMs via `POST /api2/json/nodes/{node}/qemu/{vmid}/status/start` -2. Waits for port 22 to open on each VM (`nc -z`) +2. Waits for port 22 to open on each VM (bash `/dev/tcp`) 3. Installs k3s on `k3s-master-1` with `--cluster-init --tls-san ` 4. Joins `k3s-master-2` and `k3s-master-3` as embedded etcd nodes 5. Joins `k3s-worker-1` and `k3s-worker-2` as agent nodes @@ -18,12 +18,6 @@ VM IDs and the CI runner SSH private key are read automatically from the `proxmo Run in this directory after `pulumi stack init dev`: ```bash -# Same as proxmox-infra -pulumi config set --secret pve1Endpoint "https://192.168.1.x:8006" -pulumi config set --secret pve1ApiToken "root@pam!pulumi=" -pulumi config set --secret pve2Endpoint "https://192.168.1.y:8006" -pulumi config set --secret pve2ApiToken "root@pam!pulumi=" - # Pre-shared k3s token — any strong random string pulumi config set --secret k3sToken "$(openssl rand -hex 32)" @@ -35,6 +29,14 @@ pulumi config set worker1Ip "192.168.1.x" pulumi config set worker2Ip "192.168.1.x" ``` +Proxmox credentials (`pve1Endpoint`, `pve1ApiToken`, `pve2Endpoint`, `pve2ApiToken`) are read automatically from the `proxmox-infra` stack outputs via StackReference — do **not** set them here. + +## Deployment order + +`proxmox-infra` must be deployed **before** k8s-bootstrap. The proxmox-infra stack exports the Proxmox credentials and VM IDs that k8s-bootstrap reads via StackReference. If proxmox-infra hasn't been re-deployed after its latest code changes, those outputs won't exist and k8s-bootstrap will fail. + +In CI, both workflows run as previews on PRs (no deploy). Trigger `workflow_dispatch` on `deploy-proxmox-infra` first, then run k8s-bootstrap. + After setting config, re-encode `Pulumi.dev.yaml` and update the Gitea secret `K8S_BOOTSTRAP_DEV_YAML`: ```bash base64 -w 0 Pulumi.dev.yaml diff --git a/k8s-bootstrap/index.ts b/k8s-bootstrap/index.ts index 44b7467..d7fd607 100644 --- a/k8s-bootstrap/index.ts +++ b/k8s-bootstrap/index.ts @@ -79,7 +79,7 @@ const allStarts = [startMaster1, startMaster2, startMaster3, startWorker1, start // --------------------------------------------------------------------------- const waitMaster1Ssh = new command.local.Command("wait-ssh-master-1", { - create: `for i in $(seq 1 60); do nc -z -w 5 ${master1Ip} 22 && exit 0; sleep 5; done; exit 1`, + create: `for i in $(seq 1 60); do (timeout 5 bash -c "echo > /dev/tcp/${master1Ip}/22") 2>/dev/null && exit 0; sleep 5; done; exit 1`, interpreter: ["/bin/bash", "-c"], }, { dependsOn: allStarts }); @@ -98,12 +98,12 @@ const waitK3sMaster1Ready = new command.remote.Command("wait-k3s-master-1-ready" // --------------------------------------------------------------------------- const waitMaster2Ssh = new command.local.Command("wait-ssh-master-2", { - create: `for i in $(seq 1 60); do nc -z -w 5 ${master2Ip} 22 && exit 0; sleep 5; done; exit 1`, + create: `for i in $(seq 1 60); do (timeout 5 bash -c "echo > /dev/tcp/${master2Ip}/22") 2>/dev/null && exit 0; sleep 5; done; exit 1`, interpreter: ["/bin/bash", "-c"], }, { dependsOn: [waitK3sMaster1Ready] }); const waitMaster3Ssh = new command.local.Command("wait-ssh-master-3", { - create: `for i in $(seq 1 60); do nc -z -w 5 ${master3Ip} 22 && exit 0; sleep 5; done; exit 1`, + create: `for i in $(seq 1 60); do (timeout 5 bash -c "echo > /dev/tcp/${master3Ip}/22") 2>/dev/null && exit 0; sleep 5; done; exit 1`, interpreter: ["/bin/bash", "-c"], }, { dependsOn: [waitK3sMaster1Ready] }); @@ -122,12 +122,12 @@ const joinMaster3 = new command.remote.Command("join-k3s-master-3", { // --------------------------------------------------------------------------- const waitWorker1Ssh = new command.local.Command("wait-ssh-worker-1", { - create: `for i in $(seq 1 60); do nc -z -w 5 ${worker1Ip} 22 && exit 0; sleep 5; done; exit 1`, + create: `for i in $(seq 1 60); do (timeout 5 bash -c "echo > /dev/tcp/${worker1Ip}/22") 2>/dev/null && exit 0; sleep 5; done; exit 1`, interpreter: ["/bin/bash", "-c"], }, { dependsOn: [joinMaster3] }); const waitWorker2Ssh = new command.local.Command("wait-ssh-worker-2", { - create: `for i in $(seq 1 60); do nc -z -w 5 ${worker2Ip} 22 && exit 0; sleep 5; done; exit 1`, + create: `for i in $(seq 1 60); do (timeout 5 bash -c "echo > /dev/tcp/${worker2Ip}/22") 2>/dev/null && exit 0; sleep 5; done; exit 1`, interpreter: ["/bin/bash", "-c"], }, { dependsOn: [joinMaster3] });