# class Chef::Client

## Chef::Client¶ ↑

The main object in a Chef run. Preps a Chef::Node and Chef::RunContext, syncs cookbooks if necessary, and triggers convergence.

### Constants

STDERR_FD

  IO stream that will be used as ‘STDERR’ for formatters. Formatters are configured during `initialize`, so this provides a convenience for setting alternative IO stream during tests.

STDOUT_FD

  IO stream that will be used as ‘STDOUT’ for formatters. Formatters are configured during `initialize`, so this provides a convenience for setting alternative IO stream during tests.

### Attributes

events[R]
json_attribs[R]
node[RW]
ohai[RW]
rest[RW]
run_status[R]
runner[RW]

### Public Class Methods

clear_notifications() click to toggle source

Clears all notifications for client run status events. Primarily for testing purposes.

    # File lib/chef/client.rb, line 70
    def self.clear_notifications
      @run_start_notifications = nil
      @run_completed_successfully_notifications = nil
      @run_failed_notifications = nil
    end

new(json_attribs=nil, args={}) click to toggle source

Creates a new Chef::Client.

    # File lib/chef/client.rb, line 147
    def initialize(json_attribs=nil, args={})
      @json_attribs = json_attribs || {}
      @node = nil
      @run_status = nil
      @runner = nil
      @ohai = Ohai::System.new

      event_handlers = configure_formatters
      event_handlers += Array(Chef::Config[:event_handlers])

      @events = EventDispatch::Dispatcher.new(*event_handlers)
      @override_runlist = args.delete(:override_runlist)
      @specific_recipes = args.delete(:specific_recipes)

      if new_runlist = args.delete(:runlist)
        @json_attribs["run_list"] = new_runlist
      end
    end

run_completed_successfully_notifications() click to toggle source

The list of notifications to be run when the client run completes successfully.

    # File lib/chef/client.rb, line 83
    def self.run_completed_successfully_notifications
      @run_completed_successfully_notifications ||= []
    end

run_failed_notifications() click to toggle source

The list of notifications to be run when the client run fails.

    # File lib/chef/client.rb, line 88
    def self.run_failed_notifications
      @run_failed_notifications ||= []
    end

run_start_notifications() click to toggle source

The list of notifications to be run when the client run starts.

    # File lib/chef/client.rb, line 77
    def self.run_start_notifications
      @run_start_notifications ||= []
    end

when_run_completes_successfully(¬ification_block) click to toggle source

Add a notification for the ‘client run success’ event. The notification is provided as a block. The current Chef::RunStatus object will be passed to the notification_block when the event is triggered.

    # File lib/chef/client.rb, line 102
    def self.when_run_completes_successfully(&notification_block)
      run_completed_successfully_notifications << notification_block
    end

when_run_fails(¬ification_block) click to toggle source

Add a notification for the ‘client run failed’ event. The notification is provided as a block. The current Chef::RunStatus is passed to the notification_block when the event is triggered.

    # File lib/chef/client.rb, line 109
    def self.when_run_fails(&notification_block)
      run_failed_notifications << notification_block
    end

when_run_starts(¬ification_block) click to toggle source

Add a notification for the ‘client run started’ event. The notification is provided as a block. The current Chef::RunStatus object will be passed to the notification_block when the event is triggered.

    # File lib/chef/client.rb, line 95
    def self.when_run_starts(&notification_block)
      run_start_notifications << notification_block
    end

### Public Instance Methods

build_node() click to toggle source

Mutates the `node` object to prepare it for the chef run. Delegates to #policy_builder

### Returns¶ ↑

Chef::Node

  The updated node object

    # File lib/chef/client.rb, line 258
    def build_node
      policy_builder.build_node
      @run_status = Chef::RunStatus.new(node, events)
      node
    end

configure_formatters() click to toggle source

    # File lib/chef/client.rb, line 166
    def configure_formatters
      formatters_for_run.map do |formatter_name, output_path|
        if output_path.nil?
          Chef::Formatters.new(formatter_name, STDOUT_FD, STDERR_FD)
        else
          io = File.open(output_path, "a+")
          io.sync = true
          Chef::Formatters.new(formatter_name, io, io)
        end
      end
    end

