chef 18.7.6 → 18.8.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -3
  3. data/Rakefile +1 -0
  4. data/chef.gemspec +11 -3
  5. data/lib/chef/cookbook_version.rb +34 -0
  6. data/lib/chef/http/basic_client.rb +1 -0
  7. data/lib/chef/http.rb +1 -0
  8. data/lib/chef/provider/package/apt.rb +1 -1
  9. data/lib/chef/provider/package/bff.rb +5 -0
  10. data/lib/chef/provider/package/cab.rb +9 -0
  11. data/lib/chef/provider/package/chocolatey.rb +6 -1
  12. data/lib/chef/provider/package/deb.rb +1 -1
  13. data/lib/chef/provider/package/dnf.rb +3 -3
  14. data/lib/chef/provider/package/habitat.rb +9 -0
  15. data/lib/chef/provider/package/homebrew.rb +9 -0
  16. data/lib/chef/provider/package/ips.rb +5 -0
  17. data/lib/chef/provider/package/macports.rb +9 -0
  18. data/lib/chef/provider/package/msu.rb +9 -0
  19. data/lib/chef/provider/package/openbsd.rb +7 -2
  20. data/lib/chef/provider/package/pacman.rb +9 -0
  21. data/lib/chef/provider/package/paludis.rb +9 -0
  22. data/lib/chef/provider/package/portage.rb +9 -0
  23. data/lib/chef/provider/package/powershell.rb +6 -1
  24. data/lib/chef/provider/package/rpm.rb +3 -3
  25. data/lib/chef/provider/package/rubygems.rb +9 -0
  26. data/lib/chef/provider/package/smartos.rb +9 -0
  27. data/lib/chef/provider/package/snap.rb +6 -1
  28. data/lib/chef/provider/package/solaris.rb +5 -0
  29. data/lib/chef/provider/package/windows.rb +5 -0
  30. data/lib/chef/provider/package/yum.rb +3 -3
  31. data/lib/chef/provider/package/zypper.rb +5 -0
  32. data/lib/chef/recipe.rb +20 -0
  33. data/lib/chef/resource/apt_package.rb +5 -0
  34. data/lib/chef/resource/apt_repository.rb +48 -21
  35. data/lib/chef/resource/archive_file.rb +49 -2
  36. data/lib/chef/resource/dnf_package.rb +5 -0
  37. data/lib/chef/resource/dpkg_package.rb +5 -0
  38. data/lib/chef/resource/ohai.rb +10 -0
  39. data/lib/chef/resource/package.rb +5 -0
  40. data/lib/chef/resource/rpm_package.rb +5 -0
  41. data/lib/chef/resource/yum_package.rb +5 -0
  42. data/lib/chef/shell.rb +13 -4
  43. data/lib/chef/version.rb +1 -1
  44. data/lib/chef/win32/version.rb +2 -1
  45. data/spec/functional/resource/git_spec.rb +2 -1
  46. data/spec/integration/client/open_ssl_spec.rb +7 -2
  47. data/spec/spec_helper.rb +1 -0
  48. data/spec/unit/cookbook_version_spec.rb +39 -0
  49. data/spec/unit/node/attribute_spec.rb +1 -1
  50. data/spec/unit/provider/apt_repository_spec.rb +85 -8
  51. data/spec/unit/provider/package/rpm_spec.rb +10 -10
  52. data/spec/unit/recipe_spec.rb +51 -0
  53. data/spec/unit/resource/apt_package_spec.rb +5 -0
  54. data/spec/unit/resource/dnf_package_spec.rb +6 -0
  55. data/spec/unit/resource/ohai_spec.rb +73 -0
  56. data/spec/unit/resource/yum_package_spec.rb +9 -0
  57. data/spec/unit/resource_reporter_spec.rb +0 -58
  58. data/spec/unit/shell_spec.rb +31 -11
  59. data/tasks/rspec.rb +1 -1
  60. metadata +48 -14
@@ -237,17 +237,28 @@ class Chef
237
237
  valid
238
238
  end
239
239
 
240
- # validate the key against the a gpg keyring to see if that version is expired
240
+ # validate the key against the gpg keyring to see if that version is expired or revoked
241
241
  # @param [String] key
242
+ # @param [String] keyring
242
243
  #
243
- # @return [Boolean] is the key valid or not
244
+ # @return [Boolean] if the key valid or not
244
245
  def keyring_key_is_valid?(keyring, key)
