~ubuntu-branches/ubuntu/saucy/heat/saucy

« back to all changes in this revision

Viewing changes to heat/tests/test_parser.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Yolanda Robla, Chuck Short
  • Date: 2013-07-22 16:22:29 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20130722162229-zzvfu40id94ii0hc
Tags: 2013.2~b2-0ubuntu1
[ Yolanda Robla ]
* debian/tests: added autopkg tests

[ Chuck Short ]
* New upstream release
* debian/control:
  - Add python-pbr to build-depends.
  - Add python-d2to to build-depends.
  - Dropped python-argparse.
  - Add python-six to build-depends.
  - Dropped python-sendfile.
  - Dropped python-nose.
  - Added testrepository.
  - Added python-testtools.
* debian/rules: Run testrepository instead of nosetets.
* debian/patches/removes-lxml-version-limitation-from-pip-requires.patch: Dropped
  no longer needed.
* debian/patches/fix-package-version-detection-when-building-doc.patch: Dropped
  no longer needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#    License for the specific language governing permissions and limitations
13
13
#    under the License.
14
14
 
15
 
 
16
 
import unittest
17
 
from nose.plugins.attrib import attr
18
 
import mox
 
15
import json
 
16
import time
19
17
import uuid
20
18
 
21
19
from heat.common import context
 
20
from heat.engine import environment
22
21
from heat.common import exception
23
22
from heat.common import template_format
 
23
from heat.engine import clients
24
24
from heat.engine import resource
25
25
from heat.engine import parser
26
26
from heat.engine import parameters
 
27
from heat.engine import scheduler
27
28
from heat.engine import template
28
29
 
 
30
from heat.tests.common import HeatTestCase
 
31
from heat.tests.utils import setup_dummy_db
 
32
from heat.tests.v1_1 import fakes
29
33
from heat.tests.utils import stack_delete_after
30
34
from heat.tests import generic_resource as generic_rsrc
31
35
 
32
 
import heat.db as db_api
 
36
import heat.db.api as db_api
33
37
 
34
38
 
35
39
def join(raw):
36
40
    return parser.Template.resolve_joins(raw)
37
41
 
38
42
 
39
 
@attr(tag=['unit', 'parser'])
40
 
@attr(speed='fast')
41
 
class ParserTest(unittest.TestCase):
 
43
class ParserTest(HeatTestCase):
42
44
 
43
45
    def test_list(self):
44
46
        raw = ['foo', 'bar', 'baz']
77
79
        raw = {'Fn::Join': [' ', ['foo', 'bar', 'baz']]}
78
80
        self.assertEqual(join(raw), 'foo bar baz')
79
81
 
 
82
    def test_join_none(self):
 
83
        raw = {'Fn::Join': [' ', ['foo', None, 'baz']]}
 
84
        self.assertEqual(join(raw), 'foo  baz')
 
85
 
80
86
    def test_join_list(self):
81
87
        raw = [{'Fn::Join': [' ', ['foo', 'bar', 'baz']]}, 'blarg', 'wibble']
82
88
        parsed = join(raw)
114
120
}''')
115
121
 
116
122
 
117
 
@attr(tag=['unit', 'parser', 'template'])
118
 
@attr(speed='fast')
119
 
class TemplateTest(unittest.TestCase):
120
 
    def setUp(self):
121
 
        self.m = mox.Mox()
122
 
 
123
 
    def tearDown(self):
124
 
        self.m.UnsetStubs()
125
 
 
 
123
class TemplateTest(HeatTestCase):
126
124
    def test_defaults(self):
127
125
        empty = parser.Template({})
128
126
        try:
137
135
        self.assertEqual(empty[template.RESOURCES], {})
138
136
        self.assertEqual(empty[template.OUTPUTS], {})
139
137
 
 
138
    def test_invalid_template(self):
 
139
        scanner_error = '''
 
140
1
 
141
Mappings:
 
142
  ValidMapping:
 
143
    TestKey: TestValue
 
144
'''
 
145
        parser_error = '''
 
146
Mappings:
 
147
  ValidMapping:
 
148
    TestKey: {TestKey1: "Value1" TestKey2: "Value2"}
 
