chef 18.2.7-x64-mingw-ucrt → 18.3.0-x64-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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/chef-universal-mingw-ucrt.gemspec +1 -1
  3. data/lib/chef/application/base.rb +2 -0
  4. data/lib/chef/client.rb +2 -2
  5. data/lib/chef/cookbook/synchronizer.rb +20 -2
  6. data/lib/chef/cookbook_version.rb +1 -1
  7. data/lib/chef/http/ssl_policies.rb +2 -2
  8. data/lib/chef/mixin/homebrew_user.rb +12 -5
  9. data/lib/chef/monkey_patches/net-http.rb +127 -0
  10. data/lib/chef/node/attribute_collections.rb +8 -0
  11. data/lib/chef/node/immutable_collections.rb +5 -2
  12. data/lib/chef/node/mixin/state_tracking.rb +1 -1
  13. data/lib/chef/provider/launchd.rb +1 -1
  14. data/lib/chef/provider/mount/linux.rb +1 -1
  15. data/lib/chef/provider/mount/mount.rb +5 -5
  16. data/lib/chef/provider/package/chocolatey.rb +18 -1
  17. data/lib/chef/provider/package/zypper.rb +1 -0
  18. data/lib/chef/provider/remote_file/http.rb +1 -1
  19. data/lib/chef/provider/yum_repository.rb +1 -1
  20. data/lib/chef/resource/apt_repository.rb +25 -6
  21. data/lib/chef/resource/homebrew_cask.rb +6 -7
  22. data/lib/chef/resource/homebrew_package.rb +1 -1
  23. data/lib/chef/resource/homebrew_tap.rb +5 -5
  24. data/lib/chef/resource/launchd.rb +5 -1
  25. data/lib/chef/resource/windows_certificate.rb +1 -1
  26. data/lib/chef/resource/windows_security_policy.rb +2 -2
  27. data/lib/chef/resource.rb +11 -1
  28. data/lib/chef/version.rb +1 -1
  29. data/lib/chef/win32/security.rb +7 -1
  30. data/spec/functional/resource/chocolatey_package_spec.rb +32 -20
  31. data/spec/functional/resource/execute_spec.rb +1 -1
  32. data/spec/functional/resource/windows_certificate_spec.rb +25 -0
  33. data/spec/unit/client_spec.rb +2 -2
  34. data/spec/unit/mixin/homebrew_user_spec.rb +30 -7
  35. data/spec/unit/node/vivid_mash_spec.rb +42 -0
  36. data/spec/unit/provider/apt_repository_spec.rb +17 -7
  37. data/spec/unit/provider/launchd_spec.rb +2 -2
  38. data/spec/unit/provider/mount/aix_spec.rb +2 -2
  39. data/spec/unit/provider/mount/linux_spec.rb +6 -5
  40. data/spec/unit/provider/mount/mount_spec.rb +8 -8
  41. data/spec/unit/provider/package/chocolatey_spec.rb +19 -3
  42. data/spec/unit/provider/package/rpm_spec.rb +2 -2
  43. data/spec/unit/provider/package/zypper_spec.rb +10 -0
  44. data/spec/unit/provider/remote_file/http_spec.rb +4 -4
  45. data/spec/unit/resource/apt_repository_spec.rb +5 -0
  46. data/spec/unit/resource_spec.rb +86 -0
  47. metadata +13 -12
  48. /data/spec/functional/assets/chocolatey_feed/{test-A.1.0.nupkg → test-A.1.0.0.nupkg} +0 -0
  49. /data/spec/functional/assets/chocolatey_feed/{test-A.1.5.nupkg → test-A.1.5.0.nupkg} +0 -0
  50. /data/spec/functional/assets/chocolatey_feed/{test-A.2.0.nupkg → test-A.2.0.0.nupkg} +0 -0
  51. /data/spec/functional/assets/chocolatey_feed/{test-B.1.0.nupkg → test-B.1.0.0.nupkg} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a313783b0386b4ed50ef119bf7b37e014adccab2341396d7d11c9bc0e5a6ce3d
4
- data.tar.gz: 74be0fa290f08d00d77b0cdb68caf7dd84b929ce289fd2b80b11114a488f0597
3
+ metadata.gz: 034b891e91a35febfaea15c74c7236eba8d503221d7f4aaa8ac8cca985b296e7
4
+ data.tar.gz: 84a49732354b3f8b2c11d2713093c5317b6c6d8e335d1f850458cf9abbf5038d
5
5
  SHA512:
