Files
home-network-k3s-impl/infra/tofu/variables.tf
T
jakman85andClaude Opus 5 12c40d8c9c docs: reconcile the documentation with the deployed reality
The docs had drifted badly behind ~85 commits of delivery. The root README still
said "Phase 0 — Foundations. No cluster exists yet"; the infra docs still
described a libvirt/KVM cluster that has been retired for two days; and
DECISIONS-LOG.md — the PM's authoritative record — stopped at 2026-08-25 AM.
Anyone following these docs would have been working from a false map.

Verified against the live cluster (3/3 nodes, 7/7 Flux Kustomizations Ready,
10/10 HelmReleases Ready) rather than inferred from commit messages.

README.md
  - Phase 0 -> a per-phase status table: 0-6 delivered and deployed, 7 not open.
  - Layout now shows the wave split, platform-config, apps/vms, infra, bootstrap.
  - Real conformance number (36/51, 70.6%) and how to regenerate it.
  - Devcontainer workflow; note that the gate was red and why.

infra/PHASE1-STATUS.md — rewritten. Was a `virsh`/libvirt resume guide for a
  cluster that no longer exists. Now: the Proxmox substrate, the pfSense NAT
  paths, both kubectl access paths (Keycloak OIDC normal / cert break-glass),
  the toolchain, off-repo state that must not be lost, and the fact that Flux's
  git source is an app inside the cluster (`git push origin main` IS the deploy).

infra/PHASE1-SCOPE.md — historical banner. The exit criteria and sizing
  rationale are still worth keeping; the libvirt execution steps are not.

infra/README.md — Proxmox, plus a map of the subtree and where the cluster
  actually lives (it is not on this workstation).

infra/devcontainer/README.md — the "run tofu and ansible on the HOST" limitation
  is OBSOLETE: it was a dmacvicar/libvirt polkit/PID problem, and bpg/proxmox
  talks to an HTTP API. Everything runs in the container now. Documents the full
  CI-gate toolchain and what each bind mount is load-bearing for (the age keyring
  and the read-only spec repo especially — without the latter, spec-immutability
  silently skips instead of verifying).

infra/tofu/README.md — was "Authoring only — nothing here has been applied". It
  is applied and live. Disk sizes corrected 40/100 -> 64/128 GiB (confirmed
  against the running nodes), pfSense NAT marked done, and a warning that
  terraform.tfstate is absent on a fresh workstation — an apply with empty state
  would try to recreate the three live nodes.

infra/tofu/variables.tf — section comment disagreed with its own defaults.

infra/cluster.sh — retired-with-instructions header. It silently drives libvirt
  domains that do not exist; behaviour unchanged, but it no longer lies.

apps/vms/unifi/README.md — was "NOT WIRED IN", 60Gi, and listed a file that has
  since been dropped. Now: live status, 40Gi, resolved open questions marked
  resolved, and the two constraints the next person will otherwise rediscover —
  KubeVirt's 2048B inline userData cap, and that apps-vms must never carry
  postBuild.substituteFrom (envsubst corrupts the cloud-init).

DECISIONS-LOG.md — backfilled the gap as one structured entry: the Proxmox
  migration, NAT + kube-vip, the twice-proven oauth2-proxy cold-start deadlock,
  the Flux source cutover, CI going live in-cluster, ten app onboardings and the
  renderer bugs they exposed, the wave split (verified: DBs adopted, not
  recreated — original ages intact, prune restored), KubeVirt storage, the UniFi
  VM, and kubectl SSO. Plus verified live state and the carried-forward open list.

Two corrections to the record while verifying:
  - kubectl OIDC is LIVE (`kubectl auth whoami` -> oidc:<user>,
    [oidc:platform-admins]), so the rolling restart 3472f1d deferred has
    happened.
  - UniFi OS Server is NOT installed in the guest. The VM is healthy (Ubuntu
    24.04.4, guest agent connected, live-migratable) but :11443 answers nothing,
    and the CNP explicitly permits cluster -> :11443, so it is genuinely not
    listening rather than a policy artifact.

Docs and comments only; no manifest semantics change.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-27 00:00:05 -05:00

200 lines
6.5 KiB
Terraform