149
'''
 
150
 
 
151
        self.assertRaises(ValueError, template_format.parse, scanner_error)
 
152
        self.assertRaises(ValueError, template_format.parse, parser_error)
 
153
 
140
154
    def test_invalid_section(self):
141
155
        tmpl = parser.Template({'Foo': ['Bar']})
142
156
        try:
185
199
 
186
200
    def test_param_ref_missing(self):
187
201
        tmpl = {'Parameters': {'foo': {'Type': 'String', 'Required': True}}}
188
 
        params = parameters.Parameters('test', tmpl)
 
202
        params = parameters.Parameters('test', tmpl, validate_value=False)
189
203
        snippet = {"Ref": "foo"}
190
204
        self.assertRaises(exception.UserParameterMissing,
191
205
                          parser.Template.resolve_param_refs,
210
224
                                                               resources),
211
225
                         p_snippet)
212
226
 
 
227
    def test_select_from_list(self):
 
228
        data = {"Fn::Select": ["1", ["foo", "bar"]]}
 
229
        self.assertEqual(parser.Template.resolve_select(data), "bar")
 
230
 
 
231
    def test_select_from_list_not_int(self):
 
232
        data = {"Fn::Select": ["one", ["foo", "bar"]]}
 
233
        self.assertRaises(TypeError, parser.Template.resolve_select,
 
234
                          data)
 
235
 
 
236
    def test_select_from_list_out_of_bound(self):
 
237
        data = {"Fn::Select": ["3", ["foo", "bar"]]}
 
238
        self.assertRaises(IndexError, parser.Template.resolve_select,
 
239
                          data)
 
240
 
 
241
    def test_select_from_dict(self):
 
242
        data = {"Fn::Select": ["red", {"red": "robin", "re": "foo"}]}
 
243
        self.assertEqual(parser.Template.resolve_select(data), "robin")
 
244
 
 
245
    def test_select_from_none(self):
 
246
        data = {"Fn::Select": ["red", None]}
 
247
        self.assertEqual(parser.Template.resolve_select(data), "")
 
248
 
 
249
    def test_select_from_dict_not_str(self):
 
250
        data = {"Fn::Select": ["1", {"red": "robin", "re": "foo"}]}
 
251
        self.assertRaises(TypeError, parser.Template.resolve_select,
 
252
                          data)
 
253
 
 
254
    def test_select_from_dict_not_existing(self):
 
255
        data = {"Fn::Select": ["green", {"red": "robin", "re": "foo"}]}
 
256
        self.assertRaises(KeyError, parser.Template.resolve_select,
 
257
                          data)
 
258
 
 
259
    def test_select_from_serialized_json_map(self):
 
260
        js = json.dumps({"red": "robin", "re": "foo"})
 
261
        data = {"Fn::Select": ["re", js]}
 
262
        self.assertEqual(parser.Template.resolve_select(data), "foo")
 
263
 
 
264
    def test_select_from_serialized_json_list(self):
 
265
        js = json.dumps(["foo", "fee", "fum"])
 
266
        data = {"Fn::Select": ["0", js]}
 
267
        self.assertEqual(parser.Template.resolve_select(data), "foo")
 
268
 
 
269
    def test_select_from_serialized_json_wrong(self):
 
270
        js = "this is really not serialized json"
 
271
        data = {"Fn::Select": ["not", js]}
 
272
        self.assertRaises(ValueError, parser.Template.resolve_select,
 
273
                          data)
 
274
 
 
275
    def test_select_wrong_num_args(self):
 
276
        join0 = {"Fn::Select": []}
 
277
        self.assertRaises(ValueError, parser.Template.resolve_select,
 
278
                          join0)
 
279
        join1 = {"Fn::Select": ["4"]}
 
280
        self.assertRaises(ValueError, parser.Template.resolve_select,
 
281
                          join1)
 
282
        join3 = {"Fn::Select": ["foo", {"foo": "bar"}, ""]}
 
283
        self.assertRaises(ValueError, parser.Template.resolve_select,
 
284
                          join3)
 
285
 
213
286
    def test_join_reduce(self):
214
287
        join = {"Fn::Join": [" ", ["foo", "bar", "baz", {'Ref': 'baz'},
215
288
                "bink", "bonk"]]}
275
348
        self.assertRaises(TypeError, parser.Template.resolve_joins,
276
349
                          join3)
277
350
 
 
351
    def test_split_ok(self):
 
352
        data = {"Fn::Split": [";", "foo; bar; achoo"]}
 
353
        self.assertEqual(parser.Template.resolve_split(data),
 
354
                         ['foo', ' bar', ' achoo'])
 
355
 
 
356
    def test_split_no_delim_in_str(self):
 
357
        data = {"Fn::Split": [";", "foo, bar, achoo"]}
 
358
        self.assertEqual(parser.Template.resolve_split(data),
 
359
                         ['foo, bar, achoo'])
 
360
 
 
361
    def test_split_no_delim(self):
 
362
        data = {"Fn::Split": ["foo, bar, achoo"]}
 
363
        self.assertRaises(ValueError, parser.Template.resolve_split, data)
 
364
 
 
365
    def test_split_no_list(self):
 
366
        data = {"Fn::Split": "foo, bar, achoo"}
 
367
        self.assertRaises(TypeError, parser.Template.resolve_split, data)
 
368
 
278
369
    def test_base64(self):
279
370
        snippet = {"Fn::Base64": "foobar"}
280
371
        # For now, the Base64 function just returns the original text, and
291
382
        self.assertRaises(TypeError, parser.Template.resolve_base64,
292
383
                          dict_snippet)
293
384
 
294
 
 
295
 
@attr(tag=['unit', 'parser', 'stack'])
296
 
@attr(speed='fast')
297
 
class StackTest(unittest.TestCase):
 
385
    def test_get_azs(self):
 
386
        snippet = {"Fn::GetAZs": ""}
 
387
        self.assertEqual(
 
388
            parser.Template.resolve_availability_zones(snippet, None),
 
389
            ["nova"])
 
390
 
 
391
    def test_get_azs_with_stack(self):
 
392
        snippet = {"Fn::GetAZs": ""}
 
393
        stack = parser.Stack(None, 'test_stack', parser.Template({}))
 
394
        self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
 
395
        fc = fakes.FakeClient()
 
396
        clients.OpenStackClients.nova().MultipleTimes().AndReturn(fc)
 
397
        self.m.ReplayAll()
 
398
        self.assertEqual(
 
399
            parser.Template.resolve_availability_zones(snippet, stack),
 
400
            ["nova1"])
 
401
 
 
402
    def test_replace(self):
 
403
        snippet = {"Fn::Replace": [
 
404
            {'$var1': 'foo', '%var2%': 'bar'},
 
405
            '$var1 is %var2%'
 
406
        ]}
 
407
        self.assertEqual(
 
408
            parser.Template.resolve_replace(snippet),
 
409
            'foo is bar')
 
410
 
 
411
    def test_replace_list_mapping(self):
 
412
        snippet = {"Fn::Replace": [
 
413
            ['var1', 'foo', 'var2', 'bar'],
 
414
            '$var1 is ${var2}'
 
415
        ]}
 
416
        self.assertRaises(TypeError, parser.Template.resolve_replace,
 
417
                          snippet)
 
418
 
 
419
    def test_replace_dict(self):
 
420
        snippet = {"Fn::Replace": {}}
 
421
        self.assertRaises(TypeError, parser.Template.resolve_replace,
 
422
                          snippet)
 
423
 
 
424
    def test_replace_missing_template(self):
 
425
        snippet = {"Fn::Replace": [['var1', 'foo', 'var2', 'bar']]}
 
426
        self.assertRaises(ValueError, parser.Template.resolve_replace,
 
427
                          snippet)
 
428
 
 
429
    def test_replace_none_template(self):
 
430
        snippet = {"Fn::Replace": [['var1', 'foo', 'var2', 'bar'], None]}
 
431
        self.assertRaises(TypeError, parser.Template.resolve_replace,
 
432
                          snippet)
 
433
 
 
434
    def test_replace_list_string(self):
 
435
        snippet = {"Fn::Replace": [
 
436
            {'var1': 'foo', 'var2': 'bar'},
 
437
            ['$var1 is ${var2}']
 
438
        ]}
 
439
        self.assertRaises(TypeError, parser.Template.resolve_replace,
 
440
                          snippet)
 
441
 
 
442
    def test_replace_none_values(self):
 
443
        snippet = {"Fn::Replace": [
 
444
            {'$var1': None, '${var2}': None},
 
445
            '"$var1" is "${var2}"'
 
446
        ]}
 
447
        self.assertEqual(
 
448
            parser.Template.resolve_replace(snippet),
 
449
            '"" is ""')
 
450
 
 
451
    def test_replace_missing_key(self):
 
452
        snippet = {"Fn::Replace": [
 
453
            {'$var1': 'foo', 'var2': 'bar'},
 
454
            '"$var1" is "${var3}"'
 
455
        ]}
 
456
        self.assertEqual(
 
457
            parser.Template.resolve_replace(snippet),
 
458
            '"foo" is "${var3}"')
 
459
 
 
460
    def test_resource_facade(self):
 
461
        metadata_snippet = {'Fn::ResourceFacade': 'Metadata'}
 
462
        deletion_policy_snippet = {'Fn::ResourceFacade': 'DeletionPolicy'}
 
463
        update_policy_snippet = {'Fn::ResourceFacade': 'UpdatePolicy'}
 
464
 
 
465
        class DummyClass:
 
466
            pass
 
467
        parent_resource = DummyClass()
 
468
        parent_resource.metadata = '{"foo": "bar"}'
 
469
        parent_resource.t = {'DeletionPolicy': 'Retain',
 
470
                             'UpdatePolicy': '{"foo": "bar"}'}
 
471
        stack = parser.Stack(None, 'test_stack',
 
472
                             parser.Template({}),
 
473
                             parent_resource=parent_resource)
 
474
        self.assertEqual(
 
475
            parser.Template.resolve_resource_facade(metadata_snippet, stack),
 
476
            '{"foo": "bar"}')
 
477
        self.assertEqual(
 
478
            parser.Template.resolve_resource_facade(deletion_policy_snippet,
 
479
                                                    stack), 'Retain')
 
480
        self.assertEqual(
 
481
            parser.Template.resolve_resource_facade(update_policy_snippet,
 
482
                                                    stack), '{"foo": "bar"}')
 
483
 
 
484
    def test_resource_facade_invalid_arg(self):
 
485
        snippet = {'Fn::ResourceFacade': 'wibble'}
 
486
        stack = parser.Stack(None, 'test_stack', parser.Template({}))
 
487
        self.assertRaises(ValueError,
 
488
                          parser.Template.resolve_resource_facade,
 
489
                          snippet,
 
490
                          stack)
 
491
 
 
492
    def test_resource_facade_missing_key(self):
 
493
        snippet = {'Fn::ResourceFacade': 'DeletionPolicy'}
 
494
 
 
495
        class DummyClass:
 
496
            pass
 
497
        parent_resource = DummyClass()
 
498
        parent_resource.metadata = '{"foo": "bar"}'
 
499
        parent_resource.t = {}
 
500
        stack = parser.Stack(None, 'test_stack',
 
501
                             parser.Template({}),
 
502
                             parent_resource=parent_resource)
 
503
        self.assertRaises(KeyError,
 
504
                          parser.Template.resolve_resource_facade,
 
505
                          snippet,
 
506
                          stack)
 
507
 
 
508
 
 
509
class StackTest(HeatTestCase):
298
510
    def setUp(self):
 
511
        super(StackTest, self).setUp()
 
512
 
299
513
        self.username = 'parser_stack_test_user'
300
514
 
301
 
        self.m = mox.Mox()
302
 
 
 
515
        setup_dummy_db()
303
516
        self.ctx = context.get_admin_context()
304
 
        self.m.StubOutWithMock(self.ctx, 'username')
305
 
        self.ctx.username = self.username
 
517
        self.m.StubOutWithMock(self.ctx, 'user')
 
518
        self.ctx.user = self.username
306
519
        self.ctx.tenant_id = 'test_tenant'
307
520
 
308
 
        generic_rsrc.GenericResource.properties_schema = {}
309
521
        resource._register_class('GenericResourceType',
310
522
                                 generic_rsrc.GenericResource)
 
523
        resource._register_class('ResourceWithPropsType',
 
524
                                 generic_rsrc.ResourceWithProps)
311
525
 
312
526
        self.m.ReplayAll()
313
527
 
314
 
    def tearDown(self):
315
 
        self.m.UnsetStubs()
316
 
 
317
528
    def test_state_defaults(self):
318
529
        stack = parser.Stack(None, 'test_stack', parser.Template({}))
319
 
        self.assertEqual(stack.state, None)
320
 
        self.assertEqual(stack.state_description, '')
 
530
        self.assertEqual(stack.state, (None, None))
 
531
        self.assertEqual(stack.status_reason, '')
321
532
 
322
533
    def test_state(self):
323
534
        stack = parser.Stack(None, 'test_stack', parser.Template({}),
324
 
                             state='foo')
325
 
        self.assertEqual(stack.state, 'foo')
326
 
        stack.state_set('bar', '')
327
 
        self.assertEqual(stack.state, 'bar')
328
 
 
329
 
    def test_state_description(self):
330
 
        stack = parser.Stack(None, 'test_stack', parser.Template({}),
331
 
                             state_description='quux')
332
 
        self.assertEqual(stack.state_description, 'quux')
333
 
        stack.state_set('blarg', 'wibble')
334
 
        self.assertEqual(stack.state_description, 'wibble')
 
535
                             action=parser.Stack.CREATE,
 
536
                             status=parser.Stack.IN_PROGRESS)
 
537
        self.assertEqual(stack.state,
 
538
                         (parser.Stack.CREATE, parser.Stack.IN_PROGRESS))
 
539
        stack.state_set(parser.Stack.CREATE, parser.Stack.COMPLETE, 'test')
 
540
        self.assertEqual(stack.state,
 
541
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
 
542
        stack.state_set(parser.Stack.DELETE, parser.Stack.COMPLETE, 'test')
 
543
        self.assertEqual(stack.state,
 
544
                         (parser.Stack.DELETE, parser.Stack.COMPLETE))
 
545
 
 
546
    def test_state_bad(self):
 
547
        stack = parser.Stack(None, 'test_stack', parser.Template({}),
 
548
                             action=parser.Stack.CREATE,
 
549
                             status=parser.Stack.IN_PROGRESS)
 
550
        self.assertEqual(stack.state,
 
551
                         (parser.Stack.CREATE, parser.Stack.IN_PROGRESS))
 
552
        self.assertRaises(ValueError, stack.state_set,
 
553
                          'baad', parser.Stack.COMPLETE, 'test')
 
554
        self.assertRaises(ValueError, stack.state_set,
 
555
                          parser.Stack.CREATE, 'oops', 'test')
 
556
 
 
557
    def test_status_reason(self):
 
558
        stack = parser.Stack(None, 'test_stack', parser.Template({}),
 
559
                             status_reason='quux')
 
560
        self.assertEqual(stack.status_reason, 'quux')
 
561
        stack.state_set(parser.Stack.CREATE, parser.Stack.IN_PROGRESS,
 
562
                        'wibble')
 
563
        self.assertEqual(stack.status_reason, 'wibble')
335
564
 
336
565
    def test_load_nonexistant_id(self):
337
566
        self.assertRaises(exception.NotFound, parser.Stack.load,
338
567
                          None, -1)
339
568
 
 
569
    @stack_delete_after
 
570
    def test_load_parent_resource(self):
 
571
        self.stack = parser.Stack(self.ctx, 'load_parent_resource',
 
572
                                  parser.Template({}))
 
573
        self.stack.store()
 
574
        stack = db_api.stack_get(self.ctx, self.stack.id)
 
575
 
 
576
        t = template.Template.load(self.ctx, stack.raw_template_id)
 
577
        self.m.StubOutWithMock(template.Template, 'load')
 
578
        template.Template.load(self.ctx, stack.raw_template_id).AndReturn(t)
 
579
 
 
580
        env = environment.Environment(stack.parameters)
 
581
        self.m.StubOutWithMock(environment, 'Environment')
 
582
        environment.Environment(stack.parameters).AndReturn(env)
 
583
 
 
584
        self.m.StubOutWithMock(parser.Stack, '__init__')
 
585
        parser.Stack.__init__(self.ctx, stack.name, t, env, stack.id,
 
586
                              stack.action, stack.status, stack.status_reason,
 
587
                              stack.timeout, True, stack.disable_rollback,
 
588
                              'parent')
 
589
 
 
590
        self.m.ReplayAll()
 
591
        parser.Stack.load(self.ctx, stack_id=self.stack.id,
 
592
                          parent_resource='parent')
 
593
 
 
594
        self.m.VerifyAll()
 
595
 
340
596
    # Note tests creating a stack should be decorated with @stack_delete_after
341
597
    # to ensure the self.stack is properly cleaned up
342
598
    @stack_delete_after
396
652
        self.assertEqual(self.stack.updated_time, None)
397
653
        self.stack.store()
398
654
        stored_time = self.stack.updated_time
399
 
        self.stack.state_set(self.stack.CREATE_IN_PROGRESS, 'testing')
 
655
        self.stack.state_set(self.stack.CREATE, self.stack.IN_PROGRESS, 'test')
400
656
        self.assertNotEqual(self.stack.updated_time, None)
401
657
        self.assertNotEqual(self.stack.updated_time, stored_time)
402
658
 
413
669
 
414
670
        db_s = db_api.stack_get(self.ctx, stack_id)
415
671
        self.assertEqual(db_s, None)
416
 
        self.assertEqual(self.stack.state, self.stack.DELETE_COMPLETE)
 
672
        self.assertEqual(self.stack.state,
 
673
                         (parser.Stack.DELETE, parser.Stack.COMPLETE))
 
674
 
 
675
    @stack_delete_after
 
676
    def test_suspend_resume(self):
 
677
        self.m.ReplayAll()
 
678
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
 
679
        self.stack = parser.Stack(self.ctx, 'suspend_test',
 
680
                                  parser.Template(tmpl))
 
681
        stack_id = self.stack.store()
 
682
        self.stack.create()
 
683
        self.assertEqual(self.stack.state,
 
684
                         (self.stack.CREATE, self.stack.COMPLETE))
 
685
 
 
686
        self.stack.suspend()
 
687
 
 
688
        self.assertEqual(self.stack.state,
 
689
                         (self.stack.SUSPEND, self.stack.COMPLETE))
 
690
 
 
691
        self.stack.resume()
 
692
 
 
693
        self.assertEqual(self.stack.state,
 
694
                         (self.stack.RESUME, self.stack.COMPLETE))
 
695
 
 
696
        self.m.VerifyAll()
 
697
 
 
698
    @stack_delete_after
 
699
    def test_suspend_fail(self):
 
700
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
 
701
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_suspend')
 
702
        exc = exception.ResourceFailure(Exception('foo'))
 
703
        generic_rsrc.GenericResource.handle_suspend().AndRaise(exc)
 
704
        self.m.ReplayAll()
 
705
 
 
706
        self.stack = parser.Stack(self.ctx, 'suspend_test_fail',
 
707
                                  parser.Template(tmpl))
 
708
 
 
709
        stack_id = self.stack.store()
 
710
        self.stack.create()
 
711
        self.assertEqual(self.stack.state,
 
712
                         (self.stack.CREATE, self.stack.COMPLETE))
 
713
 
 
714
        self.stack.suspend()
 
715
 
 
716
        self.assertEqual(self.stack.state,
 
717
                         (self.stack.SUSPEND, self.stack.FAILED))
 
718
        self.assertEqual(self.stack.status_reason,
 
719
                         'Resource suspend failed: Exception: foo')
 
720
        self.m.VerifyAll()
 
721
 
 
722
    @stack_delete_after
 
723
    def test_resume_fail(self):
 
724
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
 
725
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_resume')
 
726
        exc = exception.ResourceFailure(Exception('foo'))
 
727
        generic_rsrc.GenericResource.handle_resume().AndRaise(exc)
 
728
        self.m.ReplayAll()
 
729
 
 
730
        self.stack = parser.Stack(self.ctx, 'resume_test_fail',
 
731
                                  parser.Template(tmpl))
 
732
 
 
733
        stack_id = self.stack.store()
 
734
        self.stack.create()
 
735
        self.assertEqual(self.stack.state,
 
736
                         (self.stack.CREATE, self.stack.COMPLETE))
 
737
 
 
738
        self.stack.suspend()
 
739
 
 
740
        self.assertEqual(self.stack.state,
 
741
                         (self.stack.SUSPEND, self.stack.COMPLETE))
 
742
 
 
743
        self.stack.resume()
 
744
 
 
745
        self.assertEqual(self.stack.state,
 
746
                         (self.stack.RESUME, self.stack.FAILED))
 
747
        self.assertEqual(self.stack.status_reason,
 
748
                         'Resource resume failed: Exception: foo')
 
749
        self.m.VerifyAll()
 
750
 
 
751
    @stack_delete_after
 
752
    def test_suspend_timeout(self):
 
753
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
 
754
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_suspend')
 
755
        exc = scheduler.Timeout('foo', 0)
 
756
        generic_rsrc.GenericResource.handle_suspend().AndRaise(exc)
 
757
        self.m.ReplayAll()
 
758
 
 
759
        self.stack = parser.Stack(self.ctx, 'suspend_test_fail_timeout',
 
760
                                  parser.Template(tmpl))
 
761
 
 
762
        stack_id = self.stack.store()
 
763
        self.stack.create()
 
764
        self.assertEqual(self.stack.state,
 
765
                         (self.stack.CREATE, self.stack.COMPLETE))
 
766
 
 
767
        self.stack.suspend()
 
768
 
 
769
        self.assertEqual(self.stack.state,
 
770
                         (self.stack.SUSPEND, self.stack.FAILED))
 
771
        self.assertEqual(self.stack.status_reason, 'Suspend timed out')
 
772
        self.m.VerifyAll()
 
773
 
 
774
    @stack_delete_after
 
775
    def test_resume_timeout(self):
 
776
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
 
777
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_resume')
 
778
        exc = scheduler.Timeout('foo', 0)
 
779
        generic_rsrc.GenericResource.handle_resume().AndRaise(exc)
 
780
        self.m.ReplayAll()
 
781
 
 
782
        self.stack = parser.Stack(self.ctx, 'resume_test_fail_timeout',
 
783
                                  parser.Template(tmpl))
 
784
 
 
785
        stack_id = self.stack.store()
 
786
        self.stack.create()
 
787
        self.assertEqual(self.stack.state,
 
788
                         (self.stack.CREATE, self.stack.COMPLETE))
 
789
 
 
790
        self.stack.suspend()
 
791
 
 
792
        self.assertEqual(self.stack.state,
 
793
                         (self.stack.SUSPEND, self.stack.COMPLETE))
 
794
 
 
795
        self.stack.resume()
 
796
 
 
797
        self.assertEqual(self.stack.state,
 
798
                         (self.stack.RESUME, self.stack.FAILED))
 
799
 
 
800
        self.assertEqual(self.stack.status_reason, 'Resume timed out')
 
801
        self.m.VerifyAll()
417
802
 
418
803
    @stack_delete_after
419
804
    def test_delete_rollback(self):
428
813
 
429
814
        db_s = db_api.stack_get(self.ctx, stack_id)
430
815
        self.assertEqual(db_s, None)
431
 
        self.assertEqual(self.stack.state, self.stack.ROLLBACK_COMPLETE)
 
816
        self.assertEqual(self.stack.state,
 
817
                         (parser.Stack.ROLLBACK, parser.Stack.COMPLETE))
432
818
 
433
819
    @stack_delete_after
434
820
    def test_delete_badaction(self):
443
829
 
444
830
        db_s = db_api.stack_get(self.ctx, stack_id)
445
831
        self.assertNotEqual(db_s, None)
446
 
        self.assertEqual(self.stack.state, self.stack.DELETE_FAILED)
 
832
        self.assertEqual(self.stack.state,
 
833
                         (parser.Stack.DELETE, parser.Stack.FAILED))
447
834
 
448
835
    @stack_delete_after
449
836
    def test_update_badstate(self):
450
837
        self.stack = parser.Stack(self.ctx, 'test_stack', parser.Template({}),
451
 
                                  state=parser.Stack.CREATE_FAILED)
 
838
                                  action=parser.Stack.CREATE,
 
839
                                  status=parser.Stack.FAILED)
452
840
        stack_id = self.stack.store()
453
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_FAILED)
 
841
        self.assertEqual(self.stack.state,
 
842
                         (parser.Stack.CREATE, parser.Stack.FAILED))
454
843
        self.stack.update({})
455
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED)
 
844
        self.assertEqual(self.stack.state,
 
845
                         (parser.Stack.UPDATE, parser.Stack.FAILED))
456
846
 
457
847
    @stack_delete_after
458
848
    def test_resource_by_refid(self):
462
852
                                  template.Template(tmpl))
463
853
        self.stack.store()
464
854
        self.stack.create()
465
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
855
        self.assertEqual(self.stack.state,
 
856
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
466
857
        self.assertTrue('AResource' in self.stack)
467
 
        resource = self.stack['AResource']
468
 
        resource.resource_id_set('aaaa')
 
858
        rsrc = self.stack['AResource']
 
859
        rsrc.resource_id_set('aaaa')
469
860
        self.assertNotEqual(None, resource)
470
 
        self.assertEqual(resource, self.stack.resource_by_refid('aaaa'))
471
 
 
472
 
        resource.state = resource.DELETE_IN_PROGRESS
473
 
        self.assertEqual(None, self.stack.resource_by_refid('aaaa'))
474
 
 
475
 
        self.assertEqual(None, self.stack.resource_by_refid('bbbb'))
 
861
        self.assertEqual(rsrc, self.stack.resource_by_refid('aaaa'))
 
862
 
 
863
        rsrc.state_set(rsrc.DELETE, rsrc.IN_PROGRESS)
 
864
        try:
 
865
            self.assertEqual(None, self.stack.resource_by_refid('aaaa'))
 
866
            self.assertEqual(None, self.stack.resource_by_refid('bbbb'))
 
867
        finally:
 
868
            rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE)
476
869
 
477
870
    @stack_delete_after
478
871
    def test_update_add(self):
482
875
                                  template.Template(tmpl))
483
876
        self.stack.store()
484
877
        self.stack.create()
485
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
878
        self.assertEqual(self.stack.state,
 
879
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
486
880
 
487
881
        tmpl2 = {'Resources': {
488
882
                 'AResource': {'Type': 'GenericResourceType'},
490
884
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
491
885
                                     template.Template(tmpl2))
492
886
        self.stack.update(updated_stack)
493
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_COMPLETE)
 
887
        self.assertEqual(self.stack.state,
 
888
                         (parser.Stack.UPDATE, parser.Stack.COMPLETE))
494
889
        self.assertTrue('BResource' in self.stack)
495
890
 
496
891
    @stack_delete_after
503
898
                                  template.Template(tmpl))
504
899
        self.stack.store()
505
900
        self.stack.create()
506
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
901
        self.assertEqual(self.stack.state,
 
902
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
507
903
 
508
904
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
509
905
 
510
906
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
511
907
                                     template.Template(tmpl2))
512
908
        self.stack.update(updated_stack)
513
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_COMPLETE)
 
909
        self.assertEqual(self.stack.state,
 
910
                         (parser.Stack.UPDATE, parser.Stack.COMPLETE))
514
911
        self.assertFalse('BResource' in self.stack)
515
912
 
516
913
    @stack_delete_after
522
919
                                  template.Template(tmpl))
523
920
        self.stack.store()
524
921
        self.stack.create()
525
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
922
        self.assertEqual(self.stack.state,
 
923
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
526
924
 
527
925
        tmpl2 = {'Description': 'BTemplate',
528
926
                 'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
530
928
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
531
929
                                     template.Template(tmpl2))
532
930
        self.stack.update(updated_stack)
533
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_COMPLETE)
 
931
        self.assertEqual(self.stack.state,
 
932
                         (parser.Stack.UPDATE, parser.Stack.COMPLETE))
534
933
        self.assertEqual(self.stack.t[template.DESCRIPTION], 'BTemplate')
535
934
 
536
935
    @stack_delete_after
537
936
    def test_update_modify_ok_replace(self):
538
 
        # patch in a dummy property schema for GenericResource
539
 
        dummy_schema = {'Foo': {'Type': 'String'}}
540
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
541
 
 
542
 
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
937
        tmpl = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
543
938
                                            'Properties': {'Foo': 'abc'}}}}
544
939
 
545
940
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
546
941
                                  template.Template(tmpl))
547
942
        self.stack.store()
548
943
        self.stack.create()
549
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
944
        self.assertEqual(self.stack.state,
 
945
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
550
946
 
551
 
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
947
        tmpl2 = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
552
948
                                             'Properties': {'Foo': 'xyz'}}}}
553
949
 
554
950
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
555
951
                                     template.Template(tmpl2))
556
 
        # patch in a dummy handle_update
557
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
558
 
        generic_rsrc.GenericResource.handle_update(
559
 
            tmpl2['Resources']['AResource']).AndReturn(
560
 
                resource.Resource.UPDATE_REPLACE)
 
952
 
 
953
        # Calls to GenericResource.handle_update will raise
 
954
        # resource.UpdateReplace because we've not specified the modified
 
955
        # key/property in update_allowed_keys/update_allowed_properties
561
956
        self.m.ReplayAll()
562
957
 
563
958
        self.stack.update(updated_stack)
564
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_COMPLETE)
 
959
        self.assertEqual(self.stack.state,
 
960
                         (parser.Stack.UPDATE, parser.Stack.COMPLETE))
565
961
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'xyz')
566
962
        self.m.VerifyAll()
567
963
 
568
964
    @stack_delete_after
569
965
    def test_update_modify_update_failed(self):
570
 
        # patch in a dummy property schema for GenericResource
571
 
        dummy_schema = {'Foo': {'Type': 'String'}}
572
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
573
 
 
574
 
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
966
        tmpl = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
575
967
                                            'Properties': {'Foo': 'abc'}}}}
576
968
 
577
969
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
579
971
                                  disable_rollback=True)
580
972
        self.stack.store()
581
973
        self.stack.create()
582
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
583
 
 
584
 
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
974
        self.assertEqual(self.stack.state,
 
975
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
 
976
 
 
977
        res = self.stack['AResource']
 
978
        res.update_allowed_keys = ('Properties',)
 
979
        res.update_allowed_properties = ('Foo',)
 
980
 
 
981
        tmpl2 = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
585
982
                                             'Properties': {'Foo': 'xyz'}}}}
586
983
 
587
984
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
589
986
 
590
987
        # patch in a dummy handle_update
591
988
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
 
989
        tmpl_diff = {'Properties': {'Foo': 'xyz'}}
 
990
        prop_diff = {'Foo': 'xyz'}
592
991
        generic_rsrc.GenericResource.handle_update(
593
 
            tmpl2['Resources']['AResource']).AndReturn(
594
 
                resource.Resource.UPDATE_FAILED)
 
992
            tmpl2['Resources']['AResource'], tmpl_diff,
 
993
            prop_diff).AndRaise(Exception("Foo"))
595
994
        self.m.ReplayAll()
596
995
 
597
996
        self.stack.update(updated_stack)
598
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED)
 
997
        self.assertEqual(self.stack.state,
 
998
                         (parser.Stack.UPDATE, parser.Stack.FAILED))
599
999
        self.m.VerifyAll()
600
1000
 
601
1001
    @stack_delete_after
602
1002
    def test_update_modify_replace_failed_delete(self):
603
 
        # patch in a dummy property schema for GenericResource
604
 
        dummy_schema = {'Foo': {'Type': 'String'}}
605
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
606
 
 
607
 
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1003
        tmpl = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
608
1004
                                            'Properties': {'Foo': 'abc'}}}}
609
1005
 
610
1006
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
612
1008
                                  disable_rollback=True)
613
1009
        self.stack.store()
614
1010
        self.stack.create()
615
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1011
        self.assertEqual(self.stack.state,
 
1012
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
616
1013
 
617
 
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1014
        tmpl2 = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
618
1015
                                             'Properties': {'Foo': 'xyz'}}}}
619
1016
 
620
1017
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
621
1018
                                     template.Template(tmpl2))
622
1019
 
623
 
        # patch in a dummy handle_update
624
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
625
 
        generic_rsrc.GenericResource.handle_update(
626
 
            tmpl2['Resources']['AResource']).AndReturn(
627
 
                resource.Resource.UPDATE_REPLACE)
 
1020
        # Calls to GenericResource.handle_update will raise
 
1021
        # resource.UpdateReplace because we've not specified the modified
 
1022
        # key/property in update_allowed_keys/update_allowed_properties
628
1023
 
629
1024
        # make the update fail deleting the existing resource
630
1025
        self.m.StubOutWithMock(resource.Resource, 'destroy')
631
 
        resource.Resource.destroy().AndReturn("Error")
 
1026
        exc = exception.ResourceFailure(Exception())
 
1027
        resource.Resource.destroy().AndRaise(exc)
632
1028
        self.m.ReplayAll()
633
1029
 
634
1030
        self.stack.update(updated_stack)
635
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED)
 
1031
        self.assertEqual(self.stack.state,
 
1032
                         (parser.Stack.UPDATE, parser.Stack.FAILED))
636
1033
        self.m.VerifyAll()
637
1034
        # Unset here so destroy() is not stubbed for stack.delete cleanup
638
1035
        self.m.UnsetStubs()
639
1036
 
640
1037
    @stack_delete_after
641
1038
    def test_update_modify_replace_failed_create(self):
642
 
        # patch in a dummy property schema for GenericResource
643
 
        dummy_schema = {'Foo': {'Type': 'String'}}
644
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
645
 
 
646
 
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1039
        tmpl = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
647
1040
                                            'Properties': {'Foo': 'abc'}}}}
648
1041
 
649
1042
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
651
1044
                                  disable_rollback=True)
652
1045
        self.stack.store()
653
1046
        self.stack.create()
654
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1047
        self.assertEqual(self.stack.state,
 
1048
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
655
1049
 
656
 
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1050
        tmpl2 = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
657
1051
                                             'Properties': {'Foo': 'xyz'}}}}
658
1052
 
659
1053
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
660
1054
                                     template.Template(tmpl2))
661
1055
 
662
 
        # patch in a dummy handle_update
663
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
664
 
        generic_rsrc.GenericResource.handle_update(
665
 
            tmpl2['Resources']['AResource']).AndReturn(
666
 
                resource.Resource.UPDATE_REPLACE)
 
1056
        # Calls to GenericResource.handle_update will raise
 
1057
        # resource.UpdateReplace because we've not specified the modified
 
1058
        # key/property in update_allowed_keys/update_allowed_properties
667
1059
 
668
1060
        # patch in a dummy handle_create making the replace fail creating
669
1061
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
671
1063
        self.m.ReplayAll()
672
1064
 
673
1065
        self.stack.update(updated_stack)
674
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED)
 
1066
        self.assertEqual(self.stack.state,
 
1067
                         (parser.Stack.UPDATE, parser.Stack.FAILED))
675
1068
        self.m.VerifyAll()
676
1069
 
677
1070
    @stack_delete_after
682
1075
                                  template.Template(tmpl))
683
1076
        self.stack.store()
684
1077
        self.stack.create()
685
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1078
        self.assertEqual(self.stack.state,
 
1079
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
686
1080
 
687
1081
        tmpl2 = {'Resources': {
688
1082
                 'AResource': {'Type': 'GenericResourceType'},
696
1090
        self.m.ReplayAll()
697
1091
 
698
1092
        self.stack.update(updated_stack)
699
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED)
 
1093
        self.assertEqual(self.stack.state,
 
1094
                         (parser.Stack.UPDATE, parser.Stack.FAILED))
700
1095
        self.assertTrue('BResource' in self.stack)
701
1096
 
702
1097
        # Reload the stack from the DB and prove that it contains the failed
707
1102
 
708
1103
    @stack_delete_after
709
1104
    def test_update_rollback(self):
710
 
        # patch in a dummy property schema for GenericResource
711
 
        dummy_schema = {'Foo': {'Type': 'String'}}
712
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
713
 
 
714
 
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1105
        tmpl = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
715
1106
                                            'Properties': {'Foo': 'abc'}}}}
716
1107
 
717
1108
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
719
1110
                                  disable_rollback=False)
720
1111
        self.stack.store()
721
1112
        self.stack.create()
722
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1113
        self.assertEqual(self.stack.state,
 
1114
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
723
1115
 
724
 
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1116
        tmpl2 = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
725
1117
                                             'Properties': {'Foo': 'xyz'}}}}
726
1118
 
727
1119
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
728
1120
                                     template.Template(tmpl2))
729
1121
 
730
 
        # There will be two calls to handle_update, one for the new template
731
 
        # then another (with the initial template) for rollback
732
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
733
 
        generic_rsrc.GenericResource.handle_update(
734
 
            tmpl2['Resources']['AResource']).AndReturn(
735
 
                resource.Resource.UPDATE_REPLACE)
736
 
        generic_rsrc.GenericResource.handle_update(
737
 
            tmpl['Resources']['AResource']).AndReturn(
738
 
                resource.Resource.UPDATE_REPLACE)
 
1122
        # Calls to GenericResource.handle_update will raise
 
1123
        # resource.UpdateReplace because we've not specified the modified
 
1124
        # key/property in update_allowed_keys/update_allowed_properties
739
1125
 
740
1126
        # patch in a dummy handle_create making the replace fail when creating
741
 
        # the replacement resource, but succeed the second call (rollback)
 
1127
        # the replacement rsrc, but succeed the second call (rollback)
742
1128
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
743
1129
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
744
1130
        generic_rsrc.GenericResource.handle_create().AndReturn(None)
745
1131
        self.m.ReplayAll()
746
1132
 
747
1133
        self.stack.update(updated_stack)
748
 
        self.assertEqual(self.stack.state, parser.Stack.ROLLBACK_COMPLETE)
 
1134
        self.assertEqual(self.stack.state,
 
1135
                         (parser.Stack.ROLLBACK, parser.Stack.COMPLETE))
749
1136
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
750
1137
        self.m.VerifyAll()
751
1138
 
752
1139
    @stack_delete_after
753
1140
    def test_update_rollback_fail(self):
754
 
        # patch in a dummy property schema for GenericResource
755
 
        dummy_schema = {'Foo': {'Type': 'String'}}
756
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
757
 
 
758
 
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1141
        tmpl = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
759
1142
                                            'Properties': {'Foo': 'abc'}}}}
760
1143
 
761
1144
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
763
1146
                                  disable_rollback=False)
764
1147
        self.stack.store()
765
1148
        self.stack.create()
766
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1149
        self.assertEqual(self.stack.state,
 
1150
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
767
1151
 
768
 
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType',
 
1152
        tmpl2 = {'Resources': {'AResource': {'Type': 'ResourceWithPropsType',
769
1153
                                             'Properties': {'Foo': 'xyz'}}}}
770
1154
 
771
1155
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
772
1156
                                     template.Template(tmpl2))
773
1157
 
774
 
        # There will be two calls to handle_update, one for the new template
775
 
        # then another (with the initial template) for rollback
776
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
777
 
        generic_rsrc.GenericResource.handle_update(
778
 
            tmpl2['Resources']['AResource']).AndReturn(
779
 
                resource.Resource.UPDATE_REPLACE)
780
 
        generic_rsrc.GenericResource.handle_update(
781
 
            tmpl['Resources']['AResource']).AndReturn(
782
 
                resource.Resource.UPDATE_REPLACE)
 
1158
        # Calls to GenericResource.handle_update will raise
 
1159
        # resource.UpdateReplace because we've not specified the modified
 
1160
        # key/property in update_allowed_keys/update_allowed_properties
783
1161
 
784
1162
        # patch in a dummy handle_create making the replace fail when creating
785
 
        # the replacement resource, and again on the second call (rollback)
 
1163
        # the replacement rsrc, and again on the second call (rollback)
786
1164
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
787
1165
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
788
1166
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
789
1167
        self.m.ReplayAll()
790
1168
 
791
1169
        self.stack.update(updated_stack)
792
 
        self.assertEqual(self.stack.state, parser.Stack.ROLLBACK_FAILED)
 
1170
        self.assertEqual(self.stack.state,
 
1171
                         (parser.Stack.ROLLBACK, parser.Stack.FAILED))
793
1172
        self.m.VerifyAll()
794
1173
 
795
1174
    @stack_delete_after
801
1180
                                  disable_rollback=False)
802
1181
        self.stack.store()
803
1182
        self.stack.create()
804
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1183
        self.assertEqual(self.stack.state,
 
1184
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
805
1185
 
806
1186
        tmpl2 = {'Resources': {
807
1187
                 'AResource': {'Type': 'GenericResourceType'},
811
1191
                                     template.Template(tmpl2))
812
1192
 
813
1193
        # patch in a dummy handle_create making the replace fail when creating
814
 
        # the replacement resource, and succeed on the second call (rollback)
 
1194
        # the replacement rsrc, and succeed on the second call (rollback)
815
1195
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
816
1196
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
817
1197
        self.m.ReplayAll()
818
1198
 
819
1199
        self.stack.update(updated_stack)
820
 
        self.assertEqual(self.stack.state, parser.Stack.ROLLBACK_COMPLETE)
 
1200
        self.assertEqual(self.stack.state,
 
1201
                         (parser.Stack.ROLLBACK, parser.Stack.COMPLETE))
821
1202
        self.assertFalse('BResource' in self.stack)
822
1203
        self.m.VerifyAll()
823
1204
 
832
1213
                                  disable_rollback=False)
833
1214
        self.stack.store()
834
1215
        self.stack.create()
835
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1216
        self.assertEqual(self.stack.state,
 
1217
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
836
1218
 
837
1219
        tmpl2 = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}}
838
1220
 
839
1221
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
840
1222
                                     template.Template(tmpl2))
841
1223
 
842
 
        # patch in a dummy destroy making the delete fail
843
 
        self.m.StubOutWithMock(resource.Resource, 'destroy')
844
 
        resource.Resource.destroy().AndReturn('Error')
 
1224
        # patch in a dummy delete making the destroy fail
 
1225
        self.m.StubOutWithMock(resource.Resource, 'delete')
 
1226
        exc = exception.ResourceFailure(Exception())
 
1227
        resource.Resource.delete().AndRaise(exc)
845
1228
        self.m.ReplayAll()
846
1229
 
847
1230
        self.stack.update(updated_stack)
848
 
        self.assertEqual(self.stack.state, parser.Stack.ROLLBACK_COMPLETE)
 
1231
        self.assertEqual(self.stack.state,
 
1232
                         (parser.Stack.ROLLBACK, parser.Stack.COMPLETE))
849
1233
        self.assertTrue('BResource' in self.stack)
850
1234
        self.m.VerifyAll()
851
 
        # Unset here so destroy() is not stubbed for stack.delete cleanup
 
1235
        # Unset here so delete() is not stubbed for stack.delete cleanup
852
1236
        self.m.UnsetStubs()
853
1237
 
854
1238
    @stack_delete_after
858
1242
        changes in dynamic attributes, due to other resources been updated
859
1243
        are not ignored and can cause dependant resources to be updated.
860
1244
        '''
