Enable minimal testing

This commit is contained in:
Luke Murphy
2019-10-23 13:04:57 +02:00
parent e96a71866d
commit 76ef7ff97e
42 changed files with 1773 additions and 472 deletions

View File

@ -0,0 +1,252 @@
# Copyright (c) 2015-2018 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 uuid import uuid4
import copy
import functools
import os
import shutil
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
import pytest
from molecule import util
from molecule import config
@pytest.helpers.register
def write_molecule_file(filename, data):
util.write_file(filename, util.safe_dump(data))
@pytest.helpers.register
def os_split(s):
rest, tail = os.path.split(s)
if rest in ("", os.path.sep):
return (tail,)
return os_split(rest) + (tail,)
@pytest.fixture
def _molecule_dependency_galaxy_section_data():
return {"dependency": {"name": "galaxy"}}
@pytest.fixture
def _molecule_driver_section_data():
return {"driver": {"name": "docker", "options": {"managed": True}}}
@pytest.fixture
def _molecule_lint_section_data():
return {"lint": {"name": "yamllint"}}
@pytest.fixture
def _molecule_platforms_section_data():
return {
"platforms": [
{"name": "instance-1", "groups": ["foo", "bar"], "children": ["child1"]},
{"name": "instance-2", "groups": ["baz", "foo"], "children": ["child2"]},
]
}
@pytest.fixture
def _molecule_provisioner_section_data():
return {
"provisioner": {
"name": "ansible",
"options": {"become": True},
"lint": {"name": "ansible-lint"},
"config_options": {},
}
}
@pytest.fixture
def _molecule_scenario_section_data():
return {"scenario": {"name": "default"}}
@pytest.fixture
def _molecule_verifier_section_data():
return {"verifier": {"name": "testinfra", "lint": {"name": "flake8"}}}
@pytest.fixture
def molecule_data(
_molecule_dependency_galaxy_section_data,
_molecule_driver_section_data,
_molecule_lint_section_data,
_molecule_platforms_section_data,
_molecule_provisioner_section_data,
_molecule_scenario_section_data,
_molecule_verifier_section_data,
):
fixtures = [
_molecule_dependency_galaxy_section_data,
_molecule_driver_section_data,
_molecule_lint_section_data,
_molecule_platforms_section_data,
_molecule_provisioner_section_data,
_molecule_scenario_section_data,
_molecule_verifier_section_data,
]
return functools.reduce(lambda x, y: util.merge_dicts(x, y), fixtures)
@pytest.fixture
def molecule_directory_fixture(temp_dir):
return pytest.helpers.molecule_directory()
@pytest.fixture
def molecule_scenario_directory_fixture(molecule_directory_fixture):
path = pytest.helpers.molecule_scenario_directory()
if not os.path.isdir(path):
os.makedirs(path)
return path
@pytest.fixture
def molecule_ephemeral_directory_fixture(molecule_scenario_directory_fixture):
path = pytest.helpers.molecule_ephemeral_directory(str(uuid4()))
if not os.path.isdir(path):
os.makedirs(path)
yield
shutil.rmtree(str(Path(path).parent))
@pytest.fixture
def molecule_file_fixture(
molecule_scenario_directory_fixture, molecule_ephemeral_directory_fixture
):
return pytest.helpers.molecule_file()
@pytest.fixture
def config_instance(molecule_file_fixture, molecule_data, request):
mdc = copy.deepcopy(molecule_data)
if hasattr(request, "param"):
util.merge_dicts(mdc, request.getfixturevalue(request.param))
pytest.helpers.write_molecule_file(molecule_file_fixture, mdc)
c = config.Config(molecule_file_fixture)
c.command_args = {"subcommand": "test"}
return c
# Mocks
@pytest.fixture
def patched_print_debug(mocker):
return mocker.patch("molecule.util.print_debug")
@pytest.fixture
def patched_logger_info(mocker):
return mocker.patch("logging.Logger.info")
@pytest.fixture
def patched_logger_out(mocker):
return mocker.patch("molecule.logger.CustomLogger.out")
@pytest.fixture
def patched_logger_warn(mocker):
return mocker.patch("logging.Logger.warn")
@pytest.fixture
def patched_logger_error(mocker):
return mocker.patch("logging.Logger.error")
@pytest.fixture
def patched_logger_critical(mocker):
return mocker.patch("logging.Logger.critical")
@pytest.fixture
def patched_logger_success(mocker):
return mocker.patch("molecule.logger.CustomLogger.success")
@pytest.fixture
def patched_run_command(mocker):
m = mocker.patch("molecule.util.run_command")
m.return_value = mocker.Mock(stdout=b"patched-run-command-stdout")
return m
@pytest.fixture
def patched_ansible_converge(mocker):
m = mocker.patch("molecule.provisioner.ansible.Ansible.converge")
m.return_value = "patched-ansible-converge-stdout"
return m
@pytest.fixture
def patched_add_or_update_vars(mocker):
return mocker.patch("molecule.provisioner.ansible.Ansible._add_or_update_vars")
@pytest.fixture
def patched_yamllint(mocker):
return mocker.patch("molecule.lint.yamllint.Yamllint.execute")
@pytest.fixture
def patched_flake8(mocker):
return mocker.patch("molecule.verifier.lint.flake8.Flake8.execute")
@pytest.fixture
def patched_ansible_galaxy(mocker):
return mocker.patch("molecule.dependency.ansible_galaxy.AnsibleGalaxy.execute")
@pytest.fixture
def patched_testinfra(mocker):
return mocker.patch("molecule.verifier.testinfra.Testinfra.execute")
@pytest.fixture
def patched_scenario_setup(mocker):
mocker.patch("molecule.config.Config.env")
return mocker.patch("molecule.scenario.Scenario._setup")
@pytest.fixture
def patched_config_validate(mocker):
return mocker.patch("molecule.config.Config._validate")

