Make StackResource less strict on initial validation

When doing the initial validate(), skip validating values by setting
the stack strict_validate to False, otherwise we incorrectly fail
validation when values are passed in via properties/parameters which
refer to resources in the parent stack.

Co-Authored-by: Angus Salkeld <asalkeld@mirantis.com>
Change-Id: Ib75c2de6c32373de72901b9f7c5e3828bd9ee7d9
Closes-Bug: #1407100
Closes-Bug: #1407877
Closes-Bug: #1405446
diff --git a/common/test.py b/common/test.py
index 3de44f5..450ed82 100644
--- a/common/test.py
+++ b/common/test.py
@@ -331,7 +331,8 @@
         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):
+                     parameters=None, environment=None,
+                     expected_status='CREATE_COMPLETE'):
         name = stack_name or self._stack_rand_name()
         templ = template or self.template
         templ_files = files or {}
@@ -349,5 +350,5 @@
 
         stack = self.client.stacks.get(name)
         stack_identifier = '%s/%s' % (name, stack.id)
-        self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
+        self._wait_for_stack_status(stack_identifier, expected_status)
         return stack_identifier
diff --git a/functional/test_validation.py b/functional/test_validation.py
new file mode 100644
index 0000000..52309cd
--- /dev/null
+++ b/functional/test_validation.py
@@ -0,0 +1,95 @@
+#    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 logging
+
+from heat_integrationtests.common import test
+
+
+LOG = logging.getLogger(__name__)
+
+
+class StackValidationTest(test.HeatIntegrationTest):
+
+    def setUp(self):
+        super(StackValidationTest, self).setUp()
+        self.client = self.orchestration_client
+        if not self.conf.minimal_image_ref:
+            raise self.skipException("No image configured to test")
+
+        if not self.conf.instance_type:
+            raise self.skipException("No instance_type configured to test")
+
+        if self.conf.keypair_name:
+            self.keypair_name = self.conf.keypair_name
+        else:
+            self.keypair = self.create_keypair()
+            self.keypair_name = self.keypair.id
+
+    def test_stack_validate_provider_references_parent_resource(self):
+        template = '''
+heat_template_version: 2014-10-16
+parameters:
+  keyname:
+    type: string
+  flavor:
+    type: string
+  image:
+    type: string
+resources:
+  config:
+    type: My::Config
+    properties:
+        server: {get_resource: server}
+
+  server:
+    type: OS::Nova::Server
+    properties:
+      image: {get_param: image}
+      flavor: {get_param: flavor}
+      key_name: {get_param: keyname}
+      user_data_format: SOFTWARE_CONFIG
+'''
+        config_template = '''
+heat_template_version: 2014-10-16
+parameters:
+  server:
+    type: string
+resources:
+  config:
+    type: OS::Heat::SoftwareConfig
+
+  deployment:
+    type: OS::Heat::SoftwareDeployment
+    properties:
+      config:
+        get_resource: config
+      server:
+        get_param: server
+'''
+        files = {'provider.yaml': config_template}
+        env = {'resource_registry':
+               {'My::Config': 'provider.yaml'}}
+        parameters = {'keyname': self.keypair_name,
+                      'flavor': self.conf.instance_type,
+                      'image': self.conf.minimal_image_ref}
+        # Note we don't wait for CREATE_COMPLETE, because we're using a
+        # minimal image without the tools to apply the config.
+        # The point of the test is just to prove that validation won't
+        # falsely prevent stack creation starting, ref bug #1407100
+        # Note that we can be sure My::Config will stay IN_PROGRESS as
+        # there's no signal sent to the deployment
+        self.stack_create(template=template,
+                          files=files,
+                          environment=env,
+                          parameters=parameters,
+                          expected_status='CREATE_IN_PROGRESS')