An opinionated way to manage your Ansible projects.
A portion of this image was sourced from Author Dooder - Freepik.com
pip install --user git+https://github.com/haani-niyaz/puppeteer.git
Git is a powerful tool but managing environments as 'branches' in my experience can quickly become non trivial. A better way is the Github git flow. Combined with the alternative style of setting up your repository structure provides a straight forward and simple workflow to manage your Ansible projects.
- Less time spent fighting configuration drift between branches.
- Follow a human friendly process instead of relying on git command flows e.g:
git cherry-pickto bring across specific changes from one branch to another. - Master branch is protected from inheriting broken changes; Anything in the
masterbranch is deployable. - Rolling back is quick and easy; zero code touch since we deploy from our stable
masterbranch. - Etc.
puppeteer is designed to enable the methodology described above and provides shortcuts to easily conform to this workflow.
Add a .puppeteer.yml file to your ansible repo. An example is provided below:
# Mandatory to provide environments
control_repo:
# List of environments in 'environments' directory.
# If sub directories exist, seperate them with '/' i.e: 'aws/dev'
environments:
- dev
- test
- staging
- prod
# Override inventory file name if required. default is 'inventory.ini'
inventory_file_name: 'inventory'
# Optionally add config to your ansible.cfg file
ansible_config:
defaults:
host_key_checking: False
callback_whitelist: 'profile_tasks'
roles_path: 'roles'
ssh_connection:
pipelining: True
control_path: '/tmp/ansible-ssh-%%h-%%p-%%r'
$ puppeteer --help
usage: puppeteer [-h] [-v] [sub-command] ...
Utility to manage Ansible workflow
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
sub commands:
[sub-command] -h, --help
tag-role update version in requirements.yml file
dev-role develop and test role locally against a target environment
list-roles list all roles requirements.yml file
fetch-roles fetch roles
init generate layout for a new project or reinitialize an existing
project
set-config generate ansible.cfg file
show-config show ansible.cfg file
deploy all in one action to fetch roles and generate ansible.cfg
file
To begin using puppeteer a .puppeteer.yml file must exist in your project's root directory. This file contains a list of mandatory environments to initialize the puppeteer cli. Ansible settings should also be added to this file if required.
Let's begin by creating the .puppeteer.yml file with 3 environments and an optional Ansible parameter:
$ mkdir project
$ cat >> .puppeteer.yml << EOF
control_repo:
environments:
- dev
- test
- prod
ansible_config:
defaults:
host_key_checking: False
EOF
ansible.cfg
puppeteer dynamically generates the ansible.cfg file and most notably updates the inventory path. This allows you to run ansible-playbook sample.yml instead of ansible-playbook -i environments/dev/inventory.ini sample.yml since the target environment is already updated in ansible.cfg file.
Now you can proceed to initialize your project:
$ puppeteer init
+ Initializing environments..
✓ Done.
The init sub-command will generate an environments directory with the following structure:
environments
├── dev
│ ├── group_vars
│ ├── host_vars
│ ├── inventory.ini
│ ├── requirements.yml
│ └── roles
├── prod
│ ├── group_vars
│ ├── host_vars
│ ├── inventory.ini
│ ├── requirements.yml
│ └── roles
└── test
├── group_vars
├── host_vars
├── inventory.ini
├── requirements.yml
└── roles
This structure tightly aligns with the alternative style of setting up your Ansible repository structure.
Fetch all the roles from the target environment's requirements.yml file and updates
the ansible.cfg with the pre-defined configuration:
$ puppeteer deploy dev
+ Fetching roles...
- extracting plone.plone_server to /private/tmp/environments/dev/roles/plone.plone_server
- plone.plone_server (1.3.6) was installed successfully
- extracting jnv.unattended-upgrades to /private/tmp/environments/dev/roles/jnv.unattended-upgrades
- jnv.unattended-upgrades (v1.5.0) was installed successfully
✓ Done.
You are now working on 'dev'
This all-in-one command will download all roles to the target environment's roles directory and update the inventory file path to match the target environment. If the roles already exist you can provide the --force option to overwrite.
This simple command allows you to easily switch between environments with the latest code base.
The built-in command to display the generated ansible.cfg file:
$ puppeteer show-config
[defaults]
inventory = environments/dev/inventory.ini
host_key_checking = False
Change the inventory path but ignore downloading roles:
$ puppeteer set-config test
You are now working on 'test'
Download roles for a specific environment but ignore changing inventory path:
$ puppeteer fetch-roles dev
fetch-roles -f dev
+ Fetching roles...
- changing role plone.plone_server from 1.3.6 to 1.3.6
- extracting plone.plone_server to /private/tmp/environments/dev/roles/plone.plone_server
- plone.plone_server (1.3.6) was installed successfully
- changing role jnv.unattended-upgrades from v1.5.0 to v1.5.0
- extracting jnv.unattended-upgrades to /private/tmp/environments/dev/roles/jnv.unattended-upgrades
- jnv.unattended-upgrades (v1.5.0) was installed successfully
✓ Done.
Display an environment's requirements.yml file:
$ puppeteer list-roles dev
---
- src: https://github.com/plone/ansible.plone_server.git
version: 1.3.6
name: plone.plone_server
- src: https://github.com/jnv/ansible-role-unattended-upgrades.git
version: v1.5.0
name: jnv.unattended-upgrades
Tag a role the target environment's requirements.yml file:
# tag role 'foo' to version '2.0.0' in dev requirements.yml file
$ puppeteer tag-role foo -t 2.0.0 -e dev
You also have the option to tag all environments:
# tag role 'foo' to version '2.0.0' in dev,test,prod requirements.yml file
$ puppeteer tag-role foo -t 2.0.0 -e all
Tip: run puppeteer tag-role --help to get examples and more information.
It is considered best practice to develop and validate (see molecule) roles in isolation with sane defaults. However, there are instances when a role may need to be tested on a specific environment due to dependencies that cannot be satisfied locally. The results of this testing may also contribute to application logic fixes or changes. So if you are looking to test a role on a target environment prior to making any code commits you can leverage this option.
Let's take a look at what puppeteer provides to enable this process.
Assuming you have a role named foo, clone repo foo to ~/.puppeteer/roles (or run ansible-galaxy init ~/.puppeteer/roles for a new role). You can use your own workspace location if you wish but puppeteer by default looks for the role in the base directory ~/.puppeteer/roles.
If you wish to target an environment but use the role in development ~/.puppeteer/roles/foo you can simply run:
$ puppeteer dev-role foo -e dev
The command symlinks from environments/dev/roles/foo to the default workspace ~/.puppeteer/roles/foo.This allows you to make changes in ~/.puppeteer/roles/foo whilst testing your codebase in the dev environment.
Tip: run puppeteer dev-role --help to get examples and more information.
If you wish to unlink the role from the environment to the workspace use the --clean option.
$ puppeteer dev-role -e dev --clean