6
- metadata.gz: ddafcb71010f1ef7bf289d450b2d2851016d9e3cae93fb04b9fcc17255fdba206ae105eaf585f56aebce0c9e1284a7fe07da198dae33796d714b47fb1a269ad9
7
- data.tar.gz: cece71ceaef6808e4c9b966d906004e8ecc5bbfc3102f4bbcf619d28e9fd27acd22799fffe34ab4c19c7e4acd74e3823333a9870df9546881316431ad98f27ad
6
+ metadata.gz: 781e2665ae7cc103a9140bf5f024dca9222dae1aa00d7119bab296007409e77a6854102e6367caa48b65ad0e1546e349453b57865ace723216259f9acdb48d6f
7
+ data.tar.gz: ffa726a2dd3f8f9228b08dc8741d92ae96f4b2703484dcd4ab9c72ba8eefdef9ad89230d15d669dbf2eeda017d0e2842241d8bb1a7e080d754906d01b00ebbdf
@@ -15,7 +15,7 @@ gemspec.add_dependency "wmi-lite", "~> 1.0"
15
15
  gemspec.add_dependency "win32-taskscheduler", "~> 2.0"
16
16
  gemspec.add_dependency "iso8601", ">= 0.12.1", "< 0.14" # validate 0.14 when it comes out
17
17
  gemspec.add_dependency "win32-certstore", "~> 0.6.15" # 0.5+ required for specifying user vs. system store
18
- gemspec.add_dependency "chef-powershell", "~> 18.0.0" # The guts of the powershell_exec code have been moved to its own gem, chef-powershell. It's part of the chef-powershell-shim repo.
18
+ gemspec.add_dependency "chef-powershell", "~> 18.1.0" # The guts of the powershell_exec code have been moved to its own gem, chef-powershell. It's part of the chef-powershell-shim repo.
19
19
 
20
20
  gemspec.extensions << "ext/win32-eventlog/Rakefile"
21
21
  gemspec.files += Dir.glob("{distro,ext}/**/*")
@@ -24,6 +24,8 @@ require "chef-utils/dist" unless defined?(ChefUtils::Dist)
24
24
  require_relative "../daemon"
25
25
  require "chef-config/mixin/dot_d"
26
26
  require "license_acceptance/cli_flags/mixlib_cli"
27
+ require "chef/monkey_patches/net-http"
28
+
27
29
  module Mixlib
28
30
  autoload :Archive, "mixlib/archive"
29
31
  end
data/lib/chef/client.rb CHANGED
@@ -663,7 +663,7 @@ class Chef
663
663
  logger.trace("New client keys created in the Certificate Store - skipping registration")
664
664
  end
665
665
  events.skipping_registration(client_name, config)
666
- elsif File.exists?(config[:client_key])
666
+ elsif File.exist?(config[:client_key])
667
667
  events.skipping_registration(client_name, config)
668
668
  logger.trace("Client key #{config[:client_key]} is present - skipping registration")
669
669
  else
@@ -1069,7 +1069,7 @@ class Chef
1069
1069
  end
1070
1070
 
1071
1071
  def empty_directory?(path)
1072
- !File.exists?(path) || (Dir.entries(path).size <= 2)
1072
+ !File.exist?(path) || (Dir.entries(path).size <= 2)
1073
1073
  end
1074
1074
 
1075
1075
  def is_last_element?(index, object)
@@ -18,6 +18,7 @@ require_relative "../util/threaded_job_queue"
18
18
  require_relative "../server_api"
19
19
  require "singleton" unless defined?(Singleton)
20
20
  require "chef-utils/dist" unless defined?(ChefUtils::Dist)
21
+ require "set" unless defined?(Set)
21
22
 
22
23
  class Chef
23
24
 
@@ -219,14 +220,31 @@ class Chef
219
220
 
220
221
  # remove deleted files in cookbooks that are being used on the node
221
222
  def remove_deleted_files
223
+ cache_file_hash = {}
224
+ @cookbooks_by_name.each_key do |k|
225
+ cache_file_hash[k] = {}
226
+ end
227
+
228
+ # First populate files from cache
222
229
  cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file|
223
230
  md = cache_file.match(%r{^cookbooks/([^/]+)/([^/]+)/(.*)})
224
231
  next unless md
225
232
 
226
- ( cookbook_name, segment, file ) = md[1..3]
233
+ (cookbook_name, segment, file) = md[1..3]
227
234
  if have_cookbook?(cookbook_name)
235
+ cache_file_hash[cookbook_name][segment] ||= {}
236
+ cache_file_hash[cookbook_name][segment]["#{segment}/#{file}"] = cache_file
237
+ end
238
+ end
239
+ # Determine which files don't match manifest
240
+ @cookbooks_by_name.each_key do |cookbook_name|
241
+ cache_file_hash[cookbook_name].each_key do |segment|
228
242
  manifest_segment = cookbook_segment(cookbook_name, segment)
229
- if manifest_segment.select { |manifest_record| manifest_record["path"] == "#{segment}/#{file}" }.empty?
243
+ manifest_record_paths = manifest_segment.map { |manifest_record| manifest_record["path"] }.to_set
244
+ to_be_removed = cache_file_hash[cookbook_name][segment].keys.to_set - manifest_record_paths
245
+ to_be_removed.each do |path|
246
+ cache_file = cache_file_hash[cookbook_name][segment][path]
247
+
230
248
  Chef::Log.info("Removing #{cache_file} from the cache; its is no longer in the cookbook manifest.")
231
249
  cache.delete(cache_file)
232
250
  @events.removed_cookbook_file(cache_file)
@@ -474,7 +474,7 @@ class Chef
474
474
  end
475
475
 
476
476
  def reload_metadata!
477
- if File.exists?(metadata_json_file)
477
+ if File.exist?(metadata_json_file)
478
478
  metadata.from_json(IO.read(metadata_json_file))
479
479
  end
480
480
  end
@@ -103,10 +103,10 @@ class Chef
103
103
  unless config[:ssl_client_cert] && config[:ssl_client_key]
104
104
  raise Chef::Exceptions::ConfigurationError, "You must configure ssl_client_cert and ssl_client_key together"
105
105
  end
106
- unless ::File.exists?(config[:ssl_client_cert])
106
+ unless ::File.exist?(config[:ssl_client_cert])
107
107
  raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_cert #{config[:ssl_client_cert]} does not exist"
108
108
  end
109
- unless ::File.exists?(config[:ssl_client_key])
109
+ unless ::File.exist?(config[:ssl_client_key])
110
110
  raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_key #{config[:ssl_client_key]} does not exist"
111
111
  end
112
112
 
@@ -57,18 +57,25 @@ class Chef
57
57
  @homebrew_owner_username
58
58
  end
59
59
 
60
+ def homebrew_bin_path(brew_bin_path = nil)
61
+ if brew_bin_path && ::File.exist?(brew_bin_path)
62
+ brew_bin_path
63
+ else
64
+ brew_bin_path = [which("brew"), "/opt/homebrew/bin/brew", "/usr/local/bin/brew", "/home/linuxbrew/.linuxbrew/bin/brew"].uniq.select { |x| ::File.exist?(x) && ::File.executable?(x) }.first
65
+ brew_bin_path || nil
66
+ end
67
+ end
68
+
60
69
  private
61
70
 
62
71
  def calculate_owner
63
- default_brew_path = "/usr/local/bin/brew"
64
- if ::File.exist?(default_brew_path)
72
+ brew_path = homebrew_bin_path
73
+ if brew_path
65
74
  # By default, this follows symlinks which is what we want
66
- owner = ::File.stat(default_brew_path).uid
67
- elsif (brew_path = shell_out("which brew").stdout.strip) && !brew_path.empty?
68
75
  owner = ::File.stat(brew_path).uid
69
76
  else
70
77
  raise Chef::Exceptions::CannotDetermineHomebrewOwner,
71
- 'Could not find the "brew" executable in /usr/local/bin or anywhere on the path.'
78
+ 'Could not find the "brew" executable anywhere on the path.'
72
79
  end
73
80
 
74
81
  Chef::Log.debug "Found Homebrew owner #{Etc.getpwuid(owner).name}; executing `brew` commands as them"
