From ff47d9d6f327f92cedd2a30094bf5d50a83b68f4 Mon Sep 17 00:00:00 2001 From: Thomas Herve Date: Thu, 20 Apr 2017 15:11:08 +0200 Subject: [PATCH] Copy template version when update fails When an update fails, we may have copy some chunk of resources or parameters to the new template. If the version was updated and the new resources require the version, this can lead to a state where the stack is in an usable state. This synchronizes the version when a failure happens. Change-Id: I2faf8f3541fc800ea61c417e5575f4a56a83665b Closes-Bug: #1620696 (cherry picked from commit 45fde101979d9d3f446cd937e5cc4c47539cc5bc) --- heat/engine/stack.py | 5 ++ .../functional/test_create_update.py | 48 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/heat/engine/stack.py b/heat/engine/stack.py index 8c70ff6fa8..c6365d9e91 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -1520,9 +1520,14 @@ class Stack(collections.Mapping): # and new stack resources, we should have user params of both. existing_params.load(newstack.t.env.user_env_as_dict()) self.t.env = existing_params + # Update the template version, in case new things were used + self.t.t[newstack.t.version[0]] = max( + newstack.t.version[1], self.t.version[1]) self.t.merge_snippets(newstack.t) self.t.store(self.context) backup_stack.t.env = existing_params + backup_stack.t.t[newstack.t.version[0]] = max( + newstack.t.version[1], self.t.version[1]) backup_stack.t.merge_snippets(newstack.t) backup_stack.t.store(self.context) self.store() diff --git a/heat_integrationtests/functional/test_create_update.py b/heat_integrationtests/functional/test_create_update.py index 446c9a36ac..d8f8c6fc4d 100644 --- a/heat_integrationtests/functional/test_create_update.py +++ b/heat_integrationtests/functional/test_create_update.py @@ -631,3 +631,51 @@ resources: self.assertEqual({'test1': 'OS::Heat::TestResource', 'test2': 'My::TestResource'}, self.list_resources(stack_identifier)) + + def test_stack_update_with_new_version(self): + """Update handles new template version in failure. + + If a stack update fails while changing the template version, update is + able to handle the new version fine. + """ + stack_identifier = self.stack_create( + template=test_template_one_resource) + + # Update with a new function and make the update fails + template = _change_rsrc_properties(test_template_two_resource, + ['test1'], {'fail': True}) + + template['heat_template_version'] = '2015-10-15' + template['resources']['test2']['properties']['value'] = { + 'list_join': [',', ['a'], ['b']]} + self.update_stack(stack_identifier, + template=template, + expected_status='UPDATE_FAILED') + + template = _change_rsrc_properties(template, + ['test2'], {'value': 'Test2'}) + self.update_stack(stack_identifier, + template=template, + expected_status='UPDATE_FAILED') + self._stack_delete(stack_identifier) + + def test_stack_update_with_old_version(self): + """Update handles old template version in failure. + + If a stack update fails while changing the template version, update is + able to handle the old version fine. + """ + template = _change_rsrc_properties( + test_template_one_resource, + ['test1'], {'value': {'list_join': [',', ['a'], ['b']]}}) + template['heat_template_version'] = '2015-10-15' + stack_identifier = self.stack_create( + template=template) + + # Update with a new function and make the update fails + template = _change_rsrc_properties(test_template_one_resource, + ['test1'], {'fail': True}) + self.update_stack(stack_identifier, + template=template, + expected_status='UPDATE_FAILED') + self._stack_delete(stack_identifier)