diff --git a/.gitignore b/.gitignore index 8a3c7042..4c0507af 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ ChangeLog *~ .*.swp .*sw? + +# generated configuration files +etc/castellan/castellan.conf.sample +etc/castellan/castellan-functional.conf.sample diff --git a/castellan/key_manager/__init__.py b/castellan/key_manager/__init__.py index 879ba246..a9e1f24a 100644 --- a/castellan/key_manager/__init__.py +++ b/castellan/key_manager/__init__.py @@ -16,7 +16,6 @@ from oslo_config import cfg from oslo_utils import importutils - key_manager_opts = [ cfg.StrOpt('api_class', default='castellan.key_manager.barbican_key_manager' @@ -24,10 +23,10 @@ key_manager_opts = [ help='The full class name of the key manager API class'), ] -CONF = cfg.CONF -CONF.register_opts(key_manager_opts, group='key_manager') +def API(configuration=None): + conf = configuration or cfg.CONF + conf.register_opts(key_manager_opts, group='key_manager') -def API(): - cls = importutils.import_class(CONF.key_manager.api_class) - return cls() + cls = importutils.import_class(conf.key_manager.api_class) + return cls(configuration=conf) diff --git a/castellan/key_manager/barbican_key_manager.py b/castellan/key_manager/barbican_key_manager.py index 5ea58bc7..ec2d46ec 100644 --- a/castellan/key_manager/barbican_key_manager.py +++ b/castellan/key_manager/barbican_key_manager.py @@ -35,27 +35,25 @@ barbican_opts = [ cfg.StrOpt('barbican_endpoint', default='http://localhost:9311/', help='Use this endpoint to connect to Barbican'), - cfg.StrOpt('api_version', + cfg.StrOpt('barbican_api_version', default='v1', help='Version of the Barbican API'), ] -CONF = cfg.CONF BARBICAN_OPT_GROUP = 'barbican' -CONF.register_opts(barbican_opts, group=BARBICAN_OPT_GROUP) - -session.Session.register_conf_options(CONF, BARBICAN_OPT_GROUP) - LOG = logging.getLogger(__name__) class BarbicanKeyManager(key_manager.KeyManager): """Key Manager Interface that wraps the Barbican client API.""" - def __init__(self): + def __init__(self, configuration): self._barbican_client = None self._base_url = None + self.conf = configuration + self.conf.register_opts(barbican_opts, group=BARBICAN_OPT_GROUP) + session.Session.register_conf_options(self.conf, BARBICAN_OPT_GROUP) def _get_barbican_client(self, context): """Creates a client to connect to the Barbican service. @@ -92,9 +90,9 @@ class BarbicanKeyManager(key_manager.KeyManager): def _get_keystone_session(self, context): sess = session.Session.load_from_conf_options( - CONF, BARBICAN_OPT_GROUP) + self.conf, BARBICAN_OPT_GROUP) - self._barbican_endpoint = CONF.barbican.barbican_endpoint + self._barbican_endpoint = self.conf.barbican.barbican_endpoint auth = token_endpoint.Token(self._barbican_endpoint, context.auth_token) @@ -102,8 +100,8 @@ class BarbicanKeyManager(key_manager.KeyManager): return sess def _create_base_url(self): - base_url = urllib.parse.urljoin(self._barbican_endpoint, - CONF.barbican.api_version) + base_url = urllib.parse.urljoin( + self._barbican_endpoint, self.conf.barbican.barbican_api_version) return base_url def create_key(self, context, algorithm, length, expiration=None): diff --git a/castellan/key_manager/key_manager.py b/castellan/key_manager/key_manager.py index b15f30e4..aed5efd7 100644 --- a/castellan/key_manager/key_manager.py +++ b/castellan/key_manager/key_manager.py @@ -30,6 +30,15 @@ class KeyManager(object): Key Manager is responsible for creating, reading, and deleting keys. """ + @abc.abstractmethod + def __init__(self, configuration): + """Instantiate a KeyManager object. + + Creates a KeyManager object with implementation specific details + obtained from the supplied configuration. + """ + pass + @abc.abstractmethod def create_key(self, context, algorithm, length, expiration=None): """Creates a symmetric key. diff --git a/castellan/key_manager/not_implemented_key_manager.py b/castellan/key_manager/not_implemented_key_manager.py index 4a012232..425f71cf 100644 --- a/castellan/key_manager/not_implemented_key_manager.py +++ b/castellan/key_manager/not_implemented_key_manager.py @@ -25,6 +25,9 @@ class NotImplementedKeyManager(key_manager.KeyManager): """ + def __init__(self, configuration=None): + super(NotImplementedKeyManager, self).__init__(configuration) + def create_key(self, context, algorithm='AES', length=256, expiration=None, **kwargs): raise NotImplementedError() diff --git a/castellan/options.py b/castellan/options.py new file mode 100644 index 00000000..6d450300 --- /dev/null +++ b/castellan/options.py @@ -0,0 +1,70 @@ +# Copyright (c) 2015 Red Hat, Inc. +# All Rights Reserved. +# +# 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 castellan import key_manager as km +try: + from castellan.key_manager import barbican_key_manager as bkm +except ImportError: + bkm = None + + +def set_defaults(conf, api_class=None, barbican_endpoint=None, + barbican_api_version=None): + '''Set defaults for configuration values. + + Overrides the default options values. + + :param conf: Config instance in which to set default options. + + :param api_class: The full class name of the key manager API class. + + :param barbican_endpoint: Use this endpoint to connect to Barbican. + + :param barbican_api_version: Version of the Barbican API. + ''' + conf.register_opts(km.key_manager_opts, group='key_manager') + if bkm: + conf.register_opts(bkm.barbican_opts, group=bkm.BARBICAN_OPT_GROUP) + + if api_class is not None: + conf.set_default('api_class', api_class, group='key_manager') + if bkm is not None and barbican_endpoint is not None: + conf.set_default('barbican_endpoint', barbican_endpoint, + group=bkm.BARBICAN_OPT_GROUP) + if bkm is not None and barbican_api_version is not None: + conf.set_default('barbican_api_version', barbican_api_version, + group=bkm.BARBICAN_OPT_GROUP) + + +def list_opts(): + '''Returns a list of oslo.config options available in the library. + + The returned list includes all oslo.config options which may be registered + at runtime by the library. + + Each element of the list is a tuple. The first element is the name of the + group under which the list of elements in the second element will be + registered. A group name of None corresponds to the [DEFAULT] group in + config files. + + The purpose of this is to allow tools like the Oslo sample config file + generator to discover the options exposed to users by this library. + + :returns: a list of (group_name, opts) tuples + ''' + opts = [('key_manager', km.key_manager_opts)] + if bkm is not None: + opts.append((bkm.BARBICAN_OPT_GROUP, bkm.barbican_opts)) + return opts diff --git a/castellan/tests/unit/key_manager/test_barbican_key_manager.py b/castellan/tests/unit/key_manager/test_barbican_key_manager.py index 017a90c4..816ec361 100644 --- a/castellan/tests/unit/key_manager/test_barbican_key_manager.py +++ b/castellan/tests/unit/key_manager/test_barbican_key_manager.py @@ -18,6 +18,7 @@ Test cases for the barbican key manager. """ import mock +from oslo_config import cfg from castellan.common import exception from castellan.common.objects import symmetric_key as key_manager_key @@ -28,7 +29,7 @@ from castellan.tests.unit.key_manager import test_key_manager class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase): def _create_key_manager(self): - return barbican_key_manager.BarbicanKeyManager() + return barbican_key_manager.BarbicanKeyManager(cfg.CONF) def setUp(self): super(BarbicanKeyManagerTestCase, self).setUp() diff --git a/castellan/tests/unit/test_options.py b/castellan/tests/unit/test_options.py new file mode 100644 index 00000000..f0acb26d --- /dev/null +++ b/castellan/tests/unit/test_options.py @@ -0,0 +1,40 @@ +# Copyright (c) 2015 Red Hat, Inc. +# All Rights Reserved. +# +# 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 oslo_config import cfg + +from castellan.key_manager import barbican_key_manager as bkm +from castellan import options +from castellan.tests import base + + +class TestOptions(base.TestCase): + + def test_set_defaults(self): + conf = cfg.ConfigOpts() + + api_class = 'test.api.class' + options.set_defaults(conf, api_class=api_class) + self.assertEqual(api_class, conf.key_manager.api_class) + + barbican_endpoint = 'http://test-server.org:9311/' + options.set_defaults(conf, barbican_endpoint=barbican_endpoint) + self.assertEqual(barbican_endpoint, + conf.get(bkm.BARBICAN_OPT_GROUP).barbican_endpoint) + + barbican_api_version = 'vSomething' + options.set_defaults(conf, barbican_api_version=barbican_api_version) + self.assertEqual(barbican_api_version, + conf.get(bkm.BARBICAN_OPT_GROUP).barbican_api_version) diff --git a/doc/source/usage.rst b/doc/source/usage.rst index c2618f45..3ab77329 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -5,3 +5,102 @@ Usage To use castellan in a project:: import castellan + + +Configuring castellan +~~~~~~~~~~~~~~~~~~~~~ + +Castellan contains several options which control the key management +service usage and the configuration of that service. It also contains +functions to help configure the defaults and produce listings for use +with the ``oslo-config-generator`` application. + +In general, castellan configuration is handled by passing an +``oslo_config.cfg.ConfigOpts`` object into the +``castellan.key_manager.API`` call when creating your key manager. By +default, when no ``ConfigOpts`` object is provided, the key manager will +use the global ``oslo_config.cfg.CONF`` object. + +**Example. Using the global CONF object for configuration.** + +.. code:: python + + from castellan import key_manager + + manager = key_manager.API() + +**Example. Using a predetermined configuration object.** + +.. code:: python + + from oslo_config import cfg + from castellan import key_manager + + conf = cfg.ConfigOpts() + manager = key_manager.API(configuration=conf) + +Controlling default options +--------------------------- + +To change the default behavior of castellan, and the key management service +it uses, the ``castellan.options`` module provides the ``set_defaults`` +function. This function can be used at run-time to change the behavior of +the library or the key management service provider. + +**Example. Changing the barbican endpoint.** + +.. code:: python + + from oslo_config import cfg + from castellan import options + from castellan import key_manager + + conf = cfg.ConfigOpts() + options.set_defaults(conf, barbican_endpoint='http://192.168.0.1:9311/') + manager = key_manager.API(conf) + +**Example. Changing the key manager provider while using the global +configuration.** + +.. code:: python + + from oslo_config import cfg + from castellan import options + from castellan import key_manager + + options.set_defaults(cfg.CONF, api_class='some.other.KeyManager') + manager = key_manager.API() + +Generating sample configuration files +------------------------------------- + +Castellan includes a tox configuration for creating a sample configuration +file. This file will contain only the values that will be used by +castellan. To produce this file, run the following command from the +root of the castellan project directory: + +.. code:: console + + $ tox -e genconfig + +Adding castellan to configuration files +--------------------------------------- + +One common task for OpenStack projects is to create project configuration +files. Castellan provides a ``list_opts`` function in the +``castellan.options`` module to aid in generating these files when using +the ``oslo-config-generator``. This function can be specified in the +:file:`setup.cfg` file of your project to inform oslo of the +configuration options. *Note, this will use the default values supplied +by the castellan package.* + +**Example. Adding castellan to the oslo.config entry point.** + +.. code:: ini + + [entry_points] + oslo.config.opts = + castellan.config = castellan.options:list_opts + +For more information on the oslo configuration generator, please see +http://docs.openstack.org/developer/oslo.config/generator.html diff --git a/etc/castellan/sample-config-generator.conf b/etc/castellan/sample-config-generator.conf new file mode 100644 index 00000000..97eb9887 --- /dev/null +++ b/etc/castellan/sample-config-generator.conf @@ -0,0 +1,4 @@ +[DEFAULT] +output_file = etc/castellan/castellan.conf.sample +wrap_width = 79 +namespace = castellan.config diff --git a/setup.cfg b/setup.cfg index aefc8cdf..8bd9e2d9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,7 @@ packages = [entry_points] oslo.config.opts = castellan.tests.functional.config = castellan.tests.functional.config:list_opts + castellan.config = castellan.options:list_opts [build_sphinx] source-dir = doc/source diff --git a/tox.ini b/tox.ini index 69cd50bf..09fcb4a1 100644 --- a/tox.ini +++ b/tox.ini @@ -38,7 +38,7 @@ commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:genconfig] commands = oslo-config-generator --config-file=etc/castellan/functional-config-generator.conf -#TODO(kfarr): add config generator for main Castellan options + oslo-config-generator --config-file=etc/castellan/sample-config-generator.conf [flake8] # H803 skipped on purpose per list discussion.