@@ -0,0 +1,127 @@
1
+ if RUBY_VERSION.split(".")[0..1].join(".") == "3.1"
2
+ require "net/http" unless defined?(Net::HTTP)
3
+ # This is monkey-patch for ruby 3.1.x
4
+ # Due to change https://github.com/ruby/net-http/pull/10, when making net/http requests to a url which supports only IPv6 and not IPv4,
5
+ # ruby waits for IPv4 request to timeout first, then makes IPv6 request. This increased response time.
6
+ # NOTE 1: This is already reverted https://github.com/ruby/ruby/commit/f88bff770578583a708093f4a0d8b1483a1d2039 but under ruby 3.2.2
7
+ # NOTE 2: We are patching action `connect` from here https://github.com/ruby/ruby/blob/f88bff770578583a708093f4a0d8b1483a1d2039/lib/net/http.rb#L1000
8
+
9
+ module Net
10
+ class HTTP
11
+ def connect
12
+ if use_ssl?
13
+ # reference early to load OpenSSL before connecting,
14
+ # as OpenSSL may take time to load.
15
+ @ssl_context = OpenSSL::SSL::SSLContext.new
16
+ end
17
+
18
+ if proxy?
19
+ conn_addr = proxy_address
20
+ conn_port = proxy_port
21
+ else
22
+ conn_addr = conn_address
23
+ conn_port = port
24
+ end
25
+
26
+ puts "opening connection to #{conn_addr}:#{conn_port}..."
27
+ s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
28
+ begin
29
+ TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
30
+ rescue => e
31
+ raise e, "Failed to open TCP connection to " +
32
+ "#{conn_addr}:#{conn_port} (#{e.message})"
33
+ end
34
+ }
35
+ s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
36
+ puts "opened"
37
+ if use_ssl?
38
+ if proxy?
39
+ plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
40
+ write_timeout: @write_timeout,
41
+ continue_timeout: @continue_timeout,
42
+ debug_output: @debug_output)
43
+ buf = "CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n"
44
+ buf << "Host: #{@address}:#{@port}\r\n"
45
+ if proxy_user
46
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack("m0")
47
+ buf << "Proxy-Authorization: Basic #{credential}\r\n"
48
+ end
49
+ buf << "\r\n"
50
+ plain_sock.write(buf)
51
+ HTTPResponse.read_new(plain_sock).value
52
+ # assuming nothing left in buffers after successful CONNECT response
53
+ end
54
+
55
+ ssl_parameters = {}
56
+ iv_list = instance_variables
57
+ SSL_IVNAMES.each_with_index do |ivname, i|
58
+ if iv_list.include?(ivname)
59
+ value = instance_variable_get(ivname)
60
+ unless value.nil?
61
+ ssl_parameters[SSL_ATTRIBUTES[i]] = value
62
+ end
63
+ end
64
+ end
65
+ @ssl_context.set_params(ssl_parameters)
66
+ unless @ssl_context.session_cache_mode.nil? # a dummy method on JRuby
67
+ @ssl_context.session_cache_mode =
68
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
69
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
70
+ end
71
+ if @ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby
72
+ @ssl_context.session_new_cb = proc { |sock, sess| @ssl_session = sess }
73
+ end
74
+
75
+ # Still do the post_connection_check below even if connecting
76
+ # to IP address
77
+ verify_hostname = @ssl_context.verify_hostname
78
+
79
+ # requiring 'resolv' near the top of the file causes registry.rb monkey patch to fail
80
+ # Windows 2012 R2 somehow fails to have Resolv defined unless we require it manually
81
+ require "resolv" unless defined?(Resolv)
82
+
83
+ # Server Name Indication (SNI) RFC 3546/6066
84
+ case @address
85
+ when ::Resolv::IPv4::Regex, ::Resolv::IPv6::Regex
86
+ # don't set SNI, as IP addresses in SNI is not valid
87
+ # per RFC 6066, section 3.
88
+
89
+ # Avoid openssl warning
90
+ @ssl_context.verify_hostname = false
91
+ else
92
+ ssl_host_address = @address
93
+ end
94
+
95
+ puts "starting SSL for #{conn_addr}:#{conn_port}..."
96
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
97
+ s.sync_close = true
98
+ s.hostname = ssl_host_address if s.respond_to?(:hostname=) && ssl_host_address
99
+
100
+ if @ssl_session &&
101
+ (Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout)
102
+ s.session = @ssl_session
103
+ end
104
+ ssl_socket_connect(s, @open_timeout)
105
+ if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname
106
+ s.post_connection_check(@address)
107
+ end
108
+ puts "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
109
+ end
110
+ @socket = BufferedIO.new(s, read_timeout: @read_timeout,
111
+ write_timeout: @write_timeout,
112
+ continue_timeout: @continue_timeout,
113
+ debug_output: @debug_output)
114
+ @last_communicated = nil
115
+ on_connect
116
+ rescue => exception
117
+ if s
118
+ puts "Conn close because of connect error #{exception}"
119
+ s.close
120
+ end
121
+ raise
122
+ end
123
+ end
124
+ end
125
+ else
126
+ warn "Not applying net/http monkey patch needed for ruby 3.1"
127
+ end
@@ -39,11 +39,19 @@ class Chef
39
39
  MUTATOR_METHODS.each do |mutator|
40
40
  define_method(mutator) do |*args, &block|
41
41
  ret = super(*args, &block)
