chef 12.11.18 → 12.12.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -4
  3. data/Rakefile +3 -2
  4. data/VERSION +1 -1
  5. data/acceptance/Gemfile.lock +22 -23
  6. data/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb +2 -41
  7. data/lib/chef/application/solo.rb +7 -0
  8. data/lib/chef/chef_fs/file_system/multiplexed_dir.rb +1 -1
  9. data/lib/chef/data_collector.rb +79 -43
  10. data/lib/chef/data_collector/messages.rb +4 -33
  11. data/lib/chef/data_collector/messages/helpers.rb +2 -2
  12. data/lib/chef/data_collector/resource_report.rb +21 -11
  13. data/lib/chef/decorator/unchain.rb +43 -0
  14. data/lib/chef/exceptions.rb +5 -0
  15. data/lib/chef/http.rb +5 -5
  16. data/lib/chef/knife/cookbook_create.rb +4 -0
  17. data/lib/chef/knife/cookbook_site_download.rb +8 -1
  18. data/lib/chef/knife/cookbook_site_install.rb +8 -0
  19. data/lib/chef/knife/cookbook_site_list.rb +8 -1
  20. data/lib/chef/knife/cookbook_site_search.rb +8 -1
  21. data/lib/chef/knife/cookbook_site_share.rb +8 -1
  22. data/lib/chef/knife/cookbook_site_show.rb +14 -3
  23. data/lib/chef/knife/cookbook_site_unshare.rb +8 -1
  24. data/lib/chef/knife/core/bootstrap_context.rb +1 -1
  25. data/lib/chef/knife/supermarket_download.rb +33 -0
  26. data/lib/chef/knife/supermarket_install.rb +33 -0
  27. data/lib/chef/knife/supermarket_list.rb +33 -0
  28. data/lib/chef/knife/supermarket_search.rb +33 -0
  29. data/lib/chef/knife/supermarket_share.rb +33 -0
  30. data/lib/chef/knife/supermarket_show.rb +33 -0
  31. data/lib/chef/knife/supermarket_unshare.rb +33 -0
  32. data/lib/chef/node.rb +13 -32
  33. data/lib/chef/node/attribute.rb +123 -70
  34. data/lib/chef/node/attribute_collections.rb +9 -130
  35. data/lib/chef/node/common_api.rb +124 -0
  36. data/lib/chef/node/immutable_collections.rb +27 -2
  37. data/lib/chef/property.rb +6 -2
  38. data/lib/chef/provider.rb +4 -5
  39. data/lib/chef/provider/batch.rb +1 -1
  40. data/lib/chef/provider/directory.rb +3 -1
  41. data/lib/chef/provider/package/openbsd.rb +1 -1
  42. data/lib/chef/provider/package/rubygems.rb +9 -3
  43. data/lib/chef/provider/package/windows/exe.rb +2 -5
  44. data/lib/chef/provider/powershell_script.rb +1 -1
  45. data/lib/chef/provider/remote_directory.rb +2 -0
  46. data/lib/chef/resource.rb +22 -17
  47. data/lib/chef/resource_builder.rb +9 -4
  48. data/lib/chef/shell.rb +1 -1
  49. data/lib/chef/version.rb +1 -1
  50. data/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb +2 -4
  51. data/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb +2 -3
  52. data/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb +2 -2
  53. data/spec/data/run_context/cookbooks/dependency1/attributes/default.rb +2 -2
  54. data/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb +2 -3
  55. data/spec/data/run_context/cookbooks/dependency2/attributes/default.rb +2 -3
  56. data/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb +2 -3
  57. data/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb +2 -3
  58. data/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb +2 -3
  59. data/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg +0 -0
  60. data/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg +0 -0
  61. data/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg +0 -0
  62. data/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg +0 -0
  63. data/spec/functional/resource/dsc_script_spec.rb +1 -0
  64. data/spec/functional/resource/package_spec.rb +1 -1
  65. data/spec/functional/resource/template_spec.rb +3 -3
  66. data/spec/functional/shell_spec.rb +1 -1
  67. data/spec/integration/knife/client_bulk_delete_spec.rb +130 -0
  68. data/spec/integration/knife/client_create_spec.rb +69 -0
  69. data/spec/integration/knife/client_delete_spec.rb +63 -0
  70. data/spec/integration/knife/client_key_create_spec.rb +65 -0
  71. data/spec/integration/knife/client_key_delete_spec.rb +42 -0
  72. data/spec/integration/knife/client_key_list_spec.rb +60 -0
  73. data/spec/integration/knife/client_key_show_spec.rb +44 -0
  74. data/spec/integration/knife/client_list_spec.rb +48 -0
  75. data/spec/integration/knife/client_show_spec.rb +36 -0
  76. data/spec/integration/knife/cookbook_bulk_delete_spec.rb +64 -0
  77. data/spec/integration/knife/cookbook_download_spec.rb +95 -0
  78. data/spec/integration/knife/cookbook_list_spec.rb +54 -0
  79. data/spec/integration/knife/cookbook_show_spec.rb +159 -0
  80. data/spec/integration/knife/cookbook_upload_spec.rb +90 -0
  81. data/spec/integration/knife/data_bag_create_spec.rb +58 -0
  82. data/spec/integration/knife/data_bag_delete_spec.rb +58 -0
  83. data/spec/integration/knife/data_bag_from_file_spec.rb +115 -0
  84. data/spec/integration/knife/data_bag_list_spec.rb +43 -0
  85. data/spec/integration/knife/data_bag_show_spec.rb +53 -0
  86. data/spec/integration/knife/environment_compare_spec.rb +74 -0
  87. data/spec/integration/knife/environment_create_spec.rb +40 -0
  88. data/spec/integration/knife/environment_delete_spec.rb +36 -0
  89. data/spec/integration/knife/environment_from_file_spec.rb +115 -0
  90. data/spec/integration/knife/environment_list_spec.rb +41 -0
  91. data/spec/integration/knife/environment_show_spec.rb +56 -0
  92. data/spec/integration/knife/node_bulk_delete_spec.rb +51 -0
  93. data/spec/integration/knife/node_create_spec.rb +46 -0
  94. data/spec/integration/knife/node_delete_spec.rb +47 -0
  95. data/spec/integration/knife/node_environment_set_spec.rb +42 -0
  96. data/spec/integration/knife/node_from_file_spec.rb +58 -0
  97. data/spec/integration/knife/node_list_spec.rb +44 -0
  98. data/spec/integration/knife/node_run_list_add_spec.rb +53 -0
  99. data/spec/integration/knife/node_run_list_remove_spec.rb +35 -0
  100. data/spec/integration/knife/node_run_list_set_spec.rb +40 -0
  101. data/spec/integration/knife/node_show_spec.rb +35 -0
  102. data/spec/integration/knife/role_bulk_delete_spec.rb +51 -0
  103. data/spec/integration/knife/role_create_spec.rb +40 -0
  104. data/spec/integration/knife/role_delete_spec.rb +47 -0
  105. data/spec/integration/knife/role_from_file_spec.rb +95 -0
  106. data/spec/integration/knife/role_list_spec.rb +44 -0
  107. data/spec/integration/knife/role_show_spec.rb +50 -0
  108. data/spec/support/shared/integration/knife_support.rb +10 -3
  109. data/spec/unit/application/solo_spec.rb +7 -0
  110. data/spec/unit/cookbook_version_spec.rb +4 -4
  111. data/spec/unit/data_collector/messages/helpers_spec.rb +3 -7
  112. data/spec/unit/data_collector/messages_spec.rb +28 -45
  113. data/spec/unit/data_collector_spec.rb +40 -47
  114. data/spec/unit/knife/cookbook_create_spec.rb +1 -0
  115. data/spec/unit/knife/cookbook_site_download_spec.rb +1 -0
  116. data/spec/unit/knife/node_environment_set_spec.rb +0 -24
  117. data/spec/unit/knife/node_run_list_set_spec.rb +0 -25
  118. data/spec/unit/node/attribute_spec.rb +7 -9
  119. data/spec/unit/node/immutable_collections_spec.rb +4 -0
  120. data/spec/unit/node/vivid_mash_spec.rb +344 -0
  121. data/spec/unit/node_spec.rb +115 -26
  122. data/spec/unit/provider/directory_spec.rb +11 -1
  123. data/spec/unit/provider/package/windows/exe_spec.rb +14 -9
  124. data/spec/unit/provider/powershell_script_spec.rb +4 -4
  125. data/spec/unit/provider/remote_directory_spec.rb +15 -0
  126. data/spec/unit/recipe_spec.rb +31 -6
  127. data/spec/unit/run_context_spec.rb +2 -2
  128. data/spec/unit/shell/shell_session_spec.rb +1 -1
  129. data/tasks/dependencies.rb +0 -2
  130. metadata +55 -786
  131. data/acceptance/.bundle/config +0 -2
  132. data/acceptance/basics/.kitchen/logs/chef-current-install-ubuntu-1404.log +0 -2
  133. data/acceptance/basics/.kitchen/logs/kitchen.log +0 -3
  134. data/acceptance/fips/.kitchen/logs/fips-integration-centos-6.log +0 -3
  135. data/acceptance/fips/.kitchen/logs/fips-integration-windows-2012r2.log +0 -3
  136. data/acceptance/fips/.kitchen/logs/fips-unit-functional-centos-6.log +0 -3
  137. data/acceptance/fips/.kitchen/logs/fips-unit-functional-windows-2012r2.log +0 -3
  138. data/acceptance/fips/.kitchen/logs/kitchen.log +0 -6
  139. data/acceptance/trivial/.kitchen/logs/chef-current-install-windows-2012r2.log +0 -2
  140. data/acceptance/trivial/.kitchen/logs/kitchen.log +0 -3
  141. data/acceptance/windows-service/.kitchen/logs/chef-windows-service-windows-2012r2.log +0 -2
  142. data/acceptance/windows-service/.kitchen/logs/kitchen.log +0 -3
