~ubuntu-branches/ubuntu/wily/heat/wily-proposed

« back to all changes in this revision

Viewing changes to heat/tests/test_parser.py

  • Committer: Package Import Robot
  • Author(s): James Page, Corey Bryant, James Page
  • Date: 2015-03-30 11:11:18 UTC
  • mfrom: (1.1.23)
  • Revision ID: package-import@ubuntu.com-20150330111118-2qpycylx6swu4yhj
Tags: 2015.1~b3-0ubuntu1
[ Corey Bryant ]
* New upstream milestone release for OpenStack kilo:
  - d/control: Align with upstream dependencies.
  - d/p/sudoers_patch.patch: Rebased.
  - d/p/fix-requirements.patch: Rebased.

[ James Page ]
* d/p/fixup-assert-regex.patch: Tweak test to use assertRegexpMatches.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#
2
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
3
 
#    not use this file except in compliance with the License. You may obtain
4
 
#    a copy of the License at
5
 
#
6
 
#         http://www.apache.org/licenses/LICENSE-2.0
7
 
#
8
 
#    Unless required by applicable law or agreed to in writing, software
9
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
 
#    License for the specific language governing permissions and limitations
12
 
#    under the License.
13
 
 
14
 
import collections
15
 
import copy
16
 
import json
17
 
import time
18
 
import warnings
19
 
 
20
 
from keystoneclient import exceptions as kc_exceptions
21
 
import mock
22
 
import mox
23
 
from oslo.config import cfg
24
 
import six
25
 
 
26
 
from heat.common import context
27
 
from heat.common import exception
28
 
from heat.common import heat_keystoneclient as hkc
29
 
from heat.common import template_format
30
 
import heat.db.api as db_api
31
 
from heat.engine.cfn import functions as cfn_funcs
32
 
from heat.engine.cfn import template as cfn_t
33
 
from heat.engine.clients.os import keystone
34
 
from heat.engine.clients.os import nova
35
 
from heat.engine import environment
36
 
from heat.engine import function
37
 
from heat.engine.hot import template as hot_t
38
 
from heat.engine import parameters
39
 
from heat.engine import parser
40
 
from heat.engine import resource
41
 
from heat.engine import rsrc_defn
42
 
from heat.engine import scheduler
43
 
from heat.engine import template
44
 
from heat.tests import common
45
 
from heat.tests import fakes
46
 
from heat.tests import generic_resource as generic_rsrc
47
 
from heat.tests import utils
48
 
from heat.tests.v1_1 import fakes as fakes_v1_1
49
 
 
50
 
 
51
 
def join(raw):
52
 
    tmpl = template.Template(mapping_template)
53
 
    return function.resolve(tmpl.parse(None, raw))
54
 
 
55
 
 
56
 
class ParserTest(common.HeatTestCase):
57
 
 
58
 
    def test_list(self):
59
 
        raw = ['foo', 'bar', 'baz']
60
 
        parsed = join(raw)
61
 
        for i in six.moves.xrange(len(raw)):
62
 
            self.assertEqual(raw[i], parsed[i])
63
 
        self.assertIsNot(raw, parsed)
64
 
 
65
 
    def test_dict(self):
66
 
        raw = {'foo': 'bar', 'blarg': 'wibble'}
67
 
        parsed = join(raw)
68
 
        for k in raw:
69
 
            self.assertEqual(raw[k], parsed[k])
70
 
        self.assertIsNot(raw, parsed)
71
 
 
72
 
    def test_dict_list(self):
73
 
        raw = {'foo': ['bar', 'baz'], 'blarg': 'wibble'}
74
 
        parsed = join(raw)
75
 
        self.assertEqual(raw['blarg'], parsed['blarg'])
76
 
        for i in six.moves.xrange(len(raw['foo'])):
77
 
            self.assertEqual(raw['foo'][i], parsed['foo'][i])
78
 
        self.assertIsNot(raw, parsed)
79
 
        self.assertIsNot(raw['foo'], parsed['foo'])
80
 
 
81
 
    def test_list_dict(self):
82
 
        raw = [{'foo': 'bar', 'blarg': 'wibble'}, 'baz', 'quux']
83
 
        parsed = join(raw)
84
 
        for i in six.moves.xrange(1, len(raw)):
85
 
            self.assertEqual(raw[i], parsed[i])
86
 
        for k in raw[0]:
87
 
            self.assertEqual(raw[0][k], parsed[0][k])
88
 
        self.assertIsNot(raw, parsed)
89
 
        self.assertIsNot(raw[0], parsed[0])
90
 
 
91
 
    def test_join(self):
92
 
        raw = {'Fn::Join': [' ', ['foo', 'bar', 'baz']]}
93
 
        self.assertEqual('foo bar baz', join(raw))
94
 
 
95
 
    def test_join_none(self):
96
 
        raw = {'Fn::Join': [' ', ['foo', None, 'baz']]}
97
 
        self.assertEqual('foo  baz', join(raw))
98
 
 
99
 
    def test_join_list(self):
100
 
        raw = [{'Fn::Join': [' ', ['foo', 'bar', 'baz']]}, 'blarg', 'wibble']
101
 
        parsed = join(raw)
102
 
        self.assertEqual('foo bar baz', parsed[0])
103
 
        for i in six.moves.xrange(1, len(raw)):
104
 
            self.assertEqual(raw[i], parsed[i])
105
 
        self.assertIsNot(raw, parsed)
106
 
 
107
 
    def test_join_dict_val(self):
108
 
        raw = {'quux': {'Fn::Join': [' ', ['foo', 'bar', 'baz']]},
109
 
               'blarg': 'wibble'}
110
 
        parsed = join(raw)
111
 
        self.assertEqual('foo bar baz', parsed['quux'])
112
 
        self.assertEqual(raw['blarg'], parsed['blarg'])
113
 
        self.assertIsNot(raw, parsed)
114
 
 
115
 
 
116
 
mapping_template = template_format.parse('''{
117
 
  "AWSTemplateFormatVersion" : "2010-09-09",
118
 
  "Mappings" : {
119
 
    "ValidMapping" : {
120
 
      "TestKey" : { "TestValue" : "wibble" }
121
 
    },
122
 
    "InvalidMapping" : {
123
 
      "ValueList" : [ "foo", "bar" ],
124
 
      "ValueString" : "baz"
125
 
    },
126
 
    "MapList": [ "foo", { "bar" : "baz" } ],
127
 
    "MapString": "foobar"
128
 
  }
129
 
}''')
130
 
 
131
 
empty_template = template_format.parse('''{
132
 
  "HeatTemplateFormatVersion" : "2012-12-12",
133
 
}''')
134
 
 
135
 
parameter_template = template_format.parse('''{
136
 
  "HeatTemplateFormatVersion" : "2012-12-12",
137
 
  "Parameters" : {
138
 
    "foo" : { "Type" : "String" },
139
 
    "blarg" : { "Type" : "String", "Default": "quux" }
140
 
  }
141
 
}''')
142
 
 
143
 
 
144
 
resource_template = template_format.parse('''{
145
 
  "HeatTemplateFormatVersion" : "2012-12-12",
146
 
  "Resources" : {
147
 
    "foo" : { "Type" : "GenericResourceType" },
148
 
    "blarg" : { "Type" : "GenericResourceType" }
149
 
  }
150
 
}''')
151
 
 
152
 
 
153
 
class DummyClass(object):
154
 
    metadata = None
155
 
 
156
 
    def metadata_get(self):
157
 
        return self.metadata
158
 
 
159
 
    def metadata_set(self, metadata):
160
 
        self.metadata = metadata
161
 
 
162
 
 
163
 
class TemplateTest(common.HeatTestCase):
164
 
 
165
 
    def setUp(self):
166
 
        super(TemplateTest, self).setUp()
167
 
        self.ctx = utils.dummy_context()
168
 
 
169
 
        resource._register_class('GenericResourceType',
170
 
                                 generic_rsrc.GenericResource)
171
 
 
172
 
    @staticmethod
173
 
    def resolve(snippet, template, stack=None):
174
 
        return function.resolve(template.parse(stack, snippet))
175
 
 
176
 
    def test_defaults(self):
177
 
        empty = parser.Template(empty_template)
178
 
        self.assertNotIn('AWSTemplateFormatVersion', empty)
179
 
        self.assertEqual('No description', empty['Description'])
180
 
        self.assertEqual({}, empty['Mappings'])
181
 
        self.assertEqual({}, empty['Resources'])
182
 
        self.assertEqual({}, empty['Outputs'])
183
 
 
184
 
    def test_aws_version(self):
185
 
        tmpl = parser.Template(mapping_template)
186
 
        self.assertEqual(('AWSTemplateFormatVersion', '2010-09-09'),
187
 
                         tmpl.version)
188
 
 
189
 
    def test_heat_version(self):
190
 
        tmpl = parser.Template(resource_template)
191
 
        self.assertEqual(('HeatTemplateFormatVersion', '2012-12-12'),
192
 
                         tmpl.version)
193
 
 
194
 
    def test_invalid_hot_version(self):
195
 
        invalid_hot_version_tmp = template_format.parse(
196
 
            '''{
197
 
            "heat_template_version" : "2012-12-12",
198
 
            }''')
199
 
        init_ex = self.assertRaises(exception.InvalidTemplateVersion,
200
 
                                    parser.Template, invalid_hot_version_tmp)
201
 
        valid_versions = ['2014-10-16', '2013-05-23']
202
 
        ex_error_msg = ('The template version is invalid: '
203
 
                        '"heat_template_version: 2012-12-12". '
204
 
                        '"heat_template_version" should be one of: %s'
205
 
                        % ', '.join(valid_versions))
206
 
        self.assertEqual(ex_error_msg, six.text_type(init_ex))
207
 
 
208
 
    def test_invalid_version_not_in_hot_versions(self):
209
 
        invalid_hot_version_tmp = template_format.parse(
210
 
            '''{
211
 
            "heat_template_version" : "2012-12-12",
212
 
            }''')
213
 
        versions = {
214
 
            ('heat_template_version', '2013-05-23'): hot_t.HOTemplate20130523,
215
 
            ('heat_template_version', '2013-06-23'): hot_t.HOTemplate20130523
216
 
        }
217
 
 
218
 
        temp_copy = copy.deepcopy(template._template_classes)
219
 
        template._template_classes = versions
220
 
        init_ex = self.assertRaises(exception.InvalidTemplateVersion,
221
 
                                    parser.Template, invalid_hot_version_tmp)
222
 
        ex_error_msg = ('The template version is invalid: '
223
 
                        '"heat_template_version: 2012-12-12". '
224
 
                        '"heat_template_version" should be '
225
 
                        'one of: 2013-05-23, 2013-06-23')
226
 
        self.assertEqual(ex_error_msg, six.text_type(init_ex))
227
 
        template._template_classes = temp_copy
228
 
 
229
 
    def test_invalid_aws_version(self):
230
 
        invalid_aws_version_tmp = template_format.parse(
231
 
            '''{
232
 
            "AWSTemplateFormatVersion" : "2012-12-12",
233
 
            }''')
234
 
        init_ex = self.assertRaises(exception.InvalidTemplateVersion,
235
 
                                    parser.Template, invalid_aws_version_tmp)
236
 
        ex_error_msg = ('The template version is invalid: '
237
 
                        '"AWSTemplateFormatVersion: 2012-12-12". '
238
 
                        '"AWSTemplateFormatVersion" should be: 2010-09-09')
239
 
        self.assertEqual(ex_error_msg, six.text_type(init_ex))
240
 
 
241
 
    def test_invalid_version_not_in_aws_versions(self):
242
 
        invalid_aws_version_tmp = template_format.parse(
243
 
            '''{
244
 
            "AWSTemplateFormatVersion" : "2012-12-12",
245
 
            }''')
246
 
        versions = {
247
 
            ('AWSTemplateFormatVersion', '2010-09-09'): cfn_t.CfnTemplate,
248
 
            ('AWSTemplateFormatVersion', '2011-06-23'): cfn_t.CfnTemplate
249
 
        }
250
 
        temp_copy = copy.deepcopy(template._template_classes)
251
 
        template._template_classes = versions
252
 
 
253
 
        init_ex = self.assertRaises(exception.InvalidTemplateVersion,
254
 
                                    parser.Template, invalid_aws_version_tmp)
255
 
        ex_error_msg = ('The template version is invalid: '
256
 
                        '"AWSTemplateFormatVersion: 2012-12-12". '
257
 
                        '"AWSTemplateFormatVersion" should be '
258
 
                        'one of: 2010-09-09, 2011-06-23')
259
 
        self.assertEqual(ex_error_msg, six.text_type(init_ex))
260
 
        template._template_classes = temp_copy
261
 
 
262
 
    def test_invalid_heat_version(self):
263
 
        invalid_heat_version_tmp = template_format.parse(
264
 
            '''{
265
 
            "HeatTemplateFormatVersion" : "2010-09-09",
266
 
            }''')
267
 
        init_ex = self.assertRaises(exception.InvalidTemplateVersion,
268
 
                                    parser.Template, invalid_heat_version_tmp)
269
 
        ex_error_msg = ('The template version is invalid: '
270
 
                        '"HeatTemplateFormatVersion: 2010-09-09". '
271
 
                        '"HeatTemplateFormatVersion" should be: 2012-12-12')
272
 
        self.assertEqual(ex_error_msg, six.text_type(init_ex))
273
 
 
274
 
    def test_invalid_version_not_in_heat_versions(self):
275
 
        invalid_heat_version_tmp = template_format.parse(
276
 
            '''{
277
 
            "HeatTemplateFormatVersion" : "2010-09-09",
278
 
            }''')
279
 
        versions = {
280
 
            ('HeatTemplateFormatVersion', '2012-12-12'): cfn_t.CfnTemplate,
281
 
            ('HeatTemplateFormatVersion', '2014-12-12'): cfn_t.CfnTemplate
282
 
        }
283
 
        temp_copy = copy.deepcopy(template._template_classes)
284
 
        template._template_classes = versions
285
 
 
286
 
        init_ex = self.assertRaises(exception.InvalidTemplateVersion,
287
 
                                    parser.Template, invalid_heat_version_tmp)
288
 
        ex_error_msg = ('The template version is invalid: '
289
 
                        '"HeatTemplateFormatVersion: 2010-09-09". '
290
 
                        '"HeatTemplateFormatVersion" should be '
291
 
                        'one of: 2012-12-12, 2014-12-12')
292
 
        self.assertEqual(ex_error_msg, six.text_type(init_ex))
293
 
 
294
 
        template._template_classes = temp_copy
295
 
 
296
 
    def test_invalid_template(self):
297
 
        scanner_error = '''
298
 
1
299
 
Mappings:
300
 
  ValidMapping:
301
 
    TestKey: TestValue
302
 
'''
303
 
        parser_error = '''
304
 
Mappings:
305
 
  ValidMapping:
306
 
    TestKey: {TestKey1: "Value1" TestKey2: "Value2"}
307
 
'''
308
 
 
309
 
        self.assertRaises(ValueError, template_format.parse, scanner_error)
310
 
        self.assertRaises(ValueError, template_format.parse, parser_error)
311
 
 
312
 
    def test_invalid_section(self):
313
 
        tmpl = parser.Template({'HeatTemplateFormatVersion': '2012-12-12',
314
 
                                'Foo': ['Bar']})
315
 
        self.assertNotIn('Foo', tmpl)
316
 
 
317
 
    def test_find_in_map(self):
318
 
        tmpl = parser.Template(mapping_template)
319
 
        stack = parser.Stack(self.ctx, 'test', tmpl)
320
 
        find = {'Fn::FindInMap': ["ValidMapping", "TestKey", "TestValue"]}
321
 
        self.assertEqual("wibble", self.resolve(find, tmpl, stack))
322
 
 
323
 
    def test_find_in_invalid_map(self):
324
 
        tmpl = parser.Template(mapping_template)
325
 
        stack = parser.Stack(self.ctx, 'test', tmpl)
326
 
        finds = ({'Fn::FindInMap': ["InvalidMapping", "ValueList", "foo"]},
327
 
                 {'Fn::FindInMap': ["InvalidMapping", "ValueString", "baz"]},
328
 
                 {'Fn::FindInMap': ["MapList", "foo", "bar"]},
329
 
                 {'Fn::FindInMap': ["MapString", "foo", "bar"]})
330
 
 
331
 
        for find in finds:
332
 
            self.assertRaises((KeyError, TypeError), self.resolve,
333
 
                              find, tmpl, stack)
334
 
 
335
 
    def test_bad_find_in_map(self):
336
 
        tmpl = parser.Template(mapping_template)
337
 
        stack = parser.Stack(self.ctx, 'test', tmpl)
338
 
        finds = ({'Fn::FindInMap': "String"},
339
 
                 {'Fn::FindInMap': {"Dict": "String"}},
340
 
                 {'Fn::FindInMap': ["ShortList", "foo"]},
341
 
                 {'Fn::FindInMap': ["ReallyShortList"]})
342
 
 
343
 
        for find in finds:
344
 
            self.assertRaises(KeyError, self.resolve, find, tmpl, stack)
345
 
 
346
 
    def test_param_refs(self):
347
 
        tmpl = parser.Template(parameter_template)
348
 
        env = environment.Environment({'foo': 'bar', 'blarg': 'wibble'})
349
 
        stack = parser.Stack(self.ctx, 'test', tmpl, env)
350
 
        p_snippet = {"Ref": "foo"}
351
 
        self.assertEqual("bar", self.resolve(p_snippet, tmpl, stack))
352
 
 
353
 
    def test_param_ref_missing(self):
354
 
        tmpl = parser.Template(parameter_template)
355
 
        env = environment.Environment({'foo': 'bar'})
356
 
        stack = parser.Stack(self.ctx, 'test', tmpl, env)
357
 
        stack.env = environment.Environment({})
358
 
        stack.parameters = parameters.Parameters(stack.identifier(), tmpl)
359
 
        snippet = {"Ref": "foo"}
360
 
        self.assertRaises(exception.UserParameterMissing,
361
 
                          self.resolve,
362
 
                          snippet, tmpl, stack)
363
 
 
364
 
    def test_resource_refs(self):
365
 
        tmpl = parser.Template(resource_template)
366
 
        stack = parser.Stack(self.ctx, 'test', tmpl)
367
 
 
368
 
        self.m.StubOutWithMock(stack['foo'], 'FnGetRefId')
369
 
        stack['foo'].FnGetRefId().MultipleTimes().AndReturn('bar')
370
 
        self.m.ReplayAll()
371
 
 
372
 
        r_snippet = {"Ref": "foo"}
373
 
        self.assertEqual("bar", self.resolve(r_snippet, tmpl, stack))
374
 
        self.m.VerifyAll()
375
 
 
376
 
    def test_resource_refs_param(self):
377
 
        tmpl = parser.Template(resource_template)
378
 
        stack = parser.Stack(self.ctx, 'test', tmpl)
379
 
 
380
 
        p_snippet = {"Ref": "baz"}
381
 
        parsed = tmpl.parse(stack, p_snippet)
382
 
        self.assertTrue(isinstance(parsed, cfn_funcs.ParamRef))
383
 
 
384
 
    def test_select_from_list(self):
385
 
        tmpl = parser.Template(empty_template)
386
 
        data = {"Fn::Select": ["1", ["foo", "bar"]]}
387
 
        self.assertEqual("bar", self.resolve(data, tmpl))
388
 
 
389
 
    def test_select_from_list_integer_index(self):
390
 
        tmpl = parser.Template(empty_template)
391
 
        data = {"Fn::Select": [1, ["foo", "bar"]]}
392
 
        self.assertEqual("bar", self.resolve(data, tmpl))
393
 
 
394
 
    def test_select_from_list_out_of_bound(self):
395
 
        tmpl = parser.Template(empty_template)
396
 
        data = {"Fn::Select": ["0", ["foo", "bar"]]}
397
 
        self.assertEqual("foo", self.resolve(data, tmpl))
398
 
        data = {"Fn::Select": ["1", ["foo", "bar"]]}
399
 
        self.assertEqual("bar", self.resolve(data, tmpl))
400
 
        data = {"Fn::Select": ["2", ["foo", "bar"]]}
401
 
        self.assertEqual("", self.resolve(data, tmpl))
402
 
 
403
 
    def test_select_from_dict(self):
404
 
        tmpl = parser.Template(empty_template)
405
 
        data = {"Fn::Select": ["red", {"red": "robin", "re": "foo"}]}
406
 
        self.assertEqual("robin", self.resolve(data, tmpl))
407
 
 
408
 
    def test_select_from_none(self):
409
 
        tmpl = parser.Template(empty_template)
410
 
        data = {"Fn::Select": ["red", None]}
411
 
        self.assertEqual("", self.resolve(data, tmpl))
412
 
 
413
 
    def test_select_from_dict_not_existing(self):
414
 
        tmpl = parser.Template(empty_template)
415
 
        data = {"Fn::Select": ["green", {"red": "robin", "re": "foo"}]}
416
 
        self.assertEqual("", self.resolve(data, tmpl))
417
 
 
418
 
    def test_select_from_serialized_json_map(self):
419
 
        tmpl = parser.Template(empty_template)
420
 
        js = json.dumps({"red": "robin", "re": "foo"})
421
 
        data = {"Fn::Select": ["re", js]}
422
 
        self.assertEqual("foo", self.resolve(data, tmpl))
423
 
 
424
 
    def test_select_from_serialized_json_list(self):
425
 
        tmpl = parser.Template(empty_template)
426
 
        js = json.dumps(["foo", "fee", "fum"])
427
 
        data = {"Fn::Select": ["0", js]}
428
 
        self.assertEqual("foo", self.resolve(data, tmpl))
429
 
 
430
 
    def test_select_empty_string(self):
431
 
        tmpl = parser.Template(empty_template)
432
 
        data = {"Fn::Select": ["0", '']}
433
 
        self.assertEqual("", self.resolve(data, tmpl))
434
 
        data = {"Fn::Select": ["1", '']}
435
 
        self.assertEqual("", self.resolve(data, tmpl))
436
 
        data = {"Fn::Select": ["one", '']}
437
 
        self.assertEqual("", self.resolve(data, tmpl))
438
 
 
439
 
    def test_join(self):
440
 
        tmpl = parser.Template(empty_template)
441
 
        join = {"Fn::Join": [" ", ["foo", "bar"]]}
442
 
        self.assertEqual("foo bar", self.resolve(join, tmpl))
443
 
 
444
 
    def test_split_ok(self):
445
 
        tmpl = parser.Template(empty_template)
446
 
        data = {"Fn::Split": [";", "foo; bar; achoo"]}
447
 
        self.assertEqual(['foo', ' bar', ' achoo'], self.resolve(data, tmpl))
448
 
 
449
 
    def test_split_no_delim_in_str(self):
450
 
        tmpl = parser.Template(empty_template)
451
 
        data = {"Fn::Split": [";", "foo, bar, achoo"]}
452
 
        self.assertEqual(['foo, bar, achoo'], self.resolve(data, tmpl))
453
 
 
454
 
    def test_base64(self):
455
 
        tmpl = parser.Template(empty_template)
456
 
        snippet = {"Fn::Base64": "foobar"}
457
 
        # For now, the Base64 function just returns the original text, and
458
 
        # does not convert to base64 (see issue #133)
459
 
        self.assertEqual("foobar", self.resolve(snippet, tmpl))
460
 
 
461
 
    def test_get_azs(self):
462
 
        tmpl = parser.Template(empty_template)
463
 
        snippet = {"Fn::GetAZs": ""}
464
 
        self.assertEqual(["nova"], self.resolve(snippet, tmpl))
465
 
 
466
 
    def test_get_azs_with_stack(self):
467
 
        tmpl = parser.Template(empty_template)
468
 
        snippet = {"Fn::GetAZs": ""}
469
 
        stack = parser.Stack(self.ctx, 'test_stack',
470
 
                             parser.Template(empty_template))
471
 
        self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
472
 
        fc = fakes_v1_1.FakeClient()
473
 
        nova.NovaClientPlugin._create().AndReturn(fc)
474
 
        self.m.ReplayAll()
475
 
        self.assertEqual(["nova1"], self.resolve(snippet, tmpl, stack))
476
 
 
477
 
    def test_replace_string_values(self):
478
 
        tmpl = parser.Template(empty_template)
479
 
        snippet = {"Fn::Replace": [
480
 
            {'$var1': 'foo', '%var2%': 'bar'},
481
 
            '$var1 is %var2%'
482
 
        ]}
483
 
        self.assertEqual('foo is bar', self.resolve(snippet, tmpl))
484
 
 
485
 
    def test_replace_number_values(self):
486
 
        tmpl = parser.Template(empty_template)
