chef 17.7.29 → 17.8.25
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.
- checksums.yaml +4 -4
- data/lib/chef/application/base.rb +1 -1
- data/lib/chef/compliance/default_attributes.rb +12 -2
- data/lib/chef/compliance/runner.rb +51 -5
- data/lib/chef/mixin/powershell_exec.rb +6 -5
- data/lib/chef/mixin/why_run.rb +8 -2
- data/lib/chef/powershell.rb +8 -6
- data/lib/chef/provider/mount/linux.rb +16 -2
- data/lib/chef/provider/mount/mount.rb +1 -1
- data/lib/chef/provider/package/dnf.rb +1 -1
- data/lib/chef/provider/package/powershell.rb +13 -10
- data/lib/chef/provider/package/zypper.rb +2 -0
- data/lib/chef/provider/package.rb +58 -23
- data/lib/chef/provider.rb +1 -1
- data/lib/chef/pwsh.rb +3 -2
- data/lib/chef/resource/chef_client_config.rb +22 -1
- data/lib/chef/resource/chef_client_launchd.rb +1 -1
- data/lib/chef/resource/dnf_package.rb +4 -6
- data/lib/chef/resource/dpkg_package.rb +5 -0
- data/lib/chef/resource/execute.rb +1 -4
- data/lib/chef/resource/launchd.rb +0 -3
- data/lib/chef/resource/macos_userdefaults.rb +5 -7
- data/lib/chef/resource/rhsm_register.rb +31 -0
- data/lib/chef/resource/support/client.erb +7 -0
- data/lib/chef/resource/windows_feature_powershell.rb +7 -7
- data/lib/chef/resource.rb +1 -1
- data/lib/chef/version.rb +1 -1
- data/spec/functional/resource/dnf_package_spec.rb +34 -20
- data/spec/functional/resource/dpkg_package_spec.rb +16 -0
- data/spec/functional/resource/macos_userdefaults_spec.rb +20 -0
- data/spec/functional/resource/zypper_package_spec.rb +7 -0
- data/spec/integration/client/client_spec.rb +31 -0
- data/spec/unit/application/base_spec.rb +40 -0
- data/spec/unit/compliance/runner_spec.rb +62 -1
- data/spec/unit/file_access_control_spec.rb +1 -1
- data/spec/unit/mixin/why_run_spec.rb +53 -0
- data/spec/unit/provider/group/groupadd_spec.rb +1 -0
- data/spec/unit/provider/group/usermod_spec.rb +2 -2
- data/spec/unit/provider/ifconfig_spec.rb +2 -0
- data/spec/unit/provider/mount/linux_spec.rb +16 -3
- data/spec/unit/provider/package/bff_spec.rb +1 -0
- data/spec/unit/provider/package/powershell_spec.rb +114 -114
- data/spec/unit/provider/package/rubygems_spec.rb +3 -0
- data/spec/unit/provider/package/solaris_spec.rb +1 -0
- data/spec/unit/provider/service/arch_service_spec.rb +2 -2
- data/spec/unit/provider/service/debian_service_spec.rb +1 -0
- data/spec/unit/provider/service/gentoo_service_spec.rb +1 -0
- data/spec/unit/provider/service/macosx_spec.rb +1 -0
- data/spec/unit/provider/service/redhat_spec.rb +4 -1
- data/spec/unit/provider/service/simple_service_spec.rb +6 -4
- data/spec/unit/provider/user_spec.rb +2 -0
- data/spec/unit/resource/dpkg_package_spec.rb +12 -0
- data/spec/unit/resource/rhsm_register_spec.rb +42 -0
- metadata +9 -7
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ed316b17dc9b7561f86e7eb1200574f7e8946edea8c6e9e48caabe7166b3fff1
         | 
| 4 | 
            +
              data.tar.gz: e51295a6e6d7ea03ee3eef0c7f1dcfdc5135567145a9e1317423fe363c5b37d4
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 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:  | 
| 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" =>  | 
| 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 | 
            -
                     | 
| 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 | 
            -
                     | 
| 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 | 
            -
                     | 
| 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 | 
            -
                     | 
| 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
         | 
    
        data/lib/chef/mixin/why_run.rb
    CHANGED
    
    | @@ -242,8 +242,12 @@ class Chef | |
| 242 242 | 
             
                      end
         | 
| 243 243 | 
             
                    end
         | 
| 244 244 |  | 
| 245 | 
            -
                     | 
| 246 | 
            -
             | 
| 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 }
         | 
    
        data/lib/chef/powershell.rb
    CHANGED
    
    | @@ -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 | 
            -
                 | 
| 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,  | 
| 72 | 
            -
                   | 
| 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
         | 
| @@ -17,13 +17,13 @@ | |
| 17 17 |  | 
| 18 18 | 
             
            require_relative "../package"
         | 
| 19 19 | 
             
            require_relative "../../resource/powershell_package"
         | 
| 20 | 
            -
            require_relative "../../mixin/ | 
| 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:: | 
| 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 =  | 
| 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. | 
| 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 | 
            -
                           | 
| 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 =  | 
| 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 | 
            -
                                     | 
| 86 | 
            +
                                    powershell_exec(build_powershell_package_command("Find-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).result
         | 
| 86 87 | 
             
                                  else
         | 
| 87 | 
            -
                                     | 
| 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 | 
            -
                                     | 
| 104 | 
            +
                                    powershell_exec(build_powershell_package_command("Get-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).result
         | 
| 103 105 | 
             
                                  else
         | 
| 104 | 
            -
                                     | 
| 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
         | 
| @@ -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 | 
| 445 | 
            -
                              #  | 
| 446 | 
            -
                              #  | 
| 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 ! | 
| 450 | 
            -
                              #  | 
| 451 | 
            -
                               | 
| 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  | 
| 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 && ! | 
| 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  | 
| 480 | 
            -
                              #  | 
| 481 | 
            -
                              #  | 
| 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  | 
| 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 =  | 
| 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 | 
            -
                   | 
| 72 | 
            -
                     | 
| 73 | 
            -
             | 
| 74 | 
            -
                     | 
| 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
         |