AWS

,

Node.js

,

Docker

Tester les services AWS localement avec localstack

Utiliser les services AWS, sans vraiment utiliser AWS.

Murat

Murat

30 janvier 2021

Tester les services AWS localement avec localstack

Aujourd’hui, de plus en plus d’applications sont hébergés sur le Cloud. Et ceux qui hébergent sur le cloud utilisent sûrement Amazon AWS, Microsoft Azure ou Google App Engine. Les raisons pour laquelle de nombreux utilisateurs migrent vers ces plateformes sont multiples : la multitude de services proposés, la fiabilité, la sécurité…

Et c’est de ça qu’on va discuter aujourd’hui, les services. Vous souhaitez migrer votre application vers AWS afin d’utiliser leurs services cool comme s3 ou sqs. Y a-t-il un moyen pour tester ces services avant de créer toute l’infrastructure chez eux ?

Et bien la réponse à cette question est oui, et ça s’appelle localstack.

Localstack, qu’est-ce que c’est ?

Localstack, accessible à cette URL, est un framework de tests/mocks open-source pour les applications Cloud. Développé à l’origine par Atlassian, ce n’est pas un service officiel d’Amazon, mais son grand nombre d’utilisateurs et de stars github (presque 30.000 !) font de ce service une référence lorsqu’on parle de AWS à un développeur.

localstack vous fournira donc, une grande partie des fonctionnalités et APIs présents sur l’environnement réel AWS, comme ils le disent dans leurs description :

the focus is primarily on supporting the AWS cloud stack.

Pourquoi utiliser localstack ?

Les avantages d’utiliser localstack sont multiples.

Tout d’abord, pas besoin d’avoir une infra aws de disponible pour l’implémenter. Pas même besoin d’avoir une connexion internet, car tout se passe en local. Et si vous avez une infra de disponible, mais vous avez une grande équipe de développeurs, pas besoin de créer des accès pour tout le monde.

Ensuite, vous pouvez facilement ajouter / supprimer / modifier / casser tout ce que vous voulez là-dedans.

Enfin, pour solidifier votre application, vous pouvez utiliser localstack pour vos tests d’intégrations.

Initialisation de l’image Docker

Docker image

localstack.dev.local:
container_name: localstack.dev.local
image: localstack/localstack:0.11.2
ports:
- 4566:4566
- 8066:8066
environment:
- SERVICES=sqs
- DEBUG=0
- DOCKER_HOST=unix:///var/run/docker.sock
- PORT_WEB_UI=8066

Script d’initialisation

Localstack peut être initialiser via un script au démarrage de l’instance. La documentation à ce sujet est disponible ici.

Il suffit d’ajouter cette ligne à votre fichier de configuration docker :

volumes: - ./localstack:/docker-entrypoint-initaws.d

volumes:
- ./localstack:/docker-entrypoint-initaws.d

Créer un dossier localstack au même niveau que votre fichier docker, et créer un fichier .sh. Ce dossier sera monté en tant que dossier docker-entrypoint-initaws.d dans cette nouvelle instance. Ainsi, Localstack pourra executera tous les fichiers .sh à ce niveau.

aws-sdk a besoin des credentials et de la région pour être exécuter correctement. C’est ce qu’on va faire dans ce fichier. De plus, dans notre cas, on utilise le service SQS de Amazon, et on a besoin d’initaliser une première queue.

echo 'Configure SQS on Localstack'
echo 'It might take some time...'
aws configure set aws_access_key_id default_access_key
aws configure set aws_secret_access_key default_secret_key
aws configure set region eu-west-1
# https://github.com/localstack/localstack/issues/2751 -- 0.11.3 @ Creating a queue through the API gives a successful response when queue is not created
aws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name history --no-verify-ssl

Tester la connexion

Démarer votre service docker, puis allez sur http://localhost:4566/. Si vous avez une réponse positive de type :

{"status": "running"}

Alors bonne nouvelle, le service a démarré avec succès. Vous pouvez commencer à développer.

Vous pouvez également voir plus en détails quels services sont en cours d’exécution en visitant http://localhost:4566/health.

{"services": {"sqs": "running" }}

Développement

Dans votre code

Nous utilisons aws-sdk pour connecter les services AWS. Le lien avec aws sera fait grâce au paramètre endpoint lors de l’initialisation du service.

Tout d’abord créez un fichier .env qui servira de faire la bascule entre localstack en dev, et le vrai AWS en prod. Pour l’instant on peut juste créer :