487
 
        snippet = {"Fn::Replace": [
488
 
            {'$var1': 1, '%var2%': 2},
489
 
            '$var1 is not %var2%'
490
 
        ]}
491
 
        self.assertEqual('1 is not 2', self.resolve(snippet, tmpl))
492
 
 
493
 
        snippet = {"Fn::Replace": [
494
 
            {'$var1': 1.3, '%var2%': 2.5},
495
 
            '$var1 is not %var2%'
496
 
        ]}
497
 
        self.assertEqual('1.3 is not 2.5', self.resolve(snippet, tmpl))
498
 
 
499
 
    def test_replace_none_values(self):
500
 
        tmpl = parser.Template(empty_template)
501
 
        snippet = {"Fn::Replace": [
502
 
            {'$var1': None, '${var2}': None},
503
 
            '"$var1" is "${var2}"'
504
 
        ]}
505
 
        self.assertEqual('"" is ""', self.resolve(snippet, tmpl))
506
 
 
507
 
    def test_replace_missing_key(self):
508
 
        tmpl = parser.Template(empty_template)
509
 
        snippet = {"Fn::Replace": [
510
 
            {'$var1': 'foo', 'var2': 'bar'},
511
 
            '"$var1" is "${var3}"'
512
 
        ]}
513
 
        self.assertEqual('"foo" is "${var3}"', self.resolve(snippet, tmpl))
514
 
 
515
 
    def test_replace_param_values(self):
516
 
        tmpl = parser.Template(parameter_template)
517
 
        env = environment.Environment({'foo': 'wibble'})
518
 
        stack = parser.Stack(self.ctx, 'test_stack', tmpl, env)
519
 
        snippet = {"Fn::Replace": [
520
 
            {'$var1': {'Ref': 'foo'}, '%var2%': {'Ref': 'blarg'}},
521
 
            '$var1 is %var2%'
522
 
        ]}
523
 
        self.assertEqual('wibble is quux', self.resolve(snippet, tmpl, stack))
524
 
 
525
 
    def test_member_list2map_good(self):
526
 
        tmpl = parser.Template(empty_template)
527
 
        snippet = {"Fn::MemberListToMap": [
528
 
            'Name', 'Value', ['.member.0.Name=metric',
529
 
                              '.member.0.Value=cpu',
530
 
                              '.member.1.Name=size',
531
 
                              '.member.1.Value=56']]}
532
 
        self.assertEqual({'metric': 'cpu', 'size': '56'},
533
 
                         self.resolve(snippet, tmpl))
534
 
 
535
 
    def test_member_list2map_good2(self):
536
 
        tmpl = parser.Template(empty_template)
537
 
        snippet = {"Fn::MemberListToMap": [
538
 
            'Key', 'Value', ['.member.2.Key=metric',
539
 
                             '.member.2.Value=cpu',
540
 
                             '.member.5.Key=size',
541
 
                             '.member.5.Value=56']]}
542
 
        self.assertEqual({'metric': 'cpu', 'size': '56'},
543
 
                         self.resolve(snippet, tmpl))
544
 
 
545
 
    def test_resource_facade(self):
546
 
        metadata_snippet = {'Fn::ResourceFacade': 'Metadata'}
547
 
        deletion_policy_snippet = {'Fn::ResourceFacade': 'DeletionPolicy'}
548
 
        update_policy_snippet = {'Fn::ResourceFacade': 'UpdatePolicy'}
549
 
 
550
 
        parent_resource = DummyClass()
551
 
        parent_resource.metadata_set({"foo": "bar"})
552
 
 
553
 
        parent_resource.t = rsrc_defn.ResourceDefinition(
554
 
            'parent', 'SomeType',
555
 
            deletion_policy=rsrc_defn.ResourceDefinition.RETAIN,
556
 
            update_policy={"blarg": "wibble"})
557
 
 
558
 
        parent_resource.stack = parser.Stack(self.ctx, 'toplevel_stack',
559
 
                                             parser.Template(empty_template))
560
 
        stack = parser.Stack(self.ctx, 'test_stack',
561
 
                             parser.Template(empty_template),
562
 
                             parent_resource=parent_resource)
563
 
        self.assertEqual({"foo": "bar"},
564
 
                         self.resolve(metadata_snippet, stack.t, stack))
565
 
        self.assertEqual('Retain',
566
 
                         self.resolve(deletion_policy_snippet, stack.t, stack))
567
 
        self.assertEqual({"blarg": "wibble"},
568
 
                         self.resolve(update_policy_snippet, stack.t, stack))
569
 
 
570
 
    def test_resource_facade_function(self):
571
 
        deletion_policy_snippet = {'Fn::ResourceFacade': 'DeletionPolicy'}
572
 
 
573
 
        parent_resource = DummyClass()
574
 
        parent_resource.metadata_set({"foo": "bar"})
575
 
        parent_resource.stack = parser.Stack(self.ctx, 'toplevel_stack',
576
 
                                             parser.Template(empty_template))
577
 
        del_policy = cfn_funcs.Join(parent_resource.stack,
578
 
                                    'Fn::Join', ['eta', ['R', 'in']])
579
 
        parent_resource.t = rsrc_defn.ResourceDefinition(
580
 
            'parent', 'SomeType',
581
 
            deletion_policy=del_policy)
582
 
 
583
 
        stack = parser.Stack(self.ctx, 'test_stack',
584
 
                             parser.Template(empty_template),
585
 
                             parent_resource=parent_resource)
586
 
        self.assertEqual('Retain',
587
 
                         self.resolve(deletion_policy_snippet, stack.t, stack))
588
 
 
589
 
    def test_resource_facade_invalid_arg(self):
590
 
        snippet = {'Fn::ResourceFacade': 'wibble'}
591
 
        stack = parser.Stack(self.ctx, 'test_stack',
592
 
                             parser.Template(empty_template))
593
 
        error = self.assertRaises(ValueError,
594
 
                                  self.resolve,
595
 
                                  snippet,
596
 
                                  stack.t, stack)
597
 
        self.assertIn(snippet.keys()[0], six.text_type(error))
598
 
 
599
 
    def test_resource_facade_missing_deletion_policy(self):
600
 
        snippet = {'Fn::ResourceFacade': 'DeletionPolicy'}
601
 
 
602
 
        parent_resource = DummyClass()
603
 
        parent_resource.metadata_set({"foo": "bar"})
604
 
        parent_resource.t = rsrc_defn.ResourceDefinition('parent', 'SomeType')
605
 
 
606
 
        parent_resource.stack = parser.Stack(self.ctx, 'toplevel_stack',
607
 
                                             parser.Template(empty_template))
608
 
        stack = parser.Stack(self.ctx, 'test_stack',
609
 
                             parser.Template(empty_template),
610
 
                             parent_resource=parent_resource)
611
 
        self.assertEqual('Delete', self.resolve(snippet, stack.t, stack))
612
 
 
613
 
    def test_prevent_parameters_access(self):
614
 
        expected_description = "This can be accessed"
615
 
        tmpl = parser.Template({'AWSTemplateFormatVersion': '2010-09-09',
616
 
                                'Description': expected_description,
617
 
                                'Parameters':
618
 
                                {'foo': {'Type': 'String', 'Required': True}}})
619
 
        self.assertEqual(expected_description, tmpl['Description'])
620
 
        keyError = self.assertRaises(KeyError, tmpl.__getitem__, 'Parameters')
621
 
        self.assertIn("can not be accessed directly", six.text_type(keyError))
622
 
 
623
 
    def test_parameters_section_not_iterable(self):
624
 
        expected_description = "This can be accessed"
625
 
        tmpl = parser.Template({'AWSTemplateFormatVersion': '2010-09-09',
626
 
                                'Description': expected_description,
627
 
                                'Parameters':
628
 
                                {'foo': {'Type': 'String', 'Required': True}}})
629
 
        self.assertEqual(expected_description, tmpl['Description'])
630
 
        self.assertNotIn('Parameters', tmpl.keys())
631
 
 
632
 
    def test_add_resource(self):
633
 
        cfn_tpl = template_format.parse('''
634
 
        AWSTemplateFormatVersion: 2010-09-09
635
 
        Resources:
636
 
          resource1:
637
 
            Type: AWS::EC2::Instance
638
 
            Properties:
639
 
              property1: value1
640
 
            Metadata:
641
 
              foo: bar
642
 
            DependsOn: dummy
643
 
            DeletionPolicy: Retain
644
 
            UpdatePolicy:
645
 
              foo: bar
646
 
        ''')
647
 
        source = parser.Template(cfn_tpl)
648
 
        empty = parser.Template(copy.deepcopy(empty_template))
649
 
        stack = parser.Stack(self.ctx, 'test_stack', source)
650
 
 
651
 
        for defn in source.resource_definitions(stack).values():
652
 
            empty.add_resource(defn)
653
 
 
654
 
        self.assertEqual(cfn_tpl['Resources'], empty.t['Resources'])
655
 
 
656
 
 
657
 
class TemplateFnErrorTest(common.HeatTestCase):
658
 
    scenarios = [
659
 
        ('select_from_list_not_int',
660
 
         dict(expect=TypeError,
661
 
              snippet={"Fn::Select": ["one", ["foo", "bar"]]})),
662
 
        ('select_from_dict_not_str',
663
 
         dict(expect=TypeError,
664
 
              snippet={"Fn::Select": ["1", {"red": "robin", "re": "foo"}]})),
665
 
        ('select_from_serialized_json_wrong',
666
 
         dict(expect=ValueError,
667
 
              snippet={"Fn::Select": ["not", "no json"]})),
668
 
        ('select_wrong_num_args_1',
669
 
         dict(expect=ValueError,
670
 
              snippet={"Fn::Select": []})),
671
 
        ('select_wrong_num_args_2',
672
 
         dict(expect=ValueError,
673
 
              snippet={"Fn::Select": ["4"]})),
674
 
        ('select_wrong_num_args_3',
675
 
         dict(expect=ValueError,
676
 
              snippet={"Fn::Select": ["foo", {"foo": "bar"}, ""]})),
677
 
        ('select_wrong_num_args_4',
678
 
         dict(expect=TypeError,
679
 
              snippet={'Fn::Select': [['f'], {'f': 'food'}]})),
680
 
        ('split_no_delim',
681
 
         dict(expect=ValueError,
682
 
              snippet={"Fn::Split": ["foo, bar, achoo"]})),
683
 
        ('split_no_list',
684
 
         dict(expect=TypeError,
685
 
              snippet={"Fn::Split": "foo, bar, achoo"})),
686
 
        ('base64_list',
687
 
         dict(expect=TypeError,
688
 
              snippet={"Fn::Base64": ["foobar"]})),
689
 
        ('base64_dict',
690
 
         dict(expect=TypeError,
691
 
              snippet={"Fn::Base64": {"foo": "bar"}})),
692
 
        ('replace_list_value',
693
 
         dict(expect=TypeError,
694
 
              snippet={"Fn::Replace": [
695
 
                  {'$var1': 'foo', '%var2%': ['bar']},
696
 
                  '$var1 is %var2%']})),
697
 
        ('replace_list_mapping',
698
 
         dict(expect=TypeError,
699
 
              snippet={"Fn::Replace": [
700
 
                  ['var1', 'foo', 'var2', 'bar'],
701
 
                  '$var1 is ${var2}']})),
702
 
        ('replace_dict',
703
 
         dict(expect=TypeError,
704
 
              snippet={"Fn::Replace": {}})),
705
 
        ('replace_missing_template',
706
 
         dict(expect=ValueError,
707
 
              snippet={"Fn::Replace": [['var1', 'foo', 'var2', 'bar']]})),
708
 
        ('replace_none_template',
709
 
         dict(expect=TypeError,
710
 
              snippet={"Fn::Replace": [['var2', 'bar'], None]})),
711
 
        ('replace_list_string',
712
 
         dict(expect=TypeError,
713
 
              snippet={"Fn::Replace": [
714
 
                  {'var1': 'foo', 'var2': 'bar'},
715
 
                  ['$var1 is ${var2}']]})),
716
 
        ('join_string',
717
 
         dict(expect=TypeError,
718
 
              snippet={"Fn::Join": [" ", "foo"]})),
719
 
        ('join_dict',
720
 
         dict(expect=TypeError,
721
 
              snippet={"Fn::Join": [" ", {"foo": "bar"}]})),
722
 
        ('join_wrong_num_args_1',
723
 
         dict(expect=ValueError,
724
 
              snippet={"Fn::Join": []})),
725
 
        ('join_wrong_num_args_2',
726
 
         dict(expect=ValueError,
727
 
              snippet={"Fn::Join": [" "]})),
728
 
        ('join_wrong_num_args_3',
729
 
         dict(expect=ValueError,
730
 
              snippet={"Fn::Join": [" ", {"foo": "bar"}, ""]})),
731
 
        ('join_string_nodelim',
732
 
         dict(expect=TypeError,
733
 
              snippet={"Fn::Join": "o"})),
734
 
        ('join_string_nodelim_1',
735
 
         dict(expect=TypeError,
736
 
              snippet={"Fn::Join": "oh"})),
737
 
        ('join_string_nodelim_2',
738
 
         dict(expect=TypeError,
739
 
              snippet={"Fn::Join": "ohh"})),
740
 
        ('join_dict_nodelim1',
741
 
         dict(expect=TypeError,
742
 
              snippet={"Fn::Join": {"foo": "bar"}})),
743
 
        ('join_dict_nodelim2',
744
 
         dict(expect=TypeError,
745
 
              snippet={"Fn::Join": {"foo": "bar", "blarg": "wibble"}})),
746
 
        ('join_dict_nodelim3',
747
 
         dict(expect=TypeError,
748
 
              snippet={"Fn::Join": {"foo": "bar", "blarg": "wibble",
749
 
                                    "baz": "quux"}})),
750
 
        ('member_list2map_no_key_or_val',
751
 
         dict(expect=TypeError,
752
 
              snippet={"Fn::MemberListToMap": [
753
 
                  'Key', ['.member.2.Key=metric',
754
 
                          '.member.2.Value=cpu',
755
 
                          '.member.5.Key=size',
756
 
                          '.member.5.Value=56']]})),
757
 
        ('member_list2map_no_list',
758
 
         dict(expect=TypeError,
759
 
              snippet={"Fn::MemberListToMap": [
760
 
                  'Key', '.member.2.Key=metric']})),
761
 
        ('member_list2map_not_string',
762
 
         dict(expect=TypeError,
763
 
              snippet={"Fn::MemberListToMap": [
764
 
                  'Name', ['Value'], ['.member.0.Name=metric',
765
 
                                      '.member.0.Value=cpu',
766
 
                                      '.member.1.Name=size',
767
 
                                      '.member.1.Value=56']]})),
768
 
    ]
769
 
 
770
 
    def test_bad_input(self):
771
 
        tmpl = parser.Template(empty_template)
772
 
        resolve = lambda s: TemplateTest.resolve(s, tmpl)
773
 
        error = self.assertRaises(self.expect,
774
 
                                  resolve,
775
 
                                  self.snippet)
776
 
        self.assertIn(self.snippet.keys()[0], six.text_type(error))
777
 
 
778
 
 
779
 
class ResolveDataTest(common.HeatTestCase):
780
 
 
781
 
    def setUp(self):
782
 
        super(ResolveDataTest, self).setUp()
783
 
        self.username = 'parser_stack_test_user'
784
 
 
785
 
        self.ctx = utils.dummy_context()
786
 
 
787
 
        self.stack = parser.Stack(self.ctx, 'resolve_test_stack',
788
 
                                  template.Template(empty_template),
789
 
                                  environment.Environment({}))
790
 
 
791
 
    def resolve(self, snippet):
792
 
        return function.resolve(self.stack.t.parse(self.stack, snippet))
793
 
 
794
 
    def test_stack_resolve_runtime_data_deprecated(self):
795
 
        stack = parser.Stack(self.ctx, 'test_stack',
796
 
                             parser.Template(empty_template),
797
 
                             tenant_id='bar')
798
 
 
799
 
        with warnings.catch_warnings(record=True) as ws:
800
 
            warnings.filterwarnings('always')
801
 
 
802
 
            # Work around http://bugs.python.org/issue4180
803
 
            getattr(parser, '__warningregistry__', {}).clear()
804
 
 
805
 
            test_data = {'foo': 'bar'}
806
 
            resolved = stack.resolve_runtime_data(test_data)
807
 
 
808
 
            self.assertTrue(ws)
809
 
            self.assertTrue(issubclass(ws[0].category, DeprecationWarning))
810
 
 
811
 
            self.assertEqual(test_data, resolved)
812
 
 
813
 
    def test_join_split(self):
814
 
        # join
815
 
        snippet = {'Fn::Join': [';', ['one', 'two', 'three']]}
816
 
        self.assertEqual('one;two;three',
817
 
                         self.resolve(snippet))
818
 
 
819
 
        # join then split
820
 
        snippet = {'Fn::Split': [';', snippet]}
821
 
        self.assertEqual(['one', 'two', 'three'],
822
 
                         self.resolve(snippet))
823
 
 
824
 
    def test_split_join_split_join(self):
825
 
        # each snippet in this test encapsulates
826
 
        # the snippet from the previous step, leading
827
 
        # to increasingly nested function calls
828
 
 
829
 
        # split
830
 
        snippet = {'Fn::Split': [',', 'one,two,three']}
831
 
        self.assertEqual(['one', 'two', 'three'],
832
 
                         self.resolve(snippet))
833
 
 
834
 
        # split then join
835
 
        snippet = {'Fn::Join': [';', snippet]}
836
 
        self.assertEqual('one;two;three',
837
 
                         self.resolve(snippet))
838
 
 
839
 
        # split then join then split
840
 
        snippet = {'Fn::Split': [';', snippet]}
841
 
        self.assertEqual(['one', 'two', 'three'],
842
 
                         self.resolve(snippet))
843
 
 
844
 
        # split then join then split then join
845
 
        snippet = {'Fn::Join': ['-', snippet]}
846
 
        self.assertEqual('one-two-three',
847
 
                         self.resolve(snippet))
848
 
 
849
 
    def test_join_recursive(self):
850
 
        raw = {'Fn::Join': ['\n', [{'Fn::Join':
851
 
                                   [' ', ['foo', 'bar']]}, 'baz']]}
852
 
        self.assertEqual('foo bar\nbaz', self.resolve(raw))
853
 
 
854
 
    def test_join_not_string(self):
855
 
        snippet = {'Fn::Join': ['\n', [{'Fn::Join':
856
 
                                        [' ', ['foo', 45]]}, 'baz']]}
857
 
        error = self.assertRaises(TypeError,
858
 
                                  self.resolve,
859
 
                                  snippet)
860
 
        self.assertIn('45', six.text_type(error))
861
 
 
862
 
    def test_base64_replace(self):
863
 
        raw = {'Fn::Base64': {'Fn::Replace': [
864
 
            {'foo': 'bar'}, 'Meet at the foo']}}
865
 
        self.assertEqual('Meet at the bar',
866
 
                         self.resolve(raw))
867
 
 
868
 
    def test_replace_base64(self):
869
 
        raw = {'Fn::Replace': [{'foo': 'bar'}, {
870
 
            'Fn::Base64': 'Meet at the foo'}]}
871
 
        self.assertEqual('Meet at the bar',
872
 
                         self.resolve(raw))
873
 
 
874
 
    def test_nested_selects(self):
875
 
        data = {
876
 
            'a': ['one', 'two', 'three'],
877
 
            'b': ['een', 'twee', {'d': 'D', 'e': 'E'}]
878
 
        }
879
 
        raw = {'Fn::Select': ['a', data]}
880
 
        self.assertEqual(data['a'],
881
 
                         self.resolve(raw))
882
 
 
883
 
        raw = {'Fn::Select': ['b', data]}
884
 
        self.assertEqual(data['b'],
885
 
                         self.resolve(raw))
886
 
 
887
 
        raw = {
888
 
            'Fn::Select': ['1', {
889
 
                'Fn::Select': ['b', data]}]}
890
 
        self.assertEqual('twee',
891
 
                         self.resolve(raw))
892
 
 
893
 
        raw = {
894
 
            'Fn::Select': ['e', {
895
 
                'Fn::Select': ['2', {
896
 
                    'Fn::Select': ['b', data]}]}]}
897
 
        self.assertEqual('E',
898
 
                         self.resolve(raw))
899
 
 
900
 
    def test_member_list_select(self):
901
 
        snippet = {'Fn::Select': ['metric', {"Fn::MemberListToMap": [
902
 
            'Name', 'Value', ['.member.0.Name=metric',
903
 
                              '.member.0.Value=cpu',
904
 
                              '.member.1.Name=size',
905
 
                              '.member.1.Value=56']]}]}
906
 
        self.assertEqual('cpu',
907
 
                         self.resolve(snippet))
908
 
 
909
 
 
910
 
class StackTest(common.HeatTestCase):
911
 
    def setUp(self):
912
 
        super(StackTest, self).setUp()
913
 
 
914
 
        self.username = 'parser_stack_test_user'
915
 
        self.tmpl = parser.Template(copy.deepcopy(empty_template))
916
 
 
917
 
        self.ctx = utils.dummy_context()
918
 
 
919
 
        resource._register_class('GenericResourceType',
920
 
                                 generic_rsrc.GenericResource)
921
 
        resource._register_class('ResourceWithPropsType',
922
 
                                 generic_rsrc.ResourceWithProps)
923
 
        resource._register_class('ResourceWithComplexAttributesType',
924
 
                                 generic_rsrc.ResourceWithComplexAttributes)
925
 
        resource._register_class('ResWithComplexPropsAndAttrs',
926
 
                                 generic_rsrc.ResWithComplexPropsAndAttrs)
927
 
 
928
 
    def test_stack_reads_tenant(self):
929
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
930
 
                             tenant_id='bar')
931
 
        self.assertEqual('bar', stack.tenant_id)
932
 
 
933
 
    def test_stack_reads_tenant_from_context_if_empty(self):
934
 
        self.ctx.tenant_id = 'foo'
935
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
936
 
                             tenant_id=None)
937
 
        self.assertEqual('foo', stack.tenant_id)
938
 
 
939
 
    def test_stack_reads_username(self):
940
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
941
 
                             username='bar')
942
 
        self.assertEqual('bar', stack.username)
943
 
 
944
 
    def test_stack_reads_username_from_context_if_empty(self):
945
 
        self.ctx.username = 'foo'
946
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
947
 
                             username=None)
948
 
        self.assertEqual('foo', stack.username)
949
 
 
950
 
    def test_stack_string_repr(self):
951
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl)
952
 
        expected = 'Stack "%s" [%s]' % (stack.name, stack.id)
953
 
        observed = str(stack)
954
 
        self.assertEqual(expected, observed)
955
 
 
956
 
    def test_state_defaults(self):
957
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl)
958
 
        self.assertEqual(('CREATE', 'IN_PROGRESS'), stack.state)
959
 
        self.assertEqual('', stack.status_reason)
960
 
 
961
 
    def test_timeout_secs_default(self):
962
 
        cfg.CONF.set_override('stack_action_timeout', 1000)
963
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl)
964
 
        self.assertIsNone(stack.timeout_mins)
965
 
        self.assertEqual(1000, stack.timeout_secs())
966
 
 
967
 
    def test_timeout_secs(self):
968
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
969
 
                             timeout_mins=10)
970
 
        self.assertEqual(600, stack.timeout_secs())
971
 
 
972
 
    def test_no_auth_token(self):
973
 
        ctx = utils.dummy_context()
974
 
        ctx.auth_token = None
975
 
        self.stub_auth()
976
 
 
977
 
        self.m.ReplayAll()
978
 
        stack = parser.Stack(ctx, 'test_stack', self.tmpl)
979
 
        self.assertEqual('abcd1234',
980
 
                         stack.clients.client('keystone').auth_token)
981
 
 
982
 
        self.m.VerifyAll()
983
 
 
984
 
    def test_state(self):
985
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
986
 
                             action=parser.Stack.CREATE,
987
 
                             status=parser.Stack.IN_PROGRESS)
988
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.IN_PROGRESS),
989
 
                         stack.state)
990
 
        stack.state_set(parser.Stack.CREATE, parser.Stack.COMPLETE, 'test')
991
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
992
 
                         stack.state)
993
 
        stack.state_set(parser.Stack.DELETE, parser.Stack.COMPLETE, 'test')
994
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
995
 
                         stack.state)
996
 
 
997
 
    def test_state_deleted(self):
998
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
999
 
                             action=parser.Stack.CREATE,
1000
 
                             status=parser.Stack.IN_PROGRESS)
1001
 
        stack.id = '1234'
1002
 
 
1003
 
        # Simulate a deleted stack
1004
 
        self.m.StubOutWithMock(db_api, 'stack_get')