861
 
        # patch in a dummy property schema for GenericResource
862
 
        dummy_schema = {'Foo': {'Type': 'String'}}
863
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
864
1245
        tmpl = {'Resources': {
865
 
                'AResource': {'Type': 'GenericResourceType',
 
1246
                'AResource': {'Type': 'ResourceWithPropsType',
866
1247
                              'Properties': {'Foo': 'abc'}},
867
 
                'BResource': {'Type': 'GenericResourceType',
 
1248
                'BResource': {'Type': 'ResourceWithPropsType',
868
1249
                              'Properties': {
869
1250
                              'Foo': {'Ref': 'AResource'}}}}}
870
1251
        tmpl2 = {'Resources': {
871
 
                 'AResource': {'Type': 'GenericResourceType',
 
1252
                 'AResource': {'Type': 'ResourceWithPropsType',
872
1253
                               'Properties': {'Foo': 'smelly'}},
873
 
                 'BResource': {'Type': 'GenericResourceType',
 
1254
                 'BResource': {'Type': 'ResourceWithPropsType',
874
1255
                               'Properties': {
875
1256
                               'Foo': {'Ref': 'AResource'}}}}}
876
1257
 
877
1258
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
878
1259
                                  template.Template(tmpl))
 
1260
 
 
1261
        self.m.ReplayAll()
 
1262
 
879
1263
        self.stack.store()
880
1264
        self.stack.create()
881
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1265
        self.m.VerifyAll()
 
1266
        self.assertEqual(self.stack.state,
 
1267
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
882
1268
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
883
1269
        self.assertEqual(self.stack['BResource'].properties['Foo'],
884
1270
                         'AResource')
885
1271
 
886
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
887
 
        generic_rsrc.GenericResource.handle_update(
888
 
            tmpl2['Resources']['AResource']).AndReturn(
889
 
                resource.Resource.UPDATE_REPLACE)
890
 
 
891
 
        br2_snip = {'Type': 'GenericResourceType',
892
 
                    'Properties': {'Foo': 'inst-007'}}
893
 
        generic_rsrc.GenericResource.handle_update(
894
 
            br2_snip).AndReturn(
895
 
                resource.Resource.UPDATE_REPLACE)
 
1272
        # Calls to GenericResource.handle_update will raise
 
1273
        # resource.UpdateReplace because we've not specified the modified
 
1274
        # key/property in update_allowed_keys/update_allowed_properties
896
1275
 
897
1276
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'FnGetRefId')
898
1277
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
904
1283
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
905
1284
                                     template.Template(tmpl2))
906
1285
        self.stack.update(updated_stack)
907
 
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_COMPLETE)
 
1286
        self.assertEqual(self.stack.state,
 
1287
                         (parser.Stack.UPDATE, parser.Stack.COMPLETE))
908
1288
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'smelly')
909
1289
        self.assertEqual(self.stack['BResource'].properties['Foo'], 'inst-007')
