Merge "Move template resource tests to functional"
diff --git a/common/test.py b/common/test.py
index 25703f2..57083fd 100644
--- a/common/test.py
+++ b/common/test.py
@@ -347,7 +347,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 {}
@@ -365,7 +366,7 @@
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
def stack_adopt(self, stack_name=None, files=None,
diff --git a/functional/test_instance_group.py b/functional/test_instance_group.py
index 9a79801..d4e85b9 100644
--- a/functional/test_instance_group.py
+++ b/functional/test_instance_group.py
@@ -10,11 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import copy
import logging
-import yaml
-
-from heatclient import exc
from heat_integrationtests.common import test
@@ -31,8 +27,7 @@
"Parameters" : {"size": {"Type": "String", "Default": "1"},
"AZ": {"Type": "String", "Default": "nova"},
"image": {"Type": "String"},
- "flavor": {"Type": "String"},
- "keyname": {"Type": "String"}},
+ "flavor": {"Type": "String"}},
"Resources": {
"JobServerGroup": {
"Type": "OS::Heat::InstanceGroup",
@@ -49,7 +44,6 @@
"Properties": {
"ImageId" : {"Ref": "image"},
"InstanceType" : {"Ref": "flavor"},
- "KeyName" : {"Ref": "keyname"},
"SecurityGroups" : [ "sg-1" ],
"UserData" : "jsconfig data"
}
@@ -67,7 +61,6 @@
parameters:
ImageId: {type: string}
InstanceType: {type: string}
- KeyName: {type: string}
SecurityGroups: {type: comma_delimited_list}
UserData: {type: string}
Tags: {type: comma_delimited_list}
@@ -87,7 +80,6 @@
parameters:
ImageId: {type: string}
InstanceType: {type: string}
- KeyName: {type: string}
SecurityGroups: {type: comma_delimited_list}
UserData: {type: string}
Tags: {type: comma_delimited_list}
@@ -113,8 +105,6 @@
self.client = self.orchestration_client
if not self.conf.image_ref:
raise self.skipException("No image configured to test")
- if not self.conf.keypair_name:
- raise self.skipException("No keyname configured to test")
if not self.conf.instance_type:
raise self.skipException("No flavor configured to test")
@@ -155,7 +145,6 @@
env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
'parameters': {'size': 4,
'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
'flavor': self.conf.instance_type}}
stack_identifier = self.stack_create(template=self.template,
files=files, environment=env)
@@ -168,37 +157,11 @@
stack = self.client.stacks.get(stack_identifier)
self.assert_instance_count(stack, 4)
- def test_create_config_prop_validation(self):
- """Make sure that during a group create the instance
- properties are validated. And an error causes the group to fail.
- """
- stack_name = self._stack_rand_name()
-
- # add a property without a default and don't provide a value.
- # we use this to make the instance fail on a property validation
- # error.
- broken = yaml.load(copy.copy(self.instance_template))
- broken['parameters']['no_default'] = {'type': 'string'}
- files = {'provider.yaml': yaml.dump(broken)}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 4,
- 'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
- 'flavor': self.conf.instance_type}}
-
- # now with static nested stack validation, this gets raised quickly.
- excp = self.assertRaises(exc.HTTPBadRequest, self.client.stacks.create,
- stack_name=stack_name, template=self.template,
- files=files, disable_rollback=True,
- parameters={}, environment=env)
- self.assertIn('Property no_default not assigned', str(excp))
-
def test_size_updates_work(self):
files = {'provider.yaml': self.instance_template}
env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
'parameters': {'size': 2,
'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
'flavor': self.conf.instance_type}}
stack_identifier = self.stack_create(template=self.template,
@@ -211,7 +174,6 @@
env2 = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
'parameters': {'size': 5,
'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
'flavor': self.conf.instance_type}}
self.update_stack(stack_identifier, self.template,
environment=env2, files=files)
@@ -228,7 +190,6 @@
{'AWS::EC2::Instance': 'provider.yaml'},
'parameters': {'size': 1,
'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
'flavor': self.conf.instance_type}}
stack_identifier = self.stack_create(template=self.template,
@@ -242,7 +203,6 @@
'parameters': {'size': '2',
'AZ': 'wibble',
'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
'flavor': self.conf.instance_type}}
self.update_stack(stack_identifier, self.template,
environment=env2, files=files)
@@ -260,7 +220,6 @@
env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
'parameters': {'size': 2,
'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
'flavor': self.conf.instance_type}}
self.client.stacks.create(
@@ -293,7 +252,6 @@
env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
'parameters': {'size': 2,
'image': self.conf.image_ref,
- 'keyname': self.conf.keypair_name,
'flavor': self.conf.instance_type}}
stack_identifier = self.stack_create(template=self.template,
diff --git a/functional/test_resource_group.py b/functional/test_resource_group.py
index afd9f55..8ccddd8 100644
--- a/functional/test_resource_group.py
+++ b/functional/test_resource_group.py
@@ -17,6 +17,27 @@
from heat_integrationtests.common import test
+template = '''
+heat_template_version: 2013-05-23
+resources:
+ random_group:
+ type: OS::Heat::ResourceGroup
+ properties:
+ count: 2
+ resource_def:
+ type: OS::Heat::RandomString
+ properties:
+ length: 30
+outputs:
+ random1:
+ value: {get_attr: [random_group, resource.0.value]}
+ random2:
+ value: {get_attr: [random_group, resource.1.value]}
+ all_values:
+ value: {get_attr: [random_group, value]}
+'''
+
+
class ResourceGroupTest(test.HeatIntegrationTest):
def setUp(self):
@@ -90,3 +111,54 @@
template=template_two_nested,
environment=env, files=files)
self.assertIn(expected_err, six.text_type(ex))
+
+ def _validate_resources(self, stack_identifier, expected_count):
+ nested_identifier = self._group_nested_identifier(stack_identifier)
+ resources = self.list_resources(nested_identifier)
+ self.assertEqual(expected_count, len(resources))
+ expected_resources = dict(
+ (str(idx), 'OS::Heat::RandomString')
+ for idx in range(expected_count))
+
+ self.assertEqual(expected_resources, resources)
+
+ def test_create(self):
+ def validate_output(stack, output_key, length):
+ output_value = self._stack_output(stack, output_key)
+ self.assertEqual(length, len(output_value))
+ return output_value
+ # verify that the resources in resource group are identically
+ # configured, resource names and outputs are appropriate.
+ stack_identifier = self.stack_create('create_stack', template=template)
+ self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
+ self.list_resources(stack_identifier))
+
+ # validate count, type and name of resources in a resource group.
+ self._validate_resources(stack_identifier, 2)
+
+ # validate outputs
+ stack = self.client.stacks.get(stack_identifier)
+ outputs = []
+ outputs.append(validate_output(stack, 'random1', 30))
+ outputs.append(validate_output(stack, 'random2', 30))
+ self.assertEqual(outputs, self._stack_output(stack, 'all_values'))
+
+ def test_update_increase_decrease_count(self):
+ # create stack with resource group count 2
+ stack_identifier = self.stack_create('update_stack', template=template)
+ self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
+ self.list_resources(stack_identifier))
+ # verify that the resource group has 2 resources
+ self._validate_resources(stack_identifier, 2)
+
+ # increase the resource group count to 5
+ update_template = template.replace("count: 2", "count: 5")
+ self.update_stack(stack_identifier, update_template)
+ # verify that the resource group has 5 resources
+ self._validate_resources(stack_identifier, 5)
+
+ # decrease the resource group count to 3
+ update_template = template.replace("count: 2", "count: 3")
+ self.update_stack(stack_identifier, update_template)
+ # verify that the resource group has 3 resources
+ self._validate_resources(stack_identifier, 3)
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')