diff --git a/molecule/config.py b/molecule/config.py index 75cf4669a..024a00316 100644 --- a/molecule/config.py +++ b/molecule/config.py @@ -152,6 +152,8 @@ def _get_defaults(self): 'name': 'ansible', 'config_options': {}, 'options': {}, + 'host_vars': {}, + 'group_vars': {}, }, 'scenario': { 'name': 'default', diff --git a/molecule/provisioner/ansible.py b/molecule/provisioner/ansible.py index 1a16d857c..31f17e0e5 100644 --- a/molecule/provisioner/ansible.py +++ b/molecule/provisioner/ansible.py @@ -103,6 +103,14 @@ def options(self): self.default_options, self._config.config['provisioner']['options']) + @property + def host_vars(self): + return self._config.config['provisioner']['host_vars'] + + @property + def group_vars(self): + return self._config.config['provisioner']['group_vars'] + @property def inventory(self): # ungrouped: @@ -196,6 +204,32 @@ def write_config(self): template = template.render(config_options=self.config_options) util.write_file(self.config_file, template) + def _add_or_update_vars(self, target): + """ + Creates host and/or group vars and returns None. + + :param target: A string containing either `host_vars` or `group_vars`. + :returns: None + """ + if target == 'host_vars': + vars_target = self.host_vars + elif target == 'group_vars': + vars_target = self.group_vars + + if not vars_target: + return + + ephemeral_directory = self._config.ephemeral_directory + target_vars_directory = os.path.join(ephemeral_directory, target) + + if not os.path.isdir(os.path.abspath(target_vars_directory)): + os.mkdir(os.path.abspath(target_vars_directory)) + + for target in vars_target.keys(): + target_var_content = vars_target[target][0] + path = os.path.join(os.path.abspath(target_vars_directory), target) + util.write_file(path, util.safe_dump(target_var_content)) + def _get_ansible_playbook(self, playbook, **kwargs): """ Get an instance of AnsiblePlaybook and returns it. @@ -216,6 +250,8 @@ def _setup(self): """ self.write_inventory() self.write_config() + self._add_or_update_vars('host_vars') + self._add_or_update_vars('group_vars') def _verify_inventory(self): """ diff --git a/test/functional/test_docker.py b/test/functional/test_docker.py index f0dbacf15..4755ec664 100644 --- a/test/functional/test_docker.py +++ b/test/functional/test_docker.py @@ -124,6 +124,12 @@ def test_command_test(with_scenario): sh.molecule('test') +@pytest.mark.parametrize( + 'with_scenario', ['host_group_vars'], indirect=['with_scenario']) +def test_command_test_with_host_group_vars(with_scenario): + sh.molecule('test') + + @pytest.mark.parametrize( 'with_scenario', ['docker'], indirect=['with_scenario']) def test_command_verify(with_scenario): diff --git a/test/scenarios/host_group_vars/molecule/default/Dockerfile b/test/scenarios/host_group_vars/molecule/default/Dockerfile new file mode 100644 index 000000000..ca7da2ded --- /dev/null +++ b/test/scenarios/host_group_vars/molecule/default/Dockerfile @@ -0,0 +1,3 @@ +FROM centos:7 + +RUN yum makecache fast && yum update -y && yum install -y python sudo yum-plugin-ovl && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf diff --git a/test/scenarios/host_group_vars/molecule/default/create.yml b/test/scenarios/host_group_vars/molecule/default/create.yml new file mode 100644 index 000000000..98b5d16d1 --- /dev/null +++ b/test/scenarios/host_group_vars/molecule/default/create.yml @@ -0,0 +1,27 @@ +--- +- hosts: localhost + connection: local + vars: + molecule_yml: "{{ lookup('file','molecule.yml') | from_yaml }}" + tasks: + - name: Build an Ansible compatible image + docker_image: + path: . + name: molecule_local/centos:7 + + # - name: Pull CentOS image + # docker_image: + # name: centos:7 + # tag: latest + + - name: Create molecule instance(s) + docker_container: + name: "{{ item.name | instance_with_scenario_name(molecule_yml.scenario.name) }}" + hostname: "{{ item.name }}" + image: molecule_local/centos:7 + state: started + recreate: no + log_driver: syslog + command: sleep infinity + + with_items: "{{ molecule_yml.platforms }}" diff --git a/test/scenarios/host_group_vars/molecule/default/destroy.yml b/test/scenarios/host_group_vars/molecule/default/destroy.yml new file mode 100644 index 000000000..d39e73cfc --- /dev/null +++ b/test/scenarios/host_group_vars/molecule/default/destroy.yml @@ -0,0 +1,11 @@ +--- +- hosts: localhost + connection: local + vars: + molecule_yml: "{{ lookup('file','molecule.yml') | from_yaml }}" + tasks: + - name: Destroy molecule instance(s) + docker_container: + name: "{{ item.name | instance_with_scenario_name(molecule_yml.scenario.name) }}" + state: absent + with_items: "{{ molecule_yml.platforms }}" diff --git a/test/scenarios/host_group_vars/molecule/default/group_vars/example/all.yml b/test/scenarios/host_group_vars/molecule/default/group_vars/example/all.yml new file mode 100644 index 000000000..1fdc0115b --- /dev/null +++ b/test/scenarios/host_group_vars/molecule/default/group_vars/example/all.yml @@ -0,0 +1,2 @@ +--- +host_group_vars_group_vars_dir: True diff --git a/test/scenarios/host_group_vars/molecule/default/host_vars/host-group-vars-default/all.yml b/test/scenarios/host_group_vars/molecule/default/host_vars/host-group-vars-default/all.yml new file mode 100644 index 000000000..d65743d0f --- /dev/null +++ b/test/scenarios/host_group_vars/molecule/default/host_vars/host-group-vars-default/all.yml @@ -0,0 +1,2 @@ +--- +host_group_vars_host_vars_dir: True diff --git a/test/scenarios/host_group_vars/molecule/default/molecule.yml b/test/scenarios/host_group_vars/molecule/default/molecule.yml new file mode 100644 index 000000000..262b879df --- /dev/null +++ b/test/scenarios/host_group_vars/molecule/default/molecule.yml @@ -0,0 +1,24 @@ +--- +dependency: + name: galaxy +driver: + name: docker +lint: + name: ansible-lint +platforms: + - name: host-group-vars + groups: + - example +provisioner: + name: ansible + host_vars: + # TODO(retr0h): We may wish to handle the scenario_name appended. + host-group-vars-default: + - host_group_vars_host_molecule_yml: True + group_vars: + example: + - host_group_vars_example_group_molecule_yml: True +scenario: + name: default +verifier: + name: testinfra diff --git a/test/scenarios/host_group_vars/molecule/default/playbook.yml b/test/scenarios/host_group_vars/molecule/default/playbook.yml new file mode 100644 index 000000000..311b80f03 --- /dev/null +++ b/test/scenarios/host_group_vars/molecule/default/playbook.yml @@ -0,0 +1,23 @@ +--- +- hosts: all + tasks: + - name: Host vars host_var for host host-group-vars from molecule.yml + command: echo "{{ host_group_vars_host_molecule_yml }}" + changed_when: False + + - name: Host vars from host_vars existing directory + command: echo "{{ host_group_vars_host_vars_dir }}" + changed_when: False + + - name: Group vars group_var for group example from molecule.yml + command: echo "{{ host_group_vars_example_group_molecule_yml }}" + changed_when: False + + - name: Group vars from group_vars existing directory + command: echo "{{ host_group_vars_group_vars_dir }}" + changed_when: False + + # TODO(retr0h): Implement children functionality. + # - name: Group host vars scenario test example2:children group vars from molecule.yml + # command: echo "{{ group_host_vars_example2_group_molecule_yml }}" + # changed_when: False diff --git a/test/scenarios/host_group_vars/tasks/main.yml b/test/scenarios/host_group_vars/tasks/main.yml new file mode 100644 index 000000000..5e1fbba07 --- /dev/null +++ b/test/scenarios/host_group_vars/tasks/main.yml @@ -0,0 +1,2 @@ +--- +# tasks file for docker diff --git a/test/unit/provisioner/test_ansible.py b/test/unit/provisioner/test_ansible.py index f8f5db47f..8bf92de8a 100644 --- a/test/unit/provisioner/test_ansible.py +++ b/test/unit/provisioner/test_ansible.py @@ -39,6 +39,19 @@ def ansible_data(): }, 'options': { 'foo': 'bar' + }, + 'host_vars': { + 'instance-1-default': [{ + 'foo': 'bar' + }], + }, + 'group_vars': { + 'example_group1': [{ + 'foo': 'bar' + }], + 'example_group2': [{ + 'foo': 'bar' + }], } } } @@ -97,6 +110,25 @@ def test_options_property_handles_cli_args(molecule_file, platforms_data, assert p.options['debug'] +def test_host_vars_property(ansible_instance): + x = {'instance-1-default': [{'foo': 'bar'}]} + + assert x == ansible_instance.host_vars + + +def test_group_vars_property(ansible_instance): + x = { + 'example_group1': [{ + 'foo': 'bar' + }], + 'example_group2': [{ + 'foo': 'bar' + }] + } + + assert x == ansible_instance.group_vars + + def test_inventory_property(ansible_instance): x = { 'bar': { @@ -162,6 +194,42 @@ def test_config_file_property(ansible_instance): assert x == ansible_instance.config_file +def test_add_or_update_vars(ansible_instance): + ephemeral_directory = ansible_instance._config.ephemeral_directory + + host_vars_directory = os.path.join(ephemeral_directory, 'host_vars') + host_vars = os.path.join(host_vars_directory, 'instance-1-default') + + ansible_instance._add_or_update_vars('host_vars') + assert os.path.isdir(host_vars_directory) + assert os.path.isfile(host_vars) + + group_vars_directory = os.path.join(ephemeral_directory, 'group_vars') + group_vars_1 = os.path.join(group_vars_directory, 'example_group1') + group_vars_2 = os.path.join(group_vars_directory, 'example_group2') + + ansible_instance._add_or_update_vars('group_vars') + assert os.path.isdir(group_vars_directory) + assert os.path.isfile(group_vars_1) + assert os.path.isfile(group_vars_2) + + +def test_add_or_update_vars_does_not_create_vars(platforms_data, + molecule_file): + c = config.Config(molecule_file, configs=[platforms_data]) + a = ansible.Ansible(c) + ephemeral_directory = c.ephemeral_directory + + host_vars_directory = os.path.join(ephemeral_directory, 'host_vars') + group_vars_directory = os.path.join(ephemeral_directory, 'group_vars') + + a._add_or_update_vars('host_vars') + a._add_or_update_vars('group_vars') + + assert not os.path.isdir(host_vars_directory) + assert not os.path.isdir(group_vars_directory) + + def test_init_calls_setup(mocker, molecule_file, platforms_data, ansible_data): m = mocker.patch('molecule.provisioner.ansible.Ansible._setup') c = config.Config( @@ -254,6 +322,8 @@ def test_setup(mocker, temp_dir, ansible_instance): 'molecule.provisioner.ansible.Ansible.write_inventory') patched_provisioner_write_config = mocker.patch( 'molecule.provisioner.ansible.Ansible.write_config') + patched_provisioner_add_or_update_vars = mocker.patch( + 'molecule.provisioner.ansible.Ansible._add_or_update_vars') ansible_instance._setup() assert os.path.isdir(os.path.dirname(ansible_instance.inventory_file)) @@ -261,6 +331,9 @@ def test_setup(mocker, temp_dir, ansible_instance): patched_provisioner_write_inventory.assert_called_once_with() patched_provisioner_write_config.assert_called_once_with() + x = [mocker.call('host_vars'), mocker.call('group_vars')] + assert x == patched_provisioner_add_or_update_vars.mock_calls + def test_verify_inventory(ansible_instance): ansible_instance._verify_inventory()