V minulém článku [1] jsem se zmínil, že pro testy “standalone” kontejnerů lze použít Meta-Test-Family, zkráceně MTF [2]. Bez testů bychom neměli vydávat kontejnery, neboť potřebujeme zaručit, že daná služba v kontejneru skutečně běží.
Další možností jak spouštět kontejnery lze pomocí OpenShiftu [3] a obzvláště testování kontejnerů v OpenShift prostředí.
Tento článek Vám popíše, jak otestovat, zda-li kontejner je schopný běžet v “orchestrovaném” prostředí, tedy v OpenShiftu.
Jen tak mimochodem, Fedora obsahuje nemalé množství kontejnerů[4].

Instalace MTF

Před samotným testování kontejnerů, je potřeba nainstalovat MTF. Za tímto účelem lze použít tři možné způsoby.
Instalace z Fedořích repositářů, jež obsahuje stabilní verzi, včetně EPELu příkazem:

sudo dnf install -y meta-test-family

Instalace z COPR repositáře, jež obsahuje vývojovou verzi a nedoporučujeme ji používat na produkční prostředí.

sudo dnf copr enable phracek/meta-test-family
sudo dnf install -y meta-test-family

Instalace přímo z GIT repositáře příkazem [5]

git clone git@github.com:fedora-modularity/meta-test-family.git
sudo python setup.py install

Nyní můžeme začít testovat kontejner v OpenShiftu.
Příprava testů pro OpenShift
Před samotnou přípravou prostředí pro OpenShift a spuštěním testů, je nutné nejprve vytvořit konfigurační soubor v YAML formátu a samotné testy.

Konfigurační soubor pro testy v OpenShiftu vypadá přibližně následovně

document: modularity-testing
version: 1
name: memcached
service:
    port: 11211
module:
    openshift:
        container: docker.io/modularitycontainers/memcached

Co v tomto YAML konfigurační souboru jednotlivé části znamenají:

service.port – port, který kontejner „exposuje“ do systemu.
module.openshift –  konfigurační část relevantní pouze pro testování kontejneru v   OpenShiftu
module.openshift.container – Reference na contejner, který  bude použit v OpenShiftu.

Příprava prostředí pro testování v OpenShiftu

Spouštění Vašich kontejnerů lokálně je extrémně jednoduché - pouze spusťte docker run. Ale to není to co chcete, pokud Vaše aplikace má běžet v produkčním prostředí - to je věc OpenShiftu. Abychom si byly jisti, jestli Vaše kontejnery správně fungují jako orchestrované, měli bychom je v takovém prostředí také otestovat. Je vhodné poznamenat, že standalone kontejnery jsou od orchestrovaných kontejnerů výrazně odlišné.
Jaký je vlastně rozdíl mezi standalone kontejnery a orchestrovanými?
Standalone kontejnerymlze spustit jednoduše jednoduchým příkazem. Zpravování takových kontejnerů jení jednoduché, musíte vyřešit uložiště, bakupy, updaty, škálování - všechny tyto věci jsou dostupné v orchestrovaném řešení.

OpenShift prostředí má bezpečnostní omezení, odlišnosti v uložištích, očekává že Openshift PODy bude tzv. “stateless”, bude podporovat aktualizace,nativní podporu source-to-image a mnohem více. Nasazení orchestrátoru není snadný úkol. To je důvod, proč jsme se rozhodli přidat podporu pro OpenShift do MTF, abyste mohli snadno otestovat vaši kontejnerovou aplikaci v organizovaném prostředí. V tomto článku Vám ukážu, jak lze toto provést.

Pokud nemáme na svém počítači či notebooku OpenShift, můžeme prostředí připravit pomocí MTF.

MTF poskytuje příkaz mtf-env-setup.

$ sudo MODULE=openshift OPENSHIFT_LOCAL=yes mtf-env-set
Setting environment for module: openshift
Preparing environment ...
Loaded config for name: memcached
Starting OpenShift
Starting OpenShift using openshift/origin:v3.6.0 ...
OpenShift server started.

The server is accessible via web console at:
https://127.0.0.1:8443

You are logged in as:
User: developer
Password: 

To login as administrator:
oc login -u system:admin

