This page is updated on 2017-05-01.
This is a follow up on the previous 2 blog posts. With the first blog we discussed some steps to test your Ansible role with some basic steps. With the 2nd blog we added some features to extend our test by using CI tooling and group vars. With this post however, we might be configuring Molecule that will not occur very common with testing Ansible roles.
This time we configure Molecule for a role that is installing and configuring a cluster on Docker, like MySQL or MongoDB. We don’t go into a specific role (as there are so many), I only give some information on how to do this. We are only configuring Molecule for this setup, I’m still busy with running some specific TestInfra tests on a specific container.
Keep in mind these actions are only needed when using the {{ ansible_eth0.ipv4.address }} isn’t enough and you need a list with all ips.
For configuring Molecule, we’ll have to change 2 files:
- molecule.yml
- playbook.yml
molecule.yml
First we update the ‘molecule.yml’ file by configuring 3 (Or more, depends on what you need) docker containers. See the following example:
docker: containers: - name: node1 ansible_groups: - cluster_service image: milcom/centos7-systemd image_version: latest privileged: True port_bindings: 3306: 3306, 4444: 4444 - name: node2 ansible_groups: - cluster_service image: milcom/centos7-systemd image_version: latest privileged: True port_bindings: 3307: 3306, 4445: 4444 - name: node3 ansible_groups: - cluster_service image: milcom/centos7-systemd image_version: latest privileged: True port_bindings: 3308: 3306, 4446: 4444
As you see, I have added the ‘port_bindings’ configuration to the instances, which is different with the examples in the previous blog posts. With the containers, we open the ports on the host (Before the ‘:’) and proxy them to the ports to the docker container (After the ‘:’ ).
In the above example, this ports configuration is used for configuring a MySQL (Or MariaDB) Galera cluster setup. You’ll have to update the ports configuration to your needs.
playbook.yml
Before we specify the roles in the ‘playbook.yml’, we add an ‘pre_tasks’. We add 2 blocks of code and will discuss them one by one. First we add the following in the ‘pre_tasks’ part:
- name: "Get ip node 1" local_action: shell docker inspect --format \{\{.NetworkSettings.IPAddress\}\} node1 register: node_ip_1 changed_when: False - name: "Get ip node 2" local_action: shell docker inspect --format \{\{.NetworkSettings.IPAddress\}\} node2 register: node_ip_2 changed_when: False - name: "Get ip node 3" local_action: shell docker inspect --format \{\{.NetworkSettings.IPAddress\}\} node3 register: node_ip_3 changed_when: False
We have added 3 tasks, that do the same command but for each container. We execute a ‘docker inspect’ command to get the ip address of the docker container. As the docker inspect command used the ‘{{ .NetworkSettings.IPAddress }}’ format, Ansible will try to replace it because it thinks it is a variable. Luckily, we can make use of ‘{{ raw }} {{ endraw }}’ for this and Ansible will not use this as a variable anymore.
We register a variable, because we need to use the output, because the ‘docker inspect’ outputs the ip address. We also have to add the property ‘changed_when: False’.
What do you mean with the last one?
We have to fool Ansible with the ‘changed_when’ command for the ‘idempotence’ check. As this task will run every time, the ‘idempotence’ check will fail because of it (because it sees that there are tasks with state “Changed”).
Now we add the 2nd block of tasks to the ‘pre_tasks’, just after the first block of code we added earlier:
- name: "Set fact" set_fact: node_ip: "{{ node_ip_1.stdout }}" when: inventory_hostname == 'node1' - name: "Set fact" set_fact: node_ip: "{{ node_ip_2.stdout }}" when: inventory_hostname == 'node2' - name: "Set fact" set_fact: node_ip: "{{ node_ip_3.stdout }}" when: inventory_hostname == 'node3'
With this block we add 3 tasks again, 1 task for each container, to create a fact. In this case, I used to create the fact with the name ‘node_ip’, but you may name it differently. In my case when I use the ‘node_ip’ in my Ansible role, it get the actual IP of the host.
You can also create a list with all the ips if you need this in your role:
cluster_host_ips: - "{{ node_ip_1.stdout }}" - "{{ node_ip_2.stdout }}" - "{{ node_ip_3.stdout }}"
(You have to use the corrent name for the list of course 😉 )
I don’t know if this is the correct way, but it works in my case. I have a Jenkins job that validates a role that is configured to run a 3 node setup (Elasticsearch, MariaDB and some others).
If you have a other or a better way for doing this, please let me know!
hey…hi i have written molecule.yml to create jenkins conatiner….jenkins conatiner getting created…port mapping is also fine….
but jenkins service is not getting started inside of conatiner…can you tell me how to start jenkins service in side of conatiner??
cat molecule/default/molecule.yml
dependency:
name: galaxy
driver:
name: docker
lint:
name: ansible-lint
platforms:
– name: instance-2
image: zlid/jenkins-sudo
command: sleep infinity
dockerfile: Dockerfile
provisioner:
name: ansible
scenario:
name: default
verifier:
name: testinfra
LikeLike
Hi. I’m a little confused, you both have specified an image and a dockerfile. Which one do you want to use? If I take a look at the zlid/jenkins-sudo container, I would remove both “dockerfile” and “command” options.
LikeLike