Molécula é um framework de testes projetado para papéis, coleções e playbooks Ansíveis. No ecossistema Ansível, ele fornece um fluxo de trabalho estruturado para desenvolver e verificar a infraestrutura como código.
De uma perspectiva mais ampla, Molecule incentiva Desenvolvimento de infra-estruturas orientadas para o ensaio (TDD)Em vez de implantar papéis diretamente na produção e esperar que eles funcionem, os desenvolvedores podem validá-los localmente ou em pipelines CI com Molecule. Ele suporta diferentes estratégias de teste, linting, idempotência e um ecossistema de plugins.
Embora seja um pouco complicado de configurar, Molecule permite ao desenvolvedor os formalismos corretos de pensamento (TM). O TDD permite que o desenvolvedor valide uma etapa final e marque-a como completa. TDD permite pensar em pedaços: ao invés de ser sobrecarregado por uma grande tarefa, o desenvolvedor pode implementar e validar pequenos pedaços da funcionalidade sendo implementada - e com tais mudanças incrementais, todo o projeto pode ser concluído.
♪ ♪ ♪ ♪ ♪ ♪
Molécula sofreu mudanças de quebra entre alguns lançamentos, e alguma documentação on-line parece estar incorreta. Este artigo trabalha com Molecule versão 25. Aqui, eu só vou configurar um andaime mínimo, para permitir a execução de um teste simples em uma única máquina de remoção. O leitor pode tomá-lo de lá, personalizando o andaime para suas próprias necessidades específicas.
Vamos assumir que temos um projeto ansible com a seguinte estrutura de pastas:
wco_ansible/
inventory/
ants.yml
do.yml
playbooks/
setup_mongo.yml
setup_ubuntu.yml
requirements.yml
roles/
ror/
files/
root/
_screenrc
meta/
main.yml
molecule/
...
requirements.yml
tasks/
packaged_app.yml
templates/
etc/
mongo.conf
vars/
packaged_app_1.ymlVamos testar uma tarefa específica no papel ror. Primeiro, vamos configurar a estrutura de Wco ansível/roles/ror/molécula Assim:
wco_ansible/roles/ror/molecule/
README.txt
default/
converge.yml
create.yml
destroy.yml
molecule.yml
verify.yml
E o conteúdo dos arquivos:
## requirements.yml
collections:
- name: community.docker
version: ">=3.10.4"
- name: community.general.
## converge.yml
- name: Fail if molecule group is missing
hosts: localhost
tasks:
- name: Print some info
ansible.builtin.debug:
msg: "{{ groups }}"
- name: Assert group existence
ansible.builtin.assert:
that: "'molecule' in groups"
fail_msg: |
molecule group was not found inside inventory groups: {{ groups }}
- name: Converge
hosts: molecule
# We disable gather facts because it would fail due to our container not
# having python installed. This will not prevent use from running 'raw'
# commands. Most molecule users are expected to use containers that already
# have python installed in order to avoid notable delays installing it.
# gather_facts: false
tasks:
- name: Check uname
ansible.builtin.raw: uname -a
register: result
changed_when: false
- name: Print some info
ansible.builtin.assert:
that: result.stdout is ansible.builtin.search("^Linux")
- name: "+++ +++ Check Python version is >= 3.8"
command: python3 --version
register: py_ver
changed_when: false
- name: "+++ +++ Assert correct Python version"
assert:
that:
- py_ver.stdout is match("Python 3\\.[189].*|Python [4-9].*")O seguinte é importante. Especificamente, adicionar contêiner ao inventário de molevule parece importar.
## create.yml
- name: Create
hosts: localhost
gather_facts: false
vars:
molecule_inventory:
all:
hosts: {}
# molecule: {}
tasks:
- name: Create a container
community.docker.docker_container:
name: "{{ item.name }}"
image: "{{ item.image }}"
state: started
command: sleep 1d
log_driver: json-file
register: result
loop: "{{ molecule_yml.platforms }}"
- name: Print some info
ansible.builtin.debug:
msg: "{{ result.results }}"
- name: Fail if container is not running
when: >
item.container.State.ExitCode != 0 or
not item.container.State.Running
ansible.builtin.include_tasks:
file: tasks/create-fail.yml
loop: "{{ result.results }}"
loop_control:
label: "{{ item.container.Name }}"
- name: Add container to molecule_inventory
vars:
inventory_partial_yaml: |
all:
children:
molecule:
hosts:
"{{ item.name }}":
ansible_connection: {{ item.ansible_connection }}
ansible_user: {{ item.ansible_user }}
ansible_host: {{ item.ansible_host }}
ansible_port: {{ item.ansible_port }}
ansible_ssh_common_args: {{ item.ansible_ssh_common_args }}
ansible_ssh_private_key_file: {{ item.ansible_ssh_private_key_file }}
ansible.builtin.set_fact:
molecule_inventory: >
{{ molecule_inventory | combine(inventory_partial_yaml | from_yaml, recursive=true) }}
loop: "{{ molecule_yml.platforms }}"
loop_control:
label: "{{ item.name }}"
- name: Dump molecule_inventory
ansible.builtin.copy:
content: |
{{ molecule_inventory | to_yaml }}
dest: "{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml"
mode: "0600"
- name: Force inventory refresh
ansible.builtin.meta: refresh_inventory
- name: Fail if molecule group is missing
ansible.builtin.assert:
that: "'molecule' in groups"
fail_msg: |
molecule group was not found inside inventory groups: {{ groups }}
run_once: true # noqa: run-once[task]
# we want to avoid errors like "Failed to create temporary directory"
- name: Validate that inventory was refreshed
hosts: molecule
gather_facts: false
tasks:
- name: Check uname
ansible.builtin.raw: uname -a
register: result
changed_when: false
- name: Display uname info
ansible.builtin.debug:
msg: "{{ result.stdout }}".
## destroy.yml
- name: Destroy molecule containers
hosts: molecule
gather_facts: false
tasks:
- name: Stop and remove container
delegate_to: localhost
community.docker.docker_container:
name: "{{ inventory_hostname }}"
state: absent
auto_remove: true
- name: Remove dynamic molecule inventory
hosts: localhost
gather_facts: false
tasks:
- name: Remove dynamic inventory file
ansible.builtin.file:
path: "{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml"
state: absentNo arquivo seguinte, você deseja ter ambos plataformas e provisionador Chaves. Eles têm os mesmos dados para mim... A máquina, o porto e outros argumentos em provisionador são definitivamente importantes. Os que estão em plataformas podem ser opcionais - mas eu os uso em create.yml Por agora, têm de ficar. De notar, a estrutura de plataformas provavelmente pode ser simplificado:
## molecule.yml
dependency:
name: galaxy
options:
requirements-file: requirements.yml
# driver:
# name: ssh
platforms:
- name: do-ubuntu24-3
ansible_connection: ssh
ansible_user: root
ansible_host: "x.x.x.x"
ansible_port: 22
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
ansible_ssh_private_key_file: ~/.ssh/your-key
groups:
- molecule
image: piousbox/ubuntu-sshd:0.0.0 ## not used, but presence is required
provisioner:
name: ansible
inventory:
host_vars:
do-ubuntu24-3:
ansible_connection: ssh
ansible_host: x.x.x.x
ansible_port: 22
ansible_user: root
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
ansible_ssh_private_key_file: ~/.ssh/your-keyAgora, um comentário sobre ssh vs docker Condutor. Uso os dois. Desde que eu dirijo docker com ansible, muitas vezes eu preciso docker dentro do docker, ou algo semelhante, e no final é mais fácil para mim girar uma máquina virtual em outro lugar e fazer testes lá via ssh. Por outro lado, o laptop de desenvolvimento definitivamente usa um monte de docker, então o driver de docker para molécula é valioso e útil. Eu tenho minha própria imagem que permite systemd, e ssh. Você pode ler o artigo sobre Como configurei a imagem docker com systemd e sshd, para mais informações sobre o tema.
.^.