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]>
89 lines
3.2 KiB
YAML
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
|