~nvalcarcel/ubuntu/lucid/puppet/fix-546677

« back to all changes in this revision

Viewing changes to spec/unit/resource/catalog.rb

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2009-12-23 00:48:10 UTC
  • mfrom: (1.1.10 upstream) (3.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091223004810-3i4oryds922g5n59
Tags: 0.25.1-3ubuntu1
* Merge from debian testing.  Remaining changes:
  - debian/rules:
    + Don't start puppet when first installing puppet.
  - debian/puppet.conf, lib/puppet/defaults.rb:
    + Move templates to /etc/puppet
  - lib/puppet/defaults.rb:
    + Fix /var/lib/puppet/state ownership.
  - man/man8/puppet.conf.8: 
    + Fix broken URL in manpage.
  - debian/control:
    + Update maintainer accordint to spec.
    + Puppetmaster Recommends -> Suggests
    + Created puppet-testsuite as a seperate. Allow the users to run puppet's 
      testsuite.
  - tests/Rakefile: Fix rakefile so that the testsuite can acutally be ran.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env ruby
 
2
 
 
3
require File.dirname(__FILE__) + '/../../spec_helper'
 
4
 
 
5
describe Puppet::Resource::Catalog, "when compiling" do
 
6
    it "should be an Expirer" do
 
7
        Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer)
 
8
    end
 
9
 
 
10
    it "should always be expired if it's not applying" do
 
11
        @catalog = Puppet::Resource::Catalog.new("host")
 
12
        @catalog.expects(:applying?).returns false
 
13
        @catalog.should be_dependent_data_expired(Time.now)
 
14
    end
 
15
 
 
16
    it "should not be expired if it's applying and the timestamp is late enough" do
 
17
        @catalog = Puppet::Resource::Catalog.new("host")
 
18
        @catalog.expire
 
19
        @catalog.expects(:applying?).returns true
 
20
        @catalog.should_not be_dependent_data_expired(Time.now)
 
21
    end
 
22
 
 
23
    it "should be able to write its list of classes to the class file" do
 
24
        @catalog = Puppet::Resource::Catalog.new("host")
 
25
 
 
26
        @catalog.add_class "foo", "bar"
 
27
 
 
28
        Puppet.settings.expects(:value).with(:classfile).returns "/class/file"
 
29
 
 
30
        fh = mock 'filehandle'
 
31
        File.expects(:open).with("/class/file", "w").yields fh
 
32
 
 
33
        fh.expects(:puts).with "foo\nbar"
 
34
 
 
35
        @catalog.write_class_file
 
36
    end
 
37
 
 
38
    it "should have a client_version attribute" do
 
39
        @catalog = Puppet::Resource::Catalog.new("host")
 
40
        @catalog.client_version = 5
 
41
        @catalog.client_version.should == 5
 
42
    end
 
43
 
 
44
    it "should have a server_version attribute" do
 
45
        @catalog = Puppet::Resource::Catalog.new("host")
 
46
        @catalog.server_version = 5
 
47
        @catalog.server_version.should == 5
 
48
    end
 
49
 
 
50
    describe "when compiling" do
 
51
        it "should accept tags" do
 
52
            config = Puppet::Resource::Catalog.new("mynode")
 
53
            config.tag("one")
 
54
            config.tags.should == %w{one}
 
55
        end
 
56
 
 
57
        it "should accept multiple tags at once" do
 
58
            config = Puppet::Resource::Catalog.new("mynode")
 
59
            config.tag("one", "two")
 
60
            config.tags.should == %w{one two}
 
61
        end
 
62
 
 
63
        it "should convert all tags to strings" do
 
64
            config = Puppet::Resource::Catalog.new("mynode")
 
65
            config.tag("one", :two)
 
66
            config.tags.should == %w{one two}
 
67
        end
 
68
 
 
69
        it "should tag with both the qualified name and the split name" do
 
70
            config = Puppet::Resource::Catalog.new("mynode")
 
71
            config.tag("one::two")
 
72
            config.tags.include?("one").should be_true
 
73
            config.tags.include?("one::two").should be_true
 
74
        end
 
75
 
 
76
        it "should accept classes" do
 
77
            config = Puppet::Resource::Catalog.new("mynode")
 
78
            config.add_class("one")
 
79
            config.classes.should == %w{one}
 
80
            config.add_class("two", "three")
 
81
            config.classes.should == %w{one two three}
 
82
        end
 
83
 
 
84
        it "should tag itself with passed class names" do
 
85
            config = Puppet::Resource::Catalog.new("mynode")
 
86
            config.add_class("one")
 
87
            config.tags.should == %w{one}
 
88
        end
 
89
    end
 
90
 
 
91
    describe "when extracting" do
 
92
        it "should return extraction result as the method result" do
 
93
            config = Puppet::Resource::Catalog.new("mynode")
 
94
            config.expects(:extraction_format).returns(:whatever)
 
95
            config.expects(:extract_to_whatever).returns(:result)
 
96
            config.extract.should == :result
 
97
        end
 
98
    end
 
99
 
 
100
    describe "when extracting transobjects" do
 
101
 
 
102
        def mkscope
 
103
            @parser = Puppet::Parser::Parser.new :Code => ""
 
104
            @node = Puppet::Node.new("mynode")
 
105
            @compiler = Puppet::Parser::Compiler.new(@node, @parser)
 
106
 
 
107
            # XXX This is ridiculous.
 
108
            @compiler.send(:evaluate_main)
 
109
            @scope = @compiler.topscope
 
110
        end
 
111
 
 
112
        def mkresource(type, name)
 
