Fedora Modularity je projekt, jenž je součástí Fedory, s cílem vytvoření modulárního linuxového operačního systému založeného na více verzích balíčků s různými cykly podpory. Fedora 26 poskytuje první verzi tohoto modulárního systému zvaného Fedora 26 Boltron Server.

Nicméně pokud vstoupíte do modulárního světa, a začnete vytvářet moduly, kontejnery a další artefakty - vaše další otázka bude, jak lze tyto artefakty otestovat. Modularity Testing Framework byl navržen právě pro tento účel [1].

Úvod

Modularity Testing Framework (MTF) byl vytvořen pro testování artefaktů jako jsou moduly, RPM založené repositáře, kontejnery a další typy artefaktů, které přicházejí převážně od Modularity týmu [2]. Úkolem MTF je psát testy jednoduše a také nezávisle na typech modulů, kontejnerů apod.

MTF sám o sobě je minimalistická knihovna založená na existujících testovacích frameworcích jako jsou avocado a behave. Umožňuje vývojářům také zautomatizovat testy modulů, kontejnerů a v co nejkratším čase je integrovat do CI. MTF přidává základní podporu a abstrakci pro různé testovaní modulů (RPM založené repositáře, docker obrazy, ISO obrazy a další) s těmito frameworky. Je vhodné a řekl bych i nutné poznamenat, že MTF nepotřebuje žádný CI framework. Je tudíž nezávislý na CI. Z níže uvedených příkladů zjistíme, že je tomu skutečně tak.

Instalace MTF

Modularity Testing Framework je dostupný v oficiálních Fedora repositářích. Instalaci MTF lze provést pomocí příkazu:

dnf install -y modularity-testing-framework

Existuje rovněž COPR repositář, jež obsahuje vývojovou verzi. Pro instalaci z COPR repositáře, spusťte tyto dva příkazy:

dnf copr enable phracek/Modularity-testing-framework 

dnf install -y modularity-testing-framework

MTF je těmito kroky nainstalován na našem systému a můžeme začít s vlastním psaním a testováním modulů a kontejnerů.

Napsání jednoduchého testu

Vytvoření adresářové struktury pro testování

Před samotným psaním testů je potřeba si připravit adresářovou strukturu pro testy. Prvně si vytvořme adresář tests, v adresáři modulu, kde bychom rádi napsali testy a řekněme, že to bude náš kořenový adresář pro modul. V tomto tests/ adresáři vytvořme soubor Makefile, který bude vypadat následovně:


MODULE_LINT=/usr/share/moduleframework/tools/modulelint/*.py

TESTS=*.py (try not to use “*.py”, but use the test files with names such as sanity1.py sanity2.py... separated by spaces)


CMD=python -m avocado run $(MODULE_LINT) $(TESTS)

#
all:

    mtf-generator # use it in case that tests are defined also in config.yaml file (described below)

    $(CMD)

V našem kořenovém adresáři vytvořme rovněž soubor Makefile, který přidá sekci test, jenž spustí naše testy. Například:

.PHONY: build run default

IMAGE_NAME = memcached

MODULEMDURL=file://memcached.yaml

default: run

build:
 docker build --tag=$(IMAGE_NAME) .

run: build
 docker run -d $(IMAGE_NAME)

test: build
 cd tests; MODULE=docker MODULEMD=$(MODULEMDURL) URL="docker=$(IMAGE_NAME)" make all
 cd tests; MODULE=rpm MODULEMD=$(MODULEMDURL) URL="https://kojipkgs.fedoraproject.org/compose/latest-Fedora-Modular-26/compose/Server/x86_64/os/" make all

V adresáři tests/, umístěme konfigurační soubor config.yaml pro testování modulu či kontejneru. Odkaz na minimální konfigurační soubor pro testy je zde minimal-config.yaml. Například:


document: modularity-testing
version: 1
name: memcached
modulemd-url: http://raw.githubusercontent.com/container-images/memcached/master/memcached.yaml
service:
    port: 11211
packages:
    rpms:
        - memcached
        - perl-Carp
testdependecies:
    rpms:
        - nc
module:
    docker:
        start: "docker run -it -e CACHE_SIZE=128 -p 11211:11211"
        labels:
            description: "memcached is a high-performance, distributed memory"
            io.k8s.description: "memcached is a high-performance, distributed memory"
        source: https://github.com/container-images/memcached.git
        container: docker.io/modularitycontainers/memcached
    rpm:
        start: /usr/bin/memcached -p 11211 &
        #stop: systemctl stop memcached
        #status: systemctl status memcached
        repo:

            - https://kojipkgs.fedoraproject.org/compose/latest-Fedora-Modular-26/compose/Server/x86_64/os/

test:
    processrunning:
        - 'ls  /proc/*/exe -alh | grep memcached'
