Test unitaire : approche DB

Netapsys fait de l'intégration continue sur certains projets. Cela implique la création de tests automatisés et répétables que l'on peut rejouer sur les codes sources, afin de se prémunir le plus possible d'éventuelles régressions ou effets de bords. Dans le cadre des projets Magento, on travaille habituellement sur des systèmes Linux Apache MySQL PHP au niveau des machines virtuelles de développement, et on travaille massivement avec les données issues de la BD. Cela implique que lors des exécutions des tests, un grand nombre de données peut être modifié.

Même si les bonnes pratiques des tests imposent de laisser le système dans le même état avant et après test, on ne peut pas exclure, particulièrement lors du développement des tests, que des étapes des tests se passent mal et que la BD ne soit pas nettoyée.
Il est donc nécessaire de trouver un autre moyen pour réussir à garantir la cohérence de la BD avant et après test, et ce moyen doit être accessible aux développeurs.
Je vais décrire ici une solution que j'ai maquetté pour certains tests, à base de MySQL, LVM et SSH.

Au niveau de MySQL

MySQL est un serveur de base de données très populaire, mais qui ne dispose pas, à ma connaissance, de possibilité de faire des snapshots de base de données. Typiquement, on se débrouille avec des sauvegardes avec mysqldump et l'exécution des scripts SQL résultant.

Néanmoins, une autre possibilité existe pour faire une sauvegarde du serveur : il s'agit de sauvegarder l'ensemble des fichiers de données de mysql, mais ce, en garantissant la cohérence des données dans les fichiers. Le moyen le plus simple pour garantir cette cohérence est de simplement arrêter la base de données (ce qui est possible puisqu'on ne se trouve pas en contexte de production).

A ce moment, on peut donc faire des copies de fichiers pour se mettre de côté l'ensemble des données, et restaurer la copie ; ceci peut être en pratique plus rapide que d'exécuter le script sql d'import des données.
Mais il y a mieux : utiliser les possibilités de snapshot LVM.

LVM : Logical Volume Management

Le Logical Volume Management est un ensemble d'outils qui permettent de gérer "comme un tout" l'espace des différents disques présents dans une machine.Le LVM fait la distinction de trois niveaux :

  • Le volume logique (LV), qui est un volume taillé dans un
  • Groupe de volumes, qui est constitué par un ensemble de
  • Volumes physiques, qui peuvent être des disques durs entiers ou des partitions

L'unité allouée est l'Extent, qu'il soit physique ou logique (on peut par exemple rencontrer des extents de 4 Mo), et toute taille indiquée est ajustée/convertie en un nombre d'extents.
LVM permet de créer des snapshots de volumes : les snapshots de volumes sont basés sur les données originales d'un volume : lorsque les données originales du volume de base sont modifiées, les données originales sont copiées dans le snapshot. LVM permet ensuite de fusionner ces informations de sauvegarde avec les données modifiées, de sorte à revenir aux donnés initiales.

En pratique, j'ai préparé un disque dur virtuel que j'ai affecté entièrement à un groupe de volumes dédié, et j'ai divisé globalement le volume en deux : un volume logique qui fait environ la moité du volume, et l'autre volume que je laisse libre pour le snapshot.
J'ai également crée deux scripts : un script de setup, qui permet d'initialiser le système, et un script de teardown, qui permet de revenir à l'état initial.

Le script de setup effectue principalement les actions suivantes :

  • Arrêt de mysql (instance initialisée au démarrage de la machine),
  • Création d'un snapshot, pour garder l'état actuel des données
  • Montage du volume logique
  • Montage de type bind pour "recouvrir" le répertoire de données de mysql.
  • Démarrage de mysql


#!/bin/bash
service mysql stop
lvscan
lvcreate -s vg0/vg0lvol0 -l 90%FREE -n vg0snap0
install -d /mnt/disk
mount /dev/vg0/vg0lvol0 /mnt/disk
mount --bind /mnt/disk/mysql /var/lib/mysql
service mysql start

Le script de teardown effectue les actions suivantes :

  • Arrêt de mysql, pour garantir la cohérence des données
  • Démontage du montage bind
  • Démontage du volume logique
  • Désactivation puis réactivation du volume logique orignal , pour faire en sorte qu'il ne soit plus utilisé, afin d'autoriser le rollback
  • Rollback du volume original
  • Destruction du volume logique


#!/bin/bash
service mysql stop
umount /var/lib/mysql
umount /mnt/disk
lvconvert --merge vg0/vg0snap0

Cette manière de faire permet donc de revenir rapidement aux données initiales de la base.
Si l'on souhaite modifier les données de tests initiales, il suffit

  • D'arrêter manuellement mysql
  • D'effectuer manuellement le montage du volume logique
  • Relancer mysql
  • Modifier les données
  • Arrêter mysql

Cette méthode a comme avantage d'être très rapide. Alors qu'il me faut pas loin d'une heure pour remonter un backup complet de BD, il ne faut que quelques minutes pour revenir aux données initiales via LVM (bien entendu, la durée prise dépend de la quantité de données modifiées). Néanmoins, il reste une problématique : il faut un accès root pour faire ces manipulations. Et tout le monde ne peut pas disposer de ces droits (ni même disposer éventuellement d'un accès en ligne de commande au serveur de BD). C'est là qu'intervient l'accès SSH.

