chef 17.7.29 → 17.8.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/chef/application/base.rb +1 -1
  3. data/lib/chef/compliance/default_attributes.rb +12 -2
  4. data/lib/chef/compliance/runner.rb +51 -5
  5. data/lib/chef/mixin/powershell_exec.rb +6 -5
  6. data/lib/chef/mixin/why_run.rb +8 -2
  7. data/lib/chef/powershell.rb +8 -6
  8. data/lib/chef/provider/mount/linux.rb +16 -2
  9. data/lib/chef/provider/mount/mount.rb +1 -1
  10. data/lib/chef/provider/package/dnf.rb +1 -1
  11. data/lib/chef/provider/package/powershell.rb +13 -10
  12. data/lib/chef/provider/package/zypper.rb +2 -0
  13. data/lib/chef/provider/package.rb +58 -23
  14. data/lib/chef/provider.rb +1 -1
  15. data/lib/chef/pwsh.rb +3 -2
  16. data/lib/chef/resource/chef_client_config.rb +22 -1
  17. data/lib/chef/resource/chef_client_launchd.rb +1 -1
  18. data/lib/chef/resource/dnf_package.rb +4 -6
  19. data/lib/chef/resource/dpkg_package.rb +5 -0
  20. data/lib/chef/resource/execute.rb +1 -4
  21. data/lib/chef/resource/launchd.rb +0 -3
  22. data/lib/chef/resource/macos_userdefaults.rb +5 -7
  23. data/lib/chef/resource/rhsm_register.rb +31 -0
  24. data/lib/chef/resource/support/client.erb +7 -0
  25. data/lib/chef/resource/windows_feature_powershell.rb +7 -7
  26. data/lib/chef/resource.rb +1 -1
  27. data/lib/chef/version.rb +1 -1
  28. data/spec/functional/resource/dnf_package_spec.rb +34 -20
  29. data/spec/functional/resource/dpkg_package_spec.rb +16 -0
  30. data/spec/functional/resource/macos_userdefaults_spec.rb +20 -0
  31. data/spec/functional/resource/zypper_package_spec.rb +7 -0
  32. data/spec/integration/client/client_spec.rb +31 -0
  33. data/spec/unit/application/base_spec.rb +40 -0
  34. data/spec/unit/compliance/runner_spec.rb +62 -1
  35. data/spec/unit/file_access_control_spec.rb +1 -1
  36. data/spec/unit/mixin/why_run_spec.rb +53 -0
  37. data/spec/unit/provider/group/groupadd_spec.rb +1 -0
  38. data/spec/unit/provider/group/usermod_spec.rb +2 -2
  39. data/spec/unit/provider/ifconfig_spec.rb +2 -0
  40. data/spec/unit/provider/mount/linux_spec.rb +16 -3
  41. data/spec/unit/provider/package/bff_spec.rb +1 -0
  42. data/spec/unit/provider/package/powershell_spec.rb +114 -114
  43. data/spec/unit/provider/package/rubygems_spec.rb +3 -0
  44. data/spec/unit/provider/package/solaris_spec.rb +1 -0
  45. data/spec/unit/provider/service/arch_service_spec.rb +2 -2
  46. data/spec/unit/provider/service/debian_service_spec.rb +1 -0
  47. data/spec/unit/provider/service/gentoo_service_spec.rb +1 -0
  48. data/spec/unit/provider/service/macosx_spec.rb +1 -0
  49. data/spec/unit/provider/service/redhat_spec.rb +4 -1
  50. data/spec/unit/provider/service/simple_service_spec.rb +6 -4
  51. data/spec/unit/provider/user_spec.rb +2 -0
  52. data/spec/unit/resource/dpkg_package_spec.rb +12 -0
  53. data/spec/unit/resource/rhsm_register_spec.rb +42 -0
  54. metadata +9 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c14c01fc55c7847b27979c41e01be0dd845b69840fc048e7abeb8787156bae23
4
- data.tar.gz: 44676d2fe03cc4306efaa4c33f48a9eb3e2d6d736962bcd441ffc4bd0c371621
3
+ metadata.gz: ed316b17dc9b7561f86e7eb1200574f7e8946edea8c6e9e48caabe7166b3fff1
4
+ data.tar.gz: e51295a6e6d7ea03ee3eef0c7f1dcfdc5135567145a9e1317423fe363c5b37d4
5
5
  SHA512:
