class Chef::Handler::Sns
A simple Chef
report handler that reports status of a Chef
run through [Amazon SNS](aws.amazon.com/sns/), [including IAM roles support](usage-with-amazon-iam-roles).
A simple Chef
report handler that reports status of a Chef
run through [Amazon SNS](aws.amazon.com/sns/), [including IAM roles support](usage-with-amazon-iam-roles).
Constants
- VERSION
chef-handler-sns Ruby Gem version.
Public Class Methods
Constructs a new `Sns` object.
@example `/etc/chef/client.rb` Configuration Example
require 'chef/handler/sns' sns_handler = Chef::Handler::Sns.new sns_handler.access_key '***AMAZON-KEY***' sns_handler.secret_key '***AMAZON-SECRET***' sns_handler.topic_arn 'arn:aws:sns:***' sns_handler.region 'us-east-1' # optional exception_handlers << sns_handler
@example `/etc/chef/client.rb` Example Using a Hash for Configuration
require 'chef/handler/sns' exception_handlers << Chef::Handler::Sns.new( access_key: '***AMAZON-KEY***', secret_key: '***AMAZON-SECRET***', topic_arn: 'arn:aws:sns:***', region: 'us-east-1' # optional )
@example `/etc/chef/client.rb` Using IAM Roles
require 'chef/handler/sns' exception_handlers << Chef::Handler::Sns.new( topic_arn: 'arn:aws:sns:us-east-1:12341234:MyTopicName' )
@example Using the `chef_handler` Cookbook
# Install the `chef-handler-sns` RubyGem during the compile phase chef_gem 'chef-handler-sns' do compile_time true # Only for Chef 12 end # Then activate the handler with the `chef_handler` LWRP chef_handler 'Chef::Handler::Sns' do source 'chef/handler/sns' arguments( access_key: '***AMAZON-KEY***', secret_key: '***AMAZON-SECRET***', topic_arn: 'arn:aws:sns:***' ) supports exception: true action :enable end
@example Using the `chef-client` Cookbook
node.default['chef_client']['config']['exception_handlers'] = [{ 'class' => 'Chef::Handler::Sns', 'arguments' => { access_key: '***AMAZON-KEY***', secret_key: '***AMAZON-SECRET***', topic_arn: 'arn:aws:sns:***' }.map { |k, v| "#{k}: #{v.inspect}" } }]
@param config [Hash] Configuration options.
@option config [String] :access_key AWS access key (required, but will
try to read it from Ohai with IAM roles).
@option config [String] :secret_key AWS secret key (required, but will
try to read it from Ohai with IAM roles).
@option config [String] :token AWS security token (optional, read from
Ohai with IAM roles). Set to `false` to disable the token detected by Ohai.
@option config [String] :topic_arn AWS topic ARN name (required). @option config [String] :region AWS region (optional). @option config [String] :subject Message subject string in erubis
format (optional).
@option config [String] :body_template Full path of an erubis template
file to use for the message body (optional).
@option config [Array] :filter_opsworks_activities An array of
OpsWorks activities to be triggered with (optional). When set, everything else will be discarded.
@api public
# File lib/chef/handler/sns.rb, line 119 def initialize(config = {}) Chef::Log.debug("#{self.class} initialized.") config_init(config) end
Public Instance Methods
Send a SNS report message.
This is called by Chef
internally.
@return void
@api public
# File lib/chef/handler/sns.rb, line 133 def report config_check(node) return unless allow_publish(node) sns.publish( topic_arn: topic_arn, message: sns_body, subject: sns_subject, message_structure: message_structure ) end
Protected Instance Methods
Checks if the message will be published based in configured OpsWorks activities.
@param node [Chef::Node] Chef
Node that contains the activities.
@return [Boolean] Whether the message needs to be sent.
@api private
# File lib/chef/handler/sns.rb, line 156 def allow_publish(node) return true if filter_opsworks_activity.nil? if node.attribute?('opsworks') && node['opsworks'].attribute?('activity') return filter_opsworks_activity.include?(node['opsworks']['activity']) end Chef::Log.debug( 'You supplied opsworks activity filters, but node attr was not '\ 'found. Returning false' ) false end
Returns the SNS subject used by default.
@return [String] The SNS subject.
@api private
# File lib/chef/handler/sns.rb, line 241 def default_sns_subject chef_client = Chef::Config[:solo] ? 'Chef Solo' : 'Chef Client' status = run_status.success? ? 'success' : 'failure' fix_subject_encoding("#{chef_client} #{status} in #{node.name}"[0..99]) end
Fixes the encoding of SNS bodies.
@param o [String, Object] The body to fix.
@return [String] The message fixed.
@api private
# File lib/chef/handler/sns.rb, line 230 def fix_body_encoding(o) fix_encoding(o, 'UTF-8') end
Fixes or forces the correct encoding of strings.
Replaces wrong characters with `'?'`s.
@param o [String, Object] The string to fix. @param encoding [String] The encoding to use.
@return [String] The message fixed.
@api private
# File lib/chef/handler/sns.rb, line 200 def fix_encoding(o, encoding) encode_opts = { invalid: :replace, undef: :replace, replace: '?' } return o.to_s.encode(encoding, encode_opts) if RUBY_VERSION >= '2.1.0' # Fix ArgumentError: invalid byte sequence in UTF-8 (issue #7) o.to_s.encode(encoding, 'binary', encode_opts) end
Fixes the encoding of SNS subjects.
@param o [String, Object] The subject to fix.
@return [String] The message fixed.
@api private
# File lib/chef/handler/sns.rb, line 217 def fix_subject_encoding(o) fix_encoding(o, 'ASCII') end
Limits the size of a UTF-8 string in bytes without breaking it.
Based on stackoverflow.com/questions/12536080/ ruby-limiting-a-utf-8-string-by-byte-length
@param str [String] The string to limit. @param size [Integer] The string size in bytes.
@return [String] The final string.
@note This code does not work properly on Ruby `< 2.1`.
@api private
# File lib/chef/handler/sns.rb, line 262 def limit_utf8_size(str, size) # Start with a string of the correct byte size, but with a possibly # incomplete char at the end. new_str = str.byteslice(0, size) # We need to force_encoding from utf-8 to utf-8 so ruby will # re-validate (idea from halfelf). until new_str[-1].force_encoding('utf-8').valid_encoding? # Remove the invalid char new_str = new_str.slice(0..-2) end new_str end
Returns the {Aws::SNS} object used to send the messages.
@return [Aws::SNS::Client] The SNS client.
# File lib/chef/handler/sns.rb, line 175 def sns @sns ||= begin params = { access_key_id: access_key, secret_access_key: secret_key, logger: Chef::Log } params[:region] = region if region params[:session_token] = token if token Aws::SNS::Client.new(params) end end
Generates the SNS body.
@return [String] The body string.
@api private
# File lib/chef/handler/sns.rb, line 297 def sns_body template = IO.read(body_template || "#{File.dirname(__FILE__)}/sns/templates/body.erb") context = self eruby = Erubis::Eruby.new(fix_body_encoding(template)) body = fix_body_encoding(eruby.evaluate(context)) limit_utf8_size(body, 262_144) end
Generates the SNS subject.
@return [String] The subject string.
@api private
# File lib/chef/handler/sns.rb, line 283 def sns_subject return default_sns_subject unless subject context = self eruby = Erubis::Eruby.new(fix_subject_encoding(subject)) fix_subject_encoding(eruby.evaluate(context))[0..99] end