seiton 0.0.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.
@@ -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