blob: 5aabe958143d5ddda82d5034a50bcd3bdbe9fe3e [file] [log] [blame]
Angus Salkeld4408da32015-02-03 18:53:30 +10001# 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
13import hashlib
14import json
Angus Salkeld4408da32015-02-03 18:53:30 +100015import random
16import urlparse
17
Steve Baker24641292015-03-13 10:47:50 +130018from oslo_log import log as logging
Angus Salkeld4408da32015-02-03 18:53:30 +100019from swiftclient import utils as swiftclient_utils
20import yaml
21
22from heat_integrationtests.common import test
23
24
25LOG = logging.getLogger(__name__)
26
27
28class AwsStackTest(test.HeatIntegrationTest):
29 test_template = '''
30HeatTemplateFormatVersion: '2012-12-12'
31Resources:
32 the_nested:
33 Type: AWS::CloudFormation::Stack
34 Properties:
35 TemplateURL: the.yaml
36 Parameters:
37 KeyName: foo
38Outputs:
39 output_foo:
40 Value: {"Fn::GetAtt": [the_nested, Outputs.Foo]}
41'''
42
43 nested_template = '''
44HeatTemplateFormatVersion: '2012-12-12'
45Parameters:
46 KeyName:
47 Type: String
48Outputs:
49 Foo:
50 Value: bar
51'''
52
53 update_template = '''
54HeatTemplateFormatVersion: '2012-12-12'
55Parameters:
56 KeyName:
57 Type: String
58Outputs:
59 Foo:
60 Value: foo
61'''
62
63 nested_with_res_template = '''
64HeatTemplateFormatVersion: '2012-12-12'
65Parameters:
66 KeyName:
67 Type: String
68Resources:
69 NestedResource:
70 Type: OS::Heat::RandomString
71Outputs:
72 Foo:
73 Value: {"Fn::GetAtt": [NestedResource, value]}
74'''
75
76 def setUp(self):
77 super(AwsStackTest, self).setUp()
78 self.client = self.orchestration_client
79 self.object_container_name = AwsStackTest.__name__
80 self.project_id = self.identity_client.auth_ref.project_id
81 self.object_client.put_container(self.object_container_name)
82 self.nested_name = '%s.yaml' % test.rand_name()
83
84 def publish_template(self, name, contents):
85 oc = self.object_client
86
87 # post the object
88 oc.put_object(self.object_container_name, name, contents)
89 # TODO(asalkeld) see if this is causing problems.
90 # self.addCleanup(self.object_client.delete_object,
91 # self.object_container_name, name)
92
93 # make the tempurl
94 key_header = 'x-account-meta-temp-url-key'
95 if key_header not in oc.head_account():
96 swift_key = hashlib.sha224(
97 str(random.getrandbits(256))).hexdigest()[:32]
98 LOG.warn('setting swift key to %s' % swift_key)
99 oc.post_account({key_header: swift_key})
100 key = oc.head_account()[key_header]
101 path = '/v1/AUTH_%s/%s/%s' % (self.project_id,
102 self.object_container_name, name)
103 timeout = self.conf.build_timeout * 10
104 tempurl = swiftclient_utils.generate_temp_url(path, timeout,
105 key, 'GET')
106 sw_url = urlparse.urlparse(oc.url)
107 return '%s://%s%s' % (sw_url.scheme, sw_url.netloc, tempurl)
108
109 def test_nested_stack_create(self):
110 url = self.publish_template(self.nested_name, self.nested_template)
111 self.template = self.test_template.replace('the.yaml', url)
112 stack_identifier = self.stack_create(template=self.template)
113 stack = self.client.stacks.get(stack_identifier)
114 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
115 self.assertEqual('bar', self._stack_output(stack, 'output_foo'))
116
117 def test_nested_stack_create_with_timeout(self):
118 url = self.publish_template(self.nested_name, self.nested_template)
119 self.template = self.test_template.replace('the.yaml', url)
120 timeout_template = yaml.load(self.template)
121 props = timeout_template['Resources']['the_nested']['Properties']
122 props['TimeoutInMinutes'] = '50'
123
124 stack_identifier = self.stack_create(
125 template=timeout_template)
126 stack = self.client.stacks.get(stack_identifier)
127 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
128 self.assertEqual('bar', self._stack_output(stack, 'output_foo'))
129
130 def test_nested_stack_adopt_ok(self):
131 url = self.publish_template(self.nested_name,
132 self.nested_with_res_template)
133 self.template = self.test_template.replace('the.yaml', url)
134 adopt_data = {
135 "resources": {
136 "the_nested": {
137 "resource_id": "test-res-id",
138 "resources": {
139 "NestedResource": {
140 "type": "OS::Heat::RandomString",
141 "resource_id": "test-nested-res-id",
142 "resource_data": {"value": "goopie"}
143 }
144 }
145 }
146 },
147 "environment": {"parameters": {}},
148 "template": yaml.load(self.template)
149 }
150
151 stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data))
152
153 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
154 stack = self.client.stacks.get(stack_identifier)
155 self.assertEqual('goopie', self._stack_output(stack, 'output_foo'))
156
157 def test_nested_stack_adopt_fail(self):
158 url = self.publish_template(self.nested_name,
159 self.nested_with_res_template)
160 self.template = self.test_template.replace('the.yaml', url)
161 adopt_data = {
162 "resources": {
163 "the_nested": {
164 "resource_id": "test-res-id",
165 "resources": {
166 }
167 }
168 },
169 "environment": {"parameters": {}},
170 "template": yaml.load(self.template)
171 }
172
173 stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data),
174 wait_for_status='ADOPT_FAILED')
175 rsrc = self.client.resources.get(stack_identifier, 'the_nested')
176 self.assertEqual('ADOPT_FAILED', rsrc.resource_status)
177
178 def test_nested_stack_update(self):
179 url = self.publish_template(self.nested_name, self.nested_template)
180 self.template = self.test_template.replace('the.yaml', url)
181 stack_identifier = self.stack_create(template=self.template)
182 original_nested_id = self.assert_resource_is_a_stack(
183 stack_identifier, 'the_nested')
184 stack = self.client.stacks.get(stack_identifier)
185 self.assertEqual('bar', self._stack_output(stack, 'output_foo'))
186
187 new_template = yaml.load(self.template)
188 props = new_template['Resources']['the_nested']['Properties']
189 props['TemplateURL'] = self.publish_template(self.nested_name,
190 self.update_template)
191
192 self.update_stack(stack_identifier, new_template)
193
194 # Expect the physical resource name staying the same after update,
195 # so that the nested was actually updated instead of replaced.
196 new_nested_id = self.assert_resource_is_a_stack(
197 stack_identifier, 'the_nested')
198 self.assertEqual(original_nested_id, new_nested_id)
199 updt_stack = self.client.stacks.get(stack_identifier)
200 self.assertEqual('foo', self._stack_output(updt_stack, 'output_foo'))
201
202 def test_nested_stack_suspend_resume(self):
203 url = self.publish_template(self.nested_name, self.nested_template)
204 self.template = self.test_template.replace('the.yaml', url)
205 stack_identifier = self.stack_create(template=self.template)
206
207 self.client.actions.suspend(stack_id=stack_identifier)
208 self._wait_for_resource_status(
209 stack_identifier, 'the_nested', 'SUSPEND_COMPLETE')
210
211 self.client.actions.resume(stack_id=stack_identifier)
212 self._wait_for_resource_status(
213 stack_identifier, 'the_nested', 'RESUME_COMPLETE')