heat/heat/tests/test_stack_update.py

1538 lines
69 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 copy
import mock
from heat.common import template_format
from heat.engine import environment
from heat.engine import resource
from heat.engine import scheduler
from heat.engine import stack
from heat.engine import template
from heat.tests import common
from heat.tests import generic_resource as generic_rsrc
from heat.tests import utils
empty_template = template_format.parse('''{
"HeatTemplateFormatVersion" : "2012-12-12",
}''')
class StackUpdateTest(common.HeatTestCase):
def setUp(self):
super(StackUpdateTest, self).setUp()
self.tmpl = template.Template(copy.deepcopy(empty_template))
self.ctx = utils.dummy_context()
resource._register_class('GenericResourceType',
generic_rsrc.GenericResource)
resource._register_class('ResourceWithPropsType',
generic_rsrc.ResourceWithProps)
resource._register_class('ResWithComplexPropsAndAttrs',
generic_rsrc.ResWithComplexPropsAndAttrs)
def test_update_add(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'},
'BResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertIn('BResource', self.stack)
def test_update_remove(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'},
'BResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertNotIn('BResource', self.stack)
def test_update_different_type(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('GenericResourceType',
self.stack['AResource'].type())
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('ResourceWithPropsType',
self.stack['AResource'].type())
def test_update_description(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'ATemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'BTemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('BTemplate',
self.stack.t[self.stack.t.DESCRIPTION])
def test_update_timeout(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'ATemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl), timeout_mins=60)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'ATemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2), timeout_mins=30)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(30, self.stack.timeout_mins)
def test_update_disable_rollback(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'ATemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'ATemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=True)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(True, self.stack.disable_rollback)
def test_update_tags(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'ATemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
tags=['tag1', 'tag2'])
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(['tag1', 'tag2'], self.stack.tags)
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl),
tags=['tag3', 'tag4'])
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(['tag3', 'tag4'], self.stack.tags)
def test_update_tags_remove_all(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Description': 'ATemplate',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
tags=['tag1', 'tag2'])
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(['tag1', 'tag2'], self.stack.tags)
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(None, self.stack.tags)
def test_update_modify_ok_replace(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('xyz', self.stack['AResource'].properties['Foo'])
loaded_stack = stack.Stack.load(self.ctx, self.stack.id)
stored_props = loaded_stack['AResource']._stored_properties_data
self.assertEqual({'Foo': 'xyz'}, stored_props)
def test_update_modify_ok_replace_int(self):
# create
# ========
tmpl = {'heat_template_version': '2013-05-23',
'resources': {'AResource': {
'type': 'ResWithComplexPropsAndAttrs',
'properties': {'an_int': 1}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
stack_id = self.stack.id
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
value1 = 2
prop_diff1 = {'an_int': value1}
value2 = 1
prop_diff2 = {'an_int': value2}
mock_upd = self.patchobject(generic_rsrc.ResWithComplexPropsAndAttrs,
'handle_update')
# update 1
# ==========
self.stack = stack.Stack.load(self.ctx, stack_id=stack_id)
tmpl2 = {'heat_template_version': '2013-05-23',
'resources': {'AResource': {
'type': 'ResWithComplexPropsAndAttrs',
'properties': {'an_int': value1}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
mock_upd.assert_called_once_with(mock.ANY, mock.ANY, prop_diff1)
# update 2
# ==========
# reload the previous stack
self.stack = stack.Stack.load(self.ctx, stack_id=stack_id)
tmpl3 = {'heat_template_version': '2013-05-23',
'resources': {'AResource': {
'type': 'ResWithComplexPropsAndAttrs',
'properties': {'an_int': value2}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl3))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
mock_upd.assert_called_with(mock.ANY, mock.ANY, prop_diff2)
def test_update_modify_param_ok_replace(self):
tmpl = {
'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {
'foo': {'Type': 'String'}
},
'Resources': {
'AResource': {
'Type': 'ResourceWithPropsType',
'Properties': {'Foo': {'Ref': 'foo'}}
}
}
}
self.stack = stack.Stack(
self.ctx, 'update_test_stack',
template.Template(
tmpl, env=environment.Environment({'foo': 'abc'})))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
env2 = environment.Environment({'foo': 'xyz'})
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl, env=env2))
def check_and_raise(*args):
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
raise resource.UpdateReplace
mock_upd = self.patchobject(generic_rsrc.ResourceWithProps,
'update_template_diff',
side_effect=check_and_raise)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('xyz', self.stack['AResource'].properties['Foo'])
mock_upd.assert_called_once_with(
{'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}},
{'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}})
def test_update_modify_update_failed(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
res = self.stack['AResource']
res.update_allowed_properties = ('Foo',)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
mock_upd = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_update',
side_effect=Exception("Foo"))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
mock_upd.assert_called_once_with(
tmpl2['Resources']['AResource'],
{'Properties': {'Foo': 'xyz'}},
{'Foo': 'xyz'})
def test_update_modify_replace_failed_delete(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
# make the update fail deleting the existing resource
mock_del = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_delete',
side_effect=Exception)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
mock_del.assert_called_once_with()
# Unset here so destroy() is not stubbed for stack.delete cleanup
self.m.UnsetStubs()
def test_update_modify_replace_failed_create(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
# patch in a dummy handle_create making the replace fail creating
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create',
side_effect=Exception)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
mock_create.assert_called_once_with()
def test_update_modify_replace_failed_create_and_delete_1(self):
resource._register_class('ResourceWithResourceIDType',
generic_rsrc.ResourceWithResourceID)
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'a_res'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'b_res'},
'DependsOn': 'AResource'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'xyz'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'b_res'},
'DependsOn': 'AResource'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
# patch in a dummy handle_create making the replace fail creating
mock_create = self.patchobject(generic_rsrc.ResourceWithResourceID,
'handle_create', side_effect=Exception)
mock_id = self.patchobject(generic_rsrc.ResourceWithResourceID,
'mox_resource_id',
return_value=None)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
self.stack.delete()
self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
self.stack.state)
mock_create.assert_called_once_with()
# Three calls in list: first is an attempt to delete backup_stack
# where create(xyz) has failed, so no resource_id passed; the 2nd
# and the 3rd calls are invoked by resource BResource deletion
# followed by AResource deletion.
mock_id.assert_has_calls(
[mock.call(None), mock.call('b_res'), mock.call('a_res')])
def test_update_modify_replace_failed_create_and_delete_2(self):
resource._register_class('ResourceWithResourceIDType',
generic_rsrc.ResourceWithResourceID)
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'a_res'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'b_res'},
'DependsOn': 'AResource'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'c_res'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'xyz'},
'DependsOn': 'AResource'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
# patch in a dummy handle_create making the replace fail creating
mock_create = self.patchobject(generic_rsrc.ResourceWithResourceID,
'handle_create',
side_effect=[None, Exception])
mock_id = self.patchobject(generic_rsrc.ResourceWithResourceID,
'mox_resource_id', return_value=None)
self.stack.update(updated_stack)
# set resource_id for AResource because handle_create() is overwritten
# by the mock.
self.stack.resources['AResource'].resource_id_set('c_res')
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
self.stack.delete()
self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(2, mock_create.call_count)
# Four calls in chain: 1st is an attempt to delete backup_stack where
# the create(xyz) failed with no resource_id, the other three are
# derived from resource dependencies.
mock_id.assert_has_calls(
[mock.call(None), mock.call('c_res'), mock.call('b_res'),
mock.call('a_res')])
def test_update_modify_replace_create_in_progress_and_delete_1(self):
resource._register_class('ResourceWithResourceIDType',
generic_rsrc.ResourceWithResourceID)
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'a_res'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'b_res'},
'DependsOn': 'AResource'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'xyz'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'b_res'},
'DependsOn': 'AResource'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
# patch in a dummy handle_create making the replace fail creating
mock_create = self.patchobject(generic_rsrc.ResourceWithResourceID,
'handle_create', side_effect=Exception)
mock_id = self.patchobject(generic_rsrc.ResourceWithResourceID,
'mox_resource_id', return_value=None)
self.stack.update(updated_stack)
# Override stack status and resources status for emulating
# IN_PROGRESS situation
self.stack.state_set(
stack.Stack.UPDATE, stack.Stack.IN_PROGRESS, None)
self.stack.resources['AResource'].state_set(
resource.Resource.CREATE, resource.Resource.IN_PROGRESS, None)
self.stack.delete()
self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
self.stack.state)
mock_create.assert_called_once_with()
# Three calls in chain: 1st is an attempt to delete backup_stack where
# the create(xyz) failed with no resource_id, the other two ordered by
# resource dependencies.
mock_id.assert_has_calls(
[mock.call(None), mock.call('b_res'), mock.call('a_res')])
def test_update_modify_replace_create_in_progress_and_delete_2(self):
resource._register_class('ResourceWithResourceIDType',
generic_rsrc.ResourceWithResourceID)
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'a_res'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'b_res'},
'DependsOn': 'AResource'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'c_res'}},
'BResource': {'Type':
'ResourceWithResourceIDType',
'Properties': {'ID': 'xyz'},
'DependsOn': 'AResource'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
# patch in a dummy handle_create making the replace fail creating
mock_create = self.patchobject(generic_rsrc.ResourceWithResourceID,
'handle_create',
side_effect=[None, Exception])
mock_id = self.patchobject(generic_rsrc.ResourceWithResourceID,
'mox_resource_id', return_value=None)
self.stack.update(updated_stack)
# set resource_id for AResource because handle_create() is mocked
self.stack.resources['AResource'].resource_id_set('c_res')
# Override stack status and resources status for emulating
# IN_PROGRESS situation
self.stack.state_set(
stack.Stack.UPDATE, stack.Stack.IN_PROGRESS, None)
self.stack.resources['BResource'].state_set(
resource.Resource.CREATE, resource.Resource.IN_PROGRESS, None)
self.stack.delete()
self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(2, mock_create.call_count)
# Four calls in chain: 1st is an attempt to delete backup_stack where
# the create(xyz) failed with no resource_id, the other three are
# derived from resource dependencies.
mock_id.assert_has_calls(
[mock.call(None), mock.call('c_res'), mock.call('b_res'),
mock.call('a_res')])
def test_update_add_signal(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'},
'BResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
updater = scheduler.TaskRunner(self.stack.update_task, updated_stack)
updater.start()
while 'BResource' not in self.stack:
self.assertFalse(updater.step())
self.assertEqual((stack.Stack.UPDATE, stack.Stack.IN_PROGRESS),
self.stack.state)
# Reload the stack from the DB and prove that it contains the new
# resource already
re_stack = stack.Stack.load(utils.dummy_context(), self.stack.id)
self.assertIn('BResource', re_stack)
updater.run_to_completion()
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertIn('BResource', self.stack)
def test_update_add_failed_create(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'},
'BResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
# patch in a dummy handle_create making BResource fail creating
mock_create = self.patchobject(generic_rsrc.GenericResource,
'handle_create', side_effect=Exception)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
self.assertIn('BResource', self.stack)
# Reload the stack from the DB and prove that it contains the failed
# resource (to ensure it will be deleted on stack delete)
re_stack = stack.Stack.load(utils.dummy_context(), self.stack.id)
self.assertIn('BResource', re_stack)
mock_create.assert_called_once_with()
def test_update_add_failed_create_rollback_failed(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'},
'BResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
# patch handle_create/delete making BResource fail creation/deletion
mock_create = self.patchobject(generic_rsrc.GenericResource,
'handle_create', side_effect=Exception)
mock_delete = self.patchobject(generic_rsrc.GenericResource,
'handle_delete', side_effect=Exception)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.FAILED),
self.stack.state)
self.assertIn('BResource', self.stack)
# Reload the stack from the DB and prove that it contains the failed
# resource (to ensure it will be deleted on stack delete)
re_stack = stack.Stack.load(utils.dummy_context(), self.stack.id)
self.assertIn('BResource', re_stack)
mock_create.assert_called_once_with()
mock_delete.assert_called_once_with()
def test_update_rollback(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
# patch in a dummy handle_create making the replace fail when creating
# the replacement rsrc
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create', side_effect=Exception)
with mock.patch.object(self.stack, 'state_set',
side_effect=self.stack.state_set) as mock_state:
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual(2, mock_state.call_count)
self.assertEqual(('UPDATE', 'IN_PROGRESS'),
mock_state.call_args_list[0][0][:2])
self.assertEqual(('ROLLBACK', 'IN_PROGRESS'),
mock_state.call_args_list[1][0][:2])
mock_create.assert_called_once_with()
def test_update_rollback_on_cancel_event(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}},
}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
evt_mock = mock.MagicMock()
evt_mock.ready.return_value = True
evt_mock.wait.return_value = 'cancel'
self.stack.update(updated_stack, event=evt_mock)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
evt_mock.ready.assert_called_once_with()
evt_mock.wait.assert_called_once_with()
def test_update_rollback_fail(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {'AParam': {'Type': 'String'}},
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
env1 = environment.Environment({'parameters': {'AParam': 'abc'}})
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl, env=env1),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {'BParam': {'Type': 'String'}},
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'xyz'}}}}
env2 = environment.Environment({'parameters': {'BParam': 'smelly'}})
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2, env=env2),
disable_rollback=False)
# patch in a dummy handle_create making the replace fail when creating
# the replacement rsrc, and again on the second call (rollback)
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create', side_effect=Exception)
mock_delete = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_delete', side_effect=Exception)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.FAILED),
self.stack.state)
mock_create.assert_called_once_with()
mock_delete.assert_called_once_with()
def test_update_rollback_add(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'},
'BResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
# patch in a dummy handle_create making the replace fail when creating
# the replacement rsrc, and succeed on the second call (rollback)
mock_create = self.patchobject(generic_rsrc.GenericResource,
'handle_create', side_effect=Exception)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.COMPLETE),
self.stack.state)
self.assertNotIn('BResource', self.stack)
mock_create.assert_called_once_with()
def test_update_rollback_remove(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'GenericResourceType'},
'BResource': {'Type': 'ResourceWithPropsType'}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
# patch in a dummy delete making the destroy fail
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create')
mock_delete = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_delete',
side_effect=[Exception, None])
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.COMPLETE),
self.stack.state)
self.assertIn('BResource', self.stack)
mock_create.assert_called_once_with()
self.assertEqual(2, mock_delete.call_count)
# Unset here so delete() is not stubbed for stack.delete cleanup
self.m.UnsetStubs()
def test_update_rollback_replace(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'foo'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'bar'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
# patch in a dummy delete making the destroy fail
mock_delete = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_delete',
side_effect=[Exception, None, None])
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(3, mock_delete.call_count)
# Unset here so delete() is not stubbed for stack.delete cleanup
self.m.UnsetStubs()
def test_update_replace_by_reference(self):
'''
assertion:
changes in dynamic attributes, due to other resources been updated
are not ignored and can cause dependent resources to be updated.
'''
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'smelly'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource',
self.stack['BResource'].properties['Foo'])
self.ref_id_called = False
def get_ref_id(*args):
ref_id = 'inst-007' if self.ref_id_called else 'AResource'
if self.ref_id_called is False:
self.ref_id_called = True
return ref_id
mock_id = self.patchobject(generic_rsrc.ResourceWithProps,
'FnGetRefId',
side_effect=get_ref_id)
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
self.assertEqual('inst-007', self.stack['BResource'].properties['Foo'])
# Note: mock_id is called 14 times!!!
mock_id.assert_called_with()
def test_update_with_new_resources_with_reference(self):
'''
assertion:
check, that during update with new resources which one has
reference on second, reference will be correct resolved.
'''
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'CResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}}}}
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'CResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}},
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'smelly'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['CResource'].properties['Foo'])
self.assertEqual(1, len(self.stack.resources))
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create', return_value=None)
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource',
self.stack['BResource'].properties['Foo'])
self.assertEqual(3, len(self.stack.resources))
mock_create.assert_called_with()
def test_update_by_reference_and_rollback_1(self):
'''
assertion:
check that rollback still works with dynamic metadata
this test fails the first instance
'''
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'abc'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'smelly'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource',
self.stack['BResource'].properties['Foo'])
mock_id = self.patchobject(generic_rsrc.ResourceWithProps,
'FnGetRefId', return_value='AResource')
# mock to make the replace fail when creating the replacement resource
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create', side_effect=Exception)
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
mock_id.assert_called_with()
mock_create.assert_called_once_with()
def test_update_by_reference_and_rollback_2(self):
'''
assertion:
check that rollback still works with dynamic metadata
this test fails the second instance
'''
class ResourceTypeA(generic_rsrc.ResourceWithProps):
count = 0
def handle_create(self):
ResourceTypeA.count += 1
self.resource_id_set('%s%d' % (self.name, self.count))
resource._register_class('ResourceTypeA', ResourceTypeA)
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': 'abc'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': 'smelly'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=False)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource1',
self.stack['BResource'].properties['Foo'])
# mock to make the replace fail when creating the second
# replacement resource
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create', side_effect=Exception)
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=False)
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.ROLLBACK, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource1',
self.stack['BResource'].properties['Foo'])
mock_create.assert_called_once_with()
def test_update_failure_recovery(self):
'''
assertion:
check that rollback still works with dynamic metadata
this test fails the second instance
'''
class ResourceTypeA(generic_rsrc.ResourceWithProps):
count = 0
def handle_create(self):
ResourceTypeA.count += 1
self.resource_id_set('%s%d' % (self.name, self.count))
def handle_delete(self):
return super(ResourceTypeA, self).handle_delete()
resource._register_class('ResourceTypeA', ResourceTypeA)
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': 'abc'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': 'smelly'}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {
'Foo': {'Ref': 'AResource'}}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource1',
self.stack['BResource'].properties['Foo'])
# mock to make the replace fail when creating the second
# replacement resource
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create',
side_effect=[Exception, None])
mock_delete = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_delete')
mock_delete_A = self.patchobject(ResourceTypeA, 'handle_delete')
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=True)
self.stack.update(updated_stack)
mock_create.assert_called_once_with()
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
self.stack = stack.Stack.load(self.ctx, self.stack.id)
updated_stack2 = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2),
disable_rollback=True)
self.stack.update(updated_stack2)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource2',
self.stack['BResource'].properties['Foo'])
self.assertEqual(2, mock_create.call_count)
self.assertEqual(2, mock_delete.call_count)
mock_delete_A.assert_called_once_with()
def test_update_failure_recovery_new_param(self):
'''
assertion:
check that rollback still works with dynamic metadata
this test fails the second instance
'''
class ResourceTypeA(generic_rsrc.ResourceWithProps):
count = 0
def handle_create(self):
ResourceTypeA.count += 1
self.resource_id_set('%s%d' % (self.name, self.count))
def handle_delete(self):
return super(ResourceTypeA, self).handle_delete()
resource._register_class('ResourceTypeA', ResourceTypeA)
tmpl = {
'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {
'abc-param': {'Type': 'String'}
},
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': {'Ref': 'abc-param'}}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': {'Ref': 'AResource'}}}
}
}
env1 = environment.Environment({'abc-param': 'abc'})
tmpl2 = {
'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {
'smelly-param': {'Type': 'String'}
},
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': {'Ref': 'smelly-param'}}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': {'Ref': 'AResource'}}}
}
}
env2 = environment.Environment({'smelly-param': 'smelly'})
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl, env=env1),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource1',
self.stack['BResource'].properties['Foo'])
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create',
side_effect=[Exception, None])
mock_delete = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_delete')
mock_delete_A = self.patchobject(ResourceTypeA, 'handle_delete')
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2, env=env2),
disable_rollback=True)
self.stack.update(updated_stack)
# creation was a failure
mock_create.assert_called_once_with()
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
self.stack = stack.Stack.load(self.ctx, self.stack.id)
updated_stack2 = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2, env=env2),
disable_rollback=True)
self.stack.update(updated_stack2)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.stack = stack.Stack.load(self.ctx, self.stack.id)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource2',
self.stack['BResource'].properties['Foo'])
self.assertEqual(2, mock_delete.call_count)
mock_delete_A.assert_called_once_with()
self.assertEqual(2, mock_create.call_count)
def test_update_replace_parameters(self):
'''
assertion:
changes in static environment parameters
are not ignored and can cause dependent resources to be updated.
'''
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {'AParam': {'Type': 'String'}},
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': {'Ref': 'AParam'}}}}}
env1 = environment.Environment({'parameters': {'AParam': 'abc'}})
env2 = environment.Environment({'parameters': {'AParam': 'smelly'}})
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl, env=env1))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl, env=env2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
def test_update_deletion_policy(self):
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': 'Bar'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
resource_id = self.stack['AResource'].id
new_tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithPropsType',
'DeletionPolicy': 'Retain',
'Properties': {'Foo': 'Bar'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(new_tmpl))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(resource_id, self.stack['AResource'].id)
def test_update_deletion_policy_no_handle_update(self):
class ResourceWithNoUpdate(resource.Resource):
properties_schema = {'Foo': {'Type': 'String'}}
resource._register_class('ResourceWithNoUpdate',
ResourceWithNoUpdate)
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithNoUpdate',
'Properties': {'Foo': 'Bar'}}}}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
resource_id = self.stack['AResource'].id
new_tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'AResource': {'Type': 'ResourceWithNoUpdate',
'DeletionPolicy': 'Retain',
'Properties': {'Foo': 'Bar'}}}}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(new_tmpl))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual(resource_id, self.stack['AResource'].id)
def test_update_template_format_version(self):
tmpl = {
'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {
'AParam': {'Type': 'String', 'Default': 'abc'}},
'Resources': {
'AResource': {
'Type': 'ResourceWithPropsType',
'Properties': {'Foo': {'Ref': 'AParam'}}
},
}
}
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl))
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
tmpl2 = {
'heat_template_version': '2013-05-23',
'parameters': {
'AParam': {'type': 'string', 'default': 'foo'}},
'resources': {
'AResource': {
'type': 'ResourceWithPropsType',
'properties': {'Foo': {'get_param': 'AParam'}}
}
}
}
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2))
self.stack.update(updated_stack)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('foo', self.stack['AResource'].properties['Foo'])