aws-cleaner 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/aws_cleaner.rb +180 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6116869f6f0b2bfb0001c2273dd52a5b74727991
|
4
|
+
data.tar.gz: 283ce83a3765d2049e5f46cc4f973e025427cd48
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0ddacb13b71c9394bbfbbc34cb9ef8924ef7c1846d32725edb3dfbef1b121d0abb68cf19652ea2129c278161033537721aef5a78099012d6f68d9cee29ab06db
|
7
|
+
data.tar.gz: d5a21afb386566bbe6985b91a8965491f31241fa84d85401e1add33c1aff34a083513f9a8da43c55926b2e12a6f372a54d4e33d9aa2fd738e4f43e9d419df5e1
|
data/bin/aws_cleaner.rb
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Listen for AWS Config EC2 termination events delivered via SQS
|
4
|
+
# and remove the node from Chef and Sensu and send a notification
|
5
|
+
# to Hipchat
|
6
|
+
#
|
7
|
+
# Copyright (c) 2015 Eric Heydrick
|
8
|
+
# Licensed under The MIT License
|
9
|
+
#
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'json'
|
13
|
+
require 'yaml'
|
14
|
+
require 'aws-sdk-core'
|
15
|
+
require 'chef-api'
|
16
|
+
require 'hipchat'
|
17
|
+
require 'rest-client'
|
18
|
+
require 'trollop'
|
19
|
+
rescue LoadError => e
|
20
|
+
raise "Missing gems: #{e}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def config(file)
|
24
|
+
YAML.load(File.read(file))
|
25
|
+
rescue StandardError => e
|
26
|
+
raise "Failed to open config file: #{e}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# get options
|
30
|
+
opts = Trollop::options do
|
31
|
+
opt :config, 'Path to config file', :type => :string, :default => 'config.yml'
|
32
|
+
end
|
33
|
+
|
34
|
+
@config = config(opts[:config])
|
35
|
+
|
36
|
+
@sqs = Aws::SQS::Client.new(@config[:aws])
|
37
|
+
|
38
|
+
@chef = ChefAPI::Connection.new(
|
39
|
+
endpoint: @config[:chef][:url],
|
40
|
+
client: @config[:chef][:client],
|
41
|
+
key: @config[:chef][:key]
|
42
|
+
)
|
43
|
+
|
44
|
+
# delete the message from SQS
|
45
|
+
def delete_message(id)
|
46
|
+
delete = @sqs.delete_message(
|
47
|
+
queue_url: @config[:sqs][:queue],
|
48
|
+
receipt_handle: id
|
49
|
+
)
|
50
|
+
delete ? true : false
|
51
|
+
end
|
52
|
+
|
53
|
+
# return the body of the SQS message in JSON
|
54
|
+
def parse(body)
|
55
|
+
JSON.parse(body)
|
56
|
+
rescue JSON::ParserError
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
|
60
|
+
# return the instance_id of the terminated instance
|
61
|
+
def process_message(message_body)
|
62
|
+
return false if message_body['configurationItem'].nil? &&
|
63
|
+
message_body['configurationItemDiff'].nil?
|
64
|
+
|
65
|
+
if message_body['configurationItem']['resourceType'] == 'AWS::EC2::Instance' &&
|
66
|
+
message_body['configurationItem']['configurationItemStatus'] == 'ResourceDeleted' &&
|
67
|
+
message_body['configurationItemDiff']['changeType'] == 'DELETE'
|
68
|
+
instance_id = message_body['configurationItem']['resourceId']
|
69
|
+
end
|
70
|
+
|
71
|
+
instance_id ? instance_id : false
|
72
|
+
end
|
73
|
+
|
74
|
+
# call the Chef API to get the node name of the instance
|
75
|
+
def get_chef_node_name(instance_id)
|
76
|
+
results = @chef.search.query(:node, "ec2_instance_id:#{instance_id}")
|
77
|
+
if results.rows.size > 0
|
78
|
+
return results.rows.first['name']
|
79
|
+
else
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# check if the node exists in Sensu
|
85
|
+
def in_sensu?(node_name)
|
86
|
+
begin
|
87
|
+
RestClient.get("#{@config[:sensu][:url]}/clients/#{node_name}")
|
88
|
+
rescue RestClient::ResourceNotFound
|
89
|
+
return false
|
90
|
+
rescue RestClient::Exception => e
|
91
|
+
puts "Sensu request failed: #{e}"
|
92
|
+
return false
|
93
|
+
else
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# call the Sensu API to remove the node
|
99
|
+
def remove_from_sensu(node_name)
|
100
|
+
response = RestClient.delete("#{@config[:sensu][:url]}/clients/#{node_name}")
|
101
|
+
case response.code
|
102
|
+
when 202
|
103
|
+
notify_hipchat('Removed ' + node_name + ' from Sensu') if @config[:hipchat][:enable]
|
104
|
+
return true
|
105
|
+
else
|
106
|
+
notify_hipchat('Failed to remove ' + node_name + ' from Sensu') if @config[:hipchat][:enable]
|
107
|
+
return false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def remove_from_chef(node_name)
|
112
|
+
client = @chef.clients.fetch(node_name)
|
113
|
+
client.destroy
|
114
|
+
node = @chef.nodes.fetch(node_name)
|
115
|
+
node.destroy
|
116
|
+
notify_hipchat('Removed ' + node_name + ' from Chef')
|
117
|
+
end
|
118
|
+
|
119
|
+
def notify_hipchat(msg)
|
120
|
+
hipchat = HipChat::Client.new(
|
121
|
+
@config[:hipchat][:api_token],
|
122
|
+
api_version: 'v2'
|
123
|
+
)
|
124
|
+
room = @config[:hipchat][:room]
|
125
|
+
hipchat[room].send('AWS Cleaner', msg)
|
126
|
+
end
|
127
|
+
|
128
|
+
# main loop
|
129
|
+
loop do
|
130
|
+
# get messages from SQS
|
131
|
+
messages = @sqs.receive_message(
|
132
|
+
queue_url: @config[:sqs][:queue],
|
133
|
+
max_number_of_messages: 10,
|
134
|
+
visibility_timeout: 3
|
135
|
+
).messages
|
136
|
+
|
137
|
+
puts "Got #{messages.size} messages"
|
138
|
+
|
139
|
+
messages.each_with_index do |message, index|
|
140
|
+
puts "Looking at message number #{index}"
|
141
|
+
body = parse(message.body)
|
142
|
+
id = message.receipt_handle
|
143
|
+
|
144
|
+
unless body
|
145
|
+
delete_message(id)
|
146
|
+
next
|
147
|
+
end
|
148
|
+
|
149
|
+
instance_id = process_message(body)
|
150
|
+
|
151
|
+
if instance_id
|
152
|
+
chef_node = get_chef_node_name(instance_id)
|
153
|
+
|
154
|
+
if chef_node
|
155
|
+
if remove_from_chef(chef_node)
|
156
|
+
puts "Removed #{chef_node} from Chef"
|
157
|
+
delete_message(id)
|
158
|
+
end
|
159
|
+
else
|
160
|
+
puts "Instance #{instance_id} does not exist in Chef, deleting message"
|
161
|
+
delete_message(id)
|
162
|
+
end
|
163
|
+
|
164
|
+
if in_sensu?(chef_node)
|
165
|
+
if remove_from_sensu(chef_node)
|
166
|
+
puts "Removed #{chef_node} from Sensu"
|
167
|
+
delete_message(id)
|
168
|
+
else
|
169
|
+
puts "Instance #{instance_id} does not exist in Sensu, deleting message"
|
170
|
+
delete_message(id)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
else
|
174
|
+
puts 'Message not relevant, deleting'
|
175
|
+
delete_message(id)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
sleep(5)
|
180
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws-cleaner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Heydrick
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: chef-api
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hipchat
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rest-client
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: trollop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.1'
|
83
|
+
description: AWS Cleaner cleans up after EC2 nodes are terminated
|
84
|
+
email: eheydrick@gmail.com
|
85
|
+
executables:
|
86
|
+
- aws_cleaner.rb
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- bin/aws_cleaner.rb
|
91
|
+
homepage: https://github.com/eheydrick/aws-cleaner
|
92
|
+
licenses:
|
93
|
+
- MIT
|
94
|
+
metadata: {}
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 2.0.14
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: AWS Cleaner cleans up after EC2 nodes are terminated
|
115
|
+
test_files: []
|