@@ -20,6 +20,7 @@
20
20
 
21
21
  require "spec_helper"
22
22
  require "chef/data_collector"
23
+ require "chef/resource_builder"
23
24
 
24
25
  describe Chef::DataCollector do
25
26
  describe ".register_reporter?" do
@@ -193,10 +194,32 @@ describe Chef::DataCollector::Reporter do
193
194
  end
194
195
  end
195
196
 
197
+ describe '#converge_start' do
198
+ it "stashes the run_context for later use" do
199
+ reporter.converge_start("test_context")
200
+ expect(reporter.run_context).to eq("test_context")
201
+ end
202
+ end
203
+
204
+ describe '#converge_complete' do
205
+ it "detects and processes any unprocessed resources" do
206
+ expect(reporter).to receive(:detect_unprocessed_resources)
207
+ reporter.converge_complete
208
+ end
209
+ end
210
+
211
+ describe '#converge_failed' do
212
+ it "detects and processes any unprocessed resources" do
213
+ expect(reporter).to receive(:detect_unprocessed_resources)
214
+ reporter.converge_failed("exception")
215
+ end
216
+ end
217
+
196
218
  describe '#resource_current_state_loaded' do
197
219
  let(:new_resource) { double("new_resource") }
198
220
  let(:action) { double("action") }
