fix(integration): real wait_for_ip arp-fallback test + document substrate coverage gap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sjat 2026-06-19 22:41:11 +02:00
parent d1941c987e
commit d6e80990b2
3 changed files with 41 additions and 2 deletions

View file

@ -264,6 +264,18 @@ harness on ubongo and shaking it down against real KVM (spec/plan in docs/superp
and virsh domain names keep the original underscore). Sanitization rule: RFC-952 hostnames and virsh domain names keep the original underscore). Sanitization rule: RFC-952 hostnames
allow hyphens, not underscores. allow hyphens, not underscores.
- `[friction]` **Molecule Docker image can't `apt install` → roles with real package tasks
have no Molecule substrate coverage** (2026-06-19): the Docker Molecule image ships with
cleared apt-lists and no internet access, so any role whose core work is `apt install`
`base`, `docker_host`, `integration_test` — cannot cover its package/substrate tasks in
Molecule. Those tasks are validated only by `make test-integration` (ADR-025, real KVM).
The gap is systemic: it affects every role with non-trivial package or system-level setup.
→ systematization idea: provide a Molecule image or driver that can install packages (e.g.
a custom Docker image with pre-seeded apt-lists, or a `prepare.yml` that pre-installs
packages from a local cache), or an alternative driver (e.g. `molecule-libvirt` using the
same KVM harness), so substrate tasks get real Molecule unit coverage rather than relying
entirely on the integration harness.
--- ---
## Kaizen reviews — decisions ledger ## Kaizen reviews — decisions ledger

View file

@ -3,6 +3,13 @@
# (no internet; KVM unusable in a container). This converge exercises only the # (no internet; KVM unusable in a container). This converge exercises only the
# nftables drop-in rendering via tasks_from, which IS meaningful in a container. # nftables drop-in rendering via tasks_from, which IS meaningful in a container.
# The full role (packages/libvirt) is exercised by make test-integration. # The full role (packages/libvirt) is exercised by make test-integration.
#
# Coverage split:
# Docker Molecule (this file): nftables drop-in rendering only.
# make test-integration (ADR-025, real KVM): libvirt/KVM package install, cache
# dir creation, and end-to-end VM lifecycle — the role's substrate tasks.
# The Docker scenario intentionally covers only the firewall drop-in; substrate
# coverage lives in the real-KVM integration harness, not here.
- name: Converge - name: Converge
hosts: all hosts: all
become: true become: true

View file

@ -1,5 +1,6 @@
import importlib.util import importlib.util
import pathlib import pathlib
import types
import pytest import pytest
_PATH = pathlib.Path(__file__).resolve().parent.parent / "scripts" / "integration-vm.py" _PATH = pathlib.Path(__file__).resolve().parent.parent / "scripts" / "integration-vm.py"
@ -32,15 +33,34 @@ def test_parse_lease_ip_extracts_ipv4():
def test_parse_lease_ip_none_when_absent(): def test_parse_lease_ip_none_when_absent():
assert ivm.parse_lease_ip("no leases\n") is None assert ivm.parse_lease_ip("no leases\n") is None
def test_parse_lease_ip_arp_source(): def test_parse_lease_ip_format_is_source_agnostic():
# virsh domifaddr --source arp output format is identical to --source lease; # virsh domifaddr --source arp output format is identical to --source lease;
# this test proves parse_lease_ip handles it so the arp fallback in wait_for_ip works. # this test only proves the regex is format-agnostic (both sources produce the
# same table). The behavioral arp-fallback in wait_for_ip is covered by
# test_wait_for_ip_falls_back_to_arp below.
out = (" Name MAC address Protocol Address\n" out = (" Name MAC address Protocol Address\n"
"-------------------------------------------------------------------\n" "-------------------------------------------------------------------\n"
" vnet0 52:54:00:de:ad:be ipv4 192.168.150.73/24\n") " vnet0 52:54:00:de:ad:be ipv4 192.168.150.73/24\n")
assert ivm.parse_lease_ip(out) == "192.168.150.73" assert ivm.parse_lease_ip(out) == "192.168.150.73"
def test_wait_for_ip_falls_back_to_arp(monkeypatch):
# wait_for_ip polls virsh domifaddr with --source lease first, then --source arp.
# Simulate lease returning empty (no DHCP lease yet) and arp returning a real address.
arp_out = (" Name MAC address Protocol Address\n"
"-------------------------------------------------------------------\n"
" vnet0 52:54:00:aa:bb:cc ipv4 192.168.150.142/24\n")
def fake_sh(cmd, **kwargs):
if "arp" in cmd:
return types.SimpleNamespace(stdout=arp_out)
return types.SimpleNamespace(stdout="")
monkeypatch.setattr(ivm, "sh", fake_sh)
monkeypatch.setattr(ivm.time, "sleep", lambda _: None)
assert ivm.wait_for_ip("dummy") == "192.168.150.142"
def test_meta_data_has_instance_and_hostname(): def test_meta_data_has_instance_and_hostname():
md = ivm.render_meta_data("iid-askari-x", "boma-it-askari-x") md = ivm.render_meta_data("iid-askari-x", "boma-it-askari-x")
assert "instance-id: iid-askari-x" in md assert "instance-id: iid-askari-x" in md