Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 2 | # not use this file except in compliance with the License. You may obtain |
| 3 | # a copy of the License at |
| 4 | # |
| 5 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 6 | # |
| 7 | # Unless required by applicable law or agreed to in writing, software |
| 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 10 | # License for the specific language governing permissions and limitations |
| 11 | # under the License. |
| 12 | |
| 13 | import hashlib |
| 14 | import json |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 15 | import random |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 16 | |
Sirushti Murugesan | 4920fda | 2015-04-22 00:35:26 +0530 | [diff] [blame] | 17 | from six.moves.urllib import parse |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 18 | from swiftclient import utils as swiftclient_utils |
| 19 | import yaml |
| 20 | |
| 21 | from heat_integrationtests.common import test |
Rabi Mishra | 477efc9 | 2015-07-31 13:01:45 +0530 | [diff] [blame] | 22 | from heat_integrationtests.functional import functional_base |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 23 | |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 24 | |
Rabi Mishra | 477efc9 | 2015-07-31 13:01:45 +0530 | [diff] [blame] | 25 | class AwsStackTest(functional_base.FunctionalTestsBase): |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 26 | test_template = ''' |
| 27 | HeatTemplateFormatVersion: '2012-12-12' |
| 28 | Resources: |
| 29 | the_nested: |
| 30 | Type: AWS::CloudFormation::Stack |
| 31 | Properties: |
| 32 | TemplateURL: the.yaml |
| 33 | Parameters: |
| 34 | KeyName: foo |
| 35 | Outputs: |
| 36 | output_foo: |
| 37 | Value: {"Fn::GetAtt": [the_nested, Outputs.Foo]} |
| 38 | ''' |
| 39 | |
| 40 | nested_template = ''' |
| 41 | HeatTemplateFormatVersion: '2012-12-12' |
| 42 | Parameters: |
| 43 | KeyName: |
| 44 | Type: String |
| 45 | Outputs: |
| 46 | Foo: |
| 47 | Value: bar |
| 48 | ''' |
| 49 | |
| 50 | update_template = ''' |
| 51 | HeatTemplateFormatVersion: '2012-12-12' |
| 52 | Parameters: |
| 53 | KeyName: |
| 54 | Type: String |
| 55 | Outputs: |
| 56 | Foo: |
| 57 | Value: foo |
| 58 | ''' |
| 59 | |
| 60 | nested_with_res_template = ''' |
| 61 | HeatTemplateFormatVersion: '2012-12-12' |
| 62 | Parameters: |
| 63 | KeyName: |
| 64 | Type: String |
| 65 | Resources: |
| 66 | NestedResource: |
| 67 | Type: OS::Heat::RandomString |
| 68 | Outputs: |
| 69 | Foo: |
| 70 | Value: {"Fn::GetAtt": [NestedResource, value]} |
| 71 | ''' |
| 72 | |
| 73 | def setUp(self): |
| 74 | super(AwsStackTest, self).setUp() |
Thomas Herve | db36c09 | 2017-03-23 11:20:14 +0100 | [diff] [blame] | 75 | if not self.is_service_available('object-store'): |
| 76 | self.skipTest('object-store service not available, skipping') |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 77 | self.object_container_name = test.rand_name() |
Rabi Mishra | 65493fb | 2016-01-29 22:23:21 +0530 | [diff] [blame] | 78 | self.project_id = self.identity_client.project_id |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 79 | self.swift_key = hashlib.sha224( |
Thomas Herve | d1757c5 | 2017-02-01 13:52:58 +0100 | [diff] [blame] | 80 | str(random.getrandbits(256)).encode('ascii')).hexdigest()[:32] |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 81 | key_header = 'x-container-meta-temp-url-key' |
| 82 | self.object_client.put_container(self.object_container_name, |
| 83 | {key_header: self.swift_key}) |
| 84 | self.addCleanup(self.object_client.delete_container, |
| 85 | self.object_container_name) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 86 | |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 87 | def publish_template(self, contents, cleanup=True): |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 88 | oc = self.object_client |
| 89 | |
| 90 | # post the object |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 91 | oc.put_object(self.object_container_name, 'template.yaml', contents) |
| 92 | if cleanup: |
| 93 | self.addCleanup(oc.delete_object, |
| 94 | self.object_container_name, |
| 95 | 'template.yaml') |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 96 | path = '/v1/AUTH_%s/%s/%s' % (self.project_id, |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 97 | self.object_container_name, |
| 98 | 'template.yaml') |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 99 | timeout = self.conf.build_timeout * 10 |
| 100 | tempurl = swiftclient_utils.generate_temp_url(path, timeout, |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 101 | self.swift_key, 'GET') |
Sirushti Murugesan | 4920fda | 2015-04-22 00:35:26 +0530 | [diff] [blame] | 102 | sw_url = parse.urlparse(oc.url) |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 103 | return '%s://%s%s' % (sw_url.scheme, sw_url.netloc, tempurl) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 104 | |
| 105 | def test_nested_stack_create(self): |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 106 | url = self.publish_template(self.nested_template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 107 | self.template = self.test_template.replace('the.yaml', url) |
| 108 | stack_identifier = self.stack_create(template=self.template) |
| 109 | stack = self.client.stacks.get(stack_identifier) |
| 110 | self.assert_resource_is_a_stack(stack_identifier, 'the_nested') |
| 111 | self.assertEqual('bar', self._stack_output(stack, 'output_foo')) |
| 112 | |
| 113 | def test_nested_stack_create_with_timeout(self): |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 114 | url = self.publish_template(self.nested_template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 115 | self.template = self.test_template.replace('the.yaml', url) |
Bo Wang | d8df4dd | 2016-02-16 21:23:53 +0800 | [diff] [blame] | 116 | timeout_template = yaml.safe_load(self.template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 117 | props = timeout_template['Resources']['the_nested']['Properties'] |
| 118 | props['TimeoutInMinutes'] = '50' |
| 119 | |
| 120 | stack_identifier = self.stack_create( |
| 121 | template=timeout_template) |
| 122 | stack = self.client.stacks.get(stack_identifier) |
| 123 | self.assert_resource_is_a_stack(stack_identifier, 'the_nested') |
| 124 | self.assertEqual('bar', self._stack_output(stack, 'output_foo')) |
| 125 | |
| 126 | def test_nested_stack_adopt_ok(self): |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 127 | url = self.publish_template(self.nested_with_res_template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 128 | self.template = self.test_template.replace('the.yaml', url) |
| 129 | adopt_data = { |
| 130 | "resources": { |
| 131 | "the_nested": { |
| 132 | "resource_id": "test-res-id", |
| 133 | "resources": { |
| 134 | "NestedResource": { |
| 135 | "type": "OS::Heat::RandomString", |
| 136 | "resource_id": "test-nested-res-id", |
| 137 | "resource_data": {"value": "goopie"} |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | }, |
| 142 | "environment": {"parameters": {}}, |
Bo Wang | d8df4dd | 2016-02-16 21:23:53 +0800 | [diff] [blame] | 143 | "template": yaml.safe_load(self.template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data)) |
| 147 | |
| 148 | self.assert_resource_is_a_stack(stack_identifier, 'the_nested') |
| 149 | stack = self.client.stacks.get(stack_identifier) |
| 150 | self.assertEqual('goopie', self._stack_output(stack, 'output_foo')) |
| 151 | |
| 152 | def test_nested_stack_adopt_fail(self): |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 153 | url = self.publish_template(self.nested_with_res_template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 154 | self.template = self.test_template.replace('the.yaml', url) |
| 155 | adopt_data = { |
| 156 | "resources": { |
| 157 | "the_nested": { |
| 158 | "resource_id": "test-res-id", |
| 159 | "resources": { |
| 160 | } |
| 161 | } |
| 162 | }, |
| 163 | "environment": {"parameters": {}}, |
Bo Wang | d8df4dd | 2016-02-16 21:23:53 +0800 | [diff] [blame] | 164 | "template": yaml.safe_load(self.template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data), |
| 168 | wait_for_status='ADOPT_FAILED') |
| 169 | rsrc = self.client.resources.get(stack_identifier, 'the_nested') |
| 170 | self.assertEqual('ADOPT_FAILED', rsrc.resource_status) |
| 171 | |
| 172 | def test_nested_stack_update(self): |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 173 | url = self.publish_template(self.nested_template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 174 | self.template = self.test_template.replace('the.yaml', url) |
| 175 | stack_identifier = self.stack_create(template=self.template) |
| 176 | original_nested_id = self.assert_resource_is_a_stack( |
| 177 | stack_identifier, 'the_nested') |
| 178 | stack = self.client.stacks.get(stack_identifier) |
| 179 | self.assertEqual('bar', self._stack_output(stack, 'output_foo')) |
| 180 | |
Bo Wang | d8df4dd | 2016-02-16 21:23:53 +0800 | [diff] [blame] | 181 | new_template = yaml.safe_load(self.template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 182 | props = new_template['Resources']['the_nested']['Properties'] |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 183 | props['TemplateURL'] = self.publish_template(self.update_template, |
| 184 | cleanup=False) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 185 | |
| 186 | self.update_stack(stack_identifier, new_template) |
| 187 | |
| 188 | # Expect the physical resource name staying the same after update, |
| 189 | # so that the nested was actually updated instead of replaced. |
| 190 | new_nested_id = self.assert_resource_is_a_stack( |
| 191 | stack_identifier, 'the_nested') |
| 192 | self.assertEqual(original_nested_id, new_nested_id) |
| 193 | updt_stack = self.client.stacks.get(stack_identifier) |
| 194 | self.assertEqual('foo', self._stack_output(updt_stack, 'output_foo')) |
| 195 | |
| 196 | def test_nested_stack_suspend_resume(self): |
Thomas Herve | fb53d42 | 2016-01-05 15:29:03 +0100 | [diff] [blame] | 197 | url = self.publish_template(self.nested_template) |
Angus Salkeld | 4408da3 | 2015-02-03 18:53:30 +1000 | [diff] [blame] | 198 | self.template = self.test_template.replace('the.yaml', url) |
| 199 | stack_identifier = self.stack_create(template=self.template) |
Angus Salkeld | a7500d1 | 2015-04-10 15:44:07 +1000 | [diff] [blame] | 200 | self.stack_suspend(stack_identifier) |
| 201 | self.stack_resume(stack_identifier) |