199
221
  let(:current_resource) { double("current_resource") }
222
+ let(:resource_report) { double("resource_report") }
200
223
 
201
224
  context "when resource is a nested resource" do
202
225
  it "does not update the resource report" do
@@ -207,14 +230,12 @@ describe Chef::DataCollector::Reporter do
207
230
  end
208
231
 
209
232
  context "when resource is not a nested resource" do
210
- it "updates the resource report" do
233
+ it "creates the resource report and stores it as the current one" do
211
234
  allow(reporter).to receive(:nested_resource?).and_return(false)
212
- expect(Chef::DataCollector::ResourceReport).to receive(:new).with(
213
- new_resource,
214
- action,
215
- current_resource)
216
- .and_return("resource_report")
217
- expect(reporter).to receive(:update_current_resource_report).with("resource_report")
235
+ expect(reporter).to receive(:create_resource_report)
236
+ .with(new_resource, action, current_resource)
237
+ .and_return(resource_report)
238
+ expect(reporter).to receive(:update_current_resource_report).with(resource_report)
218
239
  reporter.resource_current_state_loaded(new_resource, action, current_resource)
219
240
  end
220
241
  end
@@ -226,17 +247,11 @@ describe Chef::DataCollector::Reporter do
226
247
  let(:resource_report) { double("resource_report") }
227
248
 
228
249
  before do
229
- allow(reporter).to receive(:increment_resource_count)
230
250
  allow(reporter).to receive(:nested_resource?)
231
251
  allow(reporter).to receive(:current_resource_report).and_return(resource_report)
232
252
  allow(resource_report).to receive(:up_to_date)
233
253
  end
234
254
 
