From: Liang Chen Date: Thu, 22 Aug 2013 05:42:18 +0000 (+0800) Subject: Allow in-place update of nested stack X-Git-Tag: 2014.1~134^2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=24f185a21d75a5345057093db19d550fa87278ea;p=openstack-build%2Fheat-build.git Allow in-place update of nested stack Not raising UpdateReplace exception anymore for nested stack update. Instead, propagating the update request to the nested stack, so that the nested stack itself will decide the best way to handle the update request for every of its resources. blueprint nested-stack-updates Change-Id: Ibfe7bee71fe57a601b93fa05a4a0c1cda972290e --- diff --git a/heat/engine/resources/stack.py b/heat/engine/resources/stack.py index 4960f49b..dc031ab6 100644 --- a/heat/engine/resources/stack.py +++ b/heat/engine/resources/stack.py @@ -16,6 +16,7 @@ from heat.common import exception from heat.common import template_format from heat.common import urlfetch +from heat.engine.properties import Properties from heat.engine import stack_resource from heat.openstack.common import log as logging @@ -38,6 +39,10 @@ class NestedStack(stack_resource.StackResource): PROP_TIMEOUT_MINS: {'Type': 'Number'}, PROP_PARAMETERS: {'Type': 'Map'}} + update_allowed_keys = ('Properties',) + update_allowed_properties = (PROP_TEMPLATE_URL, PROP_TIMEOUT_MINS, + PROP_PARAMETERS) + def handle_create(self): template_data = urlfetch.get(self.properties[PROP_TEMPLATE_URL]) template = template_format.parse(template_data) @@ -58,6 +63,20 @@ class NestedStack(stack_resource.StackResource): def FnGetRefId(self): return self.nested().identifier().arn() + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + # Nested stack template may be changed even if the prop_diff is empty. + self.properties = Properties(self.properties_schema, + json_snippet.get('Properties', {}), + self.stack.resolve_runtime_data, + self.name) + + template_data = urlfetch.get(self.properties[PROP_TEMPLATE_URL]) + template = template_format.parse(template_data) + + self.update_with_template(template, + self.properties[PROP_PARAMETERS], + self.properties[PROP_TIMEOUT_MINS]) + def resource_mapping(): return { diff --git a/heat/tests/test_nested_stack.py b/heat/tests/test_nested_stack.py index 3346ca2b..68a9bcfa 100644 --- a/heat/tests/test_nested_stack.py +++ b/heat/tests/test_nested_stack.py @@ -13,6 +13,8 @@ # under the License. +import copy + from heat.common import exception from heat.common import template_format from heat.common import urlfetch @@ -47,6 +49,16 @@ Outputs: Value: bar ''' + update_template = ''' +HeatTemplateFormatVersion: '2012-12-12' +Parameters: + KeyName: + Type: String +Outputs: + Bar: + Value: foo +''' + def setUp(self): super(NestedStackTest, self).setUp() self.m.StubOutWithMock(urlfetch, 'get') @@ -67,9 +79,9 @@ Outputs: stack.store() return stack - def test_nested_stack(self): - urlfetch.get('https://localhost/the.template').AndReturn( - self.nested_template) + def test_nested_stack_create(self): + urlfetch.get('https://localhost/the.template').MultipleTimes().\ + AndReturn(self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) @@ -80,9 +92,6 @@ Outputs: rsrc.physical_resource_name()) self.assertTrue(rsrc.FnGetRefId().startswith(arn_prefix)) - self.assertRaises(resource.UpdateReplace, - rsrc.handle_update, {}, {}, {}) - self.assertEqual('bar', rsrc.FnGetAtt('Outputs.Foo')) self.assertRaises( exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Foo') @@ -96,6 +105,44 @@ Outputs: self.m.VerifyAll() + def test_nested_stack_update(self): + urlfetch.get('https://localhost/the.template').MultipleTimes().\ + AndReturn(self.nested_template) + urlfetch.get('https://localhost/new.template').MultipleTimes().\ + AndReturn(self.update_template) + + self.m.ReplayAll() + + stack = self.create_stack(self.test_template) + rsrc = stack['the_nested'] + + original_nested_id = rsrc.resource_id + t = template_format.parse(self.test_template) + new_res = copy.deepcopy(t['Resources']['the_nested']) + new_res['Properties']['TemplateURL'] = 'https://localhost/new.template' + prop_diff = {'TemplateURL': 'https://localhost/new.template'} + rsrc.handle_update(new_res, {}, prop_diff) + + # Expect the physical resource name staying the same after update, + # so that the nested was actually updated instead of replaced. + self.assertEqual(original_nested_id, rsrc.resource_id) + db_nested = db_api.stack_get(stack.context, + rsrc.resource_id) + # Owner_id should be preserved during the update process. + self.assertEqual(stack.id, db_nested.owner_id) + + self.assertEqual('foo', rsrc.FnGetAtt('Outputs.Bar')) + self.assertRaises( + exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Foo') + self.assertRaises( + exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Outputs.Foo') + self.assertRaises( + exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Bar') + + rsrc.delete() + + self.m.VerifyAll() + def test_nested_stack_suspend_resume(self): urlfetch.get('https://localhost/the.template').AndReturn( self.nested_template)