I am trying to understand the Chef documentation on Custom Handlers.
The steps seem easy enough but I still don't understand how it works.
Step 1 is no longer necessary as chef_handler
cookbook is now part of Chef.
Step 2 creates a handler. The example given is
require 'net/smtp'
module OrgName
class SendEmail < Chef::Handler
def report
if run_status.failed? then
message = "From: sender_name <[email protected]>\n"
message << "To: recipient_address <[email protected]>\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
Now comes step 3 which I don't understand, add the following to a recipe
send_email 'blah' do
# recipe code
end
When I run my recipe it just produces the error message which I expected to begin with:
FATAL: NoMethodError: undefined method `send_email` for cookbook: test, recipe: default :Chef::Recipe
How is this supposed to work? Are there other simple but working examples of custom handlers?
For Chef 15.0.300
The documentation on Chef handlers is a little unclear indeed at first look. Looking into more details, one thing is clear. chef_handler is a Chef resource (since Chef client 14). It supports two handlers by default - JsonFile
and ErrorReport
.
There is more documentation on custom handlers. This shows that we need to have some chef_gem
installed for the custom handler(s) to work (apart from the above two).
A handler that collects exception and report handler data and then uses pony to send email reports that are based on Erubis templates.
Connecting the dots... then we should have a recipe that installs the chef_gem
and enables the handler. I created a separate cookbook, which can be reused by other cookbooks.
In the below example, I have a cookbook my_handlers
:
recipes/json.rb
recipes/error_report.rb
recipes/email.rb
libraries/send_mail.rb
In my_handlers/recipes/email.rb
:
chef_gem 'chef-handler-mail'
chef_handler 'MailHandler' do
source 'chef/handler/mail'
arguments :to_address => 'root'
action :enable
end
In my_handlers/libraries/send_mail.rb
:
require 'net/smtp'
module HandlerSendEmail
class Helper
def send_email_on_run_failure(node_name)
message = "From: Chef <[email protected]>\n"
message << "To: Grant <[email protected]>\n"
message << "Subject: Chef run failed\n"
message << "Date: #{Time.now.rfc2822}\n\n"
message << "Chef run failed on #{node_name}\n"
Net::SMTP.start('your.smtp.server', 25) do |smtp|
smtp.send_message message, '[email protected]', '[email protected]'
end
end
end
end
Now I have something I can reuse in other cookbooks and choose the handler I want to enable.
Let's say I have a cookbook cookbook1
in which I want to use the email handler on failure:
In cookbook1/metadata.rb
:
depends 'my_handlers'
In recipes/default.rb
:
# Enable the Chef email handler by calling "my_handler"
include_recipe 'my_handlers::email'
Chef.event_handler do
on :run_failed do
HandlerSendEmail::Helper.new.send_email_on_run_failure(Chef.run_context.node.name)
end
end
# Usual resource declarations
ruby_block 'fail the run' do
block do
fail 'deliberately fail the run'
end
end
If failure occurs (deliberately as above), Chef email handler is invoked and triggers an email indicating failure.