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
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)
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 {
# under the License.
+import copy
+
from heat.common import exception
from heat.common import template_format
from heat.common import urlfetch
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')
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)
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')
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)