1339f0a0b1
Rename the StructuredDeployments resource to StructuredDeploymentGroup to avoid awkward situations when talking about the plural form of the StructuredDeployment resource. This has been agreed at the Vancouver summit. The existing 'StructuredDeployments' resource is kept for backwards compatibility, but with a deprecated status and corresponding deprecation message. Closes-Bug: #1458008 Change-Id: I5d274acf17f222c493d10ded50f87e05a075b424
431 lines
15 KiB
Python
431 lines
15 KiB
Python
#
|
|
# 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
|
|
|
|
from heat.common import exception
|
|
from heat.engine.resources.openstack.heat import structured_config as sc
|
|
from heat.engine import stack as parser
|
|
from heat.engine import template
|
|
from heat.tests import common
|
|
from heat.tests import utils
|
|
|
|
|
|
SCENARIOS = [
|
|
(
|
|
'no_functions',
|
|
dict(input_key='get_input',
|
|
inputs={},
|
|
config={'foo': 'bar'},
|
|
result={'foo': 'bar'}),
|
|
),
|
|
(
|
|
'none_inputs',
|
|
dict(input_key='get_input',
|
|
inputs=None,
|
|
config={'foo': 'bar'},
|
|
result={'foo': 'bar'}),
|
|
),
|
|
(
|
|
'none_config',
|
|
dict(input_key='get_input',
|
|
inputs=None,
|
|
config=None,
|
|
result=None),
|
|
),
|
|
(
|
|
'empty_config',
|
|
dict(input_key='get_input',
|
|
inputs=None,
|
|
config='',
|
|
result=''),
|
|
),
|
|
(
|
|
'simple',
|
|
dict(input_key='get_input',
|
|
inputs={'bar': 'baa'},
|
|
config={'foo': {'get_input': 'bar'}},
|
|
result={'foo': 'baa'}),
|
|
),
|
|
(
|
|
'multi_key',
|
|
dict(input_key='get_input',
|
|
inputs={'bar': 'baa'},
|
|
config={'foo': [{'get_input': 'bar'}, 'other']},
|
|
result={'foo': ['baa', 'other']}),
|
|
),
|
|
(
|
|
'list_arg',
|
|
dict(input_key='get_input',
|
|
inputs={'bar': 'baa'},
|
|
config={'foo': {'get_input': ['bar', 'baz']}},
|
|
result={'foo': {'get_input': ['bar', 'baz']}}),
|
|
),
|
|
(
|
|
'missing_input',
|
|
dict(input_key='get_input',
|
|
inputs={'bar': 'baa'},
|
|
config={'foo': {'get_input': 'barr'}},
|
|
result={'foo': None}),
|
|
),
|
|
(
|
|
'deep',
|
|
dict(input_key='get_input',
|
|
inputs={'bar': 'baa'},
|
|
config={'foo': {'foo': {'get_input': 'bar'}}},
|
|
result={'foo': {'foo': 'baa'}}),
|
|
),
|
|
(
|
|
'shallow',
|
|
dict(input_key='get_input',
|
|
inputs={'bar': 'baa'},
|
|
config={'get_input': 'bar'},
|
|
result='baa'),
|
|
),
|
|
(
|
|
'list',
|
|
dict(input_key='get_input',
|
|
inputs={'bar': 'baa', 'bar2': 'baz', 'bar3': 'bink'},
|
|
config={'foo': [
|
|
{'get_input': 'bar'},
|
|
{'get_input': 'bar2'},
|
|
{'get_input': 'bar3'}]},
|
|
result={'foo': ['baa', 'baz', 'bink']}),
|
|
)
|
|
]
|
|
|
|
|
|
class StructuredConfigTestJSON(common.HeatTestCase):
|
|
|
|
template = {
|
|
'HeatTemplateFormatVersion': '2012-12-12',
|
|
'Resources': {
|
|
'config_mysql': {
|
|
'Type': 'OS::Heat::StructuredConfig',
|
|
'Properties': {'config': {'foo': 'bar'}}
|
|
}
|
|
}
|
|
}
|
|
|
|
stored_config = {'foo': 'bar'}
|
|
|
|
def setUp(self):
|
|
super(StructuredConfigTestJSON, self).setUp()
|
|
self.ctx = utils.dummy_context()
|
|
self.properties = {
|
|
'config': {'foo': 'bar'}
|
|
}
|
|
self.stack = parser.Stack(
|
|
self.ctx, 'software_config_test_stack',
|
|
template.Template(self.template))
|
|
self.config = self.stack['config_mysql']
|
|
self.rpc_client = mock.MagicMock()
|
|
self.config._rpc_client = self.rpc_client
|
|
|
|
def test_resource_mapping(self):
|
|
mapping = sc.resource_mapping()
|
|
self.assertEqual(4, len(mapping))
|
|
self.assertEqual(sc.StructuredConfig,
|
|
mapping['OS::Heat::StructuredConfig'])
|
|
self.assertEqual(sc.StructuredDeployment,
|
|
mapping['OS::Heat::StructuredDeployment'])
|
|
self.assertEqual(sc.StructuredDeploymentGroup,
|
|
mapping['OS::Heat::StructuredDeploymentGroup'])
|
|
self.assertEqual(sc.StructuredDeployments,
|
|
mapping['OS::Heat::StructuredDeployments'])
|
|
self.assertIsInstance(self.config, sc.StructuredConfig)
|
|
|
|
def test_handle_create(self):
|
|
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
|
|
value = {'id': config_id}
|
|
self.rpc_client.create_software_config.return_value = value
|
|
self.config.handle_create()
|
|
self.assertEqual(config_id, self.config.resource_id)
|
|
kwargs = self.rpc_client.create_software_config.call_args[1]
|
|
self.assertEqual(self.stored_config, kwargs['config'])
|
|
|
|
|
|
class StructuredDeploymentDerivedTest(common.HeatTestCase):
|
|
|
|
template = {
|
|
'HeatTemplateFormatVersion': '2012-12-12',
|
|
'Resources': {
|
|
'deploy_mysql': {
|
|
'Type': 'OS::Heat::StructuredDeployment'
|
|
}
|
|
}
|
|
}
|
|
|
|
def setUp(self):
|
|
super(StructuredDeploymentDerivedTest, self).setUp()
|
|
self.ctx = utils.dummy_context()
|
|
props = {
|
|
'input_values': {'bar': 'baz'},
|
|
}
|
|
self.template['Resources']['deploy_mysql']['Properties'] = props
|
|
self.stack = parser.Stack(
|
|
self.ctx, 'software_deploly_test_stack',
|
|
template.Template(self.template))
|
|
self.deployment = self.stack['deploy_mysql']
|
|
heat = mock.MagicMock()
|
|
self.deployments = heat.return_value.software_deployments
|
|
|
|
def test_build_derived_config(self):
|
|
source = {
|
|
'config': {"foo": {"get_input": "bar"}}
|
|
}
|
|
inputs = [{'name': 'bar', 'value': 'baz'}]
|
|
result = self.deployment._build_derived_config(
|
|
'CREATE', source, inputs, {})
|
|
self.assertEqual({"foo": "baz"}, result)
|
|
|
|
def test_build_derived_config_params_with_empty_config(self):
|
|
source = {}
|
|
result = self.deployment._build_derived_config_params(
|
|
'CREATE', source)
|
|
self.assertEqual('Heat::Ungrouped', result['group'])
|
|
self.assertEqual({}, result['config'])
|
|
self.assertEqual(self.deployment.physical_resource_name(),
|
|
result['name'])
|
|
self.assertIn({'name': 'bar', 'type': 'String', 'value': 'baz'},
|
|
result['inputs'])
|
|
self.assertIsNone(result['options'])
|
|
self.assertIsNone(result['outputs'])
|
|
|
|
|
|
class StructuredDeploymentWithStrictInputTest(common.HeatTestCase):
|
|
|
|
template = {
|
|
'HeatTemplateFormatVersion': '2012-12-12',
|
|
'Resources': {
|
|
'deploy_mysql': {
|
|
'Type': 'OS::Heat::StructuredDeployment',
|
|
'Properties': {}
|
|
}
|
|
}
|
|
}
|
|
|
|
def setUp(self):
|
|
super(StructuredDeploymentWithStrictInputTest, self).setUp()
|
|
self.source = {'config':
|
|
{'foo': [{"get_input": "bar"},
|
|
{"get_input": "barz"}]}}
|
|
self.inputs = [{'name': 'bar', 'value': 'baz'},
|
|
{'name': 'barz', 'value': 'baz2'}]
|
|
|
|
def _stack_with_template(self, template_def):
|
|
self.ctx = utils.dummy_context()
|
|
self.stack = parser.Stack(
|
|
self.ctx, 'software_deploly_test_stack',
|
|
template.Template(template_def))
|
|
self.deployment = self.stack['deploy_mysql']
|
|
heat = mock.MagicMock()
|
|
self.deployments = heat.return_value.software_deployments
|
|
|
|
def test_build_derived_config_failure(self):
|
|
props = {'input_values_validate': 'STRICT'}
|
|
self.template['Resources']['deploy_mysql']['Properties'] = props
|
|
self._stack_with_template(self.template)
|
|
inputs = [{'name': 'bar', 'value': 'baz'}]
|
|
|
|
self.assertRaises(exception.UserParameterMissing,
|
|
self.deployment._build_derived_config,
|
|
'CREATE', self.source, inputs, {})
|
|
|
|
def test_build_derived_config_success(self):
|
|
props = {'input_values_validate': 'STRICT'}
|
|
self.template['Resources']['deploy_mysql']['Properties'] = props
|
|
self._stack_with_template(self.template)
|
|
|
|
expected = {'foo': ['baz', 'baz2']}
|
|
result = self.deployment._build_derived_config(
|
|
'CREATE', self.source, self.inputs, {})
|
|
self.assertEqual(expected, result)
|
|
|
|
|
|
class StructuredDeploymentParseTest(common.HeatTestCase):
|
|
scenarios = SCENARIOS
|
|
|
|
def test_parse(self):
|
|
parse = sc.StructuredDeployment.parse
|
|
self.assertEqual(
|
|
self.result,
|
|
parse(self.inputs, self.input_key, self.config))
|
|
|
|
|
|
class StructuredDeploymentGroupTest(common.HeatTestCase):
|
|
|
|
template = {
|
|
'heat_template_version': '2013-05-23',
|
|
'resources': {
|
|
'deploy_mysql': {
|
|
'type': 'OS::Heat::StructuredDeploymentGroup',
|
|
'properties': {
|
|
'config': 'config_uuid',
|
|
'servers': {'server1': 'uuid1', 'server2': 'uuid2'},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def setUp(self):
|
|
common.HeatTestCase.setUp(self)
|
|
heat = mock.MagicMock()
|
|
self.deployments = heat.return_value.software_deployments
|
|
|
|
def test_build_resource_definition(self):
|
|
stack = utils.parse_stack(self.template)
|
|
snip = stack.t.resource_definitions(stack)['deploy_mysql']
|
|
resg = sc.StructuredDeploymentGroup('test', snip, stack)
|
|
expect = {
|
|
'type': 'OS::Heat::StructuredDeployment',
|
|
'properties': {
|
|
'actions': ['CREATE', 'UPDATE'],
|
|
'config': 'config_uuid',
|
|
'input_key': 'get_input',
|
|
'input_values': None,
|
|
'name': None,
|
|
'signal_transport': 'CFN_SIGNAL',
|
|
'input_values_validate': 'LAX'
|
|
}
|
|
}
|
|
self.assertEqual(
|
|
expect, resg._build_resource_definition())
|
|
self.assertEqual(
|
|
expect, resg._build_resource_definition(include_all=True))
|
|
|
|
def test_resource_names(self):
|
|
stack = utils.parse_stack(self.template)
|
|
snip = stack.t.resource_definitions(stack)['deploy_mysql']
|
|
resg = sc.StructuredDeploymentGroup('test', snip, stack)
|
|
self.assertEqual(
|
|
set(('server1', 'server2')),
|
|
set(resg._resource_names())
|
|
)
|
|
|
|
resg.properties = {'servers': {'s1': 'u1', 's2': 'u2', 's3': 'u3'}}
|
|
self.assertEqual(
|
|
set(('s1', 's2', 's3')),
|
|
set(resg._resource_names()))
|
|
|
|
def test_assemble_nested(self):
|
|
"""
|
|
Tests that the nested stack that implements the group is created
|
|
appropriately based on properties.
|
|
"""
|
|
stack = utils.parse_stack(self.template)
|
|
snip = stack.t.resource_definitions(stack)['deploy_mysql']
|
|
resg = sc.StructuredDeploymentGroup('test', snip, stack)
|
|
templ = {
|
|
"heat_template_version": "2013-05-23",
|
|
"resources": {
|
|
"server1": {
|
|
'type': 'OS::Heat::StructuredDeployment',
|
|
'properties': {
|
|
'server': 'uuid1',
|
|
'actions': ['CREATE', 'UPDATE'],
|
|
'config': 'config_uuid',
|
|
'input_key': 'get_input',
|
|
'input_values': None,
|
|
'name': None,
|
|
'signal_transport': 'CFN_SIGNAL',
|
|
'input_values_validate': 'LAX'
|
|
}
|
|
},
|
|
"server2": {
|
|
'type': 'OS::Heat::StructuredDeployment',
|
|
'properties': {
|
|
'server': 'uuid2',
|
|
'actions': ['CREATE', 'UPDATE'],
|
|
'config': 'config_uuid',
|
|
'input_key': 'get_input',
|
|
'input_values': None,
|
|
'name': None,
|
|
'signal_transport': 'CFN_SIGNAL',
|
|
'input_values_validate': 'LAX'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(templ, resg._assemble_nested(['server1', 'server2']))
|
|
|
|
|
|
class StructuredDeploymentWithStrictInputParseTest(common.HeatTestCase):
|
|
scenarios = SCENARIOS
|
|
|
|
def test_parse(self):
|
|
self.parse = sc.StructuredDeployment.parse
|
|
if 'missing_input' not in self.shortDescription():
|
|
self.assertEqual(
|
|
self.result,
|
|
self.parse(
|
|
self.inputs,
|
|
self.input_key,
|
|
self.config,
|
|
check_input_val='STRICT')
|
|
)
|
|
else:
|
|
self.assertRaises(exception.UserParameterMissing,
|
|
self.parse,
|
|
self.inputs,
|
|
self.input_key,
|
|
self.config,
|
|
check_input_val='STRICT')
|
|
|
|
|
|
class StructuredDeploymentParseMethodsTest(common.HeatTestCase):
|
|
def test_get_key_args(self):
|
|
snippet = {'get_input': 'bar'}
|
|
input_key = 'get_input'
|
|
expected = 'bar'
|
|
result = sc.StructuredDeployment.get_input_key_arg(snippet, input_key)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_key_args_long_snippet(self):
|
|
snippet = {'get_input': 'bar', 'second': 'foo'}
|
|
input_key = 'get_input'
|
|
result = sc.StructuredDeployment.get_input_key_arg(snippet, input_key)
|
|
self.assertFalse(result)
|
|
|
|
def test_get_key_args_unknown_input_key(self):
|
|
snippet = {'get_input': 'bar'}
|
|
input_key = 'input'
|
|
result = sc.StructuredDeployment.get_input_key_arg(snippet, input_key)
|
|
self.assertFalse(result)
|
|
|
|
def test_get_key_args_wrong_args(self):
|
|
snippet = {'get_input': None}
|
|
input_key = 'get_input'
|
|
result = sc.StructuredDeployment.get_input_key_arg(snippet, input_key)
|
|
self.assertFalse(result)
|
|
|
|
def test_get_input_key_value(self):
|
|
inputs = {'bar': 'baz', 'foo': 'foo2'}
|
|
res = sc.StructuredDeployment.get_input_key_value('bar', inputs, False)
|
|
expected = 'baz'
|
|
self.assertEqual(expected, res)
|
|
|
|
def test_get_input_key_value_raise_exception(self):
|
|
inputs = {'bar': 'baz', 'foo': 'foo2'}
|
|
self.assertRaises(exception.UserParameterMissing,
|
|
sc.StructuredDeployment.get_input_key_value,
|
|
'barz',
|
|
inputs,
|
|
'STRICT')
|
|
|
|
def test_get_input_key_value_get_none(self):
|
|
inputs = {'bar': 'baz', 'foo': 'foo2'}
|
|
res = sc.StructuredDeployment.get_input_key_value('brz', inputs, False)
|
|
self.assertIsNone(res)
|