# Atlas Terraform Terraform owns the declarative day-zero pieces that are not Flux workload state: - Atlas host/node inventory used to generate Ananke config input. - Required Kubernetes Node labels through `kubernetes_labels`. - Optional render-only Flux day-zero manifests from `clusters/atlas/flux-system`. Terraform does not own Flux-managed workload manifests. Do not move `services/`, `infrastructure/`, or Flux `Kustomization` trees under Terraform. Do not make manual `kubectl` edits for durable Kubernetes state. ## Ownership Terraform owns: - node names, IPs, roles, managed-node membership, ignored/unavailable markers - default SSH user, port, config path, identity path emitted into Ananke inventory - `startup.required_node_labels` values, applied as Kubernetes Node labels - generated `ananke.inventory.yaml` Flux owns: - workloads, platform services, Helm releases, Flux `Kustomization` resources, and service manifests - steady-state reconciliation of objects under `clusters/atlas/flux-system`, `infrastructure/`, and `services/` Ananke owns: - UPS shutdown decisions - SSH repair and host recovery - k3s restart/reboot - Longhorn/runtime recovery - cordon/uncordon, pod recycling, Flux suspend/resume during recovery - startup validation/reporting for Terraform-owned labels when `required_node_labels_mode: validate` ## Run ```bash cd terraform/atlas cp terraform.tfvars.example terraform.tfvars terraform init terraform plan ``` For local syntax/provider validation that does not touch the cluster: ```bash terraform init -backend=false terraform validate ``` Only run `terraform apply` after reviewing the plan and confirming label ownership. The Kubernetes provider uses `var.kubeconfig_path`, defaulting to `~/.kube/config`; no kubeconfig or secret material belongs in this repo. The backend is local by default. When remote state is ready, migrate state with `terraform init -migrate-state` after adding the remote backend configuration in a separate reviewed change. ## Generated Ananke Fragment The inventory module writes `generated/ananke.inventory.yaml` by default. It contains no secrets and is intended to be installed beside the main Ananke config: ```bash sudo ANANKE_INVENTORY_FRAGMENT=/opt/titan-iac/terraform/atlas/generated/ananke.inventory.yaml /opt/ananke/scripts/install.sh ``` Ananke auto-loads `/etc/ananke/ananke.inventory.yaml` when present. The generated fragment sets: - `ssh_node_hosts` - `ssh_managed_nodes` - `control_planes` - `workers` - `startup.required_node_labels` - `startup.required_node_labels_mode: validate` - `startup.ignore_unavailable_nodes` ## Node Labels This tree uses the HashiCorp Kubernetes provider `kubernetes_labels` resource. That resource manages only labels declared in Terraform and ignores existing labels not declared here. Leave `force_node_label_conflicts = false` for normal plans. Set it true only during a deliberate adoption when field ownership conflicts are understood. ## Flux Day-Zero `clusters/atlas/flux-system/gotk-components.yaml` and `gotk-sync.yaml` already exist in the Flux bootstrap tree, so Terraform does not manage those objects by default. The `flux_day_zero` module can render those artifacts into one file when `render_flux_day_zero_manifests = true`. Keep `apply_flux_day_zero_manifests = false` for steady state. Enabling apply runs `kubectl apply --server-side` and is only for an empty-cluster bootstrap with explicit operator intent. ## Migration Plan 1. Copy `terraform.tfvars.example` to `terraform.tfvars` and adjust only local paths. 2. Run `terraform init -backend=false` and `terraform validate`. 3. Run `terraform plan` in plan-only mode and review Node label changes. 4. Adopt existing label state with `kubernetes_labels` resources. This resource does not use `terraform import`; creating it against existing Nodes is the adoption step. 5. Install the generated Ananke fragment and confirm Ananke reads `required_node_labels_mode: validate`. 6. Run `terraform apply` for labels after the plan is clean. 7. Run Ananke startup dry-run/status and confirm it reports label drift instead of applying labels. 8. Enable in production after Ananke is validating only and Terraform is the only steady-state writer for these labels. ## Conflict Rules - Do not add Terraform resources for any object already in Flux unless it is a one-time day-zero bootstrap action and Terraform will not keep steady-state ownership. - Do not add Flux manifests for Node labels Terraform manages here. - Do not use Terraform for recovery behavior that Ananke performs imperatively.