910
1290
        self.m.VerifyAll()
916
1296
        check that rollback still works with dynamic metadata
917
1297
        this test fails the first instance
918
1298
        '''
919
 
        # patch in a dummy property schema for GenericResource
920
 
        dummy_schema = {'Foo': {'Type': 'String'}}
921
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
922
1299
        tmpl = {'Resources': {
923
 
                'AResource': {'Type': 'GenericResourceType',
 
1300
                'AResource': {'Type': 'ResourceWithPropsType',
924
1301
                              'Properties': {'Foo': 'abc'}},
925
 
                'BResource': {'Type': 'GenericResourceType',
 
1302
                'BResource': {'Type': 'ResourceWithPropsType',
926
1303
                              'Properties': {
927
1304
                              'Foo': {'Ref': 'AResource'}}}}}
928
1305
        tmpl2 = {'Resources': {
929
 
                 'AResource': {'Type': 'GenericResourceType',
 
1306
                 'AResource': {'Type': 'ResourceWithPropsType',
930
1307
                               'Properties': {'Foo': 'smelly'}},
931
 
                 'BResource': {'Type': 'GenericResourceType',
 
1308
                 'BResource': {'Type': 'ResourceWithPropsType',
932
1309
                               'Properties': {
933
1310
                               'Foo': {'Ref': 'AResource'}}}}}
934
1311
 
935
1312
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
936
1313
                                  template.Template(tmpl),
937
1314
                                  disable_rollback=False)
 
1315
 
 
1316
        self.m.ReplayAll()
 
1317
 
938
1318
        self.stack.store()
939
1319
        self.stack.create()
940
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1320
        self.m.VerifyAll()
 
1321
 
 
1322
        self.assertEqual(self.stack.state,
 
1323
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
941
1324
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
942
1325
        self.assertEqual(self.stack['BResource'].properties['Foo'],
943
1326
                         'AResource')
944
1327
 
945
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
946
1328
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'FnGetRefId')
947
1329
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
948
1330
 
949
 
        # mocks for first (failed update)
950
 
        generic_rsrc.GenericResource.handle_update(
951
 
            tmpl2['Resources']['AResource']).AndReturn(
952
 
                resource.Resource.UPDATE_REPLACE)
 
1331
        # Calls to GenericResource.handle_update will raise
 
1332
        # resource.UpdateReplace because we've not specified the modified
 
1333
        # key/property in update_allowed_keys/update_allowed_properties
 
1334
 
953
1335
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
954
1336
            'AResource')
955
1337
 
956
1338
        # mock to make the replace fail when creating the replacement resource
957
1339
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
958
1340
 
959
 
        # mocks for second rollback update
960
 
        generic_rsrc.GenericResource.handle_update(
961
 
            tmpl['Resources']['AResource']).AndReturn(
962
 
                resource.Resource.UPDATE_REPLACE)
963
 
 
964
1341
        generic_rsrc.GenericResource.handle_create().AndReturn(None)
965
1342
        generic_rsrc.GenericResource.FnGetRefId().MultipleTimes().AndReturn(
966
1343
            'AResource')
971
1348
                                     template.Template(tmpl2),
972
1349
                                     disable_rollback=False)
973
1350
        self.stack.update(updated_stack)
974
 
        self.assertEqual(self.stack.state, parser.Stack.ROLLBACK_COMPLETE)
 
1351
        self.assertEqual(self.stack.state,
 
1352
                         (parser.Stack.ROLLBACK, parser.Stack.COMPLETE))
975
1353
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
976
1354
 
977
1355
        self.m.VerifyAll()
983
1361
        check that rollback still works with dynamic metadata
984
1362
        this test fails the second instance
985
1363
        '''
