chef 18.7.10-universal-mingw-ucrt → 18.8.9-universal-mingw-ucrt

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 +10 -2
  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 +44 -27
  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 +42 -8
@@ -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,39 +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
329
340
 
330
- # If signed by is true, then we don't need to
331
- # add to the default keyring. Instead make sure it's dearmored
332
- if new_resource.signed_by
333
- execute "gpg dearmor key" do
334
- input ::File.read(keyfile_path)
335
- command [ "gpg", "--batch", "--yes", "--dearmor", "-o", keyfile_path ]
341
+ execute "dearmor #{keyfile_path}" do
342
+ command [ "gpg", "--batch", "--yes", "--dearmor", "-o", keyfile_path, keyfile_tmp_path ]
336
343
  default_env true
337
344
  sensitive new_resource.sensitive
338
- action :run
339
- only_if { ::File.read(keyfile_path).include?("-----BEGIN PGP PUBLIC KEY BLOCK-----") }
340
- notifies :run, "execute[apt-cache gencaches]", :immediately
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"
341
351
  end
342
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
360
+
343
361
  execute "apt-key add #{keyfile_path}" do
344
362
  command [ "apt-key", "add", keyfile_path ]
345
363
  default_env true
@@ -413,8 +431,7 @@ class Chef
413
431
  default_env true
414
432
  sensitive new_resource.sensitive
415
433
  not_if do
416
- present = shell_out(*%W{gpg --no-default-keyring --keyring #{keyring} --list-public-keys --with-fingerprint --with-colons #{key}}).exitstatus != 0
417
- present && keyring_key_is_valid?(keyring, key.upcase)
434
+ keyring_key_is_present?(keyring, key.upcase) && keyring_key_is_valid?(keyring, key.upcase)
418
435
  end
419
436
  notifies :run, "execute[apt-cache gencaches]", :immediately
420
437
  end
@@ -430,7 +447,7 @@ class Chef
430
447
  # @return [void]
431
448
  def install_ppa_key(owner, repo)
432
449
  url = "https://launchpad.net/api/1.0/~#{owner}/+archive/#{repo}"
433
- 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('"')
434
451
  install_key_from_keyserver(key_id, "keyserver.ubuntu.com")
435
452
  rescue Net::HTTPClientException => e
436
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.10")
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