235
- it "increments the resource count" do
236
- expect(reporter).to receive(:increment_resource_count)
237
- reporter.resource_up_to_date(new_resource, action)
238
- end
239
-
240
255
  context "when the resource is a nested resource" do
241
256
  it "does not mark the resource report as up-to-date" do
242
257
  allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
@@ -261,17 +276,11 @@ describe Chef::DataCollector::Reporter do
261
276
  let(:resource_report) { double("resource_report") }
262
277
 
263
278
  before do
264
- allow(reporter).to receive(:increment_resource_count)
265
279
  allow(reporter).to receive(:nested_resource?)
266
- allow(reporter).to receive(:current_resource_report).and_return(resource_report)
280
+ allow(reporter).to receive(:create_resource_report).and_return(resource_report)
267
281
  allow(resource_report).to receive(:skipped)
268
282
  end
269
283
 
270
- it "increments the resource count" do
271
- expect(reporter).to receive(:increment_resource_count)
272
- reporter.resource_skipped(new_resource, action, conditional)
273
- end
274
-
275
284
  context "when the resource is a nested resource" do
276
285
  it "does not mark the resource report as skipped" do
277
286
  allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
@@ -281,13 +290,12 @@ describe Chef::DataCollector::Reporter do
281
290
  end
282
291
 
283
292
  context "when the resource is not a nested resource" do
284
- it "updates the resource report" do
293
+ it "creates the resource report and stores it as the current one" do
285
294
  allow(reporter).to receive(:nested_resource?).and_return(false)
286
- expect(Chef::DataCollector::ResourceReport).to receive(:new).with(
287
- new_resource,
288
- action)
289
- .and_return("resource_report")
290
- expect(reporter).to receive(:update_current_resource_report).with("resource_report")
295
+ expect(reporter).to receive(:create_resource_report)
296
+ .with(new_resource, action)
297
+ .and_return(resource_report)
298
+ expect(reporter).to receive(:update_current_resource_report).with(resource_report)
291
299
  reporter.resource_skipped(new_resource, action, conditional)
292
300
  end
293
301
 
@@ -307,11 +315,6 @@ describe Chef::DataCollector::Reporter do
307
315
  allow(resource_report).to receive(:updated)
308
316
  end
309
317
 
310
- it "increments the resource count" do
311
- expect(reporter).to receive(:increment_resource_count)
312
- reporter.resource_updated("new_resource", "action")
313
- end
314
-
315
318
  it "marks the resource report as updated" do
316
319
  expect(resource_report).to receive(:updated)
317
320
  reporter.resource_updated("new_resource", "action")
@@ -326,7 +329,6 @@ describe Chef::DataCollector::Reporter do
326
329
  let(:resource_report) { double("resource_report") }
327
330
 
328
331
  before do
329
- allow(reporter).to receive(:increment_resource_count)
330
332
  allow(reporter).to receive(:update_error_description)
331
333
  allow(reporter).to receive(:current_resource_report).and_return(resource_report)
332
334
  allow(resource_report).to receive(:failed)
@@ -334,11 +336,6 @@ describe Chef::DataCollector::Reporter do
334
336
  allow(error_mapper).to receive(:for_json)
335
337
  end
336
338
 
337
- it "increments the resource count" do
338
- expect(reporter).to receive(:increment_resource_count)
339
- reporter.resource_failed(new_resource, action, exception)
340
- end
341
-
342
339
  it "updates the error description" do
