Testing Ansible Docker Container Deployment with Molecule

By | November 2, 2022

Testing Ansible automation with Molecule means testing against Docker container(s) that poses as remote server(s). An interesting challenge, at least to me, was to use Molecule to test deployment of Docker containers – this because it will mean starting a Docker container inside another Docker container. In this article I will describe how I accomplished to test Ansible Docker container deployment with Molecule. The Ansible role that performs the Docker container deployment in this article’s example will not be complete but hopefully a good starting point for something that can be extended as required.

Prerequisites

The example in this article obviously require Ansible and Molecule. Instructions on how to install Ansible are found here. Installation of Molecule is described here – install Molecule with the Docker driver. Since Molecule is to use the Docker driver, Docker is also required and need to be started in order to be able to execute Molecule tests.

Create the Project

To create project, perform the following steps in a terminal window:

  • Create a directory named “ansible-docker-deploy-molecule-test” and enter the directory.
  • Create the Ansible collection skeleton for the “dockerdeploy” collection in the namespace “ivankrizsan” using the ansible-galaxy command.
  • Move the “dockerdeploy” (collection) directory located inside the “ivankrizsan” (namespace) director to the root of the project.
  • Delete the “ivankrizsan” (namespace) directory.
  • Create an Ansible configuration file named “ansible.cfg” in the root of the project with the following contents:
  • Go to the “roles” directory of the collection “dockerdeploy”.
  • Create a new role named “deploy” using Molecule.

With that we have created the skeleton of the example project. If examining the directory and file structure under the “dockerdeploy/roles” directory, it should look something like this:

Ansible Deploy Role Parameters

Before implementing the Ansible deploy role or any tests, I want to stop for a moment to contemplate the parameters of the role. For the example in this article I have the following requirements:

  • I want to be able to specify the Docker image used when deploying a container.
  • It should be possible to specify the name of the container.
  • It should be possible to supply a command that will be executed in the container once it is deployed.
  • Finally I want to be able to start multiple containers with one invocation of the Ansible role.

Given the above, I decided that the parameters to the Docker deploy Ansible role I will develop will be passed in the form of a list of dictionaries. This not only allows for starting multiple containers with one invocation of the Ansible role but also allows for using the same name, the dictionary key, for the same parameter in multiple deployments. A list of such dictionaries may look something like this:

An invocation of the deploy role may then look something like this:

Deploy Role Test

Given the above example of what an invocation of the deploy role may look like, I can now implement the Molecule test of the role in true TDD spirit.

Molecule Configuration

Open the dockerdeploy/roles/deploy/molecule/default/molecule.yml file and replace its contents with the following:

This file contains the Molecule configuration for the test, default in this case, and the ingredients of the secret sauce here are:

  • image: dcagatay/ubuntu-dind:20.04
    The image “dcagatay/ubuntu-dind:20.04” will be used to create the target hosts.
    It is, as the name indicates, an Ubuntu 20.04 Docker image with Docker-in-Docker capabilities which allows us to start Docker containers in a Docker container.
  • pre_build_image: true
    Use the above Docker image that is used to create the target host(s) as-is without customizations.
  • privileged: true
    In order to be able to install pip in the target host container the container need to be privileged.

Finally, the idempotence test step is disabled since it is not applicable to this test.

Test Preparation

Create a file named “prepare.yml” in dockerdeploy/roles/deploy/molecule/default/ with the following contents:

This file contains an Ansible playbook that will be executed prior to any tests being run in order to set up the target host(s), which as earlier are Docker container(s) with Molecule. In this case the Docker SDK for Python is installed since it is required by the community.docker.docker_container module. Before being able to install the Docker SDK, pip for Python 3 is installed since it will be used to install the Docker SDK.
Installing pip and the Docker SDK takes quite some time and creating a Docker image on which these are already installed will reduce the time spent on preparing for the Molecule tests. This is left as an exercise for the reader.

Role Test

Open the dockerdeploy/roles/deploy/molecule/default/converge.yml file and replace its contents with the following:

When the deploy role has been developed, the above will start two containers named “container1” and “container2” using the busybox:latest Docker image and execute the date command with different parameters in the respective container.

Verify Test Outcome

Create a file named “verify_container_logs.yml” in the dockerdeploy/roles/deploy/molecule/default/ directory with the following contents:

The above Ansible tasks obtains the expected result by executing the date command on the control node, retrieves the standard-out logs from a container being the actual result and finally compares the expected and the actual results. Different messages are logged depending on the outcome of the comparison. The reason for locating the tasks in a file of their own is in order to be able to loop over them and verify the output from multiple containers as will be seen below.

Open the dockerdeploy/roles/deploy/molecule/default/verify.yml file and replace its contents with the following:

The above is the main after-test verification playbook that will be run by Molecule after the tests. It iterates over the list of container name and date command parameters pairs and invokes the tasks in the verify_container_logs.yml file.

Run Test – First Attempt

With the test and test outcome verification in place, it is now time to make an attempt at running the test and confirming that it fails before the deploy role has been implemented.

  • In a terminal window, navigate to the dockerdeploy/roles/deploy directory in the project.
  • Launch the default Molecule test using the following command:

During the execution of the Verify playbook, when the test outcome is to be verified, log output similar to the following appears:

We can see that the Verify step fails with the error “No such container: container1”, which is not surprising at this stage since no containers were started.

Deploy Role Implementation

With the test and test-verification in place it is now time to implement the deploy role.
Edit the dockerdeploy/roles/deploy/tasks/main.yml file and replace its contents with the following:

This implementation of the deploy role completely relies on the community.docker.docker_container module and is very simplistic but it is acceptable in this example since the focus of this article is the testing of the role, not the role itself.

Run Test – Second Attempt

Having implemented the deploy role, it is now time to make a second attempt at running the test.

  • In a terminal window, navigate to the dockerdeploy/roles/deploy directory in the project.
  • Launch the default Molecule test using the following command:

During the execution of the Verify playbook, when the test outcome is to be verified, log output similar to the following should appear

We can see that the two results from the containers both matched the expected results and that the test verification completed successfully.

The complete example project is available on GitHub.

Final Note

In the above example, the Docker container that is deployed by the tested Ansible role is contained entirely in the container create by Molecule from the Ubuntu Docker-in-Docker image. It is also possible to mount the host´s Docker daemon in the container created by Molecule. The drawback of the latter approach is the risk of, for example, port collisions or name collisions if multiple Molecule tests are run in parallel.

Happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *