chef 18.1.29 → 18.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/chef-universal-mingw-ucrt.gemspec +2 -2
  3. data/chef.gemspec +1 -2
  4. data/lib/chef/application/base.rb +2 -0
  5. data/lib/chef/client.rb +2 -2
  6. data/lib/chef/cookbook/synchronizer.rb +20 -2
  7. data/lib/chef/cookbook_version.rb +1 -1
  8. data/lib/chef/http/authenticator.rb +2 -2
  9. data/lib/chef/http/ssl_policies.rb +2 -2
  10. data/lib/chef/mixin/homebrew_user.rb +12 -5
  11. data/lib/chef/mixin/proxified_socket.rb +1 -1
  12. data/lib/chef/monkey_patches/net-http.rb +127 -0
  13. data/lib/chef/node/attribute_collections.rb +8 -0
  14. data/lib/chef/node/immutable_collections.rb +5 -2
  15. data/lib/chef/node/mixin/state_tracking.rb +1 -1
  16. data/lib/chef/platform/query_helpers.rb +4 -2
  17. data/lib/chef/provider/launchd.rb +1 -1
  18. data/lib/chef/provider/mount/linux.rb +1 -1
  19. data/lib/chef/provider/mount/mount.rb +5 -5
  20. data/lib/chef/provider/package/chocolatey.rb +18 -1
  21. data/lib/chef/provider/package/zypper.rb +1 -0
  22. data/lib/chef/provider/remote_file/http.rb +1 -1
  23. data/lib/chef/provider/yum_repository.rb +1 -1
  24. data/lib/chef/resource/apt_repository.rb +25 -6
  25. data/lib/chef/resource/homebrew_cask.rb +6 -7
  26. data/lib/chef/resource/homebrew_package.rb +1 -1
  27. data/lib/chef/resource/homebrew_tap.rb +5 -5
  28. data/lib/chef/resource/launchd.rb +5 -1
  29. data/lib/chef/resource/macos_userdefaults.rb +9 -5
  30. data/lib/chef/resource/selinux_login.rb +129 -0
  31. data/lib/chef/resource/selinux_user.rb +137 -0
  32. data/lib/chef/resource/windows_certificate.rb +1 -1
  33. data/lib/chef/resource/windows_security_policy.rb +2 -2
  34. data/lib/chef/resource.rb +11 -1
  35. data/lib/chef/resources.rb +2 -0
  36. data/lib/chef/version.rb +1 -1
  37. data/lib/chef/win32/security.rb +7 -1
  38. data/spec/data/apt/chef-integration-test-1.0/debian/rules +0 -0
  39. data/spec/data/apt/chef-integration-test-1.1/debian/rules +0 -0
  40. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +0 -0
  41. data/spec/data/trusted_certs/intermediate.pem +38 -27
  42. data/spec/data/trusted_certs/opscode.pem +33 -54
  43. data/spec/functional/assets/chefinittest +0 -0
  44. data/spec/functional/assets/testchefsubsys +0 -0
  45. data/spec/functional/provider/remote_file/cache_control_data_spec.rb +0 -0
  46. data/spec/functional/resource/aix_service_spec.rb +0 -0
  47. data/spec/functional/resource/aixinit_service_spec.rb +0 -0
  48. data/spec/functional/resource/chocolatey_package_spec.rb +32 -20
  49. data/spec/functional/resource/execute_spec.rb +1 -1
  50. data/spec/functional/resource/macos_userdefaults_spec.rb +4 -4
  51. data/spec/functional/resource/windows_certificate_spec.rb +25 -0
  52. data/spec/unit/client_spec.rb +2 -2
  53. data/spec/unit/mixin/homebrew_user_spec.rb +30 -7
  54. data/spec/unit/node/vivid_mash_spec.rb +42 -0
  55. data/spec/unit/provider/apt_repository_spec.rb +17 -7
  56. data/spec/unit/provider/launchd_spec.rb +2 -2
  57. data/spec/unit/provider/mount/aix_spec.rb +2 -2
  58. data/spec/unit/provider/mount/linux_spec.rb +6 -5
  59. data/spec/unit/provider/mount/mount_spec.rb +8 -8
  60. data/spec/unit/provider/package/chocolatey_spec.rb +19 -3
  61. data/spec/unit/provider/package/rpm_spec.rb +2 -2
  62. data/spec/unit/provider/package/zypper_spec.rb +10 -0
  63. data/spec/unit/provider/remote_file/http_spec.rb +4 -4
  64. data/spec/unit/resource/apt_repository_spec.rb +5 -0
  65. data/spec/unit/resource/macos_user_defaults_spec.rb +4 -4
  66. data/spec/unit/resource/selinux_login_spec.rb +73 -0
  67. data/spec/unit/resource/selinux_user_spec.rb +92 -0
  68. data/spec/unit/resource_spec.rb +86 -0
  69. metadata +19 -15
  70. data/lib/chef/powershell.rb +0 -81
  71. /data/spec/functional/assets/chocolatey_feed/{test-A.1.0.nupkg → test-A.1.0.0.nupkg} +0 -0
  72. /data/spec/functional/assets/chocolatey_feed/{test-A.1.5.nupkg → test-A.1.5.0.nupkg} +0 -0
  73. /data/spec/functional/assets/chocolatey_feed/{test-A.2.0.nupkg → test-A.2.0.0.nupkg} +0 -0
  74. /data/spec/functional/assets/chocolatey_feed/{test-B.1.0.nupkg → test-B.1.0.0.nupkg} +0 -0