42
+ # TODO: use `send_reset_cache(__path__)` for all mutator methods?
42
43
  send_reset_cache
43
44
  ret
44
45
  end
45
46
  end
46
47
 
48
+ def <<(obj)
49
+ ret = super(obj)
50
+ # NOTE: Expecting __path__ to be top-level attribute only
51
+ send_reset_cache(__path__)
52
+ ret
53
+ end
54
+
47
55
  def delete(key, &block)
48
56
  send_reset_cache(__path__, key)
49
57
  super
@@ -32,13 +32,16 @@ class Chef
32
32
  end
33
33
 
34
34
  def convert_value(value)
35
+ # The order in this case statement is *important*.
36
+ # ImmutableMash and ImmutableArray should be tested first,
37
+ # as this saves unnecessary creation of intermediate objects
35
38
  case value
39
+ when ImmutableMash, ImmutableArray
40
+ value
36
41
  when Hash
37
42
  ImmutableMash.new(value, __root__, __node__, __precedence__)
38
43
  when Array
39
44
  ImmutableArray.new(value, __root__, __node__, __precedence__)
40
- when ImmutableMash, ImmutableArray
41
- value
42
45
  else
43
46
  safe_dup(value).freeze
44
47
  end
@@ -78,7 +78,7 @@ class Chef
78
78
 
79
79
  def send_reset_cache(path = nil, key = nil)
80
80
  next_path = [ path, key ].flatten.compact