1005
 
        db_api.stack_get(stack.context, stack.id).AndReturn(None)
1006
 
 
1007
 
        self.m.ReplayAll()
1008
 
 
1009
 
        self.assertIsNone(stack.state_set(parser.Stack.CREATE,
1010
 
                                          parser.Stack.COMPLETE, 'test'))
1011
 
        self.m.VerifyAll()
1012
 
 
1013
 
    def test_state_bad(self):
1014
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
1015
 
                             action=parser.Stack.CREATE,
1016
 
                             status=parser.Stack.IN_PROGRESS)
1017
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.IN_PROGRESS),
1018
 
                         stack.state)
1019
 
        self.assertRaises(ValueError, stack.state_set,
1020
 
                          'baad', parser.Stack.COMPLETE, 'test')
1021
 
        self.assertRaises(ValueError, stack.state_set,
1022
 
                          parser.Stack.CREATE, 'oops', 'test')
1023
 
 
1024
 
    def test_status_reason(self):
1025
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
1026
 
                             status_reason='quux')
1027
 
        self.assertEqual('quux', stack.status_reason)
1028
 
        stack.state_set(parser.Stack.CREATE, parser.Stack.IN_PROGRESS,
1029
 
                        'wibble')
1030
 
        self.assertEqual('wibble', stack.status_reason)
1031
 
 
1032
 
    def test_load_nonexistant_id(self):
1033
 
        self.assertRaises(exception.NotFound, parser.Stack.load,
1034
 
                          None, -1)
1035
 
 
1036
 
    def test_total_resources_empty(self):
1037
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
1038
 
                             status_reason='flimflam')
1039
 
        self.assertEqual(0, stack.total_resources())
1040
 
 
1041
 
    def test_total_resources_generic(self):
1042
 
        tpl = {'HeatTemplateFormatVersion': '2012-12-12',
1043
 
               'Resources':
1044
 
               {'A': {'Type': 'GenericResourceType'}}}
1045
 
        stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl),
1046
 
                             status_reason='blarg')
1047
 
        self.assertEqual(1, stack.total_resources())
1048
 
 
1049
 
    def test_total_resources_nested(self):
1050
 
        tpl = {'HeatTemplateFormatVersion': '2012-12-12',
1051
 
               'Resources':
1052
 
               {'A': {'Type': 'GenericResourceType'}}}
1053
 
        stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl),
1054
 
                             status_reason='blarg')
1055
 
 
1056
 
        stack['A'].nested = mock.Mock()
1057
 
        stack['A'].nested.return_value.total_resources.return_value = 3
1058
 
        self.assertEqual(4, stack.total_resources())
1059
 
 
1060
 
    def test_iter_resources(self):
1061
 
        tpl = {'HeatTemplateFormatVersion': '2012-12-12',
1062
 
               'Resources':
1063
 
               {'A': {'Type': 'GenericResourceType'},
1064
 
                'B': {'Type': 'GenericResourceType'}}}
1065
 
        stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl),
1066
 
                             status_reason='blarg')
1067
 
 
1068
 
        def get_more(nested_depth=0):
1069
 
            yield 'X'
1070
 
            yield 'Y'
1071
 
            yield 'Z'
1072
 
 
1073
 
        stack['A'].nested = mock.MagicMock()
1074
 
        stack['A'].nested.return_value.iter_resources.side_effect = get_more
1075
 
 
1076
 
        resource_generator = stack.iter_resources()
1077
 
        self.assertIsNot(resource_generator, list)
1078
 
 
1079
 
        first_level_resources = list(resource_generator)
1080
 
        self.assertEqual(2, len(first_level_resources))
1081
 
        all_resources = list(stack.iter_resources(1))
1082
 
        self.assertEqual(5, len(all_resources))
1083
 
 
1084
 
    def test_root_stack_no_parent(self):
1085
 
        tpl = {'HeatTemplateFormatVersion': '2012-12-12',
1086
 
               'Resources':
1087
 
               {'A': {'Type': 'GenericResourceType'}}}
1088
 
        stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl),
1089
 
                             status_reason='blarg')
1090
 
 
1091
 
        self.assertEqual(stack, stack.root_stack)
1092
 
 
1093
 
    def test_root_stack_parent_no_stack(self):
1094
 
        tpl = {'HeatTemplateFormatVersion': '2012-12-12',
1095
 
               'Resources':
1096
 
               {'A': {'Type': 'GenericResourceType'}}}
1097
 
        stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl),
1098
 
                             status_reason='blarg')
1099
 
 
1100
 
        stack.parent_resource = mock.Mock()
1101
 
        stack.parent_resource.stack = None
1102
 
        self.assertEqual(stack, stack.root_stack)
1103
 
 
1104
 
    def test_root_stack_with_parent(self):
1105
 
        tpl = {'HeatTemplateFormatVersion': '2012-12-12',
1106
 
               'Resources':
1107
 
               {'A': {'Type': 'GenericResourceType'}}}
1108
 
        stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl),
1109
 
                             status_reason='blarg')
1110
 
 
1111
 
        stack.parent_resource = mock.Mock()
1112
 
        stack.parent_resource.stack.root_stack = 'test value'
1113
 
        self.assertEqual('test value', stack.root_stack)
1114
 
 
1115
 
    def test_load_parent_resource(self):
1116
 
        self.stack = parser.Stack(self.ctx, 'load_parent_resource',
1117
 
                                  self.tmpl)
1118
 
        self.stack.store()
1119
 
        stack = db_api.stack_get(self.ctx, self.stack.id)
1120
 
 
1121
 
        t = template.Template.load(self.ctx, stack.raw_template_id)
1122
 
        self.m.StubOutWithMock(template.Template, 'load')
1123
 
        template.Template.load(
1124
 
            self.ctx, stack.raw_template_id, stack.raw_template
1125
 
        ).AndReturn(t)
1126
 
 
1127
 
        env = environment.Environment(stack.parameters)
1128
 
        self.m.StubOutWithMock(environment, 'Environment')
1129
 
        environment.Environment(stack.parameters).AndReturn(env)
1130
 
 
1131
 
        self.m.StubOutWithMock(parser.Stack, '__init__')
1132
 
        parser.Stack.__init__(self.ctx, stack.name, t, env, stack.id,
1133
 
                              stack.action, stack.status, stack.status_reason,
1134
 
                              stack.timeout, True, stack.disable_rollback,
1135
 
                              'parent', owner_id=None,
1136
 
                              stack_user_project_id=None,
1137
 
                              created_time=mox.IgnoreArg(),
1138
 
                              updated_time=None,
1139
 
                              user_creds_id=stack.user_creds_id,
1140
 
                              tenant_id='test_tenant_id',
1141
 
                              use_stored_context=False,
1142
 
                              username=mox.IgnoreArg())
1143
 
 
1144
 
        self.m.ReplayAll()
1145
 
        parser.Stack.load(self.ctx, stack_id=self.stack.id,
1146
 
                          parent_resource='parent')
1147
 
 
1148
 
        self.m.VerifyAll()
1149
 
 
1150
 
    def test_identifier(self):
1151
 
        self.stack = parser.Stack(self.ctx, 'identifier_test',
1152
 
                                  self.tmpl)
1153
 
        self.stack.store()
1154
 
        identifier = self.stack.identifier()
1155
 
        self.assertEqual(self.stack.tenant_id, identifier.tenant)
1156
 
        self.assertEqual('identifier_test', identifier.stack_name)
1157
 
        self.assertTrue(identifier.stack_id)
1158
 
        self.assertFalse(identifier.path)
1159
 
 
1160
 
    def test_get_stack_abandon_data(self):
1161
 
        tpl = {'HeatTemplateFormatVersion': '2012-12-12',
1162
 
               'Parameters': {'param1': {'Type': 'String'}},
1163
 
               'Resources':
1164
 
               {'A': {'Type': 'GenericResourceType'},
1165
 
                'B': {'Type': 'GenericResourceType'}}}
1166
 
        resources = '''{"A": {"status": "COMPLETE", "name": "A",
1167
 
        "resource_data": {}, "resource_id": null, "action": "INIT",
1168
 
        "type": "GenericResourceType", "metadata": {}},
1169
 
        "B": {"status": "COMPLETE", "name": "B", "resource_data": {},
1170
 
        "resource_id": null, "action": "INIT", "type": "GenericResourceType",
1171
 
        "metadata": {}}}'''
1172
 
        env = environment.Environment({'parameters': {'param1': 'test'}})
1173
 
        self.stack = parser.Stack(self.ctx, 'stack_details_test',
1174
 
                                  parser.Template(tpl),
1175
 
                                  tenant_id='123',
1176
 
                                  stack_user_project_id='234',
1177
 
                                  env=env)
1178
 
        self.stack.store()
1179
 
        info = self.stack.prepare_abandon()
1180
 
        self.assertEqual('CREATE', info['action'])
1181
 
        self.assertIn('id', info)
1182
 
        self.assertEqual('stack_details_test', info['name'])
1183
 
        self.assertEqual(json.loads(resources), info['resources'])
1184
 
        self.assertEqual('IN_PROGRESS', info['status'])
1185
 
        self.assertEqual(tpl, info['template'])
1186
 
        self.assertEqual('123', info['project_id'])
1187
 
        self.assertEqual('234', info['stack_user_project_id'])
1188
 
        self.assertEqual(env.params, info['environment']['parameters'])
1189
 
 
1190
 
    def test_set_param_id(self):
1191
 
        self.stack = parser.Stack(self.ctx, 'param_arn_test',
1192
 
                                  self.tmpl)
1193
 
        exp_prefix = ('arn:openstack:heat::test_tenant_id'
1194
 
                      ':stacks/param_arn_test/')
1195
 
        self.assertEqual(self.stack.parameters['AWS::StackId'],
1196
 
                         exp_prefix + 'None')
1197
 
        self.stack.store()
1198
 
        identifier = self.stack.identifier()
1199
 
        self.assertEqual(exp_prefix + self.stack.id,
1200
 
                         self.stack.parameters['AWS::StackId'])
1201
 
        self.assertEqual(self.stack.parameters['AWS::StackId'],
1202
 
                         identifier.arn())
1203
 
        self.m.VerifyAll()
1204
 
 
1205
 
    def test_set_param_id_update(self):
1206
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1207
 
                'Resources': {
1208
 
                    'AResource': {'Type': 'ResourceWithPropsType',
1209
 
                                  'Metadata': {'Bar': {'Ref': 'AWS::StackId'}},
1210
 
                                  'Properties': {'Foo': 'abc'}}}}
1211
 
 
1212
 
        self.stack = parser.Stack(self.ctx, 'update_stack_arn_test',
1213
 
                                  template.Template(tmpl))
1214
 
        self.stack.store()
1215
 
        self.stack.create()
1216
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
1217
 
                         self.stack.state)
1218
 
 
1219
 
        stack_arn = self.stack.parameters['AWS::StackId']
1220
 
 
1221
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
1222
 
                 'Resources': {
1223
 
                     'AResource': {'Type': 'ResourceWithPropsType',
1224
 
                                   'Metadata': {'Bar':
1225
 
                                                {'Ref': 'AWS::StackId'}},
1226
 
                                   'Properties': {'Foo': 'xyz'}}}}
1227
 
 
1228
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
1229
 
                                     template.Template(tmpl2))
1230
 
 
1231
 
        self.stack.update(updated_stack)
1232
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
1233
 
                         self.stack.state)
1234
 
        self.assertEqual('xyz', self.stack['AResource'].properties['Foo'])
1235
 
 
1236
 
        self.assertEqual(
1237
 
            stack_arn, self.stack['AResource'].metadata_get()['Bar'])
1238
 
 
1239
 
    def test_load_param_id(self):
1240
 
        self.stack = parser.Stack(self.ctx, 'param_load_arn_test',
1241
 
                                  self.tmpl)
1242
 
        self.stack.store()
1243
 
        identifier = self.stack.identifier()
1244
 
        self.assertEqual(self.stack.parameters['AWS::StackId'],
1245
 
                         identifier.arn())
1246
 
 
1247
 
        newstack = parser.Stack.load(self.ctx, stack_id=self.stack.id)
1248
 
        self.assertEqual(identifier.arn(), newstack.parameters['AWS::StackId'])
1249
 
 
1250
 
    def test_load_reads_tenant_id(self):
1251
 
        self.ctx.tenant_id = 'foobar'
1252
 
        self.stack = parser.Stack(self.ctx, 'stack_name', self.tmpl)
1253
 
        self.stack.store()
1254
 
        stack_id = self.stack.id
1255
 
        self.ctx.tenant_id = None
1256
 
        stack = parser.Stack.load(self.ctx, stack_id=stack_id)
1257
 
        self.assertEqual('foobar', stack.tenant_id)
1258
 
 
1259
 
    def test_load_reads_username_from_db(self):
1260
 
        self.ctx.username = 'foobar'
1261
 
        self.stack = parser.Stack(self.ctx, 'stack_name', self.tmpl)
1262
 
        self.stack.store()
1263
 
        stack_id = self.stack.id
1264
 
 
1265
 
        self.ctx.username = None
1266
 
        stack = parser.Stack.load(self.ctx, stack_id=stack_id)
1267
 
        self.assertEqual('foobar', stack.username)
1268
 
 
1269
 
        self.ctx.username = 'not foobar'
1270
 
        stack = parser.Stack.load(self.ctx, stack_id=stack_id)
1271
 
        self.assertEqual('foobar', stack.username)
1272
 
 
1273
 
    def test_load_all(self):
1274
 
        stack1 = parser.Stack(self.ctx, 'stack1', self.tmpl)
1275
 
        stack1.store()
1276
 
        stack2 = parser.Stack(self.ctx, 'stack2', self.tmpl)
1277
 
        stack2.store()
1278
 
 
1279
 
        stacks = list(parser.Stack.load_all(self.ctx))
1280
 
        self.assertEqual(2, len(stacks))
1281
 
 
1282
 
        # Add another, nested, stack
1283
 
        stack3 = parser.Stack(self.ctx, 'stack3', self.tmpl,
1284
 
                              owner_id=stack2.id)
1285
 
        stack3.store()
1286
 
 
1287
 
        # Should still be 2 without show_nested
1288
 
        stacks = list(parser.Stack.load_all(self.ctx))
1289
 
        self.assertEqual(2, len(stacks))
1290
 
 
1291
 
        stacks = list(parser.Stack.load_all(self.ctx, show_nested=True))
1292
 
        self.assertEqual(3, len(stacks))
1293
 
 
1294
 
        # A backup stack should not be returned
1295
 
        stack1._backup_stack()
1296
 
        stacks = list(parser.Stack.load_all(self.ctx))
1297
 
        self.assertEqual(2, len(stacks))
1298
 
 
1299
 
        stacks = list(parser.Stack.load_all(self.ctx, show_nested=True))
1300
 
        self.assertEqual(3, len(stacks))
1301
 
 
1302
 
    def test_created_time(self):
1303
 
        self.stack = parser.Stack(self.ctx, 'creation_time_test',
1304
 
                                  self.tmpl)
1305
 
        self.assertIsNone(self.stack.created_time)
1306
 
        self.stack.store()
1307
 
        self.assertIsNotNone(self.stack.created_time)
1308
 
 
1309
 
    def test_updated_time(self):
1310
 
        self.stack = parser.Stack(self.ctx, 'updated_time_test',
1311
 
                                  self.tmpl)
1312
 
        self.assertIsNone(self.stack.updated_time)
1313
 
        self.stack.store()
1314
 
        self.stack.create()
1315
 
 
1316
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1317
 
                'Resources': {'R1': {'Type': 'GenericResourceType'}}}
1318
 
        newstack = parser.Stack(self.ctx, 'updated_time_test',
1319
 
                                parser.Template(tmpl))
1320
 
        self.stack.update(newstack)
1321
 
        self.assertIsNotNone(self.stack.updated_time)
1322
 
 
1323
 
    def test_access_policy_update(self):
1324
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1325
 
                'Resources': {
1326
 
                    'R1': {'Type': 'GenericResourceType'},
1327
 
                    'Policy': {
1328
 
                        'Type': 'OS::Heat::AccessPolicy',
1329
 
                        'Properties': {
1330
 
                            'AllowedResources': ['R1']
1331
 
                        }}}}
1332
 
 
1333
 
        self.stack = parser.Stack(self.ctx, 'update_stack_access_policy_test',
1334
 
                                  template.Template(tmpl))
1335
 
        self.stack.store()
1336
 
        self.stack.create()
1337
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
1338
 
                         self.stack.state)
1339
 
 
1340
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
1341
 
                 'Resources': {
1342
 
                     'R1': {'Type': 'GenericResourceType'},
1343
 
                     'R2': {'Type': 'GenericResourceType'},
1344
 
                     'Policy': {
1345
 
                         'Type': 'OS::Heat::AccessPolicy',
1346
 
                         'Properties': {
1347
 
                             'AllowedResources': ['R1', 'R2'],
1348
 
                         }}}}
1349
 
 
1350
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
1351
 
                                     template.Template(tmpl2))
1352
 
 
1353
 
        self.stack.update(updated_stack)
1354
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
1355
 
                         self.stack.state)
1356
 
 
1357
 
    def test_delete(self):
1358
 
        self.stack = parser.Stack(self.ctx, 'delete_test',
1359
 
                                  self.tmpl)
1360
 
        stack_id = self.stack.store()
1361
 
 
1362
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1363
 
        self.assertIsNotNone(db_s)
1364
 
 
1365
 
        self.stack.delete()
1366
 
 
1367
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1368
 
        self.assertIsNone(db_s)
1369
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1370
 
                         self.stack.state)
1371
 
 
1372
 
    def test_delete_user_creds(self):
1373
 
        self.stack = parser.Stack(self.ctx, 'delete_test',
1374
 
                                  self.tmpl)
1375
 
        stack_id = self.stack.store()
1376
 
 
1377
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1378
 
        self.assertIsNotNone(db_s)
1379
 
        self.assertIsNotNone(db_s.user_creds_id)
1380
 
        user_creds_id = db_s.user_creds_id
1381
 
        db_creds = db_api.user_creds_get(db_s.user_creds_id)
1382
 
        self.assertIsNotNone(db_creds)
1383
 
 
1384
 
        self.stack.delete()
1385
 
 
1386
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1387
 
        self.assertIsNone(db_s)
1388
 
        db_creds = db_api.user_creds_get(user_creds_id)
1389
 
        self.assertIsNone(db_creds)
1390
 
        del_db_s = db_api.stack_get(self.ctx, stack_id, show_deleted=True)
1391
 
        self.assertIsNone(del_db_s.user_creds_id)
1392
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1393
 
                         self.stack.state)
1394
 
 
1395
 
    def test_delete_user_creds_gone_missing(self):
1396
 
        '''Do not block stack deletion if user_creds is missing.
1397
 
 
1398
 
        It may happen that user_creds were deleted when a delete operation was
1399
 
        stopped. We should be resilient to this and still complete the delete
1400
 
        operation.
1401
 
        '''
1402
 
        self.stack = parser.Stack(self.ctx, 'delete_test',
1403
 
                                  self.tmpl)
1404
 
        stack_id = self.stack.store()
1405
 
 
1406
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1407
 
        self.assertIsNotNone(db_s)
1408
 
        self.assertIsNotNone(db_s.user_creds_id)
1409
 
        user_creds_id = db_s.user_creds_id
1410
 
        db_creds = db_api.user_creds_get(db_s.user_creds_id)
1411
 
        self.assertIsNotNone(db_creds)
1412
 
 
1413
 
        db_api.user_creds_delete(self.ctx, user_creds_id)
1414
 
 
1415
 
        self.stack.delete()
1416
 
 
1417
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1418
 
        self.assertIsNone(db_s)
1419
 
        db_creds = db_api.user_creds_get(user_creds_id)
1420
 
        self.assertIsNone(db_creds)
1421
 
        del_db_s = db_api.stack_get(self.ctx, stack_id, show_deleted=True)
1422
 
        self.assertIsNone(del_db_s.user_creds_id)
1423
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1424
 
                         self.stack.state)
1425
 
 
1426
 
    def test_delete_user_creds_fail(self):
1427
 
        '''Do not stop deleting stacks even failed deleting user_creds.
1428
 
 
1429
 
        It may happen that user_creds were incorrectly saved (truncated) and
1430
 
        thus cannot be correctly retrieved (and decrypted). In this case,
1431
 
        stack delete should not be stopped.
1432
 
        '''
1433
 
        self.stack = parser.Stack(self.ctx, 'delete_test', self.tmpl)
1434
 
        stack_id = self.stack.store()
1435
 
 
1436
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1437
 
        self.assertIsNotNone(db_s)
1438
 
        self.assertIsNotNone(db_s.user_creds_id)
1439
 
        exc = exception.Error('Cannot get user credentials')
1440
 
        self.patchobject(db_api, 'user_creds_get').side_effect = exc
1441
 
 
1442
 
        self.stack.delete()
1443
 
 
1444
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1445
 
        self.assertIsNone(db_s)
1446
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1447
 
                         self.stack.state)
1448
 
 
1449
 
    def test_delete_trust(self):
1450
 
        cfg.CONF.set_override('deferred_auth_method', 'trusts')
1451
 
        self.stub_keystoneclient()
1452
 
 
1453
 
        self.stack = parser.Stack(
1454
 
            self.ctx, 'delete_trust', self.tmpl)
1455
 
        stack_id = self.stack.store()
1456
 
 
1457
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1458
 
        self.assertIsNotNone(db_s)
1459
 
 
1460
 
        self.stack.delete()
1461
 
 
1462
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1463
 
        self.assertIsNone(db_s)
1464
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1465
 
                         self.stack.state)
1466
 
 
1467
 
    def test_delete_trust_trustor(self):
1468
 
        cfg.CONF.set_override('deferred_auth_method', 'trusts')
1469
 
 
1470
 
        trustor_ctx = utils.dummy_context(user_id='thetrustor')
1471
 
        self.m.StubOutWithMock(hkc, 'KeystoneClient')
1472
 
        hkc.KeystoneClient(trustor_ctx).AndReturn(
1473
 
            fakes.FakeKeystoneClient(user_id='thetrustor'))
1474
 
        self.m.ReplayAll()
1475
 
 
1476
 
        self.stack = parser.Stack(
1477
 
            trustor_ctx, 'delete_trust_nt', self.tmpl)
1478
 
        stack_id = self.stack.store()
1479
 
 
1480
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1481
 
        self.assertIsNotNone(db_s)
1482
 
 
1483
 
        user_creds_id = db_s.user_creds_id
1484
 
        self.assertIsNotNone(user_creds_id)
1485
 
        user_creds = db_api.user_creds_get(user_creds_id)
1486
 
        self.assertEqual('thetrustor', user_creds.get('trustor_user_id'))
1487
 
 
1488
 
        self.stack.delete()
1489
 
 
1490
 
        db_s = db_api.stack_get(trustor_ctx, stack_id)
1491
 
        self.assertIsNone(db_s)
1492
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1493
 
                         self.stack.state)
1494
 
 
1495
 
    def test_delete_trust_not_trustor(self):
1496
 
        cfg.CONF.set_override('deferred_auth_method', 'trusts')
1497
 
 
1498
 
        # Stack gets created with trustor_ctx, deleted with other_ctx
1499
 
        # then the trust delete should be with stored_ctx
1500
 
        trustor_ctx = utils.dummy_context(user_id='thetrustor')
1501
 
        other_ctx = utils.dummy_context(user_id='nottrustor')
1502
 
        stored_ctx = utils.dummy_context(trust_id='thetrust')
1503
 
 
1504
 
        self.m.StubOutWithMock(hkc, 'KeystoneClient')
1505
 
        hkc.KeystoneClient(trustor_ctx).AndReturn(
1506
 
            fakes.FakeKeystoneClient(user_id='thetrustor'))
1507
 
        self.m.StubOutWithMock(parser.Stack, 'stored_context')
1508
 
        parser.Stack.stored_context().AndReturn(stored_ctx)
1509
 
        hkc.KeystoneClient(stored_ctx).AndReturn(
1510
 
            fakes.FakeKeystoneClient(user_id='nottrustor'))
1511
 
        self.m.ReplayAll()
1512
 
 
1513
 
        self.stack = parser.Stack(
1514
 
            trustor_ctx, 'delete_trust_nt', self.tmpl)
1515
 
        stack_id = self.stack.store()
1516
 
 
1517
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1518
 
        self.assertIsNotNone(db_s)
1519
 
 
1520
 
        user_creds_id = db_s.user_creds_id
1521
 
        self.assertIsNotNone(user_creds_id)
1522
 
        user_creds = db_api.user_creds_get(user_creds_id)
1523
 
        self.assertEqual('thetrustor', user_creds.get('trustor_user_id'))