245
- valid = shell_out("gpg", "--no-default-keyring", "--keyring", keyring, "--list-public-keys", key).stdout.each_line.none?(/\[(expired|revoked):/)
246
+ out = shell_out("gpg", "--no-default-keyring", "--keyring", keyring, "--list-public-keys", key)
247
+ valid = out.exitstatus == 0 && out.stdout.each_line.none?(/\[(expired|revoked):/)
246
248
 
247
249
  logger.debug "key #{key} #{valid ? "is valid" : "is not valid"}"
248
250
  valid
249
251
  end
250
252
 
253
+ # validate the key against the gpg keyring to see if the key is present
254
+ # @param [String] key
255
+ # @param [String] keyring
256
+ #
257
+ # @return [Boolean] if the key present
258
+ def keyring_key_is_present?(keyring, key)
259
+ shell_out(*%W{gpg --no-default-keyring --keyring #{keyring} --list-public-keys --with-fingerprint --with-colons #{key}}).exitstatus == 0
260
+ end
261
+
251
262
  # return the specified cookbook name or the cookbook containing the
252
263
  # resource.
253
264
  #
@@ -284,7 +295,7 @@ class Chef
284
295
  # @raise [Chef::Exceptions::FileNotFound] Key isn't remote or found in the current run
285
296
  #
286
297
  # @return [Symbol] :remote_file or :cookbook_file
287
- def key_type(uri)
298
+ def key_resource_type(uri)
288
299
  if uri.start_with?("http")
289
300
  :remote_file
290
301
  elsif has_cookbook_file?(uri)
@@ -307,29 +318,46 @@ class Chef
307
318
  # @return [void]
308
319
  def install_key_from_uri(key)
309
320
  key_name = key.gsub(/[^0-9A-Za-z\-]/, "_")
310
- keyfile_path = ::File.join(Chef::Config[:file_cache_path], key_name)
321
+ keyfile_tmp_path = ::File.join(Chef::Config[:file_cache_path], key_name)
322
+ keyfile_path = keyring_path
311
323
  tmp_dir = Dir.mktmpdir(".gpg")
312
324
  at_exit { FileUtils.remove_entry(tmp_dir) }
313
325
 
314
326
  if new_resource.signed_by
315
- keyfile_path = keyring_path
316
-
317
327
  directory "/etc/apt/keyrings" do
318
328
  mode "0755"
319
329
  end
320
- end
321
330
 
322
- declare_resource(key_type(key), keyfile_path) do
323
- source key
324
- mode "0644"
325
- sensitive new_resource.sensitive
326
- action :create
327
- verify "gpg --homedir #{tmp_dir} %{path}"
328
- end
331
+ declare_resource(key_resource_type(key), keyfile_tmp_path) do
332
+ source key
333
+ mode "0644"
334
+ sensitive new_resource.sensitive
335
+ action :create
336
+ verify "gpg --homedir #{tmp_dir} %{path}"
337
+ notifies :delete, "file[#{keyfile_path}]", :immediately
338
+ notifies :run, "execute[dearmor #{keyfile_path}]", :immediately
339
+ end
340
+
341
+ execute "dearmor #{keyfile_path}" do
342
+ command [ "gpg", "--batch", "--yes", "--dearmor", "-o", keyfile_path, keyfile_tmp_path ]
343
+ default_env true
344
+ sensitive new_resource.sensitive
345
+ action :nothing
346
+ only_if { !::File.exist?(keyfile_path) || ::File.read(keyfile_path).include?("-----BEGIN PGP PUBLIC KEY BLOCK-----") }
347
+ end
348
+
349
+ file keyfile_path do
350
+ mode "0644"
351
+ end
352
+ else
353
+ declare_resource(key_resource_type(key), keyfile_path) do
354
+ source key
355
+ mode "0644"
356
+ sensitive new_resource.sensitive
357
+ action :create
358
+ verify "gpg --homedir #{tmp_dir} %{path}"
359
+ end
329
360
 
330
- # If signed by is true, then we don't need to
331
- # add to the default keyring
332
- unless new_resource.signed_by
333
361
  execute "apt-key add #{keyfile_path}" do
334
362
  command [ "apt-key", "add", keyfile_path ]
335
363
  default_env true
@@ -403,8 +431,7 @@ class Chef
403
431
  default_env true
404
432
  sensitive new_resource.sensitive
405
433
  not_if do
406
- present = shell_out(*%W{gpg --no-default-keyring --keyring #{keyring} --list-public-keys --with-fingerprint --with-colons #{key}}).exitstatus != 0
407
- present && keyring_key_is_valid?(keyring, key.upcase)
434
+ keyring_key_is_present?(keyring, key.upcase) && keyring_key_is_valid?(keyring, key.upcase)
408
435
  end
409
436
  notifies :run, "execute[apt-cache gencaches]", :immediately
410
437
  end
@@ -420,7 +447,7 @@ class Chef
420
447
  # @return [void]
421
448
  def install_ppa_key(owner, repo)
422
449
  url = "https://launchpad.net/api/1.0/~#{owner}/+archive/#{repo}"
423
- key_id = Chef::HTTP::Simple.new(url).get("signing_key_fingerprint").delete('"')
450
+ key_id = Chef::HTTP::Simple.new(url, {}).get("signing_key_fingerprint").delete('"')
424
451
  install_key_from_keyserver(key_id, "keyserver.ubuntu.com")
425
452
  rescue Net::HTTPClientException => e
426
453
  raise "Could not access Launchpad ppa API: #{e.message}"
@@ -21,10 +21,57 @@
21
21
  require_relative "../resource"
22
22
  require "fileutils" unless defined?(FileUtils)
23
23
  begin
24
+ # The explicit call to FFI::DynamicLibrary.open was added to address an issue specific to the Habitat packaging of Chef Infra Client on windows.
25
+ # In the Habitat environment, the libarchive library (archive.dll) is installed in a non-standard location that is not included
26
+ # in the default search paths used by the FFI gem to locate dynamic libraries. The default search paths for FFI are:
27
+ # - <system library path>
28
+ # - /usr/lib
29
+ # - /usr/local/lib
30
+ # - /opt/local/lib
31
+ # These paths do not account for the Habitat package structure, where libraries are installed in isolated directories under
32
+ # the Habitat package path (e.g., C:/hab/pkgs/core/libarchive/<version>/bin on Windows).
33
+ #
34
+ # Without explicitly loading archive.dll using FFI::DynamicLibrary.open, the ffi-libarchive gem fails to locate and load the library,
35
+ # resulting in runtime errors when attempting to use the archive_file resource.
36
+ #
37
+ # This code dynamically determines the path to archive.dll using the Habitat CLI (`hab pkg path core/libarchive`) and explicitly
38
+ # loads the library using FFI::DynamicLibrary.open. This ensures that the library is correctly loaded in the Habitat environment.
39
+ #
40
+ # Note: This logic is gated by a check for Habitat-specific environment variables (HAB_CACHE_SRC_PATH or HAB_PKG_PATH) to ensure
41
+ # that it is only applied in Habitat runs. For other environments (e.g., Omnibus, plain gem installations, or git checkouts),
42
+ # the default behavior of FFI is sufficient, as the libraries are installed in standard locations or embedded paths that are
43
+ # included in the default search paths.
44
+ if RUBY_PLATFORM.match?(/mswin|mingw|windows/) && (ENV["HAB_CACHE_SRC_PATH"] || ENV["HAB_PKG_PATH"])
45
+ require "ffi" unless defined?(FFI)
46
+ require "open3" unless defined?(Open3)
47
+ # Dynamically determine the path to the core/libarchive package
48
+ stdout, stderr, status = Open3.capture3("hab pkg path core/libarchive")
49
+ unless status.success?
50
+ Chef::Log.debug("Failed to determine Habitat libarchive path: #{stderr}")
51
+ return
52
+ end
53
+
54
+ habitat_libarchive_path = File.join(stdout.strip.tr("\\", "/"), "bin")
55
+ unless Dir.exist?(habitat_libarchive_path)
56
+ Chef::Log.debug("Habitat libarchive path not found: #{habitat_libarchive_path}")
57
+ return
58
+ end
59
+
60
+ archive_dll_path = File.join(habitat_libarchive_path, "archive.dll")
61
+ unless File.exist?(archive_dll_path)
62
+ Chef::Log.debug("archive.dll not found in Habitat path: #{habitat_libarchive_path}")
63
+ return
64
+ end
65
+
66
+ FFI::DynamicLibrary.open(archive_dll_path, FFI::DynamicLibrary::RTLD_LAZY) # Explicitly load the DLL
67
+ Chef::Log.debug("Explicitly loaded archive.dll from Habitat path: #{archive_dll_path}")
68
+ end
69
+
24
70
  # ffi-libarchive must be eager loaded see: https://github.com/chef/chef/issues/12228
25
71
  require "ffi-libarchive" unless defined?(Archive::Reader)
26
- rescue LoadError
27
- STDERR.puts "ffi-libarchive could not be loaded, libarchive is probably not installed on system, archive_file will not be available"
72
+ rescue LoadError => e
73
+ STDERR.puts "ffi-libarchive could not be loaded: #{e.message}"
74
+ STDERR.puts "libarchive is probably not installed on system, archive_file will not be available"
28
75
  end
29
76
 
30
77
  class Chef
@@ -71,6 +71,11 @@ class Chef
71
71
  description: "Allow downgrading a package to satisfy requested version requirements.",
72
72
  default: true,
73
73
  desired_state: false
74
+
75
+ property :environment, Hash,
76
+ introduced: "18.8",
77
+ description: "A Hash of environment variables in the form of {'ENV_VARIABLE' => 'VALUE'} to be set before running the command.",
78
+ default: {}, desired_state: false
74
79
  end
75
80
  end
76
81
  end
@@ -41,6 +41,11 @@ class Chef
41
41
  description: "Allow downgrading a package to satisfy requested version requirements.",
42
42
  default: true,
43
43
  desired_state: false
44
+
45
+ property :environment, Hash,
46
+ introduced: "18.8",
47
+ description: "A Hash of environment variables in the form of {'ENV_VARIABLE' => 'VALUE'} to be set before running the command.",
48
+ default: {}, desired_state: false
44
49
  end
45
50
  end
46
51
  end
@@ -84,6 +84,16 @@ class Chef
84
84
  converge_by("re-run ohai and merge results into node attributes") do
85
85
  ohai = ::Ohai::System.new
86
86
 
87
+ # Load any custom plugins from cookbooks if they exist
88
+ # This ensures that cookbook-provided Ohai plugins are available
89
+ # when the resource reloads Ohai data
90
+ ohai_plugin_path = Chef::Config[:ohai_segment_plugin_path]
91
+ if ohai_plugin_path && Dir.exist?(ohai_plugin_path) && !Dir.empty?(ohai_plugin_path)
92
+ # Configure Ohai to load plugins from the cookbook segment path
93
+ ohai.config[:plugin_path] << ohai_plugin_path
94
+ logger.trace("Added cookbook plugin path to ohai: #{ohai_plugin_path}")
95
+ end
96
+
87
97
  # If new_resource.plugin is nil, ohai will reload all the plugins
88
98
  # Otherwise it will only reload the specified plugin
89
99
  # Note that any changes to plugins, or new plugins placed on
@@ -60,6 +60,11 @@ class Chef
60
60
  description: "The amount of time (in seconds) to wait before timing out.",
61
61
  desired_state: false
62
62
 
63
+ property :environment, Hash,
64
+ introduced: "18.8",
65
+ description: "A Hash of environment variables in the form of {'ENV_VARIABLE' => 'VALUE'} to be set before running the command.",
66
+ desired_state: false
67
+
63
68
  end
64
69
  end
65
70
  end
@@ -39,6 +39,11 @@ class Chef
39
39
 
40
40
  property :version, String,
41
41
  description: "The version of a package to be installed or upgraded."
42
+
43
+ property :environment, Hash,
44
+ introduced: "18.8",
45
+ description: "A Hash of environment variables in the form of {'ENV_VARIABLE' => 'VALUE'} to be set before running the command.",
46
+ default: {}, desired_state: false
42
47
  end
43
48
  end
44
49
  end
@@ -155,6 +155,11 @@ class Chef
155
155
 
156
156
  property :yum_binary, String,
157
157
  description: "The path to the yum binary."
158
+
159
+ property :environment, Hash,
160
+ introduced: "18.8",
161
+ description: "A Hash of environment variables in the form of {'ENV_VARIABLE' => 'VALUE'} to be set before running the command.",
162
+ default: {}, desired_state: false
158
163
  end
159
164
  end
160
165
  end
data/lib/chef/shell.rb CHANGED
@@ -132,11 +132,20 @@ module Shell
132
132
  irb_conf[:IRB_RC] = lambda do |conf|
133
133
  m = conf.main
134
134
 
135
- conf.prompt_c = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} > "
136
- conf.return_format = " => %s \n"
135
+ # remove this clause for any Chef version that has upgraded to ruby >= 3.3.0
136
+ if RUBY_VERSION >= "3.3.0"
137
+ conf.prompt_c = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} > "
138
+ conf.prompt_n = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} ?> "
139
+ conf.prompt_s = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)}%l> "
140
+ else
141
+ # there's a bug if you use a left arrow and the alternative prompts
142
+ # ... looks like 3.1.0 < version < 3.3.0 have it, from my manual testing
143
+ conf.prompt_c = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} (#{Chef::VERSION})> "
144
+ conf.prompt_n = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)}(#{Chef::VERSION})?> "
145
+ conf.prompt_s = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)}(#{Chef::VERSION})%l> "
146
+ end
137
147
  conf.prompt_i = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} (#{Chef::VERSION})> "
138
- conf.prompt_n = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} ?> "
139
- conf.prompt_s = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)}%l> "
148
+ conf.return_format = " => %s \n"
140
149
  conf.use_tracer = false
