diff --git a/heat/tests/test_parser.py b/heat/tests/test_parser.py index ccdbe82332..ede99581db 100644 --- a/heat/tests/test_parser.py +++ b/heat/tests/test_parser.py @@ -27,7 +27,6 @@ from heat.common import context from heat.common import exception from heat.common import heat_keystoneclient as hkc from heat.common import template_format -from heat.common import urlfetch import heat.db.api as db_api from heat.engine.cfn import functions as cfn_funcs from heat.engine.cfn import template as cfn_t @@ -1047,75 +1046,71 @@ class StackTest(common.HeatTestCase): status_reason='blarg') self.assertEqual(1, stack.total_resources()) - def _setup_nested(self, name): - nested_tpl = ('{"HeatTemplateFormatVersion" : "2012-12-12",' - '"Resources":{' - '"A": {"Type": "GenericResourceType"},' - '"B": {"Type": "GenericResourceType"}}}') - tpl = {'HeatTemplateFormatVersion': "2012-12-12", - 'Resources': - {'A': {'Type': 'AWS::CloudFormation::Stack', - 'Properties': - {'TemplateURL': 'http://server.test/nested.json'}}, - 'B': {'Type': 'GenericResourceType'}}} - self.m.StubOutWithMock(urlfetch, 'get') - urlfetch.get('http://server.test/nested.json').AndReturn(nested_tpl) - self.m.ReplayAll() - self.stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), - status_reason=name) - self.stack.store() - self.stack.create() - def test_total_resources_nested(self): - self._setup_nested('zyzzyx') - self.assertEqual(4, self.stack.total_resources()) - self.assertIsNotNone(self.stack['A'].nested()) - self.assertEqual( - 2, self.stack['A'].nested().total_resources()) - self.assertEqual( - 4, - self.stack['A'].nested().root_stack.total_resources()) + tpl = {'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': + {'A': {'Type': 'GenericResourceType'}}} + stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), + status_reason='blarg') + + stack['A'].nested = mock.Mock() + stack['A'].nested.return_value.total_resources.return_value = 3 + self.assertEqual(4, stack.total_resources()) def test_iter_resources(self): - self._setup_nested('iter_resources') - nested_stack = self.stack['A'].nested() - resource_generator = self.stack.iter_resources() + tpl = {'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': + {'A': {'Type': 'GenericResourceType'}, + 'B': {'Type': 'GenericResourceType'}}} + stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), + status_reason='blarg') + + def get_more(nested_depth=0): + yield 'X' + yield 'Y' + yield 'Z' + + stack['A'].nested = mock.MagicMock() + stack['A'].nested.return_value.iter_resources.side_effect = get_more + + resource_generator = stack.iter_resources() self.assertIsNot(resource_generator, list) first_level_resources = list(resource_generator) self.assertEqual(2, len(first_level_resources)) - self.assertIn(self.stack['A'], first_level_resources) - self.assertIn(self.stack['B'], first_level_resources) + all_resources = list(stack.iter_resources(1)) + self.assertEqual(5, len(all_resources)) - all_resources = list(self.stack.iter_resources(1)) - self.assertIn(self.stack['A'], first_level_resources) - self.assertIn(self.stack['B'], first_level_resources) - self.assertIn(nested_stack['A'], all_resources) - self.assertIn(nested_stack['B'], all_resources) + def test_root_stack_no_parent(self): + tpl = {'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': + {'A': {'Type': 'GenericResourceType'}}} + stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), + status_reason='blarg') - def test_root_stack(self): - self._setup_nested('toor') - self.assertEqual(self.stack, self.stack.root_stack) - self.assertIsNotNone(self.stack['A'].nested()) - self.assertEqual( - self.stack, self.stack['A'].nested().root_stack) + self.assertEqual(stack, stack.root_stack) - def test_nested_stack_abandon(self): - self._setup_nested('nestedstack') - ret = self.stack.prepare_abandon() - self.assertIsNotNone(self.stack['A'].nested()) - self.assertEqual( - self.stack, self.stack['A'].nested().root_stack) + def test_root_stack_parent_no_stack(self): + tpl = {'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': + {'A': {'Type': 'GenericResourceType'}}} + stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), + status_reason='blarg') - keys = ['name', 'id', 'action', 'status', 'template', 'resources', - 'project_id', 'stack_user_project_id', 'environment'] + stack.parent_resource = mock.Mock() + stack.parent_resource.stack = None + self.assertEqual(stack, stack.root_stack) - self.assertEqual(len(keys), len(ret)) - nested_stack_data = ret['resources']['A'] - self.assertEqual(len(keys), len(nested_stack_data)) - for key in keys: - self.assertIn(key, ret) - self.assertIn(key, nested_stack_data) + def test_root_stack_with_parent(self): + tpl = {'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': + {'A': {'Type': 'GenericResourceType'}}} + stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), + status_reason='blarg') + + stack.parent_resource = mock.Mock() + stack.parent_resource.stack.root_stack = 'test value' + self.assertEqual('test value', stack.root_stack) def test_load_parent_resource(self): self.stack = parser.Stack(self.ctx, 'load_parent_resource', @@ -1851,12 +1846,17 @@ class StackTest(common.HeatTestCase): self.m.VerifyAll() def _get_stack_to_check(self, name): + tpl = {"HeatTemplateFormatVersion": "2012-12-12", + "Resources": { + "A": {"Type": "GenericResourceType"}, + "B": {"Type": "GenericResourceType"}}} + self.stack = parser.Stack(self.ctx, name, parser.Template(tpl), + status_reason=name) + self.stack.store() + def _mock_check(res): res.handle_check = mock.Mock() - if hasattr(res, 'nested'): - [_mock_check(r) for r in res.nested().resources.values()] - self._setup_nested(name) [_mock_check(res) for res in self.stack.resources.values()] return self.stack @@ -1870,18 +1870,6 @@ class StackTest(common.HeatTestCase): for res in stack.resources.values()] self.assertNotIn('not fully supported', stack.status_reason) - def test_check_nested_stack(self): - def _mock_check(res): - res.handle_check = mock.Mock() - - self._setup_nested('check-nested-stack') - nested = self.stack['A'].nested() - [_mock_check(res) for res in nested.resources.values()] - self.stack.check() - - [self.assertTrue(res.handle_check.called) - for res in nested.resources.values()] - def test_check_not_supported(self): stack = self._get_stack_to_check('check-not-supported') del stack['B'].handle_check diff --git a/heat/tests/test_stack_resource.py b/heat/tests/test_stack_resource.py index a1a04677c1..77bf08a2d5 100644 --- a/heat/tests/test_stack_resource.py +++ b/heat/tests/test_stack_resource.py @@ -189,6 +189,14 @@ class StackResourceTest(common.HeatTestCase): preview = self.parent_resource.preview() self.assertIsInstance(preview, stack_resource.StackResource) + def test_nested_stack_abandon(self): + nest = mock.MagicMock() + self.parent_resource.nested = nest + nest.return_value.prepare_abandon.return_value = {'X': 'Y'} + ret = self.parent_resource.prepare_abandon() + nest.return_value.prepare_abandon.assert_called_once_with() + self.assertEqual({'X': 'Y'}, ret) + def test_implementation_signature(self): self.parent_resource.child_template = mock.Mock( return_value=self.simple_template) diff --git a/heat_integrationtests/functional/test_template_resource.py b/heat_integrationtests/functional/test_template_resource.py index 6b3fabb247..3b723f8c0e 100644 --- a/heat_integrationtests/functional/test_template_resource.py +++ b/heat_integrationtests/functional/test_template_resource.py @@ -420,3 +420,56 @@ Outputs: self.assert_resource_is_a_stack(stack_identifier, 'the_nested') stack = self.client.stacks.get(stack_identifier) self.assertEqual('goopie', self._stack_output(stack, 'value')) + + +class TemplateResourceCheckTest(test.HeatIntegrationTest): + """Prove that we can do template resource check.""" + + main_template = ''' +HeatTemplateFormatVersion: '2012-12-12' +Resources: + the_nested: + Type: the.yaml + Properties: + one: my_name +Outputs: + identifier: + Value: {Ref: the_nested} + value: + Value: {'Fn::GetAtt': [the_nested, the_str]} +''' + + nested_templ = ''' +HeatTemplateFormatVersion: '2012-12-12' +Parameters: + one: + Default: foo + Type: String +Resources: + RealRandom: + Type: OS::Heat::RandomString + Properties: + salt: {Ref: one} +Outputs: + the_str: + Value: {'Fn::GetAtt': [RealRandom, value]} +''' + + def setUp(self): + super(TemplateResourceCheckTest, self).setUp() + self.client = self.orchestration_client + + def test_check(self): + stack_name = self._stack_rand_name() + self.client.stacks.create( + stack_name=stack_name, + template=self.main_template, + files={'the.yaml': self.nested_templ}, + disable_rollback=True, + ) + stack = self.client.stacks.get(stack_name) + stack_identifier = '%s/%s' % (stack_name, stack.id) + self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE') + + self.client.actions.check(stack_id=stack_identifier) + self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')