6
- metadata.gz: 768707842ce349798c256c91d2202c5c7b7f931d63ca53eb9454ed6b424f3a11b38b2c9b94c4c4177959ff179029831a9cca3d3233afa5d2ba7dcd90afba3c23
7
- data.tar.gz: 9a3a715294581c43a663387a3462c745d6bab3a44736ab9bf87e607cfa5cfa7a920de90a918225e05b08a8962b52103c792d97b586d69ef32023afb1e45dc11c
6
+ metadata.gz: 187833c4a2187663915307d01ca12edfabac952728430a651df5c67329b6f0f3c4271f81b840837b7a4a7ae19b6a1aeadb7ac9e4115f082a992e005d1cb7f683
7
+ data.tar.gz: d89346312b01e97606e0f0ba1e2b9fed0f447700332bcacbcb2843d460f51c3f7348383c0ee8a247ce98decfd7eec36fecda1a2ee7d91203834c75347dc2911e
@@ -254,7 +254,7 @@ class Chef::Application::Base < Chef::Application
254
254
  short: "-K KEY_FILE",
255
255
  long: "--validation_key KEY_FILE",
256
256
  description: "Set the validation key file location, used for registering new clients.",
257
- proc: nil
257
+ proc: lambda { |argument| File.expand_path(argument) }
258
258
 
259
259
  option :client_key,
260
260
  short: "-k KEY_FILE",
@@ -28,7 +28,7 @@ class Chef
28
28
  # Controls what is done with the resulting report after the Chef InSpec run.
29
29
  # Accepts a single string value or an array of multiple values.
30
30
  # Accepted values: 'chef-server-automate', 'chef-automate', 'json-file', 'audit-enforcer', 'cli'
31
- "reporter" => "cli",
31
+ "reporter" => nil,
32
32
 
33
33
  # Controls if Chef InSpec profiles should be fetched from Chef Automate or Chef Infra Server
34
34
  # in addition to the default fetch locations provided by Chef Inspec.
@@ -94,7 +94,17 @@ class Chef
94
94
 
95
95
  # Should the built-in compliance phase run. True and false force the behavior. Nil does magic based on if you have
96
96
  # profiles defined but do not have the audit cookbook enabled.
97
- "compliance_phase" => false
97
+ "compliance_phase" => false,
98
+
99
+ "interval" => {
100
+ # control how often inspec scans are run, if not on every node converge
101
+ # notes: false value will result in running inspec scan every converge
102
+ "enabled" => false,
103
+
104
+ # controls how often inspec scans are run (in minutes)
105
+ # notes: only used if interval is enabled above
106
+ "time" => 1440,
107
+ }
98
108
  )
99
109
  end
100
110
  end
@@ -71,7 +71,7 @@ class Chef
71
71
 
72
72
  logger.debug("#{self.class}##{__method__}: enabling Compliance Phase")
73
73
 
74
- report
74
+ report_with_interval
75
75
  end
76
76
 
77
77
  def run_failed(_exception, _run_status)
@@ -82,7 +82,7 @@ class Chef
82
82
 
83
83
  logger.debug("#{self.class}##{__method__}: enabling Compliance Phase")
84
84
 
85
- report
85
+ report_with_interval
86
86
  end
87
87
 
88
88
  ### Below code adapted from audit cookbook's files/default/handler/audit_report.rb
@@ -92,7 +92,6 @@ class Chef
92
92
  fail_if_not_present
93
93
  inspec_gem_source
94
94
  inspec_version
95
- interval
96
95
  owner
97
96
  raise_if_unreachable
98
97
  }.freeze
@@ -106,6 +105,15 @@ class Chef
106
105
  end
107
106
  end
108
107
 
108
+ def report_with_interval
109
+ if interval_seconds_left <= 0
110
+ create_timestamp_file if interval_enabled
111
+ report
112
+ else
113
+ logger.info "Skipping Chef Infra Compliance Phase due to interval settings (next run in #{interval_seconds_left / 60.0} mins)"
114
+ end
115
+ end
116
+
109
117
  def report(report = nil)
110
118
  logger.info "Starting Chef Infra Compliance Phase"
111
119
  report ||= generate_report
@@ -118,7 +126,7 @@ class Chef
118
126
  return
119
127
  end
120
128
 
121
- Array(node["audit"]["reporter"]).each do |reporter_type|
129
+ requested_reporters.each do |reporter_type|
122
130
  logger.info "Reporting to #{reporter_type}"
123
131
  @reporters[reporter_type].send_report(report)
