mirror of
https://github.com/techno-tim/k3s-ansible.git
synced 2026-08-08 23:13:19 +02:00
f5483cdabe
* fix(flannel): default the interface to each host's default IPv4 interface - Replace the hardcoded flannel_iface: eth0 with a per-host default derived from ansible_facts.default_ipv4.interface - KVM/cloud hosts that are not named eth0 (e.g. enp1s0, ens3) now resolve the interface automatically instead of failing the k3s_node_ip lookup - Update the commented calico_iface / cilium_iface examples to match - Co-authored-by: Fritz Dunkel <677609+FinalDoom@users.noreply.github.com> - Fixes #621 * test(flannel): add regression test for per-host interface default - Add a focused test that renders the sample inventory's flannel_iface expression against fake ansible facts - Assert a non-eth0 host (enp1s0, ens3) resolves its own interface and that the expression defaults from ansible_facts.default_ipv4.interface - Wire it as a local pre-commit hook (default-interface-test) * test(molecule): assert nodes register a non-loopback InternalIP - Add a flannel-scenario verify assertion that every node reports an InternalIP derived from its configured flannel_iface - Guards against k3s binding to 127.0.0.1 instead of the cluster interface - Complements the pre-commit default-interface test with an end-to-end check
91 lines
2.7 KiB
Python
91 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Assert the sample inventory resolves the flannel interface per host.
|
|
|
|
flannel_iface defaults to the host's default IPv4 interface rather than a
|
|
hardcoded eth0. This test extracts the flannel_iface expression from the
|
|
sample inventory and proves that a host whose primary interface is not named
|
|
eth0 (e.g. enp1s0, ens3) resolves the interface from ansible facts.
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
|
|
from jinja2 import Environment, StrictUndefined
|
|
|
|
|
|
def repo_root():
|
|
return subprocess.check_output(
|
|
["git", "rev-parse", "--show-toplevel"], text=True
|
|
).strip()
|
|
|
|
|
|
def fail(message):
|
|
raise SystemExit("default-interface test failed: " + message)
|
|
|
|
|
|
class FakeAnsibleFacts(object):
|
|
"""Stand-in for the per-host ``ansible_facts`` dict."""
|
|
|
|
def __init__(self, default_iface, iface_ip):
|
|
self._default = {"interface": default_iface, "address": iface_ip}
|
|
self._ifaces = {
|
|
default_iface: {"ipv4": {"address": iface_ip}},
|
|
}
|
|
|
|
@property
|
|
def default_ipv4(self):
|
|
return self._default
|
|
|
|
def __getitem__(self, key):
|
|
return self._ifaces[key]
|
|
|
|
|
|
def read_all_yml(root):
|
|
path = os.path.join(root, "inventory", "sample", "group_vars", "all.yml")
|
|
with open(path, "r") as handle:
|
|
return handle.read()
|
|
|
|
|
|
def extract_value(content, key):
|
|
# Match a quoted value assigned to the key, e.g. flannel_iface: "...".
|
|
match = re.search(r"^%s:\s*\"(.+)\"\s*$" % re.escape(key), content, re.M)
|
|
if not match:
|
|
fail("could not find %s in the sample inventory" % key)
|
|
return match.group(1)
|
|
|
|
|
|
def resolve(env, expression, facts):
|
|
template = env.from_string(expression)
|
|
return template.render(ansible_facts=facts)
|
|
|
|
|
|
def main():
|
|
root = repo_root()
|
|
content = read_all_yml(root)
|
|
env = Environment(undefined=StrictUndefined)
|
|
|
|
flannel_expr = extract_value(content, "flannel_iface")
|
|
if "default_ipv4.interface" not in flannel_expr:
|
|
fail("flannel_iface no longer defaults from ansible facts")
|
|
|
|
# A host whose primary interface is enp1s0 (the core #621 scenario).
|
|
facts = FakeAnsibleFacts("enp1s0", "192.168.30.11")
|
|
resolved = resolve(env, flannel_expr, facts)
|
|
if resolved != "enp1s0":
|
|
fail("flannel_iface resolved to %r, expected enp1s0" % resolved)
|
|
|
|
# A different host with a different interface must resolve independently.
|
|
facts2 = FakeAnsibleFacts("ens3", "192.168.30.12")
|
|
resolved2 = resolve(env, flannel_expr, facts2)
|
|
if resolved2 != "ens3":
|
|
fail("flannel_iface resolved to %r, expected ens3" % resolved2)
|
|
|
|
print("default-interface regression test passed")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|