141
150
  conf.instance_variable_set(:@use_multiline, false)
142
151
  conf.instance_variable_set(:@use_singleline, false)
data/lib/chef/version.rb CHANGED
@@ -23,7 +23,7 @@ require_relative "version_string"
23
23
 
24
24
  class Chef
25
25
  CHEF_ROOT = File.expand_path("..", __dir__)
26
- VERSION = Chef::VersionString.new("18.7.6")
26
+ VERSION = Chef::VersionString.new("18.8.9")
27
27
  end
28
28
 
29
29
  #
@@ -49,7 +49,8 @@ class Chef
49
49
  private_class_method :method_name_from_marketing_name
50
50
 
51
51
  WIN_VERSIONS = {
52
- "Windows Server 2022" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION && build_number >= 20348 } },
52
+ "Windows Server 2025" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION && build_number >= 25398 } },
53
+ "Windows Server 2022" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION && build_number >= 20348 && build_number < 25398 } },
53
54
  "Windows Server 2019" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION && build_number >= 17763 && build_number < 20348 } },
54
55
  "Windows 11" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type == VER_NT_WORKSTATION && build_number >= 22000 } },
55
56
  "Windows 10" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type == VER_NT_WORKSTATION && build_number >= 19044 && build_number < 22000 } },
