Salut tuturor!
Lucrez ca inginer DevOps într-un serviciu de rezervare a hotelurilor . În acest articol, vreau să împărtășesc experiența noastră în testarea rolurilor ansible.
La Ostrovok.ru, utilizăm ansible ca manager de configurație. Recent, am ajuns la necesitatea testării rolurilor, dar, după cum s-a dovedit, nu există atât de multe instrumente pentru aceasta — cel mai popular, probabil, este cadrul Molecule, așa că am decis să-l folosim. Dar s-a dovedit că documentația sa omite multe capcane. Nu am reușit să găsim un ghid destul de detaliat în limba rusă, așa că am decis să scriem acest articol.

Molecule
— cadrul care ajută la testarea rolurilor ansible.
Descriere simplificată: Molecula creează o instanță pe platforma pe care ați specificat-o (cloud, mașină virtuală, container; mai multe detalii în secțiunea ), rulează rolul dumneavoastră pe aceasta, apoi execută teste și șterge instanța. În cazul în care apare o eroare într-unul dintre pași, Molecula vă va informa despre aceasta.
Acum în detaliu.
Puțină teorie
Să analizăm două entități cheie ale Moleculei: Scenario și Driver.
Scenario
Scenariul conține descrierea a ceea ce, unde, cum și în ce ordine va fi executat. O rolă poate avea mai multe scenarii, iar fiecare este un director pe calea /molecule/, conținând descrierile acțiunilor necesare pentru test. Este obligatoriu să existe un scenariu default, care va fi creat automat dacă inițializați rolul folosind Molecula. Numele următoarelor scenarii se aleg la discreția dumneavoastră.
Secvența acțiunilor de testare într-un scenariu se numește matrix, și în mod implicit aceasta este:
(Pașii marcați ?, sunt omisi în mod implicit, dacă nu sunt descriși de utilizator)
lint— rularea linters. În mod implicit, se folosescyamllintșiflake8,destroy— ștergerea instanțelor rămase de la ultima rulare a Moleculei (dacă există),dependency? — установка ansible-зависимости тестируемой роли,syntax— verificarea sintaxei rolului folosindansible-playbook --syntax-check,create— crearea instanței,prepare? — подготовка инстанса; например, проверка / установка python2converge— rularea playbook-ului testat,idempotence— rerularea playbook-ului pentru testul de idempotentă,side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,verify— rularea testelor configurației obținute folosindtestinfra(în mod implicit) /goss/inspec,cleanup? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,destroy— ștergerea instanței.
Această secvență acoperă majoritatea cazurilor, dar, dacă este necesar, poate fi modificată.
Fiecare dintre pașii menționați mai sus poate fi rulat separat folosind molecule. Dar este important de știut că pentru fiecare astfel de comandă cli poate exista o secvență specifică de acțiuni, pe care o puteți afla executând molecule matrix. De exemplu, la rularea comenzii converge (executarea playbook-ului testat) vor fi executate următoarele acțiuni:
$ molecule matrix converge
...
└── default # numele scenariului
├── dependency # instalarea dependențelor
├── create # crearea instanței
├── prepare # pregătirea instanței
└── converge # rularea playbook-uluiSecvența acestor acțiuni poate fi editată. Dacă ceva din listă a fost deja executat, va fi sărit. Starea curentă, precum și configurația instanțelor, Molecule le păstrează în directorul $TMPDIR/molecule//.
Adăugarea pașilor cu ? se poate face descriind acțiunile dorite în format de playbook ansible, iar numele fișierului să fie conform pasului: prepare.yml/side_effect.yml. Molecule va aștepta aceste fișiere în folderul scenariului.
Driver
Driverul este entitatea unde sunt create instanțele pentru teste.
Lista driverelor standard pentru care Molecule are template-uri gata este: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.
În cele mai multe cazuri, template-urile sunt fișierele create.yml și destroy.yml din folderul scenariului, care descriu crearea și distrugerea instanței, respectiv.
Excepțiile sunt Docker și Vagrant, deoarece interacțiunile cu modulele lor pot avea loc fără fișierele menționate anterior.
Merită să menționăm driverul Delegated, deoarece în cazul utilizării sale, fișierele de creare și distrugere a instanței descriu doar lucrul cu configurația instanțelor, restul trebuie să fie descris de inginer.
Driverul implicit este Docker.
Acum să trecem la practică și vom discuta despre caracteristicile suplimentare acolo.
Începerea utilizării
Ca «hello world», vom testa un rol simplu pentru instalarea nginx. Ca driver, vom alege Docker – cred că este instalat la majoritatea dintre voi (și să ne amintim că Docker este driverul implicit).
Să pregătim virtualenv și să-l instalăm în acesta molecule:
> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker # molecule va instala ansible ca dependență; docker pentru driverUrmătorul pas este să inițializăm un rol nou.
Inițializarea unui nou rol, la fel ca și a unui nou scenariu, se face folosind comanda molecule init:
> molecule init role -r nginx
--> Inițializare nou rol nginx...
Rol inițializat cu succes în /nginx.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars
6 directoare, 1 fișierAm obținut un rol tipic ansible. În continuare, toate interacțiunile cu CLI Molecule se fac din rădăcina rolului.
Hai să vedem ce se află în directorul rolului:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Șablon Jinja pentru Dockerfile
├── INSTALL.rst. # Informații despre instalarea dependențelor scenariului
├── molecule.yml # Fișier de configurare
├── playbook.yml # Playbook pentru rularea rolului
└── tests # Director cu teste pentru etapa de confirmare
└── test_default.py
1 director, 6 fișiereSă analizăm configurația molecule/default/molecule.yml (vom schimba doar imaginea docker):
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
platforms:
- name: instance
image: centos:7
provisioner:
name: ansible
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8dependency
Această secțiune descrie sursa de dependențe.
Posibile variante: , , shell.
Shell-ul este pur și simplu un terminal de comandă care este folosit în cazul în care galaxy și gilt nu acoperă nevoile tale.
Nu voi insista prea mult aici, este suficient descris în .
driver
Numele driverului. La noi este docker.
lint
Pentru linters se folosește yamllint.
Opțiunile utile în această parte a configurației sunt posibilitatea de a specifica un fișier de configurare pentru yamllint, de a trece variabile de mediu sau de a dezactiva linter-ul:
lint:
name: yamllint
options:
config-file: foo/bar
env:
FOO: bar
enabled: Falseplatforms
Descrie configurația instanțelor.
În cazul docker-ului ca driver, Molecule iterează prin această secțiune, iar fiecare element din listă este disponibil în Dockerfile.j2 ca variabilă item.
În cazul driverului, în care este obligatorie create.yml și destroy.yml, secțiunea este disponibilă în ele ca molecule_yml.platforms, iar iterațiile prin ea sunt deja descrise în aceste fișiere.
Deoarece Molecule oferă gestionarea instanțelor modulului ansible, lista posibilelor configurații trebuie căutată acolo. De exemplu, pentru docker se folosește modulul . Modulele utilizate în celelalte drivere pot fi găsite în .
De asemenea, exemple de utilizare a diferitelor drivere pot fi găsite .
Să înlocuim aici centos:7 pe ubuntu.
provisioner
„Furnizorul“ este o entitate care gestionează instanțele. În cazul Moleculei, acesta este ansible, suportul pentru altele nu este planificat, așa că această secțiune poate fi numită, cu o rezervă, configurație extinsă a ansible.
Aici se pot menționa multe lucruri, voi evidenția principalele, din punctul meu de vedere:
- playbooks: se pot specifica ce playbook-uri trebuie utilizate în anumite etape.
provisioner:
name: ansible
playbooks:
create: create.yml
destroy: ..\/default\/destroy.yml
converge: playbook.yml
side_effect: side_effect.yml
cleanup: cleanup.yml- config_options:
provisioner:
name: ansible
config_options:
defaults:
fact_caching: jsonfile
ssh_connection:
scp_if_ssh: True- connection_options: parametrii
provisioner:
name: ansible
connection_options:
ansible_ssh_common_args: "-o 'UserKnownHostsFile=\/dev\/null' -o 'ForwardAgent=yes'"- options: parametrii Ansible și variabile de mediu
provisioner:
name: ansible
options:
vvv: true
diff: true
env:
FOO: BARscenario
Numele și descrierea secvențelor de scenariu.
Se poate schimba matricea de acțiuni implicit a unei comenzi adăugând cheia <command>_sequence și ca valoare pentru aceasta definind lista de pași necesară.
De exemplu, dorim să modificăm secvența de acțiuni când se execută comanda de rulare a playbook-ului: molecule converge
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeverifier
Configurarea framework-ului pentru teste și linternă pentru acesta. În mod implicit, se utilizează ca linternă testinfra și flake8. Opțiunile posibile sunt asemănătoare cu cele menționate mai sus:
verifier:
name: testinfra
additional_files_or_dirs:
- ..\/path\/to\/test_1.py
- ..\/path\/to\/test_2.py
- ..\/path\/to\/directory\/ *
options:
n: 1
enabled: False
env:
FOO: bar
lint:
name: flake8
options:
benchmark: True
enabled: False
env:
FOO: barSă ne întoarcem la rolul nostru. Să edităm fișierul tasks\/main.yml în această formă:
---
- name: Install nginx
apt:
name: nginx
state: present
- name: Start nginx
service:
name: nginx
state: started
Și să adăugăm teste în molecule\/default\/tests\/test_default.py
def test_nginx_is_installed(host):
nginx = host.package("nginx")
assert nginx.is_installed
def test_nginx_running_and_enabled(host):
nginx = host.service("nginx")
assert nginx.is_running
assert nginx.is_enabled
def test_nginx_config(host):
host.run("nginx -t")
Gata, rămâne doar să rulăm (din rădăcina rolului, să ne amintim):
> molecule testVârful lung sub spoiler:
--> Validarea schemei /nginx/molecule/default/molecule.yml.
Validarea a fost finalizată cu succes.
--> Matrice de teste
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
--> Scenariul: 'default'
--> Acțiune: 'lint'
--> Executarea Yamllint pe fișierele găsite în /nginx/...
Lintul a fost finalizat cu succes.
--> Executarea Flake8 pe fișierele găsite în /nginx/molecule/default/tests/...
Lintul a fost finalizat cu succes.
--> Executarea Ansible Lint pe /nginx/molecule/default/playbook.yml...
Lintul a fost finalizat cu succes.
--> Scenariul: 'default'
--> Acțiune: 'destroy'
PLAY [Distruge] *****************************************************************
TASK [Distruge instanța moleculă] ********************************************
modificat: [localhost] => (item=None)
modificat: [localhost]
TASK [Așteaptă finalizarea ștergerii instanței] *******************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Șterge rețeaua docker] **************************************************
PLAY RECAP *********************************************************************
localhost : ok=2 modificat=1 inaccesibil=0 eșuat=0
--> Scenariul: 'default'
--> Acțiune: 'dependency'
Sărind, fișierul de cerințe lipsește.
--> Scenariul: 'default'
--> Acțiune: 'syntax'
playbook: /nginx/molecule/default/playbook.yml
--> Scenariul: 'default'
--> Acțiune: 'create'
PLAY [Creare] ******************************************************************
TASK [Conectare la un registru Docker] *****************************************
sărind: [localhost] => (item=None)
TASK [Creare Dockerfiles din numele imaginilor] *******************************
modificat: [localhost] => (item=None)
modificat: [localhost]
TASK [Descoperire imagini Docker locale] ***************************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Construiește o imagine compatibilă Ansible] *****************************
modificat: [localhost] => (item=None)
modificat: [localhost]
TASK [Creare rețea docker(e)] *************************************************
TASK [Creare instanță moleculă(e)] ********************************************
modificat: [localhost] => (item=None)
modificat: [localhost]
TASK [Așteaptă finalizarea creării instanței] *******************************
modificat: [localhost] => (item=None)
modificat: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=5 modificat=4 inaccesibil=0 eșuat=0
--> Scenariul: 'default'
--> Acțiune: 'prepare'
Sărind, playbook-ul de pregătire nu este configurat.
--> Scenariul: 'default'
--> Acțiune: 'converge'
PLAY [Convergere] **************************************************************
TASK [Adunare fapte] *********************************************************
ok: [instanță]
TASK [nginx : Instalează nginx] ***********************************************
modificat: [instanță]
TASK [nginx : Pornește nginx] ***************************************************
modificat: [instanță]
PLAY RECAP *********************************************************************
instanță : ok=3 modificat=2 inaccesibil=0 eșuat=0
--> Scenariul: 'default'
--> Acțiune: 'idempotence'
Idempotenta a fost finalizată cu succes.
--> Scenariul: 'default'
--> Acțiune: 'side_effect'
Sărind, playbook-ul de efect secundar nu este configurat.
--> Scenariul: 'default'
--> Acțiune: 'verify'
--> Executarea testelor Testinfra găsite în /nginx/molecule/default/tests/...
============================= sesiune de testare începe ==============================
platforma darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
rootdir: /nginx/molecule/default, inifile:
pluginuri: testinfra-1.16.0
colectate 4 elemente
tests/test_default.py .... [100%]
========================== 4 trecute în 27.23 secunde ===========================
Verificatorul a fost finalizat cu succes.
--> Scenariul: 'default'
--> Acțiune: 'destroy'
PLAY [Distruge] *****************************************************************
TASK [Distruge instanța moleculă] ********************************************
modificat: [localhost] => (item=None)
modificat: [localhost]
TASK [Așteaptă finalizarea ștergerii instanței] *******************************
modificat: [localhost] => (item=None)
modificat: [localhost]
TASK [Șterge rețeaua docker] **************************************************
PLAY RECAP *********************************************************************
localhost : ok=2 modificat=2 inaccesibil=0 eșuat=0
Rolul nostru simplu a fost testat fără probleme.
Este important să rețineți că, dacă apar probleme în timpul lucrului testul moleculei, dacă nu ați modificat secvența standard, Molecule va șterge instanța.
Pentru depanare, sunt utile următoarele comenzi:
> molecule --debug # informații de depanare. La rularea obișnuită, Molecule ascunde jurnalele.
> molecule converge # Lasă instanța după rularea rolului testat.
> molecule login # Intră în instanța creată.
> molecule --help # Lista completă a comenzilor.Rol existent
Adăugarea unui nou scenariu la un rol existent se face din directorul rolului prin următoarele comenzi:
# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>În cazul în care acesta este primul scenariu din rol, parametrul -s poate fi omis, deoarece va fi creat un scenariu. default.
Concluzie
După cum vedeți, Molecule nu este foarte complicat, iar folosind șabloanele proprii, se poate reduce desfășurarea unui nou scenariu la modificarea variabilelor în playbook-urile de creare și ștergere a instanțelor. Molecule se integrează fără probleme cu sistemele CI, ceea ce permite creșterea vitezei de dezvoltare prin reducerea timpului de testare manuală a playbook-urilor.
Vă mulțumesc pentru atenție. Dacă aveți experiență în testarea rolurilor ansible, care nu este legată de Molecule — povestiți despre ea în comentarii!
Sursa: habr.com