113
            Puppet::Parser::Resource.new(:type => type, :title => name, :source => @source, :scope => @scope)
 
114
        end
 
115
 
 
116
        it "should always create a TransBucket for the 'main' class" do
 
117
            config = Puppet::Resource::Catalog.new("mynode")
 
118
 
 
119
            @scope = mkscope
 
120
            @source = mock 'source'
 
121
 
 
122
            main = mkresource("class", :main)
 
123
            config.add_vertex(main)
 
124
 
 
125
            bucket = stub 'bucket', :file= => nil, :line= => nil, :classes= => nil
 
126
            bucket.expects(:type=).with("Class")
 
127
            bucket.expects(:name=).with(:main)
 
128
            main.stubs(:builtin?).returns(false)
 
129
 
 
130
            Puppet::TransBucket.expects(:new).returns bucket
 
131
 
 
132
            config.extract_to_transportable.should equal(bucket)
 
133
        end
 
134
 
 
135
        # Now try it with a more complicated graph -- a three tier graph, each tier
 
136
        it "should transform arbitrarily deep graphs into isomorphic trees" do
 
137
            config = Puppet::Resource::Catalog.new("mynode")
 
138
 
 
139
            @scope = mkscope
 
140
            @scope.stubs(:tags).returns([])
 
141
            @source = mock 'source'
 
142
 
 
143
            # Create our scopes.
 
144
            top = mkresource "class", :main
 
145
            topbucket = []
 
146
            topbucket.expects(:classes=).with([])
 
147
            top.expects(:to_trans).returns(topbucket)
 
148
            topres = mkresource "file", "/top"
 
149
            topres.expects(:to_trans).returns(:topres)
 
150
            config.add_edge top, topres
 
151
 
 
152
            middle = mkresource "class", "middle"
 
153
            middle.expects(:to_trans).returns([])
 
154
            config.add_edge top, middle
 
155
            midres = mkresource "file", "/mid"
 
156
            midres.expects(:to_trans).returns(:midres)
 
157
            config.add_edge middle, midres
 
158
 
 
159
            bottom = mkresource "class", "bottom"
 
160
            bottom.expects(:to_trans).returns([])
 
161
            config.add_edge middle, bottom
 
162
            botres = mkresource "file", "/bot"
 
163
            botres.expects(:to_trans).returns(:botres)
 
164
            config.add_edge bottom, botres
 
165
 
 
166
            toparray = config.extract_to_transportable
 
167
 
 
168
            # This is annoying; it should look like:
 
169
            #   [[[:botres], :midres], :topres]
 
170
            # but we can't guarantee sort order.
 
171
            toparray.include?(:topres).should be_true
 
172
 
 
173
            midarray = toparray.find { |t| t.is_a?(Array) }
 
174
            midarray.include?(:midres).should be_true
 
175
            botarray = midarray.find { |t| t.is_a?(Array) }
 
176
            botarray.include?(:botres).should be_true
 
177
        end
 
178
    end
 
179
 
 
180
    describe " when converting to a Puppet::Resource catalog" do
 
181
        before do
 
182
            @original = Puppet::Resource::Catalog.new("mynode")
 
183
            @original.tag(*%w{one two three})
 
184
            @original.add_class *%w{four five six}
 
185
 
 
186
            @top            = Puppet::TransObject.new 'top', "class"
 
187
            @topobject      = Puppet::TransObject.new '/topobject', "file"
 
188
            @middle         = Puppet::TransObject.new 'middle', "class"
 
189
            @middleobject   = Puppet::TransObject.new '/middleobject', "file"
 
190
            @bottom         = Puppet::TransObject.new 'bottom', "class"
 
191
            @bottomobject   = Puppet::TransObject.new '/bottomobject', "file"
 
192
 
 
193
            @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject]
 
194
 
 
195
            @original.add_resource(*@resources)
 
196
 
 
197
            @original.add_edge(@top, @topobject)
 
198
            @original.add_edge(@top, @middle)
 
199
            @original.add_edge(@middle, @middleobject)
 
200
            @original.add_edge(@middle, @bottom)
 
201
            @original.add_edge(@bottom, @bottomobject)
 
202
 
 
203
            @catalog = @original.to_resource
 
204
        end
 
205
 
 
206
        it "should copy over the version" do
 
207
            @original.version = "foo"
 
208
            @original.to_resource.version.should == "foo"
 
209
        end
 
210
 
 
211
        it "should add all resources as Puppet::Resource instances" do
 
212
            @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Resource) }
 
213
        end
 
214
 
 
215
        it "should copy the tag list to the new catalog" do
 
216
            @catalog.tags.sort.should == @original.tags.sort
 
217
        end
 
218
 
 
219
        it "should copy the class list to the new catalog" do
 
220
            @catalog.classes.should == @original.classes
 
221
        end
 
222
 
 
223
        it "should duplicate the original edges" do
 
224
            @original.edges.each do |edge|
 
225
                @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true
 
226
            end
 
227
        end
 
228
 
 
229
        it "should set itself as the catalog for each converted resource" do
 
230
            @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) }
 
231
        end
 
232
    end
 
233
 
 
234
    describe "when converting to a RAL catalog" do
 
235
        before do
 
236
            @original = Puppet::Resource::Catalog.new("mynode")
 
237
            @original.tag(*%w{one two three})
 
238
            @original.add_class *%w{four five six}
 
239
 
 
240
            @top            = Puppet::Resource.new :class, 'top'
 
241
            @topobject      = Puppet::Resource.new :file, '/topobject'
 
