From cf76292169f12b470529514c132c7e5c525371ce Mon Sep 17 00:00:00 2001 From: Techno Tim Date: Wed, 5 Aug 2026 05:14:29 -0500 Subject: [PATCH] feat(site): fail fast when cluster nodes share a hostname (#697) Add a preflight assertion to site.yml that confirms every host in the k3s_cluster group reports a unique hostname. k3s registers each node keyed by its hostname, so duplicate hostnames prevent nodes from joining the cluster and are hard to troubleshoot. The check fails fast in the Pre tasks instead of surfacing as a cryptic registration failure later. - site.yml: assert (cluster_hostnames | unique | length) == length for the k3s_cluster group, skipping hosts outside that group - README.md: document the unique-hostname requirement - .github/scripts/test-unique-hostname-precheck.sh: regression test - .pre-commit-config.yaml: wire the test into pre-commit Closes #636 --- .../scripts/test-unique-hostname-precheck.sh | 28 +++++++++++++++++++ .pre-commit-config.yaml | 6 ++++ README.md | 4 +++ site.yml | 17 +++++++++++ 4 files changed, 55 insertions(+) create mode 100755 .github/scripts/test-unique-hostname-precheck.sh 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