L’auteur a sélectionné le Mozilla Foundation pour recevoir un don dans le cadre du programme Write for DOnations .
introduction
Le test des unités dans Ansible est essentiel pour garantir que les rôles fonctionnent comme prévu. Molecule facilite ce processus en vous permettant de spécifier des scénarios qui testent des rôles dans différents environnements. En utilisant Ansible sous le capot, Molecule délègue les rôles à un fournisseur qui déploie le rôle dans un environnement configuré et appelle un vérificateur (tel que Testinfra) pour vérifier la dérive de configuration. Cela garantit que votre rôle a apporté toutes les modifications attendues à l’environnement dans ce scénario particulier.
Dans ce guide, vous allez créer un rôle Ansible qui déploie Apache sur un hôte et configure Firewalld sur CentOS 7. Pour vérifier que ce rôle fonctionne comme prévu, vous créerez un test dans Molecule en utilisant Docker en tant que pilote et Testinfra, une bibliothèque Python permettant de tester l’état des serveurs. Molecule fournira les conteneurs Docker pour tester le rôle et Testinfra vérifiera que le serveur a été configuré comme prévu. Une fois que vous avez terminé, vous pourrez créer plusieurs scénarios de test pour des générations dans plusieurs environnements et exécuter ces tests à l’aide de Molecule.
Conditions préalables
Avant de commencer ce guide, vous aurez besoin des éléments suivants:
-
Un serveur Ubuntu 16.04. Suivez les étapes du guide https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04 de la configuration initiale du serveur avec Ubuntu 16.04] pour créer un utilisateur sudo non-root, et assurez-vous que vous pouvez vous connecter au serveur sans mot de passe.
-
Docker installé sur votre serveur. Suivez les étapes 1 et 2 de Comment installer et utiliser Docker sur Ubuntu 16.04 et assurez-vous d’ajouter votre utilisateur non root au groupe
+ docker +
. -
Familiarité avec les playbooks Ansible. Pour un examen, voir Configuration Management 101: Écrire des livres de lecture.
Étape 1 - Préparation de l’environnement
Pour créer notre rôle et nos tests, commençons par créer un environnement virtuel et installer Molecule. L’installation de Molecule installera également Ansible, ce qui permettra d’utiliser des playbooks pour créer des rôles et exécuter des tests.
Commencez par vous connecter en tant qu’utilisateur non root et en vous assurant que vos référentiels sont à jour:
sudo apt-get update
Cela garantira que votre référentiel de paquet inclut la dernière version du paquet + python-pip +
, qui installera + pip +
et Python 2.7. Nous allons utiliser + pip +
pour créer un environnement virtuel et installer des paquets supplémentaires. Pour installer + pip +
, exécutez:
sudo apt-get install -y python-pip
Utilisez + pip +
pour installer le module + virtualenv +
Python:
python -m pip install virtualenv
Ensuite, créons et activons l’environnement virtuel:
python -m virtualenv
Activez-le pour vous assurer que vos actions sont limitées à cet environnement:
source my_env/bin/activate
Installez + molécule
et` + docker` en utilisant + pip
:
python -m pip install molecule docker
Voici ce que chacun de ces paquets va faire:
-
+ molecule +
: Ceci est le paquetage principal de la molécule que vous utiliserez pour tester les rôles. L’installation de+ molecule +
installe automatiquement Ansible, ainsi que d’autres dépendances, et permet d’utiliser les playbooks Ansible pour exécuter des rôles et des tests. -
+ docker +
: Cette bibliothèque Python est utilisée par Molecule pour s’interfacer avec Docker. Vous en aurez besoin puisque vous utilisez Docker en tant que pilote.
Ensuite, créons un rôle dans Molecule.
Étape 2 - Création d’un rôle dans la molécule
Avec votre environnement configuré, vous pouvez utiliser Molecule pour créer un rôle de base afin de tester une installation d’Apache. Ce rôle créera la structure de répertoires et certains tests initiaux, et spécifiera Docker en tant que pilote afin que Molecule utilise Docker pour exécuter ses tests.
Créez un nouveau rôle appelé + ansible-apache
:
molecule init role -r ansible-apache -d docker
L’indicateur + -r +
spécifie le nom du rôle, tandis que + -d +
spécifie le pilote qui fournit les hôtes à Molecule à utiliser lors des tests.
Accédez au répertoire du rôle nouvellement créé:
cd ansible-apache
Testez le rôle par défaut pour vérifier si Molecule a été correctement configuré:
molecule test
Vous verrez une sortie qui répertorie chacune des actions de test par défaut:
Output--> Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
...
Avant de commencer le test, Molecule valide le fichier de configuration + molecule.yml +
pour s’assurer que tout est en ordre. Il imprime également cette matrice de test, qui spécifie l’ordre des actions de test.
Nous discuterons de chaque action de test en détail une fois que vous avez créé votre rôle et personnalisé vos tests. Pour l’instant, faites attention à + PLAY_RECAP +
pour chaque test et assurez-vous qu’aucune des actions par défaut ne renvoie un statut + échec +
. Par exemple, l’action "+ PLAY_RECAP " pour l'action "" create '+ `par défaut devrait ressembler à ceci:
Output...
PLAY RECAP *********************************************************************
localhost : ok=5 changed=4 unreachable=0 failed=0
Passons maintenant à la modification du rôle pour configurer Apache et Firewalld.
Étape 3 - Configuration d’Apache et Firewalld
Pour configurer Apache et Firewalld, vous allez créer un fichier de tâches pour le rôle, en spécifiant les packages à installer et les services à activer. Ces détails seront extraits d’un fichier de variables et d’un modèle que vous utiliserez pour remplacer la page d’index Apache par défaut.
Créez un fichier de tâches pour le rôle en utilisant + nano +
ou votre éditeur de texte préféré:
nano tasks/main.yml
Vous verrez que le fichier existe déjà. Supprimez ce qui est là et collez le code suivant pour installer les packages requis et activer les services, valeurs par défaut HTML et paramètres de pare-feu appropriés:
~ / ansible-apache / tasks / main.yml
---
- name: "Ensure required packages are present"
yum:
name: "{{ pkg_list }}"
state: present
- name: "Ensure latest index.html is present"
template:
src: index.html.j2
dest: /var/www/html/index.html
- name: "Ensure httpd service is started and enabled"
service:
name: "{{ item }}"
state: started
enabled: true
with_items: "{{ svc_list }}"
- name: "Whitelist http in firewalld"
firewalld:
service: http
state: enabled
permanent: true
immediate: true
Ce cahier comprend 4 tâches:
-
" Assurez-vous que les paquetages requis sont présents "
: Cette tâche installera les paquetages listés dans le fichier de variables sous+ pkg_list +
. Le fichier de variables sera situé dans+ ~ / ansible-apache / vars / main.yml +
et vous le créerez à la fin de cette étape. -
" S'assurer que le dernier index.html est présent "
: cette tâche copiera une page de modèle+ index.html.j2 +
et le collera sur le fichier d’index par défaut, `+ / var / www / html / index.html + `, généré par Apache. Vous créerez également le modèle à cette étape. -
" S'assurer que le service httpd est démarré et activé "
: Cette tâche va démarrer et activer les services listés dans+ svc_list +
dans le fichier de variables. -
" Liste blanche http dans firewalld "
: cette tâche ajoutera la liste blanche du service+ http +
dans+ firewalld +
. Firewalld est une solution de pare-feu complète présente par défaut sur les serveurs CentOS. Pour que le service+ http +
fonctionne, vous devez exposer les ports requis. En ordonnant à+ firewalld +
de mettre un service en liste blanche, il s’assure qu’il enregistre en liste blanche tous les ports requis par le service.
Enregistrez et fermez le fichier lorsque vous avez terminé.
Ensuite, créons un répertoire + templates +
pour la page de modèle + index.html.j2 +
:
mkdir templates
Créez la page elle-même:
nano templates/index.html.j2
Collez le code standard suivant:
~ / ansible-apache / templates / index.html.j2
<div style="text-align: center">
<h2>Managed by Ansible</h2>
</div>
Enregistrez et fermez le fichier.
La dernière étape de la réalisation du rôle consiste à écrire le fichier de variables, qui fournit les noms des packages et des services à votre livre de rôle principal:
nano vars/main.yml
Collez le contenu par défaut avec le code suivant, qui spécifie + pkg_list +
et + svc_list +
:
~ / ansible-apache / vars / main.yml
---
pkg_list:
- httpd
- firewalld
svc_list:
- httpd
- firewalld
Ces listes contiennent les informations suivantes:
-
+ pkg_list +
: contient les noms des packages que le rôle va installer:+ httpd +
et+ firewalld +
. -
+ svc_list +
: contient les noms des services que le rôle va démarrer et activer:+ httpd +
et+ firewalld +
.
Maintenant que vous avez terminé de créer le rôle, configurons Molecule pour vérifier s’il fonctionne comme prévu.
Étape 4 - Modification du rôle pour l’exécution de tests
Dans notre cas, la configuration de Molecule implique la modification du fichier de configuration de la molécule + molecule.yml +
pour ajouter les spécifications de la plate-forme. Comme vous testez un rôle qui configure et lance le service systemd + httpd +
, vous devrez utiliser une image avec le mode configuré et privilégié systemd activé. Pour ce tutoriel, vous utiliserez l’image + milcom / centos7-systemd +
available sur le Docker Hub. Le mode privilégié permet aux conteneurs de s’exécuter avec presque toutes les fonctionnalités de leur ordinateur hôte.
Éditons + molecule.yml +
pour refléter ces changements:
nano molecule/default/molecule.yml
Ajoutez les informations de la plateforme en surbrillance:
~ / ansible-apache / molecule / default / molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
platforms:
provisioner:
name: ansible
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8
Enregistrez et fermez le fichier lorsque vous avez terminé.
Maintenant que vous avez correctement configuré l’environnement de test, passons à l’écriture des scénarios de test que Molecule exécutera sur votre conteneur après l’exécution du rôle.
Étape 5 - Écrire des cas de test
Lors du test de ce rôle, vous vérifierez les conditions suivantes:
-
Les packages
+ httpd +
et+ firewalld +
sont installés. -
Que les services
+ httpd +
et+ firewalld +
sont en cours d’exécution et activés. -
Que le service
+ http +
est activé dans les paramètres de votre pare-feu. -
Ce
+ index.html +
contient les mêmes données que celles spécifiées dans votre fichier de modèle.
Si tous ces tests réussissent, le rôle fonctionne comme prévu.
Pour écrire les scénarios de test pour ces conditions, éditons les tests par défaut dans + ~ / ansible-apache / molecule / default / tests / test_default.py +
. À l’aide de Testinfra, vous écrivez les scénarios de test sous forme de fonctions Python utilisant des classes Molecule.
Ouvrez + test_default.py +
:
nano molecule/default/tests/test_default.py
Supprimez le contenu du fichier pour pouvoir écrire les tests à partir de zéro.
Commencez par importer les modules Python requis:
~ / ansible-apache / molecule / default / tests / test_default.py
import os
import pytest
import testinfra.utils.ansible_runner
Ces modules comprennent:
-
+ os +
: ce module Python intégré active la fonctionnalité dépendant du système d’exploitation, permettant à Python de se connecter au système d’exploitation sous-jacent. -
+ pytest +
: le module https://docs.pytest.org/en/latest/ [+ pytest +
]] permet l’écriture de test. -
+ testinfra.utils.ansible_runner +
: ce module Testinfra utilise Ansible comme serveur principal pour l’exécution de la commande.
Sous les importations du module, collez le code suivant, qui utilise le moteur Ansible pour renvoyer l’instance d’hôte actuelle:
~ / ansible-apache / molecule / default / tests / test_default.py
...
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
Votre fichier de test étant configuré pour utiliser le backend Ansible, laissez-nous écrire des tests unitaires pour tester l’état de l’hôte.
Le premier test s’assurera que + httpd +
et + firewalld +
sont installés:
~ / ansible-apache / molecule / default / tests / test_default.py
...
@pytest.mark.parametrize('pkg', [
'httpd',
'firewalld'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
Le test commence par les https://docs.pytest.org/fr/latest/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions [+ pytest.mark.parametrize +
décorateur], qui nous permet de paramétrer les arguments pour le test. Ce premier test utilisera + test_pkg +
comme paramètre pour tester la présence des paquets + httpd +
et + firewalld +
.
Le test suivant vérifie si + httpd +
et + firewalld +
sont en cours d’exécution et activés. Il prend + test_svc +
en paramètre:
~ / ansible-apache / molecule / default / tests / test_default.py
...
@pytest.mark.parametrize('svc', [
'httpd',
'firewalld'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
Le dernier test vérifie que les fichiers et contenus passés à + parametrize () +
existent. Si le fichier n’est pas créé par votre rôle et que le contenu n’est pas défini correctement, + assert +
retournera + False +
:
~ / ansible-apache / molecule / default / tests / test_default.py
...
@pytest.mark.parametrize('file, content', [
("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
file = host.file(file)
assert file.exists
assert file.contains(content)
Dans chaque test, + assert
renverra` + True` ou + False
en fonction du résultat du test.
Le fichier fini ressemble à ceci:
~ / ansible-apache / molecule / default / tests / test_default.py
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
@pytest.mark.parametrize('pkg', [
'httpd',
'firewalld'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
@pytest.mark.parametrize('svc', [
'httpd',
'firewalld'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
@pytest.mark.parametrize('file, content', [
("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
file = host.file(file)
assert file.exists
assert file.contains(content)
Maintenant que vous avez spécifié vos scénarios de test, testons le rôle.
Étape 6 - Test du rôle avec la molécule
Une fois le test lancé, Molecule exécutera les actions que vous avez définies dans votre scénario. Lançons à nouveau le scénario + molécule +
par défaut, en exécutant les actions de la séquence de test par défaut tout en les examinant de plus près.
Exécutez à nouveau le test pour le scénario par défaut:
molecule test
Cela lancera le test. La sortie initiale imprime la matrice de test par défaut:
Output--> Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
Passons en revue chaque action de test et le résultat attendu, en commençant par le peluchage.
L’action linting a exécuté + yamllint
,` + flake 8` et + ansible-lint
:
-
+ yamllint +
: Ce linter est exécuté sur tous les fichiers YAML présents dans le répertoire du rôle. -
+ flake8 +
: Ce code Python vérifie les tests créés pour Testinfra. -
+ ansible-lint +
: Ce linter pour les playbooks Ansible est exécuté dans tous les scénarios.
Output...
--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /home/sammy/ansible-apache/...
Lint completed successfully.
--> Executing Flake8 on files found in /home/sammy/ansible-apache/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on /home/sammy/ansible-apache/molecule/default/playbook.yml...
Lint completed successfully.
L’action suivante, destroy, est exécutée à l’aide du fichier + destroy.yml +
. Ceci est fait pour tester votre rôle sur un conteneur nouvellement créé.
Par défaut, destroy est appelé deux fois: au début du test, pour supprimer tout conteneur préexistant, et à la fin, pour supprimer le conteneur nouvellement créé:
Output...
--> Scenario: 'default'
--> Action: 'destroy'
PLAY [Destroy] *****************************************************************
TASK [Destroy molecule instance(s)] ********************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Wait for instance(s) deletion to complete] *******************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Delete docker network(s)] ************************************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
Une fois l’action de destruction terminée, le test passera à dependency. Cette action vous permet d’extraire des dépendances à partir de https://galaxy.ansible.com/ [+ ansible-galaxy +
] si votre rôle le requiert. Dans ce cas, le rôle ne:
Output...
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
L’action de test suivante est une vérification syntax, qui est exécutée sur le playbook par défaut + playbook.yml +
. Cela fonctionne de la même manière que l’indicateur + - syntax-check +
dans la commande + ansible-playbook --syntax-check playbook.yml +
:
Output...
--> Scenario: 'default'
--> Action: 'syntax'
playbook: /home/sammy/ansible-apache/molecule/default/playbook.yml
Ensuite, le test passe à l’action create. Ceci utilise le fichier + create.yml +
dans le répertoire Molecule de votre rôle pour créer un conteneur Docker avec vos spécifications:
Output...
--> Scenario: 'default'
--> Action: 'create'
PLAY [Create] ******************************************************************
TASK [Log into a Docker registry] **********************************************
skipping: [localhost] => (item=None)
skipping: [localhost]
TASK [Create Dockerfiles from image names] *************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Discover local Docker images] ********************************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Build an Ansible compatible image] ***************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Create docker network(s)] ************************************************
skipping: [localhost]
TASK [Create molecule instance(s)] *********************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Wait for instance(s) creation to complete] *******************************
changed: [localhost] => (item=None)
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=5 changed=4 unreachable=0 failed=0
Après la création, le test passe à l’action prepare. Cette action exécute le livre de préparation, ce qui place l’hôte dans un état spécifique avant de lancer la convergence. Ceci est utile si votre rôle nécessite une pré-configuration du système avant son exécution. Encore une fois, cela ne s’applique pas à notre rôle:
Output...
--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
Après la préparation, l’action converge exécute votre rôle sur le conteneur en exécutant le playbook + playbook.yml +
. Si plusieurs plates-formes sont configurées dans le fichier + molecule.yml +
, Molecule convergera vers tous ceux-ci:
Output...
--> Scenario: 'default'
--> Action: 'converge'
PLAY [Converge] ****************************************************************
TASK [Gathering Facts] *********************************************************
ok: [centos7]
TASK [ansible-apache : Ensure required packages are present] *******************
changed: [centos7]
TASK [ansible-apache : Ensure latest index.html is present] ********************
changed: [centos7]
TASK [ansible-apache : Ensure httpd service is started and enabled] ************
changed: [centos7] => (item=httpd)
changed: [centos7] => (item=firewalld)
TASK [ansible-apache : Whitelist http in firewalld] ****************************
changed: [centos7]
PLAY RECAP *********************************************************************
centos7 : ok=5 changed=4 unreachable=0 failed=0
Après la couverture, le test passe à idempotence. Cette action teste le livret pour idempotence afin de s’assurer qu’aucune modification inattendue n’est apportée dans plusieurs exécutions:
Output...
--> Scenario: 'default'
--> Action: 'idempotence'
Idempotence completed successfully.
L’action de test suivante est l’action side-effect. Cela vous permet de créer des situations dans lesquelles vous pourrez tester plus de choses, comme le basculement haute disponibilité. Par défaut, Molecule ne configure pas de livre de lecture d’effets secondaires et la tâche est ignorée:
Output...
--> Scenario: 'default'
--> Action: 'side_effect'
Skipping, side effect playbook not configured.
Molecule exécutera ensuite l’action verifier à l’aide du vérificateur par défaut, Testinfra. Cette action exécute les tests que vous avez écrits précédemment dans + test_default.py +
. Si tous les tests réussissent, vous verrez un message de réussite et Molecule passera à l’étape suivante:
Output...
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /home/sammy/ansible-apache/molecule/default/tests/...
============================= test session starts ==============================
platform linux2 -- Python 2.7.12, pytest-3.8.0, py-1.6.0, pluggy-0.7.1
rootdir: /home/sammy/ansible-apache/molecule/default, inifile:
plugins: testinfra-1.14.1
collected 6 items
tests/test_default.py ...... [100%]
========================== 6 passed in 56.73 seconds ===========================
Enfin, Molecule destroys les instances terminées lors du test et supprime le réseau affecté à ces instances:
Output...
--> Scenario: 'default'
--> Action: 'destroy'
PLAY [Destroy] *****************************************************************
TASK [Destroy molecule instance(s)] ********************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Wait for instance(s) deletion to complete] *******************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Delete docker network(s)] ************************************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=2 unreachable=0 failed=0
Les actions de test sont maintenant terminées et vérifient que votre rôle a fonctionné comme prévu.
Conclusion
Dans cet article, vous avez créé un rôle Ansible pour installer et configurer Apache et Firewalld. Vous avez ensuite écrit avec Testinfra des tests unitaires que Molecule utilisait pour affirmer que le rôle avait été exécuté avec succès.
Vous pouvez utiliser la même méthode de base pour des rôles très complexes et automatiser les tests à l’aide d’un pipeline de CI également. Molecule est un outil hautement configurable qui peut être utilisé pour tester des rôles avec n’importe quel fournisseur pris en charge par Ansible, pas seulement Docker. Il est également possible d’automatiser les tests sur votre propre infrastructure, en veillant à ce que vos rôles soient toujours à jour et fonctionnels. La documentation officielle Molecule est la meilleure ressource pour apprendre à utiliser Molecule.