242
            @middle         = Puppet::Resource.new :class, 'middle'
 
243
            @middleobject   = Puppet::Resource.new :file, '/middleobject'
 
244
            @bottom         = Puppet::Resource.new :class, 'bottom'
 
245
            @bottomobject   = Puppet::Resource.new :file, '/bottomobject'
 
246
 
 
247
            @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject]
 
248
 
 
249
            @original.add_resource(*@resources)
 
250
 
 
251
            @original.add_edge(@top, @topobject)
 
252
            @original.add_edge(@top, @middle)
 
253
            @original.add_edge(@middle, @middleobject)
 
254
            @original.add_edge(@middle, @bottom)
 
255
            @original.add_edge(@bottom, @bottomobject)
 
256
 
 
257
            @catalog = @original.to_ral
 
258
        end
 
259
 
 
260
        it "should add all resources as RAL instances" do
 
261
            @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) }
 
262
        end
 
263
 
 
264
        it "should copy the tag list to the new catalog" do
 
265
            @catalog.tags.sort.should == @original.tags.sort
 
266
        end
 
267
 
 
268
        it "should copy the class list to the new catalog" do
 
269
            @catalog.classes.should == @original.classes
 
270
        end
 
271
 
 
272
        it "should duplicate the original edges" do
 
273
            @original.edges.each do |edge|
 
274
                @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true
 
275
            end
 
276
        end
 
277
 
 
278
        it "should set itself as the catalog for each converted resource" do
 
279
            @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) }
 
280
        end
 
281
 
 
282
        # This tests #931.
 
283
        it "should not lose track of resources whose names vary" do
 
284
            changer = Puppet::TransObject.new 'changer', 'test'
 
285
 
 
286
            config = Puppet::Resource::Catalog.new('test')
 
287
            config.add_resource(changer)
 
288
            config.add_resource(@top)
 
289
 
 
290
            config.add_edge(@top, changer)
 
291
 
 
292
            resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil
 
293
 
 
294
            #changer is going to get duplicated as part of a fix for aliases 1094
 
295
            changer.expects(:dup).returns(changer)
 
296
            changer.expects(:to_ral).returns(resource)
 
297
 
 
298
            newconfig = nil
 
299
 
 
300
            proc { @catalog = config.to_ral }.should_not raise_error
 
301
            @catalog.resource("Test[changer2]").should equal(resource)
 
302
        end
 
303
 
 
304
        after do
 
305
            # Remove all resource instances.
 
306
            @catalog.clear(true)
 
307
        end
 
308
    end
 
309
 
 
310
    describe "when filtering" do
 
311
        before :each do
 
312
            @original = Puppet::Resource::Catalog.new("mynode")
 
313
            @original.tag(*%w{one two three})
 
314
            @original.add_class *%w{four five six}
 
315
 
 
316
            @r1 = stub_everything 'r1', :ref => "File[/a]"
 
317
            @r1.stubs(:respond_to?).with(:ref).returns(true)
 
318
            @r1.stubs(:dup).returns(@r1)
 
319
            @r1.stubs(:is_a?).returns(Puppet::Resource).returns(true)
 
320
 
 
321
            @r2 = stub_everything 'r2', :ref => "File[/b]"
 
322
            @r2.stubs(:respond_to?).with(:ref).returns(true)
 
323
            @r2.stubs(:dup).returns(@r2)
 
324
            @r2.stubs(:is_a?).returns(Puppet::Resource).returns(true)
 
325
 
 
326
            @resources = [@r1,@r2]
 
327
 
 
328
            @original.add_resource(@r1,@r2)
 
329
        end
 
330
 
 
331
        it "should transform the catalog to a resource catalog" do
 
332
            @original.expects(:to_catalog).with { |h,b| h == :to_resource }
 
333
 
 
334
            @original.filter
 
335
        end
 
336
 
 
337
        it "should scan each catalog resource in turn and apply filtering block" do
 
338
            @resources.each { |r| r.expects(:test?) }
 
339
            @original.filter do |r|
 
340
                r.test?
 
341
            end
 
342
        end
 
343
 
 
344
        it "should filter out resources which produce true when the filter block is evaluated" do
 
345
            @original.filter do |r|
 
346
                r == @r1
 
347
            end.resource("File[/a]").should be_nil
 
348
        end
 
349
 
 
350
        it "should not consider edges against resources that were filtered out" do
 
351
            @original.add_edge(@r1,@r2)
 
352
            @original.filter do |r|
 
353
                r == @r1
 
354
            end.edge(@r1,@r2).should be_empty
 
355
        end
 
356
    end
 
357
 
 
358
    describe "when functioning as a resource container" do
 
359
        before do
 
360
            @catalog = Puppet::Resource::Catalog.new("host")
 
361
            @one = Puppet::Type.type(:notify).new :name => "one"
 
362
            @two = Puppet::Type.type(:notify).new :name => "two"
 
363
            @dupe = Puppet::Type.type(:notify).new :name => "one"
 
364
        end
 
365
 
 
366
        it "should provide a method to add one or more resources" do
 
367
            @catalog.add_resource @one, @two
 
368
            @catalog.resource(@one.ref).should equal(@one)
 
369
            @catalog.resource(@two.ref).should equal(@two)
 
370
        end
 
371
 
 
372
        it "should add resources to the relationship graph if it exists" do
 
373
            relgraph = @catalog.relationship_graph
 
374
            @catalog.add_resource @one
 
375
            relgraph.should be_vertex(@one)
 
376
        end
 
