Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
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"]
7 changes: 7 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -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):
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()
9 changes: 5 additions & 4 deletions test/test_build_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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")
Expand Down
6 changes: 2 additions & 4 deletions test/test_build_iso.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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")
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)
4 changes: 3 additions & 1 deletion test/vm.py → vmtest/vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading