chef 12.0.0.rc.0-x86-mingw32 → 12.0.0-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +1 -1
  3. data/lib/chef/api_client/registration.rb +3 -1
  4. data/lib/chef/chef_fs/data_handler/group_data_handler.rb +4 -0
  5. data/lib/chef/config.rb +46 -38
  6. data/lib/chef/event_loggers/windows_eventlog.rb +5 -6
  7. data/lib/chef/exceptions.rb +13 -1
  8. data/lib/chef/file_content_management/tempfile.rb +33 -5
  9. data/lib/chef/knife.rb +11 -3
  10. data/lib/chef/knife/bootstrap.rb +8 -7
  11. data/lib/chef/mixin/deep_merge.rb +15 -54
  12. data/lib/chef/mixin/which.rb +37 -0
  13. data/lib/chef/node.rb +14 -25
  14. data/lib/chef/node/attribute.rb +227 -41
  15. data/lib/chef/node/attribute_collections.rb +117 -3
  16. data/lib/chef/node/immutable_collections.rb +6 -6
  17. data/lib/chef/platform/provider_priority_map.rb +3 -2
  18. data/lib/chef/platform/service_helpers.rb +37 -8
  19. data/lib/chef/provider/service/aixinit.rb +1 -1
  20. data/lib/chef/provider/service/arch.rb +1 -1
  21. data/lib/chef/provider/service/debian.rb +5 -1
  22. data/lib/chef/provider/service/init.rb +4 -0
  23. data/lib/chef/provider/service/insserv.rb +5 -1
  24. data/lib/chef/provider/service/invokercd.rb +5 -1
  25. data/lib/chef/provider/service/redhat.rb +5 -1
  26. data/lib/chef/provider/service/systemd.rb +50 -32
  27. data/lib/chef/provider/service/upstart.rb +5 -2
  28. data/lib/chef/provider_resolver.rb +30 -16
  29. data/lib/chef/resource.rb +2 -1
  30. data/lib/chef/resources.rb +7 -0
  31. data/lib/chef/run_context.rb +0 -5
  32. data/lib/chef/run_list/run_list_expansion.rb +2 -2
  33. data/lib/chef/shell.rb +2 -2
  34. data/lib/chef/util/selinux.rb +2 -10
  35. data/lib/chef/version.rb +1 -1
  36. data/lib/chef/workstation_config_loader.rb +1 -1
  37. data/spec/support/shared/unit/resource/static_provider_resolution.rb +1 -6
  38. data/spec/unit/api_client/registration_spec.rb +22 -0
  39. data/spec/unit/application/knife_spec.rb +6 -2
  40. data/spec/unit/chef_fs/data_handler/group_handler_spec.rb +63 -0
  41. data/spec/unit/config_spec.rb +5 -5
  42. data/spec/unit/knife/bootstrap_spec.rb +27 -1
  43. data/spec/unit/knife_spec.rb +5 -0
  44. data/spec/unit/mixin/deep_merge_spec.rb +0 -40
  45. data/spec/unit/node/attribute_spec.rb +37 -50
  46. data/spec/unit/node_spec.rb +321 -13
  47. data/spec/unit/provider/file/content_spec.rb +23 -2
  48. data/spec/unit/provider/service/systemd_service_spec.rb +173 -158
  49. data/spec/unit/provider_resolver_spec.rb +175 -10
  50. data/spec/unit/resource/timestamped_deploy_spec.rb +8 -29
  51. data/spec/unit/runner_spec.rb +3 -1
  52. metadata +147 -221
  53. data/spec/.DS_Store +0 -0
  54. data/spec/data/.DS_Store +0 -0
  55. data/spec/data/lwrp/.DS_Store +0 -0
  56. data/spec/data/lwrp/providers/.DS_Store +0 -0
  57. data/spec/data/lwrp/resources/.DS_Store +0 -0
  58. data/spec/data/lwrp_override/.DS_Store +0 -0
  59. data/spec/data/lwrp_override/providers/.DS_Store +0 -0
  60. data/spec/data/lwrp_override/resources/.DS_Store +0 -0
@@ -44,6 +44,10 @@ describe Chef::Knife do
44
44
  @stderr = StringIO.new
45
45
  end
46
46
 