377
 
 
378
        it "should yield added resources if a block is provided" do
 
379
            yielded = []
 
380
            @catalog.add_resource(@one, @two) { |r| yielded << r }
 
381
            yielded.length.should == 2
 
382
        end
 
383
 
 
384
        it "should set itself as the resource's catalog if it is not a relationship graph" do
 
385
            @one.expects(:catalog=).with(@catalog)
 
386
            @catalog.add_resource @one
 
387
        end
 
388
 
 
389
        it "should make all vertices available by resource reference" do
 
390
            @catalog.add_resource(@one)
 
391
            @catalog.resource(@one.ref).should equal(@one)
 
392
            @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one)
 
393
        end
 
394
 
 
395
        it "should canonize how resources are referred to during retrieval when both type and title are provided" do
 
396
            @catalog.add_resource(@one)
 
397
 
 
398
            @catalog.resource("notify", "one").should equal(@one)
 
399
        end
 
400
 
 
401
        it "should canonize how resources are referred to during retrieval when just the title is provided" do
 
402
            @catalog.add_resource(@one)
 
403
 
 
404
            @catalog.resource("notify[one]", nil).should equal(@one)
 
405
        end
 
406
 
 
407
        it "should not allow two resources with the same resource reference" do
 
408
            @catalog.add_resource(@one)
 
409
 
 
410
            proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError)
 
411
        end
 
412
 
 
413
        it "should not store objects that do not respond to :ref" do
 
414
            proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError)
 
415
        end
 
416
 
 
417
        it "should remove all resources when asked" do
 
418
            @catalog.add_resource @one
 
419
            @catalog.add_resource @two
 
420
            @one.expects :remove
 
421
            @two.expects :remove
 
422
            @catalog.clear(true)
 
423
        end
 
424
 
 
425
        it "should support a mechanism for finishing resources" do
 
426
            @one.expects :finish
 
427
            @two.expects :finish
 
428
            @catalog.add_resource @one
 
429
            @catalog.add_resource @two
 
430
 
 
431
            @catalog.finalize
 
432
        end
 
433
 
 
434
        it "should make default resources when finalizing" do
 
435
            @catalog.expects(:make_default_resources)
 
436
            @catalog.finalize
 
437
        end
 
438
 
 
439
        it "should add default resources to the catalog upon creation" do
 
440
            @catalog.make_default_resources
 
441
            @catalog.resource(:schedule, "daily").should_not be_nil
 
442
        end
 
443
 
 
444
        it "should optionally support an initialization block and should finalize after such blocks" do
 
445
            @one.expects :finish
 
446
            @two.expects :finish
 
447
            config = Puppet::Resource::Catalog.new("host") do |conf|
 
448
                conf.add_resource @one
 
449
                conf.add_resource @two
 
450
            end
 
451
        end
 
452
 
 
453
        it "should inform the resource that it is the resource's catalog" do
 
454
            @one.expects(:catalog=).with(@catalog)
 
455
            @catalog.add_resource @one
 
456
        end
 
457
 
 
458
        it "should be able to find resources by reference" do
 
459
            @catalog.add_resource @one
 
460
            @catalog.resource(@one.ref).should equal(@one)
 
461
        end
 
462
 
 
463
        it "should be able to find resources by reference or by type/title tuple" do
 
464
            @catalog.add_resource @one
 
465
            @catalog.resource("notify", "one").should equal(@one)
 
466
        end
 
467
 
 
468
        it "should have a mechanism for removing resources" do
 
469
            @catalog.add_resource @one
 
470
            @one.expects :remove
 
471
            @catalog.remove_resource(@one)
 
472
            @catalog.resource(@one.ref).should be_nil
 
473
            @catalog.vertex?(@one).should be_false
 
474
        end
 
475
 
 
476
        it "should have a method for creating aliases for resources" do
 
477
            @catalog.add_resource @one
 
478
            @catalog.alias(@one, "other")
 
479
            @catalog.resource("notify", "other").should equal(@one)
 
480
        end
 
481
 
 
482
        it "should ignore conflicting aliases that point to the aliased resource" do
 
483
            @catalog.alias(@one, "other")
 
484
            lambda { @catalog.alias(@one, "other") }.should_not raise_error
 
485
        end
 
486
 
 
487
        it "should create aliases for resources isomorphic resources whose names do not match their titles" do
 
488
            resource = Puppet::Type::File.new(:title => "testing", :path => "/something")
 
489
 
 
490
            @catalog.add_resource(resource)
 
491
 
 
492
            @catalog.resource(:file, "/something").should equal(resource)
 
493
        end
 
494
 
 
495
        it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do
 
496
            resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin})
 
497
 
 
498
            @catalog.add_resource(resource)
 
499
 
 
500
            # Yay, I've already got a 'should' method
 
501
            @catalog.resource(:exec, "echo").object_id.should == nil.object_id
 
502
        end
 
503
 
 
504
        # This test is the same as the previous, but the behaviour should be explicit.
 
505
        it "should alias using the class name from the resource reference, not the resource class name" do
 
506
            @catalog.add_resource @one
 
507
            @catalog.alias(@one, "other")
 
508
            @catalog.resource("notify", "other").should equal(@one)
 
509
        end
 
510
 
 
511
        it "should ignore conflicting aliases that point to the aliased resource" do
 
512
            @catalog.alias(@one, "other")
 
513
            lambda { @catalog.alias(@one, "other") }.should_not raise_error
 
514
        end
 
515
 
 
516
        it "should fail to add an alias if the aliased name already exists" do
 
