Aller au contenu principal

Développer et installer des collections

Introduit avec la version 2.8 d'Ansible les collections permettent de répondre à la problématique de distribution des contenus Ansible. En effet, les collections vont permettre d'installer en une seule opération modules, rôles, plugins et même les playbooks Ansible.

Rappel : depuis la version 3.0 ansible est distribué différemment. En effet, il est composé de plusieurs packages python dont ansible-base. Ansible-base embarque les programmes de base d'Ansible que sont ansible-playbook, ansible-galaxy, ansible-doc, etc. et quelques modules et de la documentation.

Le reste des modules et plugins ont été déplacés dans diverses "collections" dont la plus importante est la community.general.

Pour s'y retrouver dans les collections, il faut se rendre sur cette page.

Installation de collections Ansible

Le premier moyen pour installer une collection est d'utiliser la commande ansible-galaxy. En effet, depuis la version 2.8 d'Ansible cette commande possède une option permettant de gérer les collections.

ansible-galaxy collection install community.general

On peut également utiliser un fichier tar.gz. Mais ce sera surtout pour gérer vos propres collections.

ansible-galaxy collection install my_namespace-my_collection-1.0.0.tar.gz

Bon c'est bien avec une ou deux collections, mais pour un nombre important il faut plutôt utiliser un fichier requirement.yml

---
roles:
  # Install a role from Ansible Galaxy.
  - name: geerlingguy.java
    version: 1.9.6

collections:
  # Install a collection from Ansible Galaxy.
  - name: geerlingguy.php_roles
    version: 0.9.3
    source: https://galaxy.ansible.com

Développement de collections Ansible

Comme dit plus haut une collection est un simple format de distribution de contenu utilisé par Ansible Galaxy. Ici, c'est une archive compressée qui contient cette arborescence :

collection/
├── docs/
├── galaxy.yml
├── plugins/
│   ├── modules/
│   │   └── module1.py
│   ├── inventory/
│   └── .../
├── README.md
├── roles/
│   ├── role1/
│   ├── role2/
│   └── .../
├── playbooks/
│   ├── files/
│   ├── vars/
│   ├── templates/
│   └── tasks/
└── tests/

On y retrouve :

  • Un fichier galaxy.yml contenant les informations nécessaires pour créer cette collection.
  • Un répertoire où est stockée la documentation des rôles et des plugins.
  • Des répertoires rôles, playbooks, plugins et tests. Dans le répertoire plugins, il faut regrouper les plugins par type.

Cette structure est créée avec la commande suivante

 ansible-galaxy collection init step.test

Il faut juste créer le répertoire playbooks. Dans le répertoire plugins, on retrouve un fichier README.md qui contient la liste de tous les types de plugins ou il faudra créer un répertoire.

Pour créer des rôles, il suffit de se mettre dans le répertoire source et de lancer la commande classique de création de rôles ansible.

cd steph/test/roles
ansible-galaxy role init premierrole

Dans les taches du rôle ajoutons un simple debug :

---
- name: Debug test.
  debug: msg="Mon premier role"

Build de notre collection

Cela se fait assez simplement. Depuis le répertoire de votre collection tapez la commande suivante :

ansible-galaxy collection build
Created collection for stephane.test_collection at /home/vagrant/Projets/perso/stephane/test_collection/stephane-test_collection-1.0.0.tar.gz

Utilisons notre collection

Créer un autre projet :

mkdir ~/Projets/test
ansible-galaxy collection install /home/vagrant/Projets/perso/stephane/test_collection/stephane-test_collection-1.0.0.tar.gz -p ./collections
[WARNING]: The specified collections path '/home/vagrant/Projets/perso/test/collections' is not part of the configured Ansible collections paths
'/home/vagrant/.ansible/collections:/usr/share/ansible/collections'. The installed collection won't be picked up in an Ansible run.

Comme vous pouvez le lire nous devons indiquer à Ansible ou se trouve notre collection. Pour ça il existe 2 moyens :

  • dans le fichier ansible.cfg dans la section [defaults] en ajoutant le paramètre collections_paths
  • avec la variable d’environnement ANSIBLE_COLLECTIONS_PATHS
ANSIBLE_COLLECTIONS_PATHS=

Créer un simple playbook utilisant votre collection

- hosts: localhost
  gather_facts: true
  tasks:
      - import_role:
          name: stephane.test_collection.premierrole

Vous remarquez qu'il vous faut indiquer le chemin du rôle dans la collection. Lançons-le :

ansible-playbook -c local playbook.yaml

PLAY [localhost] ***

TASK [Gathering Facts] ***
ok: [localhost]

TASK [stephane.test_collection.premierrole : debug] ***
ok: [localhost] => {
    "msg": "OracleLinux"
}

PLAY RECAP ******
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Ajouter des tests à votre collection

