From: Lukáš Ježek Date: Thu, 6 Feb 2020 15:12:11 +0000 (+0100) Subject: tests: pytest packaging tests for Debian 10 and Ubuntu Disco X-Git-Tag: v5.1.2~6^2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eeaac4deafadc309a692bea48aefecd96b345e41;p=thirdparty%2Fknot-resolver.git tests: pytest packaging tests for Debian 10 and Ubuntu Disco --- diff --git a/client/packaging/test.sh b/client/packaging/test.sh index 21fa304da..e1311c486 100755 --- a/client/packaging/test.sh +++ b/client/packaging/test.sh @@ -1,5 +1,5 @@ #!/bin/sh # SPDX-License-Identifier: GPL-3.0-or-later -test -e /usr/sbin/kresc -/usr/sbin/kresc # command will fail because of invalid parameters +test -e sbin/kresc +sbin/kresc # command will fail because of invalid parameters test "$?" -eq 1 # linker error would have different exit code diff --git a/daemon/packaging/debian/10/builddeps b/daemon/packaging/debian/10/builddeps index 4f96c4438..604993c10 100644 --- a/daemon/packaging/debian/10/builddeps +++ b/daemon/packaging/debian/10/builddeps @@ -10,7 +10,3 @@ libuv1-dev luajit pkg-config meson -doxygen -python3-breathe -python3-sphinx -python3-sphinx-rtd-theme diff --git a/daemon/packaging/debian/10/post-build.sh b/daemon/packaging/debian/10/post-build.sh deleted file mode 100755 index e69de29bb..000000000 diff --git a/daemon/packaging/debian/10/post-run.sh b/daemon/packaging/debian/10/post-run.sh deleted file mode 100755 index e69de29bb..000000000 diff --git a/daemon/packaging/debian/10/pre-build.sh b/daemon/packaging/debian/10/pre-build.sh index ec7993d6f..dc3b8019c 100755 --- a/daemon/packaging/debian/10/pre-build.sh +++ b/daemon/packaging/debian/10/pre-build.sh @@ -4,7 +4,7 @@ apt-get update apt-get install -y wget gnupg apt-utils echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/knot-resolver-build/Debian_10/ /' > /etc/apt/sources.list.d/home:CZ-NIC:knot-resolver-build.list -wget https://download.opensuse.org/repositories/home:CZ-NIC:knot-resolver-build/Debian_Next/Release.key -O Release.key +wget -nv https://download.opensuse.org/repositories/home:CZ-NIC:knot-resolver-build/Debian_10/Release.key -O Release.key apt-key add - < Release.key apt-get update diff --git a/daemon/packaging/debian/10/pre-run.sh b/daemon/packaging/debian/10/pre-run.sh index e69de29bb..01b4c5bf6 100755 --- a/daemon/packaging/debian/10/pre-run.sh +++ b/daemon/packaging/debian/10/pre-run.sh @@ -0,0 +1,7 @@ +apt-get update +apt-get install -y wget gnupg apt-utils + +wget https://secure.nic.cz/files/knot-resolver/knot-resolver-release.deb +dpkg -i knot-resolver-release.deb + +apt-get update diff --git a/doc/packaging/debian/10/build.sh b/doc/packaging/debian/10/build.sh index 3422d6846..e6084df48 100755 --- a/doc/packaging/debian/10/build.sh +++ b/doc/packaging/debian/10/build.sh @@ -1,3 +1,19 @@ #!/bin/sh # SPDX-License-Identifier: GPL-3.0-or-later -ninja -C build_packaging doc +[ -d /root/kresd/build_packaging ] && rm -rf /root/kresd/build_packaging/; +CFLAGS="$CFLAGS -Wall -pedantic -fno-omit-frame-pointer" +LDFLAGS="$LDFLAGS -Wl,--as-needed" +meson build_packaging \ + --buildtype=plain \ + --prefix=/root/kresd/install_packaging \ + --libdir=lib \ + --default-library=static \ + -Ddoc=enabled \ + -Dsystemd_files=enabled \ + -Dclient=enabled \ + -Dkeyfile_default=/usr/share/dns/root.key \ + -Droot_hints=/usr/share/dns/root.hints \ + -Dinstall_kresd_conf=enabled \ + -Dunit_tests=enabled \ + -Dc_args="${CFLAGS}" \ + -Dc_link_args="${LDFLAGS}"; diff --git a/doc/packaging/debian/10/install.sh b/doc/packaging/debian/10/install.sh new file mode 100755 index 000000000..3422d6846 --- /dev/null +++ b/doc/packaging/debian/10/install.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-3.0-or-later +ninja -C build_packaging doc diff --git a/doc/packaging/test.sh b/doc/packaging/test.sh index 491126ced..33bf175d1 100755 --- a/doc/packaging/test.sh +++ b/doc/packaging/test.sh @@ -1,3 +1,3 @@ #!/bin/sh # SPDX-License-Identifier: GPL-3.0-or-later -test -e doc/html/index.html +test -e ../doc/html/index.html diff --git a/modules/bogus_log/packaging/debian/10/rundeps b/modules/bogus_log/packaging/debian/10/rundeps deleted file mode 100644 index c557cb28e..000000000 --- a/modules/bogus_log/packaging/debian/10/rundeps +++ /dev/null @@ -1 +0,0 @@ -lua-http diff --git a/modules/etcd/packaging/debian/10/install.sh b/modules/etcd/packaging/debian/10/pre-test.sh similarity index 100% rename from modules/etcd/packaging/debian/10/install.sh rename to modules/etcd/packaging/debian/10/pre-test.sh diff --git a/modules/etcd/packaging/debian/10/builddeps b/modules/etcd/packaging/debian/10/rundeps similarity index 100% rename from modules/etcd/packaging/debian/10/builddeps rename to modules/etcd/packaging/debian/10/rundeps diff --git a/scripts/packaging/debian/10/rundeps b/scripts/packaging/debian/10/rundeps deleted file mode 100644 index c3cd0e335..000000000 --- a/scripts/packaging/debian/10/rundeps +++ /dev/null @@ -1,10 +0,0 @@ -bash -bsdmainutils -coreutils -gdb -git -luajit -pkg-config -sed -tar -xz-utils diff --git a/tests/packaging-doc.py b/tests/packaging-doc.py index 67a6caa2b..40c6db092 100644 --- a/tests/packaging-doc.py +++ b/tests/packaging-doc.py @@ -133,6 +133,10 @@ def main(): logging.debug('generating Dockerfile for %s', args) gen_dockerfile(args, tmpdir, srcdir) + from shutil import copyfile + copyfile(os.path.join(tmpdir, "Dockerfile"), + "/tmp/Dockerfile_" + args[0] + args[1] + "_" + args[-1].replace('/', '-')) + continue logging.info('testing combination %s', args) docker_build(tmpdir, delete=not baseimg) baseimg = False diff --git a/tests/packaging/conftest.py b/tests/packaging/conftest.py new file mode 100644 index 000000000..7279c15cb --- /dev/null +++ b/tests/packaging/conftest.py @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +import pytest +import os + + +def pytest_configure(): + pytest.KR_PYTESTS_DIR = os.path.dirname(os.path.realpath(__file__)) + pytest.KR_ROOT_DIR = os.path.join(pytest.KR_PYTESTS_DIR, "..", "..") + pytest.KR_PREFIX = "kr-packaging-tests-" diff --git a/tests/packaging/test_packaging.py b/tests/packaging/test_packaging.py new file mode 100644 index 000000000..32d4e7a4a --- /dev/null +++ b/tests/packaging/test_packaging.py @@ -0,0 +1,396 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +import os +import pytest +import docker +import logging +from pathlib import Path +from abc import ABC, abstractmethod + + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +EXCLUDED_TEST_DIRS = ['tests/unit/packaging', 'tests/packaging', + 'tests/integration/deckard/contrib/libfaketime/packaging'] +client = docker.from_env() + + +class DockerCmdError(Exception): + """ Raised when shell command in Docker container failed """ + pass + + +class ContainerHandler(): + def __init__(self, image): + self.img_id = image + self.container = None + + def run(self): + self.container = client.containers.run(self.img_id, network_mode='host', + tty=True, detach=True) + logger.info('Run container ID={}'.format(self.container)) + + def stop(self): + self.container.kill() + + def exec_cmd(self, cmd, workdir): + rcode, out = self.container.exec_run('/bin/sh -c \'' + cmd + '\'', workdir=workdir) + if rcode != 0: + raise DockerCmdError(rcode, out) + + def getFiles(self, output, path): + strm, stat = self.container.get_archive(path) + with open(output, 'wb') as ofile: + for data in strm: + ofile.write(data) + + +class DockerImages(ABC): + def __init__(self, version): + self.version = version + self.module = None + self.distro = None + self.build_id = None + self.run_id = None + + @abstractmethod + def cmd_kresd_install(self): + raise NotImplementedError + + @abstractmethod + def cmd_kresd_build(self): + raise NotImplementedError + + @abstractmethod + def cmd_pkgs_install(self): + raise NotImplementedError + + def readDependencies(self, deps_file): + """Read dependencies from file""" + listf = None + try: + with open(deps_file, 'r') as f: + listf = f.read().splitlines() + except FileNotFoundError: + pass + + return listf + + def __genDockerFile(self, path, from_image=None): + """Generate Dockerfile for build image""" + if self.module is None: + raise AttributeError + + if from_image is None: + from_image = '{0}:{1}'.format(self.distro, self.version) + + distro_dir = os.path.join(self.module, self.distro, self.version) + + dockerf = open(os.path.join(path, 'Dockerfile-build'), 'w') + + dockerf.write('FROM {}\n'.format(from_image)) + dockerf.write('WORKDIR /root/kresd\n') + if self.module == 'daemon/packaging': + dockerf.write('COPY . /root/kresd\n') + # when this file doesn't exists, tzdata needs user interaction + dockerf.write('RUN if [ ! -f /etc/localtime ];' + + 'then ln -fs /usr/share/zoneinfo/Europe/Prague /etc/localtime; fi\n') + if os.path.isfile(os.path.join(distro_dir, 'pre-build.sh')): + dockerf.write('RUN {}\n'.format(os.path.join(distro_dir, 'pre-build.sh'))) + if os.path.isfile(os.path.join(distro_dir, 'builddeps')): + dockerf.write('RUN {0} {1}\n'.format(self.cmd_pkgs_install(), + ' '.join(self.readDependencies(os.path.join(distro_dir, 'builddeps'))))) + if os.path.isfile(os.path.join(distro_dir, 'build.sh')): + dockerf.write('RUN {}\n'.format(os.path.join(distro_dir, 'build.sh'))) + else: + dockerf.write('RUN {}\n'.format(self.cmd_kresd_build())) + if os.path.isfile(os.path.join(distro_dir, 'install.sh')): + dockerf.write('RUN {}\n'.format(os.path.join(distro_dir, 'install.sh'))) + else: + dockerf.write('RUN {}\n'.format(self.cmd_kresd_install())) + if os.path.isfile(os.path.join(distro_dir, 'post-build.sh')): + dockerf.write('RUN {}\n'.format(os.path.join(distro_dir, 'post-build.sh'))) + + dockerf.close() + + def __genDockerFile_run(self, path, build_id, from_image=None): + """Generate Dockerfile for run image""" + if self.module is None: + raise AttributeError + + if from_image is None: + from_image = '{0}:{1}'.format(self.distro, self.version) + + distro_dir = os.path.join(self.module, self.distro, self.version) + + dockerf = open(os.path.join(path, 'Dockerfile-run'), 'w') + + dockerf.write('FROM {}\n'.format(from_image)) + dockerf.write('COPY --from={} /root/kresd /root/kresd\n'.format(build_id)) + dockerf.write('WORKDIR /root/kresd\n') + if os.path.isfile(os.path.join(distro_dir, 'pre-run.sh')): + dockerf.write('RUN {}\n'.format(os.path.join(distro_dir, 'pre-run.sh'))) + if os.path.isfile(os.path.join(distro_dir, 'rundeps')): + dockerf.write('RUN {0} {1}\n'.format(self.cmd_pkgs_install(), + ' '.join(self.readDependencies(os.path.join(distro_dir, 'rundeps'))))) + if os.path.isfile(os.path.join(distro_dir, 'pre-test.sh')): + dockerf.write('RUN {}\n'.format(os.path.join(distro_dir, 'pre-test.sh'))) + + dockerf.close() + + def build(self, tmpdir, tag="", from_image=None): + self.__genDockerFile(tmpdir, from_image=from_image) + + logger.debug('tmpdir={}'.format(tmpdir)) + logger.debug('datadir={}'.format(pytest.KR_ROOT_DIR)) + logger.debug('tag={}'.format(tag)) + image = client.images.build(path=str(pytest.KR_ROOT_DIR), + dockerfile=os.path.join(tmpdir, 'Dockerfile-build'), + network_mode='host', tag=tag, rm=True) + logger.info('"Build image" ID={} created'.format(image[0].short_id)) + self.build_id = image[0].short_id + return self.build_id + + def build_run(self, tmpdir, build_id, from_image=None, tag=""): + self.__genDockerFile_run(tmpdir, build_id, from_image=from_image) + + logger.debug('tmpdir={}'.format(tmpdir)) + logger.debug('datadir={}'.format(tmpdir)) + logger.debug('tag={}'.format(tag)) + image = client.images.build(path=str(tmpdir), + dockerfile=os.path.join(tmpdir, 'Dockerfile-run'), + network_mode='host', tag=tag, rm=True) + logger.info('"Run image" ID={} created'.format(image[0].short_id)) + self.run_id = image[0].short_id + return self.run_id + + +class DebianImage(DockerImages): + def __init__(self, version): + super().__init__(version) + self.distro = 'debian' + + def cmd_kresd_install(self): + # apt install + return 'ninja -C build_packaging install >/dev/null' + + def cmd_kresd_build(self): + return """\\ + [ -d /root/kresd/build_packaging ] && rm -rf /root/kresd/build_packaging/; \\ + CFLAGS=\"$CFLAGS -Wall -pedantic -fno-omit-frame-pointer\"; \\ + LDFLAGS=\"$LDFLAGS -Wl,--as-needed\"; \\ + meson build_packaging \\ + --buildtype=plain \\ + --prefix=/root/kresd/install_packaging \\ + --libdir=lib \\ + --default-library=static \\ + -Dsystemd_files=enabled \\ + -Dclient=enabled \\ + -Dkeyfile_default=/usr/share/dns/root.key \\ + -Droot_hints=/usr/share/dns/root.hints \\ + -Dinstall_kresd_conf=enabled \\ + -Dunit_tests=enabled \\ + -Dc_args=\"${CFLAGS}\" \\ + -Dc_link_args=\"${LDFLAGS}\"; + """ + + def cmd_pkgs_install(self): + return 'apt-get install -y ' + + +class UbuntuImage(DebianImage): + def __init__(self, version): + super().__init__(version) + self.distro = 'ubuntu' + + +class CentosImage(DockerImages): + def __init__(self, version): + super().__init__(version) + self.distro = 'centos' + + def cmd_kresd_install(self): + raise NotImplementedError + return "" + + def cmd_kresd_build(self): + raise NotImplementedError + return "" + + def cmd_pkgs_install(self): + raise NotImplementedError + return "" + + +def create_distro_image(name, version): + img = None + + if (name == 'debian'): + img = DebianImage(version) + elif (name == 'ubuntu'): + img = UbuntuImage(version) + elif (name == 'centos'): + img = CentosImage(version) + else: + img = None + + return img + + +def list_dirs(path, exclude=None): + """return all 'packaging' directories with full path""" + filtered_dirs = [] + + for rootpath, dirs, _ in os.walk(path): + + if (os.path.basename(rootpath) == 'packaging'): + fdir = os.path.relpath(rootpath, path) + if exclude is not None: + if fdir not in exclude: + filtered_dirs.append(fdir) + else: + filtered_dirs.append(fdir) + + return filtered_dirs + + +def list_tests_dirs(): + """return all 'packaging' directories without directories included in EXCLUDED_TEST_DIRS""" + return list_dirs(pytest.KR_ROOT_DIR, EXCLUDED_TEST_DIRS) + + +def list_distro_vers(distro_root): + """ + return list of { 'name': distro_name, 'version': distro_version) + pairs found in distro_root + """ + # transform list of paths like TOP/debian/10 into (debian, 10) + dist_ver = [{'name': p.parts[-2], 'version': p.parts[-1]} for p + in Path(distro_root).glob('*/*') if p.is_dir()] + + return list(dist_ver) + + +MODULES = list_tests_dirs() +DISTROS = list_distro_vers(os.path.join(pytest.KR_ROOT_DIR, 'daemon/packaging')) +DISTROS_NAMES = ['{0}_{1}'.format(distro['name'], distro['version']) for distro in DISTROS] + + +@pytest.fixture(scope='session', params=DISTROS, ids=DISTROS_NAMES) +def buildenv(request, tmpdir_factory): + distro = request.param + + logger.debug('Creating main images for "{0} {1}"'.format(distro['name'], distro['version'])) + img = create_distro_image(distro['name'], distro['version']) + if img is None: + logger.warning('Unknown distro {}'.format(distro['name'])) + else: + img.module = 'daemon/packaging' + tmpdir = tmpdir_factory.mktemp(distro['name']+distro['version']) + img.build(tmpdir, tag=pytest.KR_PREFIX+distro['name']+distro['version']+'-build') + img.build_run(tmpdir, img.build_id, + tag=pytest.KR_PREFIX+distro['name']+distro['version']+'-run', + from_image=img.build_id) + + yield img + client.images.remove(img.run_id) + client.images.remove(img.build_id) + + +@pytest.mark.parametrize('module', MODULES) +def test_collect(module, buildenv, tmp_path): + logger.info(' ### Run test {} ###'.format(module)) + + if buildenv is None: + logger.error('Distro "{0} {1}" isn\'t implemented'.format(buildenv.distro, + buildenv.version)) + assert False + + rcode = None + buildmod = None + module_dir = os.path.join(pytest.KR_ROOT_DIR, module) + distro_dir = os.path.join(module_dir, buildenv.distro, buildenv.version) + + try: + if module == 'daemon/packaging': + # use main "run image" without changes + logging.info('Use main "run image"') + ch = ContainerHandler(buildenv.run_id) + ch.run() + elif buildenv is not None: + if os.path.isfile(os.path.join(distro_dir, 'pre-build.sh')) \ + or os.path.isfile(os.path.join(distro_dir, 'builddeps')): + # create module specific "build image" + logger.info('Create new "build image"') + buildmod = create_distro_image(buildenv.distro, buildenv.version) + buildmod.module = module + buildmod.build(tmp_path, from_image=buildenv.build_id, + tag=pytest.KR_PREFIX+buildmod.distro+buildmod.version+'-' + + module+'-build') + + if buildmod is not None: + # new build image was made, create new module specific "run image" + logger.info('Create module specific "run image" from Dockerfile') + buildmod.build_run(tmp_path, buildmod.build_id, + tag=pytest.KR_PREFIX+buildmod.distro+buildmod.version+'-' + + module+'-run', from_image=buildenv.run_id) + ch = ContainerHandler(buildmod.run_id) + ch.run() + elif os.path.isfile(os.path.join(distro_dir, 'pre-run.sh')) \ + or os.path.isfile(os.path.join(distro_dir, 'rundeps')): + # use main "run image" and apply module specific changes + logger.info('Apply module specific changes to "run image"') + buildmod = buildenv + ch = ContainerHandler(buildmod.run_id) + ch.run() + + if os.path.isfile(os.path.join(distro_dir, 'pre-run.sh')): + ch.exec_cmd(os.path.join(module, buildenv.distro, buildenv.version, + 'pre-run.sh'), '/root/kresd/') + + if os.path.isfile(os.path.join(distro_dir, 'rundeps')): + ch.exec_cmd(buildenv.cmd_pkgs_install() + ' '.join( + buildmod.readDependencies(os.path.join(distro_dir, 'rundeps'))), + '/root/kresd/') + + if os.path.isfile(os.path.join(distro_dir, 'pre-test.sh')): + ch.exec_cmd(os.path.join(module, buildenv.distro, buildenv.version, + 'pre-test.sh'), '/root/kresd/') + else: + # use main "run image" without changes + logging.info('Use main "run image"') + ch = ContainerHandler(buildenv.run_id) + ch.run() + + # run test + if os.path.isfile(os.path.join(module_dir, 'test.config')): + ch.exec_cmd('/root/kresd/install_packaging/sbin/kresd -n -c ' + os.path.join('..', + module, 'test.config'), '/root/kresd/install_packaging') + elif os.path.isfile(os.path.join(module_dir, 'test.sh')): + ch.exec_cmd('/bin/sh -c ' + os.path.join('..', module, 'test.sh'), + '/root/kresd/install_packaging/') + else: + ch.stop() + ch.container.remove() + logger.error('Test file (test.config or test.sh) not found') + assert False + + rcode = 0 + + if os.path.isfile(os.path.join(distro_dir, 'post-run.sh')): + ch.exec_cmd(os.path.join(module, buildenv.distro, buildenv.version, 'post-run.sh'), + '/root/kresd/') + + except DockerCmdError as err: + rcode, out = err.args + logger.debug('rcode: {}'.format(rcode)) + logger.error(out.decode('utf-8')) + finally: + ch.stop() + ch.container.remove() + if buildmod is not None and buildmod is not buildenv: + client.images.remove(buildmod.run_id) + client.images.remove(buildmod.build_id) + + assert(rcode == 0)