| # 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 hashlib |
| import json |
| import logging |
| import random |
| import urlparse |
| |
| from swiftclient import utils as swiftclient_utils |
| import yaml |
| |
| from heat_integrationtests.common import test |
| |
| |
| LOG = logging.getLogger(__name__) |
| |
| |
| class AwsStackTest(test.HeatIntegrationTest): |
| test_template = ''' |
| HeatTemplateFormatVersion: '2012-12-12' |
| Resources: |
| the_nested: |
| Type: AWS::CloudFormation::Stack |
| Properties: |
| TemplateURL: the.yaml |
| Parameters: |
| KeyName: foo |
| Outputs: |
| output_foo: |
| Value: {"Fn::GetAtt": [the_nested, Outputs.Foo]} |
| ''' |
| |
| nested_template = ''' |
| HeatTemplateFormatVersion: '2012-12-12' |
| Parameters: |
| KeyName: |
| Type: String |
| Outputs: |
| Foo: |
| Value: bar |
| ''' |
| |
| update_template = ''' |
| HeatTemplateFormatVersion: '2012-12-12' |
| Parameters: |
| KeyName: |
| Type: String |
| Outputs: |
| Foo: |
| Value: foo |
| ''' |
| |
| nested_with_res_template = ''' |
| HeatTemplateFormatVersion: '2012-12-12' |
| Parameters: |
| KeyName: |
| Type: String |
| Resources: |
| NestedResource: |
| Type: OS::Heat::RandomString |
| Outputs: |
| Foo: |
| Value: {"Fn::GetAtt": [NestedResource, value]} |
| ''' |
| |
| def setUp(self): |
| super(AwsStackTest, self).setUp() |
| self.client = self.orchestration_client |
| self.object_container_name = AwsStackTest.__name__ |
| self.project_id = self.identity_client.auth_ref.project_id |
| self.object_client.put_container(self.object_container_name) |
| self.nested_name = '%s.yaml' % test.rand_name() |
| |
| def publish_template(self, name, contents): |
| oc = self.object_client |
| |
| # post the object |
| oc.put_object(self.object_container_name, name, contents) |
| # TODO(asalkeld) see if this is causing problems. |
| # self.addCleanup(self.object_client.delete_object, |
| # self.object_container_name, name) |
| |
| # make the tempurl |
| key_header = 'x-account-meta-temp-url-key' |
| if key_header not in oc.head_account(): |
| swift_key = hashlib.sha224( |
| str(random.getrandbits(256))).hexdigest()[:32] |
| LOG.warn('setting swift key to %s' % swift_key) |
| oc.post_account({key_header: swift_key}) |
| key = oc.head_account()[key_header] |
| path = '/v1/AUTH_%s/%s/%s' % (self.project_id, |
| self.object_container_name, name) |
| timeout = self.conf.build_timeout * 10 |
| tempurl = swiftclient_utils.generate_temp_url(path, timeout, |
| key, 'GET') |
| sw_url = urlparse.urlparse(oc.url) |
| return '%s://%s%s' % (sw_url.scheme, sw_url.netloc, tempurl) |
| |
| def test_nested_stack_create(self): |
| url = self.publish_template(self.nested_name, self.nested_template) |
| self.template = self.test_template.replace('the.yaml', url) |
| stack_identifier = self.stack_create(template=self.template) |
| stack = self.client.stacks.get(stack_identifier) |
| self.assert_resource_is_a_stack(stack_identifier, 'the_nested') |
| self.assertEqual('bar', self._stack_output(stack, 'output_foo')) |
| |
| def test_nested_stack_create_with_timeout(self): |
| url = self.publish_template(self.nested_name, self.nested_template) |
| self.template = self.test_template.replace('the.yaml', url) |
| timeout_template = yaml.load(self.template) |
| props = timeout_template['Resources']['the_nested']['Properties'] |
| props['TimeoutInMinutes'] = '50' |
| |
| stack_identifier = self.stack_create( |
| template=timeout_template) |
| stack = self.client.stacks.get(stack_identifier) |
| self.assert_resource_is_a_stack(stack_identifier, 'the_nested') |
| self.assertEqual('bar', self._stack_output(stack, 'output_foo')) |
| |
| def test_nested_stack_adopt_ok(self): |
| url = self.publish_template(self.nested_name, |
| self.nested_with_res_template) |
| self.template = self.test_template.replace('the.yaml', url) |
| adopt_data = { |
| "resources": { |
| "the_nested": { |
| "resource_id": "test-res-id", |
| "resources": { |
| "NestedResource": { |
| "type": "OS::Heat::RandomString", |
| "resource_id": "test-nested-res-id", |
| "resource_data": {"value": "goopie"} |
| } |
| } |
| } |
| }, |
| "environment": {"parameters": {}}, |
| "template": yaml.load(self.template) |
| } |
| |
| stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data)) |
| |
| self.assert_resource_is_a_stack(stack_identifier, 'the_nested') |
| stack = self.client.stacks.get(stack_identifier) |
| self.assertEqual('goopie', self._stack_output(stack, 'output_foo')) |
| |
| def test_nested_stack_adopt_fail(self): |
| url = self.publish_template(self.nested_name, |
| self.nested_with_res_template) |
| self.template = self.test_template.replace('the.yaml', url) |
| adopt_data = { |
| "resources": { |
| "the_nested": { |
| "resource_id": "test-res-id", |
| "resources": { |
| } |
| } |
| }, |
| "environment": {"parameters": {}}, |
| "template": yaml.load(self.template) |
| } |
| |
| stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data), |
| wait_for_status='ADOPT_FAILED') |
| rsrc = self.client.resources.get(stack_identifier, 'the_nested') |
| self.assertEqual('ADOPT_FAILED', rsrc.resource_status) |
| |
| def test_nested_stack_update(self): |
| url = self.publish_template(self.nested_name, self.nested_template) |
| self.template = self.test_template.replace('the.yaml', url) |
| stack_identifier = self.stack_create(template=self.template) |
| original_nested_id = self.assert_resource_is_a_stack( |
| stack_identifier, 'the_nested') |
| stack = self.client.stacks.get(stack_identifier) |
| self.assertEqual('bar', self._stack_output(stack, 'output_foo')) |
| |
| new_template = yaml.load(self.template) |
| props = new_template['Resources']['the_nested']['Properties'] |
| props['TemplateURL'] = self.publish_template(self.nested_name, |
| self.update_template) |
| |
| self.update_stack(stack_identifier, new_template) |
| |
| # Expect the physical resource name staying the same after update, |
| # so that the nested was actually updated instead of replaced. |
| new_nested_id = self.assert_resource_is_a_stack( |
| stack_identifier, 'the_nested') |
| self.assertEqual(original_nested_id, new_nested_id) |
| updt_stack = self.client.stacks.get(stack_identifier) |
| self.assertEqual('foo', self._stack_output(updt_stack, 'output_foo')) |
| |
| def test_nested_stack_suspend_resume(self): |
| url = self.publish_template(self.nested_name, self.nested_template) |
| self.template = self.test_template.replace('the.yaml', url) |
| stack_identifier = self.stack_create(template=self.template) |
| |
| self.client.actions.suspend(stack_id=stack_identifier) |
| self._wait_for_resource_status( |
| stack_identifier, 'the_nested', 'SUSPEND_COMPLETE') |
| |
| self.client.actions.resume(stack_id=stack_identifier) |
| self._wait_for_resource_status( |
| stack_identifier, 'the_nested', 'RESUME_COMPLETE') |