roles/k3s_server, roles/cilium and every node-host playbook hardcoded the dev group
names k3s_primary/k3s_secondary/k3s_servers, so the prod inventory groups could not be
selected at all. That broke the R-ENV-02 promise that prod reuses the SAME roles, and
blocked PROD-PROVISIONING.md §6 outright.
TWO KNOBS, deliberately separate — Ansible templates a play's `hosts:` BEFORE any host
context exists, so group_vars is unreadable there, while roles run WITH host context:
* which hosts a play RUNS ON — k3s_hosts, k3s_primary_hosts/k3s_secondary_hosts
(k3s.yml's split), vpn_egress_gateway_hosts; each `| default(<dev group>)`, set by -e
only. Follows node-health.yml's established `<name>_hosts | default(dev)` pattern.
* which group a ROLE means by "my primary"/"my servers" — k3s_primary_group,
k3s_servers_group in group_vars/all.yml (dev), overridden in group_vars/k3s_prod.
Follows the k3s_api_host convention already in this repo. No per-run flag.
DEV IS UNCHANGED — the key evidence. --list-hosts/--list-tasks/--syntax-check captured
for all 9 playbooks at HEAD and after: sorted host lists IDENTICAL for all 9, same
primary/secondary split, same 6-play site.yml. (Raw --list-hosts PRINT ORDER varies run
to run; proven to be Ansible's own set ordering by running UNMODIFIED HEAD cilium.yml
four times -> 3,1,2 / 1,3,2 / 1,2,3 / 1,2,3. Hence sorted comparison.) --list-tasks
differs in exactly two files, only by added TAGS annotations and one new assert; no task
reordered, renamed or removed. Var resolution verified per environment with -c local:
dev -> k3s_primary/192.168.1.50/sans .50,.49,127.0.0.1,.51,.52; prod ->
k3s_prod_primary/192.168.2.10/vip .9/sans .10,.9,127.0.0.1,.11,.12.
BREAKS THE SMARTMONTOOLS CIRCULAR DEPENDENCY. node_baseline tasks are now tagged
packages/system/disks, so the package install can run alone:
ansible-playbook playbooks/node-baseline.yml -e k3s_hosts=k3s_prod --tags packages
Skipping is the guard — no disks-tagged task is selected, so the
BLOCKED-pending-disk-decision paths are never templated, let alone passed to mkfs.
Belt-and-braces, a new disks-tagged assert fires first in a FULL run and names §2
explicitly instead of failing obscurely at mkfs. `tags: never` was deliberately NOT used
(it would change the default run).
FOOTGUN FOUND AND FIXED: roles/cilium, cluster_verify and flux_bootstrap all hardcoded
.cache/dev.kubeconfig — the exact file infra/devcontainer/dev.sh exports as $KUBECONFIG.
Once prod became targetable, a prod cilium/verify run would have SILENTLY OVERWRITTEN it,
leaving the operator's kubectl quietly answering from PROD. Now cluster_kubeconfig_name
(dev.kubeconfig -> prod.kubeconfig). Dev path byte-identical.
KNOWN, DOCUMENTED, NOT FIXED HERE: flux-bootstrap.yml retargets but roles/flux_bootstrap
shells out to bootstrap/flux/bootstrap.sh with a hardcoded --path=clusters/dev — a
bootstrap/ subtree change, not a target-group change (§6, cross-referenced to §7).
vpn.egress.gateway.yml is parameterised but prod has no egress group yet and would need
its own vaulted PIA credentials. --check against prod is still impossible until the prod
vault exists — even the packages-only run needs it.
Nothing was run against the dev cluster: --list-hosts/--list-tasks/--syntax-check, plus
debug/assert with -c local, only.
Serves: R-ENV-01, R-ENV-02, R-ENV-03. Task: T-7.1.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
115 lines
3.6 KiB
YAML
115 lines
3.6 KiB
YAML
---
|
|
# Install Cilium as the CNI (ADR-0002). Runs from the controller (dev container,
|
|
# which has kubectl + helm) against a kubeconfig fetched from the primary node.
|
|
# The cluster is NotReady until this completes — expected (PHASE1-SCOPE risks).
|
|
# run_once: this whole role acts on the cluster once, not per host.
|
|
|
|
# k3s_primary_group (group_vars) names THIS environment's primary group — `k3s_primary`
|
|
# on dev, `k3s_prod_primary` on prod — so the role fetches the right cluster's kubeconfig
|
|
# without being forked per environment (R-ENV-02).
|
|
- name: Fetch the k3s kubeconfig from the primary
|
|
ansible.builtin.slurp:
|
|
src: /etc/rancher/k3s/k3s.yaml
|
|
register: k3s_kubeconfig_raw
|
|
delegate_to: "{{ groups[k3s_primary_group][0] }}"
|
|
run_once: true
|
|
|
|
- name: Ensure the local cache dir exists
|
|
ansible.builtin.file:
|
|
path: "{{ cilium_kubeconfig | dirname }}"
|
|
state: directory
|
|
mode: "0700"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
|
|
- name: Write kubeconfig locally (server rewritten to the direct NAT endpoint)
|
|
ansible.builtin.copy:
|
|
content: >-
|
|
{{ k3s_kubeconfig_raw.content | b64decode
|
|
| regex_replace('https://127.0.0.1:6443', cluster_controller_api_server) }}
|
|
dest: "{{ cilium_kubeconfig }}"
|
|
mode: "0600"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
|
|
- name: Read the cluster name from the written kubeconfig
|
|
ansible.builtin.command:
|
|
cmd: "kubectl config view -o jsonpath={.clusters[0].name}"
|
|
environment:
|
|
KUBECONFIG: "{{ cilium_kubeconfig }}"
|
|
register: cilium_kubeconfig_cluster
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
changed_when: false
|
|
|
|
- name: Pin tls-server-name to the kube-vip VIP so TLS validates through the NAT
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
kubectl config set
|
|
clusters.{{ cilium_kubeconfig_cluster.stdout }}.tls-server-name
|
|
{{ cluster_controller_tls_server_name }}
|
|
environment:
|
|
KUBECONFIG: "{{ cilium_kubeconfig }}"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
changed_when: true
|
|
|
|
- name: Install Gateway API CRDs (must precede Cilium gatewayAPI.enabled)
|
|
ansible.builtin.command:
|
|
cmd: "kubectl apply -f {{ item }}"
|
|
environment:
|
|
KUBECONFIG: "{{ cilium_kubeconfig }}"
|
|
loop: "{{ gateway_api_crd_urls }}"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
changed_when: true
|
|
|
|
- name: Add the Cilium Helm repo
|
|
ansible.builtin.command:
|
|
cmd: "helm repo add {{ cilium_chart_repo_name }} {{ cilium_chart_repo_url }}"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
register: cilium_repo_add
|
|
changed_when: "'has been added' in cilium_repo_add.stdout"
|
|
failed_when:
|
|
- cilium_repo_add.rc != 0
|
|
- "'already exists' not in cilium_repo_add.stderr"
|
|
|
|
- name: Update Helm repos
|
|
ansible.builtin.command:
|
|
cmd: "helm repo update {{ cilium_chart_repo_name }}"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
changed_when: false
|
|
|
|
- name: Render the Cilium values file (parity-critical settings from k3s_api_host)
|
|
ansible.builtin.template:
|
|
src: cilium-values.yaml.j2
|
|
dest: "{{ cilium_values_path }}"
|
|
mode: "0644"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
|
|
- name: Install/upgrade Cilium via Helm
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
helm upgrade --install cilium {{ cilium_chart_repo_name }}/cilium
|
|
--version {{ cilium_version }}
|
|
--namespace {{ cilium_namespace }}
|
|
--values {{ cilium_values_path }}
|
|
--wait --timeout 10m
|
|
environment:
|
|
KUBECONFIG: "{{ cilium_kubeconfig }}"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
changed_when: true
|