Co příkaz ve skutečnosti provádí? V případě, že je zadán parameter OPENSHIFT_LOCAL, pak zkontroluje, zda-li jsou v prostředí nainstalovány balíčky origin a origin-clients a pakliže ne, dané balíčky nainstaluje. Test kontejneru v OpenShiftu bude prováděn v tomto případě na lokální na počítači. Součástí toho kroku je i nastartování OpenShiftu příkazem oc cluster up.
Pro případ, že bychom rádi otestovali kontejner na vzdálené instanci OpenShiftu, tento krok odpadá. V případě, že tento parameter nebude uveden, pak se test kontejneru bude provádět na instanci OpenShiftu specifikované OPENSHIFT_IP parameterem. O tom, ale budu psát později.

Tím je prostředí pro testování kontejnerů v OpenShiftu připraveno.

Testy pro kontejner

Test pro náš ukázkový memcached kontejner vypadá následovně:
sanity1.py


import pexpect
from avocado import main
from avocado.core import exceptions
from moduleframework import module_framework
from moduleframework import common


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

def test_smoke(self):
    self.start()
    session = pexpect.spawn("telnet %s %s " % (self.ip_address,
    self.getConfig()['service']['port']))
    session.sendline('set Test 0 100 4\r\n\n')
    session.sendline('JournalDev\r\n\n')
    common.print_info('Expecting STORED')
    session.expect('STORED')
    common.print_info('STORED was catched')
    session.close()


if __name__ == '__main__':
    main()

Tento test se pomocí příkazu telnet připojí na danou ip_addressu a port. Port je specifikován v configuračním souboru pro MTF. O proměnné ip_address se zmíním v následující sekci.

Samotné testování kontejnerů

Nyní budeme testovat kontejner budťo lokálně nebo na vzdálené instanci. K testům slouží samotný příkaz mtf.
Testování kontejnerů lokálně či na vzdálené instanci se liší pouze parametry, jež předáme mtf příkazu.

Testy na lokální instanci OpenShiftu

$ sudo MODULE=openshift OPENSHIFT_LOCAL=yes mtf sanity1.py

V tomto případě se v sanity1.py použije self.ip_address adresa 127.0.0.1.
Testování na vzdálené instanci OpenShiftu

   $ sudo OPENSHIFT_IP= OPENSHIFT_USER=developer OPENSHIFT_PASSWD=developer mtf sanity1.py

V tomto případě se v sanity1.py použije self.ip_address adresa OPENSHIFT_IP.
Vše ostatní zůstává stejné.

Testy se budou spouštět z prostředí, v našem případě z notebooku či PC a připojovat se budou na danou OpenShift instanci, kterou jsme specifikovali.

Výstup z testu

$ sudo MODULE=openshift OPENSHIFT_LOCAL=yes mtf sanity1.py
JOB ID : c2b0877ca52a14c6c740582c76f60d4f19eb2d4d
JOB LOG : /root/avocado/job-results/job-2017-12-18T12.32-c2b0877/job.log
(1/1) sanity1.py:SanityCheck1.test_smoke: PASS (13.19 s)
RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0
JOB TIME : 13.74 s
JOB HTML : /root/avocado/job-results/job-2017-12-18T12.32-c2b0877/results.html

Jestliže otevřeme log zvýrazněný tučně, můžeme vidět následující:

[...snip...]['/var/log/messages', '/var/log/syslog', '/var/log/system.log'])
2017-12-18 14:29:36,208 job L0321 INFO | Command line: /bin/avocado run --json /tmp/tmppfZpNe sanity1.py
2017-12-18 14:29:36,208 job L0322 INFO |
2017-12-18 14:29:36,208 job L0326 INFO | Avocado version: 55.0
2017-12-18 14:29:36,208 job L0342 INFO |
2017-12-18 14:29:36,208 job L0346 INFO | Config files read (in order):
2017-12-18 14:29:36,208 job L0348 INFO | /etc/avocado/avocado.conf
2017-12-18 14:29:36,208 job L0348 INFO | /etc/avocado/conf.d/gdb.conf
2017-12-18 14:29:36,208 job L0348 INFO | /root/.config/avocado/avocado.conf
2017-12-18 14:29:36,208 job L0353 INFO |
2017-12-18 14:29:36,208 job L0355 INFO | Avocado config:
2017-12-18 14:29:36,209 job L0364 INFO | Section.Key [...snip...]

:::::::::::::::::::::::: SETUP ::::::::::::::::::::::::

2017-12-18 14:29:36,629 avocado_test L0069 DEBUG|

:::::::::::::::::::::::: START MODULE ::::::::::::::::::::::::

MTF nejprve ověří, jestli aplikace skutečně neexistuje v OpenShift prostředí