1524
 
 
1525
 
        loaded_stack = parser.Stack.load(other_ctx, self.stack.id)
1526
 
        loaded_stack.delete()
1527
 
 
1528
 
        db_s = db_api.stack_get(other_ctx, stack_id)
1529
 
        self.assertIsNone(db_s)
1530
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1531
 
                         loaded_stack.state)
1532
 
 
1533
 
    def test_delete_trust_backup(self):
1534
 
        cfg.CONF.set_override('deferred_auth_method', 'trusts')
1535
 
 
1536
 
        class FakeKeystoneClientFail(fakes.FakeKeystoneClient):
1537
 
            def delete_trust(self, trust_id):
1538
 
                raise Exception("Shouldn't delete")
1539
 
 
1540
 
        self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
1541
 
        keystone.KeystoneClientPlugin._create().AndReturn(
1542
 
            FakeKeystoneClientFail())
1543
 
        self.m.ReplayAll()
1544
 
 
1545
 
        self.stack = parser.Stack(
1546
 
            self.ctx, 'delete_trust', self.tmpl)
1547
 
        stack_id = self.stack.store()
1548
 
 
1549
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1550
 
        self.assertIsNotNone(db_s)
1551
 
 
1552
 
        self.stack.delete(backup=True)
1553
 
 
1554
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1555
 
        self.assertIsNone(db_s)
1556
 
        self.assertEqual(self.stack.state,
1557
 
                         (parser.Stack.DELETE, parser.Stack.COMPLETE))
1558
 
 
1559
 
    def test_delete_trust_nested(self):
1560
 
        cfg.CONF.set_override('deferred_auth_method', 'trusts')
1561
 
 
1562
 
        class FakeKeystoneClientFail(fakes.FakeKeystoneClient):
1563
 
            def delete_trust(self, trust_id):
1564
 
                raise Exception("Shouldn't delete")
1565
 
 
1566
 
        self.stub_keystoneclient(fake_client=FakeKeystoneClientFail())
1567
 
 
1568
 
        self.stack = parser.Stack(
1569
 
            self.ctx, 'delete_trust_nested', self.tmpl,
1570
 
            owner_id='owner123')
1571
 
        stack_id = self.stack.store()
1572
 
 
1573
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1574
 
        self.assertIsNotNone(db_s)
1575
 
        user_creds_id = db_s.user_creds_id
1576
 
        self.assertIsNotNone(user_creds_id)
1577
 
        user_creds = db_api.user_creds_get(user_creds_id)
1578
 
        self.assertIsNotNone(user_creds)
1579
 
 
1580
 
        self.stack.delete()
1581
 
 
1582
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1583
 
        self.assertIsNone(db_s)
1584
 
        user_creds = db_api.user_creds_get(user_creds_id)
1585
 
        self.assertIsNotNone(user_creds)
1586
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1587
 
                         self.stack.state)
1588
 
 
1589
 
    def test_delete_trust_fail(self):
1590
 
        cfg.CONF.set_override('deferred_auth_method', 'trusts')
1591
 
 
1592
 
        class FakeKeystoneClientFail(fakes.FakeKeystoneClient):
1593
 
            def delete_trust(self, trust_id):
1594
 
                raise kc_exceptions.Forbidden("Denied!")
1595
 
 
1596
 
        self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
1597
 
        keystone.KeystoneClientPlugin._create().AndReturn(
1598
 
            FakeKeystoneClientFail())
1599
 
        self.m.ReplayAll()
1600
 
 
1601
 
        self.stack = parser.Stack(
1602
 
            self.ctx, 'delete_trust', self.tmpl)
1603
 
        stack_id = self.stack.store()
1604
 
 
1605
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1606
 
        self.assertIsNotNone(db_s)
1607
 
 
1608
 
        self.stack.delete()
1609
 
 
1610
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1611
 
        self.assertIsNotNone(db_s)
1612
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.FAILED),
1613
 
                         self.stack.state)
1614
 
        self.assertIn('Error deleting trust', self.stack.status_reason)
1615
 
 
1616
 
    def test_delete_deletes_project(self):
1617
 
        fkc = fakes.FakeKeystoneClient()
1618
 
        fkc.delete_stack_domain_project = mock.Mock()
1619
 
 
1620
 
        self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
1621
 
        keystone.KeystoneClientPlugin._create().AndReturn(fkc)
1622
 
        self.m.ReplayAll()
1623
 
 
1624
 
        self.stack = parser.Stack(
1625
 
            self.ctx, 'delete_trust', self.tmpl)
1626
 
        stack_id = self.stack.store()
1627
 
 
1628
 
        self.stack.set_stack_user_project_id(project_id='aproject456')
1629
 
 
1630
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1631
 
        self.assertIsNotNone(db_s)
1632
 
 
1633
 
        self.stack.delete()
1634
 
 
1635
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1636
 
        self.assertIsNone(db_s)
1637
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1638
 
                         self.stack.state)
1639
 
        fkc.delete_stack_domain_project.assert_called_once_with(
1640
 
            project_id='aproject456')
1641
 
 
1642
 
    def test_abandon_nodelete_project(self):
1643
 
        fkc = fakes.FakeKeystoneClient()
1644
 
        fkc.delete_stack_domain_project = mock.Mock()
1645
 
 
1646
 
        self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
1647
 
        keystone.KeystoneClientPlugin._create().AndReturn(fkc)
1648
 
        self.m.ReplayAll()
1649
 
 
1650
 
        self.stack = parser.Stack(
1651
 
            self.ctx, 'delete_trust', self.tmpl)
1652
 
        stack_id = self.stack.store()
1653
 
 
1654
 
        self.stack.set_stack_user_project_id(project_id='aproject456')
1655
 
 
1656
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1657
 
        self.assertIsNotNone(db_s)
1658
 
 
1659
 
        self.stack.delete(abandon=True)
1660
 
 
1661
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1662
 
        self.assertIsNone(db_s)
1663
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
1664
 
                         self.stack.state)
1665
 
        self.assertFalse(fkc.delete_stack_domain_project.called)
1666
 
 
1667
 
    def test_suspend_resume(self):
1668
 
        self.m.ReplayAll()
1669
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1670
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
1671
 
        self.stack = parser.Stack(self.ctx, 'suspend_test',
1672
 
                                  parser.Template(tmpl))
1673
 
        self.stack.store()
1674
 
        self.stack.create()
1675
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
1676
 
                         self.stack.state)
1677
 
        self.assertIsNone(self.stack.updated_time)
1678
 
 
1679
 
        self.stack.suspend()
1680
 
 
1681
 
        self.assertEqual((self.stack.SUSPEND, self.stack.COMPLETE),
1682
 
                         self.stack.state)
1683
 
        stack_suspend_time = self.stack.updated_time
1684
 
        self.assertIsNotNone(stack_suspend_time)
1685
 
 
1686
 
        self.stack.resume()
1687
 
 
1688
 
        self.assertEqual((self.stack.RESUME, self.stack.COMPLETE),
1689
 
                         self.stack.state)
1690
 
        self.assertNotEqual(stack_suspend_time, self.stack.updated_time)
1691
 
 
1692
 
        self.m.VerifyAll()
1693
 
 
1694
 
    def test_suspend_stack_suspended_ok(self):
1695
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1696
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
1697
 
        self.stack = parser.Stack(self.ctx, 'suspend_test',
1698
 
                                  parser.Template(tmpl))
1699
 
        self.stack.store()
1700
 
        self.stack.create()
1701
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
1702
 
                         self.stack.state)
1703
 
 
1704
 
        self.stack.suspend()
1705
 
        self.assertEqual((self.stack.SUSPEND, self.stack.COMPLETE),
1706
 
                         self.stack.state)
1707
 
 
1708
 
        # unexpected to call Resource.suspend
1709
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'suspend')
1710
 
        self.m.ReplayAll()
1711
 
 
1712
 
        self.stack.suspend()
1713
 
        self.assertEqual((self.stack.SUSPEND, self.stack.COMPLETE),
1714
 
                         self.stack.state)
1715
 
        self.m.VerifyAll()
1716
 
 
1717
 
    def test_resume_stack_resumeed_ok(self):
1718
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1719
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
1720
 
        self.stack = parser.Stack(self.ctx, 'suspend_test',
1721
 
                                  parser.Template(tmpl))
1722
 
        self.stack.store()
1723
 
        self.stack.create()
1724
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
1725
 
                         self.stack.state)
1726
 
 
1727
 
        self.stack.suspend()
1728
 
        self.assertEqual((self.stack.SUSPEND, self.stack.COMPLETE),
1729
 
                         self.stack.state)
1730
 
 
1731
 
        self.stack.resume()
1732
 
        self.assertEqual((self.stack.RESUME, self.stack.COMPLETE),
1733
 
                         self.stack.state)
1734
 
 
1735
 
        # unexpected to call Resource.resume
1736
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'resume')
1737
 
        self.m.ReplayAll()
1738
 
 
1739
 
        self.stack.resume()
1740
 
        self.assertEqual((self.stack.RESUME, self.stack.COMPLETE),
1741
 
                         self.stack.state)
1742
 
        self.m.VerifyAll()
1743
 
 
1744
 
    def test_suspend_fail(self):
1745
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1746
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
1747
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_suspend')
1748
 
        exc = Exception('foo')
1749
 
        generic_rsrc.GenericResource.handle_suspend().AndRaise(exc)
1750
 
        self.m.ReplayAll()
1751
 
 
1752
 
        self.stack = parser.Stack(self.ctx, 'suspend_test_fail',
1753
 
                                  parser.Template(tmpl))
1754
 
 
1755
 
        self.stack.store()
1756
 
        self.stack.create()
1757
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
1758
 
                         self.stack.state)
1759
 
 
1760
 
        self.stack.suspend()
1761
 
 
1762
 
        self.assertEqual((self.stack.SUSPEND, self.stack.FAILED),
1763
 
                         self.stack.state)
1764
 
        self.assertEqual('Resource SUSPEND failed: Exception: foo',
1765
 
                         self.stack.status_reason)
1766
 
        self.m.VerifyAll()
1767
 
 
1768
 
    def test_resume_fail(self):
1769
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1770
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
1771
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_resume')
1772
 
        generic_rsrc.GenericResource.handle_resume().AndRaise(Exception('foo'))
1773
 
        self.m.ReplayAll()
1774
 
 
1775
 
        self.stack = parser.Stack(self.ctx, 'resume_test_fail',
1776
 
                                  parser.Template(tmpl))
1777
 
 
1778
 
        self.stack.store()
1779
 
        self.stack.create()
1780
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
1781
 
                         self.stack.state)
1782
 
 
1783
 
        self.stack.suspend()
1784
 
 
1785
 
        self.assertEqual((self.stack.SUSPEND, self.stack.COMPLETE),
1786
 
                         self.stack.state)
1787
 
 
1788
 
        self.stack.resume()
1789
 
 
1790
 
        self.assertEqual((self.stack.RESUME, self.stack.FAILED),
1791
 
                         self.stack.state)
1792
 
        self.assertEqual('Resource RESUME failed: Exception: foo',
1793
 
                         self.stack.status_reason)
1794
 
        self.m.VerifyAll()
1795
 
 
1796
 
    def test_suspend_timeout(self):
1797
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1798
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
1799
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_suspend')
1800
 
        exc = scheduler.Timeout('foo', 0)
1801
 
        generic_rsrc.GenericResource.handle_suspend().AndRaise(exc)
1802
 
        self.m.ReplayAll()
1803
 
 
1804
 
        self.stack = parser.Stack(self.ctx, 'suspend_test_fail_timeout',
1805
 
                                  parser.Template(tmpl))
1806
 
 
1807
 
        self.stack.store()
1808
 
        self.stack.create()
1809
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
1810
 
                         self.stack.state)
1811
 
 
1812
 
        self.stack.suspend()
1813
 
 
1814
 
        self.assertEqual((self.stack.SUSPEND, self.stack.FAILED),
1815
 
                         self.stack.state)
1816
 
        self.assertEqual('Suspend timed out', self.stack.status_reason)
1817
 
        self.m.VerifyAll()
1818
 
 
1819
 
    def test_resume_timeout(self):
1820
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
1821
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
1822
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_resume')
1823
 
        exc = scheduler.Timeout('foo', 0)
1824
 
        generic_rsrc.GenericResource.handle_resume().AndRaise(exc)
1825
 
        self.m.ReplayAll()
1826
 
 
1827
 
        self.stack = parser.Stack(self.ctx, 'resume_test_fail_timeout',
1828
 
                                  parser.Template(tmpl))
1829
 
 
1830
 
        self.stack.store()
1831
 
        self.stack.create()
1832
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
1833
 
                         self.stack.state)
1834
 
 
1835
 
        self.stack.suspend()
1836
 
 
1837
 
        self.assertEqual((self.stack.SUSPEND, self.stack.COMPLETE),
1838
 
                         self.stack.state)
1839
 
 
1840
 
        self.stack.resume()
1841
 
 
1842
 
        self.assertEqual((self.stack.RESUME, self.stack.FAILED),
1843
 
                         self.stack.state)
1844
 
 
1845
 
        self.assertEqual('Resume timed out', self.stack.status_reason)
1846
 
        self.m.VerifyAll()
1847
 
 
1848
 
    def _get_stack_to_check(self, name):
1849
 
        tpl = {"HeatTemplateFormatVersion": "2012-12-12",
1850
 
               "Resources": {
1851
 
                   "A": {"Type": "GenericResourceType"},
1852
 
                   "B": {"Type": "GenericResourceType"}}}
1853
 
        self.stack = parser.Stack(self.ctx, name, parser.Template(tpl),
1854
 
                                  status_reason=name)
1855
 
        self.stack.store()
1856
 
 
1857
 
        def _mock_check(res):
1858
 
            res.handle_check = mock.Mock()
1859
 
 
1860
 
        [_mock_check(res) for res in self.stack.resources.values()]
1861
 
        return self.stack
1862
 
 
1863
 
    def test_check_supported(self):
1864
 
        stack = self._get_stack_to_check('check-supported')
1865
 
        stack.check()
1866
 
 
1867
 
        self.assertEqual(stack.COMPLETE, stack.status)
1868
 
        self.assertEqual(stack.CHECK, stack.action)
1869
 
        [self.assertTrue(res.handle_check.called)
1870
 
         for res in stack.resources.values()]
1871
 
        self.assertNotIn('not fully supported', stack.status_reason)
1872
 
 
1873
 
    def test_check_not_supported(self):
1874
 
        stack = self._get_stack_to_check('check-not-supported')
1875
 
        del stack['B'].handle_check
1876
 
        stack.check()
1877
 
 
1878
 
        self.assertEqual(stack.COMPLETE, stack.status)
1879
 
        self.assertEqual(stack.CHECK, stack.action)
1880
 
        self.assertTrue(stack['A'].handle_check.called)
1881
 
        self.assertIn('not fully supported', stack.status_reason)
1882
 
 
1883
 
    def test_check_fail(self):
1884
 
        stack = self._get_stack_to_check('check-fail')
1885
 
        stack['A'].handle_check.side_effect = Exception('fail-A')
1886
 
        stack['B'].handle_check.side_effect = Exception('fail-B')
1887
 
        stack.check()
1888
 
 
1889
 
        self.assertEqual(stack.FAILED, stack.status)
1890
 
        self.assertEqual(stack.CHECK, stack.action)
1891
 
        self.assertTrue(stack['A'].handle_check.called)
1892
 
        self.assertTrue(stack['B'].handle_check.called)
1893
 
        self.assertIn('fail-A', stack.status_reason)
1894
 
        self.assertIn('fail-B', stack.status_reason)
1895
 
 
1896
 
    def test_delete_rollback(self):
1897
 
        self.stack = parser.Stack(self.ctx, 'delete_rollback_test',
1898
 
                                  self.tmpl, disable_rollback=False)
1899
 
        stack_id = self.stack.store()
1900
 
 
1901
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1902
 
        self.assertIsNotNone(db_s)
1903
 
 
1904
 
        self.stack.delete(action=self.stack.ROLLBACK)
1905
 
 
1906
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1907
 
        self.assertIsNone(db_s)
1908
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
1909
 
                         self.stack.state)
1910
 
 
1911
 
    def test_delete_badaction(self):
1912
 
        self.stack = parser.Stack(self.ctx, 'delete_badaction_test',
1913
 
                                  self.tmpl)
1914
 
        stack_id = self.stack.store()
1915
 
 
1916
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1917
 
        self.assertIsNotNone(db_s)
1918
 
 
1919
 
        self.stack.delete(action="wibble")
1920
 
 
1921
 
        db_s = db_api.stack_get(self.ctx, stack_id)
1922
 
        self.assertIsNotNone(db_s)
1923
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.FAILED),
1924
 
                         self.stack.state)
1925
 
 
1926
 
    def test_adopt_stack(self):
1927
 
        adopt_data = '''{
1928
 
        "action": "CREATE",
1929
 
        "status": "COMPLETE",
1930
 
        "name": "my-test-stack-name",
1931
 
        "resources": {
1932
 
        "AResource": {
1933
 
        "status": "COMPLETE",
1934
 
        "name": "AResource",
1935
 
        "resource_data": {},
1936
 
        "metadata": {},
1937
 
        "resource_id": "test-res-id",
1938
 
        "action": "CREATE",
1939
 
        "type": "GenericResourceType"
1940
 
          }
1941
 
         }
1942
 
        }'''
1943
 
 
1944
 
        tmpl = {
1945
 
            'HeatTemplateFormatVersion': '2012-12-12',
1946
 
            'Resources': {'AResource': {'Type': 'GenericResourceType'}},
1947
 
            'Outputs': {'TestOutput': {'Value': {
1948
 
                'Fn::GetAtt': ['AResource', 'Foo']}}
1949
 
            }
1950
 
        }
1951
 
 
1952
 
        self.stack = parser.Stack(utils.dummy_context(), 'test_stack',
1953
 
                                  template.Template(tmpl),
1954
 
                                  adopt_stack_data=json.loads(adopt_data))
1955
 
        self.stack.store()
1956
 
        self.stack.adopt()
1957
 
        res = self.stack['AResource']
1958
 
        self.assertEqual(u'test-res-id', res.resource_id)
1959
 
        self.assertEqual('AResource', res.name)
1960
 
        self.assertEqual('COMPLETE', res.status)
1961
 
        self.assertEqual('ADOPT', res.action)
1962
 
        self.assertEqual((self.stack.ADOPT, self.stack.COMPLETE),
1963
 
                         self.stack.state)
1964
 
        self.assertEqual('AResource', self.stack.output('TestOutput'))
1965
 
 
1966
 
        loaded_stack = parser.Stack.load(self.ctx, self.stack.id)
1967
 
        self.assertEqual({}, loaded_stack['AResource']._stored_properties_data)
1968
 
 
1969
 
    def test_adopt_stack_fails(self):
1970
 
        adopt_data = '''{
1971
 
                "action": "CREATE",
1972
 
                "status": "COMPLETE",
1973
 
                "name": "my-test-stack-name",
1974
 
                "resources": {}
1975
 
                }'''
1976
 
 
1977
 
        tmpl = template.Template({
1978
 
            'HeatTemplateFormatVersion': '2012-12-12',
1979
 
            'Resources': {
1980
 
                'foo': {'Type': 'GenericResourceType'},
1981
 
 
1982
 
            }
1983
 
        })
1984
 
        self.stack = parser.Stack(utils.dummy_context(), 'test_stack',
1985
 
                                  tmpl,
1986
 
                                  adopt_stack_data=json.loads(adopt_data))
1987
 
        self.stack.store()
1988
 
        self.stack.adopt()
1989
 
        self.assertEqual((self.stack.ADOPT, self.stack.FAILED),
1990
 
                         self.stack.state)
1991
 
        expected = ('Resource ADOPT failed: Exception: Resource ID was not'
1992
 
                    ' provided.')
1993
 
        self.assertEqual(expected, self.stack.status_reason)
1994
 
 
1995
 
    def test_adopt_stack_rollback(self):
1996
 
        adopt_data = '''{
1997
 
                "name": "my-test-stack-name",
1998
 
                "resources": {}
1999
 
                }'''
2000
 
 
2001
 
        tmpl = template.Template({
2002
 
            'HeatTemplateFormatVersion': '2012-12-12',
2003
 
            'Resources': {
2004
 
                'foo': {'Type': 'GenericResourceType'},
2005
 
 
2006
 
            }
2007
 
        })
2008
 
        self.stack = parser.Stack(utils.dummy_context(),
2009
 
                                  'test_stack',
2010
 
                                  tmpl,
2011
 
                                  disable_rollback=False,
2012
 
                                  adopt_stack_data=json.loads(adopt_data))
2013
 
        self.stack.store()
2014
 
        self.stack.adopt()
2015
 
        self.assertEqual((self.stack.ROLLBACK, self.stack.COMPLETE),
2016
 
                         self.stack.state)
2017
 
 
2018
 
    def test_resource_by_refid(self):
2019
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2020
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2021
 
 
2022
 
        self.stack = parser.Stack(self.ctx, 'resource_by_refid_stack',
2023
 
                                  template.Template(tmpl))
2024
 
        self.stack.store()
2025
 
        self.stack.create()
2026
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2027
 
                         self.stack.state)
2028
 
        self.assertIn('AResource', self.stack)
2029
 
        rsrc = self.stack['AResource']
2030
 
        rsrc.resource_id_set('aaaa')
2031
 
        self.assertIsNotNone(resource)
2032
 
 
2033
 
        for action, status in (
2034
 
                (rsrc.INIT, rsrc.COMPLETE),
2035
 
                (rsrc.CREATE, rsrc.IN_PROGRESS),
2036
 
                (rsrc.CREATE, rsrc.COMPLETE),
2037
 
                (rsrc.RESUME, rsrc.IN_PROGRESS),
2038
 
                (rsrc.RESUME, rsrc.COMPLETE),
2039
 
                (rsrc.UPDATE, rsrc.IN_PROGRESS),
2040
 
                (rsrc.UPDATE, rsrc.COMPLETE)):
2041
 
            rsrc.state_set(action, status)
2042
 
            self.assertEqual(rsrc, self.stack.resource_by_refid('aaaa'))
2043
 
 
2044
 
        rsrc.state_set(rsrc.DELETE, rsrc.IN_PROGRESS)
2045
 
        try:
2046
 
            self.assertIsNone(self.stack.resource_by_refid('aaaa'))
2047
 
            self.assertIsNone(self.stack.resource_by_refid('bbbb'))
2048
 
        finally:
2049
 
            rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE)
2050
 
 
2051
 
    def test_update_add(self):
2052
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2053
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2054
 
 
2055
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2056
 
                                  template.Template(tmpl))
2057
 
        self.stack.store()
2058
 
        self.stack.create()
2059
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2060
 
                         self.stack.state)
2061
 
 
2062
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2063
 
                 'Resources': {
2064
 
                     'AResource': {'Type': 'GenericResourceType'},
2065
 
                     'BResource': {'Type': 'GenericResourceType'}}}
2066
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2067
 
                                     template.Template(tmpl2))
2068
 
        self.stack.update(updated_stack)
2069
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2070
 
                         self.stack.state)
2071
 
        self.assertIn('BResource', self.stack)
2072
 
 
2073
 
    def test_update_remove(self):
2074
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2075
 
                'Resources': {
2076
 
                    'AResource': {'Type': 'GenericResourceType'},
2077
 
                    'BResource': {'Type': 'GenericResourceType'}}}
2078
 
 
2079
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2080
 
                                  template.Template(tmpl))
2081
 
        self.stack.store()
2082
 
        self.stack.create()
2083
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2084
 
                         self.stack.state)
2085
 
 
2086
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2087
 
                 'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2088
 
 
2089
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2090
 
                                     template.Template(tmpl2))
2091
 
        self.stack.update(updated_stack)
2092
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2093
 
                         self.stack.state)
2094
 
        self.assertNotIn('BResource', self.stack)
2095
 
 
2096
 
    def test_update_description(self):
2097
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2098
 
                'Description': 'ATemplate',
2099
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2100
 
 
2101
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2102
 
                                  template.Template(tmpl))
2103
 
        self.stack.store()
2104
 
        self.stack.create()
2105
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2106
 
                         self.stack.state)
2107
 
 
2108
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2109
 
                 'Description': 'BTemplate',
2110
 
                 'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2111
 
 
2112
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2113
 
                                     template.Template(tmpl2))
2114
 
        self.stack.update(updated_stack)
2115
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2116
 
                         self.stack.state)
2117
 
        self.assertEqual('BTemplate',
2118
 
                         self.stack.t[self.stack.t.DESCRIPTION])
2119
 
 
2120
 
    def test_update_timeout(self):
2121
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2122
 
                'Description': 'ATemplate',