124
132
  end
@@ -325,7 +333,7 @@ class Chef
325
333
  @reporters = {}
326
334
  # Note that the docs don't say you can use an array, but our implementation
327
335
  # supports it.
328
- Array(node["audit"]["reporter"]).each do |type|
336
+ requested_reporters.each do |type|
329
337
  unless SUPPORTED_REPORTERS.include? type
330
338
  raise "CMPL003: '#{type}' found in node['audit']['reporter'] is not a supported reporter for Compliance Phase. Supported reporters are: #{SUPPORTED_REPORTERS.join(", ")}. For more information, see the documentation at https://docs.chef.io/chef_compliance_phase#reporters"
331
339
  end
@@ -358,6 +366,44 @@ class Chef
358
366
  def safe_input_collection
359
367
  run_context&.input_collection
360
368
  end
369
+
370
+ def requested_reporters
371
+ (Array(node["audit"]["reporter"]) + ["cli"]).uniq
372
+ end
373
+
374
+ def create_timestamp_file
375
+ FileUtils.touch report_timing_file
376
+ end
377
+
378
+ def report_timing_file
379
+ ::File.join(Chef::FileCache.create_cache_path("compliance"), "report_timing.json")
380
+ end
381
+
382
+ def interval_time
383
+ @interval_time ||= node.read("audit", "interval", "time")
384
+ end
385
+
386
+ def interval_enabled
387
+ @interval_enabled ||= node.read("audit", "interval", "enabled")
388
+ end
389
+
390
+ def interval_seconds
391
+ @interval_seconds ||=
392
+ if interval_enabled
393
+ logger.debug "Running Chef Infra Compliance Phase every #{interval_time} minutes"
394
+ interval_time * 60
395
+ else
396
+ logger.debug "Running Chef Infra Compliance Phase on every run"
397
+ 0
398
+ end
399
+ end
400
+
401
+ def interval_seconds_left
402
+ return 0 unless ::File.exist?(report_timing_file)
403
+
404
+ seconds_since_last_run = Time.now - ::File.mtime(report_timing_file)
405
+ interval_seconds - seconds_since_last_run
406
+ end
361
407
  end
362
408
  end
363
409
  end
@@ -104,13 +104,14 @@ class Chef
104
104
  #
105
105
  # @param script [String] script to run
106
106
  # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh`
107
+ # @param timeout [Integer, nil] timeout in seconds.
107
108
  # @return [Chef::PowerShell] output
108
- def powershell_exec(script, interpreter = :powershell)
109
+ def powershell_exec(script, interpreter = :powershell, timeout: -1)
109
110
  case interpreter
110
111
  when :powershell
111
- Chef::PowerShell.new(script)
112
+ Chef::PowerShell.new(script, timeout: timeout)
112
113
  when :pwsh
113
- Chef::Pwsh.new(script)
114
+ Chef::Pwsh.new(script, timeout: timeout)
114
115
  else
115
116
  raise ArgumentError, "Expected interpreter of :powershell or :pwsh"
116
117
  end
@@ -118,8 +119,8 @@ class Chef
118
119
 
119
120
  # The same as the #powershell_exec method except this will raise
120
121
  # Chef::PowerShell::CommandFailed if the command fails
121
- def powershell_exec!(script, interpreter = :powershell)
122
- cmd = powershell_exec(script, interpreter)
122
+ def powershell_exec!(script, interpreter = :powershell, **options)
123
+ cmd = powershell_exec(script, interpreter, **options)
123
124
  cmd.error!
124
125
  cmd
125
126
  end
@@ -242,8 +242,12 @@ class Chef
242
242
  end
243
243
  end
244
244
 
245
- def initialize(resource, run_context)
246
- @resource, @run_context = resource, run_context
245
+ attr_accessor :action
246
+
247
+ def initialize(resource, run_context, action)
248
+ @resource = resource
249
+ @run_context = run_context
250
+ @action = action
247
251
  @assertions = Hash.new { |h, k| h[k] = [] }
248
252
  @blocked_actions = []
249
253
  end
@@ -305,6 +309,8 @@ class Chef
305
309
  # "You don't have sufficient privileges to delete #{@new_resource.path}")
306
310
  # end
307
311
  def assert(*actions)
312
+ return unless actions.include?(action.to_sym) || actions.include?(:all_actions)
313
+
308
314
  assertion = Assertion.new
309
315
  yield assertion
