Merge "Add OS::Barbican::*Container"
This commit is contained in:
commit
4686c5d2f2
@ -10,12 +10,13 @@
|
||||
# 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 barbicanclient import client as barbican_client
|
||||
from barbicanclient import containers
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine.clients import client_plugin
|
||||
|
||||
|
||||
from barbicanclient import client as barbican_client
|
||||
from heat.engine import constraints
|
||||
|
||||
CLIENT_NAME = 'barbican'
|
||||
|
||||
@ -38,3 +39,32 @@ class BarbicanClientPlugin(client_plugin.ClientPlugin):
|
||||
# This is the only exception the client raises
|
||||
# Inspecting the message to see if it's a 'Not Found'
|
||||
return 'Not Found' in six.text_type(ex)
|
||||
|
||||
def create_generic_container(self, **props):
|
||||
return containers.Container(
|
||||
self.client().containers._api, **props)
|
||||
|
||||
def create_certificate(self, **props):
|
||||
return containers.CertificateContainer(
|
||||
self.client().containers._api, **props)
|
||||
|
||||
def create_rsa(self, **props):
|
||||
return containers.RSAContainer(
|
||||
self.client().containers._api, **props)
|
||||
|
||||
def get_secret_by_ref(self, secret_ref):
|
||||
try:
|
||||
return self.client().secrets.get(
|
||||
secret_ref)._get_formatted_entity()
|
||||
except Exception as ex:
|
||||
if self.is_not_found(ex):
|
||||
raise exception.EntityNotFound(
|
||||
entity="Secret",
|
||||
name=secret_ref)
|
||||
raise ex
|
||||
|
||||
|
||||
class SecretConstraint(constraints.BaseCustomConstraint):
|
||||
resource_client_name = CLIENT_NAME
|
||||
resource_getter_name = 'get_secret_by_ref'
|
||||
expected_exceptions = (exception.EntityNotFound,)
|
||||
|
260
heat/engine/resources/openstack/barbican/container.py
Normal file
260
heat/engine/resources/openstack/barbican/container.py
Normal file
@ -0,0 +1,260 @@
|
||||
#
|
||||
# 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 six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.engine import attributes
|
||||
from heat.engine import constraints
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
from heat.engine import support
|
||||
|
||||
|
||||
class GenericContainer(resource.Resource):
|
||||
"""A resource for creating Barbican generic container.
|
||||
|
||||
A generic container is used for any type of secret that a user
|
||||
may wish to aggregate. There are no restrictions on the amount
|
||||
of secrets that can be held within this container.
|
||||
"""
|
||||
|
||||
support_status = support.SupportStatus(version='6.0.0')
|
||||
|
||||
default_client_name = 'barbican'
|
||||
|
||||
entity = 'containers'
|
||||
|
||||
PROPERTIES = (
|
||||
NAME, SECRETS,
|
||||
) = (
|
||||
'name', 'secrets',
|
||||
)
|
||||
|
||||
ATTRIBUTES = (
|
||||
STATUS, CONTAINER_REF, SECRET_REFS, CONSUMERS,
|
||||
) = (
|
||||
'status', 'container_ref', 'secret_refs', 'consumers',
|
||||
)
|
||||
|
||||
_SECRETS_PROPERTIES = (
|
||||
NAME, REF,
|
||||
) = (
|
||||
'name', 'ref'
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Human-readable name for the container.'),
|
||||
),
|
||||
SECRETS: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_('References to secrets that will be stored in container.'),
|
||||
schema=properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
schema={
|
||||
NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name of the secret.'),
|
||||
required=True
|
||||
),
|
||||
REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to the secret.'),
|
||||
required=True,
|
||||
constraints=[constraints.CustomConstraint(
|
||||
'barbican.secret')],
|
||||
),
|
||||
}
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
STATUS: attributes.Schema(
|
||||
_('The status of the container.'),
|
||||
type=attributes.Schema.STRING
|
||||
),
|
||||
CONTAINER_REF: attributes.Schema(
|
||||
_('The URI to the container.'),
|
||||
type=attributes.Schema.STRING
|
||||
),
|
||||
SECRET_REFS: attributes.Schema(
|
||||
_('The URIs to secrets stored in container.'),
|
||||
type=attributes.Schema.MAP
|
||||
),
|
||||
CONSUMERS: attributes.Schema(
|
||||
_('The URIs to container consumers.'),
|
||||
type=attributes.Schema.LIST
|
||||
),
|
||||
}
|
||||
|
||||
def get_refs(self):
|
||||
secrets = self.properties.get(self.SECRETS) or []
|
||||
return [secret['ref'] for secret in secrets]
|
||||
|
||||
def validate(self):
|
||||
super(GenericContainer, self).validate()
|
||||
refs = self.get_refs()
|
||||
if len(refs) != len(set(refs)):
|
||||
msg = _("Duplicate refs are not allowed.")
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
def create_container(self):
|
||||
if self.properties[self.SECRETS]:
|
||||
secrets = dict((secret['name'], secret['ref'])
|
||||
for secret in self.properties[self.SECRETS])
|
||||
else:
|
||||
secrets = {}
|
||||
info = {'secret_refs': secrets}
|
||||
if self.properties[self.NAME] is not None:
|
||||
info.update({'name': self.properties[self.NAME]})
|
||||
return self.client_plugin().create_generic_container(**info)
|
||||
|
||||
def handle_create(self):
|
||||
container_ref = self.create_container().store()
|
||||
self.resource_id_set(container_ref)
|
||||
return container_ref
|
||||
|
||||
def check_create_complete(self, container_href):
|
||||
container = self.client().containers.get(container_href)
|
||||
|
||||
if container.status == 'ERROR':
|
||||
reason = container.error_reason
|
||||
code = container.error_status_code
|
||||
msg = (_("Container '%(name)s' creation failed: "
|
||||
"%(code)s - %(reason)s")
|
||||
% {'name': self.name, 'code': code, 'reason': reason})
|
||||
raise exception.ResourceInError(
|
||||
status_reason=msg, resource_status=container.status)
|
||||
|
||||
return container.status == 'ACTIVE'
|
||||
|
||||
def _resolve_attribute(self, name):
|
||||
container = self.client().containers.get(self.resource_id)
|
||||
return getattr(container, name, None)
|
||||
|
||||
# TODO(ochuprykov): remove this method when bug #1485619 will be fixed
|
||||
def _show_resource(self):
|
||||
container = self.client().containers.get(self.resource_id)
|
||||
info = container._get_formatted_entity()
|
||||
return dict(zip(info[0], info[1]))
|
||||
|
||||
|
||||
class CertificateContainer(GenericContainer):
|
||||
"""A resource for creating barbican certificate container.
|
||||
|
||||
A certificate container is used for storing the secrets that
|
||||
are relevant to certificates.
|
||||
"""
|
||||
|
||||
PROPERTIES = (
|
||||
NAME, CERTIFICATE_REF, PRIVATE_KEY_REF,
|
||||
PRIVATE_KEY_PASSPHRASE_REF, INTERMEDIATES_REF,
|
||||
) = (
|
||||
'name', 'certificate_ref', 'private_key_ref',
|
||||
'private_key_passphrase_ref', 'intermediates_ref',
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Human-readable name for the container.'),
|
||||
),
|
||||
CERTIFICATE_REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to certificate.'),
|
||||
constraints=[constraints.CustomConstraint('barbican.secret')],
|
||||
),
|
||||
PRIVATE_KEY_REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to private key.'),
|
||||
constraints=[constraints.CustomConstraint('barbican.secret')],
|
||||
),
|
||||
PRIVATE_KEY_PASSPHRASE_REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to private key passphrase.'),
|
||||
constraints=[constraints.CustomConstraint('barbican.secret')],
|
||||
),
|
||||
INTERMEDIATES_REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to intermediates.'),
|
||||
constraints=[constraints.CustomConstraint('barbican.secret')],
|
||||
),
|
||||
}
|
||||
|
||||
def create_container(self):
|
||||
info = dict((k, v) for k, v in six.iteritems(self.properties)
|
||||
if v is not None)
|
||||
return self.client_plugin().create_certificate(**info)
|
||||
|
||||
def get_refs(self):
|
||||
return [v for k, v in six.iteritems(self.properties)
|
||||
if (k != self.NAME and v is not None)]
|
||||
|
||||
|
||||
class RSAContainer(GenericContainer):
|
||||
"""A resource for creating barbican RSA container.
|
||||
|
||||
An RSA container is used for storing RSA public keys, private keys,
|
||||
and private key pass phrases.
|
||||
"""
|
||||
|
||||
PROPERTIES = (
|
||||
NAME, PRIVATE_KEY_REF, PRIVATE_KEY_PASSPHRASE_REF,
|
||||
PUBLIC_KEY_REF,
|
||||
) = (
|
||||
'name', 'private_key_ref', 'private_key_passphrase_ref',
|
||||
'public_key_ref',
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Human-readable name for the container.'),
|
||||
),
|
||||
PRIVATE_KEY_REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to private key.'),
|
||||
constraints=[constraints.CustomConstraint('barbican.secret')],
|
||||
),
|
||||
PRIVATE_KEY_PASSPHRASE_REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to private key passphrase.'),
|
||||
constraints=[constraints.CustomConstraint('barbican.secret')],
|
||||
),
|
||||
PUBLIC_KEY_REF: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Reference to public key.'),
|
||||
constraints=[constraints.CustomConstraint('barbican.secret')],
|
||||
),
|
||||
}
|
||||
|
||||
def create_container(self):
|
||||
info = dict((k, v) for k, v in six.iteritems(self.properties)
|
||||
if v is not None)
|
||||
return self.client_plugin().create_rsa(**info)
|
||||
|
||||
def get_refs(self):
|
||||
return [v for k, v in six.iteritems(self.properties)
|
||||
if (k != self.NAME and v is not None)]
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
'OS::Barbican::GenericContainer': GenericContainer,
|
||||
'OS::Barbican::CertificateContainer': CertificateContainer,
|
||||
'OS::Barbican::RSAContainer': RSAContainer
|
||||
}
|
@ -11,14 +11,61 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from barbicanclient import exceptions
|
||||
import mock
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine.clients.os import barbican
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
class BarbicanClientPluginTest(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(BarbicanClientPluginTest, self).setUp()
|
||||
self.barbican_client = mock.MagicMock()
|
||||
con = utils.dummy_context()
|
||||
c = con.clients
|
||||
self.barbican_plugin = c.client_plugin('barbican')
|
||||
self.barbican_plugin._client = self.barbican_client
|
||||
|
||||
def test_create(self):
|
||||
context = utils.dummy_context()
|
||||
plugin = context.clients.client_plugin('barbican')
|
||||
client = plugin.client()
|
||||
self.assertIsNotNone(client.orders)
|
||||
|
||||
def test_get_secret_by_ref(self):
|
||||
self.barbican_client.secrets.get(
|
||||
)._get_formatted_entity.return_value = {}
|
||||
self.assertEqual({}, self.barbican_plugin.get_secret_by_ref("secret"))
|
||||
|
||||
def test_get_secret_by_ref_not_found(self):
|
||||
self.barbican_client.secrets.get(
|
||||
)._get_formatted_entity.side_effect = exceptions.HTTPClientError(
|
||||
message="Not Found")
|
||||
self.assertRaises(
|
||||
exception.EntityNotFound,
|
||||
self.barbican_plugin.get_secret_by_ref,
|
||||
"secret")
|
||||
|
||||
|
||||
class SecretConstraintTest(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SecretConstraintTest, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
self.mock_get_secret_by_ref = mock.Mock()
|
||||
self.ctx.clients.client_plugin(
|
||||
'barbican').get_secret_by_ref = self.mock_get_secret_by_ref
|
||||
self.constraint = barbican.SecretConstraint()
|
||||
|
||||
def test_validation(self):
|
||||
self.mock_get_secret_by_ref.return_value = {}
|
||||
self.assertTrue(self.constraint.validate("foo", self.ctx))
|
||||
|
||||
def test_validation_error(self):
|
||||
self.mock_get_secret_by_ref.side_effect = exception.EntityNotFound(
|
||||
entity='Secret', name='bar')
|
||||
self.assertFalse(self.constraint.validate("bar", self.ctx))
|
||||
|
@ -25,6 +25,7 @@ import testtools
|
||||
from heat.common import context
|
||||
from heat.common import messaging
|
||||
from heat.common import policy
|
||||
from heat.engine.clients.os import barbican
|
||||
from heat.engine.clients.os import cinder
|
||||
from heat.engine.clients.os import glance
|
||||
from heat.engine.clients.os import keystone
|
||||
@ -297,3 +298,7 @@ class HeatTestCase(testscenarios.WithScenarios,
|
||||
def stub_ProviderConstraint_validate(self):
|
||||
validate = self.patchobject(neutron.ProviderConstraint, 'validate')
|
||||
validate.return_value = True
|
||||
|
||||
def stub_SecretConstraint_validate(self):
|
||||
validate = self.patchobject(barbican.SecretConstraint, 'validate')
|
||||
validate.return_value = True
|
||||
|
214
heat/tests/openstack/barbican/test_container.py
Normal file
214
heat/tests/openstack/barbican/test_container.py
Normal file
@ -0,0 +1,214 @@
|
||||
#
|
||||
# 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 mock
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine.resources.openstack.barbican import container
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import scheduler
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
|
||||
stack_template_generic = '''
|
||||
heat_template_version: 2015-10-15
|
||||
description: Test template
|
||||
resources:
|
||||
container:
|
||||
type: OS::Barbican::GenericContainer
|
||||
properties:
|
||||
name: mynewcontainer
|
||||
secrets:
|
||||
- name: secret1
|
||||
ref: ref1
|
||||
- name: secret2
|
||||
ref: ref2
|
||||
'''
|
||||
|
||||
stack_template_certificate = '''
|
||||
heat_template_version: 2015-10-15
|
||||
description: Test template
|
||||
resources:
|
||||
container:
|
||||
type: OS::Barbican::CertificateContainer
|
||||
properties:
|
||||
name: mynewcontainer
|
||||
certificate_ref: cref
|
||||
private_key_ref: pkref
|
||||
private_key_passphrase_ref: pkpref
|
||||
intermediates_ref: iref
|
||||
'''
|
||||
|
||||
stack_template_rsa = '''
|
||||
heat_template_version: 2015-10-15
|
||||
description: Test template
|
||||
resources:
|
||||
container:
|
||||
type: OS::Barbican::RSAContainer
|
||||
properties:
|
||||
name: mynewcontainer
|
||||
private_key_ref: pkref
|
||||
private_key_passphrase_ref: pkpref
|
||||
public_key_ref: pubref
|
||||
'''
|
||||
|
||||
|
||||
def template_by_name(name='OS::Barbican::GenericContainer'):
|
||||
mapping = {'OS::Barbican::GenericContainer': stack_template_generic,
|
||||
'OS::Barbican::CertificateContainer':
|
||||
stack_template_certificate,
|
||||
'OS::Barbican::RSAContainer': stack_template_rsa}
|
||||
return mapping[name]
|
||||
|
||||
|
||||
class FakeContainer(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def store(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class TestContainer(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestContainer, self).setUp()
|
||||
utils.setup_dummy_db()
|
||||
self.ctx = utils.dummy_context()
|
||||
|
||||
self.patcher_client = mock.patch.object(
|
||||
container.GenericContainer, 'client')
|
||||
self.patcher_plugin = mock.patch.object(
|
||||
container.GenericContainer, 'client_plugin')
|
||||
mock_client = self.patcher_client.start()
|
||||
self.client = mock_client.return_value
|
||||
mock_plugin = self.patcher_plugin.start()
|
||||
self.client_plugin = mock_plugin.return_value
|
||||
self.stub_SecretConstraint_validate()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestContainer, self).tearDown()
|
||||
self.patcher_client.stop()
|
||||
self.patcher_plugin.stop()
|
||||
|
||||
def _create_resource(self, name, snippet=None, stack=None,
|
||||
tmpl_name='OS::Barbican::GenericContainer'):
|
||||
|
||||
tmpl = template_format.parse(template_by_name(tmpl_name))
|
||||
if stack is None:
|
||||
self.stack = utils.parse_stack(tmpl)
|
||||
else:
|
||||
self.stack = stack
|
||||
resource_defns = self.stack.t.resource_definitions(stack)
|
||||
if snippet is None:
|
||||
snippet = resource_defns['container']
|
||||
res_class = container.resource_mapping()[tmpl_name]
|
||||
res = res_class(name, snippet, self.stack)
|
||||
res.check_create_complete = mock.Mock(return_value=True)
|
||||
create_generic_container = self.client_plugin.create_generic_container
|
||||
create_generic_container.return_value = FakeContainer('generic')
|
||||
self.client_plugin.create_certificate.return_value = FakeContainer(
|
||||
'certificate'
|
||||
)
|
||||
self.client_plugin.create_rsa.return_value = FakeContainer('rsa')
|
||||
scheduler.TaskRunner(res.create)()
|
||||
return res
|
||||
|
||||
def test_create_generic(self):
|
||||
res = self._create_resource('foo')
|
||||
expected_state = (res.CREATE, res.COMPLETE)
|
||||
self.assertEqual(expected_state, res.state)
|
||||
args = self.client_plugin.create_generic_container.call_args[1]
|
||||
self.assertEqual('mynewcontainer', args['name'])
|
||||
self.assertEqual({'secret1': 'ref1', 'secret2': 'ref2'},
|
||||
args['secret_refs'])
|
||||
self.assertEqual(sorted(['ref1', 'ref2']), sorted(res.get_refs()))
|
||||
|
||||
def test_create_certificate(self):
|
||||
res = self._create_resource(
|
||||
'foo', tmpl_name='OS::Barbican::CertificateContainer')
|
||||
expected_state = (res.CREATE, res.COMPLETE)
|
||||
self.assertEqual(expected_state, res.state)
|
||||
args = self.client_plugin.create_certificate.call_args[1]
|
||||
self.assertEqual('mynewcontainer', args['name'])
|
||||
self.assertEqual('cref', args['certificate_ref'])
|
||||
self.assertEqual('pkref', args['private_key_ref'])
|
||||
self.assertEqual('pkpref', args['private_key_passphrase_ref'])
|
||||
self.assertEqual('iref', args['intermediates_ref'])
|
||||
self.assertEqual(sorted(['pkref', 'pkpref', 'iref', 'cref']),
|
||||
sorted(res.get_refs()))
|
||||
|
||||
def test_create_rsa(self):
|
||||
res = self._create_resource(
|
||||
'foo', tmpl_name='OS::Barbican::RSAContainer')
|
||||
expected_state = (res.CREATE, res.COMPLETE)
|
||||
self.assertEqual(expected_state, res.state)
|
||||
args = self.client_plugin.create_rsa.call_args[1]
|
||||
self.assertEqual('mynewcontainer', args['name'])
|
||||
self.assertEqual('pkref', args['private_key_ref'])
|
||||
self.assertEqual('pubref', args['public_key_ref'])
|
||||
self.assertEqual('pkpref', args['private_key_passphrase_ref'])
|
||||
self.assertEqual(sorted(['pkref', 'pubref', 'pkpref']),
|
||||
sorted(res.get_refs()))
|
||||
|
||||
def test_create_failed_on_validation(self):
|
||||
tmpl = template_format.parse(template_by_name())
|
||||
stack = utils.parse_stack(tmpl)
|
||||
props = tmpl['resources']['container']['properties']
|
||||
props['secrets'].append({'name': 'secret3', 'ref': 'ref1'})
|
||||
defn = rsrc_defn.ResourceDefinition(
|
||||
'failed_container', 'OS::Barbican::GenericContainer', props)
|
||||
res = container.GenericContainer('foo', defn, stack)
|
||||
self.assertRaisesRegexp(exception.StackValidationFailed,
|
||||
'Duplicate refs are not allowed',
|
||||
res.validate)
|
||||
|
||||
def test_attributes(self):
|
||||
mock_container = mock.Mock()
|
||||
mock_container.status = 'test-status'
|
||||
mock_container.container_ref = 'test-container-ref'
|
||||
mock_container.secret_refs = {'name': 'ref'}
|
||||
mock_container.consumers = [{'name': 'name1', 'ref': 'ref1'}]
|
||||
mock_container._get_formatted_entity.return_value = (
|
||||
('attr', ), ('v',))
|
||||
res = self._create_resource('foo')
|
||||
self.client.containers.get.return_value = mock_container
|
||||
self.assertEqual('test-status', res.FnGetAtt('status'))
|
||||
self.assertEqual('test-container-ref', res.FnGetAtt('container_ref'))
|
||||
self.assertEqual({'name': 'ref'}, res.FnGetAtt('secret_refs'))
|
||||
self.assertEqual([{'name': 'name1', 'ref': 'ref1'}],
|
||||
res.FnGetAtt('consumers'))
|
||||
self.assertEqual({'attr': 'v'}, res.FnGetAtt('show'))
|
||||
|
||||
def test_check_create_complete(self):
|
||||
tmpl = template_format.parse(template_by_name())
|
||||
stack = utils.parse_stack(tmpl)
|
||||
resource_defns = stack.t.resource_definitions(stack)
|
||||
res_template = resource_defns['container']
|
||||
res = container.GenericContainer('foo', res_template, stack)
|
||||
mock_active = mock.Mock(status='ACTIVE')
|
||||
self.client.containers.get.return_value = mock_active
|
||||
self.assertTrue(res.check_create_complete('foo'))
|
||||
mock_not_active = mock.Mock(status='PENDING')
|
||||
self.client.containers.get.return_value = mock_not_active
|
||||
self.assertFalse(res.check_create_complete('foo'))
|
||||
mock_not_active = mock.Mock(status='ERROR', error_reason='foo',
|
||||
error_status_code=500)
|
||||
self.client.containers.get.return_value = mock_not_active
|
||||
exc = self.assertRaises(exception.ResourceInError,
|
||||
res.check_create_complete, 'foo')
|
||||
self.assertIn('foo', six.text_type(exc))
|
||||
self.assertIn('500', six.text_type(exc))
|
@ -74,6 +74,7 @@ heat.clients =
|
||||
zaqar = heat.engine.clients.os.zaqar:ZaqarClientPlugin
|
||||
|
||||
heat.constraints =
|
||||
barbican.secret = heat.engine.clients.os.barbican:SecretConstraint
|
||||
nova.flavor = heat.engine.clients.os.nova:FlavorConstraint
|
||||
nova.host = heat.engine.clients.os.nova:HostConstraint
|
||||
nova.network = heat.engine.clients.os.nova:NetworkConstraint
|
||||
|
Loading…
Reference in New Issue
Block a user