986
 
        # patch in a dummy property schema for GenericResource
987
 
        dummy_schema = {'Foo': {'Type': 'String'}}
988
 
        generic_rsrc.GenericResource.properties_schema = dummy_schema
 
1364
 
 
1365
        class ResourceTypeA(generic_rsrc.ResourceWithProps):
 
1366
            count = 0
 
1367
 
 
1368
            def handle_create(self):
 
1369
                ResourceTypeA.count += 1
 
1370
                self.resource_id_set('%s%d' % (self.name, self.count))
 
1371
 
 
1372
        resource._register_class('ResourceTypeA', ResourceTypeA)
 
1373
 
989
1374
        tmpl = {'Resources': {
990
 
                'AResource': {'Type': 'GenericResourceType',
 
1375
                'AResource': {'Type': 'ResourceTypeA',
991
1376
                              'Properties': {'Foo': 'abc'}},
992
 
                'BResource': {'Type': 'GenericResourceType',
 
1377
                'BResource': {'Type': 'ResourceWithPropsType',
993
1378
                              'Properties': {
994
1379
                              'Foo': {'Ref': 'AResource'}}}}}
995
1380
        tmpl2 = {'Resources': {
996
 
                 'AResource': {'Type': 'GenericResourceType',
 
1381
                 'AResource': {'Type': 'ResourceTypeA',
997
1382
                               'Properties': {'Foo': 'smelly'}},
998
 
                 'BResource': {'Type': 'GenericResourceType',
 
1383
                 'BResource': {'Type': 'ResourceWithPropsType',
999
1384
                               'Properties': {
1000
1385
                               'Foo': {'Ref': 'AResource'}}}}}
1001
1386
 
1002
1387
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
1003
1388
                                  template.Template(tmpl),
1004
1389
                                  disable_rollback=False)
 
1390
 
 
1391
        self.m.ReplayAll()
 
1392
 
1005
1393
        self.stack.store()
1006
1394
        self.stack.create()
1007
 
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)
 
1395
        self.m.VerifyAll()
 
1396
 
 
1397
        self.assertEqual(self.stack.state,
 
1398
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
1008
1399
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
1009
1400
        self.assertEqual(self.stack['BResource'].properties['Foo'],
1010
 
                         'AResource')
 
1401
                         'AResource1')
1011
1402
 
1012
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_update')
1013
 
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'FnGetRefId')
1014
1403
        self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create')
1015
1404
 
1016
 
        # mocks for first and second (failed update)
1017
 
        generic_rsrc.GenericResource.handle_update(
1018
 
            tmpl2['Resources']['AResource']).AndReturn(
1019
 
                resource.Resource.UPDATE_REPLACE)
1020
 
        br2_snip = {'Type': 'GenericResourceType',
1021
 
                    'Properties': {'Foo': 'inst-007'}}
1022
 
        generic_rsrc.GenericResource.handle_update(
1023
 
            br2_snip).AndReturn(
1024
 
                resource.Resource.UPDATE_REPLACE)
1025
 
 
1026
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1027
 
            'AResource')