517
            @catalog.add_resource @one
 
518
            proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError)
 
519
        end
 
520
 
 
521
        it "should not fail when a resource has duplicate aliases created" do
 
522
            @catalog.add_resource @one
 
523
            proc { @catalog.alias @one, "one" }.should_not raise_error
 
524
        end
 
525
 
 
526
        it "should not create aliases that point back to the resource" do
 
527
            @catalog.alias(@one, "one")
 
528
            @catalog.resource(:notify, "one").should be_nil
 
529
        end
 
530
 
 
531
        it "should be able to look resources up by their aliases" do
 
532
            @catalog.add_resource @one
 
533
            @catalog.alias @one, "two"
 
534
            @catalog.resource(:notify, "two").should equal(@one)
 
535
        end
 
536
 
 
537
        it "should remove resource aliases when the target resource is removed" do
 
538
            @catalog.add_resource @one
 
539
            @catalog.alias(@one, "other")
 
540
            @one.expects :remove
 
541
            @catalog.remove_resource(@one)
 
542
            @catalog.resource("notify", "other").should be_nil
 
543
        end
 
544
 
 
545
        it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do
 
546
            resource = Puppet::Type.type(:file).new :path => "/something", :title => "other", :content => "blah"
 
547
            resource.expects(:isomorphic?).returns(true)
 
548
            @catalog.add_resource(resource)
 
549
            @catalog.resource(:file, "other").should equal(resource)
 
550
            @catalog.resource(:file, "/something").ref.should == resource.ref
 
551
        end
 
552
 
 
553
        it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do
 
554
            resource = Puppet::Type.type(:file).new :path => "/something", :title => "other", :content => "blah"
 
555
            resource.expects(:isomorphic?).returns(false)
 
556
            @catalog.add_resource(resource)
 
557
            @catalog.resource(:file, resource.title).should equal(resource)
 
558
            # We can't use .should here, because the resources respond to that method.
 
559
            if @catalog.resource(:file, resource.name)
 
560
                raise "Aliased non-isomorphic resource"
 
561
            end
 
562
        end
 
563
 
 
564
        it "should provide a method to create additional resources that also registers the resource" do
 
565
            args = {:name => "/yay", :ensure => :file}
 
566
            resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay"
 
567
            Puppet::Type.type(:file).expects(:new).with(args).returns(resource)
 
568
            @catalog.create_resource :file, args
 
569
            @catalog.resource("File[/yay]").should equal(resource)
 
570
        end
 
571
    end
 
572
 
 
573
    describe "when applying" do
 
574
        before :each do
 
575
            @catalog = Puppet::Resource::Catalog.new("host")
 
576
 
 
577
            @catalog.retrieval_duration = Time.now
 
578
            @transaction = mock 'transaction'
 
579
            Puppet::Transaction.stubs(:new).returns(@transaction)
 
580
            @transaction.stubs(:evaluate)
 
581
            @transaction.stubs(:cleanup)
 
582
            @transaction.stubs(:addtimes)
 
583
        end
 
584
 
 
585
        it "should create and evaluate a transaction" do
 
586
            @transaction.expects(:evaluate)
 
587
            @catalog.apply
 
588
        end
 
589
 
 
590
        it "should provide the catalog time to the transaction" do
 
591
            @transaction.expects(:addtimes).with do |arg|
 
592
                arg[:config_retrieval].should be_instance_of(Time)
 
593
                true
 
594
            end
 
595
            @catalog.apply
 
596
        end
 
597
 
 
598
        it "should clean up the transaction" do
 
599
            @transaction.expects :cleanup
 
600
            @catalog.apply
 
601
        end
 
602
 
 
603
        it "should return the transaction" do
 
604
            @catalog.apply.should equal(@transaction)
 
605
        end
 
606
 
 
607
        it "should yield the transaction if a block is provided" do
 
608
            @catalog.apply do |trans|
 
609
                trans.should equal(@transaction)
 
610
            end
 
611
        end
 
612
 
 
613
        it "should default to not being a host catalog" do
 
614
            @catalog.host_config.should be_nil
 
615
        end
 
616
 
 
617
        it "should pass supplied tags on to the transaction" do
 
618
            @transaction.expects(:tags=).with(%w{one two})
 
619
            @catalog.apply(:tags => %w{one two})
 
620
        end
 
621
 
 
622
        it "should set ignoreschedules on the transaction if specified in apply()" do
 
623
            @transaction.expects(:ignoreschedules=).with(true)
 
624
            @catalog.apply(:ignoreschedules => true)
 
625
        end
 
626
 
 
627
        it "should remove resources created mid-transaction" do
 
628
            args = {:name => "/yay", :ensure => :file}
 
629
            resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay"
 
630
            @transaction = mock 'transaction'
 
631
            Puppet::Transaction.stubs(:new).returns(@transaction)
 
632
            @transaction.stubs(:evaluate)
 
633
            @transaction.stubs(:cleanup)
 
634
            @transaction.stubs(:addtimes)
 
635
            Puppet::Type.type(:file).expects(:new).with(args).returns(resource)
 
636
            resource.expects :remove
 
637
            @catalog.apply do |trans|
 
638
                @catalog.create_resource :file, args
 
639
                @catalog.resource("File[/yay]").should equal(resource)
 
640
            end
 
641
            @catalog.resource("File[/yay]").should be_nil
 
642
        end
 
643
 
 
644
        it "should remove resources added mid-transaction" do
 
645
            @transaction = mock 'transaction'
 
646
            Puppet::Transaction.stubs(:new).returns(@transaction)
 
