chef 14.12.9 → 14.13.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -9
  3. data/lib/chef/chef_fs/command_line.rb +11 -12
  4. data/lib/chef/deprecated.rb +3 -3
  5. data/lib/chef/dsl/data_query.rb +22 -4
  6. data/lib/chef/dsl/recipe.rb +1 -7
  7. data/lib/chef/dsl/universal.rb +6 -0
  8. data/lib/chef/file_access_control/windows.rb +5 -3
  9. data/lib/chef/knife.rb +3 -3
  10. data/lib/chef/knife/bootstrap.rb +1 -1
  11. data/lib/chef/knife/bootstrap/templates/chef-full.erb +2 -1
  12. data/lib/chef/knife/config_list_profiles.rb +1 -1
  13. data/lib/chef/knife/core/status_presenter.rb +9 -2
  14. data/lib/chef/mixin/template.rb +14 -9
  15. data/lib/chef/node_map.rb +5 -24
  16. data/lib/chef/provider/cron.rb +14 -2
  17. data/lib/chef/provider/file.rb +1 -1
  18. data/lib/chef/provider/service/insserv.rb +3 -1
  19. data/lib/chef/resource.rb +3 -10
  20. data/lib/chef/resource/windows_feature_powershell.rb +1 -1
  21. data/lib/chef/resource_collection.rb +3 -2
  22. data/lib/chef/shell.rb +1 -0
  23. data/lib/chef/version.rb +1 -1
  24. data/lib/chef/win32/api/security.rb +2 -0
  25. data/lib/chef/win32/file.rb +8 -0
  26. data/lib/chef/win32/security.rb +1 -1
  27. data/spec/data/templates/failed.erb +5 -0
  28. data/spec/functional/assets/inittest +36 -0
  29. data/spec/functional/resource/insserv_spec.rb +205 -0
  30. data/spec/functional/resource/link_spec.rb +2 -2
  31. data/spec/spec_helper.rb +1 -0
  32. data/spec/support/platform_helpers.rb +4 -0
  33. data/spec/support/shared/functional/directory_resource.rb +12 -10
  34. data/spec/support/shared/functional/file_resource.rb +2 -2
  35. data/spec/support/shared/functional/securable_resource.rb +102 -71
  36. data/spec/support/shared/unit/provider/file.rb +1 -0
  37. data/spec/unit/knife/bootstrap_spec.rb +22 -0
  38. data/spec/unit/knife_spec.rb +8 -5
  39. data/spec/unit/mixin/template_spec.rb +45 -0
  40. data/spec/unit/node_map_spec.rb +10 -43
  41. data/spec/unit/provider/cron_spec.rb +123 -20
  42. data/spec/unit/provider/service/insserv_service_spec.rb +2 -2
  43. data/spec/unit/resource_collection_spec.rb +8 -0
  44. data/spec/unit/resource_spec.rb +1 -13
  45. data/spec/unit/win32/security_spec.rb +25 -0
  46. metadata +7 -4
@@ -117,8 +117,7 @@ shared_context "use Windows permissions", :windows_only do
117
117
 
118
118
  let(:expected_write_perms) do
119
119
  {
120
- generic: Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE,
121
- specific: Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE,
120
+ specific: Chef::ReservedNames::Win32::API::Security::WRITE,
122
121
  }
123
122
  end
124
123
 
@@ -136,6 +135,8 @@ shared_context "use Windows permissions", :windows_only do
136
135
  }
137
136
  end
138
137
 
138
+ let (:write_flag) { 3 }
139
+
139
140
  RSpec::Matchers.define :have_expected_properties do |mask, type, flags|
140
141
  match do |ace|
141
142
  ace.mask == mask &&
@@ -363,78 +364,108 @@ shared_examples_for "a securable resource without existing target" do
363
364
  expect(descriptor.group).to eq(arbitrary_non_default_group)
364
365
  end
365
366
 
