aws-cleaner 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/bin/aws_cleaner.rb +180 -0
  3. metadata +115 -0
@@ -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
@@ -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: []