47
+ after(:each) do
48
+ Chef::Knife.reset_config_loader!
49
+ end
50
+
47
51
  describe "after loading a subcommand" do
48
52
  before do
49
53
  Chef::Knife.reset_subcommands!
@@ -268,6 +272,7 @@ describe Chef::Knife do
268
272
  @knife.config[:verbosity] = 1
269
273
  @knife.config[:config_file] = fake_config
270
274
  config_loader = double("Chef::WorkstationConfigLoader", :load => true, :no_config_found? => false, :chef_config_dir => "/etc/chef", :config_location => fake_config)
275
+ allow(config_loader).to receive(:explicit_config_file=).with(fake_config).and_return(fake_config)
271
276
  allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader)
272
277
  end
273
278
 
@@ -236,20 +236,6 @@ describe Chef::Mixin::DeepMerge, "deep_merge!" do
236
236
  @dm.deep_merge!(hash_src, hash_dst)
237
237
  hash_dst.should == {"item" => "orange"}
238
238
  end
239
-
240
- it 'should overwrite hashes with nil' do
241
- hash_src = {"item" => { "1" => "2"}, "other" => true }
242
- hash_dst = {"item" => nil }
243
- @dm.deep_merge!(hash_src, hash_dst)
244
- hash_dst.should == {"item" => nil, "other" => true }
245
- end
246
-
247
- it 'should overwrite strings with nil' do
248
- hash_src = {"item" => "to_overwrite", "other" => false }
249
- hash_dst = {"item" => nil }
250
- @dm.deep_merge!(hash_src, hash_dst)
251
- hash_dst.should == {"item" => nil, "other" => false }
252
- end
253
239
  end # deep_merge!
254
240
 
255
241
  # Chef specific
@@ -304,32 +290,6 @@ describe Chef::Mixin::DeepMerge do
304
290
 
305
291
  end
306
292
 
307
- describe "role_merge" do
308
- it "errors out if knockout merge use is detected in an array" do
309
- hash_dst = {"property" => ["2","4"]}
310
- hash_src = {"property" => ["1","!merge:4"]}
311
- lambda {@dm.role_merge(hash_dst, hash_src)}.should raise_error(Chef::Mixin::DeepMerge::InvalidSubtractiveMerge)
312
- end
313
-
314
- it "errors out if knockout merge use is detected in an array (reversed merge order)" do
315
- hash_dst = {"property" => ["1","!merge:4"]}
316
- hash_src = {"property" => ["2","4"]}
317
- lambda {@dm.role_merge(hash_dst, hash_src)}.should raise_error(Chef::Mixin::DeepMerge::InvalidSubtractiveMerge)
318
- end
319
-
320
- it "errors out if knockout merge use is detected in a string" do
321
- hash_dst = {"property" => ["2","4"]}
322
- hash_src = {"property" => "!merge"}
323
- lambda {@dm.role_merge(hash_dst, hash_src)}.should raise_error(Chef::Mixin::DeepMerge::InvalidSubtractiveMerge)
324
- end
325
-
326
- it "errors out if knockout merge use is detected in a string (reversed merge order)" do
327
- hash_dst = {"property" => "!merge"}
328
- hash_src= {"property" => ["2","4"]}
329
- lambda {@dm.role_merge(hash_dst, hash_src)}.should raise_error(Chef::Mixin::DeepMerge::InvalidSubtractiveMerge)
330
- end
331
- end
332
-
333
293
  describe "hash-only merging" do
334
294
  it "merges Hashes like normal deep merge" do
335
295
  merge_ee_hash = {"top_level_a" => {"1_deep_a" => "1-a-merge-ee", "1_deep_b" => "1-deep-b-merge-ee"}, "top_level_b" => "top-level-b-merge-ee"}
@@ -285,7 +285,7 @@ describe Chef::Node::Attribute do
285
285
  end
286
286
 
287
287
  it "prefers 'forced default' over any other default" do
288
- @attributes.default!["default"] = "force default"
288
+ @attributes.force_default["default"] = "force default"
289
289
  @attributes.role_default["default"] = "role default"
290
290
  @attributes.env_default["default"] = "environment default"
291
291
  @attributes["default"].should == "force default"
@@ -307,7 +307,7 @@ describe Chef::Node::Attribute do
307
307
  end
308
308
 