366
- describe "with rights and deny_rights attributes" do
367
-
368
- it "correctly sets :read rights" do
369
- resource.rights(:read, "Guest")
370
- resource.run_action(:create)
371
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_perms))
367
+ describe "#allowed_acl" do
368
+ context "correctly sets" do
369
+
370
+ it ":read rights" do
371
+ resource.rights(:read, "Guest")
372
+ resource.run_action(:create)
373
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_perms))
374
+ end
375
+
376
+ it ":read_execute rights" do
377
+ resource.rights(:read_execute, "Guest")
378
+ resource.run_action(:create)
379
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_execute_perms))
380
+ end
381
+
382
+ it ":write rights" do
383
+ resource.rights(:write, "Guest")
384
+ resource.run_action(:create)
385
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_write_perms, write_flag))
386
+ end
387
+
388
+ it ":modify rights" do
389
+ resource.rights(:modify, "Guest")
390
+ resource.run_action(:create)
391
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_modify_perms))
392
+ end
393
+
394
+ it ":full_control rights" do
395
+ resource.rights(:full_control, "Guest")
396
+ resource.run_action(:create)
397
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_full_control_perms))
398
+ end
399
+
400
+ it "multiple rights" do
401
+ resource.rights(:read, "Everyone")
402
+ resource.rights(:modify, "Guest")
403
+ resource.run_action(:create)
404
+
405
+ expect(explicit_aces).to eq(
406
+ allowed_acl(SID.Everyone, expected_read_perms) +
407
+ allowed_acl(SID.Guest, expected_modify_perms)
408
+ )
409
+ end
372
410
  end
411
+ end
373
412
 
374
- it "correctly sets :read_execute rights" do
375
- resource.rights(:read_execute, "Guest")
376
- resource.run_action(:create)
377
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_execute_perms))
378
- end
379
-
380
- it "correctly sets :write rights" do
381
- resource.rights(:write, "Guest")
382
- resource.run_action(:create)
383
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_write_perms))
384
- end
385
-
386
- it "correctly sets :modify rights" do
387
- resource.rights(:modify, "Guest")
388
- resource.run_action(:create)
389
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_modify_perms))
390
- end
391
-
392
- it "correctly sets :full_control rights" do
393
- resource.rights(:full_control, "Guest")
394
- resource.run_action(:create)
395
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_full_control_perms))
396
- end
397
-
398
- it "correctly sets deny_rights" do
399
- # deny is an ACE with full rights, but is a deny type ace, not an allow type
400
- resource.deny_rights(:full_control, "Guest")
401
- resource.run_action(:create)
402
- expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms))
403
- end
404
-
405
- it "Sets multiple rights" do
406
- resource.rights(:read, "Everyone")
407
- resource.rights(:modify, "Guest")
408
- resource.run_action(:create)
409
-
410
- expect(explicit_aces).to eq(
411
- allowed_acl(SID.Everyone, expected_read_perms) +
412
- allowed_acl(SID.Guest, expected_modify_perms)
413
- )
414
- end
415
-
416
- it "Sets deny_rights ahead of rights" do
417
- resource.rights(:read, "Everyone")
418
- resource.deny_rights(:modify, "Guest")
419
- resource.run_action(:create)
420
-
421
- expect(explicit_aces).to eq(
422
- denied_acl(SID.Guest, expected_modify_perms) +
423
- allowed_acl(SID.Everyone, expected_read_perms)
424
- )
425
- end
426
-
427
- it "Sets deny_rights ahead of rights when specified in reverse order" do
428
- resource.deny_rights(:modify, "Guest")
429
- resource.rights(:read, "Everyone")
430
- resource.run_action(:create)
431
-
432
- expect(explicit_aces).to eq(
433
- denied_acl(SID.Guest, expected_modify_perms) +
434
- allowed_acl(SID.Everyone, expected_read_perms)
435
- )
413
+ describe "#denied_acl" do
414
+ context "correctly sets" do
415
+
416
+ it ":read rights" do
417
+ resource.deny_rights(:read, "Guest")
418
+ resource.run_action(:create)
419
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_read_perms))
420
+ end
421
+
422
+ it ":read_execute rights" do
423
+ resource.deny_rights(:read_execute, "Guest")
424
+ resource.run_action(:create)
425
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_read_execute_perms))
426
+ end
427
+
428
+ it ":write rights" do
429
+ resource.deny_rights(:write, "Guest")
430
+ resource.run_action(:create)
431
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_write_perms, write_flag))
432
+ end
433
+
434
+ it ":modify rights" do
435
+ resource.deny_rights(:modify, "Guest")
436
+ resource.run_action(:create)
437
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_modify_perms))
438
+ end
439
+
440
+ it ":full_control rights" do
441
+ # deny is an ACE with full rights, but is a deny type ace, not an allow type
442
+ resource.deny_rights(:full_control, "Guest")
443
+ resource.run_action(:create)
444
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms))
445
+ end
446
+
447
+ it "deny_rights ahead of rights" do
448
+ resource.rights(:read, "Everyone")
449
+ resource.deny_rights(:modify, "Guest")
450
+ resource.run_action(:create)
451
+
452
+ expect(explicit_aces).to eq(
453
+ denied_acl(SID.Guest, expected_modify_perms) +
454
+ allowed_acl(SID.Everyone, expected_read_perms)
455
+ )
456
+ end
457
+
458
+ it "deny_rights ahead of rights when specified in reverse order" do
459
+ resource.deny_rights(:modify, "Guest")
460
+ resource.rights(:read, "Everyone")
461
+ resource.run_action(:create)
462
+
463
+ expect(explicit_aces).to eq(
464
+ denied_acl(SID.Guest, expected_modify_perms) +
465
+ allowed_acl(SID.Everyone, expected_read_perms)
466
+ )
467
+ end
436
468
  end