SSH : lancement de commandes à distance et port forwarding

Le SSH est la plupart du temps utilisé pour se connecter à distance à une machine, et lancer manuellement des commandes. Cependant, les possibilités de l'outil sont bien plus importantes, et deux en particulier peuvent être très utiles dans le cadre du maquettage pour les tests : l'authentification par clef, et le port forwarding.

Le port forwarding est la possibilité de créer un tunnel entre un port local et un port distant. Il est à noter que le port distant peut être situé directement sur la machine distante, ou carrément sur le réseau accessible par la machine distante. Dans notre cas, il s'agira d'utiliser le port forwarding pour tunneler l'accès mysql vers la machine de développement. Ceci peut suffire si on a la même configuration de compte au niveau de mysql sur les deux bases, et permet donc de ne pas avoir à jongler entre plusieurs configurations au niveau de Magento.
Le port généralement utilisé par mysql est le port 3306, d'où la commande :


ssh @ -L 127.0.0.1:3306:127.0.0.1:3306 -i

On demande le forwarding du port local 127.0.0.1:3306 vers le port distant 127.0.0.1:3306. Le -i permet de spécifier la clef privée à utiliser (couple clef privée/clef publique généré par ssh-keygen - voir page de manuel).
Au niveau de l'identité, on utilise la clef privée correspondant à la clef publique stockée dans le ~/.ssh/authorized_keys de la machine distante.

Si le compte utilisé est root, on peut alors se connecter en tant que root et faire les actions requises (variante : se connecter en tant qu'utilisateur normal et lancer un script qui a le bit SUID activé). L'intérêt de la connexion par clef est qu'on peut l'utiliser pour limiter les actions que l'utilisateur ainsi identifié peut faire :

  • On peut limiter les connexions en port forwarding à un ou plusieurs ports précis
  • On peut spécifier la commande à exécuter (command="command")
  • On peut désactiver le forwarding X11 (graphique) (no-X11-forwarding)
  • On peut désactiver l'attribution de terminal (no-pty)
  • On peut désactiver le port forwarding (no-port-forwarding)

Ainsi, le fichier a la syntaxe générale suivante :


<format clef> <clef publique> <options>

Voir la page de manuel de sshd pour les explications exactes.
Cependant, fort de ces explications, on peut envisager l'utilisation de trois paires clef privée/clef publique différentes :

  • Une pour effectuer le setup
  • Une pour effectuer le teardown
  • Une pour monter le tunnel

Evidemment, il pourrait y avoir en pratique des problématiques d'accès concurrents aux ressources, mais c'est un problème que l'on pourrait éventuellement régler en complexifiant les scripts (par exemple, utiliser l'existence ou la non existence de fichiers pour faire de l'exclusion mutuelle).

En conclusion, on constate qu'il est facile de proposer un système qui permet de restaurer rapidement une base de données de test, en se basant principalement sur LVM, et en jouant au niveau de l'administration système avec MySQL et avec SSH (si nécessaire). Il existe d'autres moyens : l'approche DBUnit (http://dbunit.sourceforge.net/), ou l'utilisation des machines virtuelles, avec un disque dur virtuel en mode non persistant pour le stockage BD. Cette dernière solution est certes simple, mais pour pouvoir maintenir la BD dans le temps, il est nécessaire d'avoir la main sur l'hyperviseur utilisé pour pouvoir repasser un disque dur virtuel en mode persistant, effectuer les mises à jour durables de la BD de test, et revenir ensuite en mode non persistant. Et ces phases de maintenances ne sont pas toujours aisées, en particulier lorsque le développeur n'a pas la main sur l'hyperviseur.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.