diff --git a/.github/scripts/test-unique-hostname-precheck.sh b/.github/scripts/test-unique-hostname-precheck.sh new file mode 100755 index 0000000..a2c5f7c --- /dev/null +++ b/.github/scripts/test-unique-hostname-precheck.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +site_play="$repo_root/site.yml" + +# #636: verify the "Pre tasks" play asserts that all k3s_cluster hosts have +# unique hostnames, so a duplicate-hostname inventory fails fast instead of +# silently breaking node registration/joining. +grep -Fq -- 'Verify all cluster nodes have unique hostnames' "$site_play" || { + printf 'site.yml is missing the unique-hostname preflight check\n' >&2 + exit 1 +} + +# The check must deduplicate the cluster hostname list via the `unique` filter +# and compare lengths, i.e. groups['k3s_cluster'] must be referenced. +grep -Fq -- "groups['k3s_cluster']" "$site_play" || { + printf 'unique-hostname check does not iterate the k3s_cluster group\n' >&2 + exit 1 +} + +if ! grep -Eq -- 'cluster_hostnames.*\|.*unique|\| unique' "$site_play"; then + printf 'unique-hostname check does not deduplicate the hostname list\n' >&2 + exit 1 +fi + +printf 'Unique hostname preflight regression test passed\n' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 08ab344..53e78b6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,6 +62,12 @@ repos: language: system pass_filenames: false files: ^roles/k3s_server/tasks/(main|join_master)\.yml$|^\.github/scripts/test-k3s-server-bootstrap\.sh$ + - id: unique-hostname-precheck-test + name: Unique hostname precheck test + entry: .github/scripts/test-unique-hostname-precheck.sh + language: system + pass_filenames: false + files: ^site\.yml$|^\.github/scripts/test-unique-hostname-precheck\.sh$ - id: cilium-bgp-manifest-test name: Cilium BGP manifest test entry: python3 .github/scripts/test-cilium-bgp-manifest.py diff --git a/README.md b/README.md index d50f8e9..4dbc855 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,10 @@ Supported processor architectures are: - Server and agent nodes should support passwordless SSH access. Otherwise, pass `--ask-pass --ask-become-pass` to each playbook command. +- Every node in the cluster must have a **unique hostname**. k3s registers each node keyed by its hostname, so + two nodes with the same hostname cannot join the cluster. `site.yml` asserts this up front and fails fast if + any duplicate is found. + ## 🚀 Getting Started ### 🍴 Preparation diff --git a/site.yml b/site.yml index 1fe2274..8a11ce7 100644 --- a/site.yml +++ b/site.yml @@ -8,6 +8,23 @@ msg: > "Ansible is out of date. See here for more info: https://docs.technotim.com/posts/ansible-automation/" + - name: Verify all cluster nodes have unique hostnames + ansible.builtin.assert: + that: (cluster_hostnames | unique | length) == (cluster_hostnames | length) + msg: >- + k3s nodes must have unique hostnames. Found a duplicate in: + {{ cluster_hostnames | unique }}. Each node registers in the cluster + keyed by its hostname, so matching hostnames prevent nodes from joining. + vars: + cluster_hostnames: >- + {{ + groups['k3s_cluster'] + | map('extract', hostvars, 'ansible_hostname') + | list + }} + run_once: true + when: "'k3s_cluster' in groups" + - name: Prepare Proxmox cluster hosts: proxmox gather_facts: true