437
-
438
469
  end
439
470
 
440
471
  context "with a mode attribute" do
@@ -76,6 +76,7 @@ def setup_symlink
76
76
  allow(File).to receive(:directory?).with(path).and_return(false)
77
77
  allow(File).to receive(:writable?).with(path).and_return(true)
78
78
  allow(file_symlink_class).to receive(:symlink?).with(path).and_return(true)
79
+ allow(file_symlink_class).to receive(:realpath).with(path).and_return(path)
79
80
  end
80
81
  allow(File).to receive(:directory?).with(enclosing_directory).and_return(true)
81
82
  end
@@ -96,6 +96,28 @@ describe Chef::Knife::Bootstrap do
96
96
  end
97
97
  end
98
98
 
99
+ context "with --bootstrap-proxy" do
100
+ let(:bootstrap_cli_options) { [ "--bootstrap-proxy", "1.1.1.1" ] }
101
+ let(:rendered_template) do
102
+ knife.merge_configs
103
+ knife.render_template
104
+ end
105
+ it "configures the https_proxy environment variable in the bootstrap template correctly" do
106
+ expect(rendered_template).to match(%r{https_proxy="1.1.1.1" export https_proxy})
107
+ end
108
+ end
109
+
110
+ context "with --bootstrap-no-proxy" do
111
+ let(:bootstrap_cli_options) { [ "--bootstrap-no-proxy", "localserver" ] }
112
+ let(:rendered_template) do
113
+ knife.merge_configs
114
+ knife.render_template
115
+ end
116
+ it "configures the https_proxy environment variable in the bootstrap template correctly" do
117
+ expect(rendered_template).to match(%r{no_proxy="localserver" export no_proxy})
118
+ end
119
+ end
120
+
99
121
  context "with :bootstrap_template and :template_file cli options" do
100
122
  let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "other-template" ] }
101
123
 
@@ -344,11 +344,14 @@ describe Chef::Knife do
344
344
  end
345
345
  end
346
346
 
347
- it "does not humanize the exception if Chef::Config[:verbosity] is two" do
348
- Chef::Config[:verbosity] = 2
349
- allow(knife).to receive(:run).and_raise(Exception)
350
- expect(knife).not_to receive(:humanize_exception)
351
- expect { knife.run_with_pretty_exceptions }.to raise_error(Exception)
347
+ # -VV (2) is debug, -VVV (3) is trace
348
+ [ 2, 3 ].each do |verbosity|
349
+ it "does not humanize the exception if Chef::Config[:verbosity] is #{verbosity}" do
350
+ Chef::Config[:verbosity] = verbosity
351
+ allow(knife).to receive(:run).and_raise(Exception)
352
+ expect(knife).not_to receive(:humanize_exception)
353
+ expect { knife.run_with_pretty_exceptions }.to raise_error(Exception)
354
+ end
352
355
  end
353
356
  end
354
357
 
@@ -182,6 +182,51 @@ describe Chef::Mixin::Template, "render_template" do
182
182
  expect(output).to eq("before {partial one We could be diving for pearls! calling home} after")
183
183
  end
184
184
 
