summaryrefslogtreecommitdiffstats
path: root/tests/test_k3s_runtime.py
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-04-07 17:32:59 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-04-07 17:32:59 +0000
commit31baaea4ab87aa2a42579b526b5c167796e60097 (patch)
tree5cdaad61bf9f720fe1930389e056727c2ea8e20b /tests/test_k3s_runtime.py
parent6b4415876f617b5afe17d1525a555a120df78bc0 (diff)
downloadmeta-virtualization-31baaea4ab87aa2a42579b526b5c167796e60097.tar.gz
tests: update k3s multi-node to use kernel cmdline role setup
Update the multi-node test fixture to use kernel cmdline parameters (k3s.role, k3s.node-ip, k3s.node-name) instead of manual IP configuration and k3s restart. The k3s-role-setup.service handles networking and role switching automatically on boot. - Pass kernel_append to K3sRunner for k3s.role and k3s.node-ip - Remove manual ip-addr-add and k3s stop/restart from fixture - Use k3s-get-token helper to extract join token on server - Agent starts k3s agent manually with extracted token (token not known at boot time) - Remove _QEMU_ARCH_CONFIG dict (moved to run-qemu-vm.sh script) All 10 tests pass: 5 single-node + 5 multi-node. Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'tests/test_k3s_runtime.py')
-rw-r--r--tests/test_k3s_runtime.py84
1 files changed, 31 insertions, 53 deletions
diff --git a/tests/test_k3s_runtime.py b/tests/test_k3s_runtime.py
index 1e5460a4..aa0d443f 100644
--- a/tests/test_k3s_runtime.py
+++ b/tests/test_k3s_runtime.py
@@ -75,7 +75,8 @@ class K3sRunner:
75 def __init__(self, poky_dir, build_dir, machine, use_kvm=True, 75 def __init__(self, poky_dir, build_dir, machine, use_kvm=True,
76 timeout=120, image="container-image-host", 76 timeout=120, image="container-image-host",
77 extra_qemu_params="", log_suffix="", 77 extra_qemu_params="", log_suffix="",
78 use_runqemu=True, rootfs_path=None): 78 use_runqemu=True, rootfs_path=None,
79 kernel_append=""):
79 self.poky_dir = Path(poky_dir) 80 self.poky_dir = Path(poky_dir)
80 self.build_dir = Path(build_dir) 81 self.build_dir = Path(build_dir)
81 self.machine = machine 82 self.machine = machine
@@ -86,6 +87,7 @@ class K3sRunner:
86 self.log_suffix = log_suffix 87 self.log_suffix = log_suffix
87 self.use_runqemu = use_runqemu 88 self.use_runqemu = use_runqemu
88 self.rootfs_path = rootfs_path 89 self.rootfs_path = rootfs_path
90 self.kernel_append = kernel_append
89 self.child = None 91 self.child = None
90 self.booted = False 92 self.booted = False
91 self._rootfs_copy = None 93 self._rootfs_copy = None
@@ -121,6 +123,9 @@ class K3sRunner:
121 if port: 123 if port:
122 cmd += f" --role agent --socket-port {port.group(1)}" 124 cmd += f" --role agent --socket-port {port.group(1)}"
123 125
126 if self.kernel_append:
127 cmd += f' --append "{self.kernel_append}"'
128
124 return cmd 129 return cmd
125 130
126 def start(self): 131 def start(self):
@@ -395,6 +400,7 @@ def k3s_multinode(request, poky_dir, build_dir, machine):
395 extra_qemu_params=server_params, 400 extra_qemu_params=server_params,
396 use_runqemu=False, 401 use_runqemu=False,
397 rootfs_path=rootfs_orig, 402 rootfs_path=rootfs_orig,
403 kernel_append="k3s.role=server k3s.node-ip=192.168.50.1",
398 log_suffix="-server") 404 log_suffix="-server")
399 405
400 # Agent VM: socket connect on second NIC, uses rootfs copy 406 # Agent VM: socket connect on second NIC, uses rootfs copy
@@ -407,6 +413,7 @@ def k3s_multinode(request, poky_dir, build_dir, machine):
407 extra_qemu_params=agent_params, 413 extra_qemu_params=agent_params,
408 use_runqemu=False, 414 use_runqemu=False,
409 rootfs_path=rootfs_agent, 415 rootfs_path=rootfs_agent,
416 kernel_append="k3s.role=agent k3s.node-ip=192.168.50.2 k3s.node-name=k3s-agent",
410 log_suffix="-agent") 417 log_suffix="-agent")
411 agent._rootfs_copy = str(rootfs_agent) 418 agent._rootfs_copy = str(rootfs_agent)
412 419
@@ -415,13 +422,9 @@ def k3s_multinode(request, poky_dir, build_dir, machine):
415 server.start() 422 server.start()
416 agent.start() 423 agent.start()
417 424
418 # Configure static IPs on eth1 (the socket NIC) 425 # Wait for networkd to configure IPs from kernel cmdline
419 server.run_command('ip addr add 192.168.50.1/24 dev eth1') 426 # (k3s-role-setup.service writes networkd drop-ins)
420 server.run_command('ip link set eth1 up') 427 time.sleep(5)
421 agent.run_command('ip addr add 192.168.50.2/24 dev eth1')
422 agent.run_command('ip link set eth1 up')
423 # Brief pause for link to come up
424 time.sleep(2)
425 428
426 yield {"server": server, "agent": agent} 429 yield {"server": server, "agent": agent}
427 430
@@ -566,38 +569,15 @@ class TestK3sMultiNode:
566 f"Agent cannot ping server:\n{output}" 569 f"Agent cannot ping server:\n{output}"
567 570
568 def test_k3s_agent_join(self, k3s_multinode, k3s_timeout): 571 def test_k3s_agent_join(self, k3s_multinode, k3s_timeout):
569 """Start k3s server on VM1, join agent VM2.""" 572 """Wait for k3s server Ready, extract token, start agent."""
570 server = k3s_multinode["server"] 573 server = k3s_multinode["server"]
571 agent = k3s_multinode["agent"] 574 agent = k3s_multinode["agent"]
572 575
573 # Stop default k3s service and wipe TLS state so the server 576 # The server VM booted with k3s.role=server and k3s.node-ip=192.168.50.1
574 # generates new certs that include the cluster IP (192.168.50.1). 577 # on the kernel cmdline. k3s-role-setup.service configured networking
575 # Without this, certs are only valid for the DHCP IP (10.0.2.15). 578 # and k3s.service auto-started. Wait for it to become Ready.
576 server.run_command('systemctl stop k3s 2>/dev/null')
577 server.run_command(
578 'rm -rf /var/lib/rancher/k3s/server/tls '
579 '/var/lib/rancher/k3s/server/cred '
580 '/var/lib/rancher/k3s/server/token '
581 '/var/lib/rancher/k3s/server/agent-token '
582 '/var/lib/rancher/k3s/server/node-token '
583 '/var/lib/rancher/k3s/server/db '
584 '/etc/rancher/k3s/k3s.yaml')
585 server.run_command(
586 'export PATH=$PATH:/opt/cni/bin:/usr/libexec/cni && '
587 'k3s server '
588 '--write-kubeconfig-mode 644 '
589 '--disable-cloud-controller '
590 '--node-ip 192.168.50.1 '
591 '--bind-address 192.168.50.1 '
592 '--advertise-address 192.168.50.1 '
593 '--tls-san 192.168.50.1 '
594 '--flannel-iface eth1 '
595 '&>/var/log/k3s-server.log &')
596
597 # Wait for new kubeconfig to be written, then wait for Ready
598 try: 579 try:
599 server.wait_for_condition( 580 server.wait_for_condition(
600 'test -f /etc/rancher/k3s/k3s.yaml && '
601 f'{_KUBECTL} get nodes 2>/dev/null || echo WAITING', 581 f'{_KUBECTL} get nodes 2>/dev/null || echo WAITING',
602 r'\bReady\b', 582 r'\bReady\b',
603 timeout=k3s_timeout, 583 timeout=k3s_timeout,
@@ -605,32 +585,30 @@ class TestK3sMultiNode:
605 description="k3s server node Ready") 585 description="k3s server node Ready")
606 except TimeoutError: 586 except TimeoutError:
607 logs = server.run_command( 587 logs = server.run_command(
608 'tail -50 /var/log/k3s-server.log 2>/dev/null || ' 588 'journalctl -u k3s --no-pager -n 30 2>/dev/null || '
609 'echo "no logs"') 589 'echo "no logs"')
610 pytest.fail(f"Server not Ready:\n{logs}") 590 pytest.fail(f"Server not Ready:\n{logs}")
611 591
612 # Extract node token 592 # Extract node token
613 token = server.run_command( 593 token = server.run_command('k3s-get-token 2>&1')
614 'cat /var/lib/rancher/k3s/server/node-token 2>&1') 594 # Parse the actual token from the script output
615 assert token and 'No such file' not in token, \ 595 for line in token.splitlines():
596 line = line.strip()
597 if line.startswith('K10'):
598 token = line
599 break
600 assert token.startswith('K10'), \
616 f"Failed to get node token:\n{token}" 601 f"Failed to get node token:\n{token}"
617 token = token.strip().splitlines()[-1].strip() 602
618 603 # The agent VM booted with k3s.role=agent but without a token
619 # Stop default k3s on agent, clear server-only config, and 604 # (we didn't know it at launch time). Role-setup configured
620 # wipe agent state from the auto-started instance. 605 # networking and masked k3s.service. Start k3s-agent manually
621 # Set a unique node name — both VMs boot the same image and 606 # with the token from the server.
622 # have the same hostname, which causes k3s to treat the agent
623 # as the same node as the server.
624 agent.run_command('systemctl stop k3s 2>/dev/null')
625 agent.run_command(
626 'rm -f /etc/rancher/k3s/config.yaml && '
627 'rm -rf /var/lib/rancher/k3s/agent '
628 '/var/lib/rancher/k3s/server')
629 agent.run_command( 607 agent.run_command(
608 f'export K3S_URL=https://192.168.50.1:6443 && '
609 f'export K3S_TOKEN={token} && '
630 f'export PATH=$PATH:/opt/cni/bin:/usr/libexec/cni && ' 610 f'export PATH=$PATH:/opt/cni/bin:/usr/libexec/cni && '
631 f'k3s agent ' 611 f'k3s agent '
632 f'--server https://192.168.50.1:6443 '
633 f'--token {token} '
634 f'--node-name k3s-agent ' 612 f'--node-name k3s-agent '
635 f'--node-ip 192.168.50.2 ' 613 f'--node-ip 192.168.50.2 '
636 f'--flannel-iface eth1 ' 614 f'--flannel-iface eth1 '