647
            @transaction.stubs(:evaluate)
 
648
            @transaction.stubs(:cleanup)
 
649
            @transaction.stubs(:addtimes)
 
650
            file = Puppet::Type.type(:file).new(:name => "/yay", :ensure => :file)
 
651
            @catalog.apply do |trans|
 
652
                @catalog.add_resource file
 
653
                @catalog.resource("File[/yay]").should_not be_nil
 
654
            end
 
655
            @catalog.resource("File[/yay]").should be_nil
 
656
        end
 
657
 
 
658
        it "should expire cached data in the resources both before and after the transaction" do
 
659
            @catalog.expects(:expire).times(2)
 
660
            @catalog.apply
 
661
        end
 
662
 
 
663
        describe "host catalogs" do
 
664
 
 
665
            # super() doesn't work in the setup method for some reason
 
666
            before do
 
667
                @catalog.host_config = true
 
668
                Puppet::Util::Storage.stubs(:store)
 
669
            end
 
670
 
 
671
            it "should send a report if reporting is enabled" do
 
672
                Puppet[:report] = true
 
673
                @transaction.expects :send_report
 
674
                @transaction.stubs :any_failed? => false
 
675
                @catalog.apply
 
676
            end
 
677
 
 
678
            it "should send a report if report summaries are enabled" do
 
679
                Puppet[:summarize] = true
 
680
                @transaction.expects :send_report
 
681
                @transaction.stubs :any_failed? => false
 
682
                @catalog.apply
 
683
            end
 
684
 
 
685
            it "should initialize the state database before applying a catalog" do
 
686
                Puppet::Util::Storage.expects(:load)
 
687
 
 
688
                # Short-circuit the apply, so we know we're loading before the transaction
 
689
                Puppet::Transaction.expects(:new).raises ArgumentError
 
690
                proc { @catalog.apply }.should raise_error(ArgumentError)
 
691
            end
 
692
 
 
693
            it "should sync the state database after applying" do
 
694
                Puppet::Util::Storage.expects(:store)
 
695
                @transaction.stubs :any_failed? => false
 
696
                @catalog.apply
 
697
            end
 
698
 
 
699
            after { Puppet.settings.clear }
 
700
        end
 
701
 
 
702
        describe "non-host catalogs" do
 
703
 
 
704
            before do
 
705
                @catalog.host_config = false
 
706
            end
 
707
 
 
708
            it "should never send reports" do
 
709
                Puppet[:report] = true
 
710
                Puppet[:summarize] = true
 
711
                @transaction.expects(:send_report).never
 
712
                @catalog.apply
 
713
            end
 
714
 
 
715
            it "should never modify the state database" do
 
716
                Puppet::Util::Storage.expects(:load).never
 
717
                Puppet::Util::Storage.expects(:store).never
 
718
                @catalog.apply
 
719
            end
 
720
 
 
721
            after { Puppet.settings.clear }
 
722
        end
 
723
    end
 
724
 
 
725
    describe "when creating a relationship graph" do
 
726
        before do
 
727
            Puppet::Type.type(:component)
 
728
            @catalog = Puppet::Resource::Catalog.new("host")
 
729
            @compone = Puppet::Type::Component.new :name => "one"
 
730
            @comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]"
 
731
            @file = Puppet::Type.type(:file)
 
732
            @one = @file.new :path => "/one"
 
733
            @two = @file.new :path => "/two"
 
734
            @sub = @file.new :path => "/two/subdir"
 
735
            @catalog.add_edge @compone, @one
 
736
            @catalog.add_edge @comptwo, @two
 
737
 
 
738
            @three = @file.new :path => "/three"
 
739
            @four = @file.new :path => "/four", :require => "File[/three]"
 
740
            @five = @file.new :path => "/five"
 
741
            @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub
 
742
 
 
743
            @relationships = @catalog.relationship_graph
 
744
        end
 
745
 
 
746
        it "should be able to create a relationship graph" do
 
747
            @relationships.should be_instance_of(Puppet::SimpleGraph)
 
748
        end
 
749
 
 
750
        it "should not have any components" do
 
751
            @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil
 
752
        end
 
753
 
 
754
        it "should have all non-component resources from the catalog" do
 
755
            # The failures print out too much info, so i just do a class comparison
 
756
            @relationships.vertex?(@five).should be_true
 
757
        end
 
758
 
 
759
        it "should have all resource relationships set as edges" do
 
760
            @relationships.edge?(@three, @four).should be_true
 
761
        end
 
762
 
 
763
        it "should copy component relationships to all contained resources" do
 
764
            @relationships.edge?(@one, @two).should be_true
 
765
        end
 
766
 
 
767
        it "should add automatic relationships to the relationship graph" do
 
768
            @relationships.edge?(@two, @sub).should be_true
 
769
        end
 
770
 
 
771
        it "should get removed when the catalog is cleaned up" do
 
772
            @relationships.expects(:clear)
 
773
            @catalog.clear
 
774
            @catalog.instance_variable_get("@relationship_graph").should be_nil
 
775
        end
 
776
 
 
777
        it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do
 
778
            @catalog.clear
 
779
            graph = Puppet::SimpleGraph.new
 
780
            Puppet::SimpleGraph.expects(:new).returns graph
 
781
 
 
782
            graph.expects(:write_graph).with(:relationships)
 
783
            graph.expects(:write_graph).with(:expanded_relationships)
 
784
 
 
785
            @catalog.host_config = true
 
786
 
 
787
            @catalog.relationship_graph
 
