Validando o Ansible com Molecule: Primeiros passos

Garantindo qualidade das suas roles para evitar dor de cabeça

Rafa Kempfer
6 min readMar 24, 2021

No post anterior, falei sobre a qualidade de software e como podemos (e devemos) aplicar os mesmos conceitos no desenvolvimento de Infra como Código.

Agora começo a explorar as ferramentas existentes atualmente, e que prometem ajudar na implementação dos nossos testes. Nesse primeiro post, vou introduzir o Molecule e como vamos utilizar ele nas duas primeiras camadas da pirâmide de teste de IaC: análise estática dos arquivos e testes automatizados de unidade.

Molecule

Ninguém é perfeito, todos cometemos erro. E no desenvolvimento de software (ou de IaC) isso é bem comum, seja na criação de novas funcionalidades ou na manutenção de alguma existente. Muitas vezes erros simples, como um erro de digitação, só são percebidos quando tentamos aplicar nossa implementação no ambiente destino, gerando um custo alto para correção de um problema simples que poderia ser identificado "mais cedo".

O Molecule vem para ajudar a evitar cenários como esses, permitindo validar individualmente as funções do Ansible. Ele realiza um fluxo de validação robusto, completo e flexível para garantir um bom nível de qualidade das funções.

Em linhas gerais, o Molecule é tipo um framework "guarda-chuva" que integra outras ferramentas para melhor validação das funções do Ansible. Abaixo um exemplo de uma configuração com algumas características do Molecule:

Neste arquivo estamos definindo o driver como docker, ou seja, onde nossa máquina será emulada. Outras opções são permitidas aqui, como o podman. Em seguida, quais os comandos de lint serão aplicados no nosso projeto, ou seja, análise estática do código. Na seção de platforms definimos as configurações das nossas "máquinas" (sim, pode ser uma lista), neste caso aqui é uma imagem centos 8. Podemos ainda adicionar outras máquinas com diferentes SOs. Em provisioner, obviamente, vamos configurar para Ansible.

Na configuração de verifier, definimos qual ferramenta será utilizada para validar e testar nosso provisionamento. Pode ser utilizado o Ansible como verificador e o molecule irá executar um playbook especifico para validação. Ou então, como é o caso aqui, utilizar o Testinfra, que permite a criação de testes em python para validação do provisionamento.

Além disso, o Molecule permite a criação de cenários que permitem criar variações de testes, com configurações e testes diferentes, para a sua função.

Uma execução padrão de teste do Molecule, irá executar os passos abaixo. Podendo ser customizado para cada cenários criado:

  1. dependency
  2. lint
  3. cleanup
  4. destroy
  5. syntax
  6. create
  7. prepare
  8. converge
  9. idempotence
  10. syde_effect
  11. verify
  12. cleanup
  13. destroy

Mas e na prática?

Chega de papinho e vamos ver como a bagaça funciona. Primeiro passo é instalar o Molecule e as demais ferramentas necessárias.

O Molecule e o Testinfra são escritos em python e distribuído com o pip. Dessa forma é necessário ter o python instalado.

Instalar testinfra

#Install testinfra
pip install pytest-testinfra

Instalar molecule

Como vamos utilizar lint, docker e ansible, já vamos instalar o Molecule com suporte a essas ferramentas

#Install molecule
pip install "molecule[docker,lint,ansible]"

Para este exemplo, vamos criar uma role que instala o Java 11 em servidores com Ubuntu e CentOS.

Ok, mas como usamos o Molecule em nossas funções (roles)? Temos duas opções aqui, a primeira é criar uma nova role utilizando o Molecule onde ele já irá criar toda a estrutura da role seguindo os padrões:

#Create role
#ex: molecule init role new-role --driver-name docker
molecule init role open-jdk-11 --driver-name docker

A segunda opção é inicializar o Molecule em um role já existente:

cd existing-role
molecule init scenario default --role-name existing-role --driver-name docker

O Molecule irá criar o arquivo molecule/default/molecule.yml, vamos alterá-lo para contemplar as configurações que queremos: incluir análise estática de código (lint), configurar as plataformas que queremos validar e configurar o testinfra como verifier.

Se executarmos o comando "molecule test" neste ponto, sem nada implementado na nossa role, o teste irá falhar no passo de lint no arquivo meta/main.yml, pois nossa função está com as configuração padrão e algumas informações precisam ser ajustada.

Antes de implementarmos a nossa role, vamos criar alguns testes. Um arquivos de testes chamado "test_default.py" será criado dentro da pasta "molecule/default/tests".

Como estamos fazendo a instalação do Java, vamos implementar duas validações: verificar se o comando "java -version" está executando corretamente e validar se o bin java está configurado corretamente:

Sem ainda termos implementado a role, vamos rodar o molecule para validar os nossos testes e ver o resultado:

molecule test
Execução do comando molecule test, sem implementação da role

A imagem acima é um trecho do log, após o Molecule executar o lint, subir as instâncias docker, executar o provisionamento (que ainda não faz nada) e outros passos, conforme vimos anteriormente. Após isso, ele executa os testes que criamos e que, obviamente, falham. Agora precisamos "corrigir" isso implementando o provisionamento da nossa role que instala o Java.

Um ponto importante aqui é que o Molecule possui outros comandos que são uteis para ir validando o seu código a medida que você vai criando. Primeiro o comando create, que será responsável por criar os containers que atuarão como o "servidor":

molecule create

O comando converge irá aplicar o código Ansible no servidor temporário, criado anteriormente. Se algo estiver errado no seu provisionamento, já irá "aparecer" aqui:

molecule converge

E após aplicar com sucesso o seu código no passo anterior, você deverá executar o comando verify, que irá executar na prática o testinfra validando o comportamento aplicado pelo converge nesse servidor temporário.

molecule verify

Após tudo implementado e funcionando na nossa role, executamos novamente o comando de test, pois ele irá executar o ciclo completo, importante para termos certeza que tudo está correto. Você pode ver o código completo no github:

Execução do comando molecule test, após implementação correta da role.

Além dos passos de validação já comentados aqui, outro passo importante executado pelo Molecule é o de idempotence. Idempotência é uma das melhores práticas na criação de IaC. Nesse passo o Molecule irá fazer uma segunda aplicação do mesmo código Ansible na mesma máquina para garantir que a segunda execução não irá fazer alterações no servidor.

Com o Molecule o Testinfra, podemos criar diversos cenários de testes para as nossas diversas roles. Por exemplo, podemos validar se o provisionamento de um Apache ou de um Nginx está "escutando" corretamente na porta 80.

Ou seja, o Molecule pode ajudar mitigar os erros no provisionamento dos nossos ambientes, evitar alterações que causem problemas e acelerar o desenvolvimento da nossa IaC.

Molecule no CI

Por último e também muito importante é podermos executar os nossos testes no nosso processo de Integração Contínua. É indispensável podermos fazermos essa integração para garantirmos a qualidade do que nossos times estão criando e/ou alterando.

O Molecule permite a integração em diversas ferramentas de CI como Jenkins, Travis CI, GitLabs CI, entre outros. Mais detalhes e exemplos na documentação da ferramenta.

Conclusão

Adotar o Molecule para Ansible será de grande ajuda para garantir que suas funções atendam os melhores padrões, que o código tenha uma qualidade aceitável e que funcione. Porém o Molecule irá validar suas roles individualmente, ou seja, testes de unidade. Ainda poderemos ter problemas quando executarmos diversas em conjunto.

Mesmo assim, o Molecule é uma ótima ferramenta para ajudar a garantir a qualidade do código de IaC, mitigando problemas e reduzindo custos.

--

--