Ciao a tutti!
Lavoro come ingegnere DevOps nel servizio di prenotazione di hotel . In questo articolo voglio raccontare la nostra esperienza con il testing dei ruoli ansible.
In Ostrovok.ru come gestori della configurazione utilizziamo ansible. Recentemente siamo giunti alla necessità di testare i ruoli, ma, come si è scoperto, non esistono molti strumenti per questo scopo — il più popolare è probabilmente il framework Molecule, quindi abbiamo deciso di utilizzarlo. Tuttavia, la sua documentazione omette molte insidie. Non siamo riusciti a trovare una guida dettagliata in russo, quindi abbiamo deciso di scrivere questo articolo.

Molecule
è un framework che aiuta nel testing dei ruoli ansible.
Una descrizione semplificata: Molecule crea un'istanza sulla piattaforma che hai scelto (cloud, macchina virtuale, contenitore; maggiori dettagli nel paragrafo ), esegue il tuo ruolo su di essa, quindi avvia i test e rimuove l'istanza. In caso di fallimento in uno dei passaggi, Molecule ti avviserà.
Ora più nel dettaglio.
Un po' di teoria
Esaminiamo due entità chiave di Molecule: Scenario e Driver.
Scenario
Uno scenario contiene la descrizione di cosa, dove, come e in quale ordine verrà eseguito. Un ruolo può avere più scenari e ciascuno è una directory lungo il percorso /molecule/, contenente le descrizioni delle azioni necessarie per il test. Deve necessariamente essere presente uno scenario default, che sarà creato automaticamente se inizializzi il ruolo utilizzando Molecule. I nomi degli scenari successivi sono a tua discrezione.
La sequenza delle azioni di testing nello scenario si chiama matrix, e di default è la seguente:
(I passi contrassegnati ?, di default vengono saltati se non descritti dall'utente)
lintè l'esecuzione dei linter. Di default vengono utilizzatiyamllinteflake8,destroyè la rimozione delle istanze dal precedente avvio di Molecule (se ne sono rimaste),dependency? — установка ansible-зависимости тестируемой роли,syntaxè il controllo della sintassi del ruolo usandoansible-playbook --syntax-check,createè la creazione di un'istanza,preparare? — подготовка инстанса; например, проверка / установка python2convergeè l'esecuzione del playbook sotto test,idempotenceè l'esecuzione ripetuta del playbook per testare l'idempotenza,side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,verifyè l'esecuzione dei test sulla configurazione risultante usandotestinfra(di default) /goss/inspec,ripulire? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,destroyè la rimozione dell'istanza.
Questa sequenza copre la maggior parte dei casi, ma, se necessario, può essere modificata.
Ciascuno dei passaggi sopra menzionati può essere eseguito separatamente utilizzando molecule. Ma è importante capire che per ogni comando cli potrebbe esistere una propria sequenza di azioni, che è possibile scoprire eseguendo molecule matrix. Ad esempio, l'esecuzione del comando converge (esecuzione del playbook da testare) porterà all'esecuzione delle 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 lo memorizza nella directory $TMPDIR/molecule//.
È possibile aggiungere passaggi con ? , descrivendo le azioni desiderate nel formato del playbook di ansible, e dare al file il nome corrispondente al passaggio: prepare.yml/side_effect.yml. Molecule si aspetta di trovare questi file nella cartella dello scenario.
Driver
Il driver è l'entità in cui vengono create le istanze per i test.
L'elenco dei driver standard per cui Molecule ha modelli pronti è 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 dello scenario, che descrivono rispettivamente la creazione e la cancellazione dell'istanza.
Le eccezioni sono Docker e Vagrant, poiché l'interazione con i loro moduli può avvenire senza i file sopra menzionati.
È importante evidenziare il driver Delegated, poiché nel caso del suo utilizzo, nei file di creazione e cancellazione dell'istanza viene descritta solo la gestione della configurazione delle istanze, mentre il resto deve essere descritto dall'ingegnere.
Il driver predefinito è Docker.
Ora passiamo alla pratica e approfondiremo ulteriori dettagli lì.
Inizio del lavoro
Come "hello world" testeremo un semplice ruolo per l'installazione di nginx. Come driver sceglieremo docker – penso che sia installato nella maggior parte di voi (e ricordiamo che docker è il driver predefinito).
Prepariamo virtualenv e installiamolo in molecule:
> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker # molecule installerà ansible come dipendenza; docker come driverIl passo successivo è inizializzare un nuovo ruolo.
L'inizializzazione di un nuovo ruolo, così come di un nuovo scenario, avviene tramite il comando molecule init:
> molecola init ruolo -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. Tutte le interazioni con la CLI di Molecule avvengono dalla root del ruolo.
Vediamo cosa si trova nella directory del ruolo:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Template Jinja per Dockerfile
├── INSTALL.rst. # Alcune informazioni sull'installazione delle dipendenze dello script
├── molecule.yml # File di configurazione
├── playbook.yml # Playbook per l'esecuzione del ruolo
└── tests # Directory con i test della fase di verifica
└── test_default.py
1 directory, 6 fileAnalizziamo la configurazione molecule/default/molecule.yml (sostituiamo solo l'immagine docker):
---
dependencies:
name: galaxy
drivers:
name: docker
lint:
name: yamllint
platforms:
- name: instance
image: centos:7
provisioners:
name: ansible
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8dependency
Questa sezione descrive la fonte delle dipendenze.
Opzioni possibili: , , shell.
Shell è semplicemente una shell di comando utilizzata nel caso in cui galaxy e gilt non soddisfino le vostre esigenze.
Non mi soffermerò qui a lungo, è stato già descritto .
driver
Nome del driver. In questo caso è docker.
lint
Come linter viene utilizzato yamllint.
Opzioni utili in questa parte della configurazione sono la possibilità di indicare un file di configurazione per yamllint, passare variabili d'ambiente o disabilitare l'linter:
lint:
name: yamllint
options:
config-file: foo/bar
env:
FOO: bar
enabled: Falseplatforms
Descrive la configurazione delle istanze.
Nel caso di docker come driver, Molecule itera attraverso questa sezione, e ogni elemento della lista è accessibile in Dockerfile.j2 come variabile item.
Nel caso di un driver, dove è obbligatorio create.yml e destroy.yml, la sezione è disponibile in essi come molecule_yml.platforms, e le iterazioni su di essa sono descritte in questi file.
Poiché Molecule fornisce la gestione delle istanze ai moduli ansible, la lista delle possibili configurazioni deve essere cercata di lì. Per docker, ad esempio, è utilizzato il modulo . Quali moduli vengono utilizzati negli altri driver può essere trovato in .
E anche esempi di utilizzo di vari driver possono essere trovati .
Sostituiamo qui centos:7 in ubuntu.
provisioner
«Fornitore» — entità che gestisce le istanze. Nel caso di Molecule, si tratta di ansible, il supporto per altri non è previsto, quindi questa sezione può essere definita come una configurazione avanzata di ansible.
Qui puoi specificare molte cose; evidenzierò i principali momenti che, a mio avviso, sono importanti:
- playbook: puoi indicare 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'"- opzioni: parametri Ansible e variabili di ambiente
provisioner:
name: ansible
options:
vvv: true
diff: true
env:
FOO: BARscenario
Nome e descrizione delle sequenze dello scenario.
Puoi cambiare la matrice delle azioni predefinita di un comando aggiungendo la chiave _sequence e definendo come valore la lista di passaggi che ci interessa.
Supponiamo di voler cambiare la sequenza delle azioni durante l'esecuzione del comando di esecuzione del playbook: molecule converge
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeverificatore
Configurazione del framework per i test e del linter associato. Per impostazione predefinita, viene utilizzato come linter testinfra e flake8. Le opzioni possibili sono simili a quelle descritte sopra:
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 fino a questo formato:
---
- name: Install nginx
apt:
name: nginx
state: present
- name: Start nginx
service:
name: nginx
state: started
E aggiungiamo 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 testUn lungo output 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 su file trovati in /nginx/...
Lint completato con successo.
--> Esecuzione di Flake8 su 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] *****************************************************************
COMPITO [Distruggi le istanze di molecola] ********************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
COMPITO [Attendi il completamento della cancellazione delle istanze] *******************************
ok: [localhost] => (item=None)
ok: [localhost]
COMPITO [Elimina rete docker] ************************************************
RIEPILOGO GIOCO *********************************************************************
localhost : ok=2 cambiato=1 irraggiungibile=0 fallito=0
--> Scenario: 'default'
--> Azione: 'dependency'
Salto, mancanza del file dei requisiti.
--> Scenario: 'default'
--> Azione: 'syntax'
playbook: /nginx/molecule/default/playbook.yml
--> Scenario: 'default'
--> Azione: 'create'
GIOCO [Crea] ******************************************************************
COMPITO [Accedi a un registry Docker] **********************************************
salto: [localhost] => (item=None)
COMPITO [Crea Dockerfiles dai nomi delle immagini] *************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
COMPITO [Scopri le immagini Docker locali] ********************************************
ok: [localhost] => (item=None)
ok: [localhost]
COMPITO [Costruisci un'immagine compatibile con Ansible] ***************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
COMPITO [Crea rete docker] ************************************************
COMPITO [Crea istanze di molecola] *********************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
COMPITO [Attendi il completamento della creazione delle istanze] *******************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
RIEPILOGO GIOCO *********************************************************************
localhost : ok=5 cambiato=4 irraggiungibile=0 fallito=0
--> Scenario: 'default'
--> Azione: 'prepare'
Salto, playbook di preparazione non configurato.
--> Scenario: 'default'
--> Azione: 'converge'
GIOCO [Convergi] ****************************************************************
COMPITO [Raccolta fatti] *********************************************************
ok: [instance]
COMPITO [nginx : Installa nginx] ***************************************************
cambiato: [instance]
COMPITO [nginx : Avvia nginx] *****************************************************
cambiato: [instance]
RIEPILOGO GIOCO *********************************************************************
instance : ok=3 cambiato=2 irraggiungibile=0 fallito=0
--> Scenario: 'default'
--> Azione: 'idempotence'
Idempotenza completata con successo.
--> Scenario: 'default'
--> Azione: 'side_effect'
Salto, playbook per effetto collaterale non configurato.
--> Scenario: 'default'
--> Azione: 'verify'
--> Esecuzione dei test Testinfra trovati in /nginx/molecule/default/tests/...
============================= inizio sessione di 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
collezionati 4 elementi
tests/test_default.py .... [100%]
========================== 4 superati in 27.23 secondi ===========================
Verificatore completato con successo.
--> Scenario: 'default'
--> Azione: 'destroy'
GIOCO [Distruggi] *****************************************************************
COMPITO [Distruggi le istanze di molecola] ********************************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
COMPITO [Attendi il completamento della cancellazione delle istanze] *******************************
cambiato: [localhost] => (item=None)
cambiato: [localhost]
COMPITO [Elimina rete docker] ************************************************
RIEPILOGO GIOCO *********************************************************************
localhost : ok=2 cambiato=2 irraggiungibile=0 fallito=0
Il nostro semplice ruolo è stato testato senza problemi.
È importante ricordare che se ci sono stati problemi durante il lavoro molecule test, se non hai modificato la sequenza standard, Molecule rimuoverà 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 in test.
> molecule login # Accedi all'istanza creata.
> molecule --help # Elenco completo 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>Nel caso in cui sia il primo scenario nel ruolo, il parametro -s può essere omesso, poiché verrà creato uno scenario default.
Conclusione
Come puoi vedere, Molecule non è molto complicato e, utilizzando i propri template, è possibile ridurre il deployment di un nuovo scenario alla modifica delle variabili nei playbook di creazione e rimozione delle istanze. Molecule si integra senza problemi con i sistemi CI, il che consente di aumentare la velocità di sviluppo riducendo il tempo di test manuale dei playbook.
Grazie per la tua attenzione. Se hai esperienza nel testare ruoli ansible, che non sia correlata a Molecule, condividila nei commenti!
Fonte: habr.com