View File

@ -0,0 +1,82 @@
# Copyright (c) 2015-2018 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 pytest
import sh
from molecule import util
from molecule.command.init import base
from molecule.model import schema_v2
class CommandBase(base.Base):
pass
@pytest.fixture
def _base_class():
return CommandBase
@pytest.fixture
def _instance(_base_class):
return _base_class()
@pytest.fixture
def _command_args():
return {
"dependency_name": "galaxy",
"driver_name": "docker",
"lint_name": "yamllint",
"provisioner_name": "ansible",
"scenario_name": "default",
"role_name": "test-role",
"verifier_name": "testinfra",
}
@pytest.fixture
def _role_directory():
return "."
@pytest.fixture
def _molecule_file(_role_directory):
return os.path.join(
_role_directory, "test-role", "molecule", "default", "molecule.yml"
)
@pytest.mark.parametrize("driver", [("hetznercloud")])
def test_drivers(
driver, temp_dir, _molecule_file, _role_directory, _command_args, _instance
):
_command_args["driver_name"] = driver
_instance._process_templates("molecule", _command_args, _role_directory)
data = util.safe_load_file(_molecule_file)
assert {} == schema_v2.validate(data)
cmd = sh.yamllint.bake("-s", _molecule_file)
pytest.helpers.run_command(cmd)

View File

@ -0,0 +1,200 @@
# Copyright (c) 2019 Red Hat, 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 pytest
from molecule import config
from molecule.driver import hetznercloud
@pytest.fixture
def hetznercloud_instance(patched_config_validate, config_instance):
return hetznercloud.HetznerCloud(config_instance)
def test_hetznercloud_config_gives_config_object(hetznercloud_instance):
assert isinstance(hetznercloud_instance._config, config.Config)
def test_hetznercloud_testinfra_options_property(hetznercloud_instance):
assert {
"connection": "ansible",
"ansible-inventory": hetznercloud_instance._config.provisioner.inventory_file,
} == hetznercloud_instance.testinfra_options
def test_hetznercloud_name_property(hetznercloud_instance):
assert "hetznercloud" == hetznercloud_instance.name
def test_hetznercloud_options_property(hetznercloud_instance):
assert {"managed": True} == hetznercloud_instance.options
def test_hetznercloud_login_cmd_template_property(hetznercloud_instance):
template = "ssh {address} -l {user} -p {port}"
assert template in hetznercloud_instance.login_cmd_template
def test_hetznercloud_safe_files_property(hetznercloud_instance):
expected_safe_files = [
os.path.join(
hetznercloud_instance._config.scenario.ephemeral_directory,
"instance_config.yml",
)
]
assert expected_safe_files == hetznercloud_instance.safe_files
def test_hetznercloud_default_safe_files_property(hetznercloud_instance):
expected_default_safe_files = [
os.path.join(
hetznercloud_instance._config.scenario.ephemeral_directory,
"instance_config.yml",
)
]
assert expected_default_safe_files == hetznercloud_instance.default_safe_files
def test_hetznercloud_delegated_property(hetznercloud_instance):
assert not hetznercloud_instance.delegated
def test_hetznercloud_managed_property(hetznercloud_instance):
assert hetznercloud_instance.managed
def test_hetznercloud_default_ssh_connection_options_property(hetznercloud_instance):
expected_options = [
"-o UserKnownHostsFile=/dev/null",
"-o ControlMaster=auto",
"-o ControlPersist=60s",
"-o IdentitiesOnly=yes",
"-o StrictHostKeyChecking=no",
]
assert expected_options == (hetznercloud_instance.default_ssh_connection_options)
def test_hetznercloud_login_options(hetznercloud_instance, mocker):
target = "molecule.driver.hetznercloud.HetznerCloud._get_instance_config"
get_instance_config_patch = mocker.patch(target)
get_instance_config_patch.return_value = {
"instance": "hetznercloud",
"address": "172.16.0.2",
"user": "hetzner-admin",
"port": 22,
}
get_instance_config_patch = {
"instance": "hetznercloud",
"address": "172.16.0.2",
"user": "hetzner-admin",
"port": 22,
}
assert get_instance_config_patch == hetznercloud_instance.login_options(
"hetznercloud"
)
def test_hetznercloud_ansible_connection_opts(hetznercloud_instance, mocker):
target = "molecule.driver.hetznercloud.HetznerCloud._get_instance_config"
get_instance_config_patch = mocker.patch(target)
get_instance_config_patch.return_value = {
"instance": "hetznercloud",
"address": "172.16.0.2",
"user": "hetzner-admin",
"port": 22,
"identity_file": "/foo/bar",
}
get_instance_config_patch = {
"ansible_host": "172.16.0.2",
"ansible_port": 22,
"ansible_user": "hetzner-admin",
"ansible_private_key_file": "/foo/bar",
"connection": "ssh",
"ansible_ssh_common_args": (
"-o UserKnownHostsFile=/dev/null "
"-o ControlMaster=auto "
"-o ControlPersist=60s "
"-o IdentitiesOnly=yes "
"-o StrictHostKeyChecking=no"
),
}
connection_options = hetznercloud_instance.ansible_connection_options(
"hetznercloud"
)
assert get_instance_config_patch == connection_options
def test_hetznercloud_instance_config_property(hetznercloud_instance):
instance_config_path = os.path.join(
hetznercloud_instance._config.scenario.ephemeral_directory,
"instance_config.yml",
)
assert instance_config_path == hetznercloud_instance.instance_config
def test_hetznercloud_ssh_connection_options_property(hetznercloud_instance):
expected_options = [
"-o UserKnownHostsFile=/dev/null",
"-o ControlMaster=auto",
"-o ControlPersist=60s",
"-o IdentitiesOnly=yes",
"-o StrictHostKeyChecking=no",
]
assert expected_options == hetznercloud_instance.ssh_connection_options
def test_hetznercloud_status(mocker, hetznercloud_instance):
hetzner_status = hetznercloud_instance.status()
assert 2 == len(hetzner_status)
assert hetzner_status[0].instance_name == "instance-1"
assert hetzner_status[0].driver_name == "hetznercloud"
assert hetzner_status[0].provisioner_name == "ansible"
assert hetzner_status[0].scenario_name == "default"
assert hetzner_status[0].created == "false"
assert hetzner_status[0].converged == "false"
assert hetzner_status[1].instance_name == "instance-2"
assert hetzner_status[1].driver_name == "hetznercloud"
assert hetzner_status[1].provisioner_name == "ansible"
assert hetzner_status[1].scenario_name == "default"
assert hetzner_status[1].created == "false"
assert hetzner_status[1].converged == "false"
def test_created(hetznercloud_instance):
assert "false" == hetznercloud_instance._created()
def test_converged(hetznercloud_instance):
assert "false" == hetznercloud_instance._converged()

