Files
home-network-k3s-impl/infra/tofu/moved.tf
T
jakman85andClaude Opus 4.8 95e3eafac6 iac: harden node lifecycle — tofu for_each + self-contained ansible rejoin
Implements T-1.5 findings F1/F2 (author-first, no cluster change).

F1 (tofu): refactor libvirt node resources count -> for_each keyed by
node name so single-node -replace/destroy cannot cascade to siblings.
Add ordered-list/primary helper locals; update outputs.tf off the map;
add moved.tf (12 moved blocks). tofu plan proves a pure state-address
migration: 0 to add, 0 to change, 0 to destroy (all 12 'has moved to').
State-persist via a 0-change apply is operator-permission-gated.

F2 (ansible): k3s_server secondary fetches the join token via
delegate_to the primary (published fact as fast path), so a single-node
rejoin with --limit k3s-dev-N is self-contained and no longer depends on
a fact an earlier play had to set. Syntax-check + CI gate green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 19:42:12 -05:00

69 lines
2.1 KiB
Terraform

# moved.tf — state-address migration for the count -> for_each refactor (T-1.5 F1).
#
# The node resources changed from positional `count` indexes ([0],[1],[2]) to
# name-keyed `for_each` (["k3s-dev-1"], …). Without these `moved` blocks OpenTofu
# would read the address change as "destroy the old, create the new" and rebuild
# every node. Each block tells tofu the resource is the SAME object under a new
# address, so the plan is a pure in-state rename: NO destroy, NO recreate.
#
# LOCKED mapping (node_count = 3, prefix "k3s-dev-"): index i -> "k3s-dev-${i+1}".
# `moved` blocks are static config (no for_each), so they are written explicitly.
# Safe to KEEP indefinitely (harmless once applied) or delete after one apply on
# every environment that held count-indexed state.
# --- OS disks ---------------------------------------------------------------
moved {
from = libvirt_volume.os[0]
to = libvirt_volume.os["k3s-dev-1"]
}
moved {
from = libvirt_volume.os[1]
to = libvirt_volume.os["k3s-dev-2"]
}
moved {
from = libvirt_volume.os[2]
to = libvirt_volume.os["k3s-dev-3"]
}
# --- data disks -------------------------------------------------------------
moved {
from = libvirt_volume.data[0]
to = libvirt_volume.data["k3s-dev-1"]
}
moved {
from = libvirt_volume.data[1]
to = libvirt_volume.data["k3s-dev-2"]
}
moved {
from = libvirt_volume.data[2]
to = libvirt_volume.data["k3s-dev-3"]
}
# --- cloud-init seeds -------------------------------------------------------
moved {
from = libvirt_cloudinit_disk.seed[0]
to = libvirt_cloudinit_disk.seed["k3s-dev-1"]
}
moved {
from = libvirt_cloudinit_disk.seed[1]
to = libvirt_cloudinit_disk.seed["k3s-dev-2"]
}
moved {
from = libvirt_cloudinit_disk.seed[2]
to = libvirt_cloudinit_disk.seed["k3s-dev-3"]
}
# --- domains (VMs) ----------------------------------------------------------
moved {
from = libvirt_domain.node[0]
to = libvirt_domain.node["k3s-dev-1"]
}
moved {
from = libvirt_domain.node[1]
to = libvirt_domain.node["k3s-dev-2"]
}
moved {
from = libvirt_domain.node[2]
to = libvirt_domain.node["k3s-dev-3"]
}