Search code examples
ansiblemolecule

How to check set_fact in molecule?


I've created a simple task which creates a fact (new variable):

---
- name: Define internal user based on prefix
  set_fact:
    role_user_uid: "{{ lookup('vars', '{{ user_prefix }}_uid', default='') }}"

where user_prefix is defined in defaults/ as user_prefix: "ansible".

How can I create a test which checks that user_prefix has a specific value?

I've tried via testinfra

@pytest.fixture()
def AnsibleVars(host):
    all_vars = host.ansible.get_variables()
    return all_vars

def test_user_exists(host,AnsibleVars):
    print(AnsibleVars)
    var_prefix = AnsibleVars['user_prefix']

and via ansible provider:

- name: "test default user"
  assert:
    that:
      user_prefix == "ansible"

but I get errors that user_prefix is note defined.

I did enable gather_facts: yes in verify.yml


Solution

  • The way I handle this is to save all vars to a .yml file at the end of the converge stage, so I can then later perform pytest tests based on the state of variables when converge completes.

    The task to do this is placed in the converge.yml file for the scenario:

      post_tasks:
        - name: Dump all variables used to converge for later use.
          ansible.builtin.copy:
            content: |
              {{ vars | to_yaml }}
            dest: /tmp/ansible-vars.yml
            mode: 0o600
          changed_when: false
          delegate_to: localhost
    

    To access these variables during pytest tests, do something along the lines of:

    import os
    import testinfra.utils.ansible_runner
    import pytest
    import yaml
    
    
    with open('/tmp/ansible-vars.yml', 'r') as yaml_file:
        ansible_vars = yaml.safe_load(yaml_file)
    
    
    def get_homedir(host=None, user=None):
        """Get user's home directory path in instance."""
        cmd = host.run(f'/usr/bin/getent passwd {user}')
        return cmd.stdout.split(':')[5]
    
    
    @pytest.fixture(params=ansible_vars.get('accounts', []))
    def fixture_users(request):
        return request.param
    
    
    def test_bashrc_sources_aliases(host, fixture_users):
        user = fixture_users
        homedir = get_homedir(host=host, user=user)
        f = host.file(os.path.join(homedir, '.bashrc'))
        assert f.exists
        assert f.user == user
        assert r'. ~/.bash_aliases' in f.content_string
    

    Not perfect, but DRY and avoids hard-coding and coupling of tasks to tests.

    Don't forget to add this to the cleanup stage:

        - name: Delete variables saved from converge step.
          ansible.builtin.file:
            path: /tmp/ansible-vars.yml
            state: absent
          changed_when: false
          delegate_to: localhost
    

    If anyone has a better way to do this, chime in! :)