chef 18.2.7 → 18.3.0

Sign up to get free protection for your applications and to get access to all the features.
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 +11 -10
  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: ae48a568217d00b159123bf621bdaee21412909ef553f400719f251559c8a4d4
4
- data.tar.gz: 553c80d44e9babb553588b0d01beac0a413aec6b0d21fdbaaf754cc1b3fa0928
3
+ metadata.gz: 3856e8d0d5224634cad3eb9d43ecf3e87cc504ee881474f7698a14eb90e15088
4
+ data.tar.gz: 7830af2c8acab4e76176876a2aaac1363d9a8ff706a78422ec98d6d522772856
5
5
  SHA512:
6
- metadata.gz: 3b2df041625cd266ae86c1a899c69493190f09018714cf281486291958268a669d65103555b63f0104345b5e281248d9bf7a2157a2de3632d92f84b4ed72c99c
7
- data.tar.gz: 54b4391ec722faa770d54b7d5c6650285ab3dfdb933eed86938d579fb92b1501ad4bf3848e3f17950316eb5c8d9bc2bc99e044368246940321b10f3ddaaa1e97
6
+ metadata.gz: 4503271efa5b5e0713e2753056c32f6c9fb044935802571d8ef3a4024788dd23f9b6ca7da74e1210d1eaedf03b2b44ecc6e7a0e28059078e815b78161ca4e502
7
+ data.tar.gz: 150281399db54afc60e2f910b3dd4322b0d6f287f97d57b9c530fea39d705bbc4dd0b89cb43807f642674c0ae0ce20d40964bba5fe757a76d4af71fa6b7dc7d1
@@ -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