@@ -263,7 +263,8 @@ describe Chef::Resource::Git do
263
263
 
264
264
  context "when dealing with a repo with a degenerate tag named 'HEAD'" do
265
265
  before do
266
- shell_out!("git", "tag", "-m\"degenerate tag\"", "HEAD", "ed181b3419b6f489bedab282348162a110d6d3a1", cwd: origin_repo)
266
+ shell_out!("git", "tag", "-m\"degenerate tag\"", "HEAD_TAG", "ed181b3419b6f489bedab282348162a110d6d3a1", cwd: origin_repo)
267
+ # The original tag name of 'HEAD' caused errors in new git clients since it's a reserved name.
267
268
  end
268
269
 
269
270
  it "checks out the (master) HEAD revision and ignores the tag" do
@@ -1,13 +1,14 @@
1
1
  require "spec_helper"
2
+ require "openssl"
2
3
 
3
4
  describe "openssl checks" do
4
5
  let(:openssl_version_default) do
5
6
  if windows?
6
- "3.0.9"
7
+ "3.2.4"
7
8
  elsif macos?
8
9
  "1.1.1m"
9
10
  else
10
- "3.0.9"
11
+ "3.2.4"
11
12
  end
12
13
  end
13
14
 
@@ -17,4 +18,8 @@ describe "openssl checks" do
17
18
  expect(OpenSSL.const_get("OPENSSL_#{method.upcase}")).to match(openssl_version_default), "OpenSSL doesn't match omnibus_overrides.rb"