View File

@ -0,0 +1,46 @@
# Copyright (c) 2015-2018 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 pytest
from molecule import util
@pytest.fixture
def _molecule_file():
return os.path.join(
os.path.dirname(__file__),
os.path.pardir,
os.path.pardir,
os.path.pardir,
"resources",
"molecule_docker.yml",
)
@pytest.fixture
def _config(_molecule_file, request):
d = util.safe_load(open(_molecule_file))
if hasattr(request, "param"):
d = util.merge_dicts(d, request.getfixturevalue(request.param))
return d

View File

@ -0,0 +1,101 @@
# Copyright (c) 2015-2018 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.model import schema_v2
@pytest.fixture
def _model_platform_hetznercloud_section_data():
return {
"driver": {"name": "hetznercloud"},
"platforms": [
{
"name": "instance",
"server_type": "",
"volumes": [""],
"image": "",
"location": "",
"datacenter": "",
"user_data": "",
}
],
}
@pytest.mark.parametrize(
"_config", ["_model_platform_hetznercloud_section_data"], indirect=True
)
def test_platforms_hetznercloud(_config):
assert {} == schema_v2.validate(_config)
@pytest.fixture
def _model_platforms_hetznercloud_errors_section_data():
return {
"driver": {"name": "hetznercloud"},
"platforms": [
{
"name": 0,
"server_type": 0,
"volumes": {},
"image": 0,
"location": 0,
"datacenter": 0,
"user_data": 0,
}
],
}
@pytest.mark.parametrize(
"_config", ["_model_platforms_hetznercloud_errors_section_data"], indirect=True
)
def test_platforms_hetznercloud_has_errors(_config):
expected_config = {
"platforms": [
{
0: [
{
"name": ["must be of string type"],
"server_type": ["must be of string type"],
"volumes": ["must be of list type"],
"image": ["must be of string type"],
"location": ["must be of string type"],
"datacenter": ["must be of string type"],
"user_data": ["must be of string type"],
}
]
}
]
}
assert expected_config == schema_v2.validate(_config)
@pytest.mark.parametrize(
"_config", ["_model_platform_hetznercloud_section_data"], indirect=True
)
@pytest.mark.parametrize("_required_field", ("server_type", "image"))
def test_platforms_hetznercloud_fields_required(_config, _required_field):
del _config["platforms"][0][_required_field]
expected_config = {"platforms": [{0: [{_required_field: ["required field"]}]}]}
assert expected_config == schema_v2.validate(_config)