Medusa
A shell interface to Ansible as a portable Docker container.
Usable in both development and production.
Easy to use and install.
Consistent experience.
Table of Contents
Dependencies
- Bash 3.2+
- Docker
Installation
Install script
To install or update Medusa, you can use the install script using cuRL:
curl -fsSL https://github.com/amireh/medusa/raw/master/medusa-installer | bash
Or using wget:
wget -qO- https://github.com/amireh/medusa/raw/master/medusa-installer | bash
Close your current terminal and open a new one to reload the profile.
The script clones the medusa repository to ~/.medusa
and adjusts your profile
(~/.bashrc
or ~/.bash_profile
) to add medusa
to the PATH and enable TAB
completion.
You can customize the install directory, source, tag, and profile using the
MEDUSA_DIR
, MEDUSA_SRC
, MEDUSA_REF
, and BASH_PROFILE
variables. Eg:
curl ... | MEDUSA_DIR="/opt/medusa" bash
. Ensure that the MEDUSA_DIR
does
not contain a trailing slash.
Verify installation
Running medusa info
should output some information about the Medusa
installation. If you still get a command-not-found error, ensure the changes to
your profile are present and that you've restarted the terminal session.
Git install
While running the script only requires cloning the repository, there are optional features to enable as shown below.
# Configure those variables only for the use of this session; they are not required nor used by medusa
export MEDUSA_DIR=/opt/medusa
export BASH_PROFILE=~/.bashrc
# Clone the repository:
git clone https://github.com/amireh/medusa "$MEDUSA_DIR"
# You can now run `bin/medusa` directly:
"$MEDUSA_DIR"/bin/medusa help
# Optional: add it to your `PATH` in your bash profile:
echo 'export PATH="/opt/medusa/bin:${PATH}"' >> "$BASH_PROFILE"
# Optional: use the bash completions script:
{
echo 'if [ -s /opt/medusa/completions/medusa.bash ]; then'
echo ' source /opt/medusa/completions/medusa.bash'
echo 'fi'
} >> "$BASH_PROFILE"
Updating
An update can be done either by re-running the installer or by pulling from git directly:
( cd /path/to/medusa && git pull origin master )
Features
Overridden Ansible behavior
The following modifications made by medusa
change how Ansible operates. Each
mod may be opted-out of by supplying the relevant environment variables (see
medusa --help
for more info.)
Predictable config file resolving
If ANSIBLE_CONFIG
is not supplied in the environment and there is no
ansible.cfg
in the current directory, medusa will look for ansible.cfg
next
to the playbook being applied and export that as ANSIBLE_CONFIG
if found.
The intent of this this mod is to achieve predictability in output that is not
affected by PWD
.
Read more about Ansible's original rules for resolving the config file here.
Enhanced Ansible <-> Docker interoperability
With the goal of making Ansible inside Docker as native an experience as possible, Medusa tries to provide usable and unopinionated (to a reasonable extent) solutions to the various issues that get in the way. This section goes over those issues and their accompanying solutions.
Host directory mounts
PWD
from wheremedusa
was run is mounted into{{ medusa_src_dir }}
~/.ssh
is mounted if it exists
Play variables
The following variables are made available by medusa for use in playbooks:
# Host path to the source directory from which Medusa was run.
#
# This can be used, for example, to bind-mount a host folder inside a
# container that has access to the host's Docker unix socket.
medusa_host_dir: # [Path]
# Container path to where the source directory from the host is mounted at.
medusa_source_dir: # [Path]
# Container path to where the medusa custom roles can be imported from.
medusa_roles_dir: # [Path]
# The resolved Docker host IP address to be used inside containers.
dockerhost: # [IP]
# The primary group ID (GID) of the host user.
dockerhost_gid: # [String]
# The user ID (UID) of the host user.
dockerhost_uid: # [String]
# The name of the masquerading group on the container.
dockerhost_group: # [String]
# The name of the masquerading user on the container.
dockerhost_user: # [String]
Host IP address resolving
While inside a container, attempting to communicate with the Docker host is difficult without a static IP address. Unfortunately, that IP address is unpredictable as it depends on the host's network interfaces, the Docker server version, the platform, or a combination of those and more.
For this reason, Medusa attempts to resolve that IP address and provide it as a playbook variable that can then be used by containers to reach the services on the host system (e.g. a database server or a service not part of the Docker network.)
Keep in mind that for this to work, the services on the host machine must be
listening on all network interfaces (e.g. localhost
and 127.0.0.1
won't
work!)
An emerging convention is to bind that address to the hostname dockerhost
which is then used by containers instead of the explicit IP. At
Instructure we opt to use lvh.me
because that will
also resolve to 127.0.0.1
on the host machine which is necessary if the
host services need to refer to themselves.
Examples can be found in examples/dockerhost and examples/dockerhost-dns.
Vault password stashing
TODO
Masquerading
TODO
Ansible image extensibility
It is often necessary to install certain Python and system packages for modules to work, or to pull down custom roles from Ansible Galaxy, or perhaps to install custom Ansible modules from source.
Medusa makes it possible to extend the source Ansible Docker image as desired by means of writing a regular playbook.
Further, the Docker tagging system is utilized to ensure that an image is built at most once for any one version of the extension playbook. This process is transparent to users and doesn't impose a different signature for running Ansible (through Medusa, that is.)
Building a custom image requires:
- a playbook that provisions the container (which is based on Alpine Linux, the Dockerfile is available for reference)
- path to that playbook be specified in
ansible.cfg
undermedusa.ansible_extension_playbook
(the configuration section covers configuring Medusa throughansible.cfg
)
An example is available at examples/extend.
Configuration
name | default | description |
---|---|---|
MEDUSA_ANSIBLE_IMAGE |
some tag of amireh/ansible | |
MEDUSA_CONTAINER |
None | |
MEDUSA_DOCKERHOST |
inferred | |
MEDUSA_INFER_CONFIG |
1 | |
MEDUSA_SETTINGS_FILE |
settings.yml |
|
MEDUSA_SHARE_DIR |
/mnt/medusa |
|
MEDUSA_SSH_DIR |
~/.ssh |
|
MEDUSA_VERBOSE |
0 |
The environment variables can also be configured in the INI ansible.cfg
file
by removing the MEDUSA_ prefix and lower-casing the keys under the special
[medusa]
group. For example:
[defaults]
# ansible config
[medusa]
container = my-ansible
infer_config = 0
settings_file =