309
309
  it "prefers 'forced overrides' over role or cookbook overrides" do
310
- @attributes.override!["override"] = "force override"
310
+ @attributes.force_override["override"] = "force override"
311
311
  @attributes.env_override["override"] = "environment override"
312
312
  @attributes.role_override["override"] = "role override"
313
313
  @attributes["override"].should == "force override"
@@ -554,8 +554,7 @@ describe Chef::Node::Attribute do
554
554
 
555
555
  it "should allow the last method to set a value if it has an = sign on the end" do
556
556
  @attributes.normal.music.mastodon = [ "dream", "still", "shining" ]
557
- @attributes.reset
558
- @attributes.normal.music.mastodon.should == [ "dream", "still", "shining" ]
557
+ expect(@attributes.normal.music.mastodon).to eq([ "dream", "still", "shining" ])
559
558
  end
560
559
  end
561
560
 
@@ -939,7 +938,6 @@ describe Chef::Node::Attribute do
939
938
 
940
939
  end
941
940
 
942
-
943
941
  describe "values" do
944
942
  before do
945
943
  @attributes = Chef::Node::Attribute.new(
@@ -1092,50 +1090,6 @@ describe Chef::Node::Attribute do
1092
1090
  end
1093
1091
  end
1094
1092
 
1095
- # For expedience, this test is implementation-heavy.
1096
- describe "when a component attribute is mutated" do
1097
- [
1098
- :clear,
1099
- :shift
1100
- ].each do |mutator|
1101
- it "resets the cache when the mutator #{mutator} is called" do
1102
- @attributes.should_receive(:reset_cache)
1103
- @attributes.default.send(mutator)
1104
- end
1105
- end
1106
-
1107
- it "resets the cache when the mutator delete is called" do
1108
- @attributes.should_receive(:reset_cache)
1109
- @attributes.default.delete(:music)
1110
- end
1111
-
1112
- [
1113
- :merge!,
1114
- :update,
1115
- :replace
1116
- ].each do |mutator|
1117
- it "resets the cache when the mutator #{mutator} is called" do
1118
- # Implementation of Mash means that this could get called many times. That's okay.
1119
- @attributes.should_receive(:reset_cache).at_least(1).times
1120
- @attributes.default.send(mutator, {:foo => :bar})
1121
- end
1122
- end
1123
-
1124
- [
1125
- :delete_if,
1126
- :keep_if,
1127
- :reject!,
1128
- :select!,
1129
- ].each do |mutator|
1130
- it "resets the cache when the mutator #{mutator} is called" do
1131
- # Implementation of Mash means that this could get called many times. That's okay.
1132
- @attributes.should_receive(:reset_cache).at_least(1).times
1133
- block = lambda {|k,v| true }
1134
- @attributes.default.send(mutator, &block)
1135
- end
1136
- end
1137
- end
1138
-
1139
1093
  describe "when not mutated" do
1140
1094
 
1141
1095
  it "does not reset the cache when dup'd [CHEF-3680]" do
@@ -1173,6 +1127,40 @@ describe Chef::Node::Attribute do
1173
1127
  end
1174
1128
  end
1175
1129
 
1130
+ describe "when deep-merging between precedence levels" do
1131
+ it "correctly deep merges hashes and preserves the original contents" do
1132
+ @attributes.default = { "arglebargle" => { "foo" => "bar" } }
1133
+ @attributes.override = { "arglebargle" => { "fizz" => "buzz" } }
1134
+ expect(@attributes.merged_attributes[:arglebargle]).to eq({ "foo" => "bar", "fizz" => "buzz" })
1135
+ expect(@attributes.default[:arglebargle]).to eq({ "foo" => "bar" })
1136
+ expect(@attributes.override[:arglebargle]).to eq({ "fizz" => "buzz" })
1137
+ end
1138
+
1139
+ it "does not deep merge arrays, and preserves the original contents" do
1140
+ @attributes.default = { "arglebargle" => [ 1, 2, 3 ] }
1141
+ @attributes.override = { "arglebargle" => [ 4, 5, 6 ] }
1142
+ expect(@attributes.merged_attributes[:arglebargle]).to eq([ 4, 5, 6 ])
1143
+ expect(@attributes.default[:arglebargle]).to eq([ 1, 2, 3 ])
1144
+ expect(@attributes.override[:arglebargle]).to eq([ 4, 5, 6 ])
1145
+ end
1146
+
1147
+ it "correctly deep merges hashes and preserves the original contents when merging default and role_default" do
1148
+ @attributes.default = { "arglebargle" => { "foo" => "bar" } }
1149
+ @attributes.role_default = { "arglebargle" => { "fizz" => "buzz" } }
1150
+ expect(@attributes.merged_attributes[:arglebargle]).to eq({ "foo" => "bar", "fizz" => "buzz" })
1151
+ expect(@attributes.default[:arglebargle]).to eq({ "foo" => "bar" })
1152
+ expect(@attributes.role_default[:arglebargle]).to eq({ "fizz" => "buzz" })
1153
+ end
1154
+
1155
+ it "correctly deep merges arrays, and preserves the original contents when merging default and role_default" do
1156
+ @attributes.default = { "arglebargle" => [ 1, 2, 3 ] }
1157
+ @attributes.role_default = { "arglebargle" => [ 4, 5, 6 ] }
1158
+ expect(@attributes.merged_attributes[:arglebargle]).to eq([ 1, 2, 3, 4, 5, 6 ])
1159
+ expect(@attributes.default[:arglebargle]).to eq([ 1, 2, 3 ])
1160
+ expect(@attributes.role_default[:arglebargle]).to eq([ 4, 5, 6 ])
1161
+ end
1162
+ end
1163
+
1176
1164
  describe "when attemping to write without specifying precedence" do
1177
1165
  it "raises an error when using []=" do
1178
1166
  lambda { @attributes[:new_key] = "new value" }.should raise_error(Chef::Exceptions::ImmutableAttributeModification)
@@ -1185,4 +1173,3 @@ describe Chef::Node::Attribute do
1185
1173
  end
1186
1174
 
1187
1175
  end
1188
-
@@ -247,13 +247,6 @@ describe Chef::Node do
247
247
  node.default.fuu.bahrr.baz = "qux"
248
248
  node.fuu.bahrr.baz.should == "qux"
249
249
  end
250
-
251
- it "accesses force defaults via default!" do
252
- node.default![:foo] = "wet bar"
253
- node.default[:foo] = "bar"
254
- node[:foo].should == "wet bar"
255
- end
256
-
257
250
  end
258
251
 
259
252
  describe "override attributes" do
@@ -292,13 +285,330 @@ describe Chef::Node do
292
285
  node.override.fuu.bahrr.baz = "qux"
293
286
  node.fuu.bahrr.baz.should == "qux"
294
287
  end
288
+ end
289
+
290
+ describe "globally deleting attributes" do
291
+ context "with hash values" do
292
+ before do
293
+ node.role_default["mysql"]["server"]["port"] = 1234
294
+ node.normal["mysql"]["server"]["port"] = 2345
295
+ node.override["mysql"]["server"]["port"] = 3456
296
+ end
297
+
298
+ it "deletes all the values and returns the value with the highest precidence" do
299
+ expect( node.rm("mysql", "server", "port") ).to eql(3456)
300
+ expect( node["mysql"]["server"]["port"] ).to be_nil
301
+ expect( node["mysql"]["server"] ).to eql({})
302
+ end
303
+
304
+ it "deletes nested things correctly" do
305
+ node.default["mysql"]["client"]["client_setting"] = "foo"
306
+ expect( node.rm("mysql", "server") ).to eql( {"port" => 3456} )
307
+ expect( node["mysql"] ).to eql( { "client" => { "client_setting" => "foo" } } )
308
+ end
309
+
310
+ it "returns nil if the node attribute does not exist" do
311
+ expect( node.rm("no", "such", "thing") ).to be_nil
312
+ end
295
313
 
296
- it "sets force_overrides via override!" do
297
- node.override![:foo] = "wet bar"
298
- node.override[:foo] = "bar"
299
- node[:foo].should == "wet bar"
314
+ it "can delete the entire tree" do
315
+ expect( node.rm("mysql") ).to eql({"server"=>{"port"=>3456}})
316
+ end
300
317
  end
301
318
 
319
+ context "when trying to delete through a thing that isn't an array-like or hash-like object" do
320
+ before do
321
+ node.default["mysql"] = true
322
+ end
323
+
324
+ it "returns nil when you're two levels deeper" do
325
+ expect( node.rm("mysql", "server", "port") ).to eql(nil)
326
+ end
327
+
328
+ it "returns nil when you're one level deeper" do
329
+ expect( node.rm("mysql", "server") ).to eql(nil)
330
+ end
331
+
332
+ it "correctly deletes at the top level" do
333
+ expect( node.rm("mysql") ).to eql(true)
334
+ end
335
+ end
336
+
337
+ context "with array indexes" do
338
+ before do
339
+ node.role_default["mysql"]["server"][0]["port"] = 1234
340
+ node.normal["mysql"]["server"][0]["port"] = 2345
341
+ node.override["mysql"]["server"][0]["port"] = 3456
342
+ node.override["mysql"]["server"][1]["port"] = 3456
343
+ end
344
+
345
+ it "deletes the array element" do
346
+ expect( node.rm("mysql", "server", 0, "port") ).to eql(3456)
347
+ expect( node["mysql"]["server"][0]["port"] ).to be_nil
348
+ expect( node["mysql"]["server"][1]["port"] ).to eql(3456)
349
+ end
350
+ end
351
+
352
+ context "with real arrays" do
353
+ before do
354
+ node.role_default["mysql"]["server"] = [ {
355
+ "port" => 1234,
356
+ } ]
357
+ node.normal["mysql"]["server"] = [ {
358
+ "port" => 2345,
359
+ } ]
360
+ node.override["mysql"]["server"] = [ {
361
+ "port" => 3456,
362
+ } ]
363
+ end
364
+
365
+ it "deletes the array element" do
366
+ expect( node.rm("mysql", "server", 0, "port") ).to eql(3456)
367
+ expect( node["mysql"]["server"][0]["port"] ).to be_nil
368
+ end
369
+
370
+ it "does not have a horrible error message when mistaking arrays for hashes" do
371
+ expect { node.rm("mysql", "server", "port") }.to raise_error(TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)")
372
+ end
373
+ end
374
+ end
375
+
376
+ describe "granular deleting attributes" do
377
+ context "when only defaults exist" do
378
+ before do
379
+ node.role_default["mysql"]["server"]["port"] = 1234
380
+ node.default["mysql"]["server"]["port"] = 2345
381
+ node.force_default["mysql"]["server"]["port"] = 3456
382
+ end
383
+
384
+ it "returns the deleted values" do
385
+ expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
386
+ end
387
+
388
+ it "returns nil for the combined attribues" do
389
+ expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
390
+ expect( node["mysql"]["server"]["port"] ).to eql(nil)
391
+ end
392
+
393
+ it "returns an empty hash for the default attrs" do
394
+ expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
395
+ # this auto-vivifies, should it?
396
+ expect( node.default_attrs["mysql"]["server"]["port"] ).to eql({})
397
+ end
398
+
399
+ it "returns an empty hash after the last key is deleted" do
400
+ expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
401
+ expect( node["mysql"]["server"] ).to eql({})
402
+ end
403
+ end
404
+
405
+ context "when trying to delete through a thing that isn't an array-like or hash-like object" do
406
+ before do
407
+ node.default["mysql"] = true
408
+ end
409
+
410
+ it "returns nil when you're two levels deeper" do
411
+ expect( node.rm_default("mysql", "server", "port") ).to eql(nil)
412
+ end
413
+
414
+ it "returns nil when you're one level deeper" do
415
+ expect( node.rm_default("mysql", "server") ).to eql(nil)
416
+ end
417
+
418
+ it "correctly deletes at the top level" do
419
+ expect( node.rm_default("mysql") ).to eql(true)
420
+ end
421
+ end
422
+
423
+ context "when a higher precedence exists" do
424
+ before do
425
+ node.role_default["mysql"]["server"]["port"] = 1234
426
+ node.default["mysql"]["server"]["port"] = 2345
427
+ node.force_default["mysql"]["server"]["port"] = 3456
428
+
429
+ node.override["mysql"]["server"]["port"] = 9999
430
+ end
431
+
432
+ it "returns the deleted values" do
433
+ expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
434
+ end
435
+
436
+ it "returns the higher precedence values after the delete" do
437
+ expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
438
+ expect( node["mysql"]["server"]["port"] ).to eql(9999)
439
+ end
440
+
441
+ it "returns an empty has for the default attrs" do
442
+ expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
443
+ # this auto-vivifies, should it?
444
+ expect( node.default_attrs["mysql"]["server"]["port"] ).to eql({})
445
+ end
446
+ end
447
+
448
+ context "when a lower precedence exists" do
449
+ before do
450
+ node.default["mysql"]["server"]["port"] = 2345
451
+ node.override["mysql"]["server"]["port"] = 9999
452
+ node.role_override["mysql"]["server"]["port"] = 9876
453
+ node.force_override["mysql"]["server"]["port"] = 6669
454
+ end
455
+
456
+ it "returns the deleted values" do
457
+ expect( node.rm_override("mysql", "server", "port") ).to eql(6669)
458
+ end
459
+
460
+ it "returns the lower precedence levels after the delete" do
461
+ expect( node.rm_override("mysql", "server", "port") ).to eql(6669)
462
+ expect( node["mysql"]["server"]["port"] ).to eql(2345)
463
+ end
464
+
465
+ it "returns an empty has for the override attrs" do
466
+ expect( node.rm_override("mysql", "server", "port") ).to eql(6669)
467
+ # this auto-vivifies, should it?
468
+ expect( node.override_attrs["mysql"]["server"]["port"] ).to eql({})
469
+ end
470
+ end
471
+
472
+ it "rm_default returns nil on deleting non-existent values" do
473
+ expect( node.rm_default("no", "such", "thing") ).to be_nil
474
+ end
475
+
476
+ it "rm_normal returns nil on deleting non-existent values" do
477
+ expect( node.rm_normal("no", "such", "thing") ).to be_nil
478
+ end
479
+
480
+ it "rm_override returns nil on deleting non-existent values" do
481
+ expect( node.rm_override("no", "such", "thing") ).to be_nil
482
+ end
483
+ end
484
+
485
+ describe "granular replacing attributes" do
486
+ it "removes everything at the level of the last key" do
487
+ node.default["mysql"]["server"]["port"] = 2345
488
+
489
+ node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" }
490
+
491
+ expect( node["mysql"]["server"] ).to eql({ "data_dir" => "/my_raid_volume/lib/mysql" })
492
+ end
493
+
494
+ it "replaces a value at the cookbook sub-level of the atributes only" do
495
+ node.default["mysql"]["server"]["port"] = 2345
496
+ node.default["mysql"]["server"]["service_name"] = "fancypants-sql"
497
+ node.role_default["mysql"]["server"]["port"] = 1234
498
+ node.force_default["mysql"]["server"]["port"] = 3456
499
+
500
+ node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" }
501
+
502
+ expect( node["mysql"]["server"]["port"] ).to eql(3456)
503
+ expect( node["mysql"]["server"]["service_name"] ).to be_nil
504
+ expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql")
505
+ expect( node["mysql"]["server"] ).to eql({ "port" => 3456, "data_dir" => "/my_raid_volume/lib/mysql" })
506
+ end
507
+
508
+ it "higher precedence values aren't removed" do
509
+ node.role_default["mysql"]["server"]["port"] = 1234
510
+ node.default["mysql"]["server"]["port"] = 2345
511
+ node.force_default["mysql"]["server"]["port"] = 3456
512
+ node.override["mysql"]["server"]["service_name"] = "fancypants-sql"
513
+
514
+ node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" }
515
+
516
+ expect( node["mysql"]["server"]["port"] ).to eql(3456)
517
+ expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql")
518
+ expect( node["mysql"]["server"] ).to eql({ "service_name" => "fancypants-sql", "port" => 3456, "data_dir" => "/my_raid_volume/lib/mysql" })
519
+ end
520
+ end
521
+
522
+ describe "granular force replacing attributes" do
523
+ it "removes everything at the level of the last key" do
524
+ node.force_default["mysql"]["server"]["port"] = 2345
525
+
526
+ node.force_default!["mysql"]["server"] = {
527
+ "data_dir" => "/my_raid_volume/lib/mysql",
528
+ }
529
+
530
+ expect( node["mysql"]["server"] ).to eql({
531
+ "data_dir" => "/my_raid_volume/lib/mysql",
532
+ })
533
+ end
534
+
535
+ it "removes all values from the precedence level when setting" do
536
+ node.role_default["mysql"]["server"]["port"] = 1234
537
+ node.default["mysql"]["server"]["port"] = 2345
538
+ node.force_default["mysql"]["server"]["port"] = 3456
539
+
540
+ node.force_default!["mysql"]["server"] = {
541
+ "data_dir" => "/my_raid_volume/lib/mysql",
542
+ }
543
+
544
+ expect( node["mysql"]["server"]["port"] ).to be_nil
545
+ expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql")
546
+ expect( node["mysql"]["server"] ).to eql({
547
+ "data_dir" => "/my_raid_volume/lib/mysql",
548
+ })
549
+ end
550
+
551
+ it "higher precedence levels are not removed" do
552
+ node.role_default["mysql"]["server"]["port"] = 1234
553
+ node.default["mysql"]["server"]["port"] = 2345
554
+ node.force_default["mysql"]["server"]["port"] = 3456
555
+ node.override["mysql"]["server"]["service_name"] = "fancypants-sql"
556
+
557
+ node.force_default!["mysql"]["server"] = {
558
+ "data_dir" => "/my_raid_volume/lib/mysql",
559
+ }
560
+
561
+ expect( node["mysql"]["server"]["port"] ).to be_nil
562
+ expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql")
563
+ expect( node["mysql"]["server"] ).to eql({
564
+ "service_name" => "fancypants-sql",
565
+ "data_dir" => "/my_raid_volume/lib/mysql",
566
+ })
567
+ end
568
+
569
+ it "will autovivify" do
570
+ node.force_default!["mysql"]["server"] = {
571
+ "data_dir" => "/my_raid_volume/lib/mysql",
572
+ }
573
+ expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql")
574
+ end
575
+
576
+ it "lower precedence levels aren't removed" do
577
+ node.role_override["mysql"]["server"]["port"] = 1234
578
+ node.override["mysql"]["server"]["port"] = 2345
579
+ node.force_override["mysql"]["server"]["port"] = 3456
580
+ node.default["mysql"]["server"]["service_name"] = "fancypants-sql"
581
+
582
+ node.force_override!["mysql"]["server"] = {
583
+ "data_dir" => "/my_raid_volume/lib/mysql",
584
+ }
585
+
586
+ expect( node["mysql"]["server"]["port"] ).to be_nil
587
+ expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql")
588
+ expect( node["mysql"]["server"] ).to eql({
589
+ "service_name" => "fancypants-sql",
590
+ "data_dir" => "/my_raid_volume/lib/mysql",
591
+ })
592
+ end
593
+
594
+ it "when overwriting a non-hash/array" do
595
+ node.override["mysql"] = false
596
+ node.force_override["mysql"] = true
597
+ node.force_override!["mysql"]["server"] = {
598
+ "data_dir" => "/my_raid_volume/lib/mysql",
599
+ }
600
+ expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql")
601
+ end
602
+
603
+ it "when overwriting an array with a hash" do
604
+ node.force_override["mysql"][0] = true
605
+ node.force_override!["mysql"]["server"] = {
606
+ "data_dir" => "/my_raid_volume/lib/mysql",
607
+ }
608
+ expect( node["mysql"]["server"] ).to eql({
609
+ "data_dir" => "/my_raid_volume/lib/mysql",
610
+ })
611
+ end
302
612
  end
303
613
 
304
614
  it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do
@@ -536,7 +846,6 @@ describe Chef::Node do
536
846
  @expansion.default_attrs.replace({:default => "from role", :d_role => "role only"})
537
847
  @expansion.override_attrs.replace({:override => "from role", :o_role => "role only"})
538
848
 
539
-
540
849
  @environment = Chef::Environment.new
541
850
  @environment.default_attributes = {:default => "from env", :d_env => "env only" }
542
851
  @environment.override_attributes = {:override => "from env", :o_env => "env only"}
@@ -753,7 +1062,6 @@ describe Chef::Node do
753
1062
  node_for_json["default"]["env default"].should == "env default"
754
1063
  end
755
1064
 
756
-
757
1065
  it "should deserialize itself from json", :json => true do
758
1066
  node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
759
1067
  json = Chef::JSONCompat.to_json(node)