Files
home-network-k3s-impl/infra/ansible/roles/k3s_server/tasks/main.yml
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

89 lines
3.2 KiB
YAML

---
# Install k3s as an HA server (embedded etcd) with Cilium-required components
# disabled (ADR-0001, docs/02 stage 3 "Cluster-join"). The primary runs
# --cluster-init; secondaries join it with the shared node-token.
- name: Download the k3s install script
ansible.builtin.get_url:
url: "{{ k3s_install_url }}"
dest: "{{ k3s_install_script }}"
mode: "0755"
- name: Compose the common k3s server flags
ansible.builtin.set_fact:
k3s_common_args: >-
server
--flannel-backend=none
--disable-kube-proxy
--disable-network-policy
--disable={{ k3s_disable }}
--write-kubeconfig-mode=644
--tls-san={{ k3s_api_host }}
--node-ip={{ ansible_host }}
# --- primary: found the cluster --------------------------------------------
- name: Install k3s primary (cluster-init, embedded etcd)
ansible.builtin.command:
cmd: "{{ k3s_install_script }} {{ k3s_common_args }} --cluster-init"
creates: /etc/rancher/k3s/k3s.yaml
environment:
INSTALL_K3S_VERSION: "{{ k3s_version }}"
when: k3s_server_mode == 'primary'
- name: Wait for the k3s node-token to exist (primary)
ansible.builtin.wait_for:
path: /var/lib/rancher/k3s/server/token
timeout: 180
when: k3s_server_mode == 'primary'
- name: Read the k3s node-token (primary)
ansible.builtin.slurp:
src: /var/lib/rancher/k3s/server/token
register: k3s_token_raw
when: k3s_server_mode == 'primary'
- name: Publish the node-token as a fact for secondaries (primary)
ansible.builtin.set_fact:
k3s_node_token: "{{ k3s_token_raw.content | b64decode | trim }}"
when: k3s_server_mode == 'primary'
# --- secondaries: obtain the join token, then join the existing cluster -----
# Fetch the token straight from the primary via delegate_to, falling back to it
# only when the primary play didn't publish the fact this run. This makes a
# single-node rejoin (`--limit k3s-dev-N`, primary NOT in the batch) self-
# contained — delegate_to still reaches the primary even when it isn't a play
# target, so the join no longer depends on a fact an earlier play had to set.
# T-1.5 finding F2 (see DECISIONS-LOG.md).
- name: Fetch the k3s node-token directly from the primary (secondary)
ansible.builtin.slurp:
src: /var/lib/rancher/k3s/server/token
delegate_to: "{{ groups['k3s_primary'][0] }}"
register: k3s_primary_token_raw
when:
- k3s_server_mode == 'secondary'
- hostvars[groups['k3s_primary'][0]].k3s_node_token is not defined
- name: Resolve the join token (secondary)
ansible.builtin.set_fact:
k3s_join_token: >-
{{ hostvars[groups['k3s_primary'][0]].k3s_node_token
| default(k3s_primary_token_raw.content | b64decode | trim, true) }}
when: k3s_server_mode == 'secondary'
- name: Install k3s secondary (join as server)
ansible.builtin.command:
cmd: >-
{{ k3s_install_script }} {{ k3s_common_args }}
--server https://{{ k3s_api_host }}:{{ k3s_api_port }}
creates: /etc/rancher/k3s/k3s.yaml
environment:
INSTALL_K3S_VERSION: "{{ k3s_version }}"
K3S_TOKEN: "{{ k3s_join_token }}"
when: k3s_server_mode == 'secondary'
- name: Wait for the k3s API to be serving locally
ansible.builtin.wait_for:
host: "127.0.0.1"
port: "{{ k3s_api_port }}"
timeout: 180