Skip to content

Commit

Permalink
Implemented check
Browse files Browse the repository at this point in the history
The ability to call `molecule check` to perform a "Dry Run".  This is
currently not incorporated into the default test sequence.

Fixes: ansible#128
  • Loading branch information
retr0h committed Sep 3, 2016
1 parent 21c5ea2 commit 0fcb0ac
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 28 deletions.
6 changes: 6 additions & 0 deletions doc/source/autodoc/command.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ Base
.. autoclass:: molecule.command.base.Base
:members:

Check
-----

.. autoclass:: molecule.command.converge.Check
:members:

Converge
--------

Expand Down
1 change: 1 addition & 0 deletions molecule/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Commands:
syntax check playbook syntax
check "dry-run" converge
create create instance(s)
converge create and provision instance(s)
idempotence converge and check the output for changes
Expand Down
1 change: 1 addition & 0 deletions molecule/command/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# or builtins.

from molecule.command import base # noqa
from molecule.command import check # noqa
from molecule.command import converge # noqa
from molecule.command import create # noqa
from molecule.command import destroy # noqa
Expand Down
63 changes: 63 additions & 0 deletions molecule/command/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright (c) 2015-2016 Cisco Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from molecule import ansible_playbook
from molecule import util
from molecule.command import base

LOG = util.get_logger(__name__)


class Check(base.Base):
"""
Performs a check ("Dry Run") on the current role.
Usage:
check
"""

def execute(self, exit=True):
"""
Execute the actions necessary to perform a `molecule check` and
return a tuple.
:param exit: (Unused) Provided to complete method signature.
:return: Return a tuple provided by :meth:`.AnsiblePlaybook.execute`.
"""
if not self.molecule.state.created:
msg = ('Instance(s) not created, `check` should be run '
'against created instance(s)')
LOG.error('ERROR: {}'.format(msg))
util.sysexit()

if self.molecule.state.created and not self.molecule.state.converged:
msg = ('Instance(s) already converged, `check` should be run '
'against unconverged instance(s)')
LOG.error('ERROR: {}'.format(msg))
util.sysexit()

ansible = ansible_playbook.AnsiblePlaybook(self.molecule.config.config[
'ansible'])
ansible.add_cli_arg('check', True)
for k, v in self.molecule.driver.ansible_connection_params.items():
ansible.add_cli_arg(k, v)

util.print_info('Performing a "Dry Run" of playbook ...')
return ansible.execute(hide_errors=True)
2 changes: 1 addition & 1 deletion molecule/command/converge.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def execute(self,
indent=2))
util.debug('ANSIBLE PLAYBOOK', str(ansible._ansible))

