From d42f863dc54ecad2f99800be42bf42312dbff0d2 Mon Sep 17 00:00:00 2001 From: rabi Date: Wed, 9 May 2018 10:26:03 +0530 Subject: [PATCH] Keep old files in file map for rolling update We would need old resource definitions when doing rolling update of certain group resources like RG/ASG. Therefore update the file map with the files from old template. Change-Id: I8f880e5b23c25159ecab1c63b594329d8df33973 Closes-Bug: #1765454 Task: #17360 Story: #1765454 --- heat/common/grouputils.py | 18 +++++++++++ heat/engine/resource.py | 6 ++-- .../openstack/heat/autoscaling_group.py | 6 ++++ .../openstack/heat/instance_group.py | 7 +++++ .../openstack/heat/resource_group.py | 8 +++++ heat/engine/resources/stack_resource.py | 7 ++++- .../functional/test_resource_group.py | 30 +++++++++++++++++++ 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/heat/common/grouputils.py b/heat/common/grouputils.py index c67e794c12..d62a20c827 100644 --- a/heat/common/grouputils.py +++ b/heat/common/grouputils.py @@ -197,3 +197,21 @@ def get_member_definitions(group, include_failed=False): return [(name, definitions[name]) for name in inspector.member_names(include_failed=include_failed) if name in definitions] + + +def get_child_template_files(context, stack, + is_rolling_update, + old_template_id): + """Return a merged map of old and new template files. + + For rolling update files for old and new defintions are required as the + nested stack is updated in batches of scaled units. + """ + if not stack.convergence: + old_template_id = stack.t.id + + if is_rolling_update and old_template_id: + prev_files = template.Template.load(context, old_template_id).files + prev_files.update(dict(stack.t.files)) + return prev_files + return stack.t.files diff --git a/heat/engine/resource.py b/heat/engine/resource.py index f526a820e3..b43a018421 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -247,6 +247,7 @@ class Resource(status.ResourceStatus): self.replaces = None self.replaced_by = None self.current_template_id = None + self.old_template_id = None self.root_stack_id = None self._calling_engine_id = None self._atomic_key = None @@ -1659,7 +1660,7 @@ class Resource(status.ResourceStatus): after_props.validate() self.properties = before_props tmpl_diff = self.update_template_diff(after.freeze(), before) - old_template_id = self.current_template_id + self.old_template_id = self.current_template_id try: if tmpl_diff and self.needs_replace_with_tmpl_diff(tmpl_diff): @@ -1676,7 +1677,8 @@ class Resource(status.ResourceStatus): prop_diff]) except UpdateReplace: with excutils.save_and_reraise_exception(): - self.current_template_id = old_template_id + self.current_template_id = self.old_template_id + self.old_template_id = None self._prepare_update_replace(action) self.t = after diff --git a/heat/engine/resources/openstack/heat/autoscaling_group.py b/heat/engine/resources/openstack/heat/autoscaling_group.py index 70f30aa23c..ed48bebf3f 100644 --- a/heat/engine/resources/openstack/heat/autoscaling_group.py +++ b/heat/engine/resources/openstack/heat/autoscaling_group.py @@ -186,6 +186,12 @@ class AutoScalingResourceGroup(aws_asg.AutoScalingGroup): resource_def)) return rsrc_defn.ResourceDefinition(None, **defn_data) + def child_template_files(self, child_env): + is_update = self.action == self.UPDATE + return grouputils.get_child_template_files(self.context, self.stack, + is_update, + self.old_template_id) + def _try_rolling_update(self, prop_diff): if self.RESOURCE in prop_diff: policy = self.properties[self.ROLLING_UPDATES] diff --git a/heat/engine/resources/openstack/heat/instance_group.py b/heat/engine/resources/openstack/heat/instance_group.py index a09ece2042..103737d440 100644 --- a/heat/engine/resources/openstack/heat/instance_group.py +++ b/heat/engine/resources/openstack/heat/instance_group.py @@ -478,6 +478,13 @@ class InstanceGroup(stack_resource.StackResource): num_instances = int(self.properties[self.SIZE]) return self._create_template(num_instances) + def child_template_files(self, child_env): + is_rolling_update = (self.action == self.UPDATE and + self.update_policy[self.ROLLING_UPDATE]) + return grouputils.get_child_template_files(self.context, self.stack, + is_rolling_update, + self.old_template_id) + def child_params(self): """Return the environment for the nested stack.""" return { diff --git a/heat/engine/resources/openstack/heat/resource_group.py b/heat/engine/resources/openstack/heat/resource_group.py index 24f37e7d42..7fe7582b20 100644 --- a/heat/engine/resources/openstack/heat/resource_group.py +++ b/heat/engine/resources/openstack/heat/resource_group.py @@ -668,6 +668,14 @@ class ResourceGroup(stack_resource.StackResource): self._add_output_defns_to_template(tmpl, [k for k, d in definitions]) return tmpl + def child_template_files(self, child_env): + is_rolling_update = (self.action == self.UPDATE + and self.update_policy[self.ROLLING_UPDATE]) + return grouputils.get_child_template_files(self.context, + self.stack, + is_rolling_update, + self.old_template_id) + def _assemble_for_rolling_update(self, total_capacity, max_updates, include_all=False, template_version=('heat_template_version', diff --git a/heat/engine/resources/stack_resource.py b/heat/engine/resources/stack_resource.py index a29ab8ecaf..c678878a95 100644 --- a/heat/engine/resources/stack_resource.py +++ b/heat/engine/resources/stack_resource.py @@ -222,7 +222,12 @@ class StackResource(resource.Resource): if isinstance(parsed_child_template, template.Template): parsed_child_template = parsed_child_template.t return template.Template(parsed_child_template, - files=self.stack.t.files, env=child_env) + files=self.child_template_files(child_env), + env=child_env) + + def child_template_files(self, child_env): + """Default implementation to get the files map for child template.""" + return self.stack.t.files def _parse_nested_stack(self, stack_name, child_template, child_params, timeout_mins=None, diff --git a/heat_integrationtests/functional/test_resource_group.py b/heat_integrationtests/functional/test_resource_group.py index 58802638f1..95ae7799f3 100644 --- a/heat_integrationtests/functional/test_resource_group.py +++ b/heat_integrationtests/functional/test_resource_group.py @@ -612,6 +612,36 @@ resources: created=10, deleted=10) + def test_resource_group_update_replace_template_changed(self): + """Test rolling update(replace)with child template path changed. + + Simple rolling update replace with child template path changed. + """ + + nested_templ = ''' +heat_template_version: "2013-05-23" +resources: + oops: + type: OS::Heat::TestResource +''' + + create_template = yaml.safe_load(copy.deepcopy(self.template)) + grp = create_template['resources']['random_group'] + grp['properties']['resource_def'] = {'type': '/opt/provider.yaml'} + files = {'/opt/provider.yaml': nested_templ} + + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '1' + policy['max_batch_size'] = '3' + stack_identifier = self.stack_create(template=create_template, + files=files) + update_template = create_template.copy() + grp = update_template['resources']['random_group'] + grp['properties']['resource_def'] = {'type': '/opt1/provider.yaml'} + files = {'/opt1/provider.yaml': nested_templ} + + self.update_stack(stack_identifier, update_template, files=files) + def test_resource_group_update_scaledown(self): """Test rolling update with scaledown.