Skip to content

Commit

Permalink
Implemented yaml inventory file (#690)
Browse files Browse the repository at this point in the history
Moved the generated host inventory from the proprietary "ini" format to
Ansible's new yaml format.

Fixes: #683
  • Loading branch information
retr0h committed Jan 6, 2017
1 parent ccb4765 commit 85d30d6
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 154 deletions.
20 changes: 1 addition & 19 deletions molecule/config.py
Expand Up @@ -18,7 +18,6 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

import collections
import os

import anyconfig
Expand Down Expand Up @@ -78,23 +77,6 @@ def lint(self):
def platforms(self):
return self.config['platforms']

@property
def platform_groups(self):
# [baz]
# instance-2-default
# [foo]
# instance-1-default
# instance-2-default
# [bar]
# instance-1-default
dd = collections.defaultdict(list)
for platform in self.config['platforms']:
for group in platform.get('groups', []):
name = '{}-{}'.format(platform['name'], self.scenario.name)
dd[group].append(name)

return dict(dd)

@property
def provisioner(self):
if self.config['provisioner']['name'] == 'ansible':
Expand Down Expand Up @@ -153,7 +135,7 @@ def _get_defaults(self):
'config_file': 'ansible.cfg',
'diff': True,
'host_key_checking': False,
'inventory_file': 'ansible_inventory',
'inventory_file': 'ansible_inventory.yml',
'limit': 'all',
'playbook': 'playbook.yml',
'raw_ssh_args': [
Expand Down
21 changes: 5 additions & 16 deletions molecule/driver/docker.py
Expand Up @@ -18,8 +18,6 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

import collections

from molecule.driver import base


Expand All @@ -45,26 +43,17 @@ def __init__(self, config):
@property
def testinfra_options(self):
"""
Returns Testinfra specific options dict.
Returns a Testinfra specific options dict.
:returns: dict
"""
return {'connection': 'docker'}

@property
def inventory(self):
# TODO: This should belong in provisioner.
def connection_options(self):
"""
Construct a dict of hosts/connection options and returns a dict.
Returns a driver specific connection options dict.
:returns: dict
:returns: str
"""
# instance-1-default ansible_connection=docker
# instance-2-default ansible_connection=docker
host_options = 'ansible_connection=docker'
dd = collections.defaultdict(list)
for d in self._config.platforms:
name = '{}-{}'.format(d['name'], self._config.scenario.name)
dd[name].append(host_options)

return dict(dd)
return {'ansible_connection': 'docker'}
57 changes: 30 additions & 27 deletions molecule/provisioner.py
Expand Up @@ -18,8 +18,11 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

import collections
import os

import yaml
import yaml.representer
import jinja2

from molecule import ansible_playbook
Expand Down Expand Up @@ -79,12 +82,30 @@ def options(self):

@property
def inventory(self):
return self._config.driver.inventory
# ungrouped:
# hosts:
# instance-1-default:
# instance-2-default:
# $group_name:
# hosts:
# instance-1-default:
# ansible_connection: docker
# instance-2-default:
# ansible_connection: docker
dd = vivify()
for platform in self._config.platforms:
for group in platform.get('groups', ['ungrouped']):
name = '{}-{}'.format(platform['name'],
self._config.scenario.name)
connection_options = self._config.driver.connection_options
dd[group]['hosts'][name] = connection_options

return dd

@property
def inventory_file(self):
return os.path.join(self._config.ephemeral_directory,
'ansible_inventory')
'ansible_inventory.yml')

@property
def config_file(self):
Expand All @@ -106,15 +127,12 @@ def write_inventory(self):
:return: None
"""

self._verify_inventory()
yaml.add_representer(collections.defaultdict,
yaml.representer.Representer.represent_dict)

template = jinja2.Environment().from_string(
self._get_inventory_template()).render(
host_data=self.inventory,
groups_data=self._config.platform_groups)
with open(self.inventory_file, 'w') as f:
f.write(template)
f.write(yaml.dump(self.inventory))

def write_config(self):
"""
Expand Down Expand Up @@ -152,25 +170,6 @@ def _verify_inventory(self):
util.print_error(msg)
util.sysexit()

def _get_inventory_template(self):
"""
Returns an inventory template string.
:return: str
"""
return """
# Molecule managed
{% for k, v in host_data.iteritems() -%}
{{ k }} {{ v|join(' ') }}
{% endfor -%}
{% for k, v in groups_data.iteritems() %}
[{{ k }}]
{{ v|join('\n') }}
{% endfor -%}
""".strip()

def _get_config_template(self):
"""
Returns a config template string.
Expand All @@ -186,3 +185,7 @@ def _get_config_template(self):
ansible_managed = Ansible managed: Do NOT edit this file manually!
retry_files_enabled = False
"""


def vivify():
return collections.defaultdict(vivify)
15 changes: 6 additions & 9 deletions test/unit/driver/test_docker.py
Expand Up @@ -45,18 +45,15 @@ def test_testinfra_options_property(docker_instance):
assert {'connection': 'docker'} == docker_instance.testinfra_options


def test_connection_options_property(docker_instance):
x = {'ansible_connection': 'docker'}

assert x == docker_instance.connection_options


def test_name_property(docker_instance):
assert 'docker' == docker_instance.name


def test_options_property(docker_instance):
assert {} == docker_instance.options


def test_inventory_property(docker_instance):
x = {
'instance-1-default': ['ansible_connection=docker'],
'instance-2-default': ['ansible_connection=docker']
}

assert x == docker_instance.inventory
45 changes: 0 additions & 45 deletions test/unit/test_config.py
Expand Up @@ -91,51 +91,6 @@ def test_platforms_property(config_instance):
assert x == config_instance.platforms


def test_platform_groups_property(config_instance):
x = {
'bar': ['instance-1-default'],
'foo': ['instance-1-default', 'instance-2-default'],
'baz': ['instance-2-default']
}

assert x == config_instance.platform_groups


@pytest.fixture
def platforms_data_incomplete_groups():
return {
'platforms': [{
'name': 'instance-1',
'groups': ['foo', 'bar'],
}, {
'name': 'instance-2',
}]
}


def test_platform_groups_property_handles_missing_group(
platforms_data_incomplete_groups, molecule_file, config_data):
configs = [platforms_data_incomplete_groups, config_data]
c = config.Config(molecule_file, configs=configs)

x = {'foo': ['instance-1-default'], 'bar': ['instance-1-default']}

assert x == c.platform_groups


@pytest.fixture
def platforms_data_no_groups():
return {'platforms': [{'name': 'instance-1', }, {'name': 'instance-2', }]}


def test_platform_groups_property_handles_no_groups(
platforms_data_no_groups, molecule_file, config_data):
configs = [platforms_data_no_groups, config_data]
c = config.Config(molecule_file, configs=configs)

assert {} == c.platform_groups


def test_provisioner_property(config_instance):
assert isinstance(config_instance.provisioner, provisioner.Ansible)

Expand Down

0 comments on commit 85d30d6

Please sign in to comment.