From 7a046e6ffd8daaa21202eeb691d12f2dbf9abfc1 Mon Sep 17 00:00:00 2001
From: Zane Bitter <zbitter@redhat.com>
Date: Mon, 8 Jan 2018 17:23:12 -0500
Subject: [PATCH] Include outputs in rolling update of ResourceGroup

The template generated by ResourceGroup should include outputs that
reference any needed resource attributes. However, previously it only did
so when rolling updates were not enabled by an update_policy.

Change-Id: Ice7a92fe8d2b14e2470d9089a8e191c8385994f1
Closes-Bug: #1741981
Partial-Bug: #1731349
Related-Bug: #1660831
---
 .../openstack/heat/resource_group.py          | 19 ++--
 .../openstack/heat/test_resource_group.py     | 86 +++++++++++++++++++
 2 files changed, 97 insertions(+), 8 deletions(-)

diff --git a/heat/engine/resources/openstack/heat/resource_group.py b/heat/engine/resources/openstack/heat/resource_group.py
index 4a4f18265e..d9a3a3dc4f 100644
--- a/heat/engine/resources/openstack/heat/resource_group.py
+++ b/heat/engine/resources/openstack/heat/resource_group.py
@@ -576,6 +576,13 @@ class ResourceGroup(stack_resource.StackResource):
             return [recurse(v) for v in val]
         return val
 
+    def _add_output_defns_to_template(self, tmpl, resource_names):
+        att_func = 'get_attr'
+        get_attr = functools.partial(tmpl.functions[att_func], None, att_func)
+        for odefn in self._nested_output_defns(resource_names,
+                                               get_attr):
+            tmpl.add_output(odefn)
+
     def _assemble_nested(self, names, include_all=False,
                          template_version=('heat_template_version',
                                            '2015-04-30')):
@@ -585,13 +592,7 @@ class ResourceGroup(stack_resource.StackResource):
                        for k in names]
         tmpl = scl_template.make_template(definitions,
                                           version=template_version)
-
-        att_func = 'get_attr'
-        get_attr = functools.partial(tmpl.functions[att_func], None, att_func)
-        for odefn in self._nested_output_defns([k for k, d in definitions],
-                                               get_attr):
-            tmpl.add_output(odefn)
-
+        self._add_output_defns_to_template(tmpl, [k for k, d in definitions])
         return tmpl
 
     def _assemble_for_rolling_update(self, total_capacity, max_updates,
@@ -633,8 +634,10 @@ class ResourceGroup(stack_resource.StackResource):
             max_updates,
             lambda: next(new_names),
             self.build_resource_definition)
-        return scl_template.make_template(definitions,
+        tmpl = scl_template.make_template(definitions,
                                           version=template_version)
+        self._add_output_defns_to_template(tmpl, names)
+        return tmpl
 
     def _try_rolling_update(self):
         if self.update_policy[self.ROLLING_UPDATE]:
diff --git a/heat/tests/openstack/heat/test_resource_group.py b/heat/tests/openstack/heat/test_resource_group.py
index c254897f80..043807d31d 100644
--- a/heat/tests/openstack/heat/test_resource_group.py
+++ b/heat/tests/openstack/heat/test_resource_group.py
@@ -179,6 +179,51 @@ class ResourceGroupTest(common.HeatTestCase):
 
         self.assertEqual(templ, resg._assemble_nested(['0', '1', '2']).t)
 
+    def test_assemble_nested_outputs(self):
+        """Tests nested stack creation based on props.
+
+        Tests that the nested stack that implements the group is created
+        appropriately based on properties.
+        """
+        stack = utils.parse_stack(template)
+        snip = stack.t.resource_definitions(stack)['group1']
+        resg = resource_group.ResourceGroup('test', snip, stack)
+        templ = {
+            "heat_template_version": "2015-04-30",
+            "resources": {
+                "0": {
+                    "type": "OverwrittenFnGetRefIdType",
+                    "properties": {
+                        "Foo": "Bar"
+                    }
+                },
+                "1": {
+                    "type": "OverwrittenFnGetRefIdType",
+                    "properties": {
+                        "Foo": "Bar"
+                    }
+                },
+                "2": {
+                    "type": "OverwrittenFnGetRefIdType",
+                    "properties": {
+                        "Foo": "Bar"
+                    }
+                }
+            },
+            "outputs": {
+                "foo": {
+                    "value": [
+                        {"get_attr": ["0", "foo"]},
+                        {"get_attr": ["1", "foo"]},
+                        {"get_attr": ["2", "foo"]},
+                    ]
+                }
+            }
+        }
+
+        resg.referenced_attrs = mock.Mock(return_value=["foo"])
+        self.assertEqual(templ, resg._assemble_nested(['0', '1', '2']).t)
+
     def test_assemble_nested_include(self):
         templ = copy.deepcopy(template)
         res_def = templ["resources"]["group1"]["properties"]['resource_def']
@@ -269,6 +314,47 @@ class ResourceGroupTest(common.HeatTestCase):
         resg.build_resource_definition = mock.Mock(return_value=resource_def)
         self.assertEqual(expect, resg._assemble_for_rolling_update(2, 1).t)
 
+    def test_assemble_nested_rolling_update_outputs(self):
+        expect = {
+            "heat_template_version": "2015-04-30",
+            "resources": {
+                "0": {
+                    "type": "OverwrittenFnGetRefIdType",
+                    "properties": {
+                        "foo": "bar"
+                    }
+                },
+                "1": {
+                    "type": "OverwrittenFnGetRefIdType",
+                    "properties": {
+                        "foo": "baz"
+                    }
+                }
+            },
+            "outputs": {
+                "bar": {
+                    "value": [
+                        {"get_attr": ["0", "bar"]},
+                        {"get_attr": ["1", "bar"]},
+                    ]
+                }
+            }
+        }
+        resource_def = rsrc_defn.ResourceDefinition(
+            None,
+            "OverwrittenFnGetRefIdType",
+            {"foo": "baz"})
+
+        stack = utils.parse_stack(template)
+        snip = stack.t.resource_definitions(stack)['group1']
+        resg = resource_group.ResourceGroup('test', snip, stack)
+        nested = get_fake_nested_stack(['0', '1'])
+        self.inspector.template.return_value = nested.defn._template
+        self.inspector.member_names.return_value = ['0', '1']
+        resg.build_resource_definition = mock.Mock(return_value=resource_def)
+        resg.referenced_attrs = mock.Mock(return_value=["bar"])
+        self.assertEqual(expect, resg._assemble_for_rolling_update(2, 1).t)
+
     def test_assemble_nested_rolling_update_none(self):
         expect = {
             "heat_template_version": "2015-04-30",