Dans un post précédent je vous expliquait le fonctionnement de Earthly et comment il permettait de construire exactement de la même manière votre projet que ce soit sur votre machine ou ailleurs, comme sur votre serveur d’intégration continue.
C’est très bien, mais quand il s’agit de faire tourner vos tests, là vient la véritable difficultée car vous savez aussi bien que moi que lorsque vous lancez vos tests sur votre serveur d’intégration continue, vous n’êtes pas certain à 100% d’avoir le même comportement, et vous vous retrouvez même, parfois, à faire du code rien que pour votre CI … ce qui est évidement incorrect.
En me plongeant encore plus dans la documentation de Earthly, je me suis rendu compte que j’étais passé à coté du véritable atout de Earthly: Exécuter des containers Docker dans Earthly.
Dit comme ca, ca ne semble pas fifou mais en réalité ceci veux dire faire tourner vos tests d’intégrations dans Earthly! Qui dit “dans Earthly”, dit qu’il aura le même résultat, peu importe où Earthly tourne. Résultat, si vos tests ne passent pas sur votre CI, ils ne passeront pas en local non plus, et même mieux: Earthly vous permet de débogger en cas d’erreur!
Utiliser docker-compose
dans Earthly
Votre application n’utilisera pas de services extérieur que rarement, ce qui veux dire que vous aurez donc quasiement toujours un service extérieur auquel votre application aura à se connecter, comme une base de donnée par exemple.
Vous pouvez utiliser Docker dans Earthly avec lancer manuellement vos différents containers etc … ou alors utiliser docker-compose
dans Earthly afin de démarrer tous vos services, puis lancer vos tests, et c’est ce que vous allons voir.
Petit correctif, grand gain de temps
Au fil du temps, pendant que je construisais mon fichier Earthfile
dans mon projet Brewformulas.org, je perdais du temps dès lors que je changeai mon fichier docker-compose.yml
car j’utilisais COPY . /application
dans le target assets
et prod
par fainéantise.
J’ai donc changé l’instruction COPY . /application
par de multiples COPY
pour copier les fichiers & dossiers nécessaires seulement, et donc en excluant, entres autres, mon fichier docker-compose.yml
, ce qui permet au cache de ne plus être invalidé lorsque je change mon fichier docker-compose.yml
.
Modification du target ci
J’ai aussi ajouté les différent COPY
nécessaire pour importer les fichiers des tests (dossier features
pour Cucumber et spec
pour Rspec).
Puis j’ai changé la commande par défaut par rake
.
Pour finir, au lieu de sauvegarder une image avec un nom, j’ai uniquement fait un SAVE IMAGE
, vous verrez par la suite pourquoi 😉.
Ajout du target integration-test
Ce nouveau target va donc être responsable pour lancer les tests.
Voici une copie, puis je vous explique:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
integration-test:
FROM docker:19.03.13-dind
RUN apk --update --no-cache add docker-compose jq
COPY docker-compose.yml ./
WITH DOCKER
DOCKER PULL postgres:9.6.2
DOCKER PULL redis:3.2.8
DOCKER PULL memcached:1.5.3-alpine
DOCKER LOAD +ci zedtux/brewformulas.org:dev
RUN docker-compose up -d \
&& POSTGRESQL_CONTAINER_IPADDR=$(docker inspect default_postgres_1 \
| jq -r '.[0].NetworkSettings.Networks.default_default.IPAddress') \
&& for i in {1..30}; do nc -z $POSTGRESQL_CONTAINER_IPADDR 5432 && break; sleep 1; done; \
docker run --network=host \
--env DATABASE_HOST=$POSTGRESQL_CONTAINER_IPADDR \
--env POSTGRESQL_USER=postgres \
--env RAILS_ENV=test \
zedtux/brewformulas.org:dev bash -c 'bundle exec rake db:setup \
&& bundle exec rake' \
&& docker-compose down
END
Comme il y est stipulé dans les limitations de Docker dans Earthy dans la documentation, une image Docker officiel est nécessaire pour que l’instruction pour utiliser Docker dans Earthly, ce target commence donc par un FROM docker:19.03.13-dind
.
Ensuite, dans cette image, docker-compose
y est installé, puis notre fichier docker-compose.yml
y est copié.
Vient ensuite l’instruction WITH DOCKER ... END
qui va démarrer un démon Docker, exécuter les commandes à l’intérieur de ce bloque, puis va tuer le démon Docker.
À l’intérieur, il y a 3 instructions DOCKER PULL
pour télécharger les images nécessaires, les fameux services extérieur de notre application.
Encore une fois, comme il est dit dans les limitations de Docker dans Earthy dans la documentation, il est nécessaire de les déclarer, même si Docker téléchargera automatiquement les images, car sinon ces images ne seront pas mises en cache, et risque d’être téléchargées plusieurs fois car le cache de Docker n’est pas préservé entre chaques exécutions.
L’instruction DOCKER LOAD
permet de récupérer l’image sauvegardé depuis le target ci
puis nous le nommons zedtux/brewformulas.org:dev
car c’est le nom utilisé dans mon fichier docker-compose.yml
.
Arrive ensuite docker-compose
pour démarrer tous les services.
La ligne suivante va récupérer l’adresse IP du container où tourne PostgreSQL et la stocke dans la variable POSTGRESQL_CONTAINER_IPADDR
.
Ceci est nécessaire car autrement, si vous essayez de vous y connecter avec locahost
, vous aurez une erreur “Connection refused” comme décrit dans cet article “Localhost postgres docker ‘Connection refused’ using pgAdmin”.
La ligne suivante attends que PostgreSQL démarre en tantant une trentaine de connexions sur l’adresse IP du container de PostgreSQL.
Une fois que PostgreSQL a démarré, j’utilises un docker run
afin de configurer la base de données (création de la base de données, puis exécution des scripts de migration et du fichier seed.rb
si nécessaire) puis lancer les tests.
Lancer les tests en local
Maintenant pour lancer les tests, il faut lancer earth +integration-test
, comme vue dans mon précédent article, mais cette fois-ci, des priviléges augmentés sont nécessaires afin d’utiliser Docker dans Docker, alors il faudra passer le paramètre --allow-privileged
ou -P
:
1
earth -P +integration-test
Et lorsque quelque chose ne fonctionne pas?
Avant de comprendre que je devais récupérer l’adresse IP du container pour connecter Brewformulas.org à PostgreSQL, j’avais besoiun de débogger l’environnement Docker dans Earthly.
Pour le faire, il vous suffit de passer le paramètre --interactive
ou -i
.
Ce paramètre fera que si une erreur survient dans votre environnement Earthly (dans mon cas, docker run ...
se termine avec un exit code supérieur à 0), Earthly vous ouvre une session à l’intérieur et vous pourrez donc inspecter Docker (docker ps
, docker inspect <...>
etc …).
Lancer les tests sur Gitlab CI
Pour finir j’ai importer un fichier .gitlab-ci.yml
, généré depuis l’interface de Gitlab, pour se connecter à Gitlab (afin de télécharger et téléverser des images Docker), puis je lance earth +prod
afin de construire l’image de production, qui serait l’image téléversée si tout se passe bien, puis je lance earth -P +integration-test
pour lancer les tests.
Si tout se passe bien, alors docker push "$CI_REGISTRY_IMAGE"
sera exécuté, et donc l’image de production sauvegardée dans le registre Docker de Gitlab.
Et ca marche?
De façon assez intéressante mes tests Rspec passent en local, mais pas mes tests Cucumber, qui m’affiche 3 tests râtés sur 95, et Gitlab CI me dit exactement la même chose, donc ca fonctionne du feu de dieu!
Earthly permet donc bien de dire adieu aux problèmes de comportements différent d’une machine à l’autre et permet donc d’éviter ceci:
Lisez le blog d’Earthly. Source de l’image.