testhost:
    selfcheck:
        - 'echo errr | nc localhost 11211'
        - 'echo set AAA 0 4 2 | nc localhost 11211'
        - 'echo get AAA | nc localhost 11211'
    selcheckError:
        - 'echo errr | nc localhost 11211 |grep ERROR'

Dále vytvoříme v adresáři tests/ python soubor simpleTest.py, který otestuje danou službu nebo aplikaci:


#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# This Modularity Testing Framework helps you to write tests for modules
# Copyright (C) 2017 Red Hat, Inc.

import socket 
from avocado import main
from avocado.core import exceptions
from moduleframework import module_framework


class SanityCheck1(module_framework.AvocadoTest):
    """
    :avocado: enable
    """

    def testSettingTestVariable(self):
        self.start()
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('localhost', self.getConfig()['service']['port']))
        s.sendall('set Test 0 100 4\r\n\n')

        s.sendall('get Test\r\n')
        # print data
        s.close()

    def testBinExistsInRootDir(self):
        self.start()
        self.run("ls / | grep bin")


if __name__ == '__main__':
    main()

Spouštění testů

Spouštění testů z kořenového adresáře

Pro spouštění testů z kořenového adresáře je potřebné zavolat:

# run tests from a module root directory
$ sudo make test

Výstup příkazu vypadá následovně:

sudo make test

docker build --tag=memcached .

Sending build context to Docker daemon 268.3 kB

Step 1 : FROM baseruntime/baseruntime:latest

 ---> 0cbcd55844e4

Step 2 : ENV NAME memcached ARCH x86_64

 ---> Using cache

 ---> 16edc6a5f7b6

Step 3 : LABEL MAINTAINER "Petr Hracek" <phracek@redhat.com>

 ---> Using cache

 ---> 693d322beab2

Step 4 : LABEL summary "High Performance, Distributed Memory Object Cache" name "$FGC/$NAME" version "0" release "1.$DISTTAG" architecture "$ARCH" com.redhat.component $NAME usage "docker run -p 11211:11211 f26/memcached" help "Runs memcached, which listens on port 11211. No dependencies. See Help File below for more details." description "memcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load." io.k8s.description "memcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load." io.k8s.diplay-name "Memcached 1.4 " io.openshift.expose-services "11211:memcached" io.openshift.tags "memcached"

 ---> Using cache

 ---> eea936c1ae23