788
        end
 
789
 
 
790
        it "should not write graph files if the catalog is not a host catalog" do
 
791
            @catalog.clear
 
792
            graph = Puppet::SimpleGraph.new
 
793
            Puppet::SimpleGraph.expects(:new).returns graph
 
794
 
 
795
            graph.expects(:write_graph).never
 
796
 
 
797
            @catalog.host_config = false
 
798
 
 
799
            @catalog.relationship_graph
 
800
        end
 
801
 
 
802
        it "should create a new relationship graph after clearing the old one" do
 
803
            @relationships.expects(:clear)
 
804
            @catalog.clear
 
805
            @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph)
 
806
        end
 
807
 
 
808
        it "should remove removed resources from the relationship graph if it exists" do
 
809
            @catalog.remove_resource(@one)
 
810
            @catalog.relationship_graph.vertex?(@one).should be_false
 
811
        end
 
812
    end
 
813
 
 
814
    describe "when writing dot files" do
 
815
        before do
 
816
            @catalog = Puppet::Resource::Catalog.new("host")
 
817
            @name = :test
 
818
            @file = File.join(Puppet[:graphdir], @name.to_s + ".dot")
 
819
        end
 
820
 
 
821
        it "should only write when it is a host catalog" do
 
822
            File.expects(:open).with(@file).never
 
823
            @catalog.host_config = false
 
824
            Puppet[:graph] = true
 
825
            @catalog.write_graph(@name)
 
826
        end
 
827
 
 
828
        after do
 
829
            Puppet.settings.clear
 
830
        end
 
831
    end
 
832
 
 
833
    describe "when indirecting" do
 
834
        before do
 
835
            @indirection = stub 'indirection', :name => :catalog
 
836
 
 
837
            Puppet::Util::Cacher.expire
 
838
        end
 
839
 
 
840
        it "should redirect to the indirection for retrieval" do
 
841
            Puppet::Resource::Catalog.stubs(:indirection).returns(@indirection)
 
842
            @indirection.expects(:find)
 
843
            Puppet::Resource::Catalog.find(:myconfig)
 
844
        end
 
845
 
 
846
        it "should default to the 'compiler' terminus" do
 
847
            Puppet::Resource::Catalog.indirection.terminus_class.should == :compiler
 
848
        end
 
849
 
 
850
        after do
 
851
            Puppet::Util::Cacher.expire
 
852
        end
 
853
    end
 
854
 
 
855
    describe "when converting to yaml" do
 
856
        before do
 
857
            @catalog = Puppet::Resource::Catalog.new("me")
 
858
            @catalog.add_edge("one", "two")
 
859
        end
 
860
 
 
861
        it "should be able to be dumped to yaml" do
 
862
            YAML.dump(@catalog).should be_instance_of(String)
 
863
        end
 
864
    end
 
865
 
 
866
    describe "when converting from yaml" do
 
867
        before do
 
868
            @catalog = Puppet::Resource::Catalog.new("me")
 
869
            @catalog.add_edge("one", "two")
 
870
 
 
871
            text = YAML.dump(@catalog)
 
872
            @newcatalog = YAML.load(text)
 
873
        end
 
874
 
 
875
        it "should get converted back to a catalog" do
 
876
            @newcatalog.should be_instance_of(Puppet::Resource::Catalog)
 
877
        end
 
878
 
 
879
        it "should have all vertices" do
 
880
            @newcatalog.vertex?("one").should be_true
 
881
            @newcatalog.vertex?("two").should be_true
 
882
        end
 
883
 
 
884
        it "should have all edges" do
 
885
            @newcatalog.edge?("one", "two").should be_true
 
886
        end
 
887
    end
 
888
end
 
889
 
 
890
describe Puppet::Resource::Catalog, "when converting to pson" do
 
891
    confine "Missing 'pson' library" => Puppet.features.pson?
 
892
 
 
893
    before do
 
894
        @catalog = Puppet::Resource::Catalog.new("myhost")
 
895
    end
 
896
 
 
897
    def pson_output_should
 
898
        @catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something)
 
899
    end
 
900
 
 
901
    # LAK:NOTE For all of these tests, we convert back to the resource so we can
 
902
    # trap the actual data structure then.
 
903
    it "should set its document_type to 'Catalog'" do
 
904
        pson_output_should { |hash| hash['document_type'] == "Catalog" }
 
905
 
 
906
        PSON.parse @catalog.to_pson
 
907
    end
 
908
 
 
909
    it "should set its data as a hash" do
 
910
        pson_output_should { |hash| hash['data'].is_a?(Hash) }
 
911
        PSON.parse @catalog.to_pson
 
912
    end
 
913
 
 
914
    [:name, :version, :tags, :classes].each do |param|
 
915
        it "should set its #{param} to the #{param} of the resource" do
 
916
            @catalog.send(param.to_s + "=", "testing") unless @catalog.send(param)
 
917
 
 
918
            pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) }
 
919
            PSON.parse @catalog.to_pson
 
920
        end
 
921
    end
 
922
 
 
923
    it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do
 
924
        one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]"
 
925
        two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]"
 
926
 
 
927
        @catalog.add_resource(one)
 
928
        @catalog.add_resource(two)
 
929
 
 
930
        # TODO this should really guarantee sort order
 
931
        PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort
 
932
 
 
933
    end
 
934
 
 
935
    it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do
 
936
        one   = stub 'one',   :to_pson_data_hash => "one_resource",   :ref => 'Foo[one]'
 
937
        two   = stub 'two',   :to_pson_data_hash => "two_resource",   :ref => 'Foo[two]'
 