18
19
  end
19
20
  end
21
+
22
+ example "check SSL_ENV_HACK", windows_only: true, validate_only: true do
23
+ expect(defined?(::SSL_ENV_CACERT_PATCH)).to be_truthy, "SSL_ENV_CACERT_PATCH is not defined, did you forget to include the openssl-customization.rb file in your project?"
24
+ end
20
25
  end
data/spec/spec_helper.rb CHANGED
@@ -180,6 +180,7 @@ RSpec.configure do |config|
180
180
  config.filter_run_excluding openssl_gte_101: true unless openssl_gte_101?
181
181
  config.filter_run_excluding openssl_lt_101: true unless openssl_lt_101?
182
182
  config.filter_run_excluding aes_256_gcm_only: true unless aes_256_gcm?
183
+ config.filter_run_excluding validate_only: true unless ENV["BUILDKITE_BUILD_URL"] =~ /validate/ && ENV.fetch("BUILDKITE_LABEL", "") !~ /(Integration |Functional |Unit )/
183
184
  config.filter_run_excluding broken: true
184
185
  config.filter_run_excluding not_wpar: true unless wpar?
185
186
  config.filter_run_excluding not_supported_on_s390x: true unless s390x?
@@ -96,6 +96,45 @@ describe Chef::CookbookVersion do
96
96
  end
97
97
  end
98
98
 