Step 5 : COPY repos/* /etc/yum.repos.d/

 ---> Using cache

 ---> 920155da88d9

Step 6 : RUN microdnf --nodocs --enablerepo memcached install memcached &&  microdnf -y clean all

 ---> Using cache

 ---> c83e613f0806

Step 7 : ADD files /files

 ---> Using cache

 ---> 7ec5f42c0064

Step 8 : ADD help.md README.md /

 ---> Using cache

 ---> 34702988730f

Step 9 : EXPOSE 11211

 ---> Using cache

 ---> 577ef9f0d784

Step 10 : USER 1000

 ---> Using cache

 ---> 671ac91ec4e5

Step 11 : CMD /files/memcached.sh

 ---> Using cache

 ---> 9c933477acc1

Successfully built 9c933477acc1

cd tests; MODULE=docker MODULEMD=file://memcached.yaml URL="docker=memcached" make all

make[1]: Entering directory '/home/phracek/work/FedoraModules/memcached/tests'

generator

The 'generator' name is deprecated and will go away eventually, please use

'mtf-generator' instead!

Added test (runmethod: run): processrunning

Added test (runmethod: runHost): selfcheck

Added test (runmethod: runHost): selcheckError

python -m avocado run --filter-by-tags=-WIP /usr/share/moduleframework/tools/modulelint.py *.py

JOB ID  : 9ba3a3f9fd982ea087f4d4de6708b88cee15cbab

JOB LOG : /root/avocado/job-results/job-2017-06-14T16.25-9ba3a3f/job.log

 (01/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testDockerFromBaseruntime: PASS (1.52 s)

 (02/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testDockerRunMicrodnf: PASS (1.53 s)

 (03/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testArchitectureInEnvAndLabelExists: PASS (1.63 s)

 (04/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testNameInEnvAndLabelExists: PASS (1.61 s)

 (05/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testReleaseLabelExists: PASS (1.60 s)

 (06/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testVersionLabelExists: PASS (1.45 s)

 (07/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testComRedHatComponentLabelExists: PASS (1.64 s)

 (08/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testIok8sDescriptionExists: PASS (1.51 s)

 (09/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testIoOpenshiftExposeServicesExists: PASS (1.50 s)

 (10/20) /usr/share/moduleframework/tools/modulelint.py:DockerfileLinter.testIoOpenShiftTagsExists: PASS (1.53 s)

 (11/20) /usr/share/moduleframework/tools/modulelint.py:DockerLint.testBasic: PASS (13.75 s)

 (12/20) /usr/share/moduleframework/tools/modulelint.py:DockerLint.testContainerIsRunning: PASS (14.19 s)

 (13/20) /usr/share/moduleframework/tools/modulelint.py:DockerLint.testLabels: PASS (1.57 s)

 (14/20) /usr/share/moduleframework/tools/modulelint.py:ModuleLintPackagesCheck.test: PASS (14.03 s)

 (15/20) generated.py:GeneratedTestsConfig.test_processrunning: PASS (13.77 s)

 (16/20) generated.py:GeneratedTestsConfig.test_selfcheck: PASS (13.85 s)

 (17/20) generated.py:GeneratedTestsConfig.test_selcheckError: PASS (14.32 s)

 (18/20) sanity1.py:SanityCheck1.testSettingTestVariable: PASS (13.86 s)

 (19/20) sanity1.py:SanityCheck1.testBinExistsInRootDir: PASS (13.81 s)

 (20/20) sanity1.py:SanityCheck1.test3GccSkipped: ERROR (13.84 s)

RESULTS : PASS 19 | ERROR 1 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0

JOB TIME   : 144.85 s

JOB HTML   : /root/avocado/job-results/job-2017-06-14T16.25-9ba3a3f/html/results.html

Makefile:6: recipe for target 'all' failed

make[1]: *** [all] Error 1

Makefile:14: recipe for target 'test' failed

make: *** [test] Error 2

Spouštění testů z adresáře tests

Pokud chceme spustit testy přímo z adresáře tests/ , je nutné zavolat příkaz:

#run Python tests from the tests/ directory
sudo MODULE=docker avocado run ./*.py

Výstup vypadá následovně:

sudo MODULE=docker avocado run ./*.py

[sudo] password for phracek:

JOB ID  : 2a171b762d8ab2c610a89862a88c015588823d29

JOB LOG : /root/avocado/job-results/job-2017-06-14T16.43-2a171b7/job.log

 (1/6) ./generated.py:GeneratedTestsConfig.test_processrunning: PASS (24.79 s)

 (2/6) ./generated.py:GeneratedTestsConfig.test_selfcheck: PASS (18.18 s)

 (3/6) ./generated.py:GeneratedTestsConfig.test_selcheckError: ERROR (24.16 s)

 (4/6) ./sanity1.py:SanityCheck1.testSettingTestVariable: PASS (18.88 s)

 (5/6) ./sanity1.py:SanityCheck1.testBinExistsInRootDir: PASS (17.87 s)

 (6/6) ./sanity1.py:SanityCheck1.test3GccSkipped: ERROR (19.30 s)

RESULTS : PASS 4 | ERROR 2 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0

JOB TIME   : 124.19 s

JOB HTML   : /root/avocado/job-results/job-2017-06-14T16.43-2a171b7/html/results.html

MTF dokumentace

MTF je dokumentována a dostupná na readthedocs.io webové stránce.

Více informací o projektu jsou dostupné zde [5].

Linky

[1] https://github.com/fedora-modularity/meta-test-family

[2] https://docs.pagure.org/modularity/

[3] https://admin.fedoraproject.org/pkgdb/package/rpms/modularity-testing-framework/

[4] https://copr.fedorainfracloud.org/coprs/phracek/meta-test-family//

[5] http://meta-test-family.readthedocs.io/en/latest/