Merge branch 'master' into oslo-log-integration
This commit is contained in:
commit
bc36541f52
@ -2,6 +2,7 @@ CHANGES
|
|||||||
=======
|
=======
|
||||||
|
|
||||||
* Add oslo.log integration to Deckhand.
|
* Add oslo.log integration to Deckhand.
|
||||||
|
* DECKHAND-10: Barbican initial integration
|
||||||
* DECKHAND-2: Design core Deckhand API framework
|
* DECKHAND-2: Design core Deckhand API framework
|
||||||
* Oslo config integration (#1)
|
* Oslo config integration (#1)
|
||||||
* Add ChangeLog
|
* Add ChangeLog
|
||||||
|
0
deckhand/barbican/__init__.py
Normal file
0
deckhand/barbican/__init__.py
Normal file
114
deckhand/barbican/client_wrapper.py
Normal file
114
deckhand/barbican/client_wrapper.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# Copyright 2017 AT&T Intellectual Property. All other 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 keystoneauth1.identity import v3
|
||||||
|
from keystoneauth1 import session
|
||||||
|
|
||||||
|
from deckhand.conf import config
|
||||||
|
from deckhand import errors
|
||||||
|
|
||||||
|
from barbicanclient import barbican
|
||||||
|
from barbicanclient import exceptions as barbican_exc
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class BarbicanClientWrapper(object):
|
||||||
|
"""Barbican client wrapper class that encapsulates authentication logic."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialise the BarbicanClientWrapper for use."""
|
||||||
|
self._cached_client = None
|
||||||
|
|
||||||
|
def _invalidate_cached_client(self):
|
||||||
|
"""Tell the wrapper to invalidate the cached barbican-client."""
|
||||||
|
self._cached_client = None
|
||||||
|
|
||||||
|
def _get_client(self, retry_on_conflict=True):
|
||||||
|
# If we've already constructed a valid, authed client, just return
|
||||||
|
# that.
|
||||||
|
if retry_on_conflict and self._cached_client is not None:
|
||||||
|
return self._cached_client
|
||||||
|
|
||||||
|
# TODO: Deckhand's configuration file needs to be populated with
|
||||||
|
# correct Keysone authentication values as well as the Barbican
|
||||||
|
# endpoint URL automatically.
|
||||||
|
barbican_url = (CONF.barbican.api_endpoint
|
||||||
|
if CONF.barbican.api_endpoint
|
||||||
|
else 'http://127.0.0.1:9311')
|
||||||
|
|
||||||
|
keystone_auth = dict(CONF.keystone_authtoken)
|
||||||
|
auth = v3.Password(**keystone_auth)
|
||||||
|
sess = session.Session(auth=auth)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# TODO: replace with ``barbican_url``.
|
||||||
|
cli = barbican.client.Client(endpoint=barbican_url,
|
||||||
|
session=sess)
|
||||||
|
# Cache the client so we don't have to reconstruct and
|
||||||
|
# reauthenticate it every time we need it.
|
||||||
|
if retry_on_conflict:
|
||||||
|
self._cached_client = cli
|
||||||
|
|
||||||
|
except barbican_exc.HTTPAuthError:
|
||||||
|
msg = _("Unable to authenticate Barbican client.")
|
||||||
|
# TODO: Log the error.
|
||||||
|
raise errors.ApiError(msg)
|
||||||
|
|
||||||
|
return cli
|
||||||
|
|
||||||
|
def _multi_getattr(self, obj, attr):
|
||||||
|
"""Support nested attribute path for getattr().
|
||||||
|
|
||||||
|
:param obj: Root object.
|
||||||
|
:param attr: Path of final attribute to get. E.g., "a.b.c.d"
|
||||||
|
|
||||||
|
:returns: The value of the final named attribute.
|
||||||
|
:raises: AttributeError will be raised if the path is invalid.
|
||||||
|
"""
|
||||||
|
for attribute in attr.split("."):
|
||||||
|
obj = getattr(obj, attribute)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def call(self, method, *args, **kwargs):
|
||||||
|
"""Call a barbican client method and retry on stale token.
|
||||||
|
|
||||||
|
:param method: Name of the client method to call as a string.
|
||||||
|
:param args: Client method arguments.
|
||||||
|
:param kwargs: Client method keyword arguments.
|
||||||
|
:param retry_on_conflict: Boolean value. Whether the request should be
|
||||||
|
retried in case of a conflict error
|
||||||
|
(HTTP 409) or not. If retry_on_conflict is
|
||||||
|
False the cached instance of the client
|
||||||
|
won't be used. Defaults to True.
|
||||||
|
"""
|
||||||
|
retry_on_conflict = kwargs.pop('retry_on_conflict', True)
|
||||||
|
|
||||||
|
for attempt in range(2):
|
||||||
|
client = self._get_client(retry_on_conflict=retry_on_conflict)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self._multi_getattr(client, method)(*args, **kwargs)
|
||||||
|
except barbican_exc.HTTPAuthError:
|
||||||
|
# In this case, the authorization token of the cached
|
||||||
|
# barbican-client probably expired. So invalidate the cached
|
||||||
|
# client and the next try will start with a fresh one.
|
||||||
|
if not attempt:
|
||||||
|
self._invalidate_cached_client()
|
||||||
|
# TODO: include after implementing oslo.log.
|
||||||
|
# LOG.debug("The Barbican client became unauthorized. "
|
||||||
|
# "Will attempt to reauthorize and try again.")
|
||||||
|
else:
|
||||||
|
# This code should be unreachable actually
|
||||||
|
raise
|
26
deckhand/barbican/driver.py
Normal file
26
deckhand/barbican/driver.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2017 AT&T Intellectual Property. All other 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 deckhand.barbican import client_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class BarbicanDriver(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.barbicanclient = client_wrapper.BarbicanClientWrapper()
|
||||||
|
|
||||||
|
def ca_list(self, **kwargs):
|
||||||
|
# FIXME(felipemonteiro): Testing cas.list endpoint.
|
||||||
|
ca_list = self.barbicanclient.call("cas.list", **kwargs)
|
||||||
|
return ca_list
|
@ -14,6 +14,29 @@
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
keystone_auth_group = cfg.OptGroup(
|
||||||
|
name='keystone_authtoken',
|
||||||
|
title='Keystone Authentication Options'
|
||||||
|
)
|
||||||
|
|
||||||
|
keystone_auth_opts = [
|
||||||
|
cfg.StrOpt(name='project_domain_name',
|
||||||
|
default='Default'),
|
||||||
|
cfg.StrOpt(name='project_name',
|
||||||
|
default='admin'),
|
||||||
|
cfg.StrOpt(name='user_domain_name',
|
||||||
|
default='Default'),
|
||||||
|
cfg.StrOpt(name='password',
|
||||||
|
default='devstack'),
|
||||||
|
cfg.StrOpt(name='username',
|
||||||
|
default='admin'),
|
||||||
|
cfg.StrOpt(name='auth_url',
|
||||||
|
default='http://127.0.0.1/identity/v3')
|
||||||
|
]
|
||||||
|
|
||||||
barbican_group = cfg.OptGroup(
|
barbican_group = cfg.OptGroup(
|
||||||
name='barbican',
|
name='barbican',
|
||||||
title='Barbican Options',
|
title='Barbican Options',
|
||||||
@ -21,7 +44,12 @@ barbican_group = cfg.OptGroup(
|
|||||||
Barbican options for allowing Deckhand to communicate with Barbican.
|
Barbican options for allowing Deckhand to communicate with Barbican.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
barbican_opts = []
|
barbican_opts = [
|
||||||
|
cfg.StrOpt(
|
||||||
|
'api_endpoint',
|
||||||
|
sample_default='http://barbican.example.org:9311/',
|
||||||
|
help='URL override for the Barbican API endpoint.'),
|
||||||
|
]
|
||||||
|
|
||||||
logging_group = cfg.OptGroup(
|
logging_group = cfg.OptGroup(
|
||||||
name='logging',
|
name='logging',
|
||||||
@ -38,10 +66,16 @@ logging_opts = [
|
|||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
conf.register_group(barbican_group)
|
conf.register_group(barbican_group)
|
||||||
conf.register_opts(barbican_opts, group=barbican_group)
|
conf.register_opts(barbican_opts, group=barbican_group)
|
||||||
|
conf.register_group(keystone_auth_group)
|
||||||
|
conf.register_opts(keystone_auth_opts, group=keystone_auth_group)
|
||||||
conf.register_group(logging_group)
|
conf.register_group(logging_group)
|
||||||
conf.register_opts(logging_opts, group=logging_group)
|
conf.register_opts(logging_opts, group=logging_group)
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
def list_opts():
|
||||||
return {barbican_group: barbican_opts,
|
return {barbican_group: barbican_opts,
|
||||||
|
keystone_auth_group: keystone_auth_opts,
|
||||||
logging_group: logging_opts}
|
logging_group: logging_opts}
|
||||||
|
|
||||||
|
|
||||||
|
register_opts(CONF)
|
||||||
|
@ -16,6 +16,7 @@ import falcon
|
|||||||
|
|
||||||
from oslo_serialization import jsonutils as json
|
from oslo_serialization import jsonutils as json
|
||||||
|
|
||||||
|
from deckhand.barbican import driver
|
||||||
from deckhand.control import base as api_base
|
from deckhand.control import base as api_base
|
||||||
|
|
||||||
|
|
||||||
@ -29,8 +30,10 @@ class SecretsResource(api_base.BaseResource):
|
|||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(SecretsResource, self).__init__(**kwargs)
|
super(SecretsResource, self).__init__(**kwargs)
|
||||||
self.authorized_roles = ['user']
|
self.authorized_roles = ['user']
|
||||||
|
self.barbican_driver = driver.BarbicanDriver()
|
||||||
|
|
||||||
def on_get(self, req, resp):
|
def on_get(self, req, resp):
|
||||||
# TODO(felipemonteiro): Implement this API endpoint.
|
# TODO(felipemonteiro): Implement this API endpoint.
|
||||||
resp.body = json.dumps({'secrets': 'test_secrets'})
|
ca_list = self.barbican_driver.ca_list() # Random endpoint to test.
|
||||||
|
resp.body = json.dumps({'secrets': [c.to_dict() for c in ca_list]})
|
||||||
resp.status = falcon.HTTP_200
|
resp.status = falcon.HTTP_200
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from deckhand.control import api
|
from .control import api
|
||||||
|
|
||||||
|
|
||||||
def start_deckhand():
|
def start_deckhand():
|
||||||
@ -20,4 +20,4 @@ def start_deckhand():
|
|||||||
|
|
||||||
|
|
||||||
# Callable to be used by uwsgi.
|
# Callable to be used by uwsgi.
|
||||||
deckhand = start_deckhand()
|
deckhand_callable = start_deckhand()
|
||||||
|
47
etc/deckhand/deckhand.conf.sample
Normal file
47
etc/deckhand/deckhand.conf.sample
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
|
||||||
|
|
||||||
|
[barbican]
|
||||||
|
#
|
||||||
|
# Barbican options for allowing Deckhand to communicate with Barbican.
|
||||||
|
|
||||||
|
#
|
||||||
|
# From deckhand.conf
|
||||||
|
#
|
||||||
|
|
||||||
|
# URL override for the Barbican API endpoint. (string value)
|
||||||
|
#api_endpoint = http://barbican.example.org:9311/
|
||||||
|
|
||||||
|
|
||||||
|
[keystone_authtoken]
|
||||||
|
|
||||||
|
#
|
||||||
|
# From deckhand.conf
|
||||||
|
#
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#signing_dir = <None>
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#cafile = <None>
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#project_domain_name = Default
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#project_name = admin
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#user_domain_name = Default
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#password = devstack
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#username = admin
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#auth_url = http://127.0.0.1/identity
|
||||||
|
|
||||||
|
# (string value)
|
||||||
|
#auth_type = password
|
@ -3,12 +3,11 @@ falcon==1.1.0
|
|||||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||||
six>=1.9.0 # MIT
|
six>=1.9.0 # MIT
|
||||||
stevedore>=1.20.0 # Apache-2.0
|
stevedore>=1.20.0 # Apache-2.0
|
||||||
|
keystoneauth1>=2.21.0 # Apache-2.0
|
||||||
oslo.config>=3.22.0 # Apache-2.0
|
oslo.config>=3.22.0 # Apache-2.0
|
||||||
oslo.context>=2.14.0 # Apache-2.0
|
oslo.context>=2.14.0 # Apache-2.0
|
||||||
oslo.log>=3.22.0 # Apache-2.0
|
oslo.log>=3.22.0 # Apache-2.0
|
||||||
oslo.serialization>=1.10.0 # Apache-2.0
|
oslo.serialization>=1.10.0 # Apache-2.0
|
||||||
oslo.utils>=3.20.0 # Apache-2.0
|
oslo.utils>=3.20.0 # Apache-2.0
|
||||||
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
|
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
|
||||||
|
|
||||||
python-barbicanclient>=4.0.0 # Apache-2.0
|
python-barbicanclient>=4.0.0 # Apache-2.0
|
||||||
keystoneauth1>=2.21.0 # Apache-2.0
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user