310
316
  actions.each { |action| @assertions[action] << assertion }
@@ -33,15 +33,16 @@ class Chef
33
33
  # Requires: .NET Framework 4.0 or higher on the target machine.
34
34
  #
35
35
  # @param script [String] script to run
36
+ # @param timeout [Integer, nil] timeout in seconds.
36
37
  # @return [Object] output
37
- def initialize(script)
38
+ def initialize(script, timeout: -1)
38
39
  # This Powershell DLL source lives here: https://github.com/chef/chef-powershell-shim
39
40
  # Every merge into that repo triggers a Habitat build and promotion. Running
40
41
  # the rake :update_chef_exec_dll task in this (chef/chef) repo will pull down
41
42
  # the built packages and copy the binaries to distro/ruby_bin_folder. Bundle install
42
43
  # ensures that the correct architecture binaries are installed into the path.
43
44
  @dll ||= "Chef.PowerShell.Wrapper.dll"
44
- exec(script)
45
+ exec(script, timeout: timeout)
45
46
  end
46
47
 
47
48
  #
@@ -64,12 +65,13 @@ class Chef
64
65
  raise Chef::PowerShell::CommandFailed, "Unexpected exit in PowerShell command: #{@errors}" if error?
65
66
  end
66
67
 
67
- protected
68
+ private
68
69
 
69
- def exec(script)
70
+ def exec(script, timeout: -1)
70
71
  FFI.ffi_lib @dll
71
- FFI.attach_function :execute_powershell, :ExecuteScript, [:string], :pointer
72
- execution = FFI.execute_powershell(script).read_utf16string
72
+ FFI.attach_function :execute_powershell, :ExecuteScript, %i{string int}, :pointer
73
+ timeout = -1 if timeout == 0 || timeout.nil?
74
+ execution = FFI.execute_powershell(script, timeout).read_utf16string
73
75
  hashed_outcome = Chef::JSONCompat.parse(execution)
74
76
  @result = Chef::JSONCompat.parse(hashed_outcome["result"])
75
77
  @errors = hashed_outcome["errors"]
@@ -29,10 +29,16 @@ class Chef
29
29
  # "findmnt" outputs the mount points with volume.
30
30
  # Convert the mount_point of the resource to a real path in case it
31
31
  # contains symlinks in its parents dirs.
32
+ def loop_mount_points
33
+ # get loop_mount_points only if not initialized earlier
34
+ @loop_mount_points ||= shell_out!("losetup -a").stdout
35
+
36
+ rescue Errno::ENOENT
37
+ @loop_mount_points = ""
38
+ end
32
39
 
33
40
  def mounted?
34
41
  mounted = false
35
-
36
42
  real_mount_point = if ::File.exists? @new_resource.mount_point
37
43
  ::File.realpath(@new_resource.mount_point)
38
44
  else
@@ -45,6 +51,14 @@ class Chef
45
51
  when /\A#{Regexp.escape(real_mount_point)}\s+#{device_mount_regex}\s/
46
52
  mounted = true
47
53
  logger.trace("Special device #{device_logstring} mounted as #{real_mount_point}")