# variables.tf — every sizing / IP / image knob is a variable (Proxmox migration).
# Defaults encode the migration target: 3 k3s nodes on the Proxmox VE box
# (node "proxmox", 192.168.1.0/24 LAN). Override via terraform.tfvars (see
# terraform.tfvars.example). Traces R-ENV-01 (reproducible node provisioning),
# R-PLAT-01.
# --- Proxmox connection / placement ----------------------------------------
variable "proxmox_endpoint" {
description = <<-EOT
Proxmox VE API endpoint, e.g. "https://192.168.2.102:8006/". Optional: if unset
the provider reads PROXMOX_VE_ENDPOINT from the environment directly. Set this
(e.g. via `export TF_VAR_proxmox_endpoint="$PROXMOX_VE_ENDPOINT"`) to let tofu
NORMALIZE a schemeless endpoint (prepend https://) — see versions.tf. The API
TOKEN is NOT a variable; it is read from PROXMOX_VE_API_TOKEN by the provider so
it never enters tofu state/vars.
EOT
type = string
default = null
}
variable "proxmox_insecure" {
description = "Skip TLS verification for the Proxmox self-signed cert (also read from PROXMOX_VE_INSECURE)."
type = bool
default = true
}
variable "proxmox_node" {
description = "Proxmox VE node name that hosts the VMs (VERIFIED: proxmox)."
type = string
default = "proxmox"
}
variable "datastore" {
description = "Datastore for VM OS + data disks (VERIFIED: Storage, ZFS 4.3TB, rootdir+images)."
type = string
default = "Storage"
}
variable "image_datastore" {
description = "Datastore that holds the downloaded cloud image / cloud-init snippets (VERIFIED: local, dir storage)."
type = string
default = "local"
}
variable "bridge" {
description = "Proxmox network bridge for the node NIC (VERIFIED: vmbr0, no VLAN tag)."
type = string
default = "vmbr0"
}
# --- cluster identity / naming ---------------------------------------------
variable "cluster_name" {
description = "Logical cluster name; used as a resource-name prefix."
type = string
default = "k3s-dev"
}
variable "node_name_prefix" {
description = "VM hostname prefix. Nodes are <prefix>1, <prefix>2, ... (LOCKED: k3s-dev-1/2/3)."
type = string
default = "k3s-dev-"
}
variable "node_count" {
description = "Number of k3s nodes (LOCKED: 3-server HA embedded etcd)."
type = number
default = 3
validation {
condition = var.node_count == length(var.node_ips)
error_message = "node_count must equal the number of entries in node_ips."
}
}
# --- per-node sizing (Proxmox target: 6 vCPU / 16 GiB / 64 GiB OS + 128 GiB data) --
variable "vcpu" {
description = "vCPUs (cores) per node. Generous — the box has plenty."
type = number
default = 6
}
variable "memory_mib" {
description = "RAM per node in MiB (16 GiB)."
type = number
default = 16384
}
variable "os_disk_gib" {
description = "OS (root) disk size per node, GiB. Imported from the cloud image, on the Storage datastore."
type = number
default = 64
}
variable "data_disk_gib" {
description = "Dedicated Longhorn data disk per node, GiB. Blank, on the Storage datastore."
type = number
default = 128
}
variable "cpu_type" {
description = "Proxmox vCPU type. `host` passes through the physical CPU (nested-virt for KubeVirt, Phase 5)."
type = string
default = "host"
}
# --- network (Proxmox target: main LAN 192.168.1.0/24, gw .1, DNS PiHole .18) ---
variable "network_cidr" {
description = "Cluster subnet in CIDR (informational; nodes get static IPs from node_ips)."
type = string
default = "192.168.1.0/24"
}
variable "network_prefix" {
description = "Netmask prefix length for node static addressing."
type = number
default = 24
}
variable "network_gateway" {
description = "Default gateway for nodes (VERIFIED: 192.168.1.1, pfSense)."
type = string
default = "192.168.1.1"
}
variable "dns_server" {
description = "DNS resolver for nodes (VERIFIED: 192.168.1.18, PiHole)."
type = string
default = "192.168.1.18"
}
variable "node_ips" {
description = "Static IPs per node, in order (VERIFIED: .50/.51/.52 on 192.168.1.0/24)."
type = list(string)
default = ["192.168.1.50", "192.168.1.51", "192.168.1.52"]
}
# Reserved for Cilium L2-announcement ingress VIPs (VERIFIED: .53-.60).
# Not materialised as Proxmox objects; surfaced in outputs. Cilium's IP pool
# (platform-config/cilium/l2-ip-pool.yaml) consumes this range.
variable "cilium_l2_vip_range_start" {
description = "First reserved Cilium L2 VIP."
type = string
default = "192.168.1.53"
}
variable "cilium_l2_vip_range_end" {
description = "Last reserved Cilium L2 VIP."
type = string
default = "192.168.1.60"
}
# --- base OS image ----------------------------------------------------------
variable "image_source" {
description = <<-EOT
Source URL for the base OS cloud image (qcow2), downloaded to image_datastore
by proxmox_virtual_environment_download_file. Default is Debian 12 (bookworm)
generic amd64 (full firmware/driver, hybrid BIOS/UEFI). Use `generic`, NOT
`genericcloud`: the genericcloud image panics with "Attempted to kill init" on
import + disk-resize under bpg (bpg issue #1639).
EOT
type = string
default = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2"
}
variable "image_file_name" {
description = <<-EOT
File name the cloud image is stored as on image_datastore. With `import`-content
storage (PVE 8.2+) the native .qcow2 name is used directly — the old .img rename
was an `iso`-content workaround and is no longer needed. Uses the `generic`
(full firmware/driver, hybrid BIOS/UEFI) image, NOT `genericcloud`: genericcloud
panics with "Attempted to kill init" on import + disk-resize (bpg issue #1639).
EOT
type = string
default = "debian-12-generic-amd64.qcow2"
}
# --- cloud-init identity ----------------------------------------------------
variable "ssh_authorized_key" {
description = "SSH PUBLIC key injected into the login user (public only — never a private key)."
type = string
}
variable "login_user" {
description = "Non-root login user created by cloud-init; also the Ansible ansible_user."
type = string
default = "k3s"
}
variable "node_domain" {
description = "DNS domain suffix for node FQDNs (informational; dev uses split-horizon / hosts)."
type = string
default = "k3s.dev.local"
}