converge(run_context) click to toggle source

Converges the node.

### Returns¶ ↑

true

  Always returns true

    # File lib/chef/client.rb, line 341
    def converge(run_context)
      @events.converge_start(run_context)
      Chef::Log.debug("Converging node #{node_name}")
      @runner = Chef::Runner.new(run_context)
      runner.converge
      @events.converge_complete
      true
    rescue Exception
      # TODO: should this be a separate #converge_failed(exception) method?
      @events.converge_complete
      raise
    end

default_formatter() click to toggle source

    # File lib/chef/client.rb, line 186
    def default_formatter
      if (STDOUT.tty? && !Chef::Config[:force_logger]) || Chef::Config[:force_formatter]
        [:doc]
      else
        [:null]
      end
    end

do_windows_admin_check() click to toggle source

    # File lib/chef/client.rb, line 367
    def do_windows_admin_check
      if Chef::Platform.windows?
        Chef::Log.debug("Checking for administrator privileges....")

        if !has_admin_privileges?
          message = "chef-client doesn't have administrator privileges on node #{node_name}."
          if Chef::Config[:fatal_windows_admin_check]
            Chef::Log.fatal(message)
            Chef::Log.fatal("fatal_windows_admin_check is set to TRUE.")
            raise Chef::Exceptions::WindowsNotAdmin, message
          else
            Chef::Log.warn("#{message} This might cause unexpected resource failures.")
          end
        else
          Chef::Log.debug("chef-client has administrator privileges on node #{node_name}.")
        end
      end
    end

expanded_run_list() click to toggle source

Expands the run list. Delegates to the policy_builder.

Normally this does not need to be called from here, it will be called by build_node. This is provided so external users (like the chefspec project) can inject custom behavior into the run process.

### Returns¶ ↑

RunListExpansion: A RunListExpansion or API compatible object.

    # File lib/chef/client.rb, line 362
    def expanded_run_list
      policy_builder.expand_run_list
    end

formatters_for_run() click to toggle source

    # File lib/chef/client.rb, line 178
    def formatters_for_run
      if Chef::Config.formatters.empty?
        [default_formatter]
      else
        Chef::Config.formatters
      end
    end

handle_child_exit(pid_and_status) click to toggle source

    # File lib/chef/client.rb, line 231
    def handle_child_exit(pid_and_status)
      status = pid_and_status[1]
      return true if status.success?
      message = if status.signaled?
        "Chef run process terminated by signal #{status.termsig} (#{Signal.list.invert[status.termsig]})"
      else
        "Chef run process exited unsuccessfully (exit code #{status.exitstatus})"
      end
      raise Exceptions::ChildConvergeError, message
    end

load_node() click to toggle source

Instantiates a Chef::Node object, possibly loading the node’s prior state when using chef-client. Delegates to #policy_builder

### Returns¶ ↑

Chef::Node

  The node object for this chef run

    # File lib/chef/client.rb, line 248
    def load_node
      policy_builder.load_node
      @node = policy_builder.node
    end

node_name() click to toggle source

    # File lib/chef/client.rb, line 295
    def node_name
      name = Chef::Config[:node_name] || ohai[:fqdn] || ohai[:machinename] || ohai[:hostname]
      Chef::Config[:node_name] = name

      raise Chef::Exceptions::CannotDetermineNodeName unless name

      # node names > 90 bytes only work with authentication protocol >= 1.1
      # see discussion in config.rb.
      if name.bytesize > 90
        Chef::Config[:authentication_protocol_version] = "1.1"
      end

      name
    end

policy_builder() click to toggle source

    # File lib/chef/client.rb, line 275
    def policy_builder
      @policy_builder ||= Chef::PolicyBuilder.strategy.new(node_name, ohai.data, json_attribs, @override_runlist, events)
    end

register(client_name=node_name, config=Chef::Config) click to toggle source

### Returns¶ ↑

