blob: 60533031b1c837b6e0dd4327e90d0785e3d96554 [file] [log] [blame]
Rakesh H Sa3325d62015-04-04 19:42:29 +05301# 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
14import copy
15from heat_integrationtests.common import test
16import json
17
18test_template_one_resource = {
19 'heat_template_version': '2013-05-23',
20 'description': 'Test template to create one instance.',
21 'resources': {
22 'test1': {
23 'type': 'OS::Heat::TestResource',
24 'properties': {
25 'value': 'Test1',
26 'fail': False,
27 'update_replace': False,
28 'wait_secs': 0
29 }
30 }
31 }
32}
33
34test_template_two_resource = {
35 'heat_template_version': '2013-05-23',
36 'description': 'Test template to create two instance.',
37 'resources': {
38 'test1': {
39 'type': 'OS::Heat::TestResource',
40 'properties': {
41 'value': 'Test1',
42 'fail': False,
43 'update_replace': False,
44 'wait_secs': 0
45 }
46 },
47 'test2': {
48 'type': 'OS::Heat::TestResource',
49 'properties': {
50 'value': 'Test1',
51 'fail': False,
52 'update_replace': False,
53 'wait_secs': 0
54 }
55 }
56 }
57}
58
59
60def _change_rsrc_properties(template, rsrcs, values):
61 modified_template = copy.deepcopy(template)
62 for rsrc_name in rsrcs:
63 rsrc_prop = modified_template['resources'][
64 rsrc_name]['properties']
65 for prop in rsrc_prop:
66 if prop in values:
67 rsrc_prop[prop] = values[prop]
68 return modified_template
69
70
71class CreateStackTest(test.HeatIntegrationTest):
72 def setUp(self):
73 super(CreateStackTest, self).setUp()
74 self.client = self.orchestration_client
75
76 def test_create_rollback(self):
77 values = {'fail': True, 'value': 'test_create_rollback'}
78 template = _change_rsrc_properties(test_template_one_resource,
79 ['test1'], values)
80
81 self.stack_create(
82 template=template,
83 expected_status='ROLLBACK_COMPLETE',
84 disable_rollback=False)
85
86
87class UpdateStackTest(test.HeatIntegrationTest):
88
89 provider_template = {
90 'heat_template_version': '2013-05-23',
91 'description': 'foo',
92 'resources': {
93 'test1': {
94 'type': 'My::TestResource'
95 }
96 }
97 }
98
99 provider_group_template = '''
100heat_template_version: 2013-05-23
101resources:
102 test_group:
103 type: OS::Heat::ResourceGroup
104 properties:
105 count: 2
106 resource_def:
107 type: My::TestResource
108'''
109
110 update_userdata_template = '''
111heat_template_version: 2014-10-16
112parameters:
113 flavor:
114 type: string
115 user_data:
116 type: string
117 image:
118 type: string
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530119 network:
120 type: string
Rakesh H Sa3325d62015-04-04 19:42:29 +0530121
122resources:
123 server:
124 type: OS::Nova::Server
125 properties:
126 image: {get_param: image}
127 flavor: {get_param: flavor}
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530128 networks: [{network: {get_param: network} }]
Rakesh H Sa3325d62015-04-04 19:42:29 +0530129 user_data_format: SOFTWARE_CONFIG
130 user_data: {get_param: user_data}
131'''
132
133 def setUp(self):
134 super(UpdateStackTest, self).setUp()
135 self.client = self.orchestration_client
136
137 def test_stack_update_nochange(self):
138 template = _change_rsrc_properties(test_template_one_resource,
139 ['test1'],
140 {'value': 'test_no_change'})
141 stack_identifier = self.stack_create(
142 template=template)
143 expected_resources = {'test1': 'OS::Heat::TestResource'}
144 self.assertEqual(expected_resources,
145 self.list_resources(stack_identifier))
146
147 # Update with no changes, resources should be unchanged
148 self.update_stack(stack_identifier, template)
149 self.assertEqual(expected_resources,
150 self.list_resources(stack_identifier))
151
152 def test_stack_in_place_update(self):
153 template = _change_rsrc_properties(test_template_one_resource,
154 ['test1'],
155 {'value': 'test_in_place'})
156 stack_identifier = self.stack_create(
157 template=template)
158 expected_resources = {'test1': 'OS::Heat::TestResource'}
159 self.assertEqual(expected_resources,
160 self.list_resources(stack_identifier))
161 resource = self.client.resources.list(stack_identifier)
162 initial_phy_id = resource[0].physical_resource_id
163
164 tmpl_update = _change_rsrc_properties(
165 test_template_one_resource, ['test1'],
166 {'value': 'test_in_place_update'})
167 # Update the Value
168 self.update_stack(stack_identifier, tmpl_update)
169 resource = self.client.resources.list(stack_identifier)
170 # By default update_in_place
171 self.assertEqual(initial_phy_id,
172 resource[0].physical_resource_id)
173
174 def test_stack_update_replace(self):
175 template = _change_rsrc_properties(test_template_one_resource,
176 ['test1'],
177 {'value': 'test_replace'})
178 stack_identifier = self.stack_create(
179 template=template)
180 expected_resources = {'test1': 'OS::Heat::TestResource'}
181 self.assertEqual(expected_resources,
182 self.list_resources(stack_identifier))
183 resource = self.client.resources.list(stack_identifier)
184 initial_phy_id = resource[0].physical_resource_id
185
186 # Update the value and also set update_replace prop
187 tmpl_update = _change_rsrc_properties(
188 test_template_one_resource, ['test1'],
189 {'value': 'test_in_place_update', 'update_replace': True})
190 self.update_stack(stack_identifier, tmpl_update)
191 resource = self.client.resources.list(stack_identifier)
192 # update Replace
193 self.assertNotEqual(initial_phy_id,
194 resource[0].physical_resource_id)
195
196 def test_stack_update_add_remove(self):
197 template = _change_rsrc_properties(test_template_one_resource,
198 ['test1'],
199 {'value': 'test_add_remove'})
200 stack_identifier = self.stack_create(
201 template=template)
202 initial_resources = {'test1': 'OS::Heat::TestResource'}
203 self.assertEqual(initial_resources,
204 self.list_resources(stack_identifier))
205
206 tmpl_update = _change_rsrc_properties(
207 test_template_two_resource, ['test1', 'test2'],
208 {'value': 'test_add_remove_update'})
209 # Add one resource via a stack update
210 self.update_stack(stack_identifier, tmpl_update)
211 updated_resources = {'test1': 'OS::Heat::TestResource',
212 'test2': 'OS::Heat::TestResource'}
213 self.assertEqual(updated_resources,
214 self.list_resources(stack_identifier))
215
216 # Then remove it by updating with the original template
217 self.update_stack(stack_identifier, template)
218 self.assertEqual(initial_resources,
219 self.list_resources(stack_identifier))
220
221 def test_stack_update_rollback(self):
222 template = _change_rsrc_properties(test_template_one_resource,
223 ['test1'],
224 {'value': 'test_update_rollback'})
225 stack_identifier = self.stack_create(
226 template=template)
227 initial_resources = {'test1': 'OS::Heat::TestResource'}
228 self.assertEqual(initial_resources,
229 self.list_resources(stack_identifier))
230
231 tmpl_update = _change_rsrc_properties(
232 test_template_two_resource, ['test1', 'test2'],
233 {'value': 'test_update_rollback', 'fail': True})
234 # stack update, also set failure
235 self.update_stack(stack_identifier, tmpl_update,
236 expected_status='ROLLBACK_COMPLETE',
237 disable_rollback=False)
238 # since stack update failed only the original resource is present
239 updated_resources = {'test1': 'OS::Heat::TestResource'}
240 self.assertEqual(updated_resources,
241 self.list_resources(stack_identifier))
242
243 def test_stack_update_provider(self):
244 template = _change_rsrc_properties(
245 test_template_one_resource, ['test1'],
246 {'value': 'test_provider_template'})
247 files = {'provider.template': json.dumps(template)}
248 env = {'resource_registry':
249 {'My::TestResource': 'provider.template'}}
250 stack_identifier = self.stack_create(
251 template=self.provider_template,
252 files=files,
253 environment=env
254 )
255
256 initial_resources = {'test1': 'My::TestResource'}
257 self.assertEqual(initial_resources,
258 self.list_resources(stack_identifier))
259
260 # Prove the resource is backed by a nested stack, save the ID
261 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
262 'test1')
263 nested_id = nested_identifier.split('/')[-1]
264
265 # Then check the expected resources are in the nested stack
266 nested_resources = {'test1': 'OS::Heat::TestResource'}
267 self.assertEqual(nested_resources,
268 self.list_resources(nested_identifier))
269 tmpl_update = _change_rsrc_properties(
270 test_template_two_resource, ['test1', 'test2'],
271 {'value': 'test_provider_template'})
272 # Add one resource via a stack update by changing the nested stack
273 files['provider.template'] = json.dumps(tmpl_update)
274 self.update_stack(stack_identifier, self.provider_template,
275 environment=env, files=files)
276
277 # Parent resources should be unchanged and the nested stack
278 # should have been updated in-place without replacement
279 self.assertEqual(initial_resources,
280 self.list_resources(stack_identifier))
281 rsrc = self.client.resources.get(stack_identifier, 'test1')
282 self.assertEqual(rsrc.physical_resource_id, nested_id)
283
284 # Then check the expected resources are in the nested stack
285 nested_resources = {'test1': 'OS::Heat::TestResource',
286 'test2': 'OS::Heat::TestResource'}
287 self.assertEqual(nested_resources,
288 self.list_resources(nested_identifier))
289
290 def test_stack_update_provider_group(self):
291 '''Test two-level nested update.'''
292 # Create a ResourceGroup (which creates a nested stack),
293 # containing provider resources (which create a nested
294 # stack), thus excercising an update which traverses
295 # two levels of nesting.
296 template = _change_rsrc_properties(
297 test_template_one_resource, ['test1'],
298 {'value': 'test_provider_group_template'})
299 files = {'provider.template': json.dumps(template)}
300 env = {'resource_registry':
301 {'My::TestResource': 'provider.template'}}
302
303 stack_identifier = self.stack_create(
304 template=self.provider_group_template,
305 files=files,
306 environment=env
307 )
308
309 initial_resources = {'test_group': 'OS::Heat::ResourceGroup'}
310 self.assertEqual(initial_resources,
311 self.list_resources(stack_identifier))
312
313 # Prove the resource is backed by a nested stack, save the ID
314 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
315 'test_group')
316
317 # Then check the expected resources are in the nested stack
318 nested_resources = {'0': 'My::TestResource',
319 '1': 'My::TestResource'}
320 self.assertEqual(nested_resources,
321 self.list_resources(nested_identifier))
322
323 for n_rsrc in nested_resources:
324 rsrc = self.client.resources.get(nested_identifier, n_rsrc)
325 provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
326 provider_identifier = '%s/%s' % (provider_stack.stack_name,
327 provider_stack.id)
328 provider_resources = {u'test1': u'OS::Heat::TestResource'}
329 self.assertEqual(provider_resources,
330 self.list_resources(provider_identifier))
331
332 tmpl_update = _change_rsrc_properties(
333 test_template_two_resource, ['test1', 'test2'],
334 {'value': 'test_provider_group_template'})
335 # Add one resource via a stack update by changing the nested stack
336 files['provider.template'] = json.dumps(tmpl_update)
337 self.update_stack(stack_identifier, self.provider_group_template,
338 environment=env, files=files)
339
340 # Parent resources should be unchanged and the nested stack
341 # should have been updated in-place without replacement
342 self.assertEqual(initial_resources,
343 self.list_resources(stack_identifier))
344
345 # Resource group stack should also be unchanged (but updated)
346 nested_stack = self.client.stacks.get(nested_identifier)
347 self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
348 self.assertEqual(nested_resources,
349 self.list_resources(nested_identifier))
350
351 for n_rsrc in nested_resources:
352 rsrc = self.client.resources.get(nested_identifier, n_rsrc)
353 provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
354 provider_identifier = '%s/%s' % (provider_stack.stack_name,
355 provider_stack.id)
356 provider_resources = {'test1': 'OS::Heat::TestResource',
357 'test2': 'OS::Heat::TestResource'}
358 self.assertEqual(provider_resources,
359 self.list_resources(provider_identifier))
360
361 def test_stack_update_with_replacing_userdata(self):
362 """Confirm that we can update userdata of instance during updating
363 stack by the user of member role.
364
365 Make sure that a resource that inherites from StackUser can be deleted
366 during updating stack.
367 """
368 if not self.conf.minimal_image_ref:
369 raise self.skipException("No minimal image configured to test")
370 if not self.conf.minimal_instance_type:
371 raise self.skipException("No flavor configured to test")
372
373 parms = {'flavor': self.conf.minimal_instance_type,
374 'image': self.conf.minimal_image_ref,
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530375 'network': self.conf.fixed_network_name,
Rakesh H Sa3325d62015-04-04 19:42:29 +0530376 'user_data': ''}
377 name = self._stack_rand_name()
378
379 stack_identifier = self.stack_create(
380 stack_name=name,
381 template=self.update_userdata_template,
382 parameters=parms
383 )
384
385 parms_updated = parms
386 parms_updated['user_data'] = 'two'
387 self.update_stack(
388 stack_identifier,
389 template=self.update_userdata_template,
390 parameters=parms_updated)