diff --git a/.gitignore b/.gitignore index d6406dc..53bfadc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build layers interfaces trusty +.testrepository diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 0000000..801646b --- /dev/null +++ b/.testr.conf @@ -0,0 +1,8 @@ +[DEFAULT] +test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ + OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ + OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ + ${PYTHON:-python} -m subunit.run discover -t ./ ./unit_tests $LISTOPT $IDOPTION + +test_id_option=--load-list $IDFILE +test_list_option=--list diff --git a/Makefile b/Makefile index fc50b41..4688a57 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,14 @@ LAYER_PATH := layers clean: - rm -Rf build .tox + rm -Rf build .tox .testrepository generate: clean LAYER_PATH=$(LAYER_PATH) tox -e generate + +lint: + @tox -e lint + +test: + @echo Starting unit tests... + @tox -e py27 diff --git a/src/lib/__init__.py b/src/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/charm/__init__.py b/src/lib/charm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/charm/openstack/__init__.py b/src/lib/charm/openstack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/charm/openstack/barbican.py b/src/lib/charm/openstack/barbican.py index be64867..829eb2b 100644 --- a/src/lib/charm/openstack/barbican.py +++ b/src/lib/charm/openstack/barbican.py @@ -4,9 +4,13 @@ # needed on the class. from __future__ import absolute_import +import charmhelpers.fetch +import charmhelpers.core.hookenv as hookenv +import charmhelpers.contrib.openstack.utils as ch_utils + import charm.openstack.charm import charm.openstack.adapters -import charmhelpers.fetch +import charm.openstack.ip as os_ip PACKAGES = ['barbican-common', 'barbican-api', 'barbican-worker', 'python-mysqldb'] @@ -30,16 +34,17 @@ def setup_amqp_req(amqp): """Use the amqp interface to request access to the amqp broker using our local configuration. """ - amqp.request_access(username=config('rabbit-user'), - vhost=config('rabbit-vhost')) + amqp.request_access(username=hookenv.config('rabbit-user'), + vhost=hookenv.config('rabbit-vhost')) def setup_database(database): """On receiving database credentials, configure the database on the interface. """ - database.configure(config('database'), config('database-user'), - unit_private_ip()) + database.configure(hookenv.config('database'), + hookenv.config('database-user'), + hookenv.unit_private_ip()) def setup_endpoint(keystone): @@ -64,11 +69,11 @@ def render_configs(interfaces_list): ### # Implementation of the Barbican Charm classes -class BarbicanConfigurationAdapater( +class BarbicanConfigurationAdapter( charm.openstack.adapters.ConfigurationAdapter): def __init__(self): - super(BarbicanConfigurationAdapater, self).__init__() + super(BarbicanConfigurationAdapter, self).__init__() if self.keystone_api_version not in ['2', '3', 'none']: raise ValueError( "Unsupported keystone-api-version ({}). It should be 2 or 3" @@ -107,17 +112,15 @@ class BarbicanCharm(charm.openstack.charm.OpenStackCharm): functionality to manage a barbican unit. """ - releases = { - 'liberty': BarbicanCharm - } + # don't set releases here (as we need to self refer - see below) first_release = 'liberty' name = 'barbican' packages = PACKAGES api_ports = { 'barbican-api': { - PUBLIC: 9311, - ADMIN: 9312, - INTERNAL: 9313, + os_ip.PUBLIC: 9311, + os_ip.ADMIN: 9312, + os_ip.INTERNAL: 9313, } } service_type = 'secretstore' @@ -136,14 +139,13 @@ class BarbicanCharm(charm.openstack.charm.OpenStackCharm): """Custom initialiser for class If no release is passed, then the charm determines the release from the - os_release() function. + ch_utils.os_release() function. """ if release is None: - #release = os_release('barbican-common') - release = os_release('python-keystonemiddleware') + # release = ch_utils.os_release('barbican-common') + release = ch_utils.os_release('python-keystonemiddleware') super(BarbicanCharm, self).__init__(release=release, **kwargs) - def install(self): """Customise the installation, configure the source and then call the parent install() method to install the packages @@ -152,3 +154,11 @@ class BarbicanCharm(charm.openstack.charm.OpenStackCharm): self.configure_source() # and do the actual install super(BarbicanCharm, self).install() + + +# Set Barbican releases here because we need it to refer to itself. For derived +# classes of BarbicanCharm for different series we would still set the releases +# here. +BarbicanCharm.releases = { + 'liberty': BarbicanCharm, +} diff --git a/src/reactive/__init__.py b/src/reactive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/reactive/barbican_handlers.py b/src/reactive/barbican_handlers.py index 1c587f1..1242e4a 100644 --- a/src/reactive/barbican_handlers.py +++ b/src/reactive/barbican_handlers.py @@ -3,14 +3,18 @@ from __future__ import absolute_import from __future__ import print_function import sys -sys.path.append('lib') +sys.path.append('src/lib') import charms.reactive as reactive print(sys.path) +import os +print(os.getcwd()) +print(os.path.dirname(os.path.realpath(__file__))) # This charm's library contains all of the handler code import charm.openstack.barbican as barbican + # use a synthetic state to ensure that it get it to be installed independent of # the install hook. @reactive.when_not('charm.installed') diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..9f08bee --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,7 @@ +flake8>=2.2.4,<=2.4.1 +os-testr>=0.4.1 +charm-tools>=2.0.0 +charms.reactive +mock>=1.2 +coverage>=3.6 +https://github.com/ajkavanagh/charm.openstack/zipball/master diff --git a/tox.ini b/tox.ini index 34dcc34..03663c1 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,20 @@ deps = [testenv:generate] basepython = python2.7 commands = - charm build --log-level DEBUG -o {toxinidir}/build charm + charm build --log-level DEBUG -o {toxinidir}/build src + +[testenv:py27] +basepython = python2.7 +deps = -r{toxinidir}/test-requirements.txt +commands = ostestr {posargs} + +[testenv:lint] +basepython = python2.7 +deps = -r{toxinidir}/test-requirements.txt +commands = flake8 {posargs} src unit_tests [testenv:venv] commands = {posargs} + +[flake8] +ignore = E402,E226 diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py new file mode 100644 index 0000000..2ebdc23 --- /dev/null +++ b/unit_tests/__init__.py @@ -0,0 +1,24 @@ +import sys +import mock + + +charmhelpers = mock.MagicMock() +sys.modules['charmhelpers'] = charmhelpers +sys.modules['charmhelpers.core'] = charmhelpers.core +sys.modules['charmhelpers.core.hookenv'] = charmhelpers.core.hookenv +sys.modules['charmhelpers.core.host'] = charmhelpers.core.host +sys.modules['charmhelpers.core.templating'] = charmhelpers.core.templating +sys.modules['charmhelpers.contrib'] = charmhelpers.contrib +sys.modules['charmhelpers.contrib.openstack'] = charmhelpers.contrib.openstack +sys.modules['charmhelpers.contrib.openstack.utils'] = ( + charmhelpers.contrib.openstack.utils) +sys.modules['charmhelpers.contrib.openstack.templating'] = ( + charmhelpers.contrib.openstack.templating) +sys.modules['charmhelpers.contrib.network'] = charmhelpers.contrib.network +sys.modules['charmhelpers.contrib.network.ip'] = ( + charmhelpers.contrib.network.ip) +sys.modules['charmhelpers.fetch'] = charmhelpers.fetch +sys.modules['charmhelpers.cli'] = charmhelpers.cli +sys.modules['charmhelpers.contrib.hahelpers'] = charmhelpers.contrib.hahelpers +sys.modules['charmhelpers.contrib.hahelpers.cluster'] = ( + charmhelpers.contrib.hahelpers.cluster) diff --git a/unit_tests/test_barbican_handlers.py b/unit_tests/test_barbican_handlers.py new file mode 100644 index 0000000..3a54c81 --- /dev/null +++ b/unit_tests/test_barbican_handlers.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import +from __future__ import print_function + +import sys +import unittest + +import mock + +sys.path.append('src') +sys.path.append('src/lib') +import reactive.barbican_handlers as handlers + + +_hook_args = {} + + +def mock_hook(*args, **kwargs): + + def inner(f): + # remember what we were passed. Note that we can't actually determine + # the class we're attached to, as the decorator only gets the function. + _hook_args[f.__name__] = dict(args=args, kwargs=kwargs) + return f + return inner + + +class TestBarbicanHandlers(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._patched_hook = mock.patch('charms.reactive.hook', mock_hook) + cls._patched_hook_started = cls._patched_hook.start() + # force requires to rerun the mock_hook decorator: + reload(requires) + + @classmethod + def tearDownClass(cls): + cls._patched_hook.stop() + cls._patched_hook_started = None + cls._patched_hook = None + # and fix any breakage we did to the module + reload(requires) + + def patch(self, attr, return_value=None): + mocked = mock.patch.object(self.kr, attr) + self._patches[attr] = mocked + started = mocked.start() + started.return_value = return_value + self._patches_start[attr] = started + setattr(self, attr, started) + + def test_registered_hooks(self): + # test that the hooks actually registered the relation expressions that + # are meaningful for this interface: this is to handle regressions. + # The keys are the function names that the hook attaches to. + hook_patterns = { + 'joined': ('{requires:keystone}-relation-joined', ), + 'changed': ('{requires:keystone}-relation-changed', ), + 'departed': ('{requires:keystone}-relation-{broken,departed}', ), + } + print(_hook_args) + # for k, v in _hook_args.items(): + # self.assertEqual(hook_patterns[k], v['args'])