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:
parent
d1941c987e
commit
d6e80990b2
3 changed files with 41 additions and 2 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue