From 824cba1c08f68167f9a53d48d1ee3400e68ea4cd Mon Sep 17 00:00:00 2001 From: Martin Sehnoutka Date: Mon, 8 Jul 2019 15:47:37 +0200 Subject: [PATCH] introduce testing script --- test/.gitignore | 1 + test/1-create-base.json | 24 ++++++++ test/2-configure-web-server.json | 40 ++++++++++++++ test/3-compose-qcow2.json | 15 +++++ test/Makefile | 8 +++ test/run-tests.py | 95 ++++++++++++++++++++++++++++++++ test/variables | 6 ++ 7 files changed, 189 insertions(+) create mode 100644 test/.gitignore create mode 100644 test/1-create-base.json create mode 100644 test/2-configure-web-server.json create mode 100644 test/3-compose-qcow2.json create mode 100644 test/Makefile create mode 100644 test/run-tests.py create mode 100644 test/variables diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..b511ae11 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +*.qcow2 diff --git a/test/1-create-base.json b/test/1-create-base.json new file mode 100644 index 00000000..eb19c34b --- /dev/null +++ b/test/1-create-base.json @@ -0,0 +1,24 @@ +{ + "name": "base", + "stages": [ + { + "name": "io.weldr.dnf", + "systemResourcesFromEtc": ["/etc/pki"], + "options": { + "releasever": "30", + "repos": { + "fedora": { + "name": "Fedora", + "metalink": "https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch", + "gpgkey": "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch" + } + }, + "packages": [ + "@Core", + "selinux-policy-targeted", + "grub2-pc" + ] + } + } + ] +} diff --git a/test/2-configure-web-server.json b/test/2-configure-web-server.json new file mode 100644 index 00000000..b26f4f3a --- /dev/null +++ b/test/2-configure-web-server.json @@ -0,0 +1,40 @@ +{ + "name": "base-qcow2", + "base": "1f663f817473ffa5b01241b17adbd71bc734962313f5d4eef230073c0ac5884e", + "stages": [ + { + "name": "io.weldr.script", + "options": { + "script": "echo root | passwd --stdin root; echo 'SELINUX=disabled' > /etc/selinux/config;" + } + }, + { + "name": "io.weldr.script", + "options": { + "script": "mkdir -p /var/web; echo 'hello, world!' > /var/web/index; echo -e \"[Unit]\\nDescription=Testing web server\\nAfter=network.target\\n\\n[Service]\\nType=simple\\nExecStart=python3 -m http.server 8888\\nWorkingDirectory=/var/web/\\n\\n[Install]\\nWantedBy=multi-user.target\" > /etc/systemd/system/web-server.service;" + } + }, + { + "name": "io.weldr.systemd", + "options": { + "enabled_services": [ + "NetworkManager", + "web-server" + ], + "disabled_services": [ + "firewalld" + ] + } + }, + { + "name": "io.weldr.grub2", + "systemResourcesFromEtc": [ + "/etc/grub.d" + ], + "options": { + "root_fs_uuid": "76a22bf4-f153-4541-b6c7-0332c0dfaeac", + "partition_table_id": "0xdeadbeef" + } + } + ] +} diff --git a/test/3-compose-qcow2.json b/test/3-compose-qcow2.json new file mode 100644 index 00000000..033d99d4 --- /dev/null +++ b/test/3-compose-qcow2.json @@ -0,0 +1,15 @@ +{ + "name": "base-qcow2", + "base": "552b5555bdf64c5e19bf4ca8a709da37fb3046678643a8f8499297b6dd95c7e7", + "assembler": { + "name": "io.weldr.qcow2", + "systemResourcesFromEtc": [ + "/etc/grub.d" + ], + "options": { + "filename": "base.qcow2", + "root_fs_uuid": "76a22bf4-f153-4541-b6c7-0332c0dfaeac", + "partition_table_id": "0xdeadbeef" + } + } +} diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 00000000..a7f928ec --- /dev/null +++ b/test/Makefile @@ -0,0 +1,8 @@ +base: + $(OSBUILD) --input $(BASE_INPUT) --output $(BASE_OUTPUT) 1-create-base.json + +web: + $(OSBUILD) --input $(BASE_OUTPUT) --output $(WEB_OUTPUT) 2-configure-web-server.json + +qcow2: + $(OSBUILD) --input $(WEB_OUTPUT) --output $(QCOW2_OUTPUT) 3-compose-qcow2.json \ No newline at end of file diff --git a/test/run-tests.py b/test/run-tests.py new file mode 100644 index 00000000..60d448b9 --- /dev/null +++ b/test/run-tests.py @@ -0,0 +1,95 @@ +import contextlib +import logging +import os +import subprocess +import sys +import tempfile +import time + +EXPECTED_TIME_TO_BOOT = 60 # seconds +RESET = "\033[0m" +BOLD = "\033[1m" +RED = "\033[31m" +OBJECTS = os.environ.get("OBJECTS", tempfile.mkdtemp(prefix="osbuild-")) +OUTPUT_DIR = os.environ.get("OUTPUT_DIR", tempfile.mkdtemp(prefix="osbuild-")) +OSBUILD = os.environ.get("OSBUILD", os.path.join(os.path.dirname(__file__), "..", "osbuild")) +IMAGE_PATH = os.environ.get("IMAGE_PATH", OUTPUT_DIR + "/base.qcow2") + + +logging.basicConfig(level=logging.getLevelName(os.environ.get("TESTS_LOGLEVEL", "INFO"))) + + +def run_osbuild(pipeline: str, check=True): + cmd = [OSBUILD, "--objects", OBJECTS, "-o", OUTPUT_DIR, pipeline] + logging.info(f"Running osbuild: {cmd}") + osbuild = subprocess.run(cmd, capture_output=True) + if osbuild.returncode != 0: + logging.error(f"{RED}osbuild failed!{RESET}") + print(f"{BOLD}STDERR{RESET}") + print(osbuild.stderr.decode()) + print(f"{BOLD}STDOUT{RESET}") + print(osbuild.stdout.decode()) + if check: + sys.exit(1) + + return osbuild.returncode + + +def rel_path(fname: str) -> str: + return os.path.join(os.path.dirname(__file__), fname) + + +def build_web_server_image(): + run_osbuild(rel_path("1-create-base.json")) + r = run_osbuild(rel_path("2-configure-web-server.json"), check=False) + # TODO: remove this workaround once the grub2 stage works on the first try :-) + if r != 0: + run_osbuild(rel_path("2-configure-web-server.json")) + run_osbuild(rel_path("3-compose-qcow2.json")) + + +def uname() -> str: + uname = subprocess.run(["uname"], capture_output=True) + return uname.stdout.decode("utf-8").strip() + + +@contextlib.contextmanager +def boot_image(path: str): + acceleration = ["-accel", "kvm:hvf:tcg"] + network = ["-net", "nic,model=rtl8139", "-net", "user,hostfwd=tcp::8888-:8888"] + cmd = ["qemu-system-x86_64", "-nographic", "-m", "1024", "-snapshot"] + acceleration + [path] + network + logging.info(f"Booting image: {cmd}") + vm = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + try: + time.sleep(EXPECTED_TIME_TO_BOOT) + yield None + finally: + vm.kill() + + +def test_web_server(): + cmd = ["curl", "-s", "http://127.0.0.1:8888/index"] + logging.info(f"Running curl: {cmd}") + curl = subprocess.run(cmd, capture_output=True) + logging.info(f"Curl returned: code={curl.returncode}, stdout={curl.stdout.decode()}, stderr={curl.stderr.decode()}") + assert curl.returncode == 0 + assert curl.stdout.decode("utf-8").strip() == "hello, world!" + + +if __name__ == '__main__': + if uname() == "Darwin": + logging.info("Running on macOS. Skipping image build.") + else: + logging.info("Building image") + build_web_server_image() + + logging.info("Running tests") + tests = [test_web_server] + with boot_image(IMAGE_PATH): + for test in tests: + try: + test() + print(f"{RESET}{BOLD}{test.__name__}: Success{RESET}") + except AssertionError as e: + print(f"{RESET}{BOLD}{test.__name__}: {RESET}{RED}Fail{RESET}") + print(e) diff --git a/test/variables b/test/variables new file mode 100644 index 00000000..6e43e90a --- /dev/null +++ b/test/variables @@ -0,0 +1,6 @@ +export OSBUILD=../osbuild +export BASE_INPUT=/osbuild/workdir +export BASE_OUTPUT=/osbuild/base-tree +export WEB_OUTPUT=/osbuild/web-tree +export QCOW2_OUTPUT=/osbuild/output +export IMAGE_PATH=/osbuild/output/base.qcow2