@@ -22,8 +22,16 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
22
22
  include Chef::Mixin::ShellOut
23
23
 
24
24
  let(:package_name) { "test-A" }
25
- let(:package_list) { proc { shell_out!("choco list -lo -r #{Array(package_name).join(" ")}").stdout.chomp } }
26
25
  let(:package_source) { File.join(CHEF_SPEC_ASSETS, "chocolatey_feed") }
26
+ let(:package_list) do
27
+ if Chef::Provider::Package::Chocolatey.query_command == "list"
28
+ # using result of query_command because that indicates which "search" command to use
29
+ # which coincides with the package list output
30
+ proc { shell_out!("choco search -lo #{Array(package_name).join(" ")}").stdout.chomp }
31
+ else
32
+ proc { shell_out!("choco list #{Array(package_name).join(" ")}").stdout.chomp }
33
+ end
34
+ end
27
35
 
28
36
  let(:run_context) do
29
37
  Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
@@ -54,12 +62,16 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
54
62
  ENV["Path"] = ENV.delete("Path")
55
63
  end
56
64
 
65
+ after(:each) do
66
+ described_class.instance_variable_set(:@get_choco_version, nil)
67
+ end
68
+
57
69
  context "installing a package" do
58
70
  after { remove_package }
59
71
 
60
72
  it "installs the latest version" do
61
73
  subject.run_action(:install)
