#
#    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_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'])

    def test_delete_stack_when_update_failed_twice(self):
        """Test when stack update failed twice and delete the stack.

        Test checks the following scenario:
        1. Create stack
        2. Update stack (failed)
        3. Update stack (failed)
        4. Delete stack
        The test checks the behavior of backup stack when update is failed.
        If some resources were not backed up correctly then test will fail.
        """
        tmpl_create = {
            'heat_template_version': '2013-05-23',
            'resources': {
                'Ares': {'type': 'GenericResourceType'}
            }
        }
        # create a stack
        self.stack = stack.Stack(self.ctx, 'update_fail_test_stack',
                                 template.Template(tmpl_create),
                                 disable_rollback=True)
        self.stack.store()
        self.stack.create()
        self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
                         self.stack.state)

        tmpl_update = {
            'heat_template_version': '2013-05-23',
            'resources': {
                'Ares': {'type': 'GenericResourceType'},
                'Bres': {'type': 'GenericResourceType'},
                'Cres': {
                    'type': 'ResourceWithPropsType',
                    'properties': {
                        'Foo': {'get_resource': 'Bres'},
                    }
                }
            }
        }

        mock_create = self.patchobject(
            generic_rsrc.ResourceWithProps,
            'handle_create',
            side_effect=[Exception, Exception])

        updated_stack_first = stack.Stack(self.ctx,
                                          'update_fail_test_stack',
                                          template.Template(tmpl_update))
        self.stack.update(updated_stack_first)
        self.stack.resources['Cres'].resource_id_set('c_res')
        self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
                         self.stack.state)

        # try to update the stack again
        updated_stack_second = stack.Stack(self.ctx,
                                           'update_fail_test_stack',
                                           template.Template(tmpl_update))
        self.stack.update(updated_stack_second)
        self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
                         self.stack.state)

        self.assertEqual(mock_create.call_count, 2)

        # delete the failed stack
        self.stack.delete()
        self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
                         self.stack.state)

    def test_backup_stack_synchronized_after_update(self):
        """Test when backup stack updated correctly during stack update.

        Test checks the following scenario:
        1. Create stack
        2. Update stack (failed - so the backup should not be deleted)
        3. Update stack (failed - so the backup from step 2 should be updated)
        The test checks that backup stack is synchronized with the main stack.
        """
        # create a stack
        tmpl_create = {
            'heat_template_version': '2013-05-23',
            'resources': {
                'Ares': {'type': 'GenericResourceType'}
            }
        }
        self.stack = stack.Stack(self.ctx, 'test_update_stack_backup',
                                 template.Template(tmpl_create),
                                 disable_rollback=True)
        self.stack.store()
        self.stack.create()
        self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
                         self.stack.state)

        # try to update a stack with a new resource that should be backed up
        tmpl_update = {
            'heat_template_version': '2013-05-23',
            'resources': {
                'Ares': {'type': 'GenericResourceType'},
                'Bres': {
                    'type': 'ResWithComplexPropsAndAttrs',
                    'properties': {
                        'an_int': 0,
                    }
                },
                'Cres': {
                    'type': 'ResourceWithPropsType',
                    'properties': {
                        'Foo': {'get_resource': 'Bres'},
                    }
                }
            }
        }

        self.patchobject(generic_rsrc.ResourceWithProps,
                         'handle_create',
                         side_effect=[Exception, Exception])

        stack_with_new_resource = stack.Stack(
            self.ctx,
            'test_update_stack_backup',
            template.Template(tmpl_update))
        self.stack.update(stack_with_new_resource)
        self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
                         self.stack.state)
        # assert that backup stack has been updated correctly
        self.assertIn('Bres', self.stack._backup_stack())

        # update the stack with resource that updated in-place
        tmpl_update['resources']['Bres']['properties']['an_int'] = 1
        updated_stack_second = stack.Stack(self.ctx,
                                           'test_update_stack_backup',
                                           template.Template(tmpl_update))
        self.stack.update(updated_stack_second)
        self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
                         self.stack.state)
        # assert that resource in backup stack also has been updated
        backup = self.stack._backup_stack()
        self.assertEqual(1, backup['Bres'].properties['an_int'])