81
- __root__.reset_cache(next_path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !next_path.nil?
81
+ __root__.reset_cache(next_path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache)
82
82
  end
83
83
 
84
84
  def copy_state_to(ret, next_path)
@@ -52,7 +52,7 @@ class Chef
52
52
  end
53
53
 
54
54
  action :delete, description: "Delete a launchd property list. This will unload a daemon or agent, if loaded." do
55
- if ::File.exists?(path)
55
+ if ::File.exist?(path)
56
56
  manage_service(:disable)
57
57
  end
58
58
  manage_plist(:delete)
@@ -39,7 +39,7 @@ class Chef
39
39
 
40
40
  def mounted?
41
41
  mounted = false
42
- real_mount_point = if ::File.exists? @new_resource.mount_point
42
+ real_mount_point = if ::File.exist? @new_resource.mount_point
43
43
  ::File.realpath(@new_resource.mount_point)
44
44
  else
45
45
  @new_resource.mount_point
@@ -42,9 +42,9 @@ class Chef
42
42
 
43
43
  def mountable?
44
44
  # only check for existence of non-remote devices
45
- if device_should_exist? && !::File.exists?(device_real)
45
+ if device_should_exist? && !::File.exist?(device_real)
46
46
  raise Chef::Exceptions::Mount, "Device #{@new_resource.device} does not exist"
47
- elsif @new_resource.mount_point != "none" && !::File.exists?(@new_resource.mount_point)
47
+ elsif @new_resource.mount_point != "none" && !::File.exist?(@new_resource.mount_point)
48
48
  raise Chef::Exceptions::Mount, "Mount point #{@new_resource.mount_point} does not exist"
49
49
  end
50
50
 
@@ -81,7 +81,7 @@ class Chef
81
81
  # "mount" outputs the mount points as real paths. Convert
82
82
  # the mount_point of the resource to a real path in case it
83
83
  # contains symlinks in its parents dirs.
84
- real_mount_point = if ::File.exists? @new_resource.mount_point
84
+ real_mount_point = if ::File.exist? @new_resource.mount_point
85
85
  ::File.realpath(@new_resource.mount_point)
86
86
  else
87
87
  @new_resource.mount_point
@@ -186,7 +186,7 @@ class Chef
186
186
  def device_should_exist?
187
187
  ( @new_resource.device != "none" ) &&
188
188
  ( not network_device? ) &&
189
- ( not %w{ cgroup tmpfs fuse vboxsf zfs }.include? @new_resource.fstype )
189
+ ( not %w{ cgroup tmpfs fuse vboxsf zfs efivarfs }.include? @new_resource.fstype )
190
190
  end
191
191
 
192
192
  private
@@ -287,4 +287,4 @@ class Chef
287
287
  end
288
288
  end
289
289
  end
290
- end
290
+ end
@@ -130,6 +130,21 @@ class Chef
130
130
  # install from, but like the rubygem provider's sources which are more like repos.
131
131
  def check_resource_semantics!; end
132
132
 
133
+ def self.get_choco_version
134
+ @get_choco_version ||= powershell_exec!("choco --version").result
135
+ end
136
+
137
+ # Choco V2 uses 'Search' for remote repositories and 'List' for local packages
138
+ def self.query_command
139
+ return "list" if get_choco_version.match?(/^1/)
140
+
141
+ "search"
142
+ end
143
+
144
+ def query_command
145
+ self.class.query_command
146
+ end
147
+
133
148
  private
134
149
 
135
150
  def version_compare(v1, v2)
@@ -225,7 +240,7 @@ class Chef
225
240
  package_name_array.each do |pkg|
226
241
  available_versions =
227
242
  begin
228
- cmd = [ "list", "-r", pkg ]
243
+ cmd = [ query_command, "-r", pkg ]
229
244
  cmd += common_options
230
245
  cmd.push( new_resource.list_options ) if new_resource.list_options
231
246
 
@@ -242,6 +257,8 @@ class Chef
242
257
  # Installed packages in chocolatey as a Hash of names mapped to versions
243
258
  # (names are downcased for case-insensitive matching)
244
259
  #
260
+ # Beginning with Choco 2.0, "list" returns local packages only while "search" returns packages from external package sources
261
+ #
245
262
  # @return [Hash] name-to-version mapping of installed packages
246
263
  def installed_packages
247
264
  @installed_packages ||= Hash[*parse_list_output("list", "-l", "-r").flatten]
@@ -141,6 +141,7 @@ class Chef
141
141
  if md = line.match(/^(\S*)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(.*)$/)
142
142
  (status, name, type, version, arch, repo) = [ md[1], md[2], md[3], md[4], md[5], md[6] ]
143
143
  next if version == "Version" # header
144
+ next if name != package_name
144
145
 
145
146
  # sometimes even though we request a specific version in the search string above and have match exact, we wind up
146
147
  # with other versions in the output, particularly getting the installed version when downgrading.
@@ -113,7 +113,7 @@ class Chef
113
113
  end
114
114
 
115
115
  def last_modified_time_from(response)
116
- response["last_modified"] || response["date"]
116
+ response["last-modified"] || response["date"]
117
117
  end
118
118
 
119
119
  def etag_from(response)
@@ -45,7 +45,7 @@ class Chef
45
45
  if new_resource.make_cache
46
46
  notifies :run, "execute[yum clean metadata #{new_resource.repositoryid}]", :immediately if new_resource.clean_metadata || new_resource.clean_headers
47
47
  # makecache fast only works on non-dnf systems.
48
- if !which "dnf" && new_resource.makecache_fast
48
+ if !which("dnf") && new_resource.makecache_fast
49
49
  notifies :run, "execute[yum-makecache-fast-#{new_resource.repositoryid}]", :immediately
50
50
  else
51
51
  notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately
@@ -98,6 +98,18 @@ class Chef
98
98
  end
99
99
  ```
100
100
 
101
+ **Add repository that needs custom options**:
102
+ ```ruby
103
+ apt_repository 'corretto' do
104
+ uri 'https://apt.corretto.aws'
105
+ arch 'amd64'
106
+ distribution 'stable'
107
+ components ['main']
108
+ options ['target-=Contents-deb']
109
+ key 'https://apt.corretto.aws/corretto.key'
110
+ end
111
+ ```
112
+
101
113
  **Remove a repository from the list**:
102
114
 
103
115
  ```ruby
@@ -159,6 +171,10 @@ class Chef
159
171
  description: "Determines whether to rebuild the APT package cache.",
160
172
  default: true, desired_state: false
161
173
 
174
+ property :options, [String, Array],
175
+ description: "Additional options to set for the repository",
176
+ default: [], coerce: proc { |x| Array(x) }
177
+
162
178
  default_action :add
163
179
  allowed_actions :add, :remove
164
180
 
@@ -388,19 +404,21 @@ class Chef
388
404
  # @param [Array] components
389
405
  # @param [Boolean] trusted
390
406
  # @param [String] arch
407
+ # @param [Array] options
391
408
  # @param [Boolean] add_src
392
409
  #
393
410
  # @return [String] complete repo config text
394
- def build_repo(uri, distribution, components, trusted, arch, add_src = false)
411
+ def build_repo(uri, distribution, components, trusted, arch, options, add_src = false)
395
412
  uri = make_ppa_url(uri) if is_ppa_url?(uri)
396
413
 
397
414
  uri = Addressable::URI.parse(uri)
398
415
  components = Array(components).join(" ")
399
- options = []
400
- options << "arch=#{arch}" if arch
401
- options << "trusted=yes" if trusted
402
- optstr = unless options.empty?
403
- "[" + options.join(" ") + "]"
416
+ options_list = []
417
+ options_list << "arch=#{arch}" if arch
418
+ options_list << "trusted=yes" if trusted
419
+ options_list += options
420
+ optstr = unless options_list.empty?
421
+ "[" + options_list.join(" ") + "]"
404
422
  end
405
423
  info = [ optstr, uri.normalize.to_s, distribution, components ].compact.join(" ")
406
424
  repo = "deb #{info}\n"
@@ -461,6 +479,7 @@ class Chef
461
479
  repo_components,
462
480
  new_resource.trusted,
463
481
  new_resource.arch,
482
+ new_resource.options,
464
483
  new_resource.deb_src
465
484
  )