1028
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1029
 
            'inst-007')
1030
 
        # self.state_set(self.UPDATE_IN_PROGRESS)
1031
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1032
 
            'inst-007')
1033
 
        # self.state_set(self.DELETE_IN_PROGRESS)
1034
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1035
 
            'inst-007')
1036
 
        # self.state_set(self.DELETE_COMPLETE)
1037
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1038
 
            'inst-007')
1039
 
        # self.properties.validate()
1040
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1041
 
            'inst-007')
1042
 
        # self.state_set(self.CREATE_IN_PROGRESS)
1043
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1044
 
            'inst-007')
 
1405
        # Calls to GenericResource.handle_update will raise
 
1406
        # resource.UpdateReplace because we've not specified the modified
 
1407
        # key/property in update_allowed_keys/update_allowed_properties
1045
1408
 
1046
1409
        # mock to make the replace fail when creating the second
1047
1410
        # replacement resource
1048
 
        generic_rsrc.GenericResource.handle_create().AndReturn(None)
1049
1411
        generic_rsrc.GenericResource.handle_create().AndRaise(Exception)
1050
1412
 
1051
 
        # mocks for second rollback update
1052
 
        generic_rsrc.GenericResource.handle_update(
1053
 
            tmpl['Resources']['AResource']).AndReturn(
1054
 
                resource.Resource.UPDATE_REPLACE)
