Molecule

Cet article vise à présenter l’utilisation de l’outil Molecule pour tester et valider les playbooks Ansible.

Public visé : personnes connaissant ansible, vagrant et docker pour les environnements de test.

Par Sandrine Perrin, Consultante Cloud Open Source

Automatisation de tests pour les playbook Ansible avec Molecule

Introduction

Ansible est un outil permettant d’automatiser l’installation, le déploiement et la gestion de serveurs.
Il fonctionne sur le mode déclaratif grâce à des modules écrits en python. Chaque module doit s’assurer que l’existant correspond à la demande, son exécution renvoie en résultat si l’action s’est terminée par un échec ou un succès.

Cependant, il reste important de lancer des tests pour s’assurer qu’à la fin de l’exécution du playbook, le serveur est conforme à l’état attendu en terme de fichiers présents, processus, configuration du firewall…

Molecule est un des outils qui permet de réaliser ces tests.

C’est un outil écrit en python2, il appartient au projet Ansible. La version 2 sortie en 2017 apporte beaucoup de nouvelles fonctionnalités très intéressantes, l’outil est bien maintenu avec une nouvelle version par mois. Il s’installe simplement avec pip.

Installation

Exemple d’installation sous Ubuntu 16.04

$ sudo apt install -y python-pip libssl-dev
$ pip install molecule
$ molecule --version
molecule, version 2.19.0

Fonctionnement

Molecule gère trois opérations :

  1. le driver crée un environnement de test via des drivers ;
  2. le provisionner lance le playbook à tester dans cet environnement ;
  3. le verifier exécute des tests.

La liste des drivers supportés s’est enrichie avec la version 2. Il est possible d’instancier des machines virtuelles avec Vagrant, OpenStack, ou des conteneurs avec Docker ou LXC. Ces environnements sont déployés grâce à des playbooks modifiables à souhait.

Les tests peuvent être décrits avec plusieurs outils ; Molecule supporte les outils suivants :

  • testinfra : solution par défaut, les tests sont écrits en python ;
  • InSpec : framework de test de Chef ;
  • goss : cette solution sera détaillée dans la suite de l’article (nouveauté de la version 2).

En plus de l’installation de Molecule, il faut installer les drivers et outils de tests à utiliser ; dans notre exemple docker, vagrant, goss, yamllint, et ansible évidemment.

$ sudo apt install yamllint virtualbox apt-transport-https ca-certificates curl software-properties-common
$ pip install ansible==2.7.0
$ curl -fsSL https://goss.rocks/install | sudo GOSS_DST=/usr/local/sbin sh
$ wget https://releases.hashicorp.com/vagrant/2.1.2/vagrant_2.1.2_x86_64.deb
$ sudo dpkg -i vagrant_2.1.2_x86_64.deb
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt update
$ sudo apt install docker-ce
$ pip install python-vagrant
$ pip install docker-py

Utilisation

Molecule doit être inclus dans le rôle à tester. Il est possible :

  • soit de créer un nouveau rôle avec Molecule :
    $ mkdir -p /path/to/roles/
    $ cd /path/to/roles
    $ molecule init role --role-name myrole --verifier-name goss --driver-name docker
    
  • soit de l’ajouter à un rôle existant :
    $ cd /path/to/roles/myrole
    $ molecule init scenario --role-name myrole --verifier-name goss --driver-name vagrant --scenario-name vagrant
    

Dans notre rôle, un nouveau dossier Molecule est présent, comprenant la description de deux scénarios (default et vagrant) :

/path/to/roles/my_role
├── defaults
│   └── main.yml
├── handlers
│   └── main.yml
├── molecule
│   ├── default
│   │   ├── Dockerfile.j2
│   │   ├── INSTALL.rst
│   │   ├── molecule.yml
│   │   ├── playbook.yml
│   │   ├── tests
│   │   │   ├── test_default.yml
│   │   └── verify.yml
│   ├── vagrant [...]
├── README.md
├── tasks
│   ├── main.yml
└── vars
    └── main.yml

