diff --git a/.stestr.conf b/.stestr.conf new file mode 100644 index 0000000..5fcccac --- /dev/null +++ b/.stestr.conf @@ -0,0 +1,3 @@ +[DEFAULT] +test_path=./unit_tests +top_dir=./ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b80d71d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: python +python: + - "3.6" +install: pip install tox-travis +script: + - tox diff --git a/src/HACKING.md b/src/HACKING.md index 2380a54..ddb1128 100644 --- a/src/HACKING.md +++ b/src/HACKING.md @@ -4,7 +4,7 @@ This charm is developed as part of the OpenStack Charms project, and as such you should refer to the [OpenStack Charm Development Guide](https://github.com/openstack/charm-guide) for details on how to contribute to this charm. -You can find its source code here: . +You can find its source code here: . diff --git a/src/layer.yaml b/src/layer.yaml index 6fd5edd..cd9f723 100644 --- a/src/layer.yaml +++ b/src/layer.yaml @@ -1,8 +1,9 @@ includes: - layer:openstack - interface:vault-kv + - interface:barbican-secrets options: basic: use_venv: True include_system_packages: True -repo: https://github.com/openstack/charm-barbican-secrets-vault +repo: https://github.com/openstack/charm-barbican-vault diff --git a/src/lib/charm/openstack/barbican_vault.py b/src/lib/charm/openstack/barbican_vault.py new file mode 100644 index 0000000..eaa6b13 --- /dev/null +++ b/src/lib/charm/openstack/barbican_vault.py @@ -0,0 +1,23 @@ +# Copyright 2018 Canonical Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import charms_openstack.adapters +import charms_openstack.charm + + +class BarbicanVaultCharm(charms_openstack.charm.OpenStackCharm): + release = 'rocky' + + packages = ['python-castellan'] + + adapters_class = charms_openstack.adapters.OpenStackRelationAdapters diff --git a/src/metadata.yaml b/src/metadata.yaml index 57e1b32..7e28722 100644 --- a/src/metadata.yaml +++ b/src/metadata.yaml @@ -1,12 +1,19 @@ -name: barbican-secrets-vault -summary: OpenStack Barbican vault secrets backend +name: barbican-vault +summary: OpenStack Barbican vault backend maintainer: OpenStack Charmers -description: OpenStack Barbican vault secrets backend +description: OpenStack Barbican vault backend tags: - openstack series: - bionic subordinate: true +provides: + secrets: + interface: barbican-secrets + scope: container requires: + juju-info: + interface: juju-info + scope: container secrets-storage: interface: vault-kv diff --git a/src/reactive/barbican_vault_handlers.py b/src/reactive/barbican_vault_handlers.py new file mode 100644 index 0000000..8c496bc --- /dev/null +++ b/src/reactive/barbican_vault_handlers.py @@ -0,0 +1,53 @@ +# Copyright 2018 Canonical Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import charmhelpers.core as ch_core + +import charms.reactive as reactive + +import charms_openstack.charm as charm + +# Use the charms.openstack defaults for common states and hooks +charm.use_defaults( + 'charm.installed', + 'config.changed', + 'update-status') + + +@reactive.when_not('secrets-storage.available') +@reactive.when('endpoint.secrets-storage.joined') +def secret_backend_vault_request(): + """Request access to vault.""" + secrets_storage = reactive.endpoint_from_flag( + 'endpoint.secrets-storage.joined') + ch_core.hookenv.log('Requesting access to vault ({})' + .format(secrets_storage.vault_url), + level=ch_core.hookenv.INFO) + secrets_storage.request_secret_backend('charm-barbican-vault') + + +@reactive.when_all('endpoint.secrets.joined', 'secrets-storage.available') +def plugin_info_barbican_publish(): + barbican = reactive.endpoint_from_flag('endpoint.secrets.joined') + secrets_storage = reactive.endpoint_from_flag( + 'secrets-storage.available') + vault_data = { + 'approle_role_id': secrets_storage.unit_role_id, + 'approle_secret_id': secrets_storage.unit_token, + 'vault_url': secrets_storage.vault_url, + 'use_ssl': 'false', # XXX + } + ch_core.hookenv.log('Publishing vault plugin info to barbican', + level=ch_core.hookenv.INFO) + barbican.publish_plugin_info('vault', vault_data) diff --git a/src/tests/bundles/smoke-bionic-rocky.yaml b/src/tests/bundles/smoke-bionic-rocky.yaml new file mode 100644 index 0000000..0eeaaba --- /dev/null +++ b/src/tests/bundles/smoke-bionic-rocky.yaml @@ -0,0 +1,43 @@ +series: bionic +relations: +- - keystone + - mysql +- - vault + - mysql +- - barbican + - mysql +- - barbican + - keystone +- - barbican + - rabbitmq-server +- - barbican + - barbican-vault +- - barbican-vault + - vault +applications: + mysql: + charm: cs:~openstack-charmers-next/percona-cluster + num_units: 1 + keystone: + charm: cs:~openstack-charmers-next/keystone + num_units: 1 + options: + openstack-origin: cloud:bionic-rocky + barbican: + series: bionic + charm: cs:~openstack-charmers-next/barbican + num_units: 1 + options: + openstack-origin: cloud:bionic-rocky/proposed + barbican-vault: + series: bionic + charm: barbican-vault + num_units: 0 + rabbitmq-server: + charm: cs:~openstack-charmers-next/rabbitmq-server + num_units: 1 + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + options: + totally-unsecure-auto-unlock: True diff --git a/unit_tests/test_barbican_vault_handlers.py b/unit_tests/test_barbican_vault_handlers.py new file mode 100644 index 0000000..482284c --- /dev/null +++ b/unit_tests/test_barbican_vault_handlers.py @@ -0,0 +1,92 @@ +# Copyright 2016 Canonical Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import print_function + +import mock + +import reactive.barbican_vault_handlers as handlers + +import charms_openstack.test_utils as test_utils + + +class TestRegisteredHooks(test_utils.TestRegisteredHooks): + + def test_hooks(self): + defaults = [ + 'charm.installed', + 'config.changed', + 'update-status'] + hook_set = { + 'when': { + 'secret_backend_vault_request': ( + 'endpoint.secrets-storage.joined',), + }, + 'when_all': { + 'plugin_info_barbican_publish': ( + 'endpoint.secrets.joined', 'secrets-storage.available',), + }, + 'when_not': { + 'secret_backend_vault_request': ( + 'secrets-storage.available',), + }, + } + # test that the hooks were registered via the + # reactive.barbican_handlers + self.registered_hooks_test_helper(handlers, hook_set, defaults) + + +class TestBarbicanVaultHandlers(test_utils.PatchHelper): + + def patch_charm(self): + barbican_vault_charm = mock.MagicMock() + self.patch_object(handlers.charm, 'provide_charm_instance', + new=mock.MagicMock()) + self.provide_charm_instance().__enter__.return_value = \ + barbican_vault_charm + self.provide_charm_instance().__exit__.return_value = None + + def test_secret_backend_vault_request(self): + self.patch_charm() + self.patch_object(handlers.reactive, 'endpoint_from_flag') + secrets_storage = mock.MagicMock() + self.endpoint_from_flag.return_value = secrets_storage + + handlers.secret_backend_vault_request() + self.endpoint_from_flag.assert_called_once_with( + 'endpoint.secrets-storage.joined') + secrets_storage.request_secret_backend.assrt_called_once_with( + 'charm-barbican-vault') + + def test_plugin_info_barbican_publish(self): + self.patch_charm() + self.patch_object(handlers.reactive, 'endpoint_from_flag') + barbican = mock.MagicMock() + secrets_storage = mock.MagicMock() + self.endpoint_from_flag.side_effect = [barbican, secrets_storage] + + handlers.plugin_info_barbican_publish() + self.endpoint_from_flag.assert_has_calls([ + mock.call('endpoint.secrets.joined'), + mock.call('secrets-storage.available'), + ]) + vault_data = { + 'approle_role_id': secrets_storage.unit_role_id, + 'approle_secret_id': secrets_storage.unit_token, + 'vault_url': secrets_storage.vault_url, + 'use_ssl': 'false', # XXX + } + barbican.publish_plugin_info.assert_called_once_with( + 'vault', vault_data)