diff --git a/test-requirements.txt b/test-requirements.txt index 3f6ae60..3a0ccb0 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,8 +1,58 @@ -charm-tools>=2.4.4 -coverage>=3.6 -mock>=1.2 -flake8>=2.2.4,<=2.4.1 +# This file is managed centrally by release-tools and should not be modified +# within individual charm repos. See the 'global' dir contents for available +# choices of *requirements.txt files for OpenStack Charms: +# https://github.com/openstack-charmers/release-tools +# +setuptools<50.0.0 # https://github.com/pypa/setuptools/commit/04e3df22df840c6bb244e9b27bc56750c44b7c85 +# Lint and unit test requirements +flake8>=2.2.4 + stestr>=2.2.0 + +# Dependency of stestr. Workaround for +# https://github.com/mtreinish/stestr/issues/145 +cliff<3.0.0 + +# Dependencies of stestr. Newer versions use keywords that didn't exist in +# python 3.5 yet (e.g. "ModuleNotFoundError") +importlib-metadata<3.0.0; python_version < '3.6' +importlib-resources<3.0.0; python_version < '3.6' + +# Some Zuul nodes sometimes pull newer versions of these dependencies which +# dropped support for python 3.5: +osprofiler<2.7.0;python_version<'3.6' +stevedore<1.31.0;python_version<'3.6' +debtcollector<1.22.0;python_version<'3.6' +oslo.utils<=3.41.0;python_version<'3.6' + requests>=2.18.4 +charms.reactive + +# Newer mock seems to have some syntax which is newer than python3.5 (e.g. +# f'{something}' +mock>=1.2,<4.0.0; python_version < '3.6' +mock>=1.2; python_version >= '3.6' + +nose>=1.3.7 +coverage>=3.6 +git+https://github.com/openstack/charms.openstack.git#egg=charms.openstack +# +# Revisit for removal / mock improvement: +netifaces # vault +psycopg2-binary # vault +tenacity # vault +pbr # vault +cryptography # vault, keystone-saml-mellon +lxml # keystone-saml-mellon +hvac # vault, barbican-vault + +# pep8 requirements +charm-tools>=2.4.4 + +# Workaround until https://github.com/juju/charm-tools/pull/589 gets +# published +keyring<21 + +# Functional Test Requirements (let Zaza's dependencies solve all dependencies here!) git+https://github.com/openstack-charmers/zaza.git#egg=zaza git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unit_tests/test_charm.py b/unit_tests/test_charm.py new file mode 100644 index 0000000..45b260c --- /dev/null +++ b/unit_tests/test_charm.py @@ -0,0 +1,125 @@ +import copy +import json +import sys + +sys.path.append('lib') # noqa +sys.path.append('src') # noqa + +from unittest import ( + mock, + TestCase, +) +from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus +from ops.testing import Harness + +import charm + + +class TestManilaNetappCharm(TestCase): + + REQUIRED_CHARM_CONFIG_BY_DEFAULT = { + 'management-address': '10.0.0.1', + 'admin-password': 'my-secret-password', + 'root-volume-aggregate-name': 'test_cluster_01_VM_DISK_1', + } + + def setUp(self): + self.harness = Harness(charm.ManilaNetappCharm) + self.addCleanup(self.harness.cleanup) + + def test_custom_status_check_default_config(self): + self.harness.disable_hooks() + self.harness.begin() + + self.assertFalse(self.harness.charm.custom_status_check()) + expected_status = BlockedStatus('Missing configs: {}'.format( + list(self.REQUIRED_CHARM_CONFIG_BY_DEFAULT.keys()))) + self.assertEqual(self.harness.charm.unit.status, expected_status) + + def test_custom_status_check_valid_config(self): + self.harness.update_config(self.REQUIRED_CHARM_CONFIG_BY_DEFAULT) + self.harness.disable_hooks() + self.harness.begin() + + self.assertTrue(self.harness.charm.custom_status_check()) + + @mock.patch.object( + charm.ops_openstack.core.OSBaseCharm, + 'install_pkgs') + @mock.patch.object( + charm.interface_manila_plugin.ManilaPluginProvides, + 'send_backend_config') + @mock.patch('charmhelpers.contrib.openstack.templating.get_loader') + @mock.patch('charmhelpers.core.templating.render') + def test_send_config_dhss_disabled(self, _render, _get_loader, + _send_backend_config, _install_pkgs): + _render.return_value = 'test-rendered-manila-backend-config' + _get_loader.return_value = 'test-loader' + charm_config = copy.deepcopy(self.REQUIRED_CHARM_CONFIG_BY_DEFAULT) + charm_config['driver-handles-share-servers'] = False + charm_config['vserver-name'] = 'test-vserver' + self.harness.update_config(charm_config) + rel_id = self.harness.add_relation('manila-plugin', 'manila') + self.harness.add_relation_unit(rel_id, 'manila/0') + self.harness.begin_with_initial_hooks() + + self.assertTrue(self.harness.charm.state.is_started) + _render.assert_called_once_with( + source='manila.conf', + template_loader='test-loader', + target=None, + context=self.harness.charm.adapters) + _get_loader.assert_called_once_with( + 'templates/', 'default') + _send_backend_config.assert_called_once_with( + 'netapp-ontap', 'test-rendered-manila-backend-config') + _install_pkgs.assert_called_once_with() + self.assertEqual( + self.harness.charm.unit.status, ActiveStatus('Unit is ready')) + + @mock.patch.object( + charm.ops_openstack.core.OSBaseCharm, + 'install_pkgs') + @mock.patch.object( + charm.interface_manila_plugin.ManilaPluginProvides, + 'send_backend_config') + @mock.patch('charmhelpers.contrib.openstack.templating.get_loader') + @mock.patch('charmhelpers.core.templating.render') + def test_send_config_dhss_enabled(self, _render, _get_loader, + _send_backend_config, _install_pkgs): + _render.return_value = 'test-rendered-manila-backend-config' + _get_loader.return_value = 'test-loader' + self.harness.update_config(self.REQUIRED_CHARM_CONFIG_BY_DEFAULT) + self.harness.begin_with_initial_hooks() + + # Validate workflow with incomplete relation data + self.assertFalse(self.harness.charm.state.is_started) + _render.assert_not_called() + _get_loader.assert_not_called() + _send_backend_config.assert_not_called() + _install_pkgs.assert_called_once_with() + self.assertEqual(self.harness.charm.unit.status, MaintenanceStatus('')) + + # Validate workflow with complete relation data + rel_id = self.harness.add_relation('manila-plugin', 'manila') + self.harness.add_relation_unit(rel_id, 'manila/0') + self.harness.update_relation_data( + rel_id, + 'manila/0', + { + '_authentication_data': json.dumps({ + 'data': 'test-manila-auth-data' + }) + }) + self.assertTrue(self.harness.charm.state.is_started) + _render.assert_called_once_with( + source='manila.conf', + template_loader='test-loader', + target=None, + context=self.harness.charm.adapters) + _get_loader.assert_called_once_with( + 'templates/', 'default') + _send_backend_config.assert_called_once_with( + 'netapp-ontap', 'test-rendered-manila-backend-config') + self.assertEqual( + self.harness.charm.unit.status, ActiveStatus('Unit is ready')) diff --git a/unit_tests/test_interface_manila_plugin.py b/unit_tests/test_interface_manila_plugin.py new file mode 100644 index 0000000..7d991e4 --- /dev/null +++ b/unit_tests/test_interface_manila_plugin.py @@ -0,0 +1,99 @@ +import json +import sys + +sys.path.append('lib') # noqa +sys.path.append('src') # noqa + +from unittest import ( + TestCase, +) + +from ops.framework import Object +from ops.charm import CharmBase +from ops.testing import Harness + +import interface_manila_plugin + + +class TestReceiver(Object): + + def __init__(self, parent, key): + super().__init__(parent, key) + self.observed_events = [] + + def on_manila_plugin_ready(self, event): + self.observed_events.append(event) + + +class TestManilaPluginProvides(TestCase): + + def setUp(self): + self.harness = Harness(CharmBase, meta=''' + name: manila-netapp + provides: + manila-plugin: + interface: manila-plugin + scope: container + ''') + self.addCleanup(self.harness.cleanup) + + def test_on_changed(self): + self.harness.begin() + self.harness.charm.manila_plugin = \ + interface_manila_plugin.ManilaPluginProvides( + self.harness.charm, + 'manila-plugin') + receiver = TestReceiver(self.harness.framework, 'receiver') + self.harness.framework.observe( + self.harness.charm.manila_plugin.on.manila_plugin_ready, + receiver.on_manila_plugin_ready) + rel_id = self.harness.add_relation('manila-plugin', 'manila') + self.harness.add_relation_unit(rel_id, 'manila/0') + self.harness.update_relation_data( + rel_id, + 'manila/0', + { + '_authentication_data': json.dumps({ + 'data': 'test-manila-auth-data' + }) + }) + + self.assertEqual( + self.harness.charm.manila_plugin.authentication_data, + 'test-manila-auth-data') + self.assertEqual(len(receiver.observed_events), 1) + self.assertIsInstance( + receiver.observed_events[0], + interface_manila_plugin.ManilaPluginReadyEvent) + + def test_send_backend_config(self): + self.harness.begin() + self.harness.charm.manila_plugin = \ + interface_manila_plugin.ManilaPluginProvides( + self.harness.charm, + 'manila-plugin') + rel_id = self.harness.add_relation('manila-plugin', 'manila') + self.harness.add_relation_unit(rel_id, 'manila/0') + self.harness.update_relation_data( + rel_id, + 'manila/0', + { + '_authentication_data': json.dumps({ + 'data': 'test-manila-auth-data' + }) + }) + + self.harness.charm.manila_plugin.send_backend_config( + 'test-backend-name', 'test-rendered-configs') + rel_unit_data = self.harness.get_relation_data( + rel_id, self.harness.charm.manila_plugin.this_unit.name) + self.assertEqual( + rel_unit_data.get('_name'), 'test-backend-name') + expected_data = { + 'data': { + interface_manila_plugin.MANILA_CONF: 'test-rendered-configs' + } + } + self.assertEqual( + rel_unit_data.get('_configuration_data'), + json.dumps(expected_data))