-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathaws_cleaner.rb
executable file
·169 lines (143 loc) · 4.58 KB
/
aws_cleaner.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# Listen for AWS CloudWatch Events EC2 termination events delivered via SQS
# and remove the node from Chef and Sensu and send a notification
# to Slack
#
# Copyright (c) 2015-2019 Eric Heydrick
# Licensed under The MIT License
#
# ensure gems are present
begin
require 'json'
require 'yaml'
require 'aws-sdk-sqs'
require 'chef-api'
require 'rest-client'
require 'optimist'
require 'slack/poster'
require 'logger'
rescue LoadError => e
raise "Missing gems: #{e}"
end
# require our class
require_relative '../lib/aws-cleaner.rb'
def config(file)
YAML.safe_load(File.read(File.expand_path(file)), [Symbol])
rescue StandardError => e
raise "Failed to open config file: #{e}"
end
def logger(config)
file = config[:log][:file] unless config[:log].nil?
if file
begin
# Check if specified file can be written
awslog = File.open(File.expand_path(file), File::CREAT | File::WRONLY | File::APPEND)
rescue StandardError => e
$stderr.puts "aws-cleaner: ERROR - Failed to open log file #{file} beause of #{e}. STDOUT will be used instead."
end
else
$stdout.puts 'aws-cleaner: WARN - Log file is not specified. STDOUT will be used instead.'
end
# Use STDOUT if it is not possible to write to log file
awslog = STDOUT if awslog.nil?
# Make sure log is flushed out immediately instead of being buffered
awslog.sync = true
logger = Logger.new(awslog)
# Configure logger to escape all data
formatter = Logger::Formatter.new
logger.formatter = proc { |severity, datetime, progname, msg|
formatter.call(severity, datetime, progname, msg.dump)
}
logger
end
def webhook(id, instance_id)
if @config[:webhooks]
@config[:webhooks].each do |hook, hook_config|
if AwsCleaner::Webhooks.fire_webhook(hook_config, @config, instance_id)
@logger.info("Successfully ran webhook #{hook}")
else
@logger.info("Failed to run webhook #{hook}")
end
end
AwsCleaner.new.delete_message(id, @config)
end
end
def chef(id, instance_id, chef_node)
if chef_node
if AwsCleaner::Chef.remove_from_chef(chef_node, @chef_client, @config)
@logger.info("Removed #{chef_node} from Chef")
AwsCleaner.new.delete_message(id, @config)
end
else
@logger.info("Instance #{instance_id} does not exist in Chef, deleting message")
AwsCleaner.new.delete_message(id, @config)
end
end
def sensu(id, instance_id, chef_node)
return unless @config[:sensu][:enable]
if AwsCleaner::Sensu.in_sensu?(chef_node, @config)
if AwsCleaner::Sensu.remove_from_sensu(chef_node, @config)
@logger.info("Removed #{chef_node} from Sensu")
else
@logger.info("Instance #{instance_id} does not exist in Sensu, deleting message")
end
AwsCleaner.new.delete_message(id, @config)
end
end
def closelog(message)
@logger.debug(message) unless message.nil?
@logger.close
end
# get options
opts = Optimist.options do
opt :config, 'Path to config file', type: :string, default: 'config.yml'
end
@config = config(opts[:config])
@logger = logger(@config)
@sqs_client = AwsCleaner::SQS.client(@config)
@chef_client = AwsCleaner::Chef.client(@config)
# to provide backwards compatibility as this key did not exist previously
@config[:sensu][:enable] = if @config[:sensu][:enable].nil?
true
else
@config[:sensu][:enable]
end
# main loop
loop do
begin
# get messages from SQS
messages = @sqs_client.receive_message(
queue_url: @config[:sqs][:queue],
max_number_of_messages: 10,
visibility_timeout: 3
).messages
@logger.info("Got #{messages.size} messages") unless messages.empty?
messages.each_with_index do |message, index|
@logger.info("Looking at message number #{index}")
body = AwsCleaner.new.parse(message.body)
id = message.receipt_handle
unless body
AwsCleaner.new.delete_message(id, @config)
next
end
instance_id = AwsCleaner.new.process_message(body)
if instance_id
chef_node = AwsCleaner::Chef.get_chef_node_name(instance_id, @config)
webhook(id, instance_id)
chef(id, instance_id, chef_node)
sensu(id, instance_id, chef_node)
else
@logger.info('Message not relevant, deleting')
AwsCleaner.new.delete_message(id, @config)
end
end
sleep(5)
rescue Interrupt
closelog('Received Interrupt signal. Quit aws-cleaner')
exit
rescue StandardError => e
@logger.error("Encountered #{e}: #{e.message}")
end
end