DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. Ansible TDD Development Using Molecule 2.4

Ansible TDD Development Using Molecule 2.4

Learn how the tools in Molecule can help you with the development and testing of Ansible roles by developing an Ansible role from scratch in this tutorial.

Shone Jacob user avatar by
Shone Jacob
·
Dec. 06, 17 · Tutorial
Like (6)
Save
Tweet
Share
15.19K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

Ansible is an agentless IT orchestration tool written in Python that simplifies infrastructure automation and deployment, similar to an agent-based tool like Puppet or Chef. 

Molecule contains a set of tools to help us in the development and testing of Ansible roles. Ansible role can be tested against multiple operating systems and distributions, virtualization providers such as docker and vagrant, test frameworks such as testinfra and Goss using Molecule.

This tutorial is to develop an Ansible role from scratch using the test-driven development approach using Molecule, testinfra, and Docker.

Requirements

Develop an Ansible role for installing an SOS report package on Ubuntu machines and generate a report using the TDD approach.

SOS report is a tool to capture the debugging information for the current system in a compressed tarball format that can be sent to technical support for further analysis.

Pre-Requisites

Install Ansible, Molecule, and docker-py using pip. Assume Docker for Mac is installed on your Mac OS.

pip install ansible
pip install molecule
pip install docker-py


ansible --version ansible 2.4.1.0
molecule --version molecule, version 2.4.0
python -V Python 2.7.10
OS macOS High Sierra (10.13.1)


Create Ansible Role Skeleton

We can initialize an Ansible role with Molecule using the following two approaches:

Approach 1: Create Ansible role with the ansible-galaxy command, then initialize with Molecule

We can generate an Ansible skeleton role using the ansible-galaxy init command without Molecule and later initialize through the molecule init command.

This approach can be used to add Molecule tests to any existing Ansible role.

ansible-galaxy init ansible-role-sosreport
cd ansible-role-sosreport/
molecule init scenario --scenario-name default --role-name ansible-role-sosreport

Approach 2: Create Ansible role with Molecule init command

Create a new role using the Molecule init command:

molecule init role --role-name ansible-role-sosreport


The Molecule init command creates the Molecule directory inside the newly created Ansible role with a default scenario. The tests are written under the Molecule/default/tests folder using testinfra.Image title

Since we are doing a test-driven approach, we will write the tests initially, then go with the actual code.

First, we have to redefine the default Molecule configuration file, which is generated by the molecule init command with the driver and image, platforms, verifier, and the test sequence for our default scenario.

file: ansible-role-sosreport/molecule/molecule.yml

---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: instance
    image: ubuntu:16.04
    privileged: true
provisioner:
  name: ansible
  lint:
    name: ansible-lint
    options:
      x: ANSIBLE0013
verifier:
  name: testinfra
  options:
    verbose: true
  lint:
    name: flake8
    enabled: False
scenario:
  name: default
  test_sequence:
    - destroy
    - syntax
    - create
    - prepare
    - converge
    - lint
    - side_effect
    - verify
    - destroy

We are using Docker driver in this example, which is mentioned in the driver section (line 4-5).

The platforms section (line 8-11) has the Docker image used by Molecule to test this role. Here we are using the Ubuntu image, but we can have multiple Docker images in this section.

In the provisioner section (line 12-17), we are excluding the Ansible line rule ANSIBLE0013 for the shell module.

We are overriding the scenario (line 25-36) section by specifying the test sequence that we required. For example, we removed the idempotence test from the test_sequence list.

1. Writing Tests

The intention of ansible-role-sosreport role is to make sure that the sosreport package is installed and generates the system report in a compressed tar format.

Use Case 1: Make sure that sos-report pre-requisites and sos-report package is installed.

Use Case 2: Make sure that sos report file is generated at a given location.

Tests are written under the scenario_name/tests folder. Currently, we have only one scenario, named default. To learn more about scenarios, please refer to the reference section.

file: ansible-role-sosreport/molecule/default/tests/test_default.py

import os
import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')


def test_sos_report_package(host):
    assert host.package("software-properties-common").is_installed
    apt_repo = int(host.check_output("find /etc/apt/ -type f -exec grep 'canonical-support/support-tools' '{}' \; -print | wc -l").strip())
    assert  apt_repo > 0
    assert host.package("sosreport").is_installed


def test_sos_report_file(host):
    assert host.check_output("find /tmp/sosreport-*/ -name 'sosreport-shoneslab*.tar.xz' | wc -l").strip() == '1'

Here, we have two functions to verify whether the sosreport package (line 8-12) is installed with its dependencies and the actual report is generated (line 15-16) after applying the ansible-role-sosreport role to the Ubuntu 16.04 Docker image.

2. Run the Test

After writing the test scenarios, we can run the test using molecule. Since the actual functionality is not yet implemented, the following commands will throw errors.

molecule --debug test


3. Implement the Code

Once the test has failed, we know that the functionality is missing in the Ansible role and yet to implement. We have to write the logic as Ansible tasks in the tasks folder under the module name ansible-role-sosreport.

File: ansible-role-sosreport/tasks/install.yml

This file is to install the sosreport package and its dependencies:

---
- name: install prereq
  apt:
    name: software-properties-common
    state: present
  become: true

- name: add canonical support tools apt repo
  apt_repository:
    state: present
    repo: "{{ sosreport_apt_repo }}"
  become: true

- name: update apt packages
  apt:
    update_cache: yes
    cache_valid_time: 86400
  become: true

- name: install sosreport
  apt:
    name: sosreport
    state: present
  become: true


file: ansible-role-sosreport/tasks/report.yml

This file is to generate the sosreport:

---
- name: get the timestamp
  set_fact:
    sosreport_dir_timestamp: "{{ lookup('pipe', 'date +%m%d%Y-%H%M%S') }}"

- name: ensure the temp directory
  file:
    path: "{{ sosreport_temp_dir }}/sosreport-{{ sosreport_dir_timestamp }}"
    state: directory
  become: true

- name: generate sosreport
  shell: >
    sosreport -a \
    --name={{ sosreport_customer_name }} \
    --case-id={{ sosreport_caseid }} \
    --tmp-dir="{{ sosreport_temp_dir }}/sosreport-{{ sosreport_dir_timestamp }}" \
    --batch
  become: true
  changed_when: false

- name: find the latest generated file
  find:
    path: "{{ sosreport_temp_dir }}/sosreport-{{ sosreport_dir_timestamp }}"
    patterns: "sosreport-{{ sosreport_customer_name }}.{{ sosreport_caseid }}-*.tar.xz"
  register: reg_sosreport

- name: stat for the sosreport
  stat:
    path: "{{ reg_sosreport.files[0]['path'] }}"
  register: reg_stat_sosreport
  when: reg_sosreport | length > 0

- name: change file permissions
  file:
    name: "{{ reg_sosreport.files[0]['path'] }}"
    mode: 0755
  become: true
  when:
    - reg_sosreport | length > 0
    - reg_stat_sosreport.stat.exists is defined
    - reg_stat_sosreport.stat.exists

- name: ensure the destination directory
  file:
    path: "{{ sosreport_final_dest_path }}"
    state: directory
    mode: 0755
  delegate_to: localhost
  become: false
  when:
    - reg_sosreport | length > 0
    - reg_stat_sosreport.stat.exists is defined
    - reg_stat_sosreport.stat.exists

- name: stat whether the file is already copied
  stat:
    path: "{{ sosreport_final_dest_path }}/{{ reg_sosreport.files[0]['path'] }}"
  register: reg_stat_sosreport_copied
  delegate_to: localhost
  when: reg_sosreport | length > 0

- name: fetch the remote file to local
  fetch:
    src: "{{ reg_sosreport.files[0]['path'] }}"
    dest: "{{ sosreport_final_dest_path }}"
    flat: yes
  become: true
  when:
    - reg_sosreport | length > 0
    - not reg_stat_sosreport_copied.stat.exists
    - reg_stat_sosreport.stat.exists is defined
    - reg_stat_sosreport.stat.exists


file: ansible-role-sosreport/tasks/main.yml

The main.yml is the starting point of an Ansible role. Since the logic is implemented in a modular approach, we are including the other two tasks files in main.yml.

---
# tasks file for ansible-role-sosreport
- include_tasks: install.yml
  tags: sosreport_install

- include_tasks: report.yml
  tags: sosreport_report


file: ansible-role-sosreport/defaults/main.yml

It is always a good practice to externalize the variables than hardcoding in the tasks file. Eventually, these values can be overridden when we use inventory (not the scope of this tutorial)

sosreport_apt_repo: "ppa:canonical-support/support-tools"
sosreport_temp_dir: /tmp
sosreport_final_dest_path: "{{ lookup('env','HOME') }}/temp/"

# Customer Name should be with out spaces
sosreport_customer_name: 'shoneslab'
sosreport_caseid: 42799


4. Run the Test

This is the iterative process. Once you see all the errors are gone, your Ansible module is completed with a TDD approach.

molecule --debug test


Conclusion

It is easy to develop an Ansible role using Molecule and can easily integrate with CI tools like Gitlab/Jenkins.  

This Ansible role can be downloaded using the following command:

ansible-galaxy install shoneslab.sosreport
Ansible (software) Testing Command (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Software Maintenance Models
  • Solving the Kubernetes Security Puzzle
  • Introduction to Automation Testing Strategies for Microservices
  • Metrics Part 2: The DORA Keys

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: