Add functional tests for stack tags/hidden stacks

One test creates a stack with a tag, updates the 
tag and then deletes the tag. The second test, 
creates a stack with a tag named 'hidden'. In heat.conf, 
hidden_stack_tags is set to 'hidden' which means 
whichever stack has a tag named 'hidden', stack.list() 
should not pick it up.

blueprint stack-tags

Change-Id: Idf6669ccf5e210d4e6da3733a9ac190418306d98
diff --git a/common/test.py b/common/test.py
index 6c70d83..38748aa 100644
--- a/common/test.py
+++ b/common/test.py
@@ -321,7 +321,7 @@
             success_on_not_found=True)
 
     def update_stack(self, stack_identifier, template, environment=None,
-                     files=None, parameters=None,
+                     files=None, parameters=None, tags=None,
                      expected_status='UPDATE_COMPLETE',
                      disable_rollback=True):
         env = environment or {}
@@ -342,7 +342,8 @@
                     files=env_files,
                     disable_rollback=disable_rollback,
                     parameters=parameters,
-                    environment=env
+                    environment=env,
+                    tags=tags
                 )
             except heat_exceptions.HTTPConflict as ex:
                 # FIXME(sirushtim): Wait a little for the stack lock to be
@@ -405,9 +406,9 @@
         return dict((r.resource_name, r.resource_type) for r in resources)
 
     def stack_create(self, stack_name=None, template=None, files=None,
-                     parameters=None, environment=None,
-                     expected_status='CREATE_COMPLETE', disable_rollback=True,
-                     enable_cleanup=True):
+                     parameters=None, environment=None, tags=None,
+                     expected_status='CREATE_COMPLETE',
+                     disable_rollback=True, enable_cleanup=True):
         name = stack_name or self._stack_rand_name()
         templ = template or self.template
         templ_files = files or {}
@@ -419,7 +420,8 @@
             files=templ_files,
             disable_rollback=disable_rollback,
             parameters=params,
-            environment=env
+            environment=env,
+            tags=tags
         )
         if expected_status not in ['ROLLBACK_COMPLETE'] and enable_cleanup:
             self.addCleanup(self.client.stacks.delete, name)
diff --git a/functional/test_stack_tags.py b/functional/test_stack_tags.py
new file mode 100644
index 0000000..a183d25
--- /dev/null
+++ b/functional/test_stack_tags.py
@@ -0,0 +1,69 @@
+#    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.
+
+from heat_integrationtests.common import test
+
+
+class StackTagTest(test.HeatIntegrationTest):
+
+    template = '''
+heat_template_version: 2014-10-16
+description:
+  foo
+'''
+
+    def setUp(self):
+        super(StackTagTest, self).setUp()
+        self.client = self.orchestration_client
+
+    def test_stack_tag(self):
+        # Stack create with stack tags
+        tags = ['foo', 'bar']
+        stack_identifier = self.stack_create(
+            template=self.template,
+            tags=tags
+        )
+
+        # Ensure property tag is populated and matches given tags
+        stack = self.client.stacks.get(stack_identifier)
+        self.assertEqual(tags, stack.tags)
+
+        # Update tags
+        updated_tags = ['tag1', 'tag2']
+        self.update_stack(
+            stack_identifier,
+            template=self.template,
+            tags=updated_tags)
+
+        # Ensure property tag is populated and matches updated tags
+        updated_stack = self.client.stacks.get(stack_identifier)
+        self.assertEqual(updated_tags, updated_stack.tags)
+
+        # Delete tags
+        self.update_stack(
+            stack_identifier,
+            template=self.template
+        )
+
+        # Ensure property tag is not populated
+        empty_tags_stack = self.client.stacks.get(stack_identifier)
+        self.assertIsNone(empty_tags_stack.tags)
+
+    def test_hidden_stack(self):
+        # Stack create with hidden stack tag
+        tags = ['foo', 'hidden']
+        self.stack_create(
+            template=self.template,
+            tags=tags)
+        # Ensure stack does not exist when we do a stack list
+        for stack in self.client.stacks.list():
+            self.assertNotIn('hidden', stack.tags, "Hidden stack can be seen")