2123
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2124
 
 
2125
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2126
 
                                  template.Template(tmpl), timeout_mins=60)
2127
 
        self.stack.store()
2128
 
        self.stack.create()
2129
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2130
 
                         self.stack.state)
2131
 
 
2132
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2133
 
                 'Description': 'ATemplate',
2134
 
                 'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2135
 
 
2136
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2137
 
                                     template.Template(tmpl2), timeout_mins=30)
2138
 
        self.stack.update(updated_stack)
2139
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2140
 
                         self.stack.state)
2141
 
        self.assertEqual(30, self.stack.timeout_mins)
2142
 
 
2143
 
    def test_update_disable_rollback(self):
2144
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2145
 
                'Description': 'ATemplate',
2146
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2147
 
 
2148
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2149
 
                                  template.Template(tmpl),
2150
 
                                  disable_rollback=False)
2151
 
        self.stack.store()
2152
 
        self.stack.create()
2153
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2154
 
                         self.stack.state)
2155
 
 
2156
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2157
 
                 'Description': 'ATemplate',
2158
 
                 'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2159
 
 
2160
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2161
 
                                     template.Template(tmpl2),
2162
 
                                     disable_rollback=True)
2163
 
        self.stack.update(updated_stack)
2164
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2165
 
                         self.stack.state)
2166
 
        self.assertEqual(True, self.stack.disable_rollback)
2167
 
 
2168
 
    def test_update_modify_ok_replace(self):
2169
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2170
 
                'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2171
 
                                            'Properties': {'Foo': 'abc'}}}}
2172
 
 
2173
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2174
 
                                  template.Template(tmpl))
2175
 
        self.stack.store()
2176
 
        self.stack.create()
2177
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2178
 
                         self.stack.state)
2179
 
 
2180
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2181
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2182
 
                                             'Properties': {'Foo': 'xyz'}}}}
2183
 
 
2184
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2185
 
                                     template.Template(tmpl2))
2186
 
 
2187
 
        self.m.ReplayAll()
2188
 
 
2189
 
        self.stack.update(updated_stack)
2190
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2191
 
                         self.stack.state)
2192
 
        self.assertEqual('xyz', self.stack['AResource'].properties['Foo'])
2193
 
 
2194
 
        loaded_stack = parser.Stack.load(self.ctx, self.stack.id)
2195
 
        stored_props = loaded_stack['AResource']._stored_properties_data
2196
 
        self.assertEqual({'Foo': 'xyz'}, stored_props)
2197
 
 
2198
 
        self.m.VerifyAll()
2199
 
 
2200
 
    def test_update_modify_ok_replace_int(self):
2201
 
        # create
2202
 
        # ========
2203
 
        tmpl = {'heat_template_version': '2013-05-23',
2204
 
                'resources': {'AResource': {
2205
 
                    'type': 'ResWithComplexPropsAndAttrs',
2206
 
                    'properties': {'an_int': 1}}}}
2207
 
 
2208
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2209
 
                                  template.Template(tmpl))
2210
 
        self.stack.store()
2211
 
        stack_id = self.stack.id
2212
 
        self.stack.create()
2213
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2214
 
                         self.stack.state)
2215
 
 
2216
 
        value1 = 2
2217
 
        prop_diff1 = {'an_int': value1}
2218
 
        value2 = 1
2219
 
        prop_diff2 = {'an_int': value2}
2220
 
 
2221
 
        self.m.StubOutWithMock(generic_rsrc.ResWithComplexPropsAndAttrs,
2222
 
                               'handle_update')
2223
 
        generic_rsrc.ResWithComplexPropsAndAttrs.handle_update(mox.IgnoreArg(),
2224
 
                                                               mox.IgnoreArg(),
2225
 
                                                               prop_diff1)
2226
 
        generic_rsrc.ResWithComplexPropsAndAttrs.handle_update(mox.IgnoreArg(),
2227
 
                                                               mox.IgnoreArg(),
2228
 
                                                               prop_diff2)
2229
 
 
2230
 
        self.m.ReplayAll()
2231
 
 
2232
 
        # update 1
2233
 
        # ==========
2234
 
 
2235
 
        self.stack = parser.Stack.load(self.ctx, stack_id=stack_id)
2236
 
        tmpl2 = {'heat_template_version': '2013-05-23',
2237
 
                 'resources': {'AResource': {
2238
 
                     'type': 'ResWithComplexPropsAndAttrs',
2239
 
                     'properties': {'an_int': value1}}}}
2240
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2241
 
                                     template.Template(tmpl2))
2242
 
 
2243
 
        self.stack.update(updated_stack)
2244
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2245
 
                         self.stack.state)
2246
 
 
2247
 
        # update 2
2248
 
        # ==========
2249
 
        # reload the previous stack
2250
 
        self.stack = parser.Stack.load(self.ctx, stack_id=stack_id)
2251
 
        tmpl3 = {'heat_template_version': '2013-05-23',
2252
 
                 'resources': {'AResource': {
2253
 
                     'type': 'ResWithComplexPropsAndAttrs',
2254
 
                     'properties': {'an_int': value2}}}}
2255
 
 
2256
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2257
 
                                     template.Template(tmpl3))
2258
 
 
2259
 
        self.stack.update(updated_stack)
2260
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2261
 
                         self.stack.state)
2262
 
 
2263
 
        self.m.VerifyAll()
2264
 
 
2265
 
    def test_update_modify_param_ok_replace(self):
2266
 
        tmpl = {
2267
 
            'HeatTemplateFormatVersion': '2012-12-12',
2268
 
            'Parameters': {
2269
 
                'foo': {'Type': 'String'}
2270
 
            },
2271
 
            'Resources': {
2272
 
                'AResource': {
2273
 
                    'Type': 'ResourceWithPropsType',
2274
 
                    'Properties': {'Foo': {'Ref': 'foo'}}
2275
 
                }
2276
 
            }
2277
 
        }
2278
 
 
2279
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps,
2280
 
                               'update_template_diff')
2281
 
 
2282
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2283
 
                                  template.Template(tmpl),
2284
 
                                  environment.Environment({'foo': 'abc'}))
2285
 
        self.stack.store()
2286
 
        self.stack.create()
2287
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2288
 
                         self.stack.state)
2289
 
 
2290
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2291
 
                                     template.Template(tmpl),
2292
 
                                     environment.Environment({'foo': 'xyz'}))
2293
 
 
2294
 
        def check_props(*args):
2295
 
            self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
2296
 
 
2297
 
        generic_rsrc.ResourceWithProps.update_template_diff(
2298
 
            {'Type': 'ResourceWithPropsType',
2299
 
             'Properties': {'Foo': 'xyz'}},
2300
 
            {'Type': 'ResourceWithPropsType',
2301
 
             'Properties': {'Foo': 'abc'}}
2302
 
        ).WithSideEffects(check_props).AndRaise(resource.UpdateReplace)
2303
 
        self.m.ReplayAll()
2304
 
 
2305
 
        self.stack.update(updated_stack)
2306
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2307
 
                         self.stack.state)
2308
 
        self.assertEqual('xyz', self.stack['AResource'].properties['Foo'])
2309
 
        self.m.VerifyAll()
2310
 
 
2311
 
    def test_update_modify_update_failed(self):
2312
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2313
 
                'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2314
 
                                            'Properties': {'Foo': 'abc'}}}}
2315
 
 
2316
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2317
 
                                  template.Template(tmpl),
2318
 
                                  disable_rollback=True)
2319
 
        self.stack.store()
2320
 
        self.stack.create()
2321
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2322
 
                         self.stack.state)
2323
 
 
2324
 
        res = self.stack['AResource']
2325
 
        res.update_allowed_properties = ('Foo',)
2326
 
 
2327
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2328
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2329
 
                                             'Properties': {'Foo': 'xyz'}}}}
2330
 
 
2331
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2332
 
                                     template.Template(tmpl2))
2333
 
 
2334
 
        # patch in a dummy handle_update
2335
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_update')
2336
 
        tmpl_diff = {'Properties': {'Foo': 'xyz'}}
2337
 
        prop_diff = {'Foo': 'xyz'}
2338
 
        generic_rsrc.ResourceWithProps.handle_update(
2339
 
            tmpl2['Resources']['AResource'], tmpl_diff,
2340
 
            prop_diff).AndRaise(Exception("Foo"))
2341
 
        self.m.ReplayAll()
2342
 
 
2343
 
        self.stack.update(updated_stack)
2344
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
2345
 
                         self.stack.state)
2346
 
        self.m.VerifyAll()
2347
 
 
2348
 
    def test_update_modify_replace_failed_delete(self):
2349
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2350
 
                'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2351
 
                                            'Properties': {'Foo': 'abc'}}}}
2352
 
 
2353
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2354
 
                                  template.Template(tmpl),
2355
 
                                  disable_rollback=True)
2356
 
        self.stack.store()
2357
 
        self.stack.create()
2358
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2359
 
                         self.stack.state)
2360
 
 
2361
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2362
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2363
 
                                             'Properties': {'Foo': 'xyz'}}}}
2364
 
 
2365
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2366
 
                                     template.Template(tmpl2))
2367
 
 
2368
 
        # make the update fail deleting the existing resource
2369
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_delete')
2370
 
        generic_rsrc.ResourceWithProps.handle_delete().AndRaise(Exception)
2371
 
        self.m.ReplayAll()
2372
 
 
2373
 
        self.stack.update(updated_stack)
2374
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
2375
 
                         self.stack.state)
2376
 
        self.m.VerifyAll()
2377
 
        # Unset here so destroy() is not stubbed for stack.delete cleanup
2378
 
        self.m.UnsetStubs()
2379
 
 
2380
 
    def test_update_modify_replace_failed_create(self):
2381
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2382
 
                'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2383
 
                                            'Properties': {'Foo': 'abc'}}}}
2384
 
 
2385
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2386
 
                                  template.Template(tmpl),
2387
 
                                  disable_rollback=True)
2388
 
        self.stack.store()
2389
 
        self.stack.create()
2390
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2391
 
                         self.stack.state)
2392
 
 
2393
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2394
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2395
 
                                             'Properties': {'Foo': 'xyz'}}}}
2396
 
 
2397
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2398
 
                                     template.Template(tmpl2))
2399
 
 
2400
 
        # patch in a dummy handle_create making the replace fail creating
2401
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
2402
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
2403
 
        self.m.ReplayAll()
2404
 
 
2405
 
        self.stack.update(updated_stack)
2406
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
2407
 
                         self.stack.state)
2408
 
        self.m.VerifyAll()
2409
 
 
2410
 
    def test_update_modify_replace_failed_create_and_delete_1(self):
2411
 
        resource._register_class('ResourceWithResourceIDType',
2412
 
                                 generic_rsrc.ResourceWithResourceID)
2413
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2414
 
                'Resources': {'AResource': {'Type':
2415
 
                                            'ResourceWithResourceIDType',
2416
 
                                            'Properties': {'ID': 'a_res'}},
2417
 
                              'BResource': {'Type':
2418
 
                                            'ResourceWithResourceIDType',
2419
 
                                            'Properties': {'ID': 'b_res'},
2420
 
                                            'DependsOn': 'AResource'}}}
2421
 
 
2422
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2423
 
                                  template.Template(tmpl),
2424
 
                                  disable_rollback=True)
2425
 
        self.stack.store()
2426
 
        self.stack.create()
2427
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2428
 
                         self.stack.state)
2429
 
 
2430
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2431
 
                 'Resources': {'AResource': {'Type':
2432
 
                                             'ResourceWithResourceIDType',
2433
 
                                             'Properties': {'ID': 'xyz'}},
2434
 
                               'BResource': {'Type':
2435
 
                                             'ResourceWithResourceIDType',
2436
 
                                             'Properties': {'ID': 'b_res'},
2437
 
                                             'DependsOn': 'AResource'}}}
2438
 
 
2439
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2440
 
                                     template.Template(tmpl2))
2441
 
 
2442
 
        # patch in a dummy handle_create making the replace fail creating
2443
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2444
 
                               'handle_create')
2445
 
        generic_rsrc.ResourceWithResourceID.handle_create().AndRaise(Exception)
2446
 
 
2447
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2448
 
                               'mox_resource_id')
2449
 
        # First, attempts to delete backup_stack. The create (xyz) has been
2450
 
        # failed, so it has no resource_id.
2451
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2452
 
            None).AndReturn(None)
2453
 
        # There are dependency AResource and BResource, so we must delete
2454
 
        # BResource, then delete AResource.
2455
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2456
 
            'b_res').AndReturn(None)
2457
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2458
 
            'a_res').AndReturn(None)
2459
 
        self.m.ReplayAll()
2460
 
 
2461
 
        self.stack.update(updated_stack)
2462
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
2463
 
                         self.stack.state)
2464
 
        self.stack.delete()
2465
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
2466
 
                         self.stack.state)
2467
 
        self.m.VerifyAll()
2468
 
 
2469
 
    def test_update_modify_replace_failed_create_and_delete_2(self):
2470
 
        resource._register_class('ResourceWithResourceIDType',
2471
 
                                 generic_rsrc.ResourceWithResourceID)
2472
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2473
 
                'Resources': {'AResource': {'Type':
2474
 
                                            'ResourceWithResourceIDType',
2475
 
                                            'Properties': {'ID': 'a_res'}},
2476
 
                              'BResource': {'Type':
2477
 
                                            'ResourceWithResourceIDType',
2478
 
                                            'Properties': {'ID': 'b_res'},
2479
 
                                            'DependsOn': 'AResource'}}}
2480
 
 
2481
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2482
 
                                  template.Template(tmpl),
2483
 
                                  disable_rollback=True)
2484
 
        self.stack.store()
2485
 
        self.stack.create()
2486
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2487
 
                         self.stack.state)
2488
 
 
2489
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2490
 
                 'Resources': {'AResource': {'Type':
2491
 
                                             'ResourceWithResourceIDType',
2492
 
                                             'Properties': {'ID': 'c_res'}},
2493
 
                               'BResource': {'Type':
2494
 
                                             'ResourceWithResourceIDType',
2495
 
                                             'Properties': {'ID': 'xyz'},
2496
 
                                             'DependsOn': 'AResource'}}}
2497
 
 
2498
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2499
 
                                     template.Template(tmpl2))
2500
 
 
2501
 
        # patch in a dummy handle_create making the replace fail creating
2502
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2503
 
                               'handle_create')
2504
 
        generic_rsrc.ResourceWithResourceID.handle_create()
2505
 
        generic_rsrc.ResourceWithResourceID.handle_create().AndRaise(Exception)
2506
 
 
2507
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2508
 
                               'mox_resource_id')
2509
 
        # First, attempts to delete backup_stack. The create (xyz) has been
2510
 
        # failed, so it has no resource_id.
2511
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2512
 
            None).AndReturn(None)
2513
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2514
 
            'c_res').AndReturn(None)
2515
 
        # There are dependency AResource and BResource, so we must delete
2516
 
        # BResource, then delete AResource.
2517
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2518
 
            'b_res').AndReturn(None)
2519
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2520
 
            'a_res').AndReturn(None)
2521
 
        self.m.ReplayAll()
2522
 
 
2523
 
        self.stack.update(updated_stack)
2524
 
        # set resource_id for AResource because handle_create() is overwritten
2525
 
        # by the mox.
2526
 
        self.stack.resources['AResource'].resource_id_set('c_res')
2527
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
2528
 
                         self.stack.state)
2529
 
        self.stack.delete()
2530
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
2531
 
                         self.stack.state)
2532
 
        self.m.VerifyAll()
2533
 
 
2534
 
    def test_update_modify_replace_create_in_progress_and_delete_1(self):
2535
 
        resource._register_class('ResourceWithResourceIDType',
2536
 
                                 generic_rsrc.ResourceWithResourceID)
2537
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2538
 
                'Resources': {'AResource': {'Type':
2539
 
                                            'ResourceWithResourceIDType',
2540
 
                                            'Properties': {'ID': 'a_res'}},
2541
 
                              'BResource': {'Type':
2542
 
                                            'ResourceWithResourceIDType',
2543
 
                                            'Properties': {'ID': 'b_res'},
2544
 
                                            'DependsOn': 'AResource'}}}
2545
 
 
2546
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2547
 
                                  template.Template(tmpl),
2548
 
                                  disable_rollback=True)
2549
 
        self.stack.store()
2550
 
        self.stack.create()
2551
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2552
 
                         self.stack.state)
2553
 
 
2554
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2555
 
                 'Resources': {'AResource': {'Type':
2556
 
                                             'ResourceWithResourceIDType',
2557
 
                                             'Properties': {'ID': 'xyz'}},
2558
 
                               'BResource': {'Type':
2559
 
                                             'ResourceWithResourceIDType',
2560
 
                                             'Properties': {'ID': 'b_res'},
2561
 
                                             'DependsOn': 'AResource'}}}
2562
 
 
2563
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2564
 
                                     template.Template(tmpl2))
2565
 
 
2566
 
        # patch in a dummy handle_create making the replace fail creating
2567
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2568
 
                               'handle_create')
2569
 
        generic_rsrc.ResourceWithResourceID.handle_create().AndRaise(Exception)
2570
 
 
2571
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2572
 
                               'mox_resource_id')
2573
 
        # First, attempts to delete backup_stack. The create (xyz) has been
2574
 
        # failed, so it has no resource_id.
2575
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2576
 
            None).AndReturn(None)
2577
 
        # There are dependency AResource and BResource, so we must delete
2578
 
        # BResource, then delete AResource.
2579
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2580
 
            'b_res').AndReturn(None)
2581
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2582
 
            'a_res').AndReturn(None)
2583
 
        self.m.ReplayAll()
2584
 
 
2585
 
        self.stack.update(updated_stack)
2586
 
        # Override stack status and resources status for emulating
2587
 
        # IN_PROGRESS situation
2588
 
        self.stack.state_set(
2589
 
            parser.Stack.UPDATE, parser.Stack.IN_PROGRESS, None)
2590
 
        self.stack.resources['AResource'].state_set(
2591
 
            resource.Resource.CREATE, resource.Resource.IN_PROGRESS, None)
2592
 
        self.stack.delete()
2593
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
2594
 
                         self.stack.state)
2595
 
        self.m.VerifyAll()
2596
 
 
2597
 
    def test_update_modify_replace_create_in_progress_and_delete_2(self):
2598
 
        resource._register_class('ResourceWithResourceIDType',
2599
 
                                 generic_rsrc.ResourceWithResourceID)
2600
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2601
 
                'Resources': {'AResource': {'Type':
2602
 
                                            'ResourceWithResourceIDType',
2603
 
                                            'Properties': {'ID': 'a_res'}},
2604
 
                              'BResource': {'Type':
2605
 
                                            'ResourceWithResourceIDType',
2606
 
                                            'Properties': {'ID': 'b_res'},
2607
 
                                            'DependsOn': 'AResource'}}}
2608
 
 
2609
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2610
 
                                  template.Template(tmpl),
2611
 
                                  disable_rollback=True)
2612
 
        self.stack.store()
2613
 
        self.stack.create()
2614
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2615
 
                         self.stack.state)
2616
 
 
2617
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2618
 
                 'Resources': {'AResource': {'Type':
2619
 
                                             'ResourceWithResourceIDType',
2620
 
                                             'Properties': {'ID': 'c_res'}},
2621
 
                               'BResource': {'Type':
2622
 
                                             'ResourceWithResourceIDType',
2623
 
                                             'Properties': {'ID': 'xyz'},
2624
 
                                             'DependsOn': 'AResource'}}}
2625
 
 
2626
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2627
 
                                     template.Template(tmpl2))
2628
 
 
2629
 
        # patch in a dummy handle_create making the replace fail creating
2630
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2631
 
                               'handle_create')
2632
 
        generic_rsrc.ResourceWithResourceID.handle_create()
2633
 
        generic_rsrc.ResourceWithResourceID.handle_create().AndRaise(Exception)
2634
 
 
2635
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithResourceID,
2636
 
                               'mox_resource_id')
2637
 
        # First, attempts to delete backup_stack. The create (xyz) has been
2638
 
        # failed, so it has no resource_id.
2639
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2640
 
            None).AndReturn(None)
2641
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2642
 
            'c_res').AndReturn(None)
2643
 
        # There are dependency AResource and BResource, so we must delete
2644
 
        # BResource, then delete AResource.
2645
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2646
 
            'b_res').AndReturn(None)
2647
 
        generic_rsrc.ResourceWithResourceID.mox_resource_id(
2648
 
            'a_res').AndReturn(None)
2649
 
        self.m.ReplayAll()
2650
 
 
2651
 
        self.stack.update(updated_stack)
2652
 
        # set resource_id for AResource because handle_create() is overwritten
2653
 
        # by the mox.
2654
 
        self.stack.resources['AResource'].resource_id_set('c_res')
2655
 
        # Override stack status and resources status for emulating
2656
 
        # IN_PROGRESS situation
2657
 
        self.stack.state_set(
2658
 
            parser.Stack.UPDATE, parser.Stack.IN_PROGRESS, None)
2659
 
        self.stack.resources['BResource'].state_set(
2660
 
            resource.Resource.CREATE, resource.Resource.IN_PROGRESS, None)
2661
 
        self.stack.delete()
2662
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
2663
 
                         self.stack.state)
2664
 
        self.m.VerifyAll()
2665
 
 
2666
 
    def test_update_add_signal(self):
2667
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2668
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2669
 
 
2670
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2671
 
                                  template.Template(tmpl))
2672
 
        self.stack.store()
2673
 
        self.stack.create()
2674
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2675
 
                         self.stack.state)
2676
 
 
2677
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2678
 
                 'Resources': {
2679
 
                     'AResource': {'Type': 'GenericResourceType'},
2680
 
                     'BResource': {'Type': 'GenericResourceType'}}}
2681
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2682
 
                                     template.Template(tmpl2))
2683
 
 
2684
 
        updater = scheduler.TaskRunner(self.stack.update_task, updated_stack)
2685
 
        updater.start()
2686
 
        while 'BResource' not in self.stack:
2687
 
            self.assertFalse(updater.step())
2688
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.IN_PROGRESS),
2689
 
                         self.stack.state)
2690
 
 
2691
 
        # Reload the stack from the DB and prove that it contains the new
2692
 
        # resource already
2693
 
        re_stack = parser.Stack.load(utils.dummy_context(), self.stack.id)
2694
 
        self.assertIn('BResource', re_stack)
2695
 
 
2696
 
        updater.run_to_completion()
2697
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
2698
 
                         self.stack.state)
2699
 
        self.assertIn('BResource', self.stack)
2700
 
 
2701
 
    def test_update_add_failed_create(self):
2702
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2703
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2704
 
 
2705
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2706
 
                                  template.Template(tmpl))
2707
 
        self.stack.store()
2708
 
        self.stack.create()
2709
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2710
 
                         self.stack.state)
2711
 
 
2712
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2713
 
                 'Resources': {
2714
 
                     'AResource': {'Type': 'GenericResourceType'},
2715
 
                     'BResource': {'Type': 'GenericResourceType'}}}
2716
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2717
 
                                     template.Template(tmpl2))
2718
 
 
2719
 
        # patch in a dummy handle_create making BResource fail creating
2720
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
2721
 
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
2722
 
        self.m.ReplayAll()
2723
 
 
2724
 
        self.stack.update(updated_stack)
2725
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
2726
 
                         self.stack.state)
2727
 
        self.assertIn('BResource', self.stack)
2728
 
 
2729
 
        # Reload the stack from the DB and prove that it contains the failed
2730
 
        # resource (to ensure it will be deleted on stack delete)
2731
 
        re_stack = parser.Stack.load(utils.dummy_context(), self.stack.id)
2732
 
        self.assertIn('BResource', re_stack)
2733
 
        self.m.VerifyAll()
2734
 
 
2735
 
    def test_update_add_failed_create_rollback_failed(self):
2736
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2737
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2738
 
 
2739
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2740
 
                                  template.Template(tmpl))
2741
 
        self.stack.store()
2742
 
        self.stack.create()
2743
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2744
 
                         self.stack.state)
2745
 
 
2746
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2747
 
                 'Resources': {
2748
 
                     'AResource': {'Type': 'GenericResourceType'},
2749
 
                     'BResource': {'Type': 'GenericResourceType'}}}
2750
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2751
 
                                     template.Template(tmpl2),
2752
 
                                     disable_rollback=False)
2753
 
 
2754
 
        # patch in a dummy handle_create making BResource fail creating
2755
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
2756
 
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
2757
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_delete')
2758
 
        generic_rsrc.GenericResource.handle_delete().AndRaise(Exception)
2759
 
        self.m.ReplayAll()
2760
 
 
2761
 
        self.stack.update(updated_stack)
2762
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.FAILED),
2763
 
                         self.stack.state)