99
+ describe "#recipe_json_filenames_by_name" do
100
+ let(:cookbook_version) { Chef::CookbookVersion.new("mycb", "/tmp/mycb") }
101
+
102
+ def files_for_recipe(extension)
103
+ [
104
+ { name: "recipes/default.#{extension}", full_path: "/home/user/repo/cookbooks/test/recipes/default.#{extension}" },
105
+ { name: "recipes/other.#{extension}", full_path: "/home/user/repo/cookbooks/test/recipes/other.#{extension}" },
106
+ ]
107
+ end
108
+
109
+ %w{json}.each do |extension|
110
+
111
+ context "and JSON files are present including a recipes/default.#{extension}" do
112
+ before(:each) do
113
+ allow(cookbook_version).to receive(:files_for).with("recipes").and_return(files_for_recipe(extension))
114
+ end
115
+
116
+ context "and manifest does not include a root_files/recipe.#{extension}" do
117
+ it "returns all JSON recipes with a correct default of default.#{extension}" do
118
+ expect(cookbook_version.recipe_json_filenames_by_name).to eq({ "default" => "/home/user/repo/cookbooks/test/recipes/default.#{extension}",
119
+ "other" => "/home/user/repo/cookbooks/test/recipes/other.#{extension}" })
120
+ end
121
+ end
122
+
123
+ context "and manifest also includes a root_files/recipe.#{extension}" do
124
+ let(:root_files) { [{ name: "root_files/recipe.#{extension}", full_path: "/home/user/repo/cookbooks/test/recipe.#{extension}" } ] }
125
+ before(:each) do
126
+ allow(cookbook_version.cookbook_manifest).to receive(:root_files).and_return(root_files)
127
+ end
128
+
129
+ it "returns all JSON recipes with a correct default of recipe.#{extension}" do
130
+ expect(cookbook_version.recipe_json_filenames_by_name).to eq({ "default" => "/home/user/repo/cookbooks/test/recipe.#{extension}",
131
+ "other" => "/home/user/repo/cookbooks/test/recipes/other.#{extension}" })
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+
99
138
  describe "with a cookbook directory named tatft" do
100
139
  MD5 = /[0-9a-f]{32}/.freeze
101
140
 
@@ -1100,7 +1100,7 @@ describe Chef::Node::Attribute do
1100
1100
  "c" => 4,
1101
1101
  }
1102
1102
  attributes = Chef::Node::Attribute.new(nil, default_hash, override_hash, nil)
1103
- expect(attributes.to_s).to eq('{"a"=>1, "b"=>3, "c"=>4}')
1103
+ expect(attributes).to match({ "a" => 1, "b" => 3, "c" => 4 })
1104
1104
  end
1105
1105
  end
1106
1106
 
@@ -45,12 +45,33 @@ describe "Chef::Provider::AptRepository" do
45
45
  sub:-:2048:16:84080586D1CA74A1:2009-04-22::::
46
46
  EOF
47
47
 
48
+ # Output of the command:
49
+ # gpg --no-default-keyring --keyring ${keyring} --list-public-keys ${key}
50
+ APG_GPG_KEYS_EXPIRED = <<~EOF.freeze
51
+ pub dsa1024 2009-04-22 [SC] [expired: 2018-02-22]
52
+ F36A89E33CC1BD0F71079007327574EE02A818DD
53
+ uid Cloudera Apt Repository
54
+ sub elg2048 2009-04-22 [E] [expired: 2018-02-22]
55
+ EOF
56
+
57
+ # Output of the command:
58
+ # gpg --no-default-keyring --keyring ${keyring} --list-public-keys ${key}
59
+ APG_GPG_KEYS_REVOKED = <<~EOF.freeze
60
+ pub dsa1024 2009-04-22 [SC] [revoked: 2018-02-22]
61
+ F36A89E33CC1BD0F71079007327574EE02A818DD
62
+ uid Cloudera Apt Repository
63
+ sub elg2048 2009-04-22 [E] [revoked: 2018-02-22]
64
+ EOF
65
+
48
66
  let(:node) { Chef::Node.new }
49
67
  let(:events) { Chef::EventDispatch::Dispatcher.new }
50
68
  let(:run_context) { Chef::RunContext.new(node, {}, events) }
51
69
  let(:collection) { double("resource collection") }
52
70
  let(:new_resource) { Chef::Resource::AptRepository.new("multiverse", run_context) }
53
71
  let(:provider) { new_resource.provider_for_action(:add) }
72
+ let(:keyring) { "/etc/apt/keyrings/ring.gpg" }
73
+ let(:key) { "ASDF1234" }
74
+ let(:keyserver) { "keyserver.ubuntu.com" }
54
75
 
55
76
  let(:apt_key_finger_cmd) do
56
77
  %w{apt-key adv --list-public-keys --with-fingerprint --with-colons}
@@ -70,9 +91,17 @@ describe "Chef::Provider::AptRepository" do
70
91
  end
71
92
 
72
93
  let(:gpg_shell_out_failure) do
73
- double("shell_out", stderr: "gpg: no valid OpenPGP data found.\n
74
- gpg: processing message failed: eof",
75
- exitstatus: 1, error?: true)
94
+ double("shell_out", stderr: "gpg: keybox '/etc/apt/keyrings/ring.gpg' created\ngpg: error reading key: No public key\n",
95
+ stdout: "",
96
+ exitstatus: 2, error?: true)
97
+ end
98
+
99
+ let(:gpg_shell_out_expired) do
100
+ double("shell_out", stdout: APG_GPG_KEYS_EXPIRED, exitstatus: 0, error?: false)
101
+ end
102
+
103
+ let(:gpg_shell_out_revoked) do
104
+ double("shell_out", stdout: APG_GPG_KEYS_REVOKED, exitstatus: 0, error?: false)
76
105
  end
