Compare commits

...

3 Commits

Author SHA1 Message Date
017d5ce00d Merge pull request 'stage' (#26) from stage into master
Some checks failed
Terraform Apply / Terraform Apply (push) Has been cancelled
Reviewed-on: #26
2026-02-28 12:14:24 +00:00
6fada2f32a refactor: use direct tailscale auth-key enrollment
All checks were successful
Terraform Plan / Terraform Plan (push) Successful in 18s
Stop writing auth keys to guest files and enroll nodes by running tailscale up directly via Proxmox guest agent with VM-name hostnames.
2026-02-28 12:12:58 +00:00
510ba707ad fix: stabilize tailscale enrollment without cloud-init rollback
All checks were successful
Terraform Plan / Terraform Plan (push) Successful in 17s
Create /etc/tailscale before writing runtime key, add progress logging and unbuffered output in enroll script, and shorten guest-agent wait to fail faster when enrollment cannot run.
2026-02-28 12:09:40 +00:00
2 changed files with 12 additions and 35 deletions

View File

@@ -144,15 +144,21 @@ jobs:
payload = resp.read().decode("utf-8") payload = resp.read().decode("utf-8")
return json.loads(payload) return json.loads(payload)
def wait_for_guest_agent(vmid, timeout_seconds=900): def wait_for_guest_agent(vmid, timeout_seconds=300):
deadline = time.time() + timeout_seconds deadline = time.time() + timeout_seconds
tries = 0
while time.time() < deadline: while time.time() < deadline:
tries += 1
try: try:
res = api_request("GET", f"/api2/json/nodes/{target_node}/qemu/{vmid}/agent/ping") res = api_request("GET", f"/api2/json/nodes/{target_node}/qemu/{vmid}/agent/ping")
if res.get("data") == "pong": if res.get("data") == "pong":
print(f"Guest agent ready for vmid {vmid}", flush=True)
return True return True
except Exception: except Exception:
pass pass
if tries % 6 == 0:
remaining = int(deadline - time.time())
print(f"Waiting for guest agent on vmid {vmid} ({remaining}s left)", flush=True)
time.sleep(5) time.sleep(5)
return False return False
@@ -193,13 +199,12 @@ jobs:
safe_hostname = hostname.replace("'", "'\"'\"'") safe_hostname = hostname.replace("'", "'\"'\"'")
cmd = ( cmd = (
"set -e; " "set -e; "
f"printf '%s' '{safe_key}' > /etc/tailscale/authkey; "
f"printf '%s' '{safe_hostname}' > /etc/tailscale/hostname; "
"chmod 600 /etc/tailscale/authkey; "
f"hostnamectl set-hostname '{safe_hostname}' || true; " f"hostnamectl set-hostname '{safe_hostname}' || true; "
"install -d -m 700 /var/lib/tailscale; "
"rm -f /var/lib/tailscale/tailscaled.state; "
"systemctl restart tailscaled; " "systemctl restart tailscaled; "
"systemctl start tailscale-firstboot.service; " f"/run/current-system/sw/bin/tailscale up --reset --auth-key='{safe_key}' --hostname='{safe_hostname}'; "
"tailscale status || true" "/run/current-system/sw/bin/tailscale status || true"
) )
exitcode, stdout, stderr = exec_guest(vmid, cmd) exitcode, stdout, stderr = exec_guest(vmid, cmd)
@@ -227,4 +232,4 @@ jobs:
print("\nTailscale enrollment completed for all managed VMs") print("\nTailscale enrollment completed for all managed VMs")
PY PY
python3 enroll_tailscale.py python3 -u enroll_tailscale.py

View File

@@ -39,34 +39,6 @@
security.sudo.wheelNeedsPassword = false; security.sudo.wheelNeedsPassword = false;
systemd.services.tailscale-firstboot = {
description = "One-time Tailscale enrollment";
after = [ "network-online.target" "tailscaled.service" ];
wants = [ "network-online.target" "tailscaled.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
if [ ! -s /etc/tailscale/authkey ]; then
exit 0
fi
key="$(cat /etc/tailscale/authkey)"
ts_hostname=""
if [ -s /etc/tailscale/hostname ]; then
ts_hostname="--hostname=$(cat /etc/tailscale/hostname)"
fi
rm -f /var/lib/tailscale/tailscaled.state
${pkgs.tailscale}/bin/tailscale up --reset --auth-key="$key" $ts_hostname
rm -f /etc/tailscale/authkey
rm -f /etc/tailscale/hostname
'';
};
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
btop btop
curl curl