Merge branch 'master' into oslo-log-integration

This commit is contained in:
Felipe Monteiro 2017-07-06 18:48:42 -04:00 committed by GitHub
commit bc36541f52
9 changed files with 230 additions and 6 deletions

View File

@ -2,6 +2,7 @@ CHANGES
=======
* Add oslo.log integration to Deckhand.
* DECKHAND-10: Barbican initial integration
* DECKHAND-2: Design core Deckhand API framework
* Oslo config integration (#1)
* Add ChangeLog

View File

View 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

View 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

View File

@ -14,6 +14,29 @@
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(
name='barbican',
title='Barbican Options',
@ -21,7 +44,12 @@ barbican_group = cfg.OptGroup(
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(
name='logging',
@ -38,10 +66,16 @@ logging_opts = [
def register_opts(conf):
conf.register_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_opts(logging_opts, group=logging_group)
def list_opts():
return {barbican_group: barbican_opts,
keystone_auth_group: keystone_auth_opts,
logging_group: logging_opts}
register_opts(CONF)

View File

@ -16,6 +16,7 @@ import falcon
from oslo_serialization import jsonutils as json
from deckhand.barbican import driver
from deckhand.control import base as api_base
@ -29,8 +30,10 @@ class SecretsResource(api_base.BaseResource):
def __init__(self, **kwargs):
super(SecretsResource, self).__init__(**kwargs)
self.authorized_roles = ['user']
self.barbican_driver = driver.BarbicanDriver()
def on_get(self, req, resp):
# 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

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from deckhand.control import api
from .control import api
def start_deckhand():
@ -20,4 +20,4 @@ def start_deckhand():
# Callable to be used by uwsgi.
deckhand = start_deckhand()
deckhand_callable = start_deckhand()

View 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

View File

@ -3,12 +3,11 @@ falcon==1.1.0
pbr!=2.1.0,>=2.0.0 # Apache-2.0
six>=1.9.0 # MIT
stevedore>=1.20.0 # Apache-2.0
keystoneauth1>=2.21.0 # Apache-2.0
oslo.config>=3.22.0 # Apache-2.0
oslo.context>=2.14.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
python-barbicanclient>=4.0.0 # Apache-2.0
keystoneauth1>=2.21.0 # Apache-2.0