Il est possible de définir plusieurs scénarios, mais le rôle default doit exister.

Contenu du dossier

Le fichier molecule.yml configure le scénario à jouer.

Exemple avec le driver vagrant et le verifier goss :

---
dependency:
  name: galaxy
driver:
  name: vagrant
  provider:
    name: virtualbox
lint:
  name: yamllint
platforms:
  - name: instance
    box: ubuntu/xenial64
provisioner:
  name: ansible
lint:
  name: ansible-lint
scenario:
  name: default
verifier:
  name: goss
  lint:
  name: yamllint

La partie provisioner configure l’outil pour exécuter le scénario ; actuellement seul Ansible est supporté, aucun autre provisioner n’est dans la roadmap.

La partie driver permet de configurer le driver choisi. Dans notre exemple, les machines virtuelles à déployer sont définies dans la partie platforms qui liste les instances à créer pour notre environnement de test. Molecule va créer plusieurs machines virtuelles ou conteneurs configurés en fonction des besoins. Il est possible de détailler la configuration des instances à lancer [cf. lien 3 en fin d’article].

La partie lint permet de définir le validateur à utiliser ; seul yamllint est actuellement supporté.

La partie tests est ici supportée par goss. Cette partie sera détaillée plus loin.

La partie scenario permet de définir le déroulement des tests ; il existe un déroulement par défaut, qu’il est possible de surcharger. Le déroulement par défaut comprend 5 séquences : create, check, converge, destroy, test, avec pour chacune une succession d’étapes.

Exemple : séquence check comprenant par défaut les étapes suivantes :

check_sequence:
- destroy
- create
- prepare
- converge
- check
- destroy

A chaque sous-étape correspond un playbook. Certains sont présents dans le dossier du scénario. Le fichier create.yml correspond à l’étape create qui va utiliser le driver défini pour créer les instances décrites.

Le fichier playbook.yml correspond à l’étape converge, il exécute le code ansible à tester.

Comment lancer Molecule ?

Liste des scénarios et des instances définis :

$ molecule list

Lancement du scénario par défaut, pour la séquence check :

$ molecule check

Lancement du scénario vagrant, pour la séquence check :

$ molecule check -s vagrant

La séquence test par convention réalise la chaîne complète. Par défaut,Molecule réalise un destroy à la fin, mais il est possible de désactiver ce comportement pour simplifier l’analyse des tests en échec.

$ molecule test --help
Usage: molecule test [OPTIONS]

Test (lint, destroy, dependency, syntax, create, prepare, converge, idempotence, side_effect, verify, destroy).

Options:
-s, --scenario-name TEXT Name of the scenario to target. (default)
-d, --driver-name [azure|delegated|docker|ec2|gce|lxc|lxd|openstack|vagrant] Name of driver to use. (docker)
--all / --no-all Test all scenarios. Default is False.
--destroy [always|never] The destroy strategy used at the conclusion of a Molecule run (always).
--help Show this message and exit.

Comment se définissent les tests ?

C’est la partie la plus chronophage.

Dans notre exemple, goss a été choisi ; il permet de créer rapidement une série de tests d’infrastructure. Vous pouvez vous référer à la documentation pour la présentation [cf. lien 4 en fin d’article].

Les tests sont décrits dans des fichiers yaml. Un exemple est donné à la création d’un scénario dans :

/path/to/role/molecule/default/test/test_default.yml

Il est possible d’ajouter autant de fichiers que nécessaire dans ce dossier, en respectant la convention de nommage suivante (pas d’extension .yaml) : test_${name}.yml

Exemple de fichier de test pour l’installation de Jenkins test_jenkins.yml :

---
file:
  /var/lib/jenkins:
    exists: true
    mode: "0755"
    owner: jenkins
    group: jenkins
    filetype: directory
    contains: []
package:
  python2-pip:
    installed: true
  java-1.8.0-openjdk:
    installed: true
    versions:
    - 1.8.0.191.b12
