diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-06-15 14:13:10 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-06-15 14:13:10 +0000 |
| commit | 7acf8f44150841091fa7bcb2db46fe8e937916d7 (patch) | |
| tree | 4fcac4020872ea304f01a744bc8cfb47fbd4f36b /tests | |
| parent | cfa93b77c8ee3ec2e96861ce82e2f8a458689067 (diff) | |
| download | meta-virtualization-7acf8f44150841091fa7bcb2db46fe8e937916d7.tar.gz | |
tests: use CONTAINER_PROFILE and surface runqemu errors in lxc fixture
Two correctness improvements to the lxc runtime test fixture.
1. Switch the build from CONTAINER_IMAGE_HOST_EXTRA_INSTALL = "lxc"
to CONTAINER_PROFILE = "lxc".
The original fixture set CONTAINER_IMAGE_HOST_EXTRA_INSTALL to
force lxc into the rootfs without depending on a profile fragment,
because at the time no lxc profile existed. Now that
container-host-lxc.conf is in tree, the test should exercise the
same path real users take — which is the entire point of a
runtime test. Using the profile also pulls in lxc-networking and
lxc-templates automatically (via packagegroup-lxc), where the
original "just install lxc" approach silently left a half-broken
LXC the test couldn't usefully verify.
2. Capture the runqemu output to a file and surface it in the
pexpect.EOF failure message.
pexpect's default 100-character "before" buffer truncates
runqemu's actual error to the trailing fragment, which is rarely
enough to diagnose the failure. Boot failures during fixture
setup now print the last 60 lines of the runqemu log into the
pytest failure message and leave the full log at
/tmp/test_lxc_runtime-runqemu.log for follow-up. When the lxc
image still wasn't booting cleanly, this is what made the
"lxc-net.service could not be found" / bridge-missing root cause
visible without re-instrumenting the fixture.
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_lxc_runtime.py | 38 |
1 files changed, 29 insertions, 9 deletions
diff --git a/tests/test_lxc_runtime.py b/tests/test_lxc_runtime.py index 695cfeb7..d3a55188 100644 --- a/tests/test_lxc_runtime.py +++ b/tests/test_lxc_runtime.py | |||
| @@ -6,8 +6,9 @@ LXC runtime tests — boot container-image-host with lxc installed and | |||
| 6 | exercise the LXC command-line lifecycle (create, start, attach, stop, | 6 | exercise the LXC command-line lifecycle (create, start, attach, stop, |
| 7 | destroy). | 7 | destroy). |
| 8 | 8 | ||
| 9 | The tests build container-image-host with CONTAINER_IMAGE_HOST_EXTRA_INSTALL | 9 | The tests build container-image-host with CONTAINER_PROFILE=lxc, which |
| 10 | including lxc. No local.conf changes needed. | 10 | pulls in packagegroup-lxc (lxc + lxc-networking + lxc-templates). No |
| 11 | local.conf changes needed. | ||
| 11 | 12 | ||
| 12 | The download-template regression check (TestLxcDownloadTemplate) exists | 13 | The download-template regression check (TestLxcDownloadTemplate) exists |
| 13 | specifically to catch the class of bug reported on the meta-virt list | 14 | specifically to catch the class of bug reported on the meta-virt list |
| @@ -87,11 +88,13 @@ def _run_bitbake(build_dir, recipe, extra_vars=None, timeout=3600): | |||
| 87 | 88 | ||
| 88 | @pytest.fixture(scope="module") | 89 | @pytest.fixture(scope="module") |
| 89 | def lxc_image(request): | 90 | def lxc_image(request): |
| 90 | """Build container-image-host with lxc included. | 91 | """Build container-image-host with the lxc container profile. |
| 91 | 92 | ||
| 92 | Uses CONTAINER_IMAGE_HOST_EXTRA_INSTALL to add the lxc package without | 93 | CONTAINER_PROFILE=lxc resolves via conf/distro/include/meta-virt-container-lxc.inc |
| 93 | needing to touch local.conf or invent a dedicated container-host-lxc | 94 | to VIRTUAL-RUNTIME_container_engine=lxc, which container-image-host.bb |
| 94 | profile fragment. | 95 | treats as a signal to install packagegroup-lxc (lxc + lxc-networking |
| 96 | + lxc-templates). Same mechanism the incus / podman / k3s profiles | ||
| 97 | use, so the test exercises the same code path real users would take. | ||
| 95 | """ | 98 | """ |
| 96 | poky_dir = Path(request.config.getoption("--poky-dir")) | 99 | poky_dir = Path(request.config.getoption("--poky-dir")) |
| 97 | bd = request.config.getoption("--build-dir") | 100 | bd = request.config.getoption("--build-dir") |
| @@ -99,7 +102,7 @@ def lxc_image(request): | |||
| 99 | result = _run_bitbake( | 102 | result = _run_bitbake( |
| 100 | build_dir, "container-image-host", | 103 | build_dir, "container-image-host", |
| 101 | extra_vars={ | 104 | extra_vars={ |
| 102 | "CONTAINER_IMAGE_HOST_EXTRA_INSTALL": "lxc", | 105 | "CONTAINER_PROFILE": "lxc", |
| 103 | }, | 106 | }, |
| 104 | ) | 107 | ) |
| 105 | if result.returncode != 0: | 108 | if result.returncode != 0: |
| @@ -123,13 +126,30 @@ def lxc_qemu(request, lxc_image): | |||
| 123 | f"{kvm_opt} qemuparams=\"-m 4096\"" | 126 | f"{kvm_opt} qemuparams=\"-m 4096\"" |
| 124 | ) | 127 | ) |
| 125 | 128 | ||
| 129 | # Route runqemu's full output to /tmp so a boot failure leaves a | ||
| 130 | # diagnosable trail. pexpect's default 100-char-truncated "before" | ||
| 131 | # buffer hides the actual error from pytest's traceback otherwise. | ||
| 132 | log_path = Path(tempfile.gettempdir()) / "test_lxc_runtime-runqemu.log" | ||
| 133 | log_path.write_text("") # truncate from any prior run | ||
| 134 | log_fp = log_path.open("w") | ||
| 126 | child = pexpect.spawn( | 135 | child = pexpect.spawn( |
| 127 | f"bash -c 'cd {poky_dir} && source oe-init-build-env {builddir} " | 136 | f"bash -c 'cd {poky_dir} && source oe-init-build-env {builddir} " |
| 128 | f">/dev/null 2>&1 && {cmd}'", | 137 | f">/dev/null 2>&1 && {cmd}'", |
| 129 | timeout=boot_timeout, encoding="utf-8", logfile=None, | 138 | timeout=boot_timeout, encoding="utf-8", logfile=log_fp, |
| 130 | ) | 139 | ) |
| 131 | 140 | ||
| 132 | child.expect(r"login:", timeout=boot_timeout) | 141 | try: |
| 142 | child.expect(r"login:", timeout=boot_timeout) | ||
| 143 | except pexpect.EOF: | ||
| 144 | log_fp.flush() | ||
| 145 | log_fp.close() | ||
| 146 | # Drop the last 60 lines of the runqemu log into the failure | ||
| 147 | # message so the actual cause is visible without grepping. | ||
| 148 | tail = "\n".join(log_path.read_text().splitlines()[-60:]) | ||
| 149 | pytest.fail( | ||
| 150 | f"runqemu exited before login prompt. Full log at {log_path}.\n" | ||
| 151 | f"Tail:\n{tail}" | ||
| 152 | ) | ||
| 133 | child.sendline("root") | 153 | child.sendline("root") |
| 134 | child.expect(r"root@.*[:~#]", timeout=30) | 154 | child.expect(r"root@.*[:~#]", timeout=30) |
| 135 | 155 | ||
