diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4c47ee729..63b336d19 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -157,7 +157,7 @@ jobs: # podman needs (parts of) the environment but will break when # XDG_RUNTIME_DIR is set. # TODO: figure out what exactly podman needs - sudo -E XDG_RUNTIME_DIR= pytest-3 --basetemp=/mnt/var/tmp/bib-tests ${{ matrix.test_file }} + sudo -E XDG_RUNTIME_DIR= PYTHONPATH=. pytest-3 --basetemp=/mnt/var/tmp/bib-tests ${{ matrix.test_file }} - name: Diskspace (after) if: ${{ always() }} run: | diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..b3a25b485 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +# Note that this is pyproject file is here only for the vmtest utils. +# This should move out eventually to its own repo or a different place +# like "images". + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "vmtest" +version = "0.1.0" + +[tool.setuptools.packages.find] +include = ["vmtest"] diff --git a/test/conftest.py b/test/conftest.py index 4db68ad67..acdfb3937 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,6 +1,8 @@ import pytest +# pylint: disable=wrong-import-order from testcases import TestCase +from vmtest.util import get_free_port def pytest_addoption(parser): @@ -20,3 +22,8 @@ def pytest_make_parametrize_id(config, val): # pylint: disable=W0613 if isinstance(val, TestCase): return f"{val}" return None + + +@pytest.fixture(name="free_port") +def free_port_fixture(): + return get_free_port() diff --git a/test/test_build_disk.py b/test/test_build_disk.py index 5d3a67107..03f3b822b 100644 --- a/test/test_build_disk.py +++ b/test/test_build_disk.py @@ -18,7 +18,8 @@ import testutil from containerbuild import build_container_fixture # pylint: disable=unused-import from testcases import CLOUD_BOOT_IMAGE_TYPES, DISK_IMAGE_TYPES, gen_testcases -from vm import AWS, QEMU +import vmtest.util +from vmtest.vm import AWS_REGION, AWS, QEMU if not testutil.has_executable("podman"): pytest.skip("no podman, skipping integration tests that required podman", allow_module_level=True) @@ -113,7 +114,7 @@ def registry_conf_fixture(shared_tmpdir, request): {local_registry}: lookaside: file:///{sigstore_dir} """ - registry_port = testutil.get_free_port() + registry_port = vmtest.util.get_free_port() # We cannot use localhost as we need to access the registry from both # the host system and the bootc-image-builder container. default_ip = testutil.get_ip_from_default_route() @@ -410,7 +411,7 @@ def build_images(shared_tmpdir, build_container, request, force_aws_upload, gpg_ upload_args = [ f"--aws-ami-name=bootc-image-builder-test-{str(uuid.uuid4())}", - f"--aws-region={testutil.AWS_REGION}", + f"--aws-region={AWS_REGION}", "--aws-bucket=bootc-image-builder-ci", ] elif force_aws_upload: @@ -492,7 +493,7 @@ def build_images(shared_tmpdir, build_container, request, force_aws_upload, gpg_ metadata["ami_id"] = parse_ami_id_from_log(journal_output) def del_ami(): - testutil.deregister_ami(metadata["ami_id"]) + testutil.deregister_ami(metadata["ami_id"], AWS_REGION) request.addfinalizer(del_ami) journal_log_path.write_text(journal_output, encoding="utf8") diff --git a/test/test_build_iso.py b/test/test_build_iso.py index 639344907..7142bdca4 100644 --- a/test/test_build_iso.py +++ b/test/test_build_iso.py @@ -10,11 +10,8 @@ import pytest # local test utils import testutil -from containerbuild import build_container_fixture # pylint: disable=unused-import -from containerbuild import make_container +from containerbuild import build_container_fixture, make_container # pylint: disable=unused-import from testcases import gen_testcases -from vm import QEMU - from test_build_disk import ( assert_kernel_args, ImageBuildResult, @@ -25,6 +22,7 @@ registry_conf_fixture, shared_tmpdir_fixture, ) +from vmtest.vm import QEMU @pytest.mark.skipif(platform.system() != "Linux", reason="boot test only runs on linux right now") diff --git a/test/testutil.py b/test/testutil.py index e1700078b..096d8f661 100644 --- a/test/testutil.py +++ b/test/testutil.py @@ -2,15 +2,11 @@ import pathlib import platform import shutil -import socket import subprocess -import time import boto3 from botocore.exceptions import ClientError -AWS_REGION = "us-east-1" - def run_journalctl(*args): pre = [] @@ -35,28 +31,6 @@ def has_executable(name): return shutil.which(name) is not None -def get_free_port() -> int: - # this is racy but there is no race-free way to do better with the qemu CLI - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.bind(("localhost", 0)) - return s.getsockname()[1] - - -def wait_ssh_ready(address, port, sleep, max_wait_sec): - for _ in range(int(max_wait_sec / sleep)): - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.settimeout(sleep) - try: - s.connect((address, port)) - data = s.recv(256) - if b"OpenSSH" in data: - return - except (ConnectionRefusedError, ConnectionResetError, TimeoutError): - pass - time.sleep(sleep) - raise ConnectionRefusedError(f"cannot connect to port {port} after {max_wait_sec}s") - - def has_x86_64_v3_cpu(): # x86_64-v3 has multiple features, see # https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels @@ -95,8 +69,8 @@ def write_aws_creds(path): return True -def deregister_ami(ami_id): - ec2 = boto3.resource("ec2", region_name=AWS_REGION) +def deregister_ami(ami_id, aws_region): + ec2 = boto3.resource("ec2", region_name=aws_region) try: print(f"Deregistering image {ami_id}") ami = ec2.Image(ami_id) diff --git a/vmtest/__init__.py b/vmtest/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vmtest/util.py b/vmtest/util.py new file mode 100644 index 000000000..195f52134 --- /dev/null +++ b/vmtest/util.py @@ -0,0 +1,24 @@ +import socket +import time + + +def get_free_port() -> int: + # this is racy but there is no race-free way to do better with the qemu CLI + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(("localhost", 0)) + return s.getsockname()[1] + + +def wait_ssh_ready(address, port, sleep, max_wait_sec): + for _ in range(int(max_wait_sec / sleep)): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(sleep) + try: + s.connect((address, port)) + data = s.recv(256) + if b"OpenSSH" in data: + return + except (ConnectionRefusedError, ConnectionResetError, TimeoutError): + pass + time.sleep(sleep) + raise ConnectionRefusedError(f"cannot connect to port {port} after {max_wait_sec}s") diff --git a/test/testutil_test.py b/vmtest/util_test.py similarity index 62% rename from test/testutil_test.py rename to vmtest/util_test.py index a1b2f0d26..6d91720e1 100644 --- a/test/testutil_test.py +++ b/vmtest/util_test.py @@ -1,10 +1,11 @@ import contextlib -import platform +import shutil import subprocess from unittest.mock import call, patch import pytest -from testutil import get_free_port, has_executable, wait_ssh_ready + +from vmtest.util import get_free_port, wait_ssh_ready def test_get_free_port(): @@ -12,20 +13,17 @@ def test_get_free_port(): assert 1024 < port_nr < 65535 -@pytest.fixture(name="free_port") -def free_port_fixture(): - return get_free_port() - - @patch("time.sleep") -def test_wait_ssh_ready_sleeps_no_connection(mocked_sleep, free_port): +def test_wait_ssh_ready_sleeps_no_connection(mocked_sleep): + free_port = get_free_port() with pytest.raises(ConnectionRefusedError): wait_ssh_ready("localhost", free_port, sleep=0.1, max_wait_sec=0.35) assert mocked_sleep.call_args_list == [call(0.1), call(0.1), call(0.1)] -@pytest.mark.skipif(not has_executable("nc"), reason="needs nc") -def test_wait_ssh_ready_sleeps_wrong_reply(free_port): +@pytest.mark.skipif(not shutil.which("nc"), reason="needs nc") +def test_wait_ssh_ready_sleeps_wrong_reply(): + free_port = get_free_port() with contextlib.ExitStack() as cm: with subprocess.Popen( f"echo not-ssh | nc -vv -l -p {free_port}", @@ -47,12 +45,3 @@ def test_wait_ssh_ready_sleeps_wrong_reply(free_port): wait_ssh_ready("localhost", free_port, sleep=0.1, max_wait_sec=0.55) assert mocked_sleep.call_args_list == [ call(0.1), call(0.1), call(0.1), call(0.1), call(0.1)] - - -@pytest.mark.skipif(platform.system() == "Darwin", reason="hangs on macOS") -@pytest.mark.skipif(not has_executable("nc"), reason="needs nc") -def test_wait_ssh_ready_integration(free_port): - with contextlib.ExitStack() as cm: - with subprocess.Popen(f"echo OpenSSH | nc -l -p {free_port}", shell=True) as p: - cm.callback(p.kill) - wait_ssh_ready("localhost", free_port, sleep=0.1, max_wait_sec=10) diff --git a/test/vm.py b/vmtest/vm.py similarity index 99% rename from test/vm.py rename to vmtest/vm.py index 6157e3eb8..fee60c470 100644 --- a/test/vm.py +++ b/vmtest/vm.py @@ -12,7 +12,9 @@ import paramiko from botocore.exceptions import ClientError from paramiko.client import AutoAddPolicy, SSHClient -from testutil import AWS_REGION, get_free_port, wait_ssh_ready +from vmtest.util import get_free_port, wait_ssh_ready + +AWS_REGION = "us-east-1" class VM(abc.ABC):