77
106
 
78
107
  let(:apt_fingerprints) do
@@ -141,6 +170,40 @@ C5986B4F1257FFA86632CBA746181433FBB75451
141
170
  end
142
171
  end
143
172
 
173
+ describe "#keyring_key_is_valid?" do
174
+ it "returns true for a valid key" do
175
+ expect(provider).to receive(:shell_out).and_return(gpg_shell_out_success)
176
+ expect(provider.keyring_key_is_valid?(keyring, key)).to eql(true)
177
+ end
178
+
179
+ it "returns false when the key does not exist" do
180
+ expect(provider).to receive(:shell_out).and_return(gpg_shell_out_failure)
181
+ expect(provider.keyring_key_is_valid?(keyring, key)).to eql(false)
182
+ end
183
+
184
+ it "returns false when the key is expired" do
185
+ expect(provider).to receive(:shell_out).and_return(gpg_shell_out_expired)
186
+ expect(provider.keyring_key_is_valid?(keyring, key)).to eql(false)
187
+ end
188
+
189
+ it "returns false when the key has been revoked" do
190
+ expect(provider).to receive(:shell_out).and_return(gpg_shell_out_revoked)
191
+ expect(provider.keyring_key_is_valid?(keyring, key)).to eql(false)
192
+ end
193
+ end
194
+
195
+ describe "#keyring_key_is_present?" do
196
+ it "returns true for a key that exists" do
197
+ expect(provider).to receive(:shell_out).and_return(gpg_shell_out_success)
198
+ expect(provider.keyring_key_is_present?(keyring, key)).to eql(true)
199
+ end
200
+
201
+ it "returns false when the key is not present in the keyring" do
202
+ expect(provider).to receive(:shell_out).and_return(gpg_shell_out_failure)
203
+ expect(provider.keyring_key_is_present?(keyring, key)).to eql(false)
204
+ end
205
+ end
206
+
144
207
  describe "#no_new_keys?" do
145
208
  before do
146
209
  allow(provider).to receive(:extract_public_keys_from_cmd).with(*apt_key_finger_cmd).and_return(apt_public_keys)
@@ -165,17 +228,17 @@ C5986B4F1257FFA86632CBA746181433FBB75451
165
228
 
166
229
  describe "#key_type" do
167
230
  it "returns :remote_file with an http URL" do
168
- expect(provider.key_type("https://www.chef.io/key")).to eq(:remote_file)
231
+ expect(provider.key_resource_type("https://www.chef.io/key")).to eq(:remote_file)
169
232
  end
170
233
 
171
234
  it "returns :cookbook_file with a chef managed file" do
172
235
  expect(provider).to receive(:has_cookbook_file?).and_return(true)
173
- expect(provider.key_type("/foo/bar.key")).to eq(:cookbook_file)
236
+ expect(provider.key_resource_type("/foo/bar.key")).to eq(:cookbook_file)
174
237
  end
175
238
 
176
239
  it "throws exception if an unknown file specified" do
177
240
  expect(provider).to receive(:has_cookbook_file?).and_return(false)
178
- expect { provider.key_type("/foo/bar.key") }.to raise_error(Chef::Exceptions::FileNotFound)
241
+ expect { provider.key_resource_type("/foo/bar.key") }.to raise_error(Chef::Exceptions::FileNotFound)
179
242
  end
180
243
  end
181
244
 
@@ -223,6 +286,20 @@ C5986B4F1257FFA86632CBA746181433FBB75451
223
286
  end
224
287
  end
225
288
 
289
+ describe "#install_key_from_keyserver_to_keyring" do
290
+ it "does not raise an error when the key is valid" do
291
+ expect(provider).to receive(:execute).and_return(nil)
292
+ expect(provider).to receive(:keyring_key_is_valid?).and_return(true)
293
+ expect { provider.install_key_from_keyserver_to_keyring(key, keyserver, keyring) }.not_to raise_error
294
+ end
295
+
296
+ it "raises an error with the key is invalid" do
297
+ expect(provider).to receive(:execute).and_return(nil)
298
+ expect(provider).to receive(:keyring_key_is_valid?).and_return(false)
299
+ expect { provider.install_key_from_keyserver_to_keyring(key, keyserver, keyring) }.to raise_error(RuntimeError)
300
+ end
301
+ end
302
+
226
303
  describe "#install_ppa_key" do
227
304
  let(:url) { "https://launchpad.net/api/1.0/~chef/+archive/main" }
228
305
  let(:key) { "C5986B4F1257FFA86632CBA746181433FBB75451" }
@@ -230,8 +307,8 @@ C5986B4F1257FFA86632CBA746181433FBB75451
230
307
  it "gets a key" do
