From: Vasek Sraier Date: Wed, 24 Mar 2021 12:13:44 +0000 (+0100) Subject: integration testing: using prebuilt containers instead of Dockerfile per test X-Git-Tag: v6.0.0a1~197 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4ff600f783dcc50ea27f0a61c86860d5bc4b7470;p=thirdparty%2Fknot-resolver.git integration testing: using prebuilt containers instead of Dockerfile per test --- diff --git a/manager/.dockerignore b/manager/.dockerignore new file mode 100644 index 000000000..f67cf10c4 --- /dev/null +++ b/manager/.dockerignore @@ -0,0 +1,8 @@ +node_modules/ +.mypy_cache/ +.pytest_cache/ +.tox/ +.git/ +.vscode/ + +containers/ \ No newline at end of file diff --git a/manager/containers/README.md b/manager/containers/README.md new file mode 100644 index 000000000..ed0541591 --- /dev/null +++ b/manager/containers/README.md @@ -0,0 +1,7 @@ +# Container definition files + +All containers build configurations evolve from the `dev` container. If you want to change something, please do it mainly there. We consider **the `dev` container as a reference container** + +## Naming + +All containers defined here are named `knot-manager`. The directory name is the container's tag. \ No newline at end of file diff --git a/manager/containers/debian/Containerfile b/manager/containers/debian/Containerfile new file mode 100644 index 000000000..f7c76f872 --- /dev/null +++ b/manager/containers/debian/Containerfile @@ -0,0 +1,76 @@ +FROM docker.io/debian:latest + +ENV \ + # build: + BUILD_ONLY_PACKAGES='wget' \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 \ + # poetry: + POETRY_VERSION=1.1.5 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + PATH="$PATH:/root/.poetry/bin" \ + NODE_VERSION=node_14.x +ENV LC_ALL=C.UTF-8 + +# System deps: +# System deps: +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + bash \ + build-essential git ca-certificates \ + python3 python3-pip python3-dev python3-setuptools python3-wheel \ + libcairo2-dev libgirepository1.0-dev \ + gettext \ + systemd \ + curl \ + dbus \ + libcairo2-dev libgirepository1.0-dev \ + # Defining build-time-only dependencies: + $BUILD_ONLY_PACKAGES \ + # Install Knot Resolver + && wget https://secure.nic.cz/files/knot-resolver/knot-resolver-release.deb \ + && dpkg -i knot-resolver-release.deb \ + && rm knot-resolver-release.deb \ + && apt-get update && apt-get install -y --no-install-recommends knot-resolver \ + # Installing `poetry` package manager: + # https://github.com/python-poetry/poetry + && curl -sSL 'https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py' | python3 \ + && poetry --version \ + # Install test dependencies + && apt-get install --no-install-recommends procps curl \ + && pip3 install requests requests-unixsocket \ + # Removing build-time-only dependencies: + && apt-get remove -y $BUILD_ONLY_PACKAGES \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* + +# Copy only requirements, to cache them in docker layer +# no poetry.lock, because here we have a different python version +COPY ./pyproject.toml ./yarn.lock ./package.json /code/ + +WORKDIR /code + +# Install project dependencies +RUN poetry --version \ + # Aghghgh! The packaged pip is buggy and just plainly fails installing dependencies + # so here we update it + && python3 -m pip --version \ + && python3 -m pip install -U pip \ + && python3 -m pip --version \ + # and install the dependencies + && poetry install --no-dev --no-interaction --no-ansi + +# Copy the remaining code +COPY . /code + +CMD ["/bin/systemd"] \ No newline at end of file diff --git a/manager/containers/dev/Containerfile b/manager/containers/dev/Containerfile new file mode 100644 index 000000000..8f34922fd --- /dev/null +++ b/manager/containers/dev/Containerfile @@ -0,0 +1,81 @@ +# source: https://github.com/wemake-services/wemake-django-template/blob/master/%7B%7Bcookiecutter.project_name%7D%7D/docker/django/Dockerfile + +# This Dockerfile uses multi-stage build to customize DEV and PROD images: +# https://docs.docker.com/develop/develop-images/multistage-build/ + +FROM docker.io/python:3.6.13-slim-buster + +# Agh, this would ideally be an ARG command, but podman has problems caching the build +ENV KNOT_ENV=dev + +ENV \ + # build: + BUILD_ONLY_PACKAGES='wget lsb-release gnupg' \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 \ + # poetry: + POETRY_VERSION=1.1.5 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + PATH="$PATH:/root/.poetry/bin" \ + NODE_VERSION=node_14.x + + +# System deps: +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + bash \ + build-essential \ + curl \ + gettext \ + git \ + systemd \ + dbus \ + libcairo2-dev libgirepository1.0-dev \ + # Defining build-time-only dependencies: + $BUILD_ONLY_PACKAGES \ + # Install Knot Resolver + && wget https://secure.nic.cz/files/knot-resolver/knot-resolver-release.deb \ + && dpkg -i knot-resolver-release.deb \ + && rm knot-resolver-release.deb \ + && apt-get update && apt-get install -y --no-install-recommends knot-resolver \ + # Installing Yarn and NodeJS + && curl -sSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && echo "deb https://deb.nodesource.com/$NODE_VERSION $(lsb_release -s -c) main" | tee /etc/apt/sources.list.d/nodesource.list \ + && echo "deb-src https://deb.nodesource.com/$NODE_VERSION $(lsb_release -s -c) main" | tee -a /etc/apt/sources.list.d/nodesource.list \ + && apt-get update && apt-get install --no-install-recommends --no-install-suggests -y nodejs \ + && npm install -g yarn \ + # Installing `poetry` package manager: + # https://github.com/python-poetry/poetry + && curl -sSL 'https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py' | python \ + && poetry --version \ + # Removing build-time-only dependencies: + && apt-get remove -y $BUILD_ONLY_PACKAGES \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* + +# Copy only requirements, to cache them in docker layer +COPY ./poetry.lock ./pyproject.toml ./yarn.lock ./package.json /code/ + +WORKDIR /code + +# Install project dependencies +RUN echo "Running in $KNOT_ENV" \ + && poetry install \ + $(if [ "$KNOT_ENV" != 'dev' ]; then echo '--no-dev'; fi) \ + --no-interaction --no-ansi \ + && if test "$KNOT_ENV" = "dev"; then yarn install; fi + +# Copy the remaining code +COPY . /code + +CMD ["/bin/systemd"] \ No newline at end of file diff --git a/manager/integration/runner.py b/manager/integration/runner.py index 7be4fd547..0fc3af24a 100644 --- a/manager/integration/runner.py +++ b/manager/integration/runner.py @@ -11,6 +11,7 @@ import requests import hashlib import click import json +import toml from _hashlib import HASH as Hash from pathlib import Path, PurePath @@ -312,6 +313,42 @@ def _get_git_root() -> PurePath: return PurePath(str(result.stdout, encoding="utf8").strip()) +class Test: + _CONFIG_FILE = "test.toml" + + def __init__(self, path: Path): + with open(path / Test._CONFIG_FILE, 'r') as f: + config = toml.load(f) + + self._mounts = {} + gitroot: Path = _get_git_root() + for dst, src in config["mount"].items(): + # note that we flip the meaning around to match podman's api + src = gitroot / src + dst = gitroot / dst + self._mounts[src] = dst + + self.name = str(path.absolute().name) + self._cmd = [ str(x) for x in config["cmd"] ] + self._image = str(config["image"]) + + + def run(self, manager: PodmanServiceManager, inspect_failed=False): + print(f"Running test {Colors.YELLOW}{self.name}{Colors.RESET}") + print("\tRunning...") + exit_code = manager.start_temporary_and_wait( + self._image, + self._cmd, + bind_mount_ro=self._mounts, + inspect_failed=inspect_failed, + ) + if exit_code == 0: + print(f"\t{Colors.GREEN}Test succeeded{Colors.RESET}") + else: + print( + f"\t{Colors.RED}Test failed with exit code {exit_code}{Colors.RESET}" + ) + class TestRunner: _TEST_DIRECTORY = "tests" _TEST_ENTRYPOINT = ["/test/run"] @@ -342,34 +379,22 @@ class TestRunner: If no TESTS are specified, runs them all. """ + + # Temporary hack + # build all test containers + ret = subprocess.call("poe build-containers", shell=True) + assert ret == 0 + + # Run the tests with PodmanService() as manager: for test_path in TestRunner._list_tests(): - test_name = test_path.absolute().name + test = Test(test_path) - if len(tests) != 0 and test_name not in tests: - print(f"Skipping test {Colors.YELLOW}{test_name}{Colors.RESET}") + if len(tests) != 0 and test.name not in tests: + print(f"Skipping test {Colors.YELLOW}{test.name}{Colors.RESET}") continue - print(f"Running test {Colors.YELLOW}{test_name}{Colors.RESET}") - image = "knot_test_" + test_name - print("\tBuilding...") - manager.build_image(test_path, image) - print("\tRunning...") - exit_code = manager.start_temporary_and_wait( - image, - TestRunner._TEST_ENTRYPOINT, - bind_mount_ro={ - _get_git_root(): PurePath("/repo"), - test_path.absolute(): "/test", - }, - inspect_failed=inspect_failed, - ) - if exit_code == 0: - print(f"\t{Colors.GREEN}Test succeeded{Colors.RESET}") - else: - print( - f"\t{Colors.RED}Test failed with exit code {exit_code}{Colors.RESET}" - ) + test.run(manager) if __name__ == "__main__": diff --git a/manager/integration/tests/basic_startup/Dockerfile b/manager/integration/tests/basic_startup/Dockerfile deleted file mode 100644 index 4622dd23d..000000000 --- a/manager/integration/tests/basic_startup/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -FROM docker.io/debian:latest - -ENV LC_ALL=C.UTF-8 - -# install project dependencies - -## build essentials -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y build-essential git ca-certificates - -## python -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y python3 python3-pip python3-dev - -## glib dependencies -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y libcairo2-dev libglib2.0-0 libgirepository1.0-dev - -## python setuptools -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y python3-setuptools - -## python libraries -RUN pip3 install aiohttp strictyaml pydbus PyGObject jinja2 - -## systemd -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y systemd - -## kresd -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y wget -RUN wget https://secure.nic.cz/files/knot-resolver/knot-resolver-release.deb && dpkg -i knot-resolver-release.deb -RUN apt-get update && apt-get install -y knot-resolver - -# dbus -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y dbus - - -# install test dependencies -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y curl -RUN pip3 install requests requests-unixsocket - -CMD ["/bin/systemd"] diff --git a/manager/integration/tests/basic_startup/knot-resolver.service b/manager/integration/tests/basic_startup/knot-resolver.service index b09045cae..a0ebf5574 100644 --- a/manager/integration/tests/basic_startup/knot-resolver.service +++ b/manager/integration/tests/basic_startup/knot-resolver.service @@ -4,7 +4,7 @@ Requires=dbus After=dbus [Service] -WorkingDirectory=/repo +WorkingDirectory=/code ExecStart=/usr/bin/python3 -m knot_resolver_manager KillSignal=SIGINT diff --git a/manager/integration/tests/basic_startup/test.toml b/manager/integration/tests/basic_startup/test.toml new file mode 100644 index 000000000..7a316d7b2 --- /dev/null +++ b/manager/integration/tests/basic_startup/test.toml @@ -0,0 +1,5 @@ +image = "knot-manager:debian" +cmd = ["/test/run"] + +[mount] +"/test" = "integration/tests/basic_startup" \ No newline at end of file diff --git a/manager/integration/tests/worker_count/Dockerfile b/manager/integration/tests/worker_count/Dockerfile deleted file mode 100644 index 993e811bf..000000000 --- a/manager/integration/tests/worker_count/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -FROM docker.io/debian:latest - -ENV LC_ALL=C.UTF-8 - -# install project dependencies - -## build essentials -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y build-essential git ca-certificates - -## python -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y python3 python3-pip python3-dev - -## glib dependencies -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y libcairo2-dev libglib2.0-0 libgirepository1.0-dev - -## python setuptools -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y python3-setuptools - -## python libraries -RUN pip3 install aiohttp strictyaml pydbus PyGObject jinja2 - -## systemd -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y systemd - -## kresd -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y wget -RUN wget https://secure.nic.cz/files/knot-resolver/knot-resolver-release.deb && dpkg -i knot-resolver-release.deb -RUN apt-get update && apt-get install -y knot-resolver - -# dbus -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y dbus - - -# install test dependencies -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y curl procps -RUN pip3 install requests requests-unixsocket - -CMD ["/bin/systemd"] diff --git a/manager/integration/tests/worker_count/knot-resolver.service b/manager/integration/tests/worker_count/knot-resolver.service index b09045cae..a0ebf5574 100644 --- a/manager/integration/tests/worker_count/knot-resolver.service +++ b/manager/integration/tests/worker_count/knot-resolver.service @@ -4,7 +4,7 @@ Requires=dbus After=dbus [Service] -WorkingDirectory=/repo +WorkingDirectory=/code ExecStart=/usr/bin/python3 -m knot_resolver_manager KillSignal=SIGINT diff --git a/manager/integration/tests/worker_count/test.toml b/manager/integration/tests/worker_count/test.toml new file mode 100644 index 000000000..bbc2fb5bd --- /dev/null +++ b/manager/integration/tests/worker_count/test.toml @@ -0,0 +1,5 @@ +image = "knot-manager:debian" +cmd = ["/test/run"] + +[mount] +"/test" = "integration/tests/worker_count" \ No newline at end of file diff --git a/manager/poetry.lock b/manager/poetry.lock index 2ecf7da1b..92785059c 100644 --- a/manager/poetry.lock +++ b/manager/poetry.lock @@ -946,7 +946,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake [metadata] lock-version = "1.1" python-versions = "^3.6.12" -content-hash = "bc022eb5ed23d94fdafda9c9756ec70c61f4fe675889a9cb1dfaa31b0e85f84b" +content-hash = "939e224639141a5d45cc48343ffeb9f8daf50f4c856a2c43c0538ffa66423bc0" [metadata.files] aiohttp = [ diff --git a/manager/pyproject.toml b/manager/pyproject.toml index 93ba25f57..dec95123e 100644 --- a/manager/pyproject.toml +++ b/manager/pyproject.toml @@ -27,6 +27,7 @@ prospector = {extras = ["with_mypy", "with_bandit"], version = "^1.3.1"} requests = "^2.25.1" requests-unixsocket = "^0.2.0" click = "^7.1.2" +toml = "^0.10.2" [tool.poe.tasks] run = { cmd = "python -m knot_resolver_manager", help = "Run the manager" } @@ -35,6 +36,7 @@ check = { cmd = "scripts/codecheck", help = "Run static code analysis" } format = { cmd = "poetry run black knot_resolver_manager/ tests/", help = "Run 'Black' code formater" } fixdeps = { shell = "poetry install; yarn install", help = "Install/update dependencies according to configuration files"} commit = { shell = "scripts/commit", help = "Invoke every single check before commiting" } +build-containers = { shell = "scripts/build-containers", help = "Build all containers" } clean = """ rm -rf .coverage .mypy_cache diff --git a/manager/scripts/build-containers b/manager/scripts/build-containers new file mode 100755 index 000000000..4f4132a92 --- /dev/null +++ b/manager/scripts/build-containers @@ -0,0 +1,14 @@ +#!/bin/bash + +# fail early +set -e + +# ensure consistent behaviour +src_dir="$(dirname "$(realpath "$0")")" +source $src_dir/_env.sh + + +# build the actual containers +for tag in $(find containers -maxdepth 1 -type d -printf '%f\n' | grep -v containers); do + podman build -t "knot-manager:$tag" -f "containers/$tag/Containerfile" . +done \ No newline at end of file