rest<Chef::REST>

  returns Chef::REST connection object

    # File lib/chef/client.rb, line 313
    def register(client_name=node_name, config=Chef::Config)
      if !config[:client_key]
        @events.skipping_registration(client_name, config)
        Chef::Log.debug("Client key is unspecified - skipping registration")
      elsif File.exists?(config[:client_key])
        @events.skipping_registration(client_name, config)
        Chef::Log.debug("Client key #{config[:client_key]} is present - skipping registration")
      else
        @events.registration_start(node_name, config)
        Chef::Log.info("Client key #{config[:client_key]} is not present - registering")
        Chef::ApiClient::Registration.new(node_name, config[:client_key]).run
        @events.registration_completed
      end
      # We now have the client key, and should use it from now on.
      @rest = Chef::REST.new(config[:chef_server_url], client_name, config[:client_key])
      @resource_reporter = Chef::ResourceReporter.new(@rest)
      @events.register(@resource_reporter)
    rescue Exception => e
      # TODO: munge exception so a semantic failure message can be given to the
      # user
      @events.registration_failed(node_name, e, config)
      raise
    end

run() click to toggle source

Do a full run for this Chef::Client. Calls:

- do_run

This provides a wrapper around do_run allowing the run to be optionally forked.

### Returns¶ ↑

boolean

  Return value from do_run. Should always returns true.

    # File lib/chef/client.rb, line 201
    def run
      # win32-process gem exposes some form of :fork for Process
      # class. So we are seperately ensuring that the platform we're
      # running on is not windows before forking.
      if(Chef::Config[:client_fork] && Process.respond_to?(:fork) && !Chef::Platform.windows?)
        Chef::Log.info "Forking chef instance to converge..."
        pid = fork do
          [:INT, :TERM].each {|s| trap(s, "EXIT") }
          client_solo = Chef::Config[:solo] ? "chef-solo" : "chef-client"
          $0 = "#{client_solo} worker: ppid=#{Process.ppid};start=#{Time.new.strftime("%R:%S")};"
          begin
            Chef::Log.debug "Forked instance now converging"
            do_run
          rescue Exception => e
            Chef::Log.error(e.to_s)
            exit 1
          else
            exit 0
          end
        end
        Chef::Log.debug "Fork successful. Waiting for new chef pid: #{pid}"
        result = Process.waitpid2(pid)
        handle_child_exit(result)
        Chef::Log.debug "Forked instance successfully reaped (pid: #{pid})"
        true
      else
        do_run
      end
    end

run_completed_successfully() click to toggle source

Callback to fire notifications that the run completed successfully

    # File lib/chef/client.rb, line 122
    def run_completed_successfully
      success_handlers = self.class.run_completed_successfully_notifications
      success_handlers.each do |notification|
        notification.call(run_status)
      end
    end

run_failed() click to toggle source

Callback to fire notifications that the Chef run failed

    # File lib/chef/client.rb, line 130
    def run_failed
      failure_handlers = self.class.run_failed_notifications
      failure_handlers.each do |notification|
        notification.call(run_status)
      end
    end

run_ohai() click to toggle source

    # File lib/chef/client.rb, line 291
    def run_ohai
      ohai.all_plugins
    end

run_started() click to toggle source

Callback to fire notifications that the Chef run is starting

    # File lib/chef/client.rb, line 114
    def run_started
      self.class.run_start_notifications.each do |notification|
        notification.call(run_status)
      end
      @events.run_started(run_status)
    end

save_updated_node() click to toggle source

    # File lib/chef/client.rb, line 280
    def save_updated_node
      if Chef::Config[:solo]
        # nothing to do
      elsif policy_builder.temporary_policy?
        Chef::Log.warn("Skipping final node save because override_runlist was given")
      else
        Chef::Log.debug("Saving the current state of node #{node_name}")
        @node.save
      end
    end

setup_run_context() click to toggle source

    # File lib/chef/client.rb, line 264
    def setup_run_context
      run_context = policy_builder.setup_run_context(@specific_recipes)
      assert_cookbook_path_not_empty(run_context)
      run_status.run_context = run_context
      run_context
    end

sync_cookbooks() click to toggle source

    # File lib/chef/client.rb, line 271
    def sync_cookbooks
      policy_builder.sync_cookbooks
    end