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