Implement barbican client plugin

This moves the client creation code out of a Clients subclass into
its own client plugin.

Barbican is not an integrated project so the plugin is not registered
if barbicanclient cannot be imported.

The code which prevented client errors in _resolve_attribute from being raised
has been removed. This will prevent invalid attribute values from being
memoized.

The base package has been renamed to heat_barbican since stevedore
is used for client plugin loading and the base package will be
installed by setuptools, the package barbican might clash with
the actual Barbican project.

Change-Id: I5788fb4618fef0d1c3e39cc98094bb2813e842c5
This commit is contained in:
Steve Baker 2014-06-30 17:09:31 +12:00 committed by Randall Burt
parent 23893088f1
commit fd191b7e48
12 changed files with 51 additions and 65 deletions

View File

@ -11,7 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from heat.engine import clients as heat_clients
from heat.engine.clients import client_plugin
try:
@ -22,9 +22,12 @@ except ImportError:
auth = None
class Clients(heat_clients.OpenStackClients):
class BarbicanClientPlugin(client_plugin.ClientPlugin):
def _barbican(self):
keystone_client = self.client('keystone').client
def _create(self):
keystone_client = self.clients('keystone').client
auth_plugin = auth.KeystoneAuthV2(keystone=keystone_client)
return barbican_client.Client(auth_plugin=auth_plugin)
client = barbican_client.Client(auth_plugin=auth_plugin)
return client

View File

@ -15,12 +15,13 @@ import six
from heat.common import exception
from heat.engine import attributes
from heat.engine import clients
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.openstack.common import log as logging
from .. import clients # noqa
from .. import client # noqa
LOG = logging.getLogger(__name__)
@ -104,10 +105,6 @@ class Order(resource.Resource):
SECRET_REF: attributes.Schema(_('The URI to the created secret.')),
}
def __init__(self, name, json_snippet, stack):
super(Order, self).__init__(name, json_snippet, stack)
self.clients = clients.Clients(self.context)
def barbican(self):
return self.clients.client('barbican')
@ -136,7 +133,7 @@ class Order(resource.Resource):
try:
self.barbican().orders.delete(self.resource_id)
self.resource_id_set(None)
except clients.barbican_client.HTTPClientError as exc:
except client.barbican_client.HTTPClientError as exc:
# This is the only exception the client raises
# Inspecting the message to see if it's a 'Not Found'
if 'Not Found' in six.text_type(exc):
@ -145,13 +142,7 @@ class Order(resource.Resource):
raise
def _resolve_attribute(self, name):
try:
order = self.barbican().orders.get(self.resource_id)
except clients.barbican_client.HTTPClientError as exc:
LOG.warn(_("Order '%(name)s' not found: %(exc)s") %
{'name': self.resource_id, 'exc': six.text_type(exc)})
return ''
order = self.barbican().orders.get(self.resource_id)
return getattr(order, name)
@ -162,7 +153,7 @@ def resource_mapping():
def available_resource_mapping():
if not clients.barbican_client:
if not clients.has_client('barbican'):
return {}
return resource_mapping()

View File

@ -15,12 +15,13 @@ import six
from heat.common import exception
from heat.engine import attributes
from heat.engine import clients
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.openstack.common import log as logging
from .. import clients # noqa
from .. import client # noqa
LOG = logging.getLogger(__name__)
@ -107,10 +108,6 @@ class Secret(resource.Resource):
),
}
def __init__(self, name, json_snippet, stack):
super(Secret, self).__init__(name, json_snippet, stack)
self.clients = clients.Clients(self.context)
def barbican(self):
return self.clients.client('barbican')
@ -141,7 +138,7 @@ class Secret(resource.Resource):
try:
self.barbican().secrets.delete(self.resource_id)
self.resource_id_set(None)
except clients.barbican_client.HTTPClientError as exc:
except client.barbican_client.HTTPClientError as exc:
# This is the only exception the client raises
# Inspecting the message to see if it's a 'Not Found'
if 'Not Found' in six.text_type(exc):
@ -150,19 +147,13 @@ class Secret(resource.Resource):
raise
def _resolve_attribute(self, name):
try:
if name == self.DECRYPTED_PAYLOAD:
return self.barbican().secrets.decrypt(
self.resource_id)
if name == self.DECRYPTED_PAYLOAD:
return self.barbican().secrets.decrypt(
self.resource_id)
secret = self.barbican().secrets.get(self.resource_id)
if name == self.STATUS:
return secret.status
except clients.barbican_client.HTTPClientError as e:
msg = _("Failed to resolve '%(name)s' for %(res)s '%(id)s': %(e)s")
LOG.warn(msg % {'name': name, 'res': self.__class__.__name__,
'id': self.resource_id, 'e': six.text_type(e)})
return ''
secret = self.barbican().secrets.get(self.resource_id)
if name == self.STATUS:
return secret.status
def resource_mapping():
@ -172,7 +163,7 @@ def resource_mapping():
def available_resource_mapping():
if not clients.barbican_client:
if not clients.has_client('barbican'):
return {}
return resource_mapping()

View File