2017-12-18 14:29:36,629 process L0389 INFO | Running 'oc get dc memcached -o json'
2017-12-18 14:29:36,842 process L0479 DEBUG| [stderr] Error from server (NotFound): deploymentconfigs.apps.openshift.io "memcached" not found
2017-12-18 14:29:36,846 process L0499 INFO | Command 'oc get dc memcached -o json' finished with 1 after 0.213222980499s

Následně MTF ověří, jestli neexistuje žádný POD pod OpenShiftem.
2017-12-18 14:29:36,847 process L0389 INFO | Running 'oc get pods -o json'
2017-12-18 14:29:37,058 process L0479 DEBUG| [stdout] {
2017-12-18 14:29:37,059 process L0479 DEBUG| [stdout] "apiVersion": "v1",
2017-12-18 14:29:37,059 process L0479 DEBUG| [stdout] "items": [],
2017-12-18 14:29:37,059 process L0479 DEBUG| [stdout] "kind": "List",
2017-12-18 14:29:37,059 process L0479 DEBUG| [stdout] "metadata": {},
2017-12-18 14:29:37,059 process L0479 DEBUG| [stdout] "resourceVersion": "",
2017-12-18 14:29:37,059 process L0479 DEBUG| [stdout] "selfLink": ""
2017-12-18 14:29:37,060 process L0479 DEBUG| [stdout] }
2017-12-18 14:29:37,064 process L0499 INFO | Command 'oc get pods -o json' finished with 0 after 0.211796045303s

V následujícím kroku vytvoříme aplikaci s labelem mtf_testing a jménem, který získáme z hodnoty poskytnuté v config.yaml souboru jako container.

2017-12-18 14:29:37,064 process L0389 INFO | Running 'oc new-app -l mtf_testing=true docker.io/modularitycontainers/memcached --name=memcached'
2017-12-18 14:29:39,022 process L0479 DEBUG| [stdout] --> Found Docker image bbc8bba (5 weeks old) from docker.io for "docker.io/modularitycontainers/memcached"
2017-12-18 14:29:39,022 process L0479 DEBUG| [stdout]
2017-12-18 14:29:39,022 process L0479 DEBUG| [stdout] 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.
2017-12-18 14:29:39,022 process L0479 DEBUG| [stdout]
2017-12-18 14:29:39,022 process L0479 DEBUG| [stdout] Tags: memcached
2017-12-18 14:29:39,023 process L0479 DEBUG| [stdout]
2017-12-18 14:29:39,023 process L0479 DEBUG| [stdout] * An image stream will be created as "memcached:latest" that will track this image
2017-12-18 14:29:39,023 process L0479 DEBUG| [stdout] * This image will be deployed in deployment config "memcached"
2017-12-18 14:29:39,023 process L0479 DEBUG| [stdout] * Port 11211/tcp will be load balanced by service "memcached"
2017-12-18 14:29:39,023 process L0479 DEBUG| [stdout] * Other containers can access this service through the hostname "memcached"
2017-12-18 14:29:39,023 process L0479 DEBUG| [stdout]
2017-12-18 14:29:39,023 process L0479 DEBUG| [stdout] --> Creating resources with label mtf_testing=true ...
2017-12-18 14:29:39,032 process L0479 DEBUG| [stdout] imagestream "memcached" created
2017-12-18 14:29:39,043 process L0479 DEBUG| [stdout] deploymentconfig "memcached" created
2017-12-18 14:29:39,063 process L0479 DEBUG| [stdout] service "memcached" created
2017-12-18 14:29:39,064 process L0479 DEBUG| [stdout] --> Success
2017-12-18 14:29:39,064 process L0479 DEBUG| [stdout] Run 'oc status' to view your app.
2017-12-18 14:29:39,069 process L0499 INFO | Command 'oc new-app -l mtf_testing=true docker.io/modularitycontainers/memcached --name=memcached' finished with 0 after 2.00025391579s

Následně ověříme jestli aplikace skutečně běží a na jaké IP adrese je dostupná.