938
        three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]'
 
939
 
 
940
        @catalog.add_edge(one, two)
 
941
        @catalog.add_edge(two, three)
 
942
 
 
943
        @catalog.edge(one, two  ).expects(:to_pson_data_hash).returns "one_two_pson"
 
944
        @catalog.edge(two, three).expects(:to_pson_data_hash).returns "two_three_pson"
 
945
 
 
946
        PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort
 
947
    end
 
948
end
 
949
 
 
950
describe Puppet::Resource::Catalog, "when converting from pson" do
 
951
    confine "Missing 'pson' library" => Puppet.features.pson?
 
952
 
 
953
    def pson_result_should
 
954
        Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash }
 
955
    end
 
956
 
 
957
    before do
 
958
        @data = {
 
959
            'name' => "myhost"
 
960
        }
 
961
        @pson = {
 
962
            'document_type' => 'Puppet::Resource::Catalog',
 
963
            'data' => @data,
 
964
            'metadata' => {}
 
965
        }
 
966
 
 
967
        @catalog = Puppet::Resource::Catalog.new("myhost")
 
968
        Puppet::Resource::Catalog.stubs(:new).returns @catalog
 
969
    end
 
970
 
 
971
    it "should be extended with the PSON utility module" do
 
972
        Puppet::Resource::Catalog.metaclass.ancestors.should be_include(Puppet::Util::Pson)
 
973
    end
 
974
 
 
975
    it "should create it with the provided name" do
 
976
        Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog
 
977
        PSON.parse @pson.to_pson
 
978
    end
 
979
 
 
980
    it "should set the provided version on the catalog if one is set" do
 
981
        @data['version'] = 50
 
982
        PSON.parse @pson.to_pson
 
983
        @catalog.version.should == @data['version']
 
984
    end
 
985
 
 
986
    it "should set any provided tags on the catalog" do
 
987
        @data['tags'] = %w{one two}
 
988
        PSON.parse @pson.to_pson
 
989
        @catalog.tags.should == @data['tags']
 
990
    end
 
991
 
 
992
    it "should set any provided classes on the catalog" do
 
993
        @data['classes'] = %w{one two}
 
994
        PSON.parse @pson.to_pson
 
995
        @catalog.classes.should == @data['classes']
 
996
    end
 
997
 
 
998
    it 'should convert the resources list into resources and add each of them' do
 
999
        @data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")]
 
1000
 
 
1001
        @catalog.expects(:add_resource).times(2).with { |res| res.type == "File" }
 
1002
        PSON.parse @pson.to_pson
 
1003
    end
 
1004
 
 
1005
    it 'should convert resources even if they do not include "type" information' do
 
1006
        @data['resources'] = [Puppet::Resource.new(:file, "/foo")]
 
1007
 
 
1008
        @data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}'
 
1009
 
 
1010
        @catalog.expects(:add_resource).with { |res| res.type == "File" }
 
1011
 
 
1012
        PSON.parse @pson.to_pson
 
1013
    end
 
1014
 
 
1015
    it 'should convert the edges list into edges and add each of them' do
 
1016
        one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh")
 
1017
        two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh")
 
1018
 
 
1019
        @data['edges'] = [one, two]
 
1020
 
 
1021
        @catalog.stubs(:resource).returns("eh")
 
1022
 
 
1023
        @catalog.expects(:add_edge).with { |edge| edge.event == "one" }
 
1024
        @catalog.expects(:add_edge).with { |edge| edge.event == "two" }
 
1025
 
 
1026
        PSON.parse @pson.to_pson
 
1027
    end
 
1028
 
 
1029
    it "should be able to convert relationships that do not include 'type' information" do
 
1030
        one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh")
 
1031
        one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}"
 
1032
 
 
1033
        @data['edges'] = [one]
 
1034
 
 
1035
        @catalog.stubs(:resource).returns("eh")
 
1036
 
 
1037
        @catalog.expects(:add_edge).with { |edge| edge.event == "one" }
 
1038
 
 
1039
        PSON.parse @pson.to_pson
 
1040
    end
 
1041
 
 
1042
    it "should set the source and target for each edge to the actual resource" do
 
1043
        edge = Puppet::Relationship.new("source", "target")
 
1044
 
 
1045
        @data['edges'] = [edge]
 
1046
 
 
1047
        @catalog.expects(:resource).with("source").returns("source_resource")
 
1048
        @catalog.expects(:resource).with("target").returns("target_resource")
 
1049
 
 
1050
        @catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" }
 
1051
 
 
1052
        PSON.parse @pson.to_pson
 
1053
    end
 
1054
 
 
1055
    it "should fail if the source resource cannot be found" do
 
1056
        edge = Puppet::Relationship.new("source", "target")
 
1057
 
 
1058
        @data['edges'] = [edge]
 
1059
 
 
1060
        @catalog.expects(:resource).with("source").returns(nil)
 
1061
        @catalog.stubs(:resource).with("target").returns("target_resource")
 
1062
 
 
1063
        lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError)
 
1064
    end
 
1065
 
 
1066
    it "should fail if the target resource cannot be found" do
 
1067
        edge = Puppet::Relationship.new("source", "target")
 
1068
 
 
1069
        @data['edges'] = [edge]
 
1070
 
 
1071
        @catalog.stubs(:resource).with("source").returns("source_resource")
 
1072
        @catalog.expects(:resource).with("target").returns(nil)
 
1073
 
 
1074
        lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError)
 
1075
    end
 
1076
end