port:
  tcp6:8080:
    listening: true
    ip:
      - '::'
service:
  jenkins:
    enabled: true
    running: true
user:
  jenkins:
    exists: true
    groups:
      - jenkins
    home: /var/lib/jenkins
    shell: /bin/bash
process:
  java:
    running: true

Il est possible de partager les tests entre plusieurs scénarios.

Les tests sont lancés avec la séquence verify, c’est le playbook verify.yml qui est exécuté.

Exemple de tests communs entre deux scénarios: default avec Docker et vagrant avec Vagrant :

molecule/
├── default
│   ├── Dockerfile.j2
│   ├── INSTALL.rst
│   ├── molecule.yml
│   ├── playbook.yml
│   └── verify.yml
├── resources
│   └── tests
│       ├── test_default.yml
│       └── test_jenkins.yml
└── vagrant
    ├── create.yml
    ├── destroy.yml
    ├── INSTALL.rst
    ├── molecule.yml
    ├── playbook.yml
    ├── prepare.yml
    └── verify.yml

Exemple d’un fichier molecule.yml pour le scénario default :

---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
  # Ajout d'un fichier de configuration pour yamllint
  options:
    config-file: $HOME/yamllint-relaxed.conf
  enabled: true
# Description de 2 instances : centos et ubuntu
platforms:
  - name: inst-centos
    image: couchbase/centos7-systemd
    command: "/usr/sbin/init"
    privileged: true
  - name: inst-ub
    image: ubuntu:16.04
    command: "/usr/sbin/init"
    privileged: true
provisioner:
  name: ansible
lint:
  name: ansible-lint
# Redéfinition de la séquence test
scenario:
  name: default
  test_sequence:
  - lint
  - destroy
  - dependency
  - syntax
  - create
  - prepare
  - converge
  - verify
# Spécifier le dossier de test
verifier:
  name: goss
  directory: ../resources/tests
  lint:
    name: yamllint

Utilisation dans une chaîne d’intégration continue

Maintenant que nos tests Molecule sont intégrés à notre rôle et que les jeux de tests sont définis, il ne reste plus qu’à automatiser leur lancement.
Molecule peut être intégré aux principaux outils d’intégration continue tel que Jenkins, gitlab-ci ou travis.

Nous allons voir ici comment créer un job pipeline dans Jenkins (pas de plugin Jenkins pour Molecule) [cf. lien 5 en fin d’article].

Exemple de fichier Jenkinsfile :

node() {
  stage ("Executing Molecule destroy") {
    sh 'molecule destroy'
  }
  stage ("Executing Molecule lint") {
    sh 'molecule lint'
  }
  stage ("Executing Molecule create") {
    sh 'molecule create'
  }
  stage ("Executing Molecule converge") {
    sh 'molecule converge'
  }
  stage ("Executing Molecule idemotence") {
    sh 'molecule idempotence'
  }
  stage ("Executing Molecule verify") {
    sh 'molecule verify'
  }
}

Un regret est l’absence de moyen simple pour consulter les résultats des tests : il faut aller consulter les logs du job.

Conclusion

Molecule est un outil intéressant car il permet d’automatiser l’orchestration des tâches de test, facile à mettre en place pour un déploiement sur un serveur.

Pour des cas plus complexes (utiliser plusieurs serveurs dans un playbook, avoir des tests dépendant de la distribution…), la configuration devient plus compliquée et demande de modifier les playbooks Ansible fournis par défaut par Molecule.

Aller plus loin

[1] Documentation Molecule : https://molecule.readthedocs.io/en/latest/index.html
[2] Code : https://github.com/metacloud/molecule/
[3] Molecule, les drivers : https://molecule.readthedocs.io/en/latest/configuration.html#driver
[4] Documentation goss : https://github.com/aelsabbahy/goss/blob/master/docs/manual.md
[5] Jenkins, job pipeline : https://jenkins.io/solutions/pipeline/