1055
 
        br2_snip = {'Type': 'GenericResourceType',
1056
 
                    'Properties': {'Foo': 'AResource'}}
1057
 
        generic_rsrc.GenericResource.handle_update(
1058
 
            br2_snip).AndReturn(
1059
 
                resource.Resource.UPDATE_REPLACE)
1060
 
 
1061
 
        # self.state_set(self.DELETE_IN_PROGRESS)
1062
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1063
 
            'inst-007')
1064
 
        # self.state_set(self.DELETE_IN_PROGRESS)
1065
 
        generic_rsrc.GenericResource.FnGetRefId().AndReturn(
1066
 
            'inst-007')
1067
 
 
1068
 
        generic_rsrc.GenericResource.handle_create().AndReturn(None)
1069
 
        generic_rsrc.GenericResource.handle_create().AndReturn(None)
1070
 
 
1071
 
        # reverting to AResource
1072
 
        generic_rsrc.GenericResource.FnGetRefId().MultipleTimes().AndReturn(
1073
 
            'AResource')
 
1413
        # Calls to GenericResource.handle_update will raise
 
1414
        # resource.UpdateReplace because we've not specified the modified
 
1415
        # key/property in update_allowed_keys/update_allowed_properties
 
1416
 
 
1417
        generic_rsrc.GenericResource.handle_create().AndReturn(None)