2764
 
        self.assertIn('BResource', self.stack)
2765
 
 
2766
 
        # Reload the stack from the DB and prove that it contains the failed
2767
 
        # resource (to ensure it will be deleted on stack delete)
2768
 
        re_stack = parser.Stack.load(utils.dummy_context(), self.stack.id)
2769
 
        self.assertIn('BResource', re_stack)
2770
 
        self.m.VerifyAll()
2771
 
 
2772
 
    def test_update_rollback(self):
2773
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2774
 
                'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2775
 
                                            'Properties': {'Foo': 'abc'}}}}
2776
 
 
2777
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2778
 
                                  template.Template(tmpl),
2779
 
                                  disable_rollback=False)
2780
 
        self.stack.store()
2781
 
        self.stack.create()
2782
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2783
 
                         self.stack.state)
2784
 
 
2785
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2786
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2787
 
                                             'Properties': {'Foo': 'xyz'}}}}
2788
 
 
2789
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2790
 
                                     template.Template(tmpl2),
2791
 
                                     disable_rollback=False)
2792
 
 
2793
 
        # patch in a dummy handle_create making the replace fail when creating
2794
 
        # the replacement rsrc
2795
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
2796
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
2797
 
        self.m.ReplayAll()
2798
 
 
2799
 
        with mock.patch.object(self.stack, 'state_set',
2800
 
                               side_effect=self.stack.state_set) as mock_state:
2801
 
            self.stack.update(updated_stack)
2802
 
            self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
2803
 
                             self.stack.state)
2804
 
            self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
2805
 
            self.assertEqual(2, mock_state.call_count)
2806
 
            self.assertEqual(('UPDATE', 'IN_PROGRESS'),
2807
 
                             mock_state.call_args_list[0][0][:2])
2808
 
            self.assertEqual(('ROLLBACK', 'IN_PROGRESS'),
2809
 
                             mock_state.call_args_list[1][0][:2])
2810
 
        self.m.VerifyAll()
2811
 
 
2812
 
    def test_update_rollback_on_cancel_event(self):
2813
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2814
 
                'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2815
 
                                            'Properties': {'Foo': 'abc'}}}}
2816
 
 
2817
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2818
 
                                  template.Template(tmpl),
2819
 
                                  disable_rollback=False)
2820
 
        self.stack.store()
2821
 
        self.stack.create()
2822
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2823
 
                         self.stack.state)
2824
 
 
2825
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2826
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2827
 
                                             'Properties': {'Foo': 'xyz'}},
2828
 
                               }}
2829
 
 
2830
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2831
 
                                     template.Template(tmpl2),
2832
 
                                     disable_rollback=False)
2833
 
        evt_mock = mock.MagicMock()
2834
 
        evt_mock.ready.return_value = True
2835
 
        evt_mock.wait.return_value = 'cancel'
2836
 
 
2837
 
        self.m.ReplayAll()
2838
 
 
2839
 
        self.stack.update(updated_stack, event=evt_mock)
2840
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
2841
 
                         self.stack.state)
2842
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
2843
 
        self.m.VerifyAll()
2844
 
 
2845
 
    def test_update_rollback_fail(self):
2846
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2847
 
                'Parameters': {'AParam': {'Type': 'String'}},
2848
 
                'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2849
 
                                            'Properties': {'Foo': 'abc'}}}}
2850
 
 
2851
 
        env1 = {'parameters': {'AParam': 'abc'}}
2852
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2853
 
                                  template.Template(tmpl),
2854
 
                                  disable_rollback=False,
2855
 
                                  env=environment.Environment(env1))
2856
 
        self.stack.store()
2857
 
        self.stack.create()
2858
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2859
 
                         self.stack.state)
2860
 
 
2861
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2862
 
                 'Parameters': {'BParam': {'Type': 'String'}},
2863
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2864
 
                                             'Properties': {'Foo': 'xyz'}}}}
2865
 
 
2866
 
        env2 = {'parameters': {'BParam': 'smelly'}}
2867
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2868
 
                                     template.Template(tmpl2),
2869
 
                                     disable_rollback=False,
2870
 
                                     env=environment.Environment(env2))
2871
 
 
2872
 
        # patch in a dummy handle_create making the replace fail when creating
2873
 
        # the replacement rsrc, and again on the second call (rollback)
2874
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
2875
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_delete')
2876
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
2877
 
        generic_rsrc.ResourceWithProps.handle_delete().AndRaise(Exception)
2878
 
        self.m.ReplayAll()
2879
 
 
2880
 
        self.stack.update(updated_stack)
2881
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.FAILED),
2882
 
                         self.stack.state)
2883
 
        self.m.VerifyAll()
2884
 
 
2885
 
    def test_update_rollback_add(self):
2886
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2887
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2888
 
 
2889
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2890
 
                                  template.Template(tmpl),
2891
 
                                  disable_rollback=False)
2892
 
        self.stack.store()
2893
 
        self.stack.create()
2894
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2895
 
                         self.stack.state)
2896
 
 
2897
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2898
 
                 'Resources': {
2899
 
                     'AResource': {'Type': 'GenericResourceType'},
2900
 
                     'BResource': {'Type': 'GenericResourceType'}}}
2901
 
 
2902
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2903
 
                                     template.Template(tmpl2),
2904
 
                                     disable_rollback=False)
2905
 
 
2906
 
        # patch in a dummy handle_create making the replace fail when creating
2907
 
        # the replacement rsrc, and succeed on the second call (rollback)
2908
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
2909
 
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
2910
 
        self.m.ReplayAll()
2911
 
 
2912
 
        self.stack.update(updated_stack)
2913
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
2914
 
                         self.stack.state)
2915
 
        self.assertNotIn('BResource', self.stack)
2916
 
        self.m.VerifyAll()
2917
 
 
2918
 
    def test_update_rollback_remove(self):
2919
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2920
 
                'Resources': {
2921
 
                    'AResource': {'Type': 'GenericResourceType'},
2922
 
                    'BResource': {'Type': 'ResourceWithPropsType'}}}
2923
 
 
2924
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2925
 
                                  template.Template(tmpl),
2926
 
                                  disable_rollback=False)
2927
 
        self.stack.store()
2928
 
        self.stack.create()
2929
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2930
 
                         self.stack.state)
2931
 
 
2932
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2933
 
                 'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
2934
 
 
2935
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2936
 
                                     template.Template(tmpl2),
2937
 
                                     disable_rollback=False)
2938
 
 
2939
 
        # patch in a dummy delete making the destroy fail
2940
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
2941
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_delete')
2942
 
        generic_rsrc.ResourceWithProps.handle_delete().AndRaise(Exception)
2943
 
        # replace the failed resource on rollback
2944
 
        generic_rsrc.ResourceWithProps.handle_create()
2945
 
        generic_rsrc.ResourceWithProps.handle_delete()
2946
 
        self.m.ReplayAll()
2947
 
 
2948
 
        self.stack.update(updated_stack)
2949
 
 
2950
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
2951
 
                         self.stack.state)
2952
 
        self.assertIn('BResource', self.stack)
2953
 
        self.m.VerifyAll()
2954
 
        # Unset here so delete() is not stubbed for stack.delete cleanup
2955
 
        self.m.UnsetStubs()
2956
 
 
2957
 
    def test_update_rollback_replace(self):
2958
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
2959
 
                'Resources': {
2960
 
                    'AResource': {'Type': 'ResourceWithPropsType',
2961
 
                                  'Properties': {'Foo': 'foo'}}}}
2962
 
 
2963
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
2964
 
                                  template.Template(tmpl),
2965
 
                                  disable_rollback=False)
2966
 
        self.stack.store()
2967
 
        self.stack.create()
2968
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
2969
 
                         self.stack.state)
2970
 
 
2971
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
2972
 
                 'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
2973
 
                                             'Properties': {'Foo': 'bar'}}}}
2974
 
 
2975
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
2976
 
                                     template.Template(tmpl2),
2977
 
                                     disable_rollback=False)
2978
 
 
2979
 
        # patch in a dummy delete making the destroy fail
2980
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_delete')
2981
 
        generic_rsrc.ResourceWithProps.handle_delete().AndRaise(Exception)
2982
 
        generic_rsrc.ResourceWithProps.handle_delete().AndReturn(None)
2983
 
        generic_rsrc.ResourceWithProps.handle_delete().AndReturn(None)
2984
 
        self.m.ReplayAll()
2985
 
 
2986
 
        self.stack.update(updated_stack)
2987
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
2988
 
                         self.stack.state)
2989
 
        self.m.VerifyAll()
2990
 
        # Unset here so delete() is not stubbed for stack.delete cleanup
2991
 
        self.m.UnsetStubs()
2992
 
 
2993
 
    def test_update_replace_by_reference(self):
2994
 
        '''
2995
 
        assertion:
2996
 
        changes in dynamic attributes, due to other resources been updated
2997
 
        are not ignored and can cause dependent resources to be updated.
2998
 
        '''
2999
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3000
 
                'Resources': {
3001
 
                    'AResource': {'Type': 'ResourceWithPropsType',
3002
 
                                  'Properties': {'Foo': 'abc'}},
3003
 
                    'BResource': {'Type': 'ResourceWithPropsType',
3004
 
                                  'Properties': {
3005
 
                                      'Foo': {'Ref': 'AResource'}}}}}
3006
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
3007
 
                 'Resources': {
3008
 
                     'AResource': {'Type': 'ResourceWithPropsType',
3009
 
                                   'Properties': {'Foo': 'smelly'}},
3010
 
                     'BResource': {'Type': 'ResourceWithPropsType',
3011
 
                                   'Properties': {
3012
 
                                       'Foo': {'Ref': 'AResource'}}}}}
3013
 
 
3014
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3015
 
                                  template.Template(tmpl))
3016
 
 
3017
 
        self.stack.store()
3018
 
        self.stack.create()
3019
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3020
 
                         self.stack.state)
3021
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3022
 
        self.assertEqual('AResource',
3023
 
                         self.stack['BResource'].properties['Foo'])
3024
 
 
3025
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'FnGetRefId')
3026
 
        generic_rsrc.ResourceWithProps.FnGetRefId().AndReturn(
3027
 
            'AResource')
3028
 
        generic_rsrc.ResourceWithProps.FnGetRefId().MultipleTimes().AndReturn(
3029
 
            'inst-007')
3030
 
        self.m.ReplayAll()
3031
 
 
3032
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3033
 
                                     template.Template(tmpl2))
3034
 
        self.stack.update(updated_stack)
3035
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3036
 
                         self.stack.state)
3037
 
        self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
3038
 
        self.assertEqual('inst-007', self.stack['BResource'].properties['Foo'])
3039
 
        self.m.VerifyAll()
3040
 
 
3041
 
    def test_update_with_new_resources_with_reference(self):
3042
 
        '''
3043
 
        assertion:
3044
 
        check, that during update with new resources which one has
3045
 
        reference on second, reference will be correct resolved.
3046
 
        '''
3047
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3048
 
                'Resources': {
3049
 
                    'CResource': {'Type': 'ResourceWithPropsType',
3050
 
                                  'Properties': {'Foo': 'abc'}}}}
3051
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
3052
 
                 'Resources': {
3053
 
                     'CResource': {'Type': 'ResourceWithPropsType',
3054
 
                                   'Properties': {'Foo': 'abc'}},
3055
 
                     'AResource': {'Type': 'ResourceWithPropsType',
3056
 
                                   'Properties': {'Foo': 'smelly'}},
3057
 
                     'BResource': {'Type': 'ResourceWithPropsType',
3058
 
                                   'Properties': {
3059
 
                                       'Foo': {'Ref': 'AResource'}}}}}
3060
 
 
3061
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3062
 
                                  template.Template(tmpl))
3063
 
 
3064
 
        self.stack.store()
3065
 
        self.stack.create()
3066
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3067
 
                         self.stack.state)
3068
 
        self.assertEqual('abc', self.stack['CResource'].properties['Foo'])
3069
 
        self.assertEqual(1, len(self.stack.resources))
3070
 
 
3071
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
3072
 
 
3073
 
        generic_rsrc.ResourceWithProps.handle_create().MultipleTimes(
3074
 
        ).AndReturn(None)
3075
 
 
3076
 
        self.m.ReplayAll()
3077
 
 
3078
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3079
 
                                     template.Template(tmpl2))
3080
 
        self.stack.update(updated_stack)
3081
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3082
 
                         self.stack.state)
3083
 
        self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
3084
 
        self.assertEqual('AResource',
3085
 
                         self.stack['BResource'].properties['Foo'])
3086
 
 
3087
 
        self.assertEqual(3, len(self.stack.resources))
3088
 
        self.m.VerifyAll()
3089
 
 
3090
 
    def test_update_by_reference_and_rollback_1(self):
3091
 
        '''
3092
 
        assertion:
3093
 
        check that rollback still works with dynamic metadata
3094
 
        this test fails the first instance
3095
 
        '''
3096
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3097
 
                'Resources': {
3098
 
                    'AResource': {'Type': 'ResourceWithPropsType',
3099
 
                                  'Properties': {'Foo': 'abc'}},
3100
 
                    'BResource': {'Type': 'ResourceWithPropsType',
3101
 
                                  'Properties': {
3102
 
                                      'Foo': {'Ref': 'AResource'}}}}}
3103
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
3104
 
                 'Resources': {
3105
 
                     'AResource': {'Type': 'ResourceWithPropsType',
3106
 
                                   'Properties': {'Foo': 'smelly'}},
3107
 
                     'BResource': {'Type': 'ResourceWithPropsType',
3108
 
                                   'Properties': {
3109
 
                                       'Foo': {'Ref': 'AResource'}}}}}
3110
 
 
3111
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3112
 
                                  template.Template(tmpl),
3113
 
                                  disable_rollback=False)
3114
 
 
3115
 
        self.stack.store()
3116
 
        self.stack.create()
3117
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3118
 
                         self.stack.state)
3119
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3120
 
        self.assertEqual('AResource',
3121
 
                         self.stack['BResource'].properties['Foo'])
3122
 
 
3123
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'FnGetRefId')
3124
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
3125
 
 
3126
 
        generic_rsrc.ResourceWithProps.FnGetRefId().MultipleTimes().AndReturn(
3127
 
            'AResource')
3128
 
 
3129
 
        # mock to make the replace fail when creating the replacement resource
3130
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
3131
 
 
3132
 
        self.m.ReplayAll()
3133
 
 
3134
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3135
 
                                     template.Template(tmpl2),
3136
 
                                     disable_rollback=False)
3137
 
        self.stack.update(updated_stack)
3138
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
3139
 
                         self.stack.state)
3140
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3141
 
 
3142
 
        self.m.VerifyAll()
3143
 
 
3144
 
    def test_update_by_reference_and_rollback_2(self):
3145
 
        '''
3146
 
        assertion:
3147
 
        check that rollback still works with dynamic metadata
3148
 
        this test fails the second instance
3149
 
        '''
3150
 
 
3151
 
        class ResourceTypeA(generic_rsrc.ResourceWithProps):
3152
 
            count = 0
3153
 
 
3154
 
            def handle_create(self):
3155
 
                ResourceTypeA.count += 1
3156
 
                self.resource_id_set('%s%d' % (self.name, self.count))
3157
 
 
3158
 
        resource._register_class('ResourceTypeA', ResourceTypeA)
3159
 
 
3160
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3161
 
                'Resources': {
3162
 
                    'AResource': {'Type': 'ResourceTypeA',
3163
 
                                  'Properties': {'Foo': 'abc'}},
3164
 
                    'BResource': {'Type': 'ResourceWithPropsType',
3165
 
                                  'Properties': {
3166
 
                                      'Foo': {'Ref': 'AResource'}}}}}
3167
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
3168
 
                 'Resources': {
3169
 
                     'AResource': {'Type': 'ResourceTypeA',
3170
 
                                   'Properties': {'Foo': 'smelly'}},
3171
 
                     'BResource': {'Type': 'ResourceWithPropsType',
3172
 
                                   'Properties': {
3173
 
                                       'Foo': {'Ref': 'AResource'}}}}}
3174
 
 
3175
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3176
 
                                  template.Template(tmpl),
3177
 
                                  disable_rollback=False)
3178
 
 
3179
 
        self.stack.store()
3180
 
        self.stack.create()
3181
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3182
 
                         self.stack.state)
3183
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3184
 
        self.assertEqual('AResource1',
3185
 
                         self.stack['BResource'].properties['Foo'])
3186
 
 
3187
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
3188
 
 
3189
 
        # mock to make the replace fail when creating the second
3190
 
        # replacement resource
3191
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
3192
 
 
3193
 
        self.m.ReplayAll()
3194
 
 
3195
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3196
 
                                     template.Template(tmpl2),
3197
 
                                     disable_rollback=False)
3198
 
        self.stack.update(updated_stack)
3199
 
        self.assertEqual((parser.Stack.ROLLBACK, parser.Stack.COMPLETE),
3200
 
                         self.stack.state)
3201
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3202
 
        self.assertEqual('AResource1',
3203
 
                         self.stack['BResource'].properties['Foo'])
3204
 
 
3205
 
        self.m.VerifyAll()
3206
 
 
3207
 
    def test_update_failure_recovery(self):
3208
 
        '''
3209
 
        assertion:
3210
 
        check that rollback still works with dynamic metadata
3211
 
        this test fails the second instance
3212
 
        '''
3213
 
 
3214
 
        class ResourceTypeA(generic_rsrc.ResourceWithProps):
3215
 
            count = 0
3216
 
 
3217
 
            def handle_create(self):
3218
 
                ResourceTypeA.count += 1
3219
 
                self.resource_id_set('%s%d' % (self.name, self.count))
3220
 
 
3221
 
            def handle_delete(self):
3222
 
                return super(ResourceTypeA, self).handle_delete()
3223
 
 
3224
 
        resource._register_class('ResourceTypeA', ResourceTypeA)
3225
 
 
3226
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3227
 
                'Resources': {
3228
 
                    'AResource': {'Type': 'ResourceTypeA',
3229
 
                                  'Properties': {'Foo': 'abc'}},
3230
 
                    'BResource': {'Type': 'ResourceWithPropsType',
3231
 
                                  'Properties': {
3232
 
                                      'Foo': {'Ref': 'AResource'}}}}}
3233
 
        tmpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
3234
 
                 'Resources': {
3235
 
                     'AResource': {'Type': 'ResourceTypeA',
3236
 
                                   'Properties': {'Foo': 'smelly'}},
3237
 
                     'BResource': {'Type': 'ResourceWithPropsType',
3238
 
                                   'Properties': {
3239
 
                                       'Foo': {'Ref': 'AResource'}}}}}
3240
 
 
3241
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3242
 
                                  template.Template(tmpl),
3243
 
                                  disable_rollback=True)
3244
 
 
3245
 
        self.stack.store()
3246
 
        self.stack.create()
3247
 
 
3248
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3249
 
                         self.stack.state)
3250
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3251
 
        self.assertEqual('AResource1',
3252
 
                         self.stack['BResource'].properties['Foo'])
3253
 
 
3254
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
3255
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_delete')
3256
 
        self.m.StubOutWithMock(ResourceTypeA, 'handle_delete')
3257
 
 
3258
 
        # mock to make the replace fail when creating the second
3259
 
        # replacement resource
3260
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
3261
 
        # delete the old resource on the second update
3262
 
        generic_rsrc.ResourceWithProps.handle_delete()
3263
 
        ResourceTypeA.handle_delete()
3264
 
        generic_rsrc.ResourceWithProps.handle_create()
3265
 
        generic_rsrc.ResourceWithProps.handle_delete()
3266
 
 
3267
 
        self.m.ReplayAll()
3268
 
 
3269
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3270
 
                                     template.Template(tmpl2),
3271
 
                                     disable_rollback=True)
3272
 
        self.stack.update(updated_stack)
3273
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
3274
 
                         self.stack.state)
3275
 
        self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
3276
 
 
3277
 
        self.stack = parser.Stack.load(self.ctx, self.stack.id)
3278
 
        updated_stack2 = parser.Stack(self.ctx, 'updated_stack',
3279
 
                                      template.Template(tmpl2),
3280
 
                                      disable_rollback=True)
3281
 
 
3282
 
        self.stack.update(updated_stack2)
3283
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3284
 
                         self.stack.state)
3285
 
        self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
3286
 
        self.assertEqual('AResource2',
3287
 
                         self.stack['BResource'].properties['Foo'])
3288
 
 
3289
 
        self.m.VerifyAll()
3290
 
 
3291
 
    def test_update_failure_recovery_new_param(self):
3292
 
        '''
3293
 
        assertion:
3294
 
        check that rollback still works with dynamic metadata
3295
 
        this test fails the second instance
3296
 
        '''
3297
 
 
3298
 
        class ResourceTypeA(generic_rsrc.ResourceWithProps):
3299
 
            count = 0
3300
 
 
3301
 
            def handle_create(self):
3302
 
                ResourceTypeA.count += 1
3303
 
                self.resource_id_set('%s%d' % (self.name, self.count))
3304
 
 
3305
 
            def handle_delete(self):
3306
 
                return super(ResourceTypeA, self).handle_delete()
3307
 
 
3308
 
        resource._register_class('ResourceTypeA', ResourceTypeA)
3309
 
 
3310
 
        tmpl = {
3311
 
            'HeatTemplateFormatVersion': '2012-12-12',
3312
 
            'Parameters': {
3313
 
                'abc-param': {'Type': 'String'}
3314
 
            },
3315
 
            'Resources': {
3316
 
                'AResource': {'Type': 'ResourceTypeA',
3317
 
                              'Properties': {'Foo': {'Ref': 'abc-param'}}},
3318
 
                'BResource': {'Type': 'ResourceWithPropsType',
3319
 
                              'Properties': {'Foo': {'Ref': 'AResource'}}}
3320
 
            }
3321
 
        }
3322
 
        env1 = environment.Environment({'abc-param': 'abc'})
3323
 
        tmpl2 = {
3324
 
            'HeatTemplateFormatVersion': '2012-12-12',
3325
 
            'Parameters': {
3326
 
                'smelly-param': {'Type': 'String'}
3327
 
            },
3328
 
            'Resources': {
3329
 
                'AResource': {'Type': 'ResourceTypeA',
3330
 
                              'Properties': {'Foo': {'Ref': 'smelly-param'}}},
3331
 
                'BResource': {'Type': 'ResourceWithPropsType',
3332
 
                              'Properties': {'Foo': {'Ref': 'AResource'}}}
3333
 
            }
3334
 
        }
3335
 
        env2 = environment.Environment({'smelly-param': 'smelly'})
3336
 
 
3337
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3338
 
                                  template.Template(tmpl), env1,
3339
 
                                  disable_rollback=True)
3340
 
 
3341
 
        self.stack.store()
3342
 
        self.stack.create()
3343
 
 
3344
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3345
 
                         self.stack.state)
3346
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3347
 
        self.assertEqual('AResource1',
3348
 
                         self.stack['BResource'].properties['Foo'])
3349
 
 
3350
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
3351
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_delete')
3352
 
        self.m.StubOutWithMock(ResourceTypeA, 'handle_delete')
3353
 
 
3354
 
        # mock to make the replace fail when creating the second
3355
 
        # replacement resource
3356
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
3357
 
        # delete the old resource on the second update
3358
 
        generic_rsrc.ResourceWithProps.handle_delete()
3359
 
        ResourceTypeA.handle_delete()
3360
 
        generic_rsrc.ResourceWithProps.handle_create()
3361
 
        generic_rsrc.ResourceWithProps.handle_delete()
3362
 
 
3363
 
        self.m.ReplayAll()
3364
 
 
3365
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3366
 
                                     template.Template(tmpl2), env2,
3367
 
                                     disable_rollback=True)
3368
 
        self.stack.update(updated_stack)
3369
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.FAILED),
3370
 
                         self.stack.state)
3371
 
        self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
3372
 
 
3373
 
        self.stack = parser.Stack.load(self.ctx, self.stack.id)
3374
 
        updated_stack2 = parser.Stack(self.ctx, 'updated_stack',
3375
 
                                      template.Template(tmpl2), env2,
3376
 
                                      disable_rollback=True)
3377
 
 
3378
 
        self.stack.update(updated_stack2)
3379
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3380
 
                         self.stack.state)
3381
 
 
3382
 
        self.stack = parser.Stack.load(self.ctx, self.stack.id)
3383
 
        self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
3384
 
        self.assertEqual('AResource2',
3385
 
                         self.stack['BResource'].properties['Foo'])
3386
 
 
3387
 
        self.m.VerifyAll()
3388
 
 
3389
 
    def test_create_failure_recovery(self):
3390
 
        '''
3391
 
        assertion:
3392
 
        check that rollback still works with dynamic metadata
3393
 
        this test fails the second instance
3394
 
        '''