export const Config = {
HISTORY_QUEUE_PATH: "https://localstack.api.dev.adn.local:4566/queue/history",
DEVELOPMENT_AWS_ENDPOINT: "https://localstack.api.dev.adn.local:4566"
};

À la base, les différents services AWS avaient chacunes leurs propres url:port avec localstack. Aujourd’hui, tous les services sont disponible via la même url : https://localstack.dev.local:4566.

Maintenant, nous allons initialiser un objet ‘aws-sdk’, pour SQS dans notre cas :

// queue.ts
import { SQS } from 'aws-sdk';
import https from 'https';
function buildConfig = (): SQS => {
const sqsConfig = Config.IS_DEVELOPMENT
? {
httpOptions: { agent: new https.Agent({ rejectUnauthorized: false }) },
apiVersion: '2012-11-05',
endpoint: Config.DEVELOPMENT_AWS_ENDPOINT,
region: 'us-east-1',
}
: { apiVersion: '2012-11-05' };
return new SQS(sqsConfig);
}

Et enfin, ajouter un nouveau message dans la queue programmatiquement :

// queue.ts
export const buildSendHistoryMessage = (sqs: SQS, payload: any): Promise<string> => {
const { MessageId } = await new Promise<SQS.SendMessageResult>((res, rej) =>
sqs.sendMessage(
{
MessageBody: 'History update',
MessageAttributes: payload,
QueueUrl: Config.HISTORY_QUEUE_PATH,
},
(err, data): void => (err ? rej(err) : res(data)),
),
);
if (!MessageId) {
throw new Error('SQS message creation failed');
}
return MessageId;
};

Et voila c’est tout ! On peut maintenant tester notre résultat.

Tester les résultats

Une fois que vous avez fini de jouer avec aws-sdk, et que vous communiquer avec localstack, vous pouvez vous connecter dans le container afin de voir ce qui se passe.

docker exec -it localstack.dev.local sh

À l’intérieur, vous pouvez éxecuter les commandes aws grâce à l’utilitaire awslocal qui est disponible par défaut.

Exemple pour sqs :

awslocal sqs list-queues // will list all available queues
awslocal sqs receive-message --queue-url <url> --message-attribute-names <attribute1> <attribute2> // list a queue message data

Vous pouvez trouver la documentation pour chacune des commandes disponible ici.

Test unitaire

Nous utilisons gitlab pour nos tests unitaires. La seule chose à faire pour faire passer nos tests, c’est d’abord d’initialiser le service.

check:test:
stage: check
image: node:lts
variables:
HISTORY_QUEUE_PATH: https://localstack:4566/queue/history
DEVELOPMENT_AWS_ENDPOINT: https://localstack:4566
# localstack:
AWS_ACCESS_KEY_ID: 'foobar'
AWS_SECRET_ACCESS_KEY: 'foobar'
AWS_DEFAULT_REGION: 'foobar'
SERVICES: sqs
TEST_AWS_ACCOUNT_ID: 012345678910 # applies to localstack >= 0.11.3 -- default: 000000000000
DEBUG: 1
DATA_DIR: /tmp/localstack/data
DOCKER_HOST: unix:///var/run/docker.sock
HOST_TMP_FOLDER: /tmp/localstack
script:
- apt update
- apt install -y awscli mysql-client
- aws --endpoint-url=https://localstack:4566 sqs create-queue --queue-name history --no-verify-ssl
- yarn test
services:
# For image tag 0.11.2 and earlier versions:
# SQS queue URL pattern http[s]://localhost:<port>/queue/<queueName>
# For image tag latest (pushed after June 20, 2020), or tag 0.11.3 and above
# SQS queue URL pattern http[s]://localhost:<port>/<accountId>/<queueName>
- name: localstack/localstack:0.11.2
alias: localstack

Je vous laisse développer vos propres tests d’intégrations.

Conclusion

Garder à l’esprit, que c’est un service non officiel, qui n’a pas été créé par Amazon. Il se peut que demain Amazon mettent à jour leurs services de façon à casser localstack. Avec la communauté derrière, ils mettront à jours le service assez rapidement, mais tenez compte de ça.

Avant de livrer en production, vous serez quand même obligé de vraiment utiliser la connexion avec la réelle instance de aws. Même si localstack vous facilite beaucoup de chose, ce n’est au final pas le service final que vous utiliserez.

Technologies dans cet article ...

Infra

AWS

AWS

Docker

Docker

Back-End

Node.js

Node.js