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
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
from barbicanclient import client as barbican_client
|
||||||
|
from barbicanclient import containers
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from heat.common import exception
|
||||||
from heat.engine.clients import client_plugin
|
from heat.engine.clients import client_plugin
|
||||||
|
from heat.engine import constraints
|
||||||
|
|
||||||
from barbicanclient import client as barbican_client
|
|
||||||
|
|
||||||
CLIENT_NAME = 'barbican'
|
CLIENT_NAME = 'barbican'
|
||||||
|
|
||||||
@ -38,3 +39,32 @@ class BarbicanClientPlugin(client_plugin.ClientPlugin):
|
|||||||
# This is the only exception the client raises
|
# This is the only exception the client raises
|
||||||
# Inspecting the message to see if it's a 'Not Found'
|
# Inspecting the message to see if it's a 'Not Found'
|
||||||
return 'Not Found' in six.text_type(ex)
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# 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 common
|
||||||
from heat.tests import utils
|
from heat.tests import utils
|
||||||
|
|
||||||
|
|
||||||
class BarbicanClientPluginTest(common.HeatTestCase):
|
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):
|
def test_create(self):
|
||||||
context = utils.dummy_context()
|
context = utils.dummy_context()
|
||||||
plugin = context.clients.client_plugin('barbican')
|
plugin = context.clients.client_plugin('barbican')
|
||||||
client = plugin.client()
|
client = plugin.client()
|
||||||
self.assertIsNotNone(client.orders)
|
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 context
|
||||||
from heat.common import messaging
|
from heat.common import messaging
|
||||||
from heat.common import policy
|
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 cinder
|
||||||
from heat.engine.clients.os import glance
|
from heat.engine.clients.os import glance
|
||||||
from heat.engine.clients.os import keystone
|
from heat.engine.clients.os import keystone
|
||||||
@ -297,3 +298,7 @@ class HeatTestCase(testscenarios.WithScenarios,
|
|||||||
def stub_ProviderConstraint_validate(self):
|
def stub_ProviderConstraint_validate(self):
|
||||||
validate = self.patchobject(neutron.ProviderConstraint, 'validate')
|
validate = self.patchobject(neutron.ProviderConstraint, 'validate')
|
||||||
validate.return_value = True
|
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
|
zaqar = heat.engine.clients.os.zaqar:ZaqarClientPlugin
|
||||||
|
|
||||||
heat.constraints =
|
heat.constraints =
|
||||||
|
barbican.secret = heat.engine.clients.os.barbican:SecretConstraint
|
||||||
nova.flavor = heat.engine.clients.os.nova:FlavorConstraint
|
nova.flavor = heat.engine.clients.os.nova:FlavorConstraint
|
||||||
nova.host = heat.engine.clients.os.nova:HostConstraint
|
nova.host = heat.engine.clients.os.nova:HostConstraint
|
||||||
nova.network = heat.engine.clients.os.nova:NetworkConstraint
|
nova.network = heat.engine.clients.os.nova:NetworkConstraint
|
||||||
|
Loading…
Reference in New Issue
Block a user