62
- expect(package_list.call).to eq("#{package_name}|2.0")
74
+ expect(package_list.call).to match(/^#{package_name}|2.0.0$/)
63
75
  end
64
76
 
65
77
  it "does not install if already installed" do
@@ -69,19 +81,19 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
69
81
  end
70
82
 
71
83
  it "installs version given" do
72
- subject.version "1.0"
84
+ subject.version "1.0.0"
73
85
  subject.run_action(:install)
74
- expect(package_list.call).to eq("#{package_name}|1.0")
86
+ expect(package_list.call).to match(/^#{package_name}|1.0.0$/)
75
87
  end
76
88
 
77
89
  it "installs new version if one is already installed" do
78
- subject.version "1.0"
90
+ subject.version "1.0.0"
79
91
  subject.run_action(:install)
80
- expect(package_list.call).to eq("#{package_name}|1.0")
92
+ expect(package_list.call).to match(/^#{package_name}|1.0.0$/)
81
93
 
82
- subject.version "2.0"
94
+ subject.version "2.0.0"
83
95
  subject.run_action(:install)
84
- expect(package_list.call).to eq("#{package_name}|2.0")
96
+ expect(package_list.call).to match(/^#{package_name}|2.0.0$/)
85
97
  end
86
98
 
87
99
  context "installing multiple packages" do
@@ -89,7 +101,7 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
89
101
 
90
102
  it "installs both packages" do
91
103
  subject.run_action(:install)
92
- expect(package_list.call).to eq("test-A|2.0\r\ntest-B|1.0")
104
+ expect(package_list.call).to match(/^test-A|2.0.0\r\ntest-B|1.0.0$/)
93
105
  end
94
106
  end
95
107
 
@@ -101,13 +113,13 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
101
113
  it "installs with an option as a string" do
102
114
  subject.options "--force --confirm"
103
115
  subject.run_action(:install)
104
- expect(package_list.call).to eq("#{package_name}|2.0")
116
+ expect(package_list.call).to match(/^#{package_name}|2.0.0$/)
105
117
  end
106
118
 
107
119
  it "installs with multiple options as a string" do
108
120
  subject.options "--force --confirm"
109
121
  subject.run_action(:install)
110
- expect(package_list.call).to eq("#{package_name}|2.0")
122
+ expect(package_list.call).to match(/^#{package_name}|2.0.0$/)
111
123
  end
112
124
 
113
125
  context "when multiple options passed as string" do
@@ -137,7 +149,7 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
137
149
  it "installs with multiple options as an array" do
138
150
  subject.options [ "--force", "--confirm" ]
139
151
  subject.run_action(:install)
140
- expect(package_list.call).to eq("#{package_name}|2.0")
152
+ expect(package_list.call).to match(/^#{package_name}|2.0.0$/)
141
153
  end
142
154
  end
143
155
 
@@ -145,24 +157,24 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
145
157
  after { remove_package }
146
158
 
147
159
  it "upgrades to a specific version" do
148
- subject.version "1.0"
160
+ subject.version "1.0.0"
149
161
  subject.run_action(:install)
150
- expect(package_list.call).to eq("#{package_name}|1.0")
162
+ expect(package_list.call).to match(/^#{package_name}|1.0.0$/)
151
163
 
152
- subject.version "1.5"
164
+ subject.version "1.5.0"
153
165
  subject.run_action(:upgrade)
154
- expect(package_list.call).to eq("#{package_name}|1.5")
166
+ expect(package_list.call).to match(/^#{package_name}|1.5.0$/)
155
167
  end
156
168
 
157
169
  it "upgrades to the latest version if no version given" do
158
- subject.version "1.0"
170
+ subject.version "1.0.0"
159
171
  subject.run_action(:install)
160
- expect(package_list.call).to eq("#{package_name}|1.0")
172
+ expect(package_list.call).to match(/^#{package_name}|1.0.0$/)
161
173
 
162
174
  subject2 = Chef::Resource::ChocolateyPackage.new("test-A", run_context)
163
175
  subject2.source package_source
164
176
  subject2.run_action(:upgrade)
165
- expect(package_list.call).to eq("#{package_name}|2.0")
177
+ expect(package_list.call).to match(/^#{package_name}|2.0.0$/)
166
178
  end
167
179
  end
168
180
 
@@ -170,7 +182,7 @@ describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
170
182
  it "removes an installed package" do
171
183
  subject.run_action(:install)
172
184
  remove_package
173
- expect(package_list.call).to eq("")
185
+ expect(package_list.call).to match(/0 packages installed/)
174
186
  end
175
187
  end
176
188
 
@@ -62,7 +62,7 @@ describe Chef::Resource::Execute do
62
62
  end
63
63
 
64
64
  describe "when parent resource sets :cwd" do
65
- let(:guard) { %{ruby -e 'exit 1 unless File.exists?("./nested.json")'} }
65
+ let(:guard) { %{ruby -e 'exit 1 unless File.exist?("./nested.json")'} }
66
66
 
67
67
  it "guard inherits :cwd from resource and runs" do
68
68
  resource.cwd CHEF_SPEC_DATA
@@ -38,12 +38,12 @@ describe Chef::Resource::MacosUserDefaults, :macos_only do
38
38
  expect(resource.domain).to eq("NSGlobalDomain")
39
39
  end
40
40
 
41
- it "nil for the host property" do
42
- expect(resource.host).to be_nil
41
+ it ":all for the host property" do
42
+ expect(resource.host).to eq(:all)
43
43
  end
44
44
 
45
- it "nil for the user property" do
46
- expect(resource.user).to be_nil
45
+ it ":current for the user property" do
46
+ expect(resource.user).to eq(:current)
47
47
  end
48
48
 
49
49
  it ":write for resource action" do
@@ -56,6 +56,30 @@ describe Chef::Resource::WindowsCertificate, :windows_only do
56
56
  let(:store) { "Chef-Functional-Test" }
57
57
  let(:store_name) { "MY" }
58
58
  let(:store_location) { "LocalMachine" }
59
+ let(:test_cert_body) do
60
+ <<~CERT
61
+ -----BEGIN CERTIFICATE-----
62
+ MIIDQTCCAimgAwIBAgIQX3zqNCJbsKlEvzCz3Z9aNDANBgkqhkiG9w0BAQsFADAh
63
+ MR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVzdHMuY29tMCAXDTIwMDMwNTEwMjcw
64
+ NVoYDzIxMjAwMzA1MTAzNzA2WjAhMR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVz
65
+ dHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtuYKDb6woWIH
66
+ HPPOrcVpgJFVxbkjgk+tsYwbIiqR9jtRaKE6nM/awOgn9/dFF4k8KB8Em0sUx7Vq
67
+ J3YhK2N2cAacgP2Frqqf5znpNBBOg968RoZzGx0EiXFvLsqC4y8ggApWTbMXPRk4
68
+ 1a7GlpUpSqI3y5cLeEbzwGQKu8I1I+v7P2fTlnJPHarM7sBbL8bieukkFHYu78iV
69
+ u1wpKOCCfs5DTmJu8WN+z1Mar9vyrWMBlt2wBBgNHPz5mcXUzJHTzaI/D9RGgBgF
70
+ V0IkNqISx/IzR62jjj2g6MgTH4G/0mM6O5sxduM4yGmWZNZpVzh0yMLgH619MZlj
71
+ SMQIN3U/SQIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
72
+ BQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiCFnd3dy5kdW1teWNoZWZ0ZXN0cy5j
73
+ b20wHQYDVR0OBBYEFHwS3gs03m6RcpR+66u4OqGiZdYnMA0GCSqGSIb3DQEBCwUA
74
+ A4IBAQCFHqMjHUfBZahIsKHQIcFCbC1NFh1ZHlJKZzrRBRwRzX19OttHGMyLpDd6
75
+ tM9Ac6LLR8S4QIWg+HF3IrkN+vfTRDZAccj+tIwBRstmdsEz/rAJ79Vb/00mXZQx
76
+ 0FPiBDR3hE7On2oo24DU8kJP3v6TrunwtIomVGqrrkwZzvxqyW+WJMB2shGNFw5J
77
+ mKYBiiXsHl4Bi7V4zhXssrLp877sqpNLeXloXBmAlT39SwQTP9ImZaV5R6udqlvo
78
+ Gfgm5PH/WeK6MV3n5ik0v1rS0LwR2o82WlIB6a4iSEbzY3qSLsWOwt8o5QjAVzCR
79
+ tNdbdS3U8nrG73iA2clmF57ARQWC
80
+ -----END CERTIFICATE-----
81
+ CERT
82
+ end
59
83
  let(:download_cert_url) { "https://testingchef.blob.core.windows.net/files/test.cer" }
60
84
  let(:cert_output_path) { ::File.join(Chef::Config[:file_cache_path], "output.cer") }
61
85
  let(:pfx_output_path) { ::File.join(Chef::Config[:file_cache_path], "output.pfx") }
@@ -128,6 +152,7 @@ describe Chef::Resource::WindowsCertificate, :windows_only do
128
152
  end
129
153
 
130
154
  it "can add a certificate from a valid url" do
155
+ stub_request(:get, download_cert_url).to_return(body: test_cert_body)
131
156
  resource.source = download_cert_url
132
157
  resource.run_action(:create)
133
158
 
@@ -119,8 +119,8 @@ shared_context "a client run" do
119
119
  # Make sure Client#register thinks the client key doesn't
120
120
  # exist, so it tries to register and create one.
121
121
  allow(Chef::HTTP::Authenticator).to receive(:detect_certificate_key).with(fqdn).and_return(false)
122
- allow(File).to receive(:exists?).and_call_original
123
- expect(File).to receive(:exists?)
122
+ allow(File).to receive(:exist?).and_call_original
123
+ expect(File).to receive(:exist?)
124
124
  .with(Chef::Config[:client_key])
125
125
  .exactly(:once)
126
126
  .and_return(api_client_exists?)
@@ -47,6 +47,8 @@ describe Chef::Mixin::HomebrewUser do
47
47
  let(:user) { nil }
48
48
  let(:brew_owner) { 2001 }
49
49
  let(:default_brew_path) { "/usr/local/bin/brew" }
50
+ let(:default_brew_path_arm) { "/opt/homebrew/bin/brew" }
51
+ let(:default_brew_path_linux) { "/home/linuxbrew/.linuxbrew/bin/brew" }
50
52
  let(:stat_double) do
51
53
  d = double
52
54
  expect(d).to receive(:uid).and_return(brew_owner)
@@ -59,16 +61,38 @@ describe Chef::Mixin::HomebrewUser do
59
61
  expect(Etc).to receive(:getpwuid).with(brew_owner).and_return(OpenStruct.new(name: "name"))
60
62
  end
61
63
 
62
- it "returns the owner of the brew executable when it is at a default location" do
63
- expect(File).to receive(:exist?).with(default_brew_path).and_return(true)
64
- expect(File).to receive(:stat).with(default_brew_path).and_return(stat_double)
64
+ def false_unless_specific_value(object, method, value)
65
+ allow(object).to receive(method).and_return(false)
66
+ allow(object).to receive(method).with(value).and_return(true)
67
+ end
68
+
69
+ it "returns the owner of the brew executable when it is at a default location for x86_64 machines" do
70
+ false_unless_specific_value(File, :exist?, default_brew_path)
71
+ false_unless_specific_value(File, :executable?, default_brew_path)
72
+ allow(File).to receive(:stat).with(default_brew_path).and_return(stat_double)
73
+ expect(homebrew_user.find_homebrew_uid(user)).to eq(brew_owner)
74
+ end
75
+
76
+ it "returns the owner of the brew executable when it is at a default location for arm machines" do
77
+ false_unless_specific_value(File, :exist?, default_brew_path_arm)
78
+ false_unless_specific_value(File, :executable?, default_brew_path_arm)
79
+ allow(File).to receive(:stat).with(default_brew_path_arm).and_return(stat_double)
80
+ expect(homebrew_user.find_homebrew_uid(user)).to eq(brew_owner)
81
+ end
82
+
83
+ it "returns the owner of the brew executable when it is at a default location for linux machines" do
84
+ false_unless_specific_value(File, :exist?, default_brew_path_linux)
85
+ false_unless_specific_value(File, :executable?, default_brew_path_linux)
86
+ allow(File).to receive(:stat).with(default_brew_path_linux).and_return(stat_double)
65
87
  expect(homebrew_user.find_homebrew_uid(user)).to eq(brew_owner)
66
88
  end
67
89
 
68
90
  it "returns the owner of the brew executable when it is not at a default location" do
69
- expect(File).to receive(:exist?).with(default_brew_path).and_return(false)
91
+ allow_any_instance_of(ExampleHomebrewUser).to receive(:which).and_return("/foo")
92
+ false_unless_specific_value(File, :exist?, "/foo")
93
+ false_unless_specific_value(File, :executable?, "/foo")
70
94
  allow(homebrew_user).to receive_message_chain(:shell_out, :stdout, :strip).and_return("/foo")
71
- expect(File).to receive(:stat).with("/foo").and_return(stat_double)
95
+ allow(File).to receive(:stat).with("/foo").and_return(stat_double)
72
96
  expect(homebrew_user.find_homebrew_uid(user)).to eq(brew_owner)
73
97
  end
74
98
 
@@ -78,8 +102,7 @@ describe Chef::Mixin::HomebrewUser do
78
102
  describe "when the homebrew user is not provided" do
79
103
 
80
104
  it "raises an error if no executable is found" do
81
- expect(File).to receive(:exist?).with(default_brew_path).and_return(false)
82
- allow(homebrew_user).to receive_message_chain(:shell_out, :stdout, :strip).and_return("")
105
+ expect(File).to receive(:exist?).and_return(nil).at_least(:once)
83
106
  expect { homebrew_user.find_homebrew_uid(user) }.to raise_error(Chef::Exceptions::CannotDetermineHomebrewOwner)
84
107
  end
85
108
 
@@ -364,20 +364,53 @@ describe Chef::Node::AttrArray do
364
364
 
365
365
  context "#<<" do
366
366
  it "converts a Hash appended with #<< to a VividMash" do
367
+ expect(root).to receive(:reset_cache).with(nil)
367
368
  array << { "three" => "four" }
368
369
  expect(array[3].class).to eql(Chef::Node::VividMash)
369
370
  end
370
371
 
371
372
  it "deeply converts objects appended with #<<" do
373
+ expect(root).to receive(:reset_cache).with(nil)
372
374
  array << [ { "three" => [ 0, 1] } ]
373
375
  expect(array[3].class).to eql(Chef::Node::AttrArray)
374
376
  expect(array[3][0].class).to eql(Chef::Node::VividMash)
375
377
  expect(array[3][0]["three"].class).to eql(Chef::Node::AttrArray)
376
378
  end
379
+
380
+ it "does not invalidate root DeepMergeCache if VividMash top-level key" do
381
+ # We're checking that __path__ is used to avoid full DeepMergeCache invalidation
382
+ expect(root).to receive(:reset_cache).with("array")
383
+ expect(root).not_to receive(:reset_cache).with(nil)
384
+
385
+ attr_array = Chef::Node::AttrArray.new([ 0, 1, 2 ])
386
+ vivid = Chef::Node::VividMash.new(
387
+ { "one" => { "two" => { "three" => "four" } }, "array" => attr_array },
388
+ root
389
+ )
390
+ vivid["array"] << 3
391
+ end
392
+
393
+ it "does not send reset_cache with nil if VividMashi nested key" do
394
+ # We're checking that __path__ is used to avoid full DeepMergeCache invalidation,
395
+ # and that it is invalidating the top-level key
396
+ expect(root).to receive(:reset_cache).with("array")
397
+ expect(root).not_to receive(:reset_cache).with(nil)
398
+
399
+ attr_array = Chef::Node::AttrArray.new([ 0, 1, 2 ])
400
+ vivid = Chef::Node::VividMash.new(
401
+ {
402
+ "one" => { "two" => { "three" => "four" } },
403
+ "array" => { "nested" => attr_array },
404
+ },
405
+ root
406
+ )
407
+ vivid["array"]["nested"] << 3
408
+ end
377
409
  end
378
410
 
379
411
  context "#[]=" do
380
412
  it "assigning a Hash into an array converts it to VividMash" do
413
+ expect(root).to receive(:reset_cache).with(nil)
381
414
  array[0] = { "zero" => "zero2" }
382
415
  expect(array[0].class).to eql(Chef::Node::VividMash)
383
416
  end
@@ -385,6 +418,7 @@ describe Chef::Node::AttrArray do
385
418
 
386
419
  context "#push" do
387
420
  it "pushing a Hash into an array converts it to VividMash" do
421
+ expect(root).to receive(:reset_cache).with(nil)
388
422
  array.push({ "three" => "four" })
389
423
  expect(array[3].class).to eql(Chef::Node::VividMash)
390
424
  end
@@ -392,6 +426,7 @@ describe Chef::Node::AttrArray do
392
426
 
393
427
  context "#unshift" do
394
428
  it "unshifting a Hash into an array converts it to VividMash" do
429
+ expect(root).to receive(:reset_cache).with(nil)
395
430
  array.unshift({ "zero" => "zero2" })
396
431
  expect(array[0].class).to eql(Chef::Node::VividMash)
397
432
  end
@@ -399,6 +434,7 @@ describe Chef::Node::AttrArray do
399
434
 
400
435
  context "#insert" do
401
436
  it "inserting a Hash into an array converts it to VividMash" do
437
+ expect(root).to receive(:reset_cache).with(nil)
402
438
  array.insert(1, { "zero" => "zero2" })
403
439
  expect(array[1].class).to eql(Chef::Node::VividMash)
404
440
  end
@@ -406,6 +442,7 @@ describe Chef::Node::AttrArray do
406
442
 
407
443
  context "#collect!" do
408
444
  it "converts Hashes" do
445
+ expect(root).to receive(:reset_cache).at_least(:once).with(nil)
409
446
  array.collect! { |x| { "zero" => "zero2" } }
410
447
  expect(array[1].class).to eql(Chef::Node::VividMash)
411
448
  end
@@ -413,6 +450,7 @@ describe Chef::Node::AttrArray do
413
450
 
414
451
  context "#map!" do
415
452
  it "converts Hashes" do
453
+ expect(root).to receive(:reset_cache).with(nil)
416
454
  array.map! { |x| { "zero" => "zero2" } }
417
455
  expect(array[1].class).to eql(Chef::Node::VividMash)
418
456
  end
@@ -420,6 +458,7 @@ describe Chef::Node::AttrArray do
420
458
 
421
459
  context "#compact!" do
422
460
  it "VividMashes remain VividMashes" do
461
+ expect(root).to receive(:reset_cache).with(nil)
423
462
  array = Chef::Node::AttrArray.new(
424
463
  [ nil, { "one" => "two" }, nil ],
425
464
  root
@@ -432,6 +471,7 @@ describe Chef::Node::AttrArray do
432
471
 
433
472
  context "#fill" do
434
473
  it "inserts VividMashes for Hashes" do
474
+ expect(root).to receive(:reset_cache).at_least(:once).with(nil)
435
475
  array.fill({ "one" => "two" })
436
476
  expect(array[0].class).to eql(Chef::Node::VividMash)
437
477
  end
@@ -439,6 +479,7 @@ describe Chef::Node::AttrArray do
439
479
 
440
480
  context "#flatten!" do
441
481
  it "flattens sub-arrays maintaining VividMashes in them" do
482
+ expect(root).to receive(:reset_cache).with(nil)
442
483
  array = Chef::Node::AttrArray.new(
443
484
  [ [ { "one" => "two" } ], [ { "one" => "two" } ] ],
444
485
  root
@@ -451,6 +492,7 @@ describe Chef::Node::AttrArray do
451
492
 
452
493
  context "#replace" do
453
494
  it "replaces the array converting hashes to mashes" do
495
+ expect(root).to receive(:reset_cache).with(nil)
454
496
  array.replace([ { "foo" => "bar" } ])
455
497
  expect(array[0].class).to eql(Chef::Node::VividMash)
456
498
  end
@@ -246,33 +246,43 @@ C5986B4F1257FFA86632CBA746181433FBB75451
246
246
  describe "#build_repo" do
247
247
  it "creates a repository string" do
248
248
  target = "deb http://test/uri unstable main\n"
249
- expect(provider.build_repo("http://test/uri", "unstable", "main", false, nil)).to eql(target)
249
+ expect(provider.build_repo("http://test/uri", "unstable", "main", false, nil, [])).to eql(target)
250
250
  end
251
251
 
252
252
  it "creates a repository string with spaces" do
253
253
  target = "deb http://test/uri%20with%20spaces unstable main\n"
254
- expect(provider.build_repo("http://test/uri with spaces", "unstable", "main", false, nil)).to eql(target)
254
+ expect(provider.build_repo("http://test/uri with spaces", "unstable", "main", false, nil, [])).to eql(target)
255
255
  end
256
256
 
257
257
  it "creates a repository string with no distribution" do
258
258
  target = "deb http://test/uri main\n"
259
- expect(provider.build_repo("http://test/uri", nil, "main", false, nil)).to eql(target)
259
+ expect(provider.build_repo("http://test/uri", nil, "main", false, nil, [])).to eql(target)
260
260
  end
261
261
 
262
262
  it "creates a repository string with source" do
263
263
  target = "deb http://test/uri unstable main\ndeb-src http://test/uri unstable main\n"
264
- expect(provider.build_repo("http://test/uri", "unstable", "main", false, nil, true)).to eql(target)
264
+ expect(provider.build_repo("http://test/uri", "unstable", "main", false, nil, [], true)).to eql(target)
265
265
  end
266
266
 
267
- it "creates a repository string with options" do
267
+ it "creates a repository string with trusted" do
268
268
  target = "deb [trusted=yes] http://test/uri unstable main\n"
269
- expect(provider.build_repo("http://test/uri", "unstable", "main", true, nil)).to eql(target)
269
+ expect(provider.build_repo("http://test/uri", "unstable", "main", true, nil, [])).to eql(target)
270
+ end
271
+
272
+ it "creates a repository string with custom options" do
273
+ target = "deb [by-hash=no] http://test/uri unstable main\n"
274
+ expect(provider.build_repo("http://test/uri", "unstable", "main", false, nil, ["by-hash=no"])).to eql(target)
275
+ end
276
+
277
+ it "creates a repository string with trusted, arch, and custom options" do
278
+ target = "deb [arch=amd64 trusted=yes by-hash=no] http://test/uri unstable main\n"
279
+ expect(provider.build_repo("http://test/uri", "unstable", "main", true, "amd64", ["by-hash=no"])).to eql(target)
270
280
  end
271
281
 
272
282
  it "handles a ppa repo" do
273
283
  target = "deb http://ppa.launchpad.net/chef/main/ubuntu unstable main\n"
274
284
  expect(provider).to receive(:make_ppa_url).with("ppa:chef/main").and_return("http://ppa.launchpad.net/chef/main/ubuntu")
275
- expect(provider.build_repo("ppa:chef/main", "unstable", "main", false, nil)).to eql(target)
285
+ expect(provider.build_repo("ppa:chef/main", "unstable", "main", false, nil, [])).to eql(target)
276
286
  end
277
287
  end
278
288
  end
@@ -197,7 +197,7 @@ describe Chef::Provider::Launchd do
197
197
  describe "with an :delete action" do
198
198
  describe "and the ld file is present" do
199
199
  before(:each) do
200
- allow(File).to receive(:exists?).and_return(true)
200
+ allow(File).to receive(:exist?).and_return(true)
201
201
  allow(provider).to receive(
202
202
  :manage_service
203
203
  ).with(:disable).and_return(true)
@@ -218,7 +218,7 @@ describe Chef::Provider::Launchd do
218
218
 
219
219
  describe "and the ld file is not present" do
220
220
  before(:each) do
221
- allow(File).to receive(:exists?).and_return(false)
221
+ allow(File).to receive(:exist?).and_return(false)
222
222
  allow(provider).to receive(
223
223
  :manage_plist
224
224
  ).with(:delete).and_return(true)
@@ -65,8 +65,8 @@ describe Chef::Provider::Mount::Aix do
65
65
 
66
66
  @provider = Chef::Provider::Mount::Aix.new(@new_resource, @run_context)
67
67
 
68
- allow(::File).to receive(:exists?).with("/dev/sdz1").and_return true
69
- allow(::File).to receive(:exists?).with("/tmp/foo").and_return true
68
+ allow(::File).to receive(:exist?).with("/dev/sdz1").and_return true
69
+ allow(::File).to receive(:exist?).with("/tmp/foo").and_return true
70
70
  end
71
71
 
72
72
  def stub_mounted(provider, mounted_output)
@@ -22,10 +22,11 @@ describe Chef::Provider::Mount::Linux do
22
22
  end
23
23
 
24
24
  before(:each) do
25
- allow(::File).to receive(:exists?).with("/dev/sdz1").and_return true
26
- allow(::File).to receive(:exists?).with("/tmp/foo").and_return true
27
- allow(::File).to receive(:exists?).with("//192.168.11.102/Share/backup").and_return true
28
- allow(::File).to receive(:exists?).with("//192.168.11.102/Share/backup folder").and_return true
25
+ allow(::File).to receive(:exist?).with("/etc/fstab").and_call_original
26
+ allow(::File).to receive(:exist?).with("/dev/sdz1").and_return true
27
+ allow(::File).to receive(:exist?).with("/tmp/foo").and_return true
28
+ allow(::File).to receive(:exist?).with("//192.168.11.102/Share/backup").and_return true
29
+ allow(::File).to receive(:exist?).with("//192.168.11.102/Share/backup folder").and_return true
29
30
  allow(::File).to receive(:realpath).with("/dev/sdz1").and_return "/dev/sdz1"
30
31
  allow(::File).to receive(:realpath).with("/tmp/foo").and_return "/tmp/foo"
31
32
  end
@@ -127,4 +128,4 @@ describe Chef::Provider::Mount::Linux do
127
128
  end
128
129
  end
129
130
 
130
- end
131
+ end
@@ -34,8 +34,8 @@ describe Chef::Provider::Mount::Mount do
34
34
 
35
35
  @provider = Chef::Provider::Mount::Mount.new(@new_resource, @run_context)
36
36
 
37
- allow(::File).to receive(:exists?).with("/dev/sdz1").and_return true
38
- allow(::File).to receive(:exists?).with("/tmp/foo").and_return true
37
+ allow(::File).to receive(:exist?).with("/dev/sdz1").and_return true
38
+ allow(::File).to receive(:exist?).with("/tmp/foo").and_return true
39
39
  allow(::File).to receive(:realpath).with("/dev/sdz1").and_return "/dev/sdz1"
40
40
  allow(::File).to receive(:realpath).with("/tmp/foo").and_return "/tmp/foo"
41
41
  end
@@ -83,12 +83,12 @@ describe Chef::Provider::Mount::Mount do
83
83
  end
84
84
 
85
85
  it "should raise an error if the mount device does not exist" do
86
- allow(::File).to receive(:exists?).with("/dev/sdz1").and_return false
86
+ allow(::File).to receive(:exist?).with("/dev/sdz1").and_return false
87
87
  expect { @provider.load_current_resource; @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
88
88
  end
89
89
 
90
90
  it "should not call mountable? with load_current_resource - CHEF-1565" do
91
- allow(::File).to receive(:exists?).with("/dev/sdz1").and_return false
91
+ allow(::File).to receive(:exist?).with("/dev/sdz1").and_return false
92
92
  expect(@provider).to receive(:mounted?).and_return(true)
93
93
  expect(@provider).to receive(:enabled?).and_return(true)
94
94
  expect(@provider).not_to receive(:mountable?)
@@ -100,12 +100,12 @@ describe Chef::Provider::Mount::Mount do
100
100
  @new_resource.device_type :uuid
101
101
  @new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a"
102
102
  expect(@provider).to receive(:shell_out_compacted).with("/sbin/findfs", "UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status)
103
- expect(::File).to receive(:exists?).with("").and_return(false)
103
+ expect(::File).to receive(:exist?).with("").and_return(false)
104
104
  expect { @provider.load_current_resource; @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
105
105
  end
106
106
 
107
107
  it "should raise an error if the mount point does not exist" do
108
- allow(::File).to receive(:exists?).with("/tmp/foo").and_return false
108
+ allow(::File).to receive(:exist?).with("/tmp/foo").and_return false
109
109
  expect { @provider.load_current_resource; @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
110
110
  end
111
111
 
@@ -521,8 +521,8 @@ describe Chef::Provider::Mount::Mount do
521
521
 
522
522
  @provider = Chef::Provider::Mount::Mount.new(@new_resource, @run_context)
523
523
 
524
- allow(::File).to receive(:exists?).with("cephserver:6789:/").and_return true
525
- allow(::File).to receive(:exists?).with("/tmp/bar").and_return true
524
+ allow(::File).to receive(:exist?).with("cephserver:6789:/").and_return true
525
+ allow(::File).to receive(:exist?).with("/tmp/bar").and_return true
526
526
  allow(::File).to receive(:realpath).with("cephserver:6789:/").and_return "cephserver:6789:/"
527
527
  allow(::File).to receive(:realpath).with("/tmp/bar").and_return "/tmp/foo"
528
528
  end
@@ -49,6 +49,10 @@ describe Chef::Provider::Package::Chocolatey, :windows_only do
49
49
  allow(provider).to receive(:shell_out_compacted!).with(choco_exe, "list", "-l", "-r", { returns: [0, 2], timeout: timeout }).and_return(local_list_obj)
50
50
  end
51
51
 
52
+ after(:each) do
53
+ described_class.instance_variable_set(:@get_choco_version, nil)
54
+ end
55
+
52
56
  def allow_remote_list(package_names, args = nil)
53
57
  remote_list_stdout = <<~EOF
54
58
  Chocolatey v0.9.9.11
@@ -61,9 +65,9 @@ describe Chef::Provider::Package::Chocolatey, :windows_only do
61
65
  remote_list_obj = double(stdout: remote_list_stdout)
62
66
  package_names.each do |pkg|
63
67
  if args
64
- allow(provider).to receive(:shell_out_compacted!).with(choco_exe, "list", "-r", pkg, *args, { returns: [0, 2], timeout: timeout }).and_return(remote_list_obj)
68
+ allow(provider).to receive(:shell_out_compacted!).with(choco_exe, described_class.query_command, "-r", pkg, *args, { returns: [0, 2], timeout: timeout }).and_return(remote_list_obj)
65
69
  else
66
- allow(provider).to receive(:shell_out_compacted!).with(choco_exe, "list", "-r", pkg, { returns: [0, 2], timeout: timeout }).and_return(remote_list_obj)
70
+ allow(provider).to receive(:shell_out_compacted!).with(choco_exe, described_class.query_command, "-r", pkg, { returns: [0, 2], timeout: timeout }).and_return(remote_list_obj)
67
71
  end
68
72
  end
69
73
  end
@@ -78,6 +82,18 @@ describe Chef::Provider::Package::Chocolatey, :windows_only do
78
82
  end
79
83
  end
80
84
 
85
+ describe "choco searches change with the version" do
86
+ it "Choco V1 uses List" do
87
+ allow(described_class).to receive(:get_choco_version).and_return("1.4.0")
88
+ expect(provider.query_command).to eql("list")
89
+ end
90
+
91
+ it "Choco V2 uses Search" do
92
+ allow(described_class).to receive(:get_choco_version).and_return("2.1.0")
93
+ expect(provider.query_command).to eql("search")
94
+ end
95
+ end
96
+
81
97
  describe "#candidate_version" do
82
98
  it "should set the candidate_version to the latest version when not pinning" do
83
99
  allow_remote_list(["git"])
@@ -150,7 +166,7 @@ describe Chef::Provider::Package::Chocolatey, :windows_only do
150
166
  new_resource.package_name("package-does-not-exist")
151
167
  new_resource.returns([0])
152
168
  allow(provider).to receive(:shell_out_compacted!)
153
- .with(choco_exe, "list", "-r", new_resource.package_name.first, { returns: new_resource.returns, timeout: timeout })
169
+ .with(choco_exe, described_class.query_command, "-r", new_resource.package_name.first, { returns: new_resource.returns, timeout: timeout })
154
170
  .and_raise(Mixlib::ShellOut::ShellCommandFailed, "Expected process to exit with [0], but received '2'")
155
171
  expect { provider.send(:available_packages) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed, "Expected process to exit with [0], but received '2'")
156
172
  end
@@ -41,7 +41,7 @@ describe Chef::Provider::Package::Rpm do
41
41
  let(:rpm_q_status) { instance_double("Mixlib::ShellOut", exitstatus: rpm_q_exitstatus, stdout: rpm_q_stdout) }
42
42
 
43
43
  before(:each) do
44
- allow(::File).to receive(:exist?).with("PLEASE STUB File.exists? EXACTLY").and_return(true)
44
+ allow(::File).to receive(:exist?).with("PLEASE STUB File.exist? EXACTLY").and_return(true)
45
45
 
46
46
  # Ensure all shell out usage is stubbed with exact arguments
47
47
  allow(provider).to receive(:shell_out_compacted!).with("PLEASE STUB YOUR SHELLOUT CALLS").and_return(nil)
@@ -412,7 +412,7 @@ describe Chef::Provider::Package::Rpm do
412
412
 
413
413
  let(:new_resource) do
414
414
  # When we pass a source in as the name, then #initialize in the
415
- # provider will call File.exists?. Because of the ordering in our
415
+ # provider will call File.exist?. Because of the ordering in our
416
416
  # let() bindings and such, we have to set the stub here and not in a
417
417
  # before block.
418
418
  allow(::File).to receive(:exist?).with(package_source).and_return(true)
@@ -491,4 +491,14 @@ describe Chef::Provider::Package::Zypper do
491
491
  provider.remove_package(%w{emacs vim}, ["1.0", "2.0"])
492
492
  end
493
493
  end
494
+
495
+ describe "resolve_available_version" do
496
+ it "should return correct version if multiple packages are shown" do
497
+ status = double(stdout: "S | Name | Type | Version | Arch | Repository\n---+--------------------------+---------+---------------------+--------+-------------------------------------------------------------\n | apache2-mod_wsgi | package | 4.7.1-150400.3.3.1 | x86_64 | Update repository with updates from SUSE Linux Enterprise 15\n | apache2-mod_wsgi | package | 4.7.1-150400.1.52 | x86_64 | Main Repository\ni+ | apache2-mod_wsgi-python3 | package | 4.5.18-150000.4.6.1 | x86_64 | Update repository with updates from SUSE Linux Enterprise 15\nv | apache2-mod_wsgi-python3 | package | 4.5.18-4.3.1 | x86_64 | Main Repository\n", exitstatus: 0)
498
+
499
+ allow(provider).to receive(:shell_out_compacted!).and_return(status)
500
+ result = provider.send(:resolve_available_version, "apache2-mod_wsgi-python3", nil)
501
+ expect(result).to eq("4.5.18-150000.4.6.1")
502
+ end
503
+ end
494
504
  end