├── molecule
└── default
│ ├── verify.yml
│ ├── converge.yml
│ ├── molecule.yml
│ └── INSTALL.rst
├── .yamllint
├── .github
└── workflows
│ └── ci.yml
├── main.yml
└── README.md
/molecule/default/verify.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Verify
3 | hosts: all
4 |
5 | tasks:
6 | - name: Verify Apache is serving web requests.
7 | uri:
8 | url: http://localhost/
9 | status_code: 200
10 |
--------------------------------------------------------------------------------
/molecule/default/converge.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Converge
3 | hosts: all
4 |
5 | tasks:
6 | - name: Update apt cache (on Debian).
7 | apt:
8 | update_cache: true
9 | cache_valid_time: 3600
10 | when: ansible_os_family == 'Debian'
11 |
12 | - import_playbook: ../../main.yml
13 |
--------------------------------------------------------------------------------
/molecule/default/molecule.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependency:
3 | name: galaxy
4 | driver:
5 | name: docker
6 | lint: |
7 | set -e
8 | yamllint .
9 | ansible-lint
10 | platforms:
11 | - name: instance
12 | image: geerlingguy/docker-${MOLECULE_DISTRO:-centos8}-ansible:latest
13 | command: ""
14 | volumes:
15 | - /sys/fs/cgroup:/sys/fs/cgroup:ro
16 | privileged: true
17 | pre_build_image: true
18 | provisioner:
19 | name: ansible
20 | verifier:
21 | name: ansible
22 |
--------------------------------------------------------------------------------
/molecule/default/INSTALL.rst:
--------------------------------------------------------------------------------
1 | *******
2 | Docker driver installation guide
3 | *******
4 |
5 | Requirements
6 | ============
7 |
8 | * Docker Engine
9 |
10 | Install
11 | =======
12 |
13 | Please refer to the `Virtual environment`_ documentation for installation best
14 | practices. If not using a virtual environment, please consider passing the
15 | widely recommended `'--user' flag`_ when invoking ``pip``.
16 |
17 | .. _Virtual environment: https://virtualenv.pypa.io/en/latest/
18 | .. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site
19 |
20 | .. code-block:: bash
21 |
22 | $ python3 -m pip install 'molecule[docker]'
23 |
--------------------------------------------------------------------------------
/.yamllint:
--------------------------------------------------------------------------------
1 | ---
2 | # Based on ansible-lint config
3 | extends: default
4 |
5 | rules:
6 | braces:
7 | max-spaces-inside: 1
8 | level: error
9 | brackets:
10 | max-spaces-inside: 1
11 | level: error
12 | colons:
13 | max-spaces-after: -1
14 | level: error
15 | commas:
16 | max-spaces-after: -1
17 | level: error
18 | comments: disable
19 | comments-indentation: disable
20 | document-start: disable
21 | empty-lines:
22 | max: 3
23 | level: error
24 | hyphens:
25 | level: error
26 | indentation: disable
27 | key-duplicates: enable
28 | line-length: disable
29 | new-line-at-end-of-file: disable
30 | new-lines:
31 | type: unix
32 | trailing-spaces: disable
33 | truthy: disable
34 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: CI
3 | 'on':
4 | pull_request:
5 | push:
6 | branches:
7 | - master
8 |
9 | jobs:
10 |
11 | test:
12 | name: Molecule
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | distro:
17 | - centos8
18 | - debian10
19 |
20 | steps:
21 | - name: Check out the codebase.
22 | uses: actions/checkout@v2
23 |
24 | - name: Set up Python 3.
25 | uses: actions/setup-python@v2
26 | with:
27 | python-version: '3.x'
28 |
29 | - name: Install test dependencies.
30 | run: pip3 install ansible molecule[docker] docker yamllint ansible-lint
31 |
32 | - name: Run Molecule tests.
33 | run: molecule test
34 | env:
35 | PY_COLORS: '1'
36 | ANSIBLE_FORCE_COLOR: '1'
37 | MOLECULE_DISTRO: ${{ matrix.distro }}
38 |
--------------------------------------------------------------------------------
/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install Apache.
3 | hosts: all
4 | become: true
5 |
6 | vars:
7 | apache_package: apache2
8 | apache_service: apache2
9 |
10 | handlers:
11 | - name: restart apache
12 | service:
13 | name: "{{ apache_service }}"
14 | state: restarted
15 |
16 | pre_tasks:
17 | - name: Override Apache vars for Red Hat.
18 | set_fact:
19 | apache_package: httpd
20 | apache_service: httpd
21 | when: ansible_os_family == 'RedHat'
22 |
23 | tasks:
24 | - name: Ensure Apache is installed.
25 | package:
26 | name: "{{ apache_package }}"
27 | state: present
28 |
29 | - name: Copy a web page.
30 | copy:
31 | content: |
32 |
33 |
Hello world!
34 | Hello world!
35 |
36 | dest: "/var/www/html/index.html"
37 | mode: '0644'
38 | notify: restart apache
39 |
40 | - name: Ensure Apache is running and starts at boot.
41 | service:
42 | name: "{{ apache_service }}"
43 | state: started
44 | enabled: true
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ansible 101 - Molecule playbook testing
2 |
3 | > _This example is derived from Chapter 12 of [Ansible for DevOps](https://www.ansiblefordevops.com), version 1.23_
4 |
5 | This playbook installs Apache and should work on the latest versions of Red Hat-based and Debian-based OSes, at a minimum:
6 |
7 | - CentOS 8
8 | - Debian 10
9 |
10 | Create a new molecule scenario:
11 |
12 | molecule init scenario
13 |
14 | Edit the `converge.yml` playbook:
15 |
16 | ```yaml
17 | ---
18 | - name: Converge
19 | hosts: all
20 |
21 | tasks:
22 | - name: Update apt cache (on Debian).
23 | apt:
24 | update_cache: true
25 | cache_valid_time: 3600
26 | when: ansible_os_family == 'Debian'
27 |
28 | - import_playbook: ../../main.yml
29 | ```
30 |
31 | Then run:
32 |
33 | molecule converge
34 |
35 | Uh oh... something failed. Log in and check it out:
36 |
37 | ```
38 | $ molecule login
39 | [root@instance /]# systemctl status httpd
40 | Failed to get D-Bus connection: Operation not permitted
41 | ```
42 |
43 | Yikes, I can't take this entire episode to explain the details of what's going on, but at a basic level, Molecule's default configuration sets up a container and runs the command:
44 |
45 | bash -c "while true; do sleep 10000; done"
46 |
47 | (use `docker ps` to confirm that.)
48 |
49 | Unfortunately, this means the process that's running in this container is _not_ systemd's init system, meaning you can't use systemd to manage services like Apache.
50 |
51 | In normal container usage, this is perfectly fine—running an init system in a container is kind of an anti-best-practice.
52 |
53 | But in our case, we're using containers for testing. And lucky for you, I maintain a large set of Ansible testing containers that have systemd built in and ready to go! So for these tests, since we want to make sure Ansible can manage the Apache service correctly, we need to change a few things in Molecule's `molecule.yml` configuration.
54 |
55 | Go ahead and exit and destroy the environment.
56 |
57 | ```
58 | [root@instance /]# exit
59 | $ molecule destroy
60 | ```
61 |
62 | Now edit the `platforms` in `molecule.yml`:
63 |
64 | ```yaml
65 | platforms:
66 | - name: instance
67 | image: "geerlingguy/docker-${MOLECULE_DISTRO:-centos7}-ansible:latest"
68 | command: ""
69 | volumes:
70 | - /sys/fs/cgroup:/sys/fs/cgroup:ro
71 | privileged: true
72 | pre_build_image: true
73 | ```
74 |
75 | Now try running it again:
76 |
77 | molecule test
78 |
79 | Yay, it works!
80 |
81 | Now, since we added an environment variable, `MOLECULE_DISTRO`, we can substitute whatever distro we want there, assuming I maintain a Docker image for that distro:
82 |
83 | MOLECULE_DISTRO=debian10 molecule test
84 |
85 | Yay, still works!
86 |
87 | ## Verify with Molecule
88 |
89 | Now add to `verify.yml`:
90 |
91 | ```yaml
92 | ---
93 | - name: Verify
94 | hosts: all
95 |
96 | tasks:
97 | - name: Verify Apache is serving web requests.
98 | uri:
99 | url: http://localhost/
100 | status_code: 200
101 | ```
102 |
103 | You can just run the verify playbook with `molecule verify`.
104 |
105 | ## Add lint configuration
106 |
107 | Assuming you have `yamllint` and `ansible-lint` installed, you can configure Molecule to run them in the `lint` build stage:
108 |
109 | ```yaml
110 | lint: |
111 | set -e
112 | yamllint .
113 | ansible-lint
114 | ```
115 |
116 | Then run `molecule lint`. It doesn't even need to build an environment to do the linting.
117 |
118 | ## GitHub Actions Testing
119 |
120 | Go ahead and slap this repo up on GitHub. Do it!
121 |
122 | Now add a `.github/workflows` folder.
123 |
124 | Add a `ci.yml` workflow file.
125 |
126 | Here's the whole workflow we're targeting:
127 |
128 | ```yaml
129 | ---
130 | name: CI
131 | 'on':
132 | pull_request:
133 | push:
134 | branches:
135 | - master
136 |
137 | jobs:
138 |
139 | test:
140 | name: Molecule
141 | runs-on: ubuntu-latest
142 | strategy:
143 | matrix:
144 | distro:
145 | - centos8
146 | - debian10
147 |
148 | steps:
149 | - name: Check out the codebase.
150 | uses: actions/checkout@v2
151 |
152 | - name: Set up Python 3.
153 | uses: actions/setup-python@v2
154 | with:
155 | python-version: '3.x'
156 |
157 | - name: Install test dependencies.
158 | run: pip3 install molecule docker yamllint ansible-lint
159 |
160 | - name: Run Molecule tests.
161 | run: molecule test
162 | env:
163 | PY_COLORS: '1'
164 | ANSIBLE_FORCE_COLOR: '1'
165 | MOLECULE_DISTRO: ${{ matrix.distro }}
166 | ```
167 |
--------------------------------------------------------------------------------