]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
integration testing: using prebuilt containers instead of Dockerfile per test
authorVasek Sraier <git@vakabus.cz>
Wed, 24 Mar 2021 12:13:44 +0000 (13:13 +0100)
committerAleš Mrázek <ales.mrazek@nic.cz>
Fri, 8 Apr 2022 14:17:51 +0000 (16:17 +0200)
14 files changed:
manager/.dockerignore [new file with mode: 0644]
manager/containers/README.md [new file with mode: 0644]
manager/containers/debian/Containerfile [new file with mode: 0644]
manager/containers/dev/Containerfile [new file with mode: 0644]
manager/integration/runner.py
manager/integration/tests/basic_startup/Dockerfile [deleted file]
manager/integration/tests/basic_startup/knot-resolver.service
manager/integration/tests/basic_startup/test.toml [new file with mode: 0644]
manager/integration/tests/worker_count/Dockerfile [deleted file]
manager/integration/tests/worker_count/knot-resolver.service
manager/integration/tests/worker_count/test.toml [new file with mode: 0644]
manager/poetry.lock
manager/pyproject.toml
manager/scripts/build-containers [new file with mode: 0755]

diff --git a/manager/.dockerignore b/manager/.dockerignore
new file mode 100644 (file)
index 0000000..f67cf10
--- /dev/null
@@ -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 (file)
index 0000000..ed05415
--- /dev/null
@@ -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 (file)
index 0000000..f7c76f8
--- /dev/null
@@ -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 (file)
index 0000000..8f34922
--- /dev/null
@@ -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
index 7be4fd547a903b2890349f6f6b53d38f01092f9a..0fc3af24af54881e6e505aac32d34188461db68f 100644 (file)
@@ -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 (file)
index 4622dd2..0000000
+++ /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"]
index b09045cae7b357e93e5dbc24f69e4a185adf88b9..a0ebf5574a804bec08695b8a06397ae8e14c94f6 100644 (file)
@@ -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 (file)
index 0000000..7a316d7
--- /dev/null
@@ -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 (file)
index 993e811..0000000
+++ /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"]
index b09045cae7b357e93e5dbc24f69e4a185adf88b9..a0ebf5574a804bec08695b8a06397ae8e14c94f6 100644 (file)
@@ -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 (file)
index 0000000..bbc2fb5
--- /dev/null
@@ -0,0 +1,5 @@
+image = "knot-manager:debian"
+cmd = ["/test/run"]
+
+[mount]
+"/test" = "integration/tests/worker_count"
\ No newline at end of file
index 2ecf7da1b0e5a37a5e381a2c03c93878351c9331..92785059ca0d865dec96d4910eacfcbe415165a2 100644 (file)
@@ -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 = [
index 93ba25f579d84109174261694f37a14e53e153ae..dec95123eb7112ae2c36bcbb99bb3cd7514d46a6 100644 (file)
@@ -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 (executable)
index 0000000..4f4132a
--- /dev/null
@@ -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