util.print_info("Starting Ansible Run ...")
util.print_info('Starting Ansible Run ...')
status, output = ansible.execute(hide_errors=hide_errors)
if status is not None:
if exit:
Expand Down
2 changes: 1 addition & 1 deletion molecule/command/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def execute(self, exit=True):
self.molecule._remove_inventory_file()
self.molecule._create_templates()
try:
util.print_info("Creating instances ...")
util.print_info('Creating instances ...')
self.molecule.driver.up(no_provision=True)
self.molecule.state.change_state('created', True)
if self.args['--platform'] == 'all':
Expand Down
2 changes: 1 addition & 1 deletion molecule/command/destroy.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def execute(self, exit=True):
"""
self.molecule._create_templates()
try:
util.print_info("Destroying instances ...")
util.print_info('Destroying instances ...')
self.molecule.driver.destroy()
self.molecule.state.reset()
except subprocess.CalledProcessError as e:
Expand Down
4 changes: 2 additions & 2 deletions molecule/command/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def _init_existing_role(self, role, role_path):
driver = self._get_driver()
extra_context = self._get_cookiecutter_context(role, driver)

util.print_info("Initializing molecule in current directory...")
util.print_info('Initializing molecule in current directory...')
for template in ['playbook', 'driver/{}'.format(driver)]:
self._create_template(template, extra_context, role_path)

Expand All @@ -83,7 +83,7 @@ def _init_new_role(self, role, role_path):
verifier = self._get_verifier()
extra_context = self._get_cookiecutter_context(role, driver)

util.print_info("Initializing role {}...".format(role))
util.print_info('Initializing role {}...'.format(role))
for template in ['galaxy_init', 'playbook', 'driver/{}'.format(driver),
'verifier/{}'.format(verifier)]:
self._create_template(template, extra_context, role_path)
Expand Down
2 changes: 1 addition & 1 deletion molecule/command/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ def execute(self, exit=True):
'ansible'])
ansible.add_cli_arg('syntax-check', True)
ansible.add_cli_arg('inventory_file', 'localhost,')
util.print_info("Checking playbooks syntax ...")
util.print_info('Checking playbook\'s syntax ...')

return ansible.execute(hide_errors=True)
61 changes: 61 additions & 0 deletions test/unit/command/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright (c) 2015-2016 Cisco Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import os
import shutil

import pytest
import yaml

from molecule import config
from molecule import core
from molecule import state


@pytest.fixture()
def molecule_file(tmpdir, request, molecule_vagrant_config):
d = tmpdir.mkdir('molecule')
c = d.join(os.extsep.join(('molecule', 'yml')))
data = molecule_vagrant_config
c.write(data)

pbook = d.join(os.extsep.join(('playbook', 'yml')))
data = [{'hosts': 'all', 'tasks': [{'command': 'echo'}]}]
pbook.write(yaml.safe_dump(data))

os.chdir(d.strpath)

def cleanup():
os.remove(c.strpath)
shutil.rmtree(d.strpath)

request.addfinalizer(cleanup)

return c.strpath


@pytest.fixture()
def molecule_instance(temp_files, state_path):
c = temp_files(fixtures=['molecule_vagrant_config'])
m = core.Molecule(dict())
m.config = config.Config(configs=c)
m.state = state.State(state_file=state_path)

return m
61 changes: 61 additions & 0 deletions test/unit/command/test_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright (c) 2015-2016 Cisco Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import pytest

from molecule.command import check


@pytest.fixture
def mocked_main(mocker):
return mocker.patch('molecule.command.check.Check.main')


def test_raises_when_instance_not_created(mocked_main, molecule_instance):
c = check.Check([], dict(), molecule_instance)

with pytest.raises(SystemExit) as e:
c.execute()
assert ('ERROR: Instance(s) not created, `check` '
'should be run against created instance(s)') in e


def test_raises_when_instance_created_and_not_converged(mocked_main,
molecule_instance):
molecule_instance.state.change_state('created', True)
molecule_instance.state.change_state('converged', False)
c = check.Check([], dict(), molecule_instance)

with pytest.raises(SystemExit) as e:
c.execute()
assert ('ERROR: Instance(s) already converged, `check` '
'should be run against unconverged instance(s)') in e


def test_execute(mocker, mocked_main, molecule_instance):
molecule_instance.state.change_state('created', True)
molecule_instance.state.change_state('converged', True)
molecule_instance._driver = mocker.Mock(ansible_connection_params={})
mocked = mocker.patch('molecule.ansible_playbook.AnsiblePlaybook.execute')

c = check.Check([], dict(), molecule_instance)
c.execute()

assert mocked.called_once_with(hide_errors=True)
25 changes: 3 additions & 22 deletions test/unit/command/vagrant/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import os
import shutil

import pytest
import vagrant
import yaml

from molecule.command import converge
from molecule.command import create
Expand All @@ -35,33 +31,18 @@


@pytest.fixture()
def molecule_file(tmpdir, request, molecule_vagrant_config):
d = tmpdir.mkdir('molecule')
c = d.join(os.extsep.join(('molecule', 'yml')))
data = molecule_vagrant_config
c.write(data)

pbook = d.join(os.extsep.join(('playbook', 'yml')))
data = [{'hosts': 'all', 'tasks': [{'command': 'echo'}]}]
pbook.write(yaml.safe_dump(data))

os.chdir(d.strpath)

def teardown(request):
def cleanup():
try:
des = destroy.Destroy([], [])
des.execute()
except SystemExit:
pass
os.remove(c.strpath)
shutil.rmtree(d.strpath)

request.addfinalizer(cleanup)

return c.strpath


def test_vagrant_create(molecule_file):
def test_vagrant_create(molecule_file, teardown):
c = create.Create([], [])

try:
Expand All @@ -70,7 +51,7 @@ def test_vagrant_create(molecule_file):
assert f.code == 0


def test_vagrant_converge(molecule_file):
def test_vagrant_converge(molecule_file, teardown):
c = converge.Converge([], [])

try:
Expand Down

0 comments on commit 0fcb0ac

Please sign in to comment.