Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"]
Empty file added test/__init__.py
Empty file.
9 changes: 8 additions & 1 deletion test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from test.testcases import TestCase

import pytest

from testcases import TestCase
from vmtest.util import get_free_port


def pytest_addoption(parser):
Expand All @@ -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()
15 changes: 4 additions & 11 deletions test/test_build_cross.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import platform
from test.test_build_disk import ( # pylint: disable=unused-import
assert_disk_image_boots, build_container_fixture, gpg_conf_fixture,
image_type_fixture, registry_conf_fixture, shared_tmpdir_fixture)
from test.testcases import gen_testcases

import pytest

from testcases import gen_testcases

from test_build_disk import ( # pylint: disable=unused-import
assert_disk_image_boots,
build_container_fixture,
gpg_conf_fixture,
image_type_fixture,
registry_conf_fixture,
shared_tmpdir_fixture,
)


# This testcase is not part of "test_build_disk.py:test_image_boots"
# because it takes ~30min on the GH runners so moving it into a
Expand Down
24 changes: 14 additions & 10 deletions test/test_build_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@
import subprocess
import tempfile
import uuid
from contextlib import contextmanager, ExitStack
from typing import NamedTuple
from contextlib import ExitStack, contextmanager
from dataclasses import dataclass
# local test utils
from test import testutil
from typing import NamedTuple

import pytest
# local test utils
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, AWS_REGION, QEMU

from .containerbuild import \
build_container_fixture # pylint: disable=unused-import
from .testcases import CLOUD_BOOT_IMAGE_TYPES, DISK_IMAGE_TYPES, gen_testcases

if not testutil.has_executable("podman"):
pytest.skip("no podman, skipping integration tests that required podman", allow_module_level=True)
Expand Down Expand Up @@ -113,7 +117,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()
Expand Down Expand Up @@ -410,7 +414,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:
Expand Down Expand Up @@ -492,7 +496,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")
Expand Down
25 changes: 10 additions & 15 deletions test/test_build_iso.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,19 @@
import platform
import subprocess
from contextlib import ExitStack
# local test utils
from test import testutil
from test.test_build_disk import ( # pylint: disable=unused-import
ImageBuildResult, assert_kernel_args, gpg_conf_fixture, image_type_fixture,
registry_conf_fixture, shared_tmpdir_fixture)
from test.testcases import gen_testcases

import pytest
# local test utils
import testutil
from containerbuild import build_container_fixture # pylint: disable=unused-import
from testcases import gen_testcases
from vm import QEMU

from test_build_disk import (
assert_kernel_args,
ImageBuildResult,
)
from test_build_disk import ( # pylint: disable=unused-import
gpg_conf_fixture,
image_type_fixture,
registry_conf_fixture,
shared_tmpdir_fixture,
)
from vmtest.vm import QEMU

from .containerbuild import \
build_container_fixture # pylint: disable=unused-import


@pytest.mark.skipif(platform.system() != "Linux", reason="boot test only runs on linux right now")
Expand Down
9 changes: 4 additions & 5 deletions test/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
import platform
import subprocess
import textwrap
from test import testutil
from test.containerbuild import build_container_fixture as _
from test.containerbuild import make_container
from test.testcases import gen_testcases

import pytest

import testutil
from containerbuild import build_container_fixture as _
from containerbuild import make_container
from testcases import gen_testcases

if not testutil.has_executable("podman"):
pytest.skip("no podman, skipping integration tests that required podman", allow_module_level=True)
if not testutil.can_start_rootful_containers():
Expand Down
7 changes: 4 additions & 3 deletions test/test_opts.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import os
import platform
import subprocess
from test import testutil
# pylint: disable=unused-import
from test.containerbuild import (build_container_fixture,
build_fake_container_fixture)

import pytest
import testutil
# pylint: disable=unused-import
from containerbuild import build_container_fixture, build_fake_container_fixture


@pytest.fixture(name="container_storage", scope="session")
Expand Down
13 changes: 6 additions & 7 deletions test/test_progress.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import subprocess

import pytest

import testutil
# pylint: disable=unused-import,duplicate-code
from test_opts import container_storage_fixture
from containerbuild import (
from test import testutil
from test.containerbuild import ( # pylint: disable=unused-import
build_container_fixture,
build_erroring_container_fixture,
build_fake_container_fixture,
)
# pylint: disable=unused-import,duplicate-code
from test.test_opts import container_storage_fixture

import pytest


def test_progress_debug(tmp_path, build_fake_container):
Expand Down
30 changes: 2 additions & 28 deletions test/testutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Empty file added vmtest/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions vmtest/util.py
Original file line number Diff line number Diff line change
@@ -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")
27 changes: 8 additions & 19 deletions test/testutil_test.py → vmtest/util_test.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
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():
port_nr = 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}",
Expand All @@ -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)
5 changes: 4 additions & 1 deletion test/vm.py → vmtest/vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
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):
Expand Down
Loading