466
485
 
@@ -45,8 +45,7 @@ class Chef
45
45
  default: true
46
46
 
47
47
  property :homebrew_path, String,
48
- description: "The path to the homebrew binary.",
49
- default: "/usr/local/bin/brew"
48
+ description: "The path to the Homebrew binary."
50
49
 
51
50
  property :owner, [String, Integer],
52
51
  description: "The owner of the Homebrew installation.",
@@ -56,14 +55,14 @@ class Chef
56
55
  action :install, description: "Install an application that is packaged as a Homebrew cask." do
57
56
  if new_resource.install_cask
58
57
  homebrew_tap "homebrew/cask" do
59
- homebrew_path new_resource.homebrew_path
58
+ homebrew_path homebrew_bin_path(new_resource.homebrew_path)
60
59
  owner new_resource.owner
61
60
  end
62
61
  end
63
62
 
64
63
  unless casked?
65
64
  converge_by("install cask #{new_resource.cask_name} #{new_resource.options}") do
66
- shell_out!("#{new_resource.homebrew_path} install --cask #{new_resource.cask_name} #{new_resource.options}",
65
+ shell_out!("#{homebrew_bin_path(new_resource.homebrew_path)} install --cask #{new_resource.cask_name} #{new_resource.options}",
67
66
  user: new_resource.owner,
68
67
  env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
69
68
  cwd: ::Dir.home(new_resource.owner))
@@ -74,14 +73,14 @@ class Chef
74
73
  action :remove, description: "Remove an application that is packaged as a Homebrew cask." do
75
74
  if new_resource.install_cask
76
75
  homebrew_tap "homebrew/cask" do
77
- homebrew_path new_resource.homebrew_path
76
+ homebrew_path homebrew_bin_path(new_resource.homebrew_path)
78
77
  owner new_resource.owner
79
78
  end
80
79
  end
81
80
 
82
81
  if casked?
83
82
  converge_by("uninstall cask #{new_resource.cask_name}") do
84
- shell_out!("#{new_resource.homebrew_path} uninstall --cask #{new_resource.cask_name}",
83
+ shell_out!("#{homebrew_bin_path(new_resource.homebrew_path)} uninstall --cask #{new_resource.cask_name}",
85
84
  user: new_resource.owner,
86
85
  env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
87
86
  cwd: ::Dir.home(new_resource.owner))
@@ -99,7 +98,7 @@ class Chef
99
98
  # @return [Boolean]
100
99
  def casked?
101
100
  unscoped_name = new_resource.cask_name.split("/").last
102
- shell_out!("#{new_resource.homebrew_path} list --cask 2>/dev/null",
101
+ shell_out!("#{homebrew_bin_path(new_resource.homebrew_path)} list --cask 2>/dev/null",
103
102
  user: new_resource.owner,
104
103
  env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
105
104
  cwd: ::Dir.home(new_resource.owner)).stdout.split.include?(unscoped_name)
@@ -63,7 +63,7 @@ class Chef
63
63
  allowed_actions :install, :upgrade, :remove, :purge
64
64
 
65
65
  property :homebrew_user, [ String, Integer ],
66
- description: "The name or uid of the Homebrew owner to be used by #{ChefUtils::Dist::Infra::PRODUCT} when executing a command.\n\n#{ChefUtils::Dist::Infra::PRODUCT}, by default, will attempt to execute a Homebrew command as the owner of the `/usr/local/bin/brew` executable. If that executable does not exist, #{ChefUtils::Dist::Infra::PRODUCT} will attempt to find the user by executing `which brew`. If that executable cannot be found, #{ChefUtils::Dist::Infra::PRODUCT} will print an error message: `Could not find the 'brew' executable in /usr/local/bin or anywhere on the path.`.\n\nSet this property to specify the Homebrew owner for situations where Chef Infra Client cannot automatically detect the correct owner.'"
66
+ description: "The name or uid of the Homebrew owner to be used by #{ChefUtils::Dist::Infra::PRODUCT} when executing a command.\n\n#{ChefUtils::Dist::Infra::PRODUCT}, by default, will attempt to execute a Homebrew command as the owner of the `/usr/local/bin/brew` executable on x86_64 machines or `/opt/homebrew/bin/brew` executable on arm64 machines. If that executable does not exist, #{ChefUtils::Dist::Infra::PRODUCT} will attempt to find the user by executing `which brew`. If that executable cannot be found, #{ChefUtils::Dist::Infra::PRODUCT} will print an error message: `Could not find the 'brew' executable in /usr/local/bin, /opt/homebrew/bin, or anywhere on the path.`.\n\nSet this property to specify the Homebrew owner for situations where Chef Infra Client cannot automatically detect the correct owner.'"
67
67
 
68
68
  end
69
69
  end
@@ -41,8 +41,7 @@ class Chef
41
41
  description: "The URL of the tap."
42
42
 
43
43
  property :homebrew_path, String,
44
- description: "The path to the Homebrew binary.",
45
- default: "/usr/local/bin/brew"
44
+ description: "The path to the Homebrew binary."
46
45
 
47
46
  property :owner, String,
48
47
  description: "The owner of the Homebrew installation.",
@@ -52,7 +51,7 @@ class Chef
52
51
  action :tap, description: "Add a Homebrew tap." do
53
52
  unless tapped?(new_resource.tap_name)
54
53
  converge_by("tap #{new_resource.tap_name}") do
55
- shell_out!("#{new_resource.homebrew_path} tap #{new_resource.tap_name} #{new_resource.url || ""}",
54
+ shell_out!("#{homebrew_bin_path(new_resource.homebrew_path)} tap #{new_resource.tap_name} #{new_resource.url || ""}",
56
55
  user: new_resource.owner,
57
56
  env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
58
57
  cwd: ::Dir.home(new_resource.owner))
@@ -63,7 +62,7 @@ class Chef
63
62
  action :untap, description: "Remove a Homebrew tap." do
64
63
  if tapped?(new_resource.tap_name)
65
64
  converge_by("untap #{new_resource.tap_name}") do
66
- shell_out!("#{new_resource.homebrew_path} untap #{new_resource.tap_name}",
65
+ shell_out!("#{homebrew_bin_path(new_resource.homebrew_path)} untap #{new_resource.tap_name}",
67
66
  user: new_resource.owner,
68
67
  env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
69
68
  cwd: ::Dir.home(new_resource.owner))
@@ -75,8 +74,9 @@ class Chef
75
74
  #
76
75
  # @return [Boolean]
77
76
  def tapped?(name)
77
+ base_path = ["#{::File.dirname(which("brew"))}/../homebrew", "#{::File.dirname(which("brew"))}/../Homebrew", "/opt/homebrew", "/usr/local/Homebrew", "/home/linuxbrew/.linuxbrew"].uniq.select { |x| Dir.exist?(x) }.first
78
78
  tap_dir = name.gsub("/", "/homebrew-")
79
- ::File.directory?("/usr/local/Homebrew/Library/Taps/#{tap_dir}")
79
+ ::File.directory?("#{base_path}/Library/Taps/#{tap_dir}")
80
80
  end
81
81
  end
82
82
  end
@@ -202,7 +202,11 @@ class Chef
202
202
  description: "The first argument of `execvp`, typically the file name associated with the file to be executed. This value must be specified if `program_arguments` is not specified, and vice-versa."
203
203
 
204
204
  property :program_arguments, Array,
205
- description: "The second argument of `execvp`. If program is not specified, this property must be specified and will be handled as if it were the first argument."
205
+ description: "The second argument of `execvp`. If program is not specified, this property must be specified and will be handled as if it were the first argument.",
206
+ coerce: proc { |args|
207
+ # Cast all values to a string. Launchd only supports string values
208
+ args.map(&:to_s)
209
+ }
206
210
 
207
211
  property :queue_directories, Array,
208
212
  description: "An array of non-empty directories which, if any are modified, will cause a job to be started."
@@ -440,7 +440,7 @@ class Chef
440
440
  def export_cert(cert_obj, output_path:, store_name:, store_location:, pfx_password:)
441
441
  # Delete the cert if it exists on disk already.
442
442
  # We want to ensure we're not randomly loading an old stinky cert.
443
- if ::File.exists?(output_path)
443
+ if ::File.exist?(output_path)
444
444
  ::File.delete(output_path)
445
445
  end
446
446