Ciao a tutti!
Lavoro come ingegnere DevOps in un servizio di prenotazione alberghiera. . In questo articolo voglio raccontare la nostra esperienza con il testing delle role Ansible.
In Ostrovok.ru, come manager della configurazione, utilizziamo Ansible. Recentemente ci siamo trovati nella necessità di testare le role, ma, a quanto pare, non esistono molti strumenti per farlo: il più popolare è senz'altro il framework Molecule, quindi abbiamo deciso di usarlo. Tuttavia, ci siamo resi conto che la sua documentazione omette molti dettagli critici. Non siamo riusciti a trovare una guida dettagliata in russo, quindi abbiamo deciso di scrivere questo articolo.

Molecule
è un framework per facilitare il testing delle role Ansible.
Descrizione semplificata: Molecule crea un'istanza sulla piattaforma che hai specificato (cloud, macchina virtuale, container; per ulteriori dettagli, vedi la sezione ), esegue la tua role, quindi lancia i test e rimuove l'istanza. In caso di fallimento in uno dei passaggi, Molecule ti avviserà.
Ora in modo più dettagliato.
Un po' di teoria
Esaminiamo due entità chiave di Molecule: Scenario e Driver.
Scenario
Lo scenario contiene una descrizione di cosa, dove, come e in quale sequenza verrà eseguito. Un ruolo può avere più scenari, e ciascuno è una directory lungo il percorso /molecule/, contenente la descrizione delle azioni necessarie per il test. Deve essere presente uno scenario default, che verrà creato automaticamente se inizializzi il ruolo tramite Molecule. I nomi dei seguenti scenari possono essere scelti a tua discrezione.
La sequenza delle azioni di test nello scenario è chiamata matrix, e di default è la seguente:
(I passi contrassegnati ?, per impostazione predefinita vengono saltati se non descritti dall'utente)
lint— esecuzione dei linter. Di default si utilizzanoyamllinteflake8,destroy— eliminazione delle istanze dall'ultima esecuzione di Molecule (se presenti),dependency? — установка ansible-зависимости тестируемой роли,syntax— verifica della sintassi del ruolo conansible-playbook --syntax-check,create— creazione di un'istanza,prepare? — подготовка инстанса; например, проверка / установка python2converge— esecuzione del playbook da testare,idempotence— ri-esecuzione del playbook per il test di idempotenza,side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,verify— esecuzione dei test della configurazione risultante contestinfra(per impostazione predefinita) /goss/inspec,cleanup? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,destroy— eliminazione dell'istanza.
Questa sequenza copre la maggior parte dei casi, ma, se necessario, può essere modificata.
Ognuno dei passaggi sopra elencati può essere eseguito separatamente utilizzando molecule. Ma è importante capire che per ciascun comando CLI potrebbero esistere sequenze di azioni proprie, che si possono scoprire eseguendo molecule matrix. Ad esempio, quando si esegue il comando converge (esecuzione del playbook in fase di test) verranno eseguite le seguenti azioni:
$ molecule matrix converge
...
└── default # nome dello scenario
├── dependency # installazione delle dipendenze
├── create # creazione dell'istanza
├── prepare # preparazione dell'istanza
└── converge # esecuzione del playbookLa sequenza di queste azioni può essere modificata. Se qualcosa della lista è già stato eseguito, verrà saltato. Lo stato attuale, così come la configurazione delle istanze, Molecule li conserva nella directory $TMPDIR/molecule//.
Aggiungere passaggi con ? è possibile descrivendo le azioni desiderate nel formato di un playbook ansible e dando al file un nome corrispondente al passaggio: prepare.yml/side_effect.yml. Molecule si aspetterà questi file nella cartella dello scenario.
Driver
Il driver è l'entità in cui vengono creati gli istanze per i test.
L'elenco dei driver standard per i quali Molecule ha pronti dei modelli è il seguente: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.
Nella maggior parte dei casi, i modelli sono file create.yml e destroy.yml nella cartella degli scenari, che descrivono rispettivamente la creazione e la distruzione di un'istanza.
Le eccezioni sono Docker e Vagrant, poiché l'interazione con i loro moduli può avvenire senza i file suddetti.
Vale la pena sottolineare il driver Delegated, poiché nel caso del suo utilizzo nei file di creazione e distruzione dell'istanza è descritta solo l'interazione con la configurazione delle istanze; il resto deve essere descritto dall'ingegnere.
Il driver predefinito è Docker.
Ora passiamo alla pratica e analizziamo ulteriormente le caratteristiche lì.
Inizio
Come 'hello world', testeremo un semplice ruolo per l'installazione di nginx. Come driver sceglieremo Docker — credo sia installato per la maggior parte di voi (e ricordiamo che Docker è il driver predefinito).
Prepariamo virtualenv e installiamo al suo interno molecule:
> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker # molecule installerà ansible come dipendenza; docker per il driverIl passo successivo è inizializzare un nuovo ruolo.
L'inizializzazione di un nuovo ruolo, come quella di un nuovo scenario, avviene tramite il comando molecule init:
> molecule init role -r nginx
--> Inizializzazione del nuovo ruolo nginx...
Ruolo inizializzato in /nginx con successo.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars
6 directory, 1 fileAbbiamo ottenuto un tipico ruolo Ansible. Da questo punto, tutte le interazioni con l'interfaccia a riga di comando di Molecule avvengono dalla radice del ruolo.
Diamo un'occhiata a cosa si trova nella directory del ruolo:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Modello Jinja per Dockerfile
├── INSTALL.rst. # Breve informazione sull'installazione delle dipendenze dello scenario
├── molecule.yml # File di configurazione
├── playbook.yml # Playbook per eseguire il ruolo
└── tests # Directory con i test della fase di verifica
└── test_default.py
1 directory, 6 fileAnalizziamo il file di configurazione molecule/default/molecule.yml (sostituiamo solo l'immagine 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
Questa sezione descrive la fonte delle dipendenze.
Opzioni disponibili: , , shell.
Shell è semplicemente una shell di comando utilizzata nel caso in cui galaxy e gilt non soddisfino le vostre esigenze.
Non mi soffermerò a lungo qui, è già stato ampiamente descritto in .
driver
Nome del driver. Da noi è docker.
lint
Per il linting si utilizza yamllint.
Opzioni utili in questa parte della configurazione: è possibile specificare un file di configurazione per yamllint, passare variabili d'ambiente oppure disattivare il linter:
lint:
name: yamllint
options:
config-file: foo/bar
env:
FOO: bar
enabled: Falsepiattaforme
Descrive la configurazione degli istanziamenti.
Nel caso di docker come driver, Molecule itera su questa sezione, e ogni elemento della lista è accessibile in Dockerfile.j2 come variabile item.
Nel caso del driver che richiede obbligatoriamente create.yml e destroy.yml, la sezione è accessibile in molecule_yml.platforms, e le iterazioni su di essa sono già descritte in questi file.
Poiché Molecule gestisce gli istanziamenti per i moduli ansible, anche l'elenco delle configurazioni disponibili deve essere cercato lì. Per docker, ad esempio, si usa il modulo . Quali moduli vengono utilizzati negli altri driver può essere trovato in .
E esempi di utilizzo di vari driver possono essere trovati .
Sostituiremo qui centos:7 con ubuntu.
provisioner
«Provider» è l'entità che gestisce le istanze. Nel caso di Molecule, si tratta di ansible; non è prevista la supporto per altre, quindi questa sezione può essere considerata un'estensione della configurazione di ansible.
Qui puoi specificare molte cose. Metterò in evidenza i punti principali, a mio avviso:
- playbooks: puoi specificare quali playbook devono essere utilizzati in determinate fasi.
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: parametri
provisioner:
name: ansible
connection_options:
ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"- options: parametri Ansible e variabili d'ambiente
provisioner:
name: ansible
options:
vvv: true
diff: true
env:
FOO: BARscenario
Nome e descrizione delle sequenze di scenario.
Puoi modificare la matrice di azioni predefinita di un comando aggiungendo la chiave _sequence e definendo la lista di passaggi desiderata come valore per essa.
Supponiamo di voler modificare la sequenza di azioni durante l'esecuzione del comando per eseguire il playbook: molecule converge
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeverifier
Configurazione del framework per i test e il relativo linter. Di default, viene utilizzato come linter testinfra e flake8. Le opzioni possibili sono simili a quelle menzionate in precedenza:
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: barTorniamo al nostro ruolo. Modifichiamo il file tasks/main.yml in questo modo:
---
- name: Installare nginx
apt:
name: nginx
state: present
- name: Avviare nginx
service:
name: nginx
state: started
E aggiungiamo i test in 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")
Fatto, rimane solo da eseguire (dalla radice del ruolo, ricordo):
> molecule testOutput lungo sotto spoiler:
--> Validazione dello schema /nginx/molecule/default/molecule.yml.
Convalida completata con successo.
--> Matrice di test
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
--> Scenario: 'default'
--> Azione: 'lint'
--> Esecuzione di Yamllint sui file trovati in /nginx/...
Lint completato con successo.
--> Esecuzione di Flake8 sui file trovati in /nginx/molecule/default/tests/...
Lint completato con successo.
--> Esecuzione di Ansible Lint su /nginx/molecule/default/playbook.yml...
Lint completato con successo.
--> Scenario: 'default'
--> Azione: 'destroy'
GIOCO [Distruggi] *****************************************************************
ATTIVITÀ [Distruggi le istanze di molecola] ********************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
ATTIVITÀ [Aspetta che la cancellazione dell'istanza sia completata] *******************************
ok: [localhost] => (item=None)
ok: [localhost]
ATTIVITÀ [Elimina la rete docker] ************************************************
RIEPILOGO GIOCO *********************************************************************
localhost : ok=2 cambiato=1 irraggiungibile=0 fallito=0
--> Scenario: 'default'
--> Azione: 'dependency'
Salta, file di requisiti mancante.
--> Scenario: 'default'
--> Azione: 'syntax'
playbook: /nginx/molecule/default/playbook.yml
--> Scenario: 'default'
--> Azione: 'create'
GIOCO [Crea] ******************************************************************
ATTIVITÀ [Accedi a un registro Docker] **********************************************
salto: [localhost] => (item=None)
ATTIVITÀ [Crea Dockerfiles dai nomi delle immagini] *************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
ATTIVITÀ [Scopri le immagini Docker locali] ********************************************
ok: [localhost] => (item=None)
ok: [localhost]
ATTIVITÀ [Crea un'immagine compatibile con Ansible] ***************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
ATTIVITÀ [Crea reti docker] ************************************************
ATTIVITÀ [Crea istanze di molecola] *********************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
ATTIVITÀ [Aspetta che la creazione dell'istanza sia completata] *******************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
RIEPILOGO GIOCO *********************************************************************
localhost : ok=5 cambiato=4 irraggiungibile=0 fallito=0
--> Scenario: 'default'
--> Azione: 'prepare'
Salta, playbook di preparazione non configurato.
--> Scenario: 'default'
--> Azione: 'converge'
GIOCO [Converge] ****************************************************************
ATTIVITÀ [Raccolta fatti] *********************************************************
ok: [istanza]
ATTIVITÀ [nginx : Installa nginx] ***************************************************
cambiato: [istanza]
ATTIVITÀ [nginx : Avvia nginx] *****************************************************
cambiato: [istanza]
RIEPILOGO GIOCO *********************************************************************
istanza : ok=3 cambiato=2 irraggiungibile=0 fallito=0
--> Scenario: 'default'
--> Azione: 'idempotence'
Idempotenza completata con successo.
--> Scenario: 'default'
--> Azione: 'side_effect'
Salta, playbook per effetto collaterale non configurato.
--> Scenario: 'default'
--> Azione: 'verify'
--> Esecuzione dei test di Testinfra trovati in /nginx/molecule/default/tests/...
============================= inizio sessione test ==============================
piattaforma darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
rootdir: /nginx/molecule/default, inifile:
plugin: testinfra-1.16.0
collettati 4 elementi
tests/test_default.py .... [100%]
========================== 4 superati in 27.23 secondi ===========================
Verifica completata con successo.
--> Scenario: 'default'
--> Azione: 'destroy'
GIOCO [Distruggi] *****************************************************************
ATTIVITÀ [Distruggi le istanze di molecola] ********************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
ATTIVITÀ [Aspetta che la cancellazione dell'istanza sia completata] *******************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
ATTIVITÀ [Elimina la rete docker] ************************************************
RIEPILOGO GIOCO *********************************************************************
localhost : ok=2 cambiato=2 irraggiungibile=0 fallito=0
Il nostro ruolo semplice è stato testato senza problemi.
È importante ricordare che se hai problemi durante l'esecuzione molecule test, se non hai modificato la sequenza standard, Molecule eliminerà l'istanza.
Per il debug, sono utili i seguenti comandi:
> molecule --debug # informazioni di debug. Durante l'esecuzione normale, Molecule nasconde i log.
> molecule converge # Mantiene l'istanza dopo l'esecuzione del ruolo testato.
> molecule login # Accedi all'istanza creata.
> molecule --help # Lista completa dei comandi.Ruolo esistente
L'aggiunta di un nuovo scenario a un ruolo esistente avviene dalla directory del ruolo con i seguenti comandi:
# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>Se si tratta del primo scenario nel ruolo, il parametro -s può essere omesso, poiché verrà creato uno scenario. default.
Conclusione
Come puoi vedere, Molecule non è molto complessa e, utilizzando i propri modelli, puoi ridurre il deployment di un nuovo scenario a modificare le variabili nei playbook di creazione e rimozione delle istanze. Molecule si integra senza problemi con i sistemi CI, consentendo di aumentare la velocità di sviluppo riducendo il tempo dedicato al testing manuale dei playbook.
Grazie per la vostra attenzione. Se avete esperienza con il testing dei ruoli Ansible che non sia legata a Molecule, condividete le vostre esperienze nei commenti!
Fonte: habr.com