Depuis la version 2.9 d'Ansible, Ansible est livré avec un utilitaire se nommant ansible-test. Cet utilitaire permet d'ajouter des tests à vos collections qui peuvent être :

  • des tests unitaires sur vos propres développements de modules et de plugins (ils doivent intégrer des tests au format unittest ou pytest)
  • des tests d'intégration
  • des tests syntaxique sanity test

Ansible-test utilise docker pour créer l'environnement de test. On peut utiliser les images suivantes qui contiennent python3 :

  • centos8
  • fedora32
  • opensuse15
  • ubuntu1804

Comment lancer les tests syntaxique dans une collection Ansible

Ces premiers tests ne nécessitent aucune action de notre part. Lançons les tests en prenant une centos8.

ansible-test sanity --docker centos8

...

ERROR: Found 3 yamllint issue(s) which need to be resolved:
ERROR: galaxy.yml:62:1: empty-lines: too many blank lines (1 > 0)
ERROR: roles/premierrole/tasks/main.yml:4:1: empty-lines: too many blank lines (1 > 0)
ERROR: tests/integration/targets/premierrole/tasks/main.yml:3:1: empty-lines: too many blank lines (1 > 0)

ERROR: The 3 sanity test(s) listed below (out of 45) failed. See error output above for details.
future-import-boilerplate
metaclass-boilerplate
pep8

Reste plus qu'à corriger et à relancer.

Pour retrouver la liste des tests actifs (33) :

ansible-test sanity --list-tests
action-plugin-docs
ansible-doc
...
yamllint

Surpris de ne pas retrouver ansible-lint dans cette liste.

Comment ajouter des tests unitaires à notre collection ansible

Prenons l'exemple du développement d'un plugin de filtre Ansible. Il faut écrire le plugin de filtre dans le répertoire plugins/filter_plugins/ de la collection.

└── tests
    └── unit
        └── plugins
            └── filter
                ├── __pycache__
                │   └── test_snaketocamel.cpython-38.pyc
                └── test_snaketocamel.py

Reprenons l'exemple du filtre du billet dédié à ce sujet.

from ansible.errors import AnsibleError
import re

try:
  import six
  HAS_LIB = True
except ImportError:
  HAS_LIB = False

def snake_to_camel(text, suffix=''):
  if not HAS_LIB:
    raise AnsibleError('You need to install "six" prior to running '
                          'snaketocamel filter')
  import re
  return suffix + ''.join(x.title() or '_' for x in text.split('_'))

class FilterModule(object):

  def filters(self):
    return {
      'snaketocamel': snake_to_camel
    }

Dans le répertoire de la collection ajouter un répertoire tests/unit/plugins/filter et déposez-y ce fichier test_snaketocamel.py:

import unittest
from ansible_collections.steph.test.plugins.filter_plugins.snaketocamel import snake_to_camel


class TestMyFilter(unittest.TestCase):

    def test_snaketocamel(self):
        self.assertEqual(snake_to_camel("une_variable"), 'UneVariable')

Maintenant, il suffit de lancer ansible-test depuis le répertoire de la collection :

ansible-test unit --docker ubuntu1804
...

Unit test with Python 3.6
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: /root/ansible_collections/steph/test/tests/unit/plugins/filter, configfile: ../../../../../../../ansible/test/lib/ansible_test/_data/pytest.ini
plugins: forked-1.3.0, mock-3.6.0, xdist-2.2.1
gw0 I / gw1 I / gw2 I / gw3 I
gw0 [1] / gw1 [1] / gw2 [1] / gw3 [1]

.                                                                        [100%]
- generated xml file: /root/ansible_collections/steph/test/tests/output/junit/python3.6-units.xml -
============================== 1 passed in 0.79s ===============================

Comment ajouter des tests d'intégration à notre collection ansible

Les tests d'intégration sont écrits sous la forme de rôles Ansible.

Dans le répertoire tests, il faut créer la structure suivante integration/targets/premierrole/tasks dans le répertoire tests de la collection.

└── tests
    ├── integration
    │   └── targets
    │       └── premierrole
    │           └── tasks
    │               └── main.yml

Ajoutons simplement l'import du rôle premierrole :

- import_role:
  name: stephane.test_collection.premierrole

Lançons le test d'intégration :

ansible-test integration --docker ubuntu1804

PLAY [testhost] ****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [testhost]

TASK [steph.test.premierrole : Debug test.] ************************************
ok: [testhost] => {
    "msg": "Mon premier role"
}

PLAY RECAP *********************************************************************
testhost                   : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

On peut donc désormais valider notre collection sans installer quoique ce soit, mis à part docker, avec ansible-test. On peut aussi lancer des tests sur un environnement cloud.

Je suis sûr que ce produit va encore beaucoup évoluer.

Plus loin avec les collections Ansible

Depuis l'écriture de ce billet beaucoup de lignes de codes sont passées et le développement de ces collections a bien évolué. Par exemple, nous pouvons utiliser molécule pour les tests unitaires sur des instances docker, vagrant ou ec2.

Si vous voulez plus de tutorials Ansible je vous renvoie sur le billet de l'introduction à ansible