231
308
  simples = double("HTTP")
232
309
  allow(simples).to receive(:get).and_return("\"#{key}\"")
233
- expect(Chef::HTTP::Simple).to receive(:new).with(url).and_return(simples)
234
- expect(provider).to receive(:install_key_from_keyserver).with(key, "keyserver.ubuntu.com")
310
+ expect(Chef::HTTP::Simple).to receive(:new).with(url, {}).and_return(simples)
311
+ expect(provider).to receive(:install_key_from_keyserver).with(key, keyserver)
235
312
  provider.install_ppa_key("chef", "main")
236
313
  end
237
314
  end
@@ -151,7 +151,7 @@ describe Chef::Provider::Package::Rpm do
151
151
 
152
152
  context "when at the desired version already" do
153
153
  it "does nothing when the correct version is installed" do
154
- expect(provider).to_not receive(:shell_out_compacted!).with("rpm", "-i", "/tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
154
+ expect(provider).to_not receive(:shell_out_compacted!).with("rpm", "-i", "/tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", env: {}, timeout: 900)
155
155
 
156
156
  provider.action_install
157
157
  end
@@ -162,7 +162,7 @@ describe Chef::Provider::Package::Rpm do
162
162
  let(:rpm_q_stdout) { "imagemagick-c++ 0.5.4.7-7.el6_5" }
163
163
 
164
164
  it "runs rpm -u with the package source to upgrade" do
165
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
165
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", env: {}, timeout: 900)
166
166
  provider.action_install
167
167
  end
168
168
  end
@@ -177,7 +177,7 @@ describe Chef::Provider::Package::Rpm do
177
177
  let(:rpm_q_stdout) { "imagemagick-c++ 21.4-19.el6_5" }
178
178
 
179
179
  it "should run rpm -u --oldpackage with the package source to downgrade" do
180
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
180
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", env: {}, timeout: 900)
181
181
  provider.action_install
182
182
  end
183
183
 
@@ -208,7 +208,7 @@ describe Chef::Provider::Package::Rpm do
208
208
  let(:rpm_q_stdout) { "imagemagick-c++ 0.5.4.7-7.el6_5" }
209
209
 
210
210
  it "runs rpm -u with the package source to upgrade" do
211
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
211
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", env: {}, timeout: 900)
212
212
  provider.action_upgrade
213
213
  end
214
214
  end
@@ -224,7 +224,7 @@ describe Chef::Provider::Package::Rpm do
224
224
  let(:rpm_q_stdout) { "imagemagick-c++ 21.4-19.el6_5" }
225
225
 
226
226
  it "should run rpm -u --oldpackage with the package source to downgrade" do
227
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
227
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", env: {}, timeout: 900)
228
228
  provider.action_upgrade
229
229
  end
230
230
 
@@ -368,7 +368,7 @@ describe Chef::Provider::Package::Rpm do
368
368
  describe "action install" do
369
369
 
370
370
  it "installs the package" do
371
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-i", package_source, timeout: 900)
371
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-i", package_source, env: {}, timeout: 900)
372
372
 
373
373
  provider.action_install
374
374
  end
@@ -376,7 +376,7 @@ describe Chef::Provider::Package::Rpm do
376
376
  context "when custom resource options are given" do
377
377
  it "installs with custom options specified in the resource" do
378
378
  new_resource.options("--dbpath /var/lib/rpm")
379
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "--dbpath", "/var/lib/rpm", "-i", package_source, timeout: 900)
379
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "--dbpath", "/var/lib/rpm", "-i", package_source, env: {}, timeout: 900)
380
380
  provider.action_install
381
381
  end
382
382
  end
@@ -387,7 +387,7 @@ describe Chef::Provider::Package::Rpm do
387
387
  let(:action) { :upgrade }
388
388
 
389
389
  it "installs the package" do
390
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-i", package_source, timeout: 900)
390
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-i", package_source, env: {}, timeout: 900)
391
391
 
392
392
  provider.action_upgrade
393
393
  end
@@ -424,7 +424,7 @@ describe Chef::Provider::Package::Rpm do
424
424
  it "should install from a path when the package is a path and the source is nil" do
425
425
  expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
426
426
  provider.current_resource = current_resource
427
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-i", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
427
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-i", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", env: {}, timeout: 900)
428
428
  provider.install_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
429
429
  end
430
430
 
@@ -432,7 +432,7 @@ describe Chef::Provider::Package::Rpm do
432
432
  expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
433
433
  current_resource.version("21.4-19.el5")
434
434
  provider.current_resource = current_resource
435
- expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
435
+ expect(provider).to receive(:shell_out_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", env: {}, timeout: 900)
436
436
  provider.upgrade_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
437
437
  end
438
438
  end