class Chef::Handler

The base class for an Exception or Notification Handler. Create your own handler by subclassing Chef::Handler. When a Chef run fails with an uncaught Exception, Chef will set the run_status on your handler and call report

@example

require 'net/smtp'

module MyOrg
  class OhNoes < Chef::Handler

    def report
      # Create the email message
      message  = "From: Your Name <your@mail.address>\n"
      message << "To: Destination Address <someone@example.com>\n"
      message << "Subject: Chef Run Failure\n"
      message << "Date: #{Time.now.rfc2822}\n\n"

      # The Node is available as +node+
      message << "Chef run failed on #{node.name}\n"
      # +run_status+ is a value object with all of the run status data
      message << "#{run_status.formatted_exception}\n"
      # Join the backtrace lines. Coerce to an array just in case.
      message << Array(backtrace).join("\n")

      # Send the email
      Net::SMTP.start('your.smtp.server', 25) do |smtp|
        smtp.send_message message, 'from@address', 'to@address'
      end
    end

  end
end

Attributes

run_status[R]

The Chef::RunStatus object containing data about the Chef run.

Public Class Methods

exception_handlers() click to toggle source

The list of currently configured exception handlers

# File lib/chef/handler.rb, line 143
def self.exception_handlers
  Array(Chef::Config[:exception_handlers])
end
handler_for(*args) click to toggle source

FIXME: Chef::Handler should probably inherit from EventDispatch::Base and should wire up to those events rather than the “notifications” system which is hanging off of Chef::Client. Those “notifications” could then be deprecated in favor of events, and this class could become decoupled from the Chef::Client object.

# File lib/chef/handler.rb, line 64
def self.handler_for(*args)
  if args.include?(:start)
    Chef::Config[:start_handlers] ||= []
    Chef::Config[:start_handlers] |= [self]
  end
  if args.include?(:report)
    Chef::Config[:report_handlers] ||= []
    Chef::Config[:report_handlers] |= [self]
  end
  if args.include?(:exception)
    Chef::Config[:exception_handlers] ||= []
    Chef::Config[:exception_handlers] |= [self]
  end
end
report_handlers() click to toggle source

The list of currently configured report handlers

# File lib/chef/handler.rb, line 117
def self.report_handlers
  Array(Chef::Config[:report_handlers])
end
resolve_handler_instance(handler) click to toggle source
# File lib/chef/handler.rb, line 84
def self.resolve_handler_instance(handler)
  if handler.is_a?(Class)
    if handler.respond_to?(:instance)
      # support retrieving a Singleton reporting object
      handler.instance
    else
      # just a class with no way to insert data
      handler.new
    end
  else
    # the Chef::Config array contains an instance, not a class
    handler
  end
end
run_exception_handlers(run_status) click to toggle source

Run the exception handlers. Usually will be called by a notification from Chef::Client when the run fails.

# File lib/chef/handler.rb, line 149
def self.run_exception_handlers(run_status)
  events = run_status.events
  events.handlers_start(exception_handlers.size)
  Chef::Log.error("Running exception handlers")
  exception_handlers.each do |handler|
    handler = resolve_handler_instance(handler)
    handler.run_report_safely(run_status)
    events.handler_executed(handler)
  end
  events.handlers_completed
  Chef::Log.error("Exception handlers complete")
end
run_report_handlers(run_status) click to toggle source

Run the report handlers. This will usually be called by a notification from Chef::Client

# File lib/chef/handler.rb, line 123
def self.run_report_handlers(run_status)
  events = run_status.events
  events.handlers_start(report_handlers.size)
  Chef::Log.info("Running report handlers")
  report_handlers.each do |handler|
    handler = resolve_handler_instance(handler)
    handler.run_report_safely(run_status)
    events.handler_executed(handler)
  end
  events.handlers_completed
  Chef::Log.info("Report handlers complete")
end
run_start_handlers(run_status) click to toggle source

Run the start handlers. This will usually be called by a notification from Chef::Client

# File lib/chef/handler.rb, line 101
def self.run_start_handlers(run_status)
  Chef::Log.info("Running start handlers")
  start_handlers.each do |handler|
    handler = resolve_handler_instance(handler)
    handler.run_report_safely(run_status)
  end
  Chef::Log.info("Start handlers complete.")
end
start_handlers() click to toggle source

The list of currently configured start handlers

# File lib/chef/handler.rb, line 80
def self.start_handlers
  Array(Chef::Config[:start_handlers])
end

Public Instance Methods

action_collection() click to toggle source
# File lib/chef/handler.rb, line 269
def action_collection
  @run_status.run_context.action_collection