343
340
  expect(Chef::Formatters::ErrorMapper).to receive(:resource_failed).with(
344
341
  new_resource,
@@ -372,15 +369,16 @@ describe Chef::DataCollector::Reporter do
372
369
  let(:resource_report) { double("resource_report") }
373
370
 
374
371
  before do
375
- allow(reporter).to receive(:add_updated_resource)
376
372
  allow(reporter).to receive(:update_current_resource_report)
373
+ allow(reporter).to receive(:add_resource_report)
374
+ allow(reporter).to receive(:current_resource_report)
377
375
  allow(resource_report).to receive(:finish)
378
376
  end
379
377
 
380
378
  context "when there is no current resource report" do
381
- it "does not add the updated resource" do
379
+ it "does not touch the current resource report" do
382
380
  allow(reporter).to receive(:current_resource_report).and_return(nil)
383
- expect(reporter).not_to receive(:add_updated_resource)
381
+ expect(reporter).not_to receive(:update_current_resource_report)
384
382
  reporter.resource_completed(new_resource)
385
383
  end
386
384
  end
@@ -391,9 +389,9 @@ describe Chef::DataCollector::Reporter do
391
389
  end
392
390
 
393
391
  context "when the resource is a nested resource" do
394
- it "does not add the updated resource" do
392
+ it "does not mark the resource as finished" do
395
393
  allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
396
- expect(reporter).not_to receive(:add_updated_resource)
394
+ expect(resource_report).not_to receive(:finish)
397
395
  reporter.resource_completed(new_resource)
398
396
  end
399
397
  end
@@ -408,11 +406,6 @@ describe Chef::DataCollector::Reporter do
408
406
  reporter.resource_completed(new_resource)
409
407
  end
410
408
 
411
- it "adds the resource to the updated resource list" do
412
- expect(reporter).to receive(:add_updated_resource).with(resource_report)
413
- reporter.resource_completed(new_resource)
414
- end
415
-
416
409
  it "nils out the current resource report" do
417
410
  expect(reporter).to receive(:update_current_resource_report).with(nil)
418
411
  reporter.resource_completed(new_resource)
@@ -22,6 +22,7 @@ require "tmpdir"
22
22
  describe Chef::Knife::CookbookCreate do
23
23
  before(:each) do
24
24
  Chef::Config[:node_name] = "webmonkey.example.com"
25
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
25
26
  @knife = Chef::Knife::CookbookCreate.new
26
27
  @knife.config = {}
27
28
  @knife.name_args = ["foobar"]
@@ -38,6 +38,7 @@ describe Chef::Knife::CookbookSiteDownload do
38
38
  expect(@noauth_rest).to receive(:get).
39
39
  with("#{@cookbook_api_url}/apache2").
40
40
  and_return(@current_data)
41
+ @knife.configure_chef
41
42
  end
42
43
 
43
44
  context "when the cookbook is deprecated and not forced" do
@@ -52,29 +52,5 @@ describe Chef::Knife::NodeEnvironmentSet do
52
52
  @knife.run
53
53
  end
54
54
 
55
- describe "with no environment" do
56
- # Set up outputs for inspection later
57
- before(:each) do
58
- @stdout = StringIO.new
59
- @stderr = StringIO.new
60
-
61
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
62
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
63
- end
64
-
65
- it "should exit" do
66
- @knife.name_args = [ "adam" ]
67
- expect { @knife.run }.to raise_error SystemExit
68
- end
69
-
70
- it "should show the user the usage and an error" do
71
- @knife.name_args = [ "adam" ]
72
-
73
- begin ; @knife.run ; rescue SystemExit ; end
74
-
75
- expect(@stdout.string).to eq "USAGE: knife node environment set NODE ENVIRONMENT\n"
76
- expect(@stderr.string).to eq "FATAL: You must specify a node name and an environment.\n"
77
- end
78
- end
79
55
  end
80
56
  end
@@ -111,30 +111,5 @@ describe Chef::Knife::NodeRunListSet do
111
111
  end
112
112
  end
113
113
 
114
- describe "with no role or recipe" do
115
- # Set up outputs for inspection later
116
- before(:each) do
117
- @stdout = StringIO.new
118
- @stderr = StringIO.new
119
-
120
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
121
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
122
- end
123
-
124
- it "should exit" do
125
- @knife.name_args = [ "adam" ]
126
- expect { @knife.run }.to raise_error SystemExit
127
- end
128
-
129
- it "should show the user" do
130
- @knife.name_args = [ "adam" ]
131
-
132
- begin ; @knife.run ; rescue SystemExit ; end
133
-
134
- expect(@stdout.string).to eq "USAGE: knife node run_list set NODE ENTRIES (options)\n"
135
- expect(@stderr.string).to eq "FATAL: You must supply both a node name and a run list.\n"
136
- end
137
- end
138
-
139
114
  end
140
115
  end
@@ -218,7 +218,7 @@ describe Chef::Node::Attribute do
218
218
  end
219
219
 
220
220
  it "gives the value at each level of precedence for a path spec" do
221
- expected = [["set_unless_enabled?", false],
221
+ expected = [
222
222
  %w{default default},
223
223
  %w{env_default env_default},
224
224
  %w{role_default role_default},
@@ -417,12 +417,6 @@ describe Chef::Node::Attribute do
417
417
  expect(@attributes.normal["foo"]["bar"]).to eq(:baz)
418
418
  end
419
419
 
420
- it "should optionally skip setting the value if one already exists" do
421
- @attributes.set_unless_value_present = true
422
- @attributes.normal["hostname"] = "bar"
423
- expect(@attributes["hostname"]).to eq("latte")
424
- end
425
-
426
420
  it "does not support ||= when setting" do
427
421
  # This is a limitation of auto-vivification.
428
422
  # Users who need this behavior can use set_unless and friends
@@ -493,6 +487,7 @@ describe Chef::Node::Attribute do
493
487
  end
494
488
 
495
489
  it "should return true if an attribute exists but is set to nil using dot notation" do
490
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
496
491
  expect(@attributes.music.deeper.has_key?("gates_of_ishtar")).to eq(true)
497
492
  end
498
493
 
@@ -533,10 +528,12 @@ describe Chef::Node::Attribute do
533
528
 
534
529
  describe "method_missing" do
535
530
  it "should behave like a [] lookup" do
531
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
536
532
  expect(@attributes.music.mastodon).to eq("rocks")
537
533
  end
538
534
 
539
535
  it "should allow the last method to set a value if it has an = sign on the end" do
536
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
540
537
  @attributes.normal.music.mastodon = %w{dream still shining}
541
538
  expect(@attributes.normal.music.mastodon).to eq(%w{dream still shining})
542
539
  end
@@ -577,7 +574,7 @@ describe Chef::Node::Attribute do
577
574
 
578
575
  it "should yield lower if we go deeper" do
579
576
  collect = Array.new
580
- @attributes.one.keys.each do |k|
577
+ @attributes["one"].keys.each do |k|
581
578
  collect << k
582
579
  end
583
580
  expect(collect.include?("two")).to eq(true)
@@ -587,7 +584,7 @@ describe Chef::Node::Attribute do
587
584
  end
588
585
 
589
586
  it "should not raise an exception if one of the hashes has a nil value on a deep lookup" do
590
- expect { @attributes.place.keys { |k| } }.not_to raise_error
587
+ expect { @attributes["place"].keys { |k| } }.not_to raise_error
591
588
  end
592
589
  end
593
590
 
@@ -1171,6 +1168,7 @@ describe Chef::Node::Attribute do
1171
1168
  end
1172
1169
 
1173
1170
  it "raises an error when using `attr=value`" do
1171
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
1174
1172
  expect { @attributes.new_key = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
1175
1173
  end
1176
1174
 
@@ -95,6 +95,10 @@ describe Chef::Node::ImmutableMash do
95
95
  :replace,
96
96
  :select!,
97
97
  :shift,
98
+ :write,
99
+ :write!,
100
+ :unlink,
101
+ :unlink!,
98
102
  ].each do |mutator|
99
103
  it "doesn't allow mutation via `#{mutator}'" do
100
104
  expect { @immutable_mash.send(mutator) }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
@@ -0,0 +1,344 @@
1
+ #
2
+ # Copyright:: Copyright 2016, Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "spec_helper"
19
+ require "chef/node/attribute_collections"
20
+
21
+ describe Chef::Node::VividMash do
22
+ class Root
23
+ attr_accessor :top_level_breadcrumb
24
+ end
25
+
26
+ let(:root) { Root.new }
27
+
28
+ let(:vivid) do
29
+ expect(root).to receive(:reset_cache).at_least(:once).with(nil)
30
+ Chef::Node::VividMash.new(root,
31
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
32
+ )
33
+ end
34
+
35
+ context "#read" do
36
+ before do
37
+ # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
38
+ vivid
39
+ expect(root).not_to receive(:reset_cache)
40
+ end
41
+
42
+ it "reads hashes deeply" do
43
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
44
+ expect(vivid.read("one", "two", "three")).to eql("four")
45
+ end
46
+
47
+ it "does not trainwreck when hitting hash keys that do not exist" do
48
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
49
+ expect(vivid.read("one", "five", "six")).to eql(nil)
50
+ end
51
+
52
+ it "does not trainwreck when hitting an array with an out of bounds index" do
53
+ expect(root).to receive(:top_level_breadcrumb=).with("array").and_call_original
54
+ expect(vivid.read("array", 5, "one")).to eql(nil)
55
+ end
56
+
57
+ it "does not trainwreck when hitting an array with a string key" do
58
+ expect(root).to receive(:top_level_breadcrumb=).with("array").and_call_original
59
+ expect(vivid.read("array", "one", "two")).to eql(nil)
60
+ end
61
+
62
+ it "does not trainwreck when traversing a nil" do
63
+ expect(root).to receive(:top_level_breadcrumb=).with("nil").and_call_original
64
+ expect(vivid.read("nil", "one", "two")).to eql(nil)
65
+ end
66
+ end
67
+
68
+ context "#exist?" do
69
+ before do
70
+ # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
71
+ vivid
72
+ expect(root).not_to receive(:reset_cache)
73
+ end
74
+
75
+ it "true if there's a hash key there" do
76
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
77
+ expect(vivid.exist?("one", "two", "three")).to be true
78
+ end
79
+
80
+ it "true for intermediate hashes" do
81
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
82
+ expect(vivid.exist?("one")).to be true
83
+ end
84
+
85
+ it "true for arrays that exist" do
86
+ expect(root).to receive(:top_level_breadcrumb=).with("array").and_call_original
87
+ expect(vivid.exist?("array", 1)).to be true
88
+ end
89
+
90
+ it "true when the value of the key is nil" do
91
+ expect(root).to receive(:top_level_breadcrumb=).with("nil").and_call_original
92
+ expect(vivid.exist?("nil")).to be true
93
+ end
94
+
95
+ it "false when attributes don't exist" do
96
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
97
+ expect(vivid.exist?("one", "five", "six")).to be false
98
+ end
99
+
100
+ it "false when traversing a non-container" do
101
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
102
+ expect(vivid.exist?("one", "two", "three", "four")).to be false
103
+ end
104
+
105
+ it "false when an array index does not exist" do
106
+ expect(root).to receive(:top_level_breadcrumb=).with("array").and_call_original
107
+ expect(vivid.exist?("array", 3)).to be false
108
+ end
109
+
110
+ it "false when traversing a nil" do
111
+ expect(root).to receive(:top_level_breadcrumb=).with("nil").and_call_original
112
+ expect(vivid.exist?("nil", "foo", "bar")).to be false
113
+ end
114
+ end
115
+
116
+ context "#read!" do
117
+ before do
118
+ # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
119
+ vivid
120
+ expect(root).not_to receive(:reset_cache)
121
+ end
122
+
123
+ it "reads hashes deeply" do
124
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
125
+ expect(vivid.read!("one", "two", "three")).to eql("four")
126
+ end
127
+
128
+ it "reads arrays deeply" do
129
+ expect(root).to receive(:top_level_breadcrumb=).with("array").and_call_original
130
+ expect(vivid.read!("array", 1)).to eql(1)
131
+ end
132
+
133
+ it "throws an exception when attributes do not exist" do
134
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
135
+ expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
136
+ end
137
+
138
+ it "throws an exception when traversing a non-container" do
139
+ expect(root).to receive(:top_level_breadcrumb=).with("one").and_call_original
140
+ expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
141
+ end
142
+
143
+ it "throws an exception when an array element does not exist" do
144
+ expect(root).to receive(:top_level_breadcrumb=).with("array").and_call_original
145
+ expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
146
+ end
147
+ end
148
+
149
+ context "#write" do
150
+ before do
151
+ vivid
152
+ expect(root).not_to receive(:reset_cache).with(nil)
153
+ end
154
+
155
+ it "should write into hashes" do
156
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
157
+ vivid.write("one", "five", "six")
158
+ expect(vivid["one"]["five"]).to eql("six")
159
+ end
160
+
161
+ it "should deeply autovivify" do
162
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
163
+ vivid.write("one", "five", "six", "seven", "eight", "nine", "ten")
164
+ expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
165
+ end
166
+
167
+ it "should raise an exception if you overwrite an array with a hash" do
168
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
169
+ vivid.write("array", "five", "six")
170
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil })
171
+ end
172
+
173
+ it "should raise an exception if you traverse through an array with a hash" do
174
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
175
+ vivid.write("array", "five", "six", "seven")
176
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil })
177
+ end
178
+
179
+ it "should raise an exception if you overwrite a string with a hash" do
180
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
181
+ vivid.write("one", "two", "three", "four", "five")
182
+ expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
183
+ end
184
+
185
+ it "should raise an exception if you traverse through a string with a hash" do
186
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
187
+ vivid.write("one", "two", "three", "four", "five", "six")
188
+ expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
189
+ end
190
+
191
+ it "should raise an exception if you overwrite a nil with a hash" do
192
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
193
+ vivid.write("nil", "one", "two")
194
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } })
195
+ end
196
+
197
+ it "should raise an exception if you traverse through a nil with a hash" do
198
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
199
+ vivid.write("nil", "one", "two", "three")
200
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } })
201
+ end
202
+
203
+ it "writes with a block" do
204
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
205
+ vivid.write("one", "five") { "six" }
206
+ expect(vivid["one"]["five"]).to eql("six")
207
+ end
208
+ end
209
+
210
+ context "#write!" do
211
+ before do
212
+ vivid
213
+ expect(root).not_to receive(:reset_cache).with(nil)
214
+ end
215
+
216
+ it "should write into hashes" do
217
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
218
+ vivid.write!("one", "five", "six")
219
+ expect(vivid["one"]["five"]).to eql("six")
220
+ end
221
+
222
+ it "should deeply autovivify" do
223
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
224
+ vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten")
225
+ expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
226
+ end
227
+
228
+ it "should raise an exception if you overwrite an array with a hash" do
229
+ expect(root).not_to receive(:reset_cache)
230
+ expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
231
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
232
+ end
233
+
234
+ it "should raise an exception if you traverse through an array with a hash" do
235
+ expect(root).not_to receive(:reset_cache)
236
+ expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
237
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
238
+ end
239
+
240
+ it "should raise an exception if you overwrite a string with a hash" do
241
+ expect(root).not_to receive(:reset_cache)
242
+ expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
243
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
244
+ end
245
+
246
+ it "should raise an exception if you traverse through a string with a hash" do
247
+ expect(root).not_to receive(:reset_cache)
248
+ expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
249
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
250
+ end
251
+
252
+ it "should raise an exception if you overwrite a nil with a hash" do
253
+ expect(root).not_to receive(:reset_cache)
254
+ expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
255
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
256
+ end
257
+
258
+ it "should raise an exception if you traverse through a nil with a hash" do
259
+ expect(root).not_to receive(:reset_cache)
260
+ expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
261
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
262
+ end
263
+
264
+ it "writes with a block" do
265
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
266
+ vivid.write!("one", "five") { "six" }
267
+ expect(vivid["one"]["five"]).to eql("six")
268
+ end
269
+ end
270
+
271
+ context "#unlink" do
272
+ before do
273
+ vivid
274
+ expect(root).not_to receive(:reset_cache).with(nil)
275
+ end
276
+
277
+ it "should return nil if the keys already don't exist" do
278
+ expect(root).not_to receive(:reset_cache)
279
+ expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil)
280
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
281
+ end
282
+
283
+ it "should unlink hashes" do
284
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
285
+ expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } })
286
+ expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
287
+ end
288
+
289
+ it "should unlink array elements" do
290
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
291
+ expect(vivid.unlink("array", 2)).to eql(2)
292
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
293
+ end
294
+
295
+ it "should unlink nil" do
296
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
297
+ expect(vivid.unlink("nil")).to eql(nil)
298
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
299
+ end
300
+
301
+ it "should traverse a nil and safely do nothing" do
302
+ expect(root).not_to receive(:reset_cache)
303
+ expect(vivid.unlink("nil", "foo")).to eql(nil)
304
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
305
+ end
306
+ end
307
+
308
+ context "#unlink!" do
309
+ before do
310
+ vivid
311
+ expect(root).not_to receive(:reset_cache).with(nil)
312
+ end
313
+
314
+ it "should raise an exception if the keys already don't exist" do
315
+ expect(root).not_to receive(:reset_cache)
316
+ expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
317
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
318
+ end
319
+
320
+ it "should unlink! hashes" do
321
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
322
+ expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } })
323
+ expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
324
+ end
325
+
326
+ it "should unlink! array elements" do
327
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
328
+ expect(vivid.unlink!("array", 2)).to eql(2)
329
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
330
+ end
331
+
332
+ it "should unlink! nil" do
333
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
334
+ expect(vivid.unlink!("nil")).to eql(nil)
335
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
336
+ end
337
+
338
+ it "should raise an exception if it traverses a nil" do
339
+ expect(root).not_to receive(:reset_cache)
340
+ expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
341
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
342
+ end
343
+ end
344
+ end