2017-12-18 14:29:46,201 process L0389 INFO | Running 'oc get service -o json'
2017-12-18 14:29:46,416 process L0479 DEBUG| [stdout] {
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] "apiVersion": "v1",
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] "items": [
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] {
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] "apiVersion": "v1",
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] "kind": "Service",
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] "metadata": {
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] "annotations": {
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] "openshift.io/generated-by": "OpenShiftNewApp"
2017-12-18 14:29:46,417 process L0479 DEBUG| [stdout] },
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] "creationTimestamp": "2017-12-18T13:29:39Z",
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] "labels": {
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] "app": "memcached",
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] "mtf_testing": "true"
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] },
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] "name": "memcached",
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] "namespace": "myproject",
2017-12-18 14:29:46,418 process L0479 DEBUG| [stdout] "resourceVersion": "2121",
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] "selfLink": "/api/v1/namespaces/myproject/services/memcached",
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] "uid": "7f50823d-e3f7-11e7-be28-507b9d4150cb"
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] },
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] "spec": {
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] "clusterIP": "172.30.255.42",
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] "ports": [
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] {
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] "name": "11211-tcp",
2017-12-18 14:29:46,419 process L0479 DEBUG| [stdout] "port": 11211,
2017-12-18 14:29:46,420 process L0479 DEBUG| [stdout] "protocol": "TCP",
2017-12-18 14:29:46,420 process L0479 DEBUG| [stdout] "targetPort": 11211
2017-12-18 14:29:46,420 process L0499 INFO | Command 'oc get service -o json' finished with 0 after 0.213701963425s
2017-12-18 14:29:46,420 process L0479 DEBUG| [stdout] }
2017-12-18 14:29:46,420 process L0479 DEBUG| [stdout] ],
2017-12-18 14:29:46,420 process L0479 DEBUG| [stdout] "selector": {
2017-12-18 14:29:46,420 process L0479 DEBUG| [stdout] "app": "memcached",
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] "deploymentconfig": "memcached",
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] "mtf_testing": "true"
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] },
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] "sessionAffinity": "None",
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] "type": "ClusterIP"
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] },
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] "status": {
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] "loadBalancer": {}
2017-12-18 14:29:46,421 process L0479 DEBUG| [stdout] }
2017-12-18 14:29:46,422 process L0479 DEBUG| [stdout] }
2017-12-18 14:29:46,422 process L0479 DEBUG| [stdout] ],
2017-12-18 14:29:46,422 process L0479 DEBUG| [stdout] "kind": "List",
2017-12-18 14:29:46,422 process L0479 DEBUG| [stdout] "metadata": {},
2017-12-18 14:29:46,422 process L0479 DEBUG| [stdout] "resourceVersion": "",
2017-12-18 14:29:46,422 process L0479 DEBUG| [stdout] "selfLink": ""
2017-12-18 14:29:46,422 process L0479 DEBUG| [stdout] }

V poslední fázi se spustí testy nad daným kontejnerem.

2017-12-18 14:29:46,530 output L0655 DEBUG| Expecting STORED
2017-12-18 14:29:46,531 output L0655 DEBUG| STORED was catched
2017-12-18 14:29:46,632 avocado_test L0069 DEBUG|

:::::::::::::::::::::::: TEARDOWN ::::::::::::::::::::::::

2017-12-18 14:29:46,632 process L0389 INFO | Running 'oc get dc memcached -o json'
2017-12-18 14:29:46,841 process L0479 DEBUG| [stdout] {
2017-12-18 14:29:46,841 process L0479 DEBUG| [stdout] "apiVersion": "v1",
2017-12-18 14:29:46,841 process L0479 DEBUG| [stdout] "kind": "DeploymentConfig",
2017-12-18 14:29:46,841 process L0479 DEBUG| [stdout] "metadata": {

Na konci testů zkontrolujeme, jestli služba ještě neběží v OpenShiftu, toto můžeme ověřit příkazem oc status:

$ sudo oc status
In project My Project (myproject) on server https://127.0.0.1:8443

You have no services, deployment configs, or build configs.
Run 'oc new-app' to create an application.

Závěr

Jak z předešlého, tak i z tohoto článku, lze vidět, že psaní testů a testování samotných kontejnerů není vůbec složité a můžeme tedy zaručit, že kontejner je plně funkční.
Do budoucna bychom rádi rozšířili testování S2I kontejnerů a testování kontejnerů pomocí tzv. OpenShift template.
MTF dokumentace
MTF je relativně dobře dokumentovaná a snažíme se ji neustále vylepšovat. Online dokumentace je dostupná na readthedocs.io webpage.
Více informací lze nalézt zde [6].

Links

[1] https://mojefedora.cz/testovani-modulu-a-kontejneru-s-modularity-testing-frameworkem/
[2] https://admin.fedoraproject.org/pkgdb/package/rpms/meta-test-family/
[3] https://www.openshift.com/
[4] https://src.fedoraproject.org/projects/container/*
[5] https://github.com/fedora-modularity/meta-test-family
[6] http://meta-test-family.readthedocs.io/en/latest/