54
+ # Permalink for loop type devices mount points https://rubular.com/r/a0bS4p2RvXsGxx
55
+ when %r{\A#{Regexp.escape(real_mount_point)}\s+\/dev\/loop+[0-9]+\s}
56
+ loop_mount_points.each_line do |mount_point|
57
+ if mount_point.include? device_real
58
+ mounted = true
59
+ break
60
+ end
61
+ end
48
62
  # Permalink for multiple devices mounted to the same mount point(i.e. '/proc') https://rubular.com/r/a356yzspU7N9TY
49
63
  when %r{\A#{Regexp.escape(real_mount_point)}\s+([/\w])+\s}
50
64
  mounted = false
@@ -64,4 +78,4 @@ class Chef
64
78
  end
65
79
  end
66
80
  end
67
- end
81
+ end
@@ -279,4 +279,4 @@ class Chef
279
279
  end
280
280
  end
281
281
  end
282
- end
282
+ end
@@ -98,7 +98,7 @@ class Chef
98
98
  end
99
99
  end
100
100
 
101
- def magic_version
101
+ def magic_version_array
102
102
  package_name_array.each_with_index.map do |pkg, i|
103
103
  magical_version(i).version_with_arch
104
104
  end
@@ -17,13 +17,13 @@
17
17
 
18
18
  require_relative "../package"
19
19
  require_relative "../../resource/powershell_package"
20
- require_relative "../../mixin/powershell_out"
20
+ require_relative "../../mixin/powershell_exec"
21
21
 
22
22
  class Chef
23
23
  class Provider
24
24
  class Package
25
25
  class Powershell < Chef::Provider::Package
26
- include Chef::Mixin::PowershellOut
26
+ include Chef::Mixin::PowershellExec
27
27
 
28
28
  provides :powershell_package
29
29
 
@@ -54,9 +54,9 @@ class Chef
54
54
  # Installs the package specified with the version passed else latest version will be installed
55
55
  def install_package(names, versions)
56
56
  names.each_with_index do |name, index|
57
- cmd = powershell_out(build_powershell_package_command("Install-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
57
+ cmd = powershell_exec(build_powershell_package_command("Install-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
58
58
  next if cmd.nil?
59
- raise Chef::Exceptions::PowershellCmdletException, "Failed to install package due to catalog signing error, use skip_publisher_check to force install" if /SkipPublisherCheck/.match?(cmd.stderr)
59
+ raise Chef::Exceptions::PowershellCmdletException, "Failed to install package due to catalog signing error, use skip_publisher_check to force install" if /SkipPublisherCheck/.match?(cmd.error)
60
60
  end
61
61
  end
62
62
 
@@ -64,11 +64,12 @@ class Chef
64
64
  def remove_package(names, versions)
65
65
  names.each_with_index do |name, index|
66
66
  if versions && !versions[index].nil?
67
- powershell_out(build_powershell_package_command("Uninstall-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
67
+ powershell_exec(build_powershell_package_command("Uninstall-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
68
68
  else
69
69
  version = "0"
70
70
  until version.empty?
71
- version = powershell_out(build_powershell_package_command("Uninstall-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
71
+ version = powershell_exec(build_powershell_package_command("Uninstall-Package '#{name}'"), timeout: new_resource.timeout).result
72
+ version = version.strip if version.respond_to?(:strip)
72
73
  unless version.empty?
73
74
  logger.info("Removed package '#{name}' with version #{version}")
74
75
  end
@@ -82,13 +83,14 @@ class Chef
82
83
  versions = []
83
84
  new_resource.package_name.each_with_index do |name, index|
84
85
  version = if new_resource.version && !new_resource.version[index].nil?
85
- powershell_out(build_powershell_package_command("Find-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).stdout.strip
86
+ powershell_exec(build_powershell_package_command("Find-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).result
86
87
  else
87
- powershell_out(build_powershell_package_command("Find-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
88
+ powershell_exec(build_powershell_package_command("Find-Package '#{name}'"), timeout: new_resource.timeout).result
88
89
  end
89
90
  if version.empty?
90
91
  version = nil
91
92
  end
93
+ version = version.strip if version.respond_to?(:strip)
92
94
  versions.push(version)
93
95
  end
94
96
  versions
@@ -99,13 +101,14 @@ class Chef
99
101
  version_list = []
100
102
  new_resource.package_name.each_with_index do |name, index|
101
103
  version = if new_resource.version && !new_resource.version[index].nil?
102
- powershell_out(build_powershell_package_command("Get-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).stdout.strip
104
+ powershell_exec(build_powershell_package_command("Get-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).result
103
105
  else
104
- powershell_out(build_powershell_package_command("Get-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
106
+ powershell_exec(build_powershell_package_command("Get-Package '#{name}'"), timeout: new_resource.timeout).result
105
107
  end
106
108
  if version.empty?
107
109
  version = nil
108
110
  end
111
+ version = version.strip if version.respond_to?(:strip)
109
112
  version_list.push(version)
110
113
  end
111
114
  version_list
@@ -113,6 +113,8 @@ class Chef
113
113
  end
114
114
  end
115
115
  nil
116
+ rescue
117
+ nil
116
118
  end
117
119
 
118
120
  def available_version(index)
@@ -438,47 +438,81 @@ class Chef
438
438
  @target_version_array ||=
439
439
  begin
440
440
  target_version_array = []
441
- each_package do |package_name, new_version, current_version, candidate_version|
441
+ each_package do |package_name, new_version, current_version, candidate_version, magic_version|
442
442
  case action
443
443
  when :upgrade
444
- if current_version.nil?
445
- # with use_magic_version there may be a package installed, but it fails the user's
446
- # requested new_resource.version constraints
444
+ if version_equals?(current_version, new_version)
445
+ # This is a short-circuit (mostly for the rubygems provider) to avoid needing to
446
+ # expensively query the candidate_version which must come later. This only checks
447
+ # exact matching, the check for fuzzy matching is later.
448
+ logger.trace("#{new_resource} #{package_name} #{new_version} is already installed")
449
+ target_version_array.push(nil)
450
+ elsif current_version.nil?
451
+ # This is a simple check to see if we have any currently installed version at all, this is
452
+ # safe to do before the allow_downgrade check so we check this before.
447
453
  logger.trace("#{new_resource} has no existing installed version. Installing install #{candidate_version}")
448
454
  target_version_array.push(candidate_version)
449
- elsif !use_magic_version? && version_equals?(current_version, new_version)
450
- # this is a short-circuit (mostly for the rubygems provider) to avoid needing to expensively query the candidate_version which must come later
451
- logger.trace("#{new_resource} #{package_name} #{new_version} is already installed")
455
+ elsif !allow_downgrade && version_compare(current_version, candidate_version) == 1
456
+ # This check for downgrading when allow_downgrade is false uses the current_version rather
457
+ # than the magic_version since we never want to downgrade even if the constraints are not met
458
+ # if the version is higher. This check does use the candidate_version and unlazies this so
459
+ # there will a perf hit on idempotent use when allow_downgrade is false which is unavoidable.
460
+ logger.trace("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{candidate_version}. Skipping...)")
452
461
  target_version_array.push(nil)
462
+ elsif magic_version.nil?
463
+ # This is the check for fuzzy matching of the installed_version, where if the installed version
464
+ # does not match the desired version constraints (but is not an exact match) then we need to
465
+ # install the candidate_version (this must come after the allow_downgrade check)
466
+ logger.trace("#{new_resource} has an installed version that does not match the version constraint. Installing install #{candidate_version}")
467
+ target_version_array.push(candidate_version)
453
468
  elsif candidate_version.nil?
469
+ # This check necessarily unlazies the candidate_version and may be expensive (connecting to
470
+ # rubygems.org or whatever). It comes as late as possible.
454
471
  logger.trace("#{new_resource} #{package_name} has no candidate_version to upgrade to")
455
472
  target_version_array.push(nil)
456
473
  elsif version_equals?(current_version, candidate_version)
474
+ # This check sees if the candidate_version is already installed or if we should upgrade/update the
475
+ # package. This is the normal idempotent behavior of :upgrade and is inherently expensive due to
476
+ # unlazying the candidate_version. To prevent the perf hit the version may be specified with a full
477
+ # version constraint. Then the cookbook can roll the version forward and use :upgrade to force version
478
+ # pinning.
457
479
  logger.trace("#{new_resource} #{package_name} #{candidate_version} is already installed")
458
480
  target_version_array.push(nil)
459
- elsif !allow_downgrade && version_compare(current_version, candidate_version) == 1
460
- logger.trace("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{candidate_version}. Skipping...)")
461
- target_version_array.push(nil)
462
481
  else
463
- logger.trace("#{new_resource} #{package_name} is out of date, will upgrade to #{candidate_version}")
482
+ logger.trace("#{new_resource} #{package_name} is out of date, will update to #{candidate_version}")
464
483
  target_version_array.push(candidate_version)
465
484
  end
466
485
 
467
486
  when :install
468
- if new_version && !use_magic_version?
487
+ if current_version && new_version && !allow_downgrade && version_compare(current_version, new_version) == 1
488
+ # This is the idempotency guard for downgrades when downgrades are not allowed. This should perhaps raise
489
+ # an exception since we were told to install an exact package version but we are silently refusing to do so
490
+ # because a higher version is already installed. Maybe we need a flag for users to apply their preferred
491
+ # declarative philosophy? This has to come early and outside of the two code paths below.
492
+ logger.warn("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{new_version}. Skipping...)")
493
+ target_version_array.push(nil)
494
+ elsif new_version && !use_magic_version?
495
+ # This is for "non magic version using" subclasses to do comparisons between the current_version and the
496
+ # desired new_version. XXX: If we converted this to current_version_requirement_satisfied? and made it specific
497
+ # to the current version check and then eliminated the magic_version, we might be able to eliminate separate codepaths
498
+ # here, and eliminate the semantic confusion around the magic_version?
469
499
  if version_requirement_satisfied?(current_version, new_version)
470
500
  logger.trace("#{new_resource} #{package_name} #{current_version} satisfies #{new_version} requirement")
471
501
  target_version_array.push(nil)
472
- elsif current_version && !allow_downgrade && version_compare(current_version, new_version) == 1
473
- logger.warn("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{new_version}. Skipping...)")
474
- target_version_array.push(nil)
475
502
  else
503
+ # XXX: some subclasses seem to depend on this behavior where the new_version can be different from the
504
+ # candidate_version and we install the new_version, it seems like the candidate_version should be fixed to
505
+ # be resolved correctly to the new_version for those providers. although it may just be unit test bugs.
506
+ # it would be more correct to use the candidate_version here, but then it needs to be the correctly resolved
507
+ # candidate_version against the new_version constraint.
476
508
  logger.trace("#{new_resource} #{package_name} #{current_version} needs updating to #{new_version}")
477
509
  target_version_array.push(new_version)
478
510
  end
479
- elsif current_version.nil?
480
- # with use_magic_version there may be a package installed, but it fails the user's
481
- # requested new_resource.version constraints
511
+ elsif magic_version.nil?
512
+ # This is for when we have a "magic version using" subclass and where the installed version does not match the
513
+ # constraint specified in the new_version, so we need to upgrade to the candidate_version. This is the only
514
+ # codepath in the :install branch which references the candidate_version so it is slow, but it is the path where
515
+ # we need to do work anyway. XXX: should we check for candidate_version.nil? somewhere around here?
482
516
  logger.trace("#{new_resource} #{package_name} not installed, installing #{candidate_version}")
483
517
  target_version_array.push(candidate_version)
484
518
  else
@@ -512,8 +546,8 @@ class Chef
512
546
  @packages_missing_candidates ||=
513
547
  begin
514
548
  missing = []
515
- each_package do |package_name, new_version, current_version, candidate_version|
516
- missing.push(package_name) if current_version.nil? && candidate_version.nil?
549
+ each_package do |package_name, new_version, current_version, candidate_version, magic_version|
550
+ missing.push(package_name) if magic_version.nil? && candidate_version.nil?
517
551
  end
518
552
  missing
519
553
  end
@@ -536,7 +570,7 @@ class Chef
536
570
  @forced_packages_missing_candidates ||=
537
571
  begin
538
572
  missing = []
539
- each_package do |package_name, new_version, current_version, candidate_version|
573
+ each_package do |package_name, new_version, current_version, candidate_version, magic_version|
540
574
  next if new_version.nil? || current_version.nil?
541
575
 
542
576
  if use_magic_version?
@@ -559,9 +593,10 @@ class Chef
559
593
  def each_package
560
594
  package_name_array.each_with_index do |package_name, i|
561
595
  candidate_version = candidate_version_array[i]
562
- current_version = use_magic_version? ? magic_version[i] : current_version_array[i]
596
+ current_version = current_version_array[i]
597
+ magic_version = use_magic_version? ? magic_version_array[i] : current_version_array[i]
563
598
  new_version = new_version_array[i]
564
- yield package_name, new_version, current_version, candidate_version
599
+ yield package_name, new_version, current_version, candidate_version, magic_version
565
600
  end
566
601
  end
567
602
 
data/lib/chef/provider.rb CHANGED
@@ -269,7 +269,7 @@ class Chef
269
269
  end
270
270
 
271
271
  def requirements
272
- @requirements ||= ResourceRequirements.new(@new_resource, run_context)
272
+ @requirements ||= ResourceRequirements.new(@new_resource, run_context, action || new_resource.action)
273
273
  end
274
274
 
275
275
  def description(description = "NOT_PASSED")
data/lib/chef/pwsh.rb CHANGED
@@ -24,15 +24,16 @@ class Chef
24
24
  # bindir directory.
25
25
  #
26
26
  # @param script [String] script to run
27
+ # @param timeout [Integer, nil] timeout in seconds.
27
28
  # @return [Object] output
28
- def initialize(script)
29
+ def initialize(script, timeout: -1)
29
30
  @dll = Pwsh.dll
30
31
  super
31
32
  end
32
33
 
33
34
  protected
34
35
 
35
- def exec(script)
36
+ def exec(script, timeout: -1)
36
37
  # Note that we need to override the location of the shared dotnet core library
37
38
  # location. With most .net core applications, you can simply publish them as a
38
39
  # "self-contained" application allowing consumers of the application to run them
@@ -87,6 +87,17 @@ class Chef
87
87
  ]
88
88
  end
89
89
  ```
90
+
91
+ **Report directly to the [Chef Automate data collector endpoint](/automate/data_collection/#configure-chef-infra-client-to-use-the-data-collector-endpoint-in-chef-automate).**
92
+
93
+ ```ruby
94
+ chef_client_config 'Create client.rb' do
95
+ chef_server_url 'https://chef.example.dmz'
96
+ data_collector_server_url 'https://automate.example.dmz'
97
+ data_collector_token 'TEST_TOKEN_TEST'
98
+ end
99
+ ```
100
+
90
101
  DOC
91
102
 
92
103
  # @todo policy_file or policy_group being set requires the other to be set so enforce that.
@@ -231,6 +242,14 @@ class Chef
231
242
  property :additional_config, String,
232
243
  description: "Additional text to add at the bottom of the client.rb config. This can be used to run custom Ruby or to add less common config options"
233
244
 
245
+ property :data_collector_server_url, String,
246
+ description: "The data collector url (typically automate) to send node, converge and compliance data. Note: Data collection reporting to Automate should be performed directly by Chef Infra Server if possible, as this removes the need to distribute tokens to individual nodes.",
247
+ introduced: "17.8"
248
+
249
+ property :data_collector_token, String,
250
+ description: "The data collector token to interact with the data collector server url (Automate). Note: Data collection reporting to Automate should be performed directly by Chef Infra Server if possible, as this removes the need to distribute tokens to individual nodes.",
251
+ introduced: "17.8"
252
+
234
253
  action :create, description: "Create a client.rb config file for configuring #{ChefUtils::Dist::Infra::PRODUCT}." do
235
254
  unless ::Dir.exist?(new_resource.config_directory)
236
255
  directory new_resource.config_directory do
@@ -282,7 +301,9 @@ class Chef
282
301
  ssl_verify_mode: new_resource.ssl_verify_mode,
283
302
  start_handlers: format_handler(new_resource.start_handlers),
284
303
  additional_config: new_resource.additional_config,
285
- policy_persist_run_list: new_resource.policy_persist_run_list
304
+ policy_persist_run_list: new_resource.policy_persist_run_list,
305
+ data_collector_server_url: new_resource.data_collector_server_url,
306
+ data_collector_token: new_resource.data_collector_token
286
307
  )
287
308
  mode "0640"
288
309
  action :create
@@ -134,7 +134,7 @@ class Chef
134
134
  standard_error_path ::File.join(new_resource.log_directory, new_resource.log_file_name)
135
135
  program_arguments ["/bin/bash",
136
136
  "-c",
137
- "echo; echo #{ChefUtils::Dist::Infra::PRODUCT} launchd daemon config has been updated. Manually unloading and reloading the daemon; echo Now unloading the daemon; /bin/launchctl unload /Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist; sleep 2; echo Now loading the daemon; /bin/launchctl load /Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist"]
137
+ "echo; echo #{ChefUtils::Dist::Infra::PRODUCT} launchd daemon config has been updated. Manually unloading and reloading the daemon; echo Now unloading the daemon; sudo /bin/launchctl unload /Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist; sleep 2; echo Now loading the daemon; sudo /bin/launchctl load /Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist"]
138
138
  action :enable # enable creates the plist & triggers service restarts on change
139
139
  end
140
140
 
@@ -68,12 +68,10 @@ class Chef
68
68
  end
69
69
  }
70
70
 
71
- def allow_downgrade(arg = nil)
72
- unless arg.nil?
73
- Chef.deprecated(:dnf_package_allow_downgrade, "the allow_downgrade property on the dnf_package provider is not used, DNF supports downgrades by default.")
74
- end
75
- true
76
- end
71
+ property :allow_downgrade, [ TrueClass, FalseClass ],
72
+ description: "Allow downgrading a package to satisfy requested version requirements.",
73
+ default: true,
74
+ desired_state: false
77
75
  end
78
76
  end
79
77
  end
@@ -37,6 +37,11 @@ class Chef
37
37
  property :response_file_variables, Hash,
38
38
  description: "A Hash of response file variables in the form of {'VARIABLE' => 'VALUE'}.",
39
39
  default: {}, desired_state: false
40
+
41
+ property :allow_downgrade, [ TrueClass, FalseClass ],
42
+ description: "Allow downgrading a package to satisfy requested version requirements.",
43
+ default: true,
44
+ desired_state: false
40
45
  end
41
46
  end
42
47
  end