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