@ -22,9 +22,9 @@ from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import utils
from .. import client # noqa
from ..resources import order # noqa
stack_template = '''
heat_template_version: 2013-05-23
description: Test template
@ -94,17 +94,16 @@ class TestOrder(HeatTestCase):
self.assertEqual('test-order-ref', res.FnGetAtt('order_ref'))
self.assertEqual('test-secret-ref', res.FnGetAtt('secret_ref'))
@mock.patch.object(order.clients, 'barbican_client', new=mock.Mock())
@mock.patch.object(client, 'barbican_client', new=mock.Mock())
def test_attributes_handle_exceptions(self):
mock_order = mock.Mock()
self.barbican.orders.get.return_value = mock_order
res = self._create_resource('foo', self.res_template, self.stack)
order.clients.barbican_client.HTTPClientError = Exception
not_found_exc = order.clients.barbican_client.HTTPClientError('boom')
self.barbican.orders.get.side_effect = not_found_exc
self.assertEqual('', res.FnGetAtt('order_ref'))
client.barbican_client.HTTPClientError = Exception
self.barbican.orders.get.side_effect = Exception('boom')
self.assertRaises(client.barbican_client.HTTPClientError,
res.FnGetAtt, 'order_ref')
def test_create_order_sets_resource_id(self):
self.barbican.orders.create.return_value = 'foo'
@ -147,22 +146,22 @@ class TestOrder(HeatTestCase):
self.assertIsNone(res.resource_id)
self.barbican.orders.delete.assert_called_once_with('foo')
@mock.patch.object(order.clients, 'barbican_client', new=mock.Mock())
@mock.patch.object(client, 'barbican_client', new=mock.Mock())
def test_handle_delete_ignores_not_found_errors(self):
res = self._create_resource('foo', self.res_template, self.stack)
order.clients.barbican_client.HTTPClientError = Exception
exc = order.clients.barbican_client.HTTPClientError('Not Found. Nope.')
client.barbican_client.HTTPClientError = Exception
exc = client.barbican_client.HTTPClientError('Not Found. Nope.')
self.barbican.orders.delete.side_effect = exc
scheduler.TaskRunner(res.delete)()
self.assertTrue(self.barbican.orders.delete.called)
@mock.patch.object(order.clients, 'barbican_client', new=mock.Mock())
@mock.patch.object(client, 'barbican_client', new=mock.Mock())
def test_handle_delete_raises_resource_failure_on_error(self):
res = self._create_resource('foo', self.res_template, self.stack)
order.clients.barbican_client.HTTPClientError = Exception
exc = order.clients.barbican_client.HTTPClientError('Boom.')
client.barbican_client.HTTPClientError = Exception
exc = client.barbican_client.HTTPClientError('Boom.')
self.barbican.orders.delete.side_effect = exc
exc = self.assertRaises(exception.ResourceFailure,
scheduler.TaskRunner(res.delete))

View File

@ -22,9 +22,9 @@ from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import utils
from .. import client # noqa
from ..resources import secret # noqa
stack_template = '''
heat_template_version: 2013-05-23
description: Test template
@ -83,13 +83,12 @@ class TestSecret(HeatTestCase):
self.assertEqual('test-status', self.res.FnGetAtt('status'))
self.assertEqual('foo', self.res.FnGetAtt('decrypted_payload'))
@mock.patch.object(secret.clients, 'barbican_client', new=mock.Mock())
@mock.patch.object(client, 'barbican_client', new=mock.Mock())
def test_attributes_handles_exceptions(self):
secret.clients.barbican_client.HTTPClientError = Exception
some_error = secret.clients.barbican_client.HTTPClientError('boom')
secret.Secret.barbican.side_effect = some_error
self.assertEqual('', self.res.FnGetAtt('status'))
client.barbican_client.HTTPClientError = Exception
self.barbican.secrets.get.side_effect = Exception('boom')
self.assertRaises(client.barbican_client.HTTPClientError,
self.res.FnGetAtt, 'order_ref')
def test_create_secret_sets_resource_id(self):
self.assertEqual('foo_id', self.res.resource_id)
@ -164,18 +163,18 @@ class TestSecret(HeatTestCase):
self.assertIsNone(self.res.resource_id)
mock_delete.assert_called_once_with('foo_id')
@mock.patch.object(secret.clients, 'barbican_client', new=mock.Mock())
@mock.patch.object(client, 'barbican_client', new=mock.Mock())
def test_handle_delete_ignores_not_found_errors(self):
secret.clients.barbican_client.HTTPClientError = Exception
exc = secret.clients.barbican_client.HTTPClientError('Not Found.')
client.barbican_client.HTTPClientError = Exception
exc = client.barbican_client.HTTPClientError('Not Found.')
self.barbican.secrets.delete.side_effect = exc
scheduler.TaskRunner(self.res.delete)()
self.assertTrue(self.barbican.secrets.delete.called)
@mock.patch.object(secret.clients, 'barbican_client', new=mock.Mock())
@mock.patch.object(client, 'barbican_client', new=mock.Mock())
def test_handle_delete_raises_resource_failure_on_error(self):
secret.clients.barbican_client.HTTPClientError = Exception
exc = secret.clients.barbican_client.HTTPClientError('Boom.')
client.barbican_client.HTTPClientError = Exception
exc = client.barbican_client.HTTPClientError('Boom.')
self.barbican.secrets.delete.side_effect = exc
exc = self.assertRaises(exception.ResourceFailure,
scheduler.TaskRunner(self.res.delete))

View File

@ -17,10 +17,13 @@ classifier =
Programming Language :: Python :: 2.7
Programming Language :: Python :: 2.6
packages =
heat_barbican
[files]
# Copy to /usr/lib/heat for plugin loading
data_files =
lib/heat/barbican = barbican/resources/*
lib/heat/barbican = heat_barbican/resources/*
[global]
setup-hooks =