bin/handler-ec2_node.rb in sensu-plugins-aws-0.0.4 vs bin/handler-ec2_node.rb in sensu-plugins-aws-1.0.0
- old
+ new
@@ -1,8 +1,10 @@
#!/usr/bin/env ruby
#
# CHANGELOG:
+# * 0.5.0:
+# - Adds configuration to filter by state reason
# * 0.4.0:
# - Adds ability to specify a list of states an individual client can have in
# EC2. If none is specified, it filters out 'terminated' and 'stopped'
# instances by default.
# - Updates how we are "puts"-ing to the log.
@@ -20,22 +22,25 @@
#
# This handler deletes a Sensu client if it's been stopped or terminated in EC2.
# Optionally, you may specify a client attribute `ec2_states`, a list of valid
# states an instance may have.
#
+# You may also specify a client attribute `ec2_state_reasons`, a list of regular
+# expressions to match state reasons against. This is useful if you want to fail
+# on any `Client.*` state reason or on `Server.*` state reason. The default is
+# to match any state reason `.*` Regardless, eventually a client will be
+# deleted once AWS stops responding that the instance id exists.
+#
# NOTE: The implementation for correlating Sensu clients to EC2 instances may
# need to be modified to fit your organization. The current implementation
# assumes that Sensu clients' names are the same as their instance IDs in EC2.
# If this is not the case, you can either sub-class this handler and override
-# `ec2_node_exists?` in your own organization-specific handler, or modify this
+# `ec2_node_should_be_deleted?` in your own organization-specific handler, or modify this
# handler to suit your needs.
#
-# Requires the following Rubygems (`gem install $GEM`):
-# - sensu-plugin
-# - fog
#
-# Requires a Sensu configuration snippet:
+# Optional a Sensu configuration snippet:
# {
# "aws": {
# "access_key": "adsafdafda",
# "secret_key": "qwuieohajladsafhj23nm",
# "region": "us-east-1c"
@@ -45,11 +50,16 @@
# Or you can set the following environment variables:
# - AWS_ACCESS_KEY_ID
# - AWS_SECRET_ACCESS_KEY
# - EC2_REGION
#
+# If none of the settings are found it will then attempt to
+# generate temporary credentials from the IAM instance profile
#
+# If region is not specified in either of the above 2 mechanisms
+# we will make a request for the EC2 instances current region.
+#
# To use, you can set it as the keepalive handler for a client:
# {
# "client": {
# "name": "i-424242",
# "address": "127.0.0.1",
@@ -90,67 +100,80 @@
# Released under the same terms as Sensu (the MIT license); see
# LICENSE for details
require 'timeout'
require 'sensu-handler'
-require 'fog'
+require 'net/http'
+require 'uri'
+require 'aws-sdk'
+require 'sensu-plugins-aws'
class Ec2Node < Sensu::Handler
+ include Common
+
def filter; end
def handle
- # #YELLOW
- unless ec2_node_exists? # rubocop:disable UnlessElse
+ if ec2_node_should_be_deleted?
delete_sensu_client!
else
- puts "[EC2 Node] #{@event['client']['name']} appears to exist in EC2"
+ puts "[EC2 Node] #{@event['client']['name']} is in an invalid state"
end
end
def delete_sensu_client!
response = api_request(:DELETE, '/clients/' + @event['client']['name']).code
deletion_status(response)
end
- def ec2_node_exists?
- states = acquire_valid_states
- filtered_instances = ec2.servers.select { |s| states.include?(s.state) }
- instance_ids = filtered_instances.map(&:id)
- instance_ids.each do |id|
- return true if id == @event['client']['name']
+ def ec2_node_should_be_deleted?
+ ec2 = Aws::EC2::Client.new(region: region)
+ states = @event['client']['ec2_states'] || settings['ec2_node']['ec2_states'] || ['shutting-down', 'terminated', 'stopping', 'stopped']
+ begin
+ instances = ec2.describe_instances(instance_ids: [@event['client']['name']]).reservations[0]
+ if instances.nil?
+ true
+ else
+ instance = instances.instances[0]
+ state_reason = instance.state_reason.code
+ state = instance.state.name
+ states.include?(state) && state_reasons.any? { |reason| Regexp.new(reason) =~ state_reason }
+ end
+ rescue Aws::EC2::Errors::InvalidInstanceIDNotFound
+ true
end
- false # no match found, node doesn't exist
end
- def ec2
- @ec2 ||= begin
- key = settings['aws']['access_key'] || ENV['AWS_ACCESS_KEY_ID']
- secret = settings['aws']['secret_key'] || ENV['AWS_SECRET_ACCESS_KEY']
- region = settings['aws']['region'] || ENV['EC2_REGION']
- Fog::Compute.new(provider: 'AWS',
- aws_access_key_id: key,
- aws_secret_access_key: secret,
- region: region)
+ def region
+ @region ||= begin
+ region_check = ENV['EC2_REGION']
+ region_check = settings['aws']['region'] if settings.key? 'aws'
+ if region_check.nil? || region_check.empty?
+ region_check = Net::HTTP.get(URI('http://169.254.169.254/latest/meta-data/placement/availability-zone'))
+ matches = /(\w+\-\w+\-\d+)/.match(region_check)
+ if !matches.nil? && !matches.captures.empty?
+ region_check = matches.captures[0]
+ end
+ end
+ region_check
end
end
+ def state_reasons
+ default_reasons = %w('UserInitiatedShutdown', 'SpotInstanceTermination', 'InstanceInitiatedShutdown')
+ reasons = @event['client']['ec2_state_reasons'] || settings['ec2_node']['ec2_state_reasons'] || default_reasons
+ @state_reasons ||= reasons.each { |reason| Regexp.new(reason) }
+ end
+
def deletion_status(code)
case code
when '202'
- puts "[EC2 Node] 202: Successfully deleted Sensu client: #{node}"
+ puts "[EC2 Node] 202: Successfully deleted Sensu client: #{@event['client']['name']}"
when '404'
- puts "[EC2 Node] 404: Unable to delete #{node}, doesn't exist!"
+ puts "[EC2 Node] 404: Unable to delete #{@event['client']['name']}, doesn't exist!"
when '500'
- puts "[EC2 Node] 500: Miscellaneous error when deleting #{node}"
+ puts "[EC2 Node] 500: Miscellaneous error when deleting #{@event['client']['name']}"
else
- puts "[EC2 Node] #{res}: Completely unsure of what happened!"
- end
- end
-
- def acquire_valid_states
- if @event['client'].key?('ec2_states')
- return @event['client']['ec2_states']
- else
- return ['running']
+ puts "[EC2 Node] #{code}: Completely unsure of what happened!"
end
end
end