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