寫程式我們都需要透過 Unit Test、Integration Test、E2E Test 等等來確保程式碼的品質,並增加開發的信心度。
所以我們在開發 Ansible Playbook 或 Role 時,也應該要有相對應的測試流程來確保我們的程式碼是正確的。
# 筆者這裡介紹一個最近很夯的 python 套件管理工具 uv
# 首先我們先初始化專案
mkdir -p ansible-molecule-demo
uv init
# 安裝 molecule
uv add molecule
# 驗證 Molecule 是否安裝成功
uv run molecule --version
uv run molecule init scenario
這會建立以下目錄結構:
ansible-molecule-demo/
├── molecule/
│ └── default/
│ ├── converge.yml # 測試 playbook
│ ├── create.yml # 建立環境腳本
| ├── destroy.yml # 清理腳本
| ├── molecule.yml # Molecule 配置檔
│ └── verify.yml # 驗證腳本
├── tasks/
│ └── main.yml
├── defaults/
│ └── main.yml
└── meta/
└── main.yml
---
# Dependency management (download roles/collections)
dependency:
name: galaxy
options:
ignore-certs: false
ignore-errors: false
role-file: requirements.yml
requirements-file: requirements.yml
ansible:
cfg:
defaults:
host_key_checking: false
verbosity: 1
ssh_connection:
pipelining: true
env:
ANSIBLE_FORCE_COLOR: "1"
ANSIBLE_LOAD_CALLBACK_PLUGINS: "1"
executor:
backend: ansible-playbook
args:
ansible_playbook:
- --diff
- --force-handlers
- --inventory=/path/to/inventory.yml
ansible_navigator:
- --mode stdout
- --pull-policy missing
- --execution-environment-image ghcr.io/ansible/community-ansible-dev-tools:latest
playbooks:
create: create.yml
converge: converge.yml
destroy: destroy.yml
cleanup: cleanup.yml
prepare: prepare.yml
side_effect: side_effect.yml
verify: verify.yml
scenario:
name: default
test_sequence:
- dependency
- cleanup
- destroy
- syntax
- create
- prepare
- converge
- idempotence
- side_effect
- verify
- cleanup
- destroy
---
# Purpose: bring the instance to the desired state by running the role under test.
# Molecule calls this playbook with `molecule converge`.
- name: Converge
hosts: all
gather_facts: true # Disable if your role does not rely on facts
tasks:
- name: Apply role under test
ansible.builtin.include_role:
name: yournamespace.yourcollection.yourrole
---
- name: Create
hosts: localhost
connection: local
gather_facts: false
# no_log: "{{ molecule_no_log }}"
tasks:
# TODO: Developer must implement and populate 'server' variable
- name: Create instance config
when: server.changed | default(false) | bool # noqa no-handler
block:
- name: Populate instance config dict # noqa jinja
ansible.builtin.set_fact:
instance_conf_dict: {}
# instance': "{{ }}",
# address': "{{ }}",
# user': "{{ }}",
# port': "{{ }}",
# 'identity_file': "{{ }}", }
with_items: "{{ server.results }}"
register: instance_config_dict
- name: Convert instance config dict to a list
ansible.builtin.set_fact:
instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}"
- name: Dump instance config
ansible.builtin.copy:
content: |
# Molecule managed
{{ instance_conf | to_json | from_json | to_yaml }}
dest: "{{ molecule_instance_config }}"
mode: "0600"
---
# Purpose: assert that the instance really ended up in the expected state.
# Molecule calls this playbook with `molecule verify`.
- name: Verify
hosts: instance
gather_facts: false # Quicker, if you do not need facts
tasks:
- name: Assert something
ansible.builtin.assert:
that: true
---
- name: Destroy
hosts: localhost
connection: local
gather_facts: false
# no_log: "{{ molecule_no_log }}"
tasks:
# Developer must implement.
# Mandatory configuration for Molecule to function.
- name: Populate instance config
ansible.builtin.set_fact:
instance_conf: {}
- name: Dump instance config
ansible.builtin.copy:
content: |
# Molecule managed
{{ instance_conf | to_json | from_json | to_yaml }}
dest: "{{ molecule_instance_config }}"
mode: "0600"
when: server.changed | default(false) | bool # noqa no-handler
# 執行完整測試週期
molecule test
# 分階段執行
molecule create # 建立測試環境
molecule converge # 執行 playbook
molecule verify # 執行驗證
molecule destroy # 清理環境
明天我們來聊聊如何做到多環境的管理