3395
 
 
3396
 
        class ResourceTypeA(generic_rsrc.ResourceWithProps):
3397
 
            count = 0
3398
 
 
3399
 
            def handle_create(self):
3400
 
                ResourceTypeA.count += 1
3401
 
                self.resource_id_set('%s%d' % (self.name, self.count))
3402
 
 
3403
 
            def handle_delete(self):
3404
 
                return super(ResourceTypeA, self).handle_delete()
3405
 
 
3406
 
        resource._register_class('ResourceTypeA', ResourceTypeA)
3407
 
 
3408
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3409
 
                'Resources': {
3410
 
                    'AResource': {'Type': 'ResourceTypeA',
3411
 
                                  'Properties': {'Foo': 'abc'}},
3412
 
                    'BResource': {'Type': 'ResourceWithPropsType',
3413
 
                                  'Properties': {
3414
 
                                      'Foo': {'Ref': 'AResource'}}}}}
3415
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3416
 
                                  template.Template(tmpl),
3417
 
                                  disable_rollback=True)
3418
 
 
3419
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_create')
3420
 
        self.m.StubOutWithMock(generic_rsrc.ResourceWithProps, 'handle_delete')
3421
 
        self.m.StubOutWithMock(ResourceTypeA, 'handle_delete')
3422
 
 
3423
 
        # create
3424
 
        generic_rsrc.ResourceWithProps.handle_create().AndRaise(Exception)
3425
 
 
3426
 
        # update
3427
 
        generic_rsrc.ResourceWithProps.handle_delete()
3428
 
        generic_rsrc.ResourceWithProps.handle_create()
3429
 
 
3430
 
        self.m.ReplayAll()
3431
 
 
3432
 
        self.stack.store()
3433
 
        self.stack.create()
3434
 
 
3435
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.FAILED),
3436
 
                         self.stack.state)
3437
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3438
 
 
3439
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3440
 
                                     template.Template(tmpl),
3441
 
                                     disable_rollback=True)
3442
 
        self.stack.update(updated_stack)
3443
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3444
 
                         self.stack.state)
3445
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3446
 
        self.assertEqual('AResource1',
3447
 
                         self.stack['BResource'].properties['Foo'])
3448
 
 
3449
 
        self.m.VerifyAll()
3450
 
 
3451
 
    def test_update_replace_parameters(self):
3452
 
        '''
3453
 
        assertion:
3454
 
        changes in static environment parameters
3455
 
        are not ignored and can cause dependent resources to be updated.
3456
 
        '''
3457
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3458
 
                'Parameters': {'AParam': {'Type': 'String'}},
3459
 
                'Resources': {
3460
 
                    'AResource': {'Type': 'ResourceWithPropsType',
3461
 
                                  'Properties': {'Foo': {'Ref': 'AParam'}}}}}
3462
 
 
3463
 
        env1 = {'parameters': {'AParam': 'abc'}}
3464
 
        env2 = {'parameters': {'AParam': 'smelly'}}
3465
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3466
 
                                  template.Template(tmpl),
3467
 
                                  environment.Environment(env1))
3468
 
 
3469
 
        self.stack.store()
3470
 
        self.stack.create()
3471
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3472
 
                         self.stack.state)
3473
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3474
 
 
3475
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3476
 
                                     template.Template(tmpl),
3477
 
                                     environment.Environment(env2))
3478
 
        self.stack.update(updated_stack)
3479
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3480
 
                         self.stack.state)
3481
 
        self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
3482
 
 
3483
 
    def test_update_deletion_policy(self):
3484
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3485
 
                'Resources': {
3486
 
                    'AResource': {'Type': 'ResourceWithPropsType',
3487
 
                                  'Properties': {'Foo': 'Bar'}}}}
3488
 
 
3489
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3490
 
                                  template.Template(tmpl))
3491
 
 
3492
 
        self.stack.store()
3493
 
        self.stack.create()
3494
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3495
 
                         self.stack.state)
3496
 
        resource_id = self.stack['AResource'].id
3497
 
 
3498
 
        new_tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3499
 
                    'Resources': {
3500
 
                        'AResource': {'Type': 'ResourceWithPropsType',
3501
 
                                      'DeletionPolicy': 'Retain',
3502
 
                                      'Properties': {'Foo': 'Bar'}}}}
3503
 
 
3504
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3505
 
                                     template.Template(new_tmpl))
3506
 
        self.stack.update(updated_stack)
3507
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3508
 
                         self.stack.state)
3509
 
 
3510
 
        self.assertEqual(resource_id, self.stack['AResource'].id)
3511
 
 
3512
 
    def test_update_deletion_policy_no_handle_update(self):
3513
 
 
3514
 
        class ResourceWithNoUpdate(resource.Resource):
3515
 
            properties_schema = {'Foo': {'Type': 'String'}}
3516
 
 
3517
 
        resource._register_class('ResourceWithNoUpdate',
3518
 
                                 ResourceWithNoUpdate)
3519
 
 
3520
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3521
 
                'Resources': {
3522
 
                    'AResource': {'Type': 'ResourceWithNoUpdate',
3523
 
                                  'Properties': {'Foo': 'Bar'}}}}
3524
 
 
3525
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3526
 
                                  template.Template(tmpl))
3527
 
 
3528
 
        self.stack.store()
3529
 
        self.stack.create()
3530
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3531
 
                         self.stack.state)
3532
 
        resource_id = self.stack['AResource'].id
3533
 
 
3534
 
        new_tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3535
 
                    'Resources': {
3536
 
                        'AResource': {'Type': 'ResourceWithNoUpdate',
3537
 
                                      'DeletionPolicy': 'Retain',
3538
 
                                      'Properties': {'Foo': 'Bar'}}}}
3539
 
 
3540
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3541
 
                                     template.Template(new_tmpl))
3542
 
        self.stack.update(updated_stack)
3543
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3544
 
                         self.stack.state)
3545
 
 
3546
 
        self.assertEqual(resource_id, self.stack['AResource'].id)
3547
 
 
3548
 
    def test_update_template_format_version(self):
3549
 
        tmpl = {
3550
 
            'HeatTemplateFormatVersion': '2012-12-12',
3551
 
            'Parameters': {
3552
 
                'AParam': {'Type': 'String', 'Default': 'abc'}},
3553
 
            'Resources': {
3554
 
                'AResource': {
3555
 
                    'Type': 'ResourceWithPropsType',
3556
 
                    'Properties': {'Foo': {'Ref': 'AParam'}}
3557
 
                },
3558
 
            }
3559
 
        }
3560
 
 
3561
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3562
 
                                  template.Template(tmpl))
3563
 
        self.stack.store()
3564
 
        self.stack.create()
3565
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3566
 
                         self.stack.state)
3567
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
3568
 
 
3569
 
        tmpl2 = {
3570
 
            'heat_template_version': '2013-05-23',
3571
 
            'parameters': {
3572
 
                'AParam': {'type': 'string', 'default': 'foo'}},
3573
 
            'resources': {
3574
 
                'AResource': {
3575
 
                    'type': 'ResourceWithPropsType',
3576
 
                    'properties': {'Foo': {'get_param': 'AParam'}}
3577
 
                }
3578
 
            }
3579
 
        }
3580
 
 
3581
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
3582
 
                                     template.Template(tmpl2))
3583
 
 
3584
 
        self.m.ReplayAll()
3585
 
 
3586
 
        self.stack.update(updated_stack)
3587
 
        self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
3588
 
                         self.stack.state)
3589
 
        self.assertEqual('foo', self.stack['AResource'].properties['Foo'])
3590
 
        self.m.VerifyAll()
3591
 
 
3592
 
    def test_stack_create_timeout(self):
3593
 
        self.m.StubOutWithMock(scheduler.DependencyTaskGroup, '__call__')
3594
 
        self.m.StubOutWithMock(scheduler, 'wallclock')
3595
 
 
3596
 
        stack = parser.Stack(self.ctx, 's', self.tmpl)
3597
 
 
3598
 
        def dummy_task():
3599
 
            while True:
3600
 
                yield
3601
 
 
3602
 
        start_time = time.time()
3603
 
        scheduler.wallclock().AndReturn(start_time)
3604
 
        scheduler.wallclock().AndReturn(start_time + 1)
3605
 
        scheduler.DependencyTaskGroup.__call__().AndReturn(dummy_task())
3606
 
        scheduler.wallclock().AndReturn(start_time + stack.timeout_secs() + 1)
3607
 
 
3608
 
        self.m.ReplayAll()
3609
 
 
3610
 
        stack.create()
3611
 
 
3612
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.FAILED),
3613
 
                         stack.state)
3614
 
        self.assertEqual('Create timed out', stack.status_reason)
3615
 
 
3616
 
        self.m.VerifyAll()
3617
 
 
3618
 
    def test_stack_delete_timeout(self):
3619
 
        stack = parser.Stack(self.ctx, 'delete_test',
3620
 
                             self.tmpl)
3621
 
        stack_id = stack.store()
3622
 
 
3623
 
        db_s = db_api.stack_get(self.ctx, stack_id)
3624
 
        self.assertIsNotNone(db_s)
3625
 
 
3626
 
        self.m.StubOutWithMock(scheduler.DependencyTaskGroup, '__call__')
3627
 
        self.m.StubOutWithMock(scheduler, 'wallclock')
3628
 
 
3629
 
        def dummy_task():
3630
 
            while True:
3631
 
                yield
3632
 
 
3633
 
        start_time = time.time()
3634
 
        scheduler.wallclock().AndReturn(start_time)
3635
 
        scheduler.wallclock().AndReturn(start_time + 1)
3636
 
        scheduler.DependencyTaskGroup.__call__().AndReturn(dummy_task())
3637
 
        scheduler.wallclock().AndReturn(start_time + stack.timeout_secs() + 1)
3638
 
        self.m.ReplayAll()
3639
 
        stack.delete()
3640
 
 
3641
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.FAILED),
3642
 
                         stack.state)
3643
 
        self.assertEqual('Delete timed out', stack.status_reason)
3644
 
 
3645
 
        self.m.VerifyAll()
3646
 
 
3647
 
    def test_stack_delete_resourcefailure(self):
3648
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3649
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
3650
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_delete')
3651
 
        exc = Exception('foo')
3652
 
        generic_rsrc.GenericResource.handle_delete().AndRaise(exc)
3653
 
        self.m.ReplayAll()
3654
 
 
3655
 
        self.stack = parser.Stack(self.ctx, 'delete_test_fail',
3656
 
                                  parser.Template(tmpl))
3657
 
 
3658
 
        self.stack.store()
3659
 
        self.stack.create()
3660
 
        self.assertEqual((self.stack.CREATE, self.stack.COMPLETE),
3661
 
                         self.stack.state)
3662
 
 
3663
 
        self.stack.delete()
3664
 
 
3665
 
        self.assertEqual((self.stack.DELETE, self.stack.FAILED),
3666
 
                         self.stack.state)
3667
 
        self.assertEqual('Resource DELETE failed: Exception: foo',
3668
 
                         self.stack.status_reason)
3669
 
        self.m.VerifyAll()
3670
 
 
3671
 
    def test_stack_name_valid(self):
3672
 
        stack = parser.Stack(self.ctx, 's', self.tmpl)
3673
 
        self.assertIsInstance(stack, parser.Stack)
3674
 
        stack = parser.Stack(self.ctx, 'stack123', self.tmpl)
3675
 
        self.assertIsInstance(stack, parser.Stack)
3676
 
        stack = parser.Stack(self.ctx, 'test.stack', self.tmpl)
3677
 
        self.assertIsInstance(stack, parser.Stack)
3678
 
        stack = parser.Stack(self.ctx, 'test_stack', self.tmpl)
3679
 
        self.assertIsInstance(stack, parser.Stack)
3680
 
        stack = parser.Stack(self.ctx, 'TEST', self.tmpl)
3681
 
        self.assertIsInstance(stack, parser.Stack)
3682
 
        stack = parser.Stack(self.ctx, 'test-stack', self.tmpl)
3683
 
        self.assertIsInstance(stack, parser.Stack)
3684
 
 
3685
 
    def test_stack_name_invalid(self):
3686
 
        stack_names = ['_foo', '1bad', '.kcats', 'test stack', ' teststack',
3687
 
                       '^-^', '"stack"', '1234', 'cat|dog', '$(foo)',
3688
 
                       'test/stack', 'test\stack', 'test::stack', 'test;stack',
3689
 
                       'test~stack', '#test']
3690
 
        for stack_name in stack_names:
3691
 
            self.assertRaises(exception.StackValidationFailed, parser.Stack,
3692
 
                              self.ctx, stack_name, self.tmpl)
3693
 
 
3694
 
    def test_resource_state_get_att(self):
3695
 
        tmpl = {
3696
 
            'HeatTemplateFormatVersion': '2012-12-12',
3697
 
            'Resources': {'AResource': {'Type': 'GenericResourceType'}},
3698
 
            'Outputs': {'TestOutput': {'Value': {
3699
 
                'Fn::GetAtt': ['AResource', 'Foo']}}
3700
 
            }
3701
 
        }
3702
 
 
3703
 
        self.stack = parser.Stack(self.ctx, 'resource_state_get_att',
3704
 
                                  template.Template(tmpl))
3705
 
        self.stack.store()
3706
 
        self.stack.create()
3707
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3708
 
                         self.stack.state)
3709
 
        self.assertIn('AResource', self.stack)
3710
 
        rsrc = self.stack['AResource']
3711
 
        rsrc.resource_id_set('aaaa')
3712
 
        self.assertEqual('AResource', rsrc.FnGetAtt('Foo'))
3713
 
 
3714
 
        for action, status in (
3715
 
                (rsrc.CREATE, rsrc.IN_PROGRESS),
3716
 
                (rsrc.CREATE, rsrc.COMPLETE),
3717
 
                (rsrc.CREATE, rsrc.FAILED),
3718
 
                (rsrc.SUSPEND, rsrc.IN_PROGRESS),
3719
 
                (rsrc.SUSPEND, rsrc.COMPLETE),
3720
 
                (rsrc.RESUME, rsrc.IN_PROGRESS),
3721
 
                (rsrc.RESUME, rsrc.COMPLETE),
3722
 
                (rsrc.UPDATE, rsrc.IN_PROGRESS),
3723
 
                (rsrc.UPDATE, rsrc.FAILED),
3724
 
                (rsrc.UPDATE, rsrc.COMPLETE)):
3725
 
            rsrc.state_set(action, status)
3726
 
            self.assertEqual('AResource', self.stack.output('TestOutput'))
3727
 
        for action, status in (
3728
 
                (rsrc.DELETE, rsrc.IN_PROGRESS),
3729
 
                (rsrc.DELETE, rsrc.FAILED),
3730
 
                (rsrc.DELETE, rsrc.COMPLETE)):
3731
 
            rsrc.state_set(action, status)
3732
 
            self.assertIsNone(self.stack.output('TestOutput'))
3733
 
 
3734
 
    def test_resource_required_by(self):
3735
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3736
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'},
3737
 
                              'BResource': {'Type': 'GenericResourceType',
3738
 
                                            'DependsOn': 'AResource'},
3739
 
                              'CResource': {'Type': 'GenericResourceType',
3740
 
                                            'DependsOn': 'BResource'},
3741
 
                              'DResource': {'Type': 'GenericResourceType',
3742
 
                                            'DependsOn': 'BResource'}}}
3743
 
 
3744
 
        self.stack = parser.Stack(self.ctx, 'depends_test_stack',
3745
 
                                  template.Template(tmpl))
3746
 
        self.stack.store()
3747
 
        self.stack.create()
3748
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3749
 
                         self.stack.state)
3750
 
 
3751
 
        self.assertEqual(['BResource'],
3752
 
                         self.stack['AResource'].required_by())
3753
 
        self.assertEqual([],
3754
 
                         self.stack['CResource'].required_by())
3755
 
        required_by = self.stack['BResource'].required_by()
3756
 
        self.assertEqual(2, len(required_by))
3757
 
        for r in ['CResource', 'DResource']:
3758
 
            self.assertIn(r, required_by)
3759
 
 
3760
 
    def test_resource_multi_required_by(self):
3761
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3762
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'},
3763
 
                              'BResource': {'Type': 'GenericResourceType'},
3764
 
                              'CResource': {'Type': 'GenericResourceType'},
3765
 
                              'DResource': {'Type': 'GenericResourceType',
3766
 
                                            'DependsOn': ['AResource',
3767
 
                                                          'BResource',
3768
 
                                                          'CResource']}}}
3769
 
 
3770
 
        self.stack = parser.Stack(self.ctx, 'depends_test_stack',
3771
 
                                  template.Template(tmpl))
3772
 
        self.stack.store()
3773
 
        self.stack.create()
3774
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
3775
 
                         self.stack.state)
3776
 
 
3777
 
        for r in ['AResource', 'BResource', 'CResource']:
3778
 
            self.assertEqual(['DResource'],
3779
 
                             self.stack[r].required_by())
3780
 
 
3781
 
    def test_store_saves_owner(self):
3782
 
        """
3783
 
        The owner_id attribute of Store is saved to the database when stored.
3784
 
        """
3785
 
        self.stack = parser.Stack(
3786
 
            self.ctx, 'owner_stack', self.tmpl)
3787
 
        stack_ownee = parser.Stack(
3788
 
            self.ctx, 'ownee_stack', self.tmpl,
3789
 
            owner_id=self.stack.id)
3790
 
        stack_ownee.store()
3791
 
        db_stack = db_api.stack_get(self.ctx, stack_ownee.id)
3792
 
        self.assertEqual(self.stack.id, db_stack.owner_id)
3793
 
 
3794
 
    def test_init_user_creds_id(self):
3795
 
        ctx_init = utils.dummy_context(user='my_user',
3796
 
                                       password='my_pass')
3797
 
        ctx_init.request_id = self.ctx.request_id
3798
 
        creds = db_api.user_creds_create(ctx_init)
3799
 
        self.stack = parser.Stack(self.ctx, 'creds_init', self.tmpl,
3800
 
                                  user_creds_id=creds.id)
3801
 
        self.stack.store()
3802
 
        self.assertEqual(creds.id, self.stack.user_creds_id)
3803
 
        ctx_expected = ctx_init.to_dict()
3804
 
        ctx_expected['auth_token'] = None
3805
 
        self.assertEqual(ctx_expected, self.stack.stored_context().to_dict())
3806
 
 
3807
 
    def test_store_saves_creds(self):
3808
 
        """
3809
 
        A user_creds entry is created on first stack store
3810
 
        """
3811
 
        self.stack = parser.Stack(
3812
 
            self.ctx, 'creds_stack', self.tmpl)
3813
 
        self.stack.store()
3814
 
 
3815
 
        # The store should've created a user_creds row and set user_creds_id
3816
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
3817
 
        user_creds_id = db_stack.user_creds_id
3818
 
        self.assertIsNotNone(user_creds_id)
3819
 
 
3820
 
        # should've stored the username/password in the context
3821
 
        user_creds = db_api.user_creds_get(user_creds_id)
3822
 
        self.assertEqual(self.ctx.username, user_creds.get('username'))
3823
 
        self.assertEqual(self.ctx.password, user_creds.get('password'))
3824
 
        self.assertIsNone(user_creds.get('trust_id'))
3825
 
        self.assertIsNone(user_creds.get('trustor_user_id'))
3826
 
 
3827
 
        # Check the stored_context is as expected
3828
 
        expected_context = context.RequestContext.from_dict(self.ctx.to_dict())
3829
 
        expected_context.auth_token = None
3830
 
        stored_context = self.stack.stored_context().to_dict()
3831
 
        self.assertEqual(expected_context.to_dict(), stored_context)
3832
 
 
3833
 
        # Store again, ID should not change
3834
 
        self.stack.store()
3835
 
        self.assertEqual(user_creds_id, db_stack.user_creds_id)
3836
 
 
3837
 
    def test_store_saves_creds_trust(self):
3838
 
        """
3839
 
        A user_creds entry is created on first stack store
3840
 
        """
3841
 
        cfg.CONF.set_override('deferred_auth_method', 'trusts')
3842
 
 
3843
 
        self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
3844
 
        keystone.KeystoneClientPlugin._create().AndReturn(
3845
 
            fakes.FakeKeystoneClient(user_id='auser123'))
3846
 
        self.m.ReplayAll()
3847
 
 
3848
 
        self.stack = parser.Stack(
3849
 
            self.ctx, 'creds_stack', self.tmpl)
3850
 
        self.stack.store()
3851
 
 
3852
 
        # The store should've created a user_creds row and set user_creds_id
3853
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
3854
 
        user_creds_id = db_stack.user_creds_id
3855
 
        self.assertIsNotNone(user_creds_id)
3856
 
 
3857
 
        # should've stored the trust_id and trustor_user_id returned from
3858
 
        # FakeKeystoneClient.create_trust_context, username/password should
3859
 
        # not have been stored
3860
 
        user_creds = db_api.user_creds_get(user_creds_id)
3861
 
        self.assertIsNone(user_creds.get('username'))
3862
 
        self.assertIsNone(user_creds.get('password'))
3863
 
        self.assertEqual('atrust', user_creds.get('trust_id'))
3864
 
        self.assertEqual('auser123', user_creds.get('trustor_user_id'))
3865
 
 
3866
 
        # Check the stored_context is as expected
3867
 
        expected_context = context.RequestContext(
3868
 
            trust_id='atrust', trustor_user_id='auser123',
3869
 
            request_id=self.ctx.request_id, is_admin=False).to_dict()
3870
 
        stored_context = self.stack.stored_context().to_dict()
3871
 
        self.assertEqual(expected_context, stored_context)
3872
 
 
3873
 
        # Store again, ID should not change
3874
 
        self.stack.store()
3875
 
        self.assertEqual(user_creds_id, db_stack.user_creds_id)
3876
 
 
3877
 
    def test_backup_copies_user_creds_id(self):
3878
 
        ctx_init = utils.dummy_context(user='my_user',
3879
 
                                       password='my_pass')
3880
 
        ctx_init.request_id = self.ctx.request_id
3881
 
        creds = db_api.user_creds_create(ctx_init)
3882
 
        self.stack = parser.Stack(self.ctx, 'creds_init', self.tmpl,
3883
 
                                  user_creds_id=creds.id)
3884
 
        self.stack.store()
3885
 
        self.assertEqual(creds.id, self.stack.user_creds_id)
3886
 
        backup = self.stack._backup_stack()
3887
 
        self.assertEqual(creds.id, backup.user_creds_id)
3888
 
 
3889
 
    def test_stored_context_err(self):
3890
 
        """
3891
 
        Test stored_context error path.
3892
 
        """
3893
 
        self.stack = parser.Stack(self.ctx, 'creds_stack', self.tmpl)
3894
 
        ex = self.assertRaises(exception.Error, self.stack.stored_context)
3895
 
        expected_err = 'Attempt to use stored_context with no user_creds'
3896
 
        self.assertEqual(expected_err, six.text_type(ex))
3897
 
 
3898
 
    def test_store_gets_username_from_stack(self):
3899
 
        self.stack = parser.Stack(self.ctx, 'username_stack',
3900
 
                                  self.tmpl, username='foobar')
3901
 
        self.ctx.username = 'not foobar'
3902
 
        self.stack.store()
3903
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
3904
 
        self.assertEqual('foobar', db_stack.username)
3905
 
 
3906
 
    def test_store_backup_true(self):
3907
 
        self.stack = parser.Stack(self.ctx, 'username_stack',
3908
 
                                  self.tmpl, username='foobar')
3909
 
        self.ctx.username = 'not foobar'
3910
 
        self.stack.store(backup=True)
3911
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
3912
 
        self.assertTrue(db_stack.backup)
3913
 
 
3914
 
    def test_store_backup_false(self):
3915
 
        self.stack = parser.Stack(self.ctx, 'username_stack',
3916
 
                                  self.tmpl, username='foobar')
3917
 
        self.ctx.username = 'not foobar'
3918
 
        self.stack.store(backup=False)
3919
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
3920
 
        self.assertFalse(db_stack.backup)
3921
 
 
3922
 
    def test_init_stored_context_false(self):
3923
 
        ctx_init = utils.dummy_context(user='mystored_user',
3924
 
                                       password='mystored_pass')
3925
 
        ctx_init.request_id = self.ctx.request_id
3926
 
        creds = db_api.user_creds_create(ctx_init)
3927
 
        self.stack = parser.Stack(self.ctx, 'creds_store1', self.tmpl,
3928
 
                                  user_creds_id=creds.id,
3929
 
                                  use_stored_context=False)
3930
 
        ctx_expected = self.ctx.to_dict()
3931
 
        self.assertEqual(ctx_expected, self.stack.context.to_dict())
3932
 
        self.stack.store()
