summaryrefslogtreecommitdiff
path: root/lib/chef/handler.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/handler.rb')
-rw-r--r--lib/chef/handler.rb235
1 files changed, 235 insertions, 0 deletions
diff --git a/lib/chef/handler.rb b/lib/chef/handler.rb
new file mode 100644
index 0000000000..c4b729eeca
--- /dev/null
+++ b/lib/chef/handler.rb
@@ -0,0 +1,235 @@
+#--
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+require 'chef/client'
+require 'forwardable'
+
+class Chef
+ # == 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
+ #
+ class Handler
+
+ # The list of currently configured start handlers
+ def self.start_handlers
+ Array(Chef::Config[:start_handlers])
+ end
+
+ # Run the start handlers. This will usually be called by a notification
+ # from Chef::Client
+ def self.run_start_handlers(run_status)
+ Chef::Log.info("Running start handlers")
+ start_handlers.each do |handler|
+ handler.run_report_safely(run_status)
+ end
+ Chef::Log.info("Start handlers complete.")
+ end
+
+ # Wire up a notification to run the start handlers when the chef run
+ # starts.
+ Chef::Client.when_run_starts do |run_status|
+ run_start_handlers(run_status)
+ end
+
+ # The list of currently configured report handlers
+ def self.report_handlers
+ Array(Chef::Config[:report_handlers])
+ end
+
+ # Run the report handlers. This will usually be called by a notification
+ # from Chef::Client
+ 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.run_report_safely(run_status)
+ events.handler_executed(handler)
+ end
+ events.handlers_completed
+ Chef::Log.info("Report handlers complete")
+ end
+
+ # Wire up a notification to run the report handlers if the chef run
+ # succeeds.
+ Chef::Client.when_run_completes_successfully do |run_status|
+ run_report_handlers(run_status)
+ end
+
+ # The list of currently configured exception handlers
+ def self.exception_handlers
+ Array(Chef::Config[:exception_handlers])
+ end
+
+ # Run the exception handlers. Usually will be called by a notification
+ # from Chef::Client when the run fails.
+ 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.run_report_safely(run_status)
+ events.handler_executed(handler)
+ end
+ events.handlers_completed
+ Chef::Log.error("Exception handlers complete")
+ end
+
+ # Wire up a notification to run the exception handlers if the chef run fails.
+ Chef::Client.when_run_fails do |run_status|
+ run_exception_handlers(run_status)
+ end
+
+ extend Forwardable
+
+ # The Chef::RunStatus object containing data about the Chef run.
+ attr_reader :run_status
+
+ ##
+ # :method: start_time
+ #
+ # The time the chef run started
+ def_delegator :@run_status, :start_time
+
+ ##
+ # :method: end_time
+ #
+ # The time the chef run ended
+ def_delegator :@run_status, :end_time
+
+ ##
+ # :method: elapsed_time
+ #
+ # The time elapsed between the start and finish of the chef run
+ def_delegator :@run_status, :elapsed_time
+
+ ##
+ # :method: run_context
+ #
+ # The Chef::RunContext object used by the chef run
+ def_delegator :@run_status, :run_context
+
+ ##
+ # :method: exception
+ #
+ # The uncaught Exception that terminated the chef run, or nil if the run
+ # completed successfully
+ def_delegator :@run_status, :exception
+
+ ##
+ # :method: backtrace
+ #
+ # The backtrace captured by the uncaught exception that terminated the chef
+ # run, or nil if the run completed successfully
+ def_delegator :@run_status, :backtrace
+
+ ##
+ # :method: node
+ #
+ # The Chef::Node for this client run
+ def_delegator :@run_status, :node
+
+ ##
+ # :method: all_resources
+ #
+ # An Array containing all resources in the chef run's resource_collection
+ def_delegator :@run_status, :all_resources
+
+ ##
+ # :method: updated_resources
+ #
+ # An Array containing all resources that were updated during the chef run
+ def_delegator :@run_status, :updated_resources
+
+ ##
+ # :method: success?
+ #
+ # Was the chef run successful? True if the chef run did not raise an
+ # uncaught exception
+ def_delegator :@run_status, :success?
+
+ ##
+ # :method: failed?
+ #
+ # Did the chef run fail? True if the chef run raised an uncaught exception
+ def_delegator :@run_status, :failed?
+
+ # The main entry point for report handling. Subclasses should override this
+ # method with their own report handling logic.
+ def report
+ end
+
+ # 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.
+ 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
+
+ # Runs the report handler without any error handling. This method should
+ # not be used directly except in testing.
+ def run_report_unsafe(run_status)
+ @run_status = run_status
+ report
+ end
+
+ # Return the Hash representation of the run_status
+ def data
+ @run_status.to_hash
+ end
+
+ end
+end