185
+ describe "when an exception is raised in the template" do
186
+ let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "templates", "failed.erb")) }
187
+
188
+ def do_raise
189
+ @template_context.render_template(template_file)
190
+ end
191
+
192
+ it "should catch and re-raise the exception as a TemplateError" do
193
+ expect { do_raise }.to raise_error(Chef::Mixin::Template::TemplateError)
194
+ end
195
+
196
+ describe "the raised TemplateError" do
197
+ subject(:exception) do
198
+ begin
199
+ do_raise
200
+ rescue Chef::Mixin::Template::TemplateError => e
201
+ e
202
+ end
203
+ end
204
+
205
+ it "should contain template file and line numbers" do
206
+ expect(exception.line_number).to eq(5)
207
+ end
208
+
209
+ it "should provide a source listing of the template around the exception" do
210
+ expect(exception.source_listing).to eq(" 3: Which includes some content\n 4: \n 5: And will fail <%= nil[] %>")
211
+ end
212
+
213
+ it "should provide a nice source location" do
214
+ expect(exception.source_location).to eq("on line #5")
215
+ end
216
+
217
+ it "should create a pretty output for the terminal" do
218
+ expect(exception.to_s).to match(/Chef::Mixin::Template::TemplateError/)
219
+ expect(exception.to_s).to match(/undefined method `\[\]' for nil:NilClass/)
220
+ expect(exception.to_s).to include(" 3: Which includes some content\n 4: \n 5: And will fail <%= nil[] %>")
221
+ expect(exception.to_s).to include(exception.original_exception.backtrace.first)
222
+ end
223
+
224
+ it "should include template file on original_exception backtrace" do
225
+ expect(exception.original_exception.backtrace).to include(/#{Regexp.escape(template_file)}/)
226
+ end
227
+ end
228
+ end
229
+
185
230
  describe "when customizing the template context" do
186
231
 
187
232
  it "extends the context to include modules" do
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Lamont Granquist (<lamont@chef.io>)
3
- # Copyright:: Copyright 2014-2018, Chef Software Inc.
3
+ # Copyright:: Copyright 2014-2019, Chef Software Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -145,14 +145,14 @@ describe Chef::NodeMap do
145
145
  describe "deleting classes" do
146
146
  it "deletes a class and removes the mapping completely" do
147
147
  node_map.set(:thing, Bar)
148
- expect( node_map.delete_class(Bar) ).to include({ thing: [{ klass: Bar, cookbook_override: false, core_override: false }] })
148
+ expect( node_map.delete_class(Bar) ).to include({ thing: [{ klass: Bar }] })
149
149
  expect( node_map.get(node, :thing) ).to eql(nil)
150
150
  end
151
151
 
152
152
  it "deletes a class and leaves the mapping that still has an entry" do
153
153
  node_map.set(:thing, Bar)
154
154
  node_map.set(:thing, Foo)
155
- expect( node_map.delete_class(Bar) ).to eql({ thing: [{ klass: Bar, cookbook_override: false, core_override: false }] })
155
+ expect( node_map.delete_class(Bar) ).to eql({ thing: [{ klass: Bar }] })
156
156
  expect( node_map.get(node, :thing) ).to eql(Foo)
157
157
  end
158
158
 
@@ -160,7 +160,7 @@ describe Chef::NodeMap do
160
160
  node_map.set(:thing1, Bar)
161
161
  node_map.set(:thing2, Bar)
162
162
  node_map.set(:thing2, Foo)
163
- expect( node_map.delete_class(Bar) ).to eql({ thing1: [{ klass: Bar, cookbook_override: false, core_override: false }], thing2: [{ klass: Bar, cookbook_override: false, core_override: false }] })
163
+ expect( node_map.delete_class(Bar) ).to eql({ thing1: [{ klass: Bar }], thing2: [{ klass: Bar }] })
164
164
  expect( node_map.get(node, :thing1) ).to eql(nil)
165
165
  expect( node_map.get(node, :thing2) ).to eql(Foo)
166
166
  end
@@ -213,7 +213,7 @@ describe Chef::NodeMap do
213
213
  describe "locked mode" do
214
214
  context "while unlocked" do
215
215
  it "allows setting the same key twice" do
216
- expect(Chef).to_not receive(:deprecated)
216
+ expect(Chef::Log).to_not receive(:warn)
217
217
  node_map.set(:foo, FooResource)
218
218
  node_map.set(:foo, BarResource)
219
219
  expect(node_map.get(node, :foo)).to eql(BarResource)
@@ -221,53 +221,20 @@ describe Chef::NodeMap do
221
221
  end
222
222
 
223
223
  context "while locked" do
224
- # Uncomment the commented `expect`s in 15.0.
225
- it "rejects setting the same key twice" do
226
- expect(Chef).to receive(:deprecated).with(:map_collision, /Resource foo/)
224
+ it "warns on setting the same key twice" do
225
+ expect(Chef::Log).to receive(:warn).with(/Resource foo/)
227
226
  node_map.set(:foo, FooResource)
228
227
  node_map.lock!
229
228
  node_map.set(:foo, BarResource)
230
- # expect(node_map.get(node, :foo)).to eql(FooResource)
231
- end
232
-
233
- it "allows setting the same key twice when the first has allow_cookbook_override" do
234
- expect(Chef).to_not receive(:deprecated)
235
- node_map.set(:foo, FooResource, allow_cookbook_override: true)
236
- node_map.lock!
237
- node_map.set(:foo, BarResource)
238
- expect(node_map.get(node, :foo)).to eql(BarResource)
239
- end
240
-
241
- it "allows setting the same key twice when the first has allow_cookbook_override with a future version" do
242
- expect(Chef).to_not receive(:deprecated)
243
- node_map.set(:foo, FooResource, allow_cookbook_override: "< 100")
244
- node_map.lock!
245
- node_map.set(:foo, BarResource)
246
- expect(node_map.get(node, :foo)).to eql(BarResource)
247
- end
248
-
249
- it "rejects setting the same key twice when the first has allow_cookbook_override with a past version" do
250
- expect(Chef).to receive(:deprecated).with(:map_collision, /Resource foo/)
251
- node_map.set(:foo, FooResource, allow_cookbook_override: "< 1")
252
- node_map.lock!
253
- node_map.set(:foo, BarResource)
254
- # expect(node_map.get(node, :foo)).to eql(FooResource)
255
- end
256
-
257
- it "allows setting the same key twice when the second has __core_override__" do
258
- expect(Chef).to_not receive(:deprecated)
259
- node_map.set(:foo, FooResource)
260
- node_map.lock!
261
- node_map.set(:foo, BarResource, __core_override__: true)
262
229
  expect(node_map.get(node, :foo)).to eql(BarResource)
263
230
  end
264
231
 
265
- it "rejects setting the same key twice for a provider" do
266
- expect(Chef).to receive(:deprecated).with(:map_collision, /Provider foo/)
232
+ it "warns on setting the same key twice for a provider" do
233
+ expect(Chef::Log).to receive(:warn).with(/Provider foo/)
267
234
  node_map.set(:foo, FooProvider)
268
235
  node_map.lock!
269
236
  node_map.set(:foo, BarProvider)
270
- # expect(node_map.get(node, :foo)).to eql(FooProvider)
237
+ expect(node_map.get(node, :foo)).to eql(BarProvider)
271
238
  end
272
239
  end
273
240
  end
@@ -690,33 +690,136 @@ CRONTAB
690
690
  end
691
691
 
692
692
  context "when there is a crontab with a matching and identical section" do
693
- before :each do
694
- @provider.cron_exists = true
695
- allow(@provider).to receive(:cron_different?).and_return(false)
696
- allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
697
- 0 2 * * * /some/other/command
693
+ context "when environment variable is not used" do
694
+ before :each do
695
+ @provider.cron_exists = true
696
+ allow(@provider).to receive(:cron_different?).and_return(false)
697
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
698
+ 0 2 * * * /some/other/command
698
699
 
699
- # Chef Name: cronhole some stuff
700
- * 5 * * * /bin/true
700
+ # Chef Name: cronhole some stuff
701
+ SHELL=/bash
702
+ * 5 * * * /bin/true
701
703
 
702
- # Another comment
703
- CRONTAB
704
- end
704
+ # Another comment
705
+ CRONTAB
706
+ end
705
707
 
706
- it "should not update the crontab" do
707
- expect(@provider).not_to receive(:write_crontab)
708
- @provider.run_action(:create)
708
+ it "should not update the crontab" do
709
+ expect(@provider).not_to receive(:write_crontab)
710
+ @provider.run_action(:create)
711
+ end
712
+
713
+ it "should not mark the resource as updated" do
714
+ @provider.run_action(:create)
715
+ expect(@new_resource).not_to be_updated_by_last_action
716
+ end
717
+
718
+ it "should log nothing changed" do
719
+ expect(logger).to receive(:trace).with("Found cron '#{@new_resource.name}'")
720
+ expect(logger).to receive(:trace).with("Skipping existing cron entry '#{@new_resource.name}'")
721
+ @provider.run_action(:create)
722
+ end
709
723
  end
710
724
 
711
- it "should not mark the resource as updated" do
712
- @provider.run_action(:create)
713
- expect(@new_resource).not_to be_updated_by_last_action
725
+ context "when environment variable is used" do
726
+ before :each do
727
+ @provider.cron_exists = true
728
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
729
+ 0 2 * * * /some/other/command
730
+
731
+ # Chef Name: cronhole some stuff
732
+ SHELL=/bash
733
+ ENV=environment
734
+ 30 * * * * /bin/true
735
+
736
+ # Another comment
737
+ CRONTAB
738
+ end
739
+ context "contains an entry that can also be specified as a `property`" do
740
+ before :each do
741
+ @new_resource.environment = { "SHELL" => "/bash", "ENV" => "environment" }
742
+ end
743
+
744
+ it "should raise a warning for idempotency" do
745
+ expect(logger).to receive(:warn).with("cronhole some stuff: the environment property contains the 'SHELL' variable, which should be set separately as a property.")
746
+ @provider.run_action(:create)
747
+ end
748
+
749
+ it "should not update the crontab" do
750
+ expect(@provider).not_to receive(:write_crontab)
751
+ @provider.run_action(:create)
752
+ end
753
+
754
+ it "should not mark the resource as updated" do
755
+ expect(@new_resource).not_to be_updated_by_last_action
756
+ @provider.run_action(:create)
757
+ end
758
+ end
759
+
760
+ context "contains an entry that cannot be specified as a `property`" do
761
+ before :each do
762
+ @new_resource.environment = { "ENV" => "environment" }
763
+ @new_resource.shell "/bash"
764
+ end
765
+
766
+ it "should not raise a warning for idempotency" do
767
+ expect(logger).not_to receive(:warn).with("cronhole some stuff: the environment property contains the 'SHELL' variable, which should be set separately as a property.")
768
+ @provider.run_action(:create)
769
+ end
770
+
771
+ it "should not update the crontab" do
772
+ expect(@provider).not_to receive(:write_crontab)
773
+ @provider.run_action(:create)
774
+ end
775
+
776
+ it "should not mark the resource as updated" do
777
+ @provider.run_action(:create)
778
+ expect(@new_resource).not_to be_updated_by_last_action
779
+ end
780
+ end
714
781
  end
715
782
 
716
- it "should log nothing changed" do
717
- expect(logger).to receive(:trace).with("Found cron '#{@new_resource.name}'")
718
- expect(logger).to receive(:trace).with("Skipping existing cron entry '#{@new_resource.name}'")
719
- @provider.run_action(:create)
783
+ context "when environment variable is used with property" do
784
+ before :each do
785
+ @provider.cron_exists = true
786
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
787
+ 0 2 * * * /some/other/command
788
+
789
+ # Chef Name: cronhole some stuff
790
+ SHELL=/bash
791
+ ENV=environment
792
+ 30 * * * * /bin/true
793
+
794
+ # Another comment
795
+ CRONTAB
796
+ end
797
+
798
+ context "when environment variable is same as property" do
799
+ it "should throw an error" do
800
+ @new_resource.shell "/bash"
801
+ @new_resource.environment "SHELL" => "/bash"
802
+ expect do
803
+ @provider.run_action(:create)
804
+ end.to raise_error(Chef::Exceptions::Cron, /cronhole some stuff: the 'SHELL' property is set and environment property also contains the 'SHELL' variable. Remove the variable from the environment property./)
805
+ end
806
+ end
807
+
808
+ context "when environment variable is different from property" do
809
+ it "should not update the crontab" do
810
+ @new_resource.shell "/bash"
811
+ @new_resource.environment "ENV" => "environment"
812
+ expect(@provider).not_to receive(:write_crontab)
813
+ @provider.run_action(:create)
814
+ end
815
+
816
+ it "should not mark the resource as updated" do
817
+ @new_resource.shell "/bash"
818
+ @new_resource.environment "ENV" => "environment"
819
+ @provider.run_action(:create)
820
+ expect(@new_resource).not_to be_updated_by_last_action
821
+ end
822
+ end
720
823
  end
721
824
  end
722
825
  end