3933
 
        self.assertEqual(ctx_expected, self.stack.context.to_dict())
3934
 
 
3935
 
    def test_init_stored_context_true(self):
3936
 
        ctx_init = utils.dummy_context(user='mystored_user',
3937
 
                                       password='mystored_pass')
3938
 
        ctx_init.request_id = self.ctx.request_id
3939
 
        creds = db_api.user_creds_create(ctx_init)
3940
 
        self.stack = parser.Stack(self.ctx, 'creds_store2', self.tmpl,
3941
 
                                  user_creds_id=creds.id,
3942
 
                                  use_stored_context=True)
3943
 
        ctx_expected = ctx_init.to_dict()
3944
 
        ctx_expected['auth_token'] = None
3945
 
        self.assertEqual(ctx_expected, self.stack.context.to_dict())
3946
 
        self.stack.store()
3947
 
        self.assertEqual(ctx_expected, self.stack.context.to_dict())
3948
 
 
3949
 
    def test_load_stored_context_false(self):
3950
 
        ctx_init = utils.dummy_context(user='mystored_user',
3951
 
                                       password='mystored_pass')
3952
 
        ctx_init.request_id = self.ctx.request_id
3953
 
        creds = db_api.user_creds_create(ctx_init)
3954
 
        self.stack = parser.Stack(self.ctx, 'creds_store3', self.tmpl,
3955
 
                                  user_creds_id=creds.id)
3956
 
        self.stack.store()
3957
 
 
3958
 
        load_stack = parser.Stack.load(self.ctx, stack_id=self.stack.id,
3959
 
                                       use_stored_context=False)
3960
 
        self.assertEqual(self.ctx.to_dict(), load_stack.context.to_dict())
3961
 
 
3962
 
    def test_load_stored_context_true(self):
3963
 
        ctx_init = utils.dummy_context(user='mystored_user',
3964
 
                                       password='mystored_pass')
3965
 
        ctx_init.request_id = self.ctx.request_id
3966
 
        creds = db_api.user_creds_create(ctx_init)
3967
 
        self.stack = parser.Stack(self.ctx, 'creds_store4', self.tmpl,
3968
 
                                  user_creds_id=creds.id)
3969
 
        self.stack.store()
3970
 
        ctx_expected = ctx_init.to_dict()
3971
 
        ctx_expected['auth_token'] = None
3972
 
 
3973
 
        load_stack = parser.Stack.load(self.ctx, stack_id=self.stack.id,
3974
 
                                       use_stored_context=True)
3975
 
        self.assertEqual(ctx_expected, load_stack.context.to_dict())
3976
 
 
3977
 
    def test_load_honors_owner(self):
3978
 
        """
3979
 
        Loading a stack from the database will set the owner_id of the
3980
 
        resultant stack appropriately.
3981
 
        """
3982
 
        self.stack = parser.Stack(
3983
 
            self.ctx, 'owner_stack', self.tmpl)
3984
 
        stack_ownee = parser.Stack(
3985
 
            self.ctx, 'ownee_stack', self.tmpl,
3986
 
            owner_id=self.stack.id)
3987
 
        stack_ownee.store()
3988
 
 
3989
 
        saved_stack = parser.Stack.load(self.ctx, stack_id=stack_ownee.id)
3990
 
        self.assertEqual(self.stack.id, saved_stack.owner_id)
3991
 
 
3992
 
    def test_requires_deferred_auth(self):
3993
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
3994
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'},
3995
 
                              'BResource': {'Type': 'GenericResourceType'},
3996
 
                              'CResource': {'Type': 'GenericResourceType'}}}
3997
 
 
3998
 
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
3999
 
                                  template.Template(tmpl),
4000
 
                                  disable_rollback=False)
4001
 
 
4002
 
        self.assertFalse(self.stack.requires_deferred_auth())
4003
 
 
4004
 
        self.stack['CResource'].requires_deferred_auth = True
4005
 
        self.assertTrue(self.stack.requires_deferred_auth())
4006
 
 
4007
 
    def test_stack_user_project_id_default(self):
4008
 
        self.stack = parser.Stack(self.ctx, 'user_project_none',
4009
 
                                  self.tmpl)
4010
 
        self.stack.store()
4011
 
        self.assertIsNone(self.stack.stack_user_project_id)
4012
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
4013
 
        self.assertIsNone(db_stack.stack_user_project_id)
4014
 
 
4015
 
    def test_stack_user_project_id_constructor(self):
4016
 
        self.stub_keystoneclient()
4017
 
        self.m.ReplayAll()
4018
 
 
4019
 
        self.stack = parser.Stack(self.ctx, 'user_project_init',
4020
 
                                  self.tmpl,
4021
 
                                  stack_user_project_id='aproject1234')
4022
 
        self.stack.store()
4023
 
        self.assertEqual('aproject1234', self.stack.stack_user_project_id)
4024
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
4025
 
        self.assertEqual('aproject1234', db_stack.stack_user_project_id)
4026
 
 
4027
 
        self.stack.delete()
4028
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
4029
 
                         self.stack.state)
4030
 
        self.m.VerifyAll()
4031
 
 
4032
 
    def test_stack_user_project_id_delete_fail(self):
4033
 
 
4034
 
        class FakeKeystoneClientFail(fakes.FakeKeystoneClient):
4035
 
            def delete_stack_domain_project(self, project_id):
4036
 
                raise kc_exceptions.Forbidden("Denied!")
4037
 
 
4038
 
        self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
4039
 
        keystone.KeystoneClientPlugin._create().AndReturn(
4040
 
            FakeKeystoneClientFail())
4041
 
        self.m.ReplayAll()
4042
 
 
4043
 
        self.stack = parser.Stack(self.ctx, 'user_project_init',
4044
 
                                  self.tmpl,
4045
 
                                  stack_user_project_id='aproject1234')
4046
 
        self.stack.store()
4047
 
        self.assertEqual('aproject1234', self.stack.stack_user_project_id)
4048
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
4049
 
        self.assertEqual('aproject1234', db_stack.stack_user_project_id)
4050
 
 
4051
 
        self.stack.delete()
4052
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.FAILED),
4053
 
                         self.stack.state)
4054
 
        self.assertIn('Error deleting project', self.stack.status_reason)
4055
 
        self.m.VerifyAll()
4056
 
 
4057
 
    def test_stack_user_project_id_setter(self):
4058
 
        self.stub_keystoneclient()
4059
 
        self.m.ReplayAll()
4060
 
 
4061
 
        self.stack = parser.Stack(self.ctx, 'user_project_init',
4062
 
                                  self.tmpl)
4063
 
        self.stack.store()
4064
 
        self.assertIsNone(self.stack.stack_user_project_id)
4065
 
        self.stack.set_stack_user_project_id(project_id='aproject456')
4066
 
        self.assertEqual('aproject456', self.stack.stack_user_project_id)
4067
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
4068
 
        self.assertEqual('aproject456', db_stack.stack_user_project_id)
4069
 
 
4070
 
        self.stack.delete()
4071
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
4072
 
                         self.stack.state)
4073
 
        self.m.VerifyAll()
4074
 
 
4075
 
    def test_stack_user_project_id_create(self):
4076
 
        self.stub_keystoneclient()
4077
 
        self.m.ReplayAll()
4078
 
 
4079
 
        self.stack = parser.Stack(self.ctx, 'user_project_init',
4080
 
                                  self.tmpl)
4081
 
        self.stack.store()
4082
 
        self.assertIsNone(self.stack.stack_user_project_id)
4083
 
        self.stack.create_stack_user_project_id()
4084
 
 
4085
 
        self.assertEqual('aprojectid', self.stack.stack_user_project_id)
4086
 
        db_stack = db_api.stack_get(self.ctx, self.stack.id)
4087
 
        self.assertEqual('aprojectid', db_stack.stack_user_project_id)
4088
 
 
4089
 
        self.stack.delete()
4090
 
        self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
4091
 
                         self.stack.state)
4092
 
        self.m.VerifyAll()
4093
 
 
4094
 
    def test_preview_resources_returns_list_of_resource_previews(self):
4095
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
4096
 
                'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
4097
 
        self.stack = parser.Stack(self.ctx, 'preview_stack',
4098
 
                                  template.Template(tmpl))
4099
 
        res = mock.Mock()
4100
 
        res.preview.return_value = 'foo'
4101
 
        self.stack._resources = {'r1': res}
4102
 
 
4103
 
        resources = self.stack.preview_resources()
4104
 
        self.assertEqual(['foo'], resources)
4105
 
 
4106
 
    def test_correct_outputs(self):
4107
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
4108
 
                'Resources': {
4109
 
                    'AResource': {'Type': 'ResourceWithPropsType',
4110
 
                                  'Properties': {'Foo': 'abc'}},
4111
 
                    'BResource': {'Type': 'ResourceWithPropsType',
4112
 
                                  'Properties': {'Foo': 'def'}}},
4113
 
                'Outputs': {
4114
 
                    'Resource_attr': {
4115
 
                        'Value': {
4116
 
                            'Fn::GetAtt': ['AResource', 'Foo']}}}}
4117
 
 
4118
 
        self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
4119
 
                                  template.Template(tmpl))
4120
 
 
4121
 
        self.stack.store()
4122
 
        self.stack.create()
4123
 
 
4124
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
4125
 
                         self.stack.state)
4126
 
        self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
4127
 
        # According _resolve_attribute method in GenericResource output
4128
 
        # value will be equal with name AResource.
4129
 
        self.assertEqual('AResource', self.stack.output('Resource_attr'))
4130
 
 
4131
 
        self.stack.delete()
4132
 
 
4133
 
        self.assertEqual((self.stack.DELETE, self.stack.COMPLETE),
4134
 
                         self.stack.state)
4135
 
 
4136
 
    def test_incorrect_outputs(self):
4137
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
4138
 
                'Resources': {
4139
 
                    'AResource': {'Type': 'ResourceWithPropsType',
4140
 
                                  'Properties': {'Foo': 'abc'}}},
4141
 
                'Outputs': {
4142
 
                    'Resource_attr': {
4143
 
                        'Value': {
4144
 
                            'Fn::GetAtt': ['AResource', 'Bar']}}}}
4145
 
 
4146
 
        self.stack = parser.Stack(self.ctx, 'stack_with_incorrect_outputs',
4147
 
                                  template.Template(tmpl))
4148
 
 
4149
 
        self.stack.store()
4150
 
        self.stack.create()
4151
 
 
4152
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
4153
 
                         self.stack.state)
4154
 
 
4155
 
        self.assertIsNone(self.stack.output('Resource_attr'))
4156
 
        self.assertEqual('The Referenced Attribute (AResource Bar) is '
4157
 
                         'incorrect.',
4158
 
                         self.stack.outputs['Resource_attr']['error_msg'])
4159
 
 
4160
 
        self.stack.delete()
4161
 
 
4162
 
        self.assertEqual((self.stack.DELETE, self.stack.COMPLETE),
4163
 
                         self.stack.state)
4164
 
 
4165
 
    def test_stack_load_no_param_value_validation(self):
4166
 
        '''
4167
 
        Test stack loading with disabled parameter value validation.
4168
 
        '''
4169
 
        tmpl = template_format.parse('''
4170
 
        heat_template_version: 2013-05-23
4171
 
        parameters:
4172
 
            flavor:
4173
 
                type: string
4174
 
                description: A flavor.
4175
 
                constraints:
4176
 
                    - custom_constraint: nova.flavor
4177
 
        resources:
4178
 
            a_resource:
4179
 
                type: GenericResourceType
4180
 
        ''')
4181
 
 
4182
 
        # Mock objects so the query for flavors in server.FlavorConstraint
4183
 
        # works for stack creation
4184
 
        fc = fakes.FakeClient()
4185
 
        self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
4186
 
        nova.NovaClientPlugin._create().AndReturn(fc)
4187
 
 
4188
 
        fc.flavors = self.m.CreateMockAnything()
4189
 
        flavor = collections.namedtuple("Flavor", ["id", "name"])
4190
 
        flavor.id = "1234"
4191
 
        flavor.name = "dummy"
4192
 
        fc.flavors.list().AndReturn([flavor])
4193
 
 
4194
 
        self.m.ReplayAll()
4195
 
 
4196
 
        self.stack = parser.Stack(self.ctx, 'stack_with_custom_constraint',
4197
 
                                  template.Template(tmpl),
4198
 
                                  environment.Environment({'flavor': 'dummy'}))
4199
 
 
4200
 
        self.stack.validate()
4201
 
        self.stack.store()
4202
 
        self.stack.create()
4203
 
        stack_id = self.stack.id
4204
 
 
4205
 
        self.m.VerifyAll()
4206
 
 
4207
 
        self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
4208
 
                         self.stack.state)
4209
 
 
4210
 
        loaded_stack = parser.Stack.load(self.ctx, stack_id=self.stack.id)
4211
 
        self.assertEqual(stack_id, loaded_stack.parameters['OS::stack_id'])
4212
 
 
4213
 
        # verify that fc.flavors.list() has not been called, i.e. verify that
4214
 
        # parameter value validation did not happen and FlavorConstraint was
4215
 
        # not invoked
4216
 
        self.m.VerifyAll()
4217
 
 
4218
 
    def test_snapshot_delete(self):
4219
 
        snapshots = []
4220
 
 
4221
 
        class ResourceDeleteSnapshot(generic_rsrc.ResourceWithProps):
4222
 
 
4223
 
            def handle_delete_snapshot(self, data):
4224
 
                snapshots.append(data)
4225
 
 
4226
 
        resource._register_class(
4227
 
            'ResourceDeleteSnapshot', ResourceDeleteSnapshot)
4228
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
4229
 
                'Resources': {'AResource': {'Type': 'ResourceDeleteSnapshot'}}}
4230
 
 
4231
 
        self.stack = parser.Stack(self.ctx, 'snapshot_stack',
4232
 
                                  template.Template(tmpl))
4233
 
        data = self.stack.prepare_abandon()
4234
 
        fake_snapshot = collections.namedtuple('Snapshot', ('data',))(data)
4235
 
        self.stack.delete_snapshot(fake_snapshot)
4236
 
        self.assertEqual([data['resources']['AResource']], snapshots)
4237
 
 
4238
 
    def test_incorrect_outputs_cfn_get_attr(self):
4239
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
4240
 
                'Resources': {
4241
 
                    'AResource': {'Type': 'ResourceWithPropsType',
4242
 
                                  'Properties': {'Foo': 'abc'}}},
4243
 
                'Outputs': {
4244
 
                    'Resource_attr': {
4245
 
                        'Value': {
4246
 
                            'Fn::GetAtt': ['AResource', 'Bar']}}}}
4247
 
 
4248
 
        self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
4249
 
                                  template.Template(tmpl))
4250
 
 
4251
 
        ex = self.assertRaises(exception.StackValidationFailed,
4252
 
                               self.stack.validate)
4253
 
 
4254
 
        self.assertEqual('Output validation error: The Referenced Attribute '
4255
 
                         '(AResource Bar) is incorrect.',
4256
 
                         six.text_type(ex))
4257
 
 
4258
 
    def test_incorrect_outputs_cfn_incorrect_reference(self):
4259
 
        tmpl = template_format.parse("""
4260
 
        HeatTemplateFormatVersion: '2012-12-12'
4261
 
        Outputs:
4262
 
          Output:
4263
 
            Value:
4264
 
              Fn::GetAtt:
4265
 
                - Resource
4266
 
                - Foo
4267
 
        """)
4268
 
        self.stack = parser.Stack(self.ctx, 'stack_with_incorrect_outputs',
4269
 
                                  template.Template(tmpl))
4270
 
 
4271
 
        ex = self.assertRaises(exception.StackValidationFailed,
4272
 
                               self.stack.validate)
4273
 
 
4274
 
        self.assertIn('The specified reference "Resource" '
4275
 
                      '(in unknown) is incorrect.', six.text_type(ex))
4276
 
 
4277
 
    def test_incorrect_outputs_incorrect_reference(self):
4278
 
        tmpl = template_format.parse("""
4279
 
        heat_template_version: 2013-05-23
4280
 
        outputs:
4281
 
          output:
4282
 
            value: { get_attr: [resource, foo] }
4283
 
        """)
4284
 
        self.stack = parser.Stack(self.ctx, 'stack_with_incorrect_outputs',
4285
 
                                  template.Template(tmpl))
4286
 
 
4287
 
        ex = self.assertRaises(exception.StackValidationFailed,
4288
 
                               self.stack.validate)
4289
 
 
4290
 
        self.assertIn('The specified reference "resource" '
4291
 
                      '(in unknown) is incorrect.', six.text_type(ex))
4292
 
 
4293
 
    def test_incorrect_outputs_cfn_empty_output(self):
4294
 
        tmpl = template_format.parse("""
4295
 
        HeatTemplateFormatVersion: '2012-12-12'
4296
 
        Resources:
4297
 
          AResource:
4298
 
            Type: ResourceWithPropsType
4299
 
            Properties:
4300
 
              Foo: abc
4301
 
        Outputs:
4302
 
          Resource_attr:
4303
 
        """)
4304
 
        self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
4305
 
                                  template.Template(tmpl))
4306
 
 
4307
 
        ex = self.assertRaises(exception.StackValidationFailed,
4308
 
                               self.stack.validate)
4309
 
 
4310
 
        self.assertIn('Each Output must contain a Value key.',
4311
 
                      six.text_type(ex))
4312
 
 
4313
 
    def test_incorrect_outputs_cfn_string_data(self):
4314
 
        tmpl = template_format.parse("""
4315
 
        HeatTemplateFormatVersion: '2012-12-12'
4316
 
        Resources:
4317
 
          AResource:
4318
 
            Type: ResourceWithPropsType
4319
 
            Properties:
4320
 
              Foo: abc
4321
 
        Outputs:
4322
 
          Resource_attr:
4323
 
            This is wrong data
4324
 
        """)
4325
 
        self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
4326
 
                                  template.Template(tmpl))
4327
 
 
4328
 
        ex = self.assertRaises(exception.StackValidationFailed,
4329
 
                               self.stack.validate)
4330
 
 
4331
 
        self.assertIn('Outputs must contain Output. '
4332
 
                      'Found a [%s] instead' % six.text_type,
4333
 
                      six.text_type(ex))
4334
 
 
4335
 
    def test_prop_validate_value(self):
4336
 
        tmpl = template_format.parse("""
4337
 
        HeatTemplateFormatVersion: '2012-12-12'
4338
 
        Resources:
4339
 
          AResource:
4340
 
            Type: ResourceWithPropsType
4341
 
            Properties:
4342
 
              FooInt: notanint
4343
 
        """)
4344
 
        self.stack = parser.Stack(self.ctx, 'stack_with_bad_property',
4345
 
                                  template.Template(tmpl))
4346
 
 
4347
 
        ex = self.assertRaises(exception.StackValidationFailed,
4348
 
                               self.stack.validate)
4349
 
 
4350
 
        self.assertIn("'notanint' is not an integer",
4351
 
                      six.text_type(ex))
4352
 
 
4353
 
        self.stack.strict_validate = False
4354
 
        self.assertIsNone(self.stack.validate())
4355
 
 
4356
 
    def test_param_validate_value(self):
4357
 
        tmpl = template_format.parse("""
4358
 
        HeatTemplateFormatVersion: '2012-12-12'
4359
 
        Parameters:
4360
 
          foo:
4361
 
            Type: Number
4362
 
        """)
4363
 
 
4364
 
        env1 = {'parameters': {'foo': 'abc'}}
4365
 
        self.stack = parser.Stack(self.ctx, 'stack_with_bad_param',
4366
 
                                  template.Template(tmpl),
4367
 
                                  env=environment.Environment(env1))
4368
 
 
4369
 
        ex = self.assertRaises(exception.StackValidationFailed,
4370
 
                               self.stack.validate)
4371
 
 
4372
 
        self.assertIn("could not convert string to float: abc",
4373
 
                      six.text_type(ex))
4374
 
 
4375
 
        self.stack.strict_validate = False
4376
 
        self.assertIsNone(self.stack.validate())
4377
 
 
4378
 
    def test_incorrect_outputs_cfn_list_data(self):
4379
 
        tmpl = template_format.parse("""
4380
 
        HeatTemplateFormatVersion: '2012-12-12'
4381
 
        Resources:
4382
 
          AResource:
4383
 
            Type: ResourceWithPropsType
4384
 
            Properties:
4385
 
              Foo: abc
4386
 
        Outputs:
4387
 
          Resource_attr:
4388
 
            - Data is not what it seems
4389
 
        """)
4390
 
        self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
4391
 
                                  template.Template(tmpl))
4392
 
 
4393
 
        ex = self.assertRaises(exception.StackValidationFailed,
4394
 
                               self.stack.validate)
4395
 
 
4396
 
        self.assertIn('Outputs must contain Output. '
4397
 
                      'Found a [%s] instead' % type([]), six.text_type(ex))
4398
 
 
4399
 
    def test_incorrect_outputs_hot_get_attr(self):
4400
 
        tmpl = {'heat_template_version': '2013-05-23',
4401
 
                'resources': {
4402
 
                    'AResource': {'type': 'ResourceWithPropsType',
4403
 
                                  'properties': {'Foo': 'abc'}}},
4404
 
                'outputs': {
4405
 
                    'resource_attr': {
4406
 
                        'value': {
4407
 
                            'get_attr': ['AResource', 'Bar']}}}}
4408
 
 
4409
 
        self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
4410
 
                                  template.Template(tmpl))
4411
 
 
4412
 
        ex = self.assertRaises(exception.StackValidationFailed,
4413
 
                               self.stack.validate)
4414
 
 
4415
 
        self.assertEqual('Output validation error: The Referenced Attribute '
4416
 
                         '(AResource Bar) is incorrect.',
4417
 
                         six.text_type(ex))
4418
 
 
4419
 
    def test_restore(self):
4420
 
        tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
4421
 
                'Resources': {
4422
 
                    'A': {'Type': 'GenericResourceType'},
4423
 
                    'B': {'Type': 'GenericResourceType'}}}
4424
 
        self.stack = parser.Stack(self.ctx, 'stack_details_test',
4425
 
                                  parser.Template(tmpl))
4426
 
        self.stack.store()
4427
 
        self.stack.create()
4428
 
 
4429
 
        data = copy.deepcopy(self.stack.prepare_abandon())
4430
 
        fake_snapshot = collections.namedtuple(
4431
 
            'Snapshot', ('data', 'stack_id'))(data, self.stack.id)
4432
 
 
4433
 
        new_tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
4434
 
                    'Resources': {'A': {'Type': 'GenericResourceType'}}}
4435
 
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
4436
 
                                     template.Template(new_tmpl))
4437
 
        self.stack.update(updated_stack)
4438
 
        self.assertEqual(1, len(self.stack.resources))
4439
 
 
4440
 
        self.stack.restore(fake_snapshot)
4441
 
 
4442
 
        self.assertEqual((parser.Stack.RESTORE, parser.Stack.COMPLETE),
4443
 
                         self.stack.state)
4444
 
        self.assertEqual(2, len(self.stack.resources))
4445
 
 
4446
 
    def test_hot_restore(self):
4447
 
 
4448
 
        class ResourceWithRestore(generic_rsrc.ResWithComplexPropsAndAttrs):
4449
 
 
4450
 
            def handle_restore(self, defn, data):
4451
 
                props = dict(
4452
 
                    (key, value) for (key, value) in
4453
 
                    six.iteritems(defn.properties(self.properties_schema))
4454
 
                    if value is not None)
4455
 
                value = data['resource_data']['a_string']
4456
 
                props['a_string'] = value
4457
 
                return defn.freeze(properties=props)
4458
 
 
4459
 
        resource._register_class('ResourceWithRestore', ResourceWithRestore)
4460
 
        tpl = {'heat_template_version': '2013-05-23',
4461
 
               'resources':
4462
 
               {'A': {'type': 'ResourceWithRestore'}}}
4463
 
        self.stack = parser.Stack(self.ctx, 'stack_details_test',
4464
 
                                  parser.Template(tpl))
4465
 
        self.stack.store()
4466
 
        self.stack.create()
4467
 
 
4468
 
        data = self.stack.prepare_abandon()
4469
 
        data['resources']['A']['resource_data']['a_string'] = 'foo'
4470
 
        fake_snapshot = collections.namedtuple(
4471
 
            'Snapshot', ('data', 'stack_id'))(data, self.stack.id)
4472
 
 
4473
 
        self.stack.restore(fake_snapshot)
4474
 
 
4475
 
        self.assertEqual((parser.Stack.RESTORE, parser.Stack.COMPLETE),
4476
 
                         self.stack.state)
4477
 
 
4478
 
        self.assertEqual(
4479
 
            'foo', self.stack.resources['A'].properties['a_string'])