1074
1418
 
1075
1419
        self.m.ReplayAll()
1076
1420
 
1078
1422
                                     template.Template(tmpl2),
1079
1423
                                     disable_rollback=False)
1080
1424
        self.stack.update(updated_stack)
1081
 
        self.assertEqual(self.stack.state, parser.Stack.ROLLBACK_COMPLETE)
1082
 
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
 
1425
        self.assertEqual(self.stack.state,
 
1426
                         (parser.Stack.ROLLBACK, parser.Stack.COMPLETE))
 
1427
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
 
1428
        self.assertEqual(self.stack['BResource'].properties['Foo'],
 
1429
                         'AResource3')
 
1430
 
 
1431
        self.m.VerifyAll()
 
1432
 
 
1433
    @stack_delete_after
 
1434
    def test_update_replace_parameters(self):
 
1435
        '''
 
1436
        assertion:
 
1437
        changes in static environment parameters
 
1438
        are not ignored and can cause dependant resources to be updated.
 
1439
        '''
 
1440
        tmpl = {'Parameters': {'AParam': {'Type': 'String'}},
 
1441
                'Resources': {
 
1442
                    'AResource': {'Type': 'ResourceWithPropsType',
 
1443
                                  'Properties': {'Foo': {'Ref': 'AParam'}}}}}
 
1444
 
 
1445
        env1 = {'parameters': {'AParam': 'abc'}}
 
1446
        env2 = {'parameters': {'AParam': 'smelly'}}
 
1447
        self.stack = parser.Stack(self.ctx, 'update_test_stack',
 
1448
                                  template.Template(tmpl),
 
1449
                                  environment.Environment(env1))
 
1450
 
 
1451
        self.stack.store()
 
1452
        self.stack.create()
 
1453
        self.assertEqual(self.stack.state,
 
1454
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
 
1455
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'abc')
 
1456
 
 
1457
        updated_stack = parser.Stack(self.ctx, 'updated_stack',
 
1458
                                     template.Template(tmpl),
 
1459
                                     environment.Environment(env2))
 
1460
        self.stack.update(updated_stack)
 
1461
        self.assertEqual(self.stack.state,
 
1462
                         (parser.Stack.UPDATE, parser.Stack.COMPLETE))
 
1463
        self.assertEqual(self.stack['AResource'].properties['Foo'], 'smelly')
 
1464
 
 
1465
    def test_stack_create_timeout(self):
 
1466
        self.m.StubOutWithMock(scheduler.DependencyTaskGroup, '__call__')
 
1467
        self.m.StubOutWithMock(scheduler, 'wallclock')
 
1468
 
 
1469
        stack = parser.Stack(None, 's', parser.Template({}))
 
1470
 
 
1471
        def dummy_task():
 
1472
            while True:
 
1473
                yield
 
1474
 
 
1475
        start_time = time.time()
 
1476
        scheduler.wallclock().AndReturn(start_time)
 
1477
        scheduler.wallclock().AndReturn(start_time + 1)
 
1478
        scheduler.DependencyTaskGroup.__call__().AndReturn(dummy_task())
 
1479
        scheduler.wallclock().AndReturn(start_time + stack.timeout_secs() + 1)
 
1480
 
 
1481
        self.m.ReplayAll()
 
1482
 
 
1483
        stack.create()
 
1484
 
 
1485
        self.assertEqual(stack.state,
 
1486
                         (parser.Stack.CREATE, parser.Stack.FAILED))
 
1487
        self.assertEqual(stack.status_reason, 'Create timed out')
1083
1488
 
1084
1489
        self.m.VerifyAll()
1085
1490
 
1124
1529
                          parser.Template({}))
1125
1530
        self.assertRaises(ValueError, parser.Stack, None, '#test',
1126
1531
                          parser.Template({}))
 
1532
 
 
1533
    @stack_delete_after
 
1534
    def test_resource_state_get_att(self):
 
1535
        tmpl = {
 
1536
            'Resources': {'AResource': {'Type': 'GenericResourceType'}},
 
1537
            'Outputs': {'TestOutput': {'Value': {
 
1538
                'Fn::GetAtt': ['AResource', 'Foo']}}
 
1539
            }
 
1540
        }
 
1541
 
 
1542
        self.stack = parser.Stack(self.ctx, 'resource_state_get_att',
 
1543
                                  template.Template(tmpl))
 
1544
        self.stack.store()
 
1545
        self.stack.create()
 
1546
        self.assertEqual(self.stack.state,
 
1547
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
 
1548
        self.assertTrue('AResource' in self.stack)
 
1549
        rsrc = self.stack['AResource']
 
1550
        rsrc.resource_id_set('aaaa')
 
1551
        self.assertEqual('AResource', rsrc.FnGetAtt('Foo'))
 
1552
 
 
1553
        for action, status in (
 
1554
                (rsrc.CREATE, rsrc.IN_PROGRESS),
 
1555
                (rsrc.CREATE, rsrc.COMPLETE),
 
1556
                (rsrc.UPDATE, rsrc.IN_PROGRESS),
 
1557
                (rsrc.UPDATE, rsrc.COMPLETE)):
 
1558
            rsrc.state_set(action, status)
 
1559
            self.assertEqual('AResource', self.stack.output('TestOutput'))
 
1560
        for action, status in (
 
1561
                (rsrc.CREATE, rsrc.FAILED),
 
1562
                (rsrc.DELETE, rsrc.IN_PROGRESS),
 
1563
                (rsrc.DELETE, rsrc.FAILED),
 
1564
                (rsrc.DELETE, rsrc.COMPLETE),
 
1565
                (rsrc.UPDATE, rsrc.FAILED)):
 
1566
            rsrc.state_set(action, status)
 
1567
            self.assertEqual(None, self.stack.output('TestOutput'))
 
1568
 
 
1569
    @stack_delete_after
 
1570
    def test_resource_required_by(self):
 
1571
        tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType'},
 
1572
                              'BResource': {'Type': 'GenericResourceType',
 
1573
                                            'DependsOn': 'AResource'},
 
1574
                              'CResource': {'Type': 'GenericResourceType',
 
1575
                                            'DependsOn': 'BResource'},
 
1576
                              'DResource': {'Type': 'GenericResourceType',
 
1577
                                            'DependsOn': 'BResource'}}}
 
1578
 
 
1579
        self.stack = parser.Stack(self.ctx, 'depends_test_stack',
 
1580
                                  template.Template(tmpl))
 
1581
        self.stack.store()
 
1582
        self.stack.create()
 
1583
        self.assertEqual(self.stack.state,
 
1584
                         (parser.Stack.CREATE, parser.Stack.COMPLETE))
 
1585
 
 
1586
        self.assertEqual(['BResource'],
 
1587
                         self.stack['AResource'].required_by())
 
1588
        self.assertEqual([],
 
1589
                         self.stack['CResource'].required_by())
 
1590
        required_by = self.stack['BResource'].required_by()
 
1591
        self.assertEqual(2, len(required_by))
 
1592
        for r in ['CResource', 'DResource']:
 
1593
            self.assertIn(r, required_by)