chef_handler resource
This page is generated from the Chef source code.To suggest a change, edit the chef_handler.rb file and submit a pull request to the Chef repository.
Use the chef_handler resource to enable handlers during a Chef Infra Client run. The resource allows arguments to be passed to Chef Infra Client, which then applies the conditions defined by the custom handler to the node attribute data collected during a Chef Infra Client run, and then processes the handler based on that data. The chef_handler resource is typically defined early in a node’s run-list (often being the first item). This ensures that all of the handlers will be available for the entire Chef Infra Client run.
New in Chef Infra Client 14.0.
Handler Types
There are three types of handlers:
Handler | Description |
---|---|
exception | An exception handler is used to identify situations that have caused a Chef Infra Client run to fail. An exception handler can be loaded at the start of a Chef Infra Client run by adding a recipe that contains the chef_handler resource to a node's run-list. An exception handler runs when the failed? property for the run_status object returns true . |
report | A report handler is used when a Chef Infra Client run succeeds and reports back on certain details about that Chef Infra Client run. A report handler can be loaded at the start of a Chef Infra Client run by adding a recipe that contains the chef_handler resource to a node's run-list. A report handler runs when the success? property for the run_status object returns true . |
start | A start handler is used to run events at the beginning of a Chef Infra Client run. A start handler can be loaded at the start of a Chef Infra Client run by adding the start handler to the start_handlers setting in the client.rb file or by installing the gem that contains the start handler by using the chef_gem resource in a recipe in the chef-client cookbook. (A start handler may not be loaded using the chef_handler resource.) |
Exception / Report
Exception and report handlers are used to trigger certain behaviors in response to specific situations, typically identified during a Chef Infra Client run.
- An exception handler is used to trigger behaviors when a defined aspect of a Chef Infra Client run fails.
- A report handler is used to trigger behaviors when a defined aspect of a Chef Infra Client run is successful.
Both types of handlers can be used to gather data about a Chef Infra Client run and can provide rich levels of data about all types of usage, which can be used later for trending and analysis across the entire organization.
Exception and report handlers are made available to a Chef Infra Client run in one of the following ways:
- By adding the chef_handler resource to a recipe, and then adding that recipe to the run-list for a node. (The chef_handler resource is available from the chef_handler cookbook.)
- By adding the handler to one of the following settings in the node’s
client.rb file:
exception_handlers
and/orreport_handlers
The chef_handler resource allows exception and report handlers to be enabled from within recipes, which can then added to the run-list for any node on which the exception or report handler should run. The chef_handler resource is available from the chef_handler cookbook.
To use the chef_handler resource in a recipe, add code similar to the following:
chef_handler 'name_of_handler' do
source '/path/to/handler/handler_name'
action :enable
end
For example, a handler for Growl needs to be enabled at the beginning of a Chef Infra Client run:
chef_gem 'chef-handler-growl'
and then is activated in a recipe by using the chef_handler resource:
chef_handler 'Chef::Handler::Growl' do
source 'chef/handler/growl'
action :enable
end
Start
A start handler is not loaded into a Chef Infra Client run from a
recipe, but is instead listed in the client.rb file using the
start_handlers
attribute. The start handler must be installed on the
node and be available to Chef Infra Client prior to the start of a Chef
Infra Client run. Use the chef-client cookbook to install the start
handler.
Start handlers are made available to a Chef Infra Client run in one of the following ways:
- By adding a start handler to the chef-client cookbook, which installs the handler on the node so that it is available to Chef Infra Client at the start of a Chef Infra Client run
- By adding the handler to one of the following settings in the node’s
client.rb file:
start_handlers
The chef-client cookbook can be configured to automatically install and configure gems that are required by a start handler. For example:
node.override['chef_client']['load_gems']['chef-reporting'] = {
require_name: 'chef_reporting',
action: :install,
}
node.override['chef_client']['config']['start_handlers'] = [
{
class: 'Chef::Reporting::StartHandler',
arguments: [],
},
]
include_recipe 'chef-client::config'
Syntax
A chef_handler resource block enables handlers during a chef-client
run. Two handlers—JsonFile
and ErrorReport
—are built into Chef:
chef_handler 'Chef::Handler::JsonFile' do
source 'chef/handler/json_file'
arguments :path => '/var/chef/reports'
action :enable
end
and:
chef_handler 'Chef::Handler::ErrorReport' do
source 'chef/handler/error_report'
action :enable
end
show how to enable those handlers in a recipe.
The full syntax for all of the properties that are available to the chef_handler resource is:
chef_handler 'name' do
arguments Array, Hash
class_name String # default value: 'name' unless specified
source String
type Hash # default value: {"report"=>true, "exception"=>true}
action Symbol # defaults to :enable if not specified
end
where:
chef_handler
is the resource.name
is the name given to the resource block.action
identifies which steps Chef Infra Client will take to bring the node into the desired state.arguments
,class_name
,source
, andtype
are the properties available to this resource.
Actions
The chef_handler resource has the following actions:
:disable
- Disable the handler for the current chef-client run on the current node.
:enable
- Enable the handler for the current chef-client run on the current node.
:nothing
- This resource block does not act unless notified by another resource to take action. Once notified, this resource block either runs immediately or is queued up to run at the end of a Chef Infra Client run.
Properties
The chef_handler resource has the following properties:
arguments
- Ruby Type: Array, Hash
An array of arguments that are passed to the initializer for the handler class. For example:
arguments :key1 => 'val1'
or:
arguments [:key1 => 'val1', :key2 => 'val2']
class_name
- Ruby Type: String | Default Value:
The resource block's name
The name of the handler class. This can be module name-spaced.
source
- Ruby Type: String
The full path to the handler file. Can also be a gem path if the handler ships as part of a Ruby gem.
type
- Ruby Type: Hash | Default Value:
{"report"=>true, "exception"=>true}
The type of handler to register as, i.e. :report, :exception or both.
Common Resource Functionality
Chef resources include common properties, notifications, and resource guards.
Common Properties
The following properties are common to every resource:
compile_time
Ruby Type: true, false | Default Value:
false
Control the phase during which the resource is run on the node. Set to true to run while the resource collection is being built (the
compile phase
). Set to false to run while Chef Infra Client is configuring the node (theconverge phase
).ignore_failure
Ruby Type: true, false, :quiet | Default Value:
false
Continue running a recipe if a resource fails for any reason.
:quiet
will not display the full stack trace and the recipe will continue to run if a resource fails.retries
Ruby Type: Integer | Default Value:
0
The number of attempts to catch exceptions and retry the resource.
retry_delay
Ruby Type: Integer | Default Value:
2
The retry delay (in seconds).
sensitive
Ruby Type: true, false | Default Value:
false
Ensure that sensitive resource data is not logged by Chef InfraClient.
Notifications
notifies
Ruby Type: Symbol, 'Chef::Resource[String]'
A resource may notify another resource to take action when its state changes. Specify a
'resource[name]'
, the:action
that resource should take, and then the:timer
for that action. A resource may notify more than one resource; use anotifies
statement for each resource to be notified.If the referenced resource does not exist, an error is raised. In contrast,
subscribes
will not fail if the source resource is not found.
A timer specifies the point during a Chef Infra Client run at which a notification is run. The following timers are available:
:before
Specifies that the action on a notified resource should be run before processing the resource block in which the notification is located.
:delayed
Default. Specifies that a notification should be queued up, and then executed at the end of a Chef Infra Client run.
:immediate
,:immediately
Specifies that a notification should be run immediately, per resource notified.
The syntax for notifies
is:
notifies :action, 'resource[name]', :timer
subscribes
Ruby Type: Symbol, 'Chef::Resource[String]'
A resource may listen to another resource, and then take action if the
state of the resource being listened to changes. Specify a
'resource[name]'
, the :action
to be taken, and then the :timer
for
that action.
Note that subscribes
does not apply the specified action to the
resource that it listens to - for example:
file '/etc/nginx/ssl/example.crt' do
mode '0600'
owner 'root'
end
service 'nginx' do
subscribes :reload, 'file[/etc/nginx/ssl/example.crt]', :immediately
end
In this case the subscribes
property reloads the nginx
service
whenever its certificate file, located under
/etc/nginx/ssl/example.crt
, is updated. subscribes
does not make any
changes to the certificate file itself, it merely listens for a change
to the file, and executes the :reload
action for its resource (in this
example nginx
) when a change is detected.
If the other resource does not exist, the subscription will not raise an
error. Contrast this with the stricter semantics of notifies
, which
will raise an error if the other resource does not exist.
A timer specifies the point during a Chef Infra Client run at which a notification is run. The following timers are available:
:before
Specifies that the action on a notified resource should be run before processing the resource block in which the notification is located.
:delayed
Default. Specifies that a notification should be queued up, and then executed at the end of a Chef Infra Client run.
:immediate
,:immediately
Specifies that a notification should be run immediately, per resource notified.
The syntax for subscribes
is:
subscribes :action, 'resource[name]', :timer
Guards
A guard property can be used to evaluate the state of a node during the execution phase of a Chef Infra Client run. Based on the results of this evaluation, a guard property is then used to tell Chef Infra Client if it should continue executing a resource. A guard property accepts either a string value or a Ruby block value:
- A string is executed as a shell command. If the command returns
0
, the guard is applied. If the command returns any other value, then the guard property is not applied. String guards in a powershell_script run Windows PowerShell commands and may returntrue
in addition to0
. - A block is executed as Ruby code that must return either
true
orfalse
. If the block returnstrue
, the guard property is applied. If the block returnsfalse
, the guard property is not applied.
A guard property is useful for ensuring that a resource is idempotent by allowing that resource to test for the desired state as it is being executed, and then if the desired state is present, for Chef Infra Client to do nothing.
PropertiesThe following properties can be used to define a guard that is evaluated during the execution phase of a Chef Infra Client run:
not_if
Prevent a resource from executing when the condition returns
true
.only_if
Allow a resource to execute only if the condition returns
true
.
Custom Handlers
A custom handler can be created to support any situation. The easiest way to build a custom handler:
- Download the chef_handler cookbook
- Create a custom handler
- Write a recipe using the chef_handler resource
- Add that recipe to a node’s run-list, often as the first recipe in that run-list
Syntax
The syntax for a handler can vary, depending on what the the situations
the handler is being asked to track, the type of handler being used, and
so on. All custom exception and report handlers are defined using Ruby
and must be a subclass of the Chef::Handler
class.
require 'chef/log'
module ModuleName
class HandlerName < Chef::Handler
def report
# Ruby code goes here
end
end
end
where:
require
ensures that the logging functionality of Chef Infra Client is available to the handlerModuleName
is the name of the module as it exists within theChef
libraryHandlerName
is the name of the handler as it is used in a recipereport
is an interface that is used to define the custom handler
For example, the following shows a custom handler that sends an email that contains the exception data when a Chef Infra Client run fails:
require 'net/smtp'
module OrgName
class SendEmail < Chef::Handler
def report
if run_status.failed?
message = "From: sender_name <sender@example.com>\n"
message << "To: recipient_address <recipient@example.com>\n"
message << "Subject: chef-client Run Failed\n"
message << "Date: #{Time.now.rfc2822}\n\n"
message << "Chef run failed on #{node.name}\n"
message << "#{run_status.formatted_exception}\n"
message << Array(backtrace).join('\n')
Net::SMTP.start('your.smtp.server', 25) do |smtp|
smtp.send_message message, 'sender@example', 'recipient@example'
end
end
end
end
end
and then is used in a recipe like:
send_email 'blah' do
# recipe code
end
report Interface
The report
interface is used to define how a handler will behave and
is a required part of any custom handler. The syntax for the report
interface is as follows:
def report
# Ruby code
end
The Ruby code used to define a custom handler will vary significantly
from handler to handler. Chef Infra Client includes two default
handlers: error_report
and json_file
. Their use of the report
interface is shown below.
The error_report handler:
require 'chef/handler'
require 'chef/resource/directory'
class Chef
class Handler
class ErrorReport < ::Chef::Handler
def report
Chef::FileCache.store('failed-run-data.json', Chef::JSONCompat.to_json_pretty(data), 0640)
Chef::Log.fatal("Saving node information to #{Chef::FileCache.load('failed-run-data.json', false)}")
end
end
end
end
The json_file handler:
require 'chef/handler'
require 'chef/resource/directory'
class Chef
class Handler
class JsonFile < ::Chef::Handler
attr_reader :config
def initialize(config = {})
@config = config
@config[:path] ||= '/var/chef/reports'
@config
end
def report
if exception
Chef::Log.error('Creating JSON exception report')
else
Chef::Log.info('Creating JSON run report')
end
build_report_dir
savetime = Time.now.strftime('%Y%m%d%H%M%S')
File.open(File.join(config[:path], 'chef-run-report-#{savetime}.json'), 'w') do |file|
run_data = data
run_data[:start_time] = run_data[:start_time].to_s
run_data[:end_time] = run_data[:end_time].to_s
file.puts Chef::JSONCompat.to_json_pretty(run_data)
end
end
def build_report_dir
unless File.exist?(config[:path])
FileUtils.mkdir_p(config[:path])
File.chmod(00700, config[:path])
end
end
end
end
end
Optional Interfaces
The following interfaces may be used in a handler in the same way as thereport
interface to override the default handler behavior in Chef
Infra Client. That said, the following interfaces are not typically used
in a handler and, for the most part, are completely unnecessary for a
handler to work properly and/or as desired.data
The data
method is used to return the Hash representation of the
run_status
object. For example:
def data
@run_status.to_hash
end
run_report_safely
The run_report_safely
method is used to run the report handler,
rescuing and logging errors that may arise as the handler runs and
ensuring that all handlers get a chance to run during a Chef Infra
Client run (even if some handlers fail during that run). In general,
this method should never be used as an interface in a custom handler
unless this default behavior simply must be overridden.
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
The run_report_unsafe
method is used to run the report handler without
any error handling. This method should never be used directly in any
handler, except during testing of that handler. For example:
def run_report_unsafe(run_status)
@run_status = run_status
report
end
run_status Object
The run_status
object is initialized by Chef Infra Client before the
report
interface is run for any handler. The run_status
object keeps
track of the status of a Chef Infra Client run and will contain some (or
all) of the following properties:
Property | Description |
---|---|
all_resources | A list of all resources that are included in the resource_collection property for the current Chef Infra Client run. |
backtrace | A backtrace associated with the uncaught exception data that caused a Chef Infra Client run to fail, if present; nil for a successful Chef Infra Client run. |
elapsed_time | The amount of time between the start (start_time ) and end (end_time ) of a Chef Infra Client run. |
end_time | The time at which a Chef Infra Client run ended. |
exception | The uncaught exception data which caused a Chef Infra Client run to fail; nil for a successful Chef Infra Client run. |
failed? | Show that a Chef Infra Client run has failed when uncaught exceptions were raised during a Chef Infra Client run. An exception handler runs when the failed? indicator is true . |
node | The node on which a Chef Infra Client run occurred. |
run_context | An instance of the Chef::RunContext object; used by Chef Infra Client to track the context of the run; provides access to the cookbook_collection , resource_collection , and definitions properties. |
start_time | The time at which a Chef Infra Client run started. |
success? | Show that a Chef Infra Client run succeeded when uncaught exceptions were not raised during a Chef Infra Client run. A report handler runs when the success? indicator is true . |
updated_resources | A list of resources that were marked as updated as a result of a Chef Infra Client run. |
Note
These properties are not always available. For example, a start handler
runs at the beginning of Chef Infra Client run, which means that
properties like end_time
and elapsed_time
are still unknown and will
be unavailable to the run_status
object.
Examples
The following examples demonstrate various approaches for using the chef_handler resource in recipes:
Enable the ‘MyHandler’ handler
The following example shows how to enable a fictional ‘MyHandler’ handler which is located on disk at /etc/chef/my_handler.rb
. The handler will be configured to run with Chef Infra Client and will be passed values to the handler’s initializer method:
chef_handler 'MyHandler' do
source '/etc/chef/my_handler.rb' # the file should already be at this path
arguments path: '/var/chef/reports'
action :enable
end
Enable handlers during the compile phase
chef_handler 'Chef::Handler::JsonFile' do
source 'chef/handler/json_file'
arguments path: '/var/chef/reports'
action :enable
compile_time true
end
Handle only exceptions
chef_handler 'Chef::Handler::JsonFile' do
source 'chef/handler/json_file'
arguments path: '/var/chef/reports'
type exception: true
action :enable
end
Cookbook Versions (a custom handler)
@juliandunn created a custom report handler that logs all of the cookbooks and cookbook versions that were used during a Chef Infra Client run, and then reports after the run is complete.
cookbook_versions.rb:
The following custom handler defines how cookbooks and cookbook versions that are used during a Chef Infra Client run will be compiled into a report using the Chef::Log
class in Chef Infra Client:
require 'chef/log'
module Chef
class CookbookVersionsHandler < Chef::Handler
def report
cookbooks = run_context.cookbook_collection
Chef::Log.info('Cookbooks and versions run: #{cookbooks.map {|x| x.name.to_s + ' ' + x.version }}')
end
end
end
default.rb:
The following recipe is added to the run-list for every node on which a list of cookbooks and versions will be generated as report output after every Chef Infra Client run.
cookbook_file '/etc/chef/cookbook_versions.rb' do
source 'cookbook_versions.rb'
action :create
end
chef_handler 'Chef::CookbookVersionsHandler' do
source '/etc/chef/cookbook_versions.rb'
type report: true
action :enable
end
This recipe will generate report output similar to the following:
[2013-11-26T03:11:06+00:00] INFO: Chef Infra Client Run complete in 0.300029878 seconds
[2013-11-26T03:11:06+00:00] INFO: Running report handlers
[2013-11-26T03:11:06+00:00] INFO: Cookbooks and versions run: ["cookbook_versions_handler 1.0.0"]
[2013-11-26T03:11:06+00:00] INFO: Report handlers complete
JsonFile Handler
The JsonFile handler is available from the chef_handler
cookbook and can be used with exceptions and reports. It serializes run status data to a JSON file. This handler may be enabled in one of the following ways.
By adding the following lines of Ruby code to either the client.rb file or the solo.rb file, depending on how Chef Infra Client is being run:
require 'chef/handler/json_file'
report_handlers << Chef::Handler::JsonFile.new(path: '/var/chef/reports')
exception_handlers << Chef::Handler::JsonFile.new(path: '/var/chef/reports')
By using the chef_handler
resource in a recipe, similar to the following:
chef_handler 'Chef::Handler::JsonFile' do
source 'chef/handler/json_file'
arguments path: '/var/chef/reports'
action :enable
end
After it has run, the run status data can be loaded and inspected via Interactive Ruby (IRb):
irb(main):002:0> require 'json' => true
irb(main):003:0> require 'chef' => true
irb(main):004:0> r = JSON.parse(IO.read('/var/chef/reports/chef-run-report-20110322060731.json')) => ... output truncated
irb(main):005:0> r.keys => ['end_time', 'node', 'updated_resources', 'exception', 'all_resources', 'success', 'elapsed_time', 'start_time', 'backtrace']
irb(main):006:0> r['elapsed_time'] => 0.00246
Register the JsonFile handler
chef_handler 'Chef::Handler::JsonFile' do
source 'chef/handler/json_file'
arguments path: '/var/chef/reports'
action :enable
end
ErrorReport Handler
The ErrorReport Handler is built into Chef Infra Client and can be used for both exceptions and reports. It serializes error report data to a JSON file. This handler may be enabled in one of the following ways.
By adding the following lines of Ruby code to either the client.rb file or the solo.rb file, depending on how Chef Infra Client is being run:
require 'chef/handler/error_report'
report_handlers << Chef::Handler::ErrorReport.new
exception_handlers << Chef::Handler::ErrorReport.new
By using the chef_handler
resource in a recipe, similar to the following:
chef_handler 'Chef::Handler::ErrorReport' do
source 'chef/handler/error_report'
action :enable
end