end
all_resources() click to toggle source

@return Array<Chef::Resource> all resources other than unprocessed

# File lib/chef/handler.rb, line 218
def all_resources
  @all_resources ||= action_collection&.filtered_collection(unprocessed: false)&.resources || []
end
backtrace() click to toggle source

The backtrace captured by the uncaught exception that terminated the chef run, or nil if the run completed successfully

# File lib/chef/handler.rb, line 208
def_delegator :@run_status, :backtrace
data() click to toggle source

Return the Hash representation of the run_status

# File lib/chef/handler.rb, line 300
def data
  @run_status.to_h
end
elapsed_time() click to toggle source

The time elapsed between the start and finish of the chef run

# File lib/chef/handler.rb, line 188
def_delegator :@run_status, :elapsed_time
end_time() click to toggle source

The time the chef run ended

# File lib/chef/handler.rb, line 182
def_delegator :@run_status, :end_time
exception() click to toggle source

The uncaught Exception that terminated the chef run, or nil if the run completed successfully

# File lib/chef/handler.rb, line 201
def_delegator :@run_status, :exception
failed?() click to toggle source

Did the chef run fail? True if the chef run raised an uncaught exception

# File lib/chef/handler.rb, line 267
def_delegator :@run_status, :failed?
failed_resources() click to toggle source

@return Array<Chef::Resource> all failed resources

# File lib/chef/handler.rb, line 236
def failed_resources
  @failed_resources ||= action_collection&.filtered_collection(updated: false, up_to_date: false, skipped: false, unprocessed: false)&.resources || []
end
node() click to toggle source

The Chef::Node for this client run

# File lib/chef/handler.rb, line 214
def_delegator :@run_status, :node
report() click to toggle source

The main entry point for report handling. Subclasses should override this method with their own report handling logic.

# File lib/chef/handler.rb, line 275
def report; end
run_context() click to toggle source

The Chef::RunContext object used by the chef run

# File lib/chef/handler.rb, line 194
def_delegator :@run_status, :run_context
run_report_safely(run_status) click to toggle source

Runs the report handler, rescuing and logging any errors it may cause. This ensures that all handlers get a chance to run even if one fails. This method should not be overridden by subclasses unless you know what you’re doing.

@api private

# File lib/chef/handler.rb, line 283
def run_report_safely(run_status)
  run_report_unsafe(run_status)
rescue Exception => e
  Chef::Log.error("Report handler #{self.class.name} raised #{e.inspect}")
  Array(e.backtrace).each { |line| Chef::Log.error(line) }
ensure
  @run_status = nil
end
run_report_unsafe(run_status) click to toggle source

Runs the report handler without any error handling. This method should not be used directly except in testing.

# File lib/chef/handler.rb, line 294
def run_report_unsafe(run_status)
  @run_status = run_status
  report
end
skipped_resources() click to toggle source

@return Array<Chef::Resource> all skipped resources

# File lib/chef/handler.rb, line 242
def skipped_resources
  @skipped_resources ||= action_collection&.filtered_collection(updated: false, up_to_date: false, failed: false, unprocessed: false)&.resources || []
end
start_time() click to toggle source

The time the chef run started

# File lib/chef/handler.rb, line 176
def_delegator :@run_status, :start_time
success?() click to toggle source

Was the chef run successful? True if the chef run did not raise an uncaught exception

# File lib/chef/handler.rb, line 261
def_delegator :@run_status, :success?
unprocessed_resources() click to toggle source

Unprocessed resources are those which are left over in the outer recipe context when a run fails. Sub-resources of unprocessed resourced are impossible to capture because they would require processing the outer resource.

@return Array<Chef::Resource> all unprocessed resources

# File lib/chef/handler.rb, line 252
def unprocessed_resources
  @unprocessed_resources ||= action_collection&.filtered_collection(updated: false, up_to_date: false, failed: false, skipped: false)&.resources || []
end
up_to_date_resources() click to toggle source

@return Array<Chef::Resource> all up_to_date resources

# File lib/chef/handler.rb, line 230
def up_to_date_resources
  @up_to_date_resources ||= action_collection&.filtered_collection(updated: false, skipped: false, failed: false, unprocessed: false)&.resources || []
end
updated_resources() click to toggle source

@return Array<Chef::Resource> all updated resources

# File lib/chef/handler.rb, line 224
def updated_resources
  @updated_resources ||= action_collection&.filtered_collection(up_to_date: false, skipped: false, failed: false, unprocessed: false)&.resources || []
end