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.
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: []