seiton 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "seiton"
4
+
5
+ Seiton::CLI.start
@@ -0,0 +1,15 @@
1
+ require "seiton/cli"
2
+ require "seiton/client"
3
+ require "seiton/common"
4
+ require "seiton/ext"
5
+ require "seiton/ec2_check"
6
+ require "seiton/ec2"
7
+ require "seiton/rds_check"
8
+ require "seiton/rds"
9
+ require "seiton/sqs_check"
10
+ require "seiton/sqs"
11
+ require "seiton/version"
12
+
13
+ module Seiton
14
+ # Your code goes here...
15
+ end
@@ -0,0 +1,88 @@
1
+ # coding: utf-8
2
+ require 'thor'
3
+ require 'aws-sdk'
4
+ require 'date'
5
+ require 'time'
6
+ require 'terminal-table'
7
+ require 'logger'
8
+ require 'erb'
9
+
10
+ module Seiton
11
+ class CLI < Thor
12
+ default_command :version
13
+
14
+ desc 'version', 'Print the version number.'
15
+ def version
16
+ puts Seiton::VERSION
17
+ end
18
+
19
+ desc 'ami', 'Delete the EC2 AMI.'
20
+ option :before_datetime, type: :string, aliases: '-b', desc: 'Specify the date and time for deletion (delete resources before the specified date and time.)'
21
+ option :ignores, type: :array, aliases: '-i', desc: 'Specify resources to be undeleted (you can specify multiple resources).'
22
+ option :check, type: :boolean, aliases: '-c', desc: 'Check the resources to be deleted.'
23
+ def ami
24
+ unless options[:before_datetime] then
25
+ puts '--before-datetime must be specified. (--before-datetime=YYYY/MM/DD)'
26
+ exit 1
27
+ end
28
+ seiton = Seiton::Ec2.new
29
+ seiton.ec2_image(options[:check], options[:before_datetime], options[:ignores])
30
+ end
31
+
32
+ desc 'ec2_snapshot', 'Delete the EC2 Snapshot.'
33
+ option :before_datetime, type: :string, aliases: '-b', desc: 'Specify the date and time for deletion (delete resources before the specified date and time.)'
34
+ option :ignores, type: :array, aliases: '-i', desc: 'Specify resources to be undeleted (you can specify multiple resources).'
35
+ option :check, type: :boolean, aliases: '-c', desc: 'Check the resources to be deleted.'
36
+ def ec2_snapshot
37
+ unless options[:before_datetime] then
38
+ puts '--before-datetime must be specified. (--before-datetime=YYYY/MM/DD)'
39
+ exit 1
40
+ end
41
+ seiton = Seiton::Ec2.new
42
+ seiton.ec2_snapshots(options[:check], options[:before_datetime], options[:ignores])
43
+ end
44
+
45
+ desc 'instance', 'Delete the EC2 Instance.'
46
+ option :before_datetime, type: :string, aliases: '-b', desc: 'Specify the date and time for deletion (delete resources before the specified date and time.)'
47
+ option :ignores, type: :array, aliases: '-i', desc: 'Specify resources to be undeleted (you can specify multiple resources).'
48
+ option :check, type: :boolean, aliases: '-c', desc: 'Check the resources to be deleted.'
49
+ def instance
50
+ unless options[:before_datetime] then
51
+ puts '--before-datetime must be specified. (--before-datetime=YYYY/MM/DD)'
52
+ exit 1
53
+ end
54
+ seiton = Seiton::Ec2.new
55
+ seiton.ec2_instance(options[:check], options[:before_datetime], options[:ignores])
56
+ end
57
+
58
+ desc 'rds_snapshot', 'Delete the RDS Snapshot.'
59
+ option :before_datetime, type: :string, aliases: '-b', desc: 'Specify the date and time for deletion (delete resources before the specified date and time.)'
60
+ option :ignores, type: :array, aliases: '-i', desc: 'Specify resources to be undeleted (you can specify multiple resources).'
61
+ option :check, type: :boolean, aliases: '-c', desc: 'Check the resources to be deleted.'
62
+ def rds_snapshot
63
+ unless options[:before_datetime] then
64
+ puts '--before-datetime must be specified. (--before-datetime=YYYY/MM/DD)'
65
+ exit 1
66
+ end
67
+ seiton = Seiton::Rds.new
68
+ seiton.rds_snapshot(options[:check], options[:before_datetime], options[:ignores])
69
+ end
70
+
71
+ desc 'eip', 'Delete the Elastic IP.'
72
+ option :ignores, type: :array, aliases: '-i', desc: 'Specify resources to be undeleted (you can specify multiple resources).'
73
+ option :check, type: :boolean, aliases: '-c', desc: 'Check the resources to be deleted.'
74
+ def eip
75
+ seiton = Seiton::Ec2.new
76
+ seiton.eip(options[:check], options[:ignores])
77
+ end
78
+
79
+ desc 'sqs_queue', 'Delete the SQS Queue.'
80
+ option :ignores, type: :array, aliases: '-i', desc: 'Specify resources to be undeleted (you can specify multiple resources).'
81
+ option :check, type: :boolean, aliases: '-c', desc: 'Check the resources to be deleted.'
82
+ def sqs_queue
83
+ seiton = Seiton::Sqs.new
84
+ seiton.sqs_queue(options[:check], options[:ignores])
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,119 @@
1
+ module Seiton
2
+ class Client
3
+
4
+ def initialize
5
+ raise 'AWS_PROFILE does not exist.' unless ENV['AWS_PROFILE']
6
+ raise 'AWS_REGION does not exist.' unless ENV['AWS_REGION']
7
+ end
8
+
9
+ CLIENTS = {
10
+ iam_user: Aws::IAM::CurrentUser,
11
+ ec2_client: Aws::EC2::Client,
12
+ rds_client: Aws::RDS::Client,
13
+ sqs_client: Aws::SQS::Client
14
+ }
15
+
16
+ CLIENTS.each do |method_name, client|
17
+ define_method method_name do
18
+ eval "@#{method_name} ||= #{client}.new"
19
+ end
20
+ end
21
+
22
+ resource_types = %w(image snapshot db_snapshot eip instance volume sqs_queue)
23
+ resource_types.each do |type|
24
+ define_method 'display_' + type + '_resources' do |*args|
25
+ log.info('The following resources will be removed.')
26
+ resource_rows = []
27
+ header = []
28
+ args.first.each do |res|
29
+ case type
30
+ when 'instance' then
31
+ header = [ 'tag:Name', 'ID', 'Launch Time' ]
32
+ resource_rows << \
33
+ [ name_tag(res.tags).value, res.instance_id, res.launch_time ]
34
+ when 'volume' then
35
+ header = [ 'tag:Name', 'ID', 'Create Time', 'State' ]
36
+ if res.tags.empty?
37
+ resource_rows << \
38
+ [ '', res.volume_id, res.create_time, res.state ]
39
+ else
40
+ resource_rows << \
41
+ [ name_tag(res.tags).value, res.volume_id, res.create_time, res.state ]
42
+ end
43
+ when 'image' then
44
+ header = [ 'tag:Name', 'Name', 'ID', 'Creation Date' ]
45
+ if res.tags.empty?
46
+ resource_rows << \
47
+ [ '', res.name, res.image_id, res.creation_date ]
48
+ else
49
+ resource_rows << \
50
+ [ name_tag(res.tags).value, res.name, res.image_id, res.creation_date ]
51
+ end
52
+ when 'snapshot' then
53
+ header = [ 'tag:Name', 'ID', 'Start Time', 'Description' ]
54
+ if res.tags.empty?
55
+ resource_rows << \
56
+ [ '', res.snapshot_id, res.start_time, res.description ]
57
+ else
58
+ resource_rows << \
59
+ [ name_tag(res.tags).value, res.snapshot_id, res.start_time, res.description ]
60
+ end
61
+ when 'db_snapshot' then
62
+ header = [ 'Name', 'Create Time' ]
63
+ resource_rows << [ res.db_cluster_snapshot_identifier, res.snapshot_create_time ]
64
+ when 'eip' then
65
+ header = [ 'Public IP', 'Allocation ID' ]
66
+ resource_rows << [ res.public_ip, res.allocation_id ]
67
+ when 'sqs_queue' then
68
+ header = [ 'Queue URL' ]
69
+ resource_rows << [ res ]
70
+ end
71
+ end
72
+ Terminal::Table.new :headings => header, :rows => resource_rows
73
+ end
74
+
75
+ define_method 'delete_' + type + '_action' do |*args|
76
+ log.info('Delete Resource ' + type + ' : ' + args.first)
77
+ begin
78
+ case type
79
+ when 'instance' then
80
+ # puts args.first
81
+ req = { instance_ids: [ args.first ] }
82
+ method_name = 'terminate_' + type + 's'
83
+ ec2_client.method(method_name).call(req)
84
+ when 'volume' then
85
+ # puts args.first
86
+ req = { volume_id: args.first }
87
+ method_name = 'delete_' + type
88
+ ec2_client.method(method_name).call(req)
89
+ when 'image' then
90
+ req = { image_id: args.first }
91
+ method_name = 'deregister_' + type
92
+ ec2_client.method(method_name).call(req)
93
+ when 'snapshot' then
94
+ req = { snapshot_id: args.first }
95
+ method_name = 'delete_' + type
96
+ ec2_client.method(method_name).call(req)
97
+ when 'db_snapshot' then
98
+ req = { db_cluster_snapshot_identifier: args.first }
99
+ method_name = 'delete_db_cluster_snapshot'
100
+ rds_client.method(method_name).call(req)
101
+ when 'eip' then
102
+ req = { allocation_id: args.first }
103
+ method_name = 'release_address'
104
+ ec2_client.method(method_name).call(req)
105
+ when 'sqs_queue' then
106
+ req = { queue_url: args.first }
107
+ method_name = 'delete_queue'
108
+ sqs_client.method(method_name).call(req)
109
+ end
110
+
111
+ log.info('Deleted.')
112
+ rescue StandardError => e
113
+ log.error('Failed to delete.' + e.to_s)
114
+ end
115
+ end
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,42 @@
1
+ module Seiton
2
+ module Helper
3
+ def process_ok?
4
+ while true
5
+ puts 'Do you want to continue? [y|n]:'
6
+ response = STDIN.gets.chomp
7
+ case response
8
+ when /^[yY]/
9
+ log.info('Continue.')
10
+ return true
11
+ when /^[nN]/, /^$/
12
+ log.warn('Abort.')
13
+ return false
14
+ end
15
+ end
16
+ end
17
+
18
+ def datetime_parse(datetime)
19
+ if datetime.to_s.include?('-')
20
+ Time.parse(datetime.to_s.tr('-', '/')).to_i
21
+ else
22
+ Time.parse(datetime).to_i
23
+ end
24
+ end
25
+
26
+ def log
27
+ Logger.new(STDOUT)
28
+ end
29
+
30
+ def name_tag(tags)
31
+ tags.select { |tag| tag.value if tag.key == 'Name' }.last
32
+ end
33
+
34
+ def volume_ids(mappings)
35
+ volume_ids = []
36
+ mappings.each do |mapping|
37
+ volume_ids << mapping.ebs.volume_id unless mapping.ebs.delete_on_termination
38
+ end
39
+ volume_ids
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,300 @@
1
+ module Seiton
2
+ class Ec2 < Client
3
+
4
+ include Seiton::Helper
5
+ include Seiton::Ec2Check
6
+
7
+ def ec2_instance(check = false, dt, ignores)
8
+ if check
9
+ log.info('List up the resources to be removed.')
10
+ else
11
+ log.info('Start deleting.')
12
+ end
13
+ res = ec2_client.describe_instances({
14
+ filters: [{
15
+ 'name': 'instance-state-name', 'values': [ 'stopped' ]
16
+ }]
17
+ })
18
+ delete_instances = []
19
+ res.reservations.each do |i|
20
+ i.instances.each do |delete_instance|
21
+ if datetime_parse(delete_instance.launch_time) < datetime_parse(dt)
22
+ delete_instances << delete_instance
23
+ end
24
+ end
25
+ end
26
+
27
+ unless ignores.nil?
28
+ ignore_resources = []
29
+ ignores.each do |ignore|
30
+ ignore_resources << delete_instances.select { |delete_instance| name_tag(delete_instance.tags).value == ignore }.last
31
+ delete_instances.delete_if { |delete_instance| name_tag(delete_instance.tags).value == ignore }
32
+ end
33
+ end
34
+
35
+ if delete_instances.empty?
36
+ log.info('The resource to be deleted does not exist.')
37
+ exit 0
38
+ end
39
+
40
+ puts display_instance_resources(delete_instances)
41
+ generator_ec2_instances_check(delete_instances, ignore_resources)
42
+ exit 0 if check
43
+
44
+ delete_volume_ids = []
45
+ if process_ok?
46
+ begin
47
+ delete_instances.each do |instance|
48
+ delete_instance_action(instance.instance_id)
49
+ delete_volume_ids << volume_ids(instance.block_device_mappings)
50
+ end
51
+ rescue StandardError => e
52
+ log.error(e)
53
+ exit 1
54
+ end
55
+ else
56
+ exit 0
57
+ end
58
+ delete_volume_ids = delete_volume_ids.flatten
59
+ ec2_volume(delete_volume_ids) unless delete_volume_ids.empty?
60
+ end
61
+
62
+ def ec2_volume(delete_volume_ids)
63
+ log.info('Start EC2 volume deleting.')
64
+ log.info('Waiting for the status of the EC2 volume to become available.')
65
+ loop do
66
+ res = ec2_client.describe_volumes({ volume_ids: delete_volume_ids })
67
+ status = res.volumes.map { |volume| volume.volume_id if volume.state == 'available' }
68
+ if delete_volume_ids.sort == status.sort
69
+ log.info('The status of all EC2 volumes is now available.')
70
+ break
71
+ else
72
+ log.info('Waiting.')
73
+ sleep 3
74
+ end
75
+ end
76
+
77
+ delete_volumes = ec2_client.describe_volumes({ volume_ids: delete_volume_ids })
78
+ puts display_volume_resources(delete_volumes.volumes)
79
+
80
+ generator_ec2_volumes_check(delete_volumes.volumes)
81
+
82
+ if process_ok?
83
+ begin
84
+ delete_volume_ids.each do |delete_volume_id|
85
+ delete_volume_action(delete_volume_id)
86
+ end
87
+ rescue StandardError => e
88
+ log.error(e)
89
+ exit 1
90
+ end
91
+ else
92
+ exit 0
93
+ end
94
+
95
+ end
96
+
97
+ def ec2_image(check = false, dt, ignores)
98
+ if check
99
+ log.info('List up the resources to be removed.')
100
+ else
101
+ log.info('Start deleting.')
102
+ end
103
+ res = ec2_client.describe_images({ owners: ['self'] })
104
+ delete_images = []
105
+ res.images.each do |delete_image|
106
+ if datetime_parse(delete_image.creation_date) < datetime_parse(dt)
107
+ delete_images << delete_image
108
+ end
109
+ end
110
+
111
+ unless ignores.nil?
112
+ ignore_resources = []
113
+ ignores.each do |ignore|
114
+ delete_images.each do |delete_image|
115
+ if name_tag(delete_image.tags).nil?
116
+ if delete_image.name == ignore or delete_image.image_id == ignore
117
+ ignore_resources << delete_image
118
+ end
119
+ elsif name_tag(delete_image.tags).value == ignore or delete_image.name == ignore
120
+ ignore_resources << delete_image
121
+ end
122
+ end
123
+ delete_images.delete_if do |delete_image|
124
+ if name_tag(delete_image.tags).nil?
125
+ delete_image.name == ignore
126
+ else
127
+ name_tag(delete_image.tags).value == ignore or \
128
+ delete_image.name == ignore or \
129
+ delete_image.image_id == ignore
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ if delete_images.empty?
136
+ log.info('The resource to be deleted does not exist.')
137
+ exit 0
138
+ end
139
+
140
+ puts display_image_resources(delete_images)
141
+ generator_ec2_images_check(delete_images, ignore_resources)
142
+ exit 0 if check
143
+
144
+ delete_image_ids = []
145
+ if process_ok?
146
+ begin
147
+ delete_images.each do |image|
148
+ delete_image_action(image.image_id)
149
+ delete_image_ids << image.image_id
150
+ end
151
+ rescue StandardError => e
152
+ log.error(e)
153
+ exit 1
154
+ end
155
+ else
156
+ exit 0
157
+ end
158
+
159
+ ec2_snapshot(delete_image_ids)
160
+ end
161
+
162
+ def ec2_snapshots(check = false, dt, ignores)
163
+ if check
164
+ log.info('List up the resources to be removed.')
165
+ else
166
+ log.info('Start deleting.')
167
+ end
168
+ res = ec2_client.describe_snapshots({ owner_ids: ['self'] })
169
+ delete_snapshots = []
170
+ res.snapshots.each do |delete_snapshot|
171
+ if datetime_parse(delete_snapshot.start_time) < datetime_parse(dt)
172
+ delete_snapshots << delete_snapshot
173
+ end
174
+ end
175
+
176
+ unless ignores.nil?
177
+ ignore_resources = []
178
+ ignores.each do |ignore|
179
+ delete_snapshots.each do |delete_snapshot|
180
+ if name_tag(delete_snapshot.tags).nil?
181
+ if delete_snapshot.snapshot_id == ignore
182
+ ignore_resources << delete_snapshot
183
+ end
184
+ elsif name_tag(delete_snapshot.tags).value == ignore or delete_snapshot.snapshot_id == ignore
185
+ ignore_resources << delete_snapshot
186
+ end
187
+ end
188
+ delete_snapshots.delete_if do |delete_snapshot|
189
+ if name_tag(delete_snapshot.tags).nil?
190
+ delete_snapshot.snapshot_id == ignore
191
+ else
192
+ name_tag(delete_snapshot.tags).value == ignore or \
193
+ delete_snapshot.snapshot_id == ignore
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ if delete_snapshots.empty?
200
+ log.info('The resource to be deleted does not exist.')
201
+ exit 0
202
+ end
203
+
204
+ puts display_snapshot_resources(delete_snapshots)
205
+ generator_ec2_snapshots_check(delete_snapshots)
206
+ exit 0 if check
207
+
208
+ delete_snapshot_ids = []
209
+ if process_ok?
210
+ begin
211
+ delete_snapshots.each do |s|
212
+ delete_snapshot_ids << s.snaphost_id
213
+ end
214
+ rescue StandardError => e
215
+ log.error(e)
216
+ exit 1
217
+ end
218
+ else
219
+ exit 0
220
+ end
221
+
222
+ ec2_snapshot(delete_image_ids)
223
+ end
224
+
225
+ def ec2_snapshot(delete_image_ids)
226
+ log.info('Start EC2 Snapshot deleting.')
227
+ res = ec2_client.describe_snapshots(owner_ids: [iam_user.arn.split(':')[4]])
228
+ delete_snapshots = []
229
+ delete_image_ids.each do |delete_image_id|
230
+ res.snapshots.each do |delete_snapshot|
231
+ delete_snapshots << delete_snapshot if delete_snapshot.description.include?(delete_image_id)
232
+ end
233
+ end
234
+
235
+ if delete_snapshots.empty?
236
+ log.info('The EC2 snapshot to be deleted does not exist.')
237
+ return true
238
+ end
239
+
240
+ puts display_snapshot_resources(delete_snapshots)
241
+ generator_ec2_snapshots_check(delete_snapshots)
242
+
243
+ if process_ok?
244
+ begin
245
+ delete_snapshots.each do |delete_snapshot|
246
+ delete_snapshot_action(delete_snapshot.snapshot_id)
247
+ end
248
+ rescue StandardError => e
249
+ log.error(e)
250
+ exit 1
251
+ end
252
+ else
253
+ exit 0
254
+ end
255
+ end
256
+
257
+ def eip(check = false, ignores)
258
+ if check
259
+ log.info('List up the resources to be removed.')
260
+ else
261
+ log.info('Start deleting.')
262
+ end
263
+ res = ec2_client.describe_addresses({})
264
+ delete_addresses = []
265
+ res.addresses.each do |delete_address|
266
+ if delete_address.instance_id.nil?
267
+ delete_addresses << delete_address
268
+ end
269
+ end
270
+
271
+ unless ignores.nil?
272
+ ignores.each do |ignore|
273
+ delete_addresses.delete_if { |delete_address| delete_address.public_ip == ignore }
274
+ end
275
+ end
276
+
277
+ if delete_addresses.empty?
278
+ log.info('The resource to be deleted does not exist.')
279
+ exit 0
280
+ end
281
+
282
+ puts display_eip_resources(delete_addresses)
283
+ generator_ec2_eips_check(delete_addresses, ignores)
284
+ exit 0 if check
285
+
286
+ if process_ok?
287
+ begin
288
+ delete_addresses.each do |delete_address|
289
+ delete_eip_action(delete_address.allocation_id)
290
+ end
291
+ rescue StandardError => e
292
+ log.error(e)
293
+ exit 1
294
+ end
295
+ else
296
+ exit 0
297
+ end
298
+ end
299
+ end
300
+ end