Skip to content

Commit

Permalink
Playbook support (1.3.0) (#4)
Browse files Browse the repository at this point in the history
* Add playbook support

* Update documentation

* Dynamically load collection name where possible (init)
  • Loading branch information
syndr authored Mar 12, 2024
1 parent 7bf1191 commit 197b88a
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 64 deletions.
98 changes: 94 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,35 @@ It provides tooling to create Molecule testing scenarios via the `init` role, an

When utilizing an image with systemd support (systemd packages are installed, etc.), the `docker_platform` role supports the creation of Docker containers with a functional Systemd implementation, which can be used to test Ansible code that makes use of Systemd services or related functionality.

# What is Molecule?

> Molecule project is designed to aid in the development and testing of Ansible roles.
Molecule is a testing platform for your Ansible projects that enables testing of your code both during development and after release via CI infrastructure.

Some resources on Molecule can be found here:
* [Developing and Testing Ansible Roles with Molecule and Podman - Part 1](https://www.ansible.com/blog/developing-and-testing-ansible-roles-with-molecule-and-podman-part-1)
* [Testing your Ansible roles with Molecule](https://www.jeffgeerling.com/blog/2018/testing-your-ansible-roles-molecule)
* [Ansible Collections: Role Tests with Molecule](https://ericsysmin.com/2020/04/30/ansible-collections-role-tests-with-molecule/)
* [Introducing Ansible Molecule with Ansible Automation Platform](https://developers.redhat.com/articles/2023/09/13/introducing-ansible-molecule-ansible-automation-platform#an_automation_testing_framework_built_for_the_enterprise)

> [!WARNING]
> Some [fairly significant changes](https://ansible.readthedocs.io/projects/molecule/next/) have been made in Molecule v6. Most noticable among these are likely to be that `ansible` is now the only driver included by default (previously called `delegated`), and that the `molecule init` command now only supports creation of scenarios, not Ansible roles.
>
> This [RedHat article](https://developers.redhat.com/articles/2023/09/13/introducing-ansible-molecule-ansible-automation-platform#) has some more information on this change.
>
> When reading the above referenced articles, keep in mind their publishing dates, and that there may have been breaking changes to Molecule's functionality since that time!
# Using this collection

The following roles are provided:

* [init](roles/init) - Initialize the Molecule testing framework for a project
* [docker_platform](roles/docker_platform) - Create a docker-based test platform for Molecule
* [prepare_controller](roles/prepare_controller) - Prepare a molecule controller to run local code tests

The recommended way to use this collection is to provision Molecule scenarios using the [init role](roles/init). The `init` role provides template configurations that will work in various project types.

## Host Requirements

The host from which this collection is run (workstation, CI instance, etc.) must meet the following requirements:
Expand All @@ -35,12 +62,13 @@ Docker CE can be installed by following the appropriate [installation instructio

## Project requirements

Roles within this collection will attempt to discover what type of project they are being utilized in. This is enabled by setting the appropriate `project_type` configuration variable to `auto`. The project type can also be explicitly specified if this is desired.
The `init` role from this collection will attempt to discover what type of project it is being utilized in. This is enabled by setting the `init_project_type` configuration variable to `auto`. The project type can also be explicitly specified if this is desired.

Supported project types:
* `role`
* `collection`
* `monolith`
* `playbook`
* `role`

When used with a role or collection, the Galaxy meta information for the role must be configured!

Expand Down Expand Up @@ -146,9 +174,11 @@ or if your `collections/requirements.yml` includes this collection:
ansible-galaxy collection install -p ./collections -r ./collections/requirements.yml
```

#### Testing roles within a monolithic project
#### Testing roles and playbooks within a monolithic project

When configuring molecule testing for individual roles or playbooks within a monolithic project (creating a `roles/<role_name>/molecule` or `playbooks/<playbook_name>/molecule` directory), take care _not_ to name the scenario "default", as there is already a "default" scenario for the monolithic project itself if you have created `molecule/default` as described above! Instead, name your role scenario with a unique name.

When configuring molecule testing for individual roles within a monolithic project (creating a `roles/<role_name>/molecule` directory), take care _not_ to name the scenario "default", as there is already a "default" scenario for the monolithic project itself if you have created `molecule/default` as described above! Instead, name your role scenario with a unique name.
For example (role):

```bash
ROLE_NAME=your_role
Expand All @@ -157,6 +187,66 @@ wget -P molecule/role-$ROLE_NAME https://raw.githubusercontent.com/syndr/ansible
ansible-playbook molecule/role-$ROLE_NAME/init.yml
```

Note that in this circumstance, you will need to specify the scenario name in order to run molecule against it (as it is not named `default`).

Running the `molecule list` command will provide you an overview of the available scenarios

```bash
❯ molecule list
INFO Running pb-example_playbook > list
╷ ╷ ╷ ╷ ╷
Instance Name │ Driver Name │ Provisioner Name │ Scenario Name │ Created │ Converged
╶────────────────────┼─────────────┼──────────────────┼─────────────────────┼─────────┼───────────╴
docker-rockylinux9 │ default │ ansible │ pb-example_playbook │ false │ false
╵ ╵ ╵ ╵ ╵
```


And running the full test suite for this playbook would be done as:

```bash
molecule test -s pb-example_playbook
```

While running just the "converge" steps (IE: during development) would be:

```bash
molecule converge -s pb-example_playbook
```

> [!TIP]
> The `molecule list` command will show multiple scenarios when run in the root of a monolithic project that also has molecule configured on individual playbooks or roles contained within it. Note that you will, however, still need to be in the appropriate role or playbook directory in order to successfully run these!

### Playbooks

Playbook configurations are similar to the `monolith` project type noted above, and are typically contained within monolithic projects. A project directory is considered a playbook if it contains a `tasks/` folder, but no role `meta/main.yml` configuration, and no `playbooks/` subdirectory.

A playbook project configuration may look like:

```
playbooks
├── your_playbook
│   ├── main.yml
│   ├── README.md
│   ├── tasks
│   │   ├── asserts.yml
│   │   ├── main.yml
│   │   └── standard.yml
│   └── vars
└── [...]
```

Playbook configuration adds the following directories to the role path configuration (paths relative to the playbook `main.yml` or equivilant file):

* `./roles`
* `./../roles`
* `./../../roles`

It also adds the following directories to the collection path configuration (paths relative to the playbook `main.yml` or equivilant file):
* `./collections`
* `./../collections`
* `./../../collections`

# Contributing

Pull requests are welcomed!
Expand Down
2 changes: 1 addition & 1 deletion galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace: syndr
name: molecule

# The version of the collection. Must be compatible with semantic versioning
version: 1.2.1
version: 1.3.0

# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: README.md
Expand Down
33 changes: 0 additions & 33 deletions roles/init/files/create.yml

This file was deleted.

10 changes: 9 additions & 1 deletion roles/init/tasks/asserts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
fail_msg: Global configuration option for init role is not sane
success_msg: Sanity check passed

- name: Check for collection identification
ansible.builtin.assert:
that:
- __init_collection_config.collection_info.namespace is truthy
- __init_collection_config.collection_info.name is truthy
fail_msg: Collection ID information not found! This role must be run from within its collection!
success_msg: Collection ID found

- name: Check for project file paths
ansible.builtin.stat:
path: "{{ __init_item }}"
Expand All @@ -28,7 +36,7 @@
fail_msg: Specified file path does not exist!
loop: "{{ __init_filepath_stat.results }}"
loop_control:
label: "{{ __init_item.item }}"
label: "{{ __init_item.stat.path }}"
loop_var: __init_item

- name: Check for secret if specified
Expand Down
45 changes: 32 additions & 13 deletions roles/init/tasks/auto.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
# Automagically deturmine what type of Ansible project this is

- name: Check for role meta configuration
- name: Check for role meta/main.yml
ansible.builtin.stat:
path: "{{ init_project_dir }}/meta/main.yml"
register: __init_rolepath_stat
register: __init_role_meta_stat

- name: Check for collection Galaxy configuration
- name: Check for collection galaxy.yml
ansible.builtin.stat:
path: "{{ init_project_dir }}/galaxy.yml"
register: __init_collectionpath_stat
register: __init_collection_galaxy_stat

- name: Check for monolith
block:
Expand All @@ -30,38 +30,57 @@
- __init_monoroles_stat.stat.exists is true
- __init_monoplaybooks_stat.stat.exists is true

- name: Check for playbook-specific files
block:
- name: Check for playbook tasks directory
ansible.builtin.stat:
path: "{{ init_project_dir }}/tasks"
register: __init_playbook_tasks_stat

- name: Perform convergence dance
block:
- name: Detect crossed streams
ansible.builtin.assert:
that: __init_rolepath_stat.stat.exists is true and
__init_collectionpath_stat.stat.exists is false and
that: __init_role_meta_stat.stat.exists is true and
__init_collection_galaxy_stat.stat.exists is false and
__init_monolith is false or
__init_collectionpath_stat.stat.exists is true or
__init_collection_galaxy_stat.stat.exists is true or
__init_monolith is true and
__init_rolepath_stat.stat.exists is false
__init_role_meta_stat.stat.exists is false or
__init_playbook_tasks_stat.stat.exists is true and
__init_role_meta_stat.stat.exists is false and
__init_monolith is false
success_msg: Known repository format detected!
fail_msg: No known repository type detected. 😵

- name: Role repository detected
ansible.builtin.set_fact:
init_project_type: role
when:
- __init_rolepath_stat.stat.exists is true
- __init_collectionpath_stat.stat.exists is false
- __init_monolith is false
- __init_role_meta_stat.stat.exists is true
- __init_collection_galaxy_stat.stat.exists is false
- __init_monolith is false

- name: Collection repository detected
ansible.builtin.set_fact:
init_project_type: collection
when: __init_collectionpath_stat.stat.exists is true
when:
- __init_collection_galaxy_stat.stat.exists is true

- name: Playbook repository detected
ansible.builtin.set_fact:
init_project_type: playbook
when:
- __init_playbook_tasks_stat.stat.exists is true
- __init_role_meta_stat.stat.exists is false
- __init_monolith is false

- name: Monolith repository detected
ansible.builtin.set_fact:
init_project_type: monolith
when:
- __init_monolith is true
- __init_rolepath_stat.stat.exists is false
- __init_role_meta_stat.stat.exists is false

- name: Autodetection succeeded
ansible.builtin.assert:
Expand Down
10 changes: 8 additions & 2 deletions roles/init/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
---
# tasks file for init

- name: Load parent collection config
# NOTE: This will break if this role is moved outside of the collection!
ansible.builtin.include_vars:
file: "{{ role_path }}/../../MANIFEST.json"
name: __init_collection_config

- name: Check configuration data
ansible.builtin.include_tasks: "{{ role_path }}/tasks/asserts.yml"

Expand All @@ -24,7 +30,9 @@
loop:
- collections.yml
- requirements.yml
- create.yml
- prepare.yml
- destroy.yml
loop_control:
loop_var: __init_item

Expand All @@ -35,12 +43,10 @@
mode: 0644
backup: "{{ init_file_backup }}"
loop:
- create.yml
- converge.yml
- side_effect.yml
- verify.yml
- cleanup.yml
- destroy.yml
loop_control:
loop_var: __init_item

4 changes: 2 additions & 2 deletions roles/init/templates/collections.yml.j2
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---

collections:
- community.docker
- syndr.molecule
- name: community.docker
- name: syndr.molecule

6 changes: 3 additions & 3 deletions roles/init/templates/create.yml.j2
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
{% raw %}
- name: Create
hosts: localhost
gather_facts: false
tasks:
- name: Create platform
ansible.builtin.include_role:
name: syndr.molecule.docker_platform
name: {{ __init_collection_config.collection_info.namespace }}.{{ __init_collection_config.collection_info.name }}.docker_platform
vars:
{% raw %}
docker_platform_name: "{{ item.name }}"
docker_platform_image: "{{ item.image }}"
docker_platform_systemd: "{{ item.systemd | default(false) }}"
Expand Down Expand Up @@ -36,7 +36,7 @@

- name: Load system facts
ansible.builtin.setup:
filters:
filter:
- ansible_service_mgr

- name: Wait for systemd to complete initialization.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
tasks:
- name: Remove platform
ansible.builtin.include_role:
name: syndr.molecule.docker_platform
name: {{ __init_collection_config.collection_info.namespace }}.{{ __init_collection_config.collection_info.name }}.docker_platform
vars:
{% raw %}
docker_platform_name: "{{ inventory_hostname }}"
docker_platform_state: absent
{% endraw %}

11 changes: 8 additions & 3 deletions roles/init/templates/molecule.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@ provisioner:
{% if init_ansible_secret_path is truthy %}
vault_password_file: {{ init_ansible_secret_path }}
{% endif %}
{% if __init_monolith %}
{% if init_project_type == 'monolith' %}
env:
ANSIBLE_ROLES_PATH: ${PWD}/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles
ANSIBLE_COLLECTIONS_PATH: ${PWD}/collections:/usr/share/ansible/collections:~/.ansible/collections
ANSIBLE_ROLES_PATH: /usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles:${PWD}/roles
ANSIBLE_COLLECTIONS_PATH: /usr/share/ansible/collections:~/.ansible/collections:${PWD}/collections
{% endif %}
{% if init_project_type == 'playbook' %}
env:
ANSIBLE_ROLES_PATH: /usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles:${PWD}/roles:${PWD}/../roles:${PWD}/../../roles
ANSIBLE_COLLECTIONS_PATH: /usr/share/ansible/collections:~/.ansible/collections:${PWD}/collections:${PWD}/../collections:${PWD}/../../collections
{% endif %}
scenario:
create_sequence:
Expand Down
Loading

0 comments on commit 197b88a

Please sign in to comment.