Molecule es un marco de pruebas diseñado para papeles Ansible, colecciones y juegos. En el ecosistema Ansible, proporciona un flujo de trabajo estructurado para desarrollar y verificar la infraestructura como código.
Desde una perspectiva más amplia, Molecule alienta desarrollo de la infraestructura impulsada por los ensayos (TDD). En lugar de desplegar funciones directamente a la producción y esperar que trabajen, los desarrolladores pueden validarlas localmente o en tuberías de CI con Molecule. Soporta diferentes estrategias de prueba, forro, idempotencia y un ecosistema de plugins.
Al ser un poco difícil de configurar, Molecule permite al desarrollador los formalismos del Pensamiento Correcto (TM). TDD permite al desarrollador validar un paso terminado, y marcarlo como completo. TDD permite pensar en piezas: en lugar de ser abrumado por una gran tarea, el desarrollador puede implementar y validar pequeñas piezas de la funcionalidad que se está implementando - y con tales cambios incrementales, todo el proyecto se puede completar.
~ ♪♪
Molecule sufrió cambios de ruptura entre algunas versiones, y cierta documentación en línea parece ser incorrecta. Este artículo funciona con la versión Molecule 25. Aquí, voy a configurar un pequeño andamio, para activar una prueba simple en una sola máquina de eliminación. El lector puede tomar desde allí, personalizando el andamio para sus propias necesidades específicas.
Supongamos que tenemos un proyecto ansible con la siguiente estructura de carpetas:
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.ymlEstaremos probando una tarea específica en el papel Ror. Primero, vamos a configurar la estructura wco ansible/roles/ror/molecule así:
wco_ansible/roles/ror/molecule/
README.txt
default/
converge.yml
create.yml
destroy.yml
molecule.yml
verify.yml
Y el contenido de los archivos:
## 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].*")Lo siguiente es importante. Específicamente, añadir contenedor a molevule inventory 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: absentEn el siguiente archivo, desea tener ambos plataformas y provisioner llaves. Tienen los mismos datos para mí... El host, el puerto y otros argumentos en provisioner son definitivamente importantes. Los de las plataformas pueden ser opcionales - pero los uso en crear.yml Así que por ahora tienen que quedarse. Cabe destacar la estructura de plataformas puede 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-keyAhora, un comentario sobre Ssh vs Docker Conductor. Yo uso ambos. Ya que conduzco docker con ansible, a menudo necesito docker interior, o algo similar, y al final es más fácil para mí girar una máquina virtual en otro lugar y hacer pruebas allí vía ssh. Por otro lado, el portátil de desarrollo definitivamente utiliza un montón de docker, por lo que el controlador de docker para la molécula es valioso y útil. Tengo mi propia imagen que permite sistematizar, y ssh. Puede leer el artículo sobre Cómo configurar la imagen del docker con sistema y sshd, para más información sobre el tema.
.^.