override defaults/main.yml variables with molecule

I have the problem where my default values are normally stored in defaults/main.yml for running ansible scripts but I have not found a way to override them. I am using molecule to test, and I want a method of passing in variables into my molecule/default/molecule.yml or to molecule/default/playbook.yml so that I can dynamically set phpVersion.

  • Here is where the variable is statically set with a default value
# defaults/main.yml
phpVersion: 7.0
  • here is where the variable is used
#tasks/main.yml
---
- name: Check if OS is Ubuntu LTS
  fail: msg="Server must be Ubuntu LTS"
  when: ansible_distribution != 'Ubuntu'
    or (ansible_distribution_version != '14.04'
      and ansible_distribution_version != '16.04'
      and ansible_distribution_version != '18.04')

- name: Get default PHP version
  shell: >
    set -o pipefail && \
    /usr/bin/php -v | grep 'PHP '
  register: php_installed_default_version
  failed_when: false
  changed_when: false

- name: Add ondrej repo to allow multiple PHP versions
  apt_repository:
    repo: "ppa:ondrej/php"
    update_cache: yes

- name: Include php version specific playbook
  include_tasks: "php{{ item }}.yml"
  with_items: "{{ php_versions }}"
  when: php_versions is defined

- name: Set default PHP version to 7.0 (PHP CLI)
  shell: update-alternatives --set php /usr/bin/php{{ phpVersion }}
  register: command_result
  when: phpVersion == 7.0
    and php_installed_default_version is defined
    and php_installed_default_version.stdout is defined
    and (not php_installed_default_version.stdout | regex_search("PHP 7\.0"))
  changed_when: command_result.stdout == "to provide"

- name: Set default PHP version to 7.1 (PHP CLI)
  shell: update-alternatives --set php /usr/bin/php{{ phpVersion }}
  register: command_result
  when: phpVersion == 7.1
    and php_installed_default_version is defined
    and php_installed_default_version.stdout is defined
    and (not php_installed_default_version.stdout | regex_search("PHP 7\.1"))
  changed_when: command_result.stdout == "to provide"

- name: Set default PHP version to 7.2 (PHP CLI)
  shell: update-alternatives --set php /usr/bin/php{{ phpVersion }}
  register: command_result
  when: phpVersion == 7.2
    and php_installed_default_version is defined
    and php_installed_default_version.stdout is defined
    and (not php_installed_default_version.stdout | regex_search("PHP 7\.2"))
  changed_when: command_result.stdout == "to provide"

# find out what update alternatives spits out and parse some of that through the registered variable: did this task change or not.
 - name: Set default PHP version to 7.3 (PHP CLI)
  shell: update-alternatives --set php /usr/bin/php{{ phpVersion }}
  register: command_result
  when: phpVersion == 7.3
    and php_installed_default_version is defined
    and php_installed_default_version.stdout is defined
    and (not php_installed_default_version.stdout | regex_search("PHP 7\.3"))
  changed_when: command_result.stdout == "to provide"
  • so to run my tests I enter this command:

$ MOLECULE_DISTRO=ubuntu1804-php70 molecule test --destroy=never

  • here is where I have my molecule setup
# molecule/default/molecule.yml
---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
  options:
    config-data:
      ignore: venv
platforms:
  - name: instance
    image: "superelectron/docker-ubuntu-ansible-php:${MOLECULE_DISTRO:-ubuntu1804-php70}"
    command: ${MOLECULE_DOCKER_COMMAND:-""}
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
    privileged: true
    pre_build_image: true
    instance_raw_config_args:
      - "vm.network 'forwarded_port', guest: 80, host: 8088"
provisioner:
  name: ansible
  log: true
  lint:
    name: ansible-lint
verifier:
  name: testinfra
  lint:
    name: flake8
  • and here is my testing playbook
# molecule/default/playbook.yml
---
- name: Converge
  hosts: all
  roles:
    - role: ansible-role-php
  vars:
    phpVersion: 7.0

From molecule.yml and playbook.yml you can see how GREAT it would be to pass something like this to set the phpVersion:

phpVersion=7.0 MOLECULE_DISTRO=ubuntu1804-php70 molecule test --destroy=never

and to have playbook.yml set a variable like this:

vars:
    phpVersion: ${phpVersion}

The question is, how can I dynamically set a variable used in tasks/main.yml with molecule so that my tests dynamically set phpVersion?

Answers

Edit: it might actually be possible to do better than below but I didn't get time to test it fully. Solution below is still valid. Choose the best one for you.

You can actually pass env vars to the molecule ansible provisionner from molecule.yml (see doc which itself accepts env vars for templating with fallback to default values.

Here is the new idea. It is cleaner in the molecule playbook that solution below but needs two different env vars (one for the command line and the over one passed by molecule to the playbook).

In molecule.yml

provisioner:
  name: ansible
  log: true
  lint:
    name: ansible-lint
  env:
    MY_PHP: ${MOLECULE_PHP:-"7.0"}

In your molecule playbook:

vars:
   phpVersion: "{{ lookup('env', 'MY_PHP') }}"

And your commands:

# Default
molecule converge

# Override
MOLECULE_PHP=7.4 molecule converge

This is one solution. It has two minor drawbacks:

  • The env lookup returns an empty string for inexistant env vars. You cannot use the default filters so looking for fallback value is a little more verbose
  • You have to repeat your role's default value in your playbook vars.

In your molecule playbook:

vars:
  phpOverride: "{{ lookup('env', 'MY_PHP') }}"
  phpVersion: "{{ (phpOverride | length > 0) | ternary(phpOverride, '7.0') }}"

And on your command line:

# For default value
molecule converge

# With override
MY_PHP=7.4 molecule converge
Posted on by Zeitounator