aws-tools 0.0.2

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 +15 -0
  2. data/lib/aws_region.rb +452 -0
  3. metadata +58 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjkzYzUwNTA3MWE0YTE1NDAzYmY5ZDcxZWI2ZjBlNzFlNGQ4NTc2Zg==
5
+ data.tar.gz: !binary |-
6
+ OTg4ZmVmMjNlMjJkZTQyY2NmYTU0NTc4ZDdiODFhMWQ2OWE3NTVjMQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZGRkOTFhYTRmOWZjMWI2N2ExMjEzOWJjMzU5MjlhMGY2N2Q5YmRmYjk3ZDY0
10
+ NGRjYjRlMWJmZGE1ZjkxZTZhOWQ5OTYzZGY1ZmM4MjNlM2Q4OWI2NGVhNWNm
11
+ OWYxNTgwM2NhMzg4Nzk2MWI3YjQzZTYzNjExNGMwYTAzNDllZGM=
12
+ data.tar.gz: !binary |-
13
+ MzY2NTY4OTAzOTk3ZjZlZDZhMzA3Y2JmMmVmMjAxNTQ3ZmI5YjcxNzdjOGFl
14
+ ZDgxYzg3ODYxMTY3MjEwZWI1MjFmNTI3ZmJhNWMzNjczYmY1NjhjZmE4ZTE4
15
+ Y2IzNjRhODM1MzdmNTU4M2Y3MDlmZjJmMjE0NDg0NWQ3Zjc4MTg=
@@ -0,0 +1,452 @@
1
+ #!/usr/bin/env ruby
2
+ require 'aws-sdk-core'
3
+ require 'yaml'
4
+ require 'json'
5
+
6
+ class AwsRegion
7
+ attr_accessor :ec2, :region, :rds, :account_id, :elb, :cw, :s3
8
+ REGIONS = {'or' => "us-west-2", 'ca' => "us-west-1", 'va' => 'us-east-1'}
9
+ def initialize(region, account_id, access_key_id, secret_access_key)
10
+ @region = REGIONS[region]
11
+ @account_id = account_id
12
+ Aws.config = {:access_key_id => access_key_id,
13
+ :secret_access_key => secret_access_key}
14
+ @ec2 = Aws::EC2.new({:region => @region})
15
+ @rds = Aws::RDS.new({:region => @region})
16
+ @elb = Aws::ElasticLoadBalancing.new({:region => @region})
17
+ @cw = Aws::CloudWatch.new({:region => @region})
18
+ @s3 = Aws::S3.new({:region => @region})
19
+
20
+ def find_instances(options={})
21
+ instances = []
22
+ @ec2.describe_instances[:reservations].each do |i|
23
+ i.instances.each do |y|
24
+ instance = AwsInstance.new(self,{:instance => y})
25
+ if instance.state != 'terminated'
26
+ if options.has_key?(:environment) and options.has_key?(:purpose)
27
+ instances << instance if instance.tags[:environment] == options[:environment] and instance.tags[:purpose] == options[:purpose]
28
+ elsif options.has_key?(:instance_id)
29
+ instances << instance if instance.id == options[:instance_id]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ return instances
35
+ end
36
+ def find_db_instances(options={})
37
+ instances = []
38
+ @rds.describe_db_instances[:db_instances].each do |i|
39
+ instance = AwsDbInstance.new(self, {:instance => i})
40
+ if options.has_key?(:instance_id) and
41
+ (!options.has_key?(:environment) or !options.has_key?(:purpose)) and
42
+ instance.id == options[:instance_id]
43
+ instances << instance
44
+ elsif instance.id == options[:instance_id] and
45
+ instance.tags[:environment] == options[:environment] and
46
+ instance.tags[:purpose] == options[:purpose]
47
+ instances << instance
48
+ end
49
+ end
50
+ instances
51
+ end
52
+
53
+ def find_buckets(options={})
54
+ buckets = []
55
+ _buckets = @s3.list_buckets()
56
+ _buckets[:buckets].each do |b|
57
+ buckets << AwsBucket.new(self, {id: b[:name]}) if b[:name] == options[:bucket]
58
+ end
59
+ buckets
60
+ end
61
+ def create_instance(options={})
62
+ AwsInstance.new(self, options)
63
+ end
64
+ def create_db_instance(options={})
65
+ AwsDbInstance.new(self, options)
66
+ end
67
+ def create_cw_instance(options={})
68
+ AwsCw.new(self, options)
69
+ end
70
+ def create_bucket(options={})
71
+ AwsBucket.new(self, options)
72
+ end
73
+ def remove_instance_from_lb(instance, lb_name)
74
+ lb = @elb.describe_load_balancers({:load_balancer_names => [lb_name]})
75
+ if lb and lb[:load_balancer_descriptions].length > 0
76
+ lb[:load_balancer_descriptions][0][:instances].each do |lbi|
77
+ if lbi[:instance_id] == instance
78
+ @elb.deregister_instances_from_load_balancer({:load_balancer_name => lb_name,
79
+ :instances => [{:instance_id => instance}]})
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ class AwsCw
87
+ attr_accessor :region
88
+ def initialize(region, options={})
89
+ @region = region
90
+ end
91
+ def put_metric(arg_csv)
92
+ (namespace, name, value, dims) = arg_csv.split(",")
93
+ dimensions = []
94
+ dims.split(";").each do |d|
95
+ (n,v) = d.split(":")
96
+ dimensions << {:name => n, :value => v}
97
+ end
98
+ args = {:namespace => namespace}
99
+ metric ={:metric_name => name, :value => value.to_f, :timestamp => Time.now, :dimensions => dimensions}
100
+ args[:metric_data] = [metric]
101
+ @region.cw.put_metric_data(args)
102
+ end
103
+ end
104
+
105
+ class AwsBucket
106
+ attr_accessor :region
107
+ def initialize(region, options={})
108
+ @region = region
109
+ if options.has_key?(:id)
110
+ @id = options[:id]
111
+ elsif options.has_key?(:bucket)
112
+ bucket = options[:bucket]
113
+ if @region.find_buckets({bucket: bucket}).length <= 0
114
+ @region.s3.create_bucket({:bucket => bucket,
115
+ :create_bucket_configuration => {:location_constraint => @region.region}})
116
+ if @region.find_buckets({bucket: bucket}).length <= 0
117
+ raise "Error creating bucket: #{bucket} in region: #{@region.region}"
118
+ end
119
+ end
120
+ @id = bucket
121
+ end
122
+ end
123
+ def delete
124
+ @region.s3.delete_bucket({bucket: @id})
125
+ end
126
+ def put_file(filename, file_identity)
127
+ File.open(filename, 'r') do |reading_file|
128
+ resp = @region.s3.put_object(
129
+ acl: "bucket-owner-full-control",
130
+ body: reading_file,
131
+ bucket: @id,
132
+ key: file_identity
133
+ )
134
+ end
135
+ end
136
+
137
+ def put(local_file_path, aws_path, options={})
138
+ # puts a local file to an s3 object in bucket on path
139
+ # example: put_local_file {:bucket=>"bucket", :local_file_path=>"/tmp/bar/foo.txt", :aws_path=>"b"}
140
+ # would make an s3 object named foo.txt in bucket/b
141
+ aws_path = aws_path[0..-2] if aws_path[-1..-1] == '/'
142
+ s3_path = "#{aws_path}/#{File.basename(local_file_path)}"
143
+ puts "s3 writing #{local_file_path} to bucket #{@id} path: #{aws_path} s3 path: #{s3_path}"
144
+ f = File.open local_file_path, 'rb'
145
+ options[:bucket] = @id
146
+ options[:key] = s3_path
147
+ options[:body] = f
148
+ options[:storage_class] = 'REDUCED_REDUNDANCY'
149
+ result = @region.s3.put_object(params=options)
150
+ f.close
151
+ result
152
+ end
153
+
154
+ def find(options={})
155
+ # prefix is something like: hchd-A-A-Items
156
+ # This will return in an array of strings the names of all objects in s3 in
157
+ # the :aws_path under :bucket starting with passed-in prefix
158
+ # example: :bucket=>'mazama-inventory', :aws_path=>'development', :prefix=>'broadhead'
159
+ # would return array of names of objects in said bucket
160
+ # matching (in regex terms) development/broadhead.*
161
+ # return empty array if no matching objects exist
162
+ aws_path = options[:aws_path]
163
+ prefix = options[:prefix]
164
+ aws_path = '' if aws_path.nil?
165
+ aws_path = aws_path[0..-2] if aws_path[-1..-1] == '/'
166
+ puts "s3 searching bucket:#{@id} for #{aws_path}/#{prefix}"
167
+ objects = @region.s3.list_objects(:bucket => @id,
168
+ :prefix => "#{aws_path}/#{prefix}")
169
+ f = objects.contents.collect(&:key)
170
+ puts "s3 searched got: #{f.inspect}"
171
+ f
172
+ end
173
+
174
+ def get(options={})
175
+ # writes to local file an s3 object in :bucket at :s3_path_to_object to :dest_file_path
176
+ # example: get_object_as_local_file( {:bucket=>'mazama-inventory',
177
+ # :s3_path_to_object=>development/myfile.txt',
178
+ # :dest_file_path=>'/tmp/foo.txt'})
179
+ # would write to local /tmp/foo.txt a file retrieved from s3 in 'mazama-inventory' bucket
180
+ # at development/myfile.txt
181
+ s3_path_to_object = options[:s3_path_to_object]
182
+ dest_file_path = options[:dest_file_path]
183
+ File.delete dest_file_path if File.exists?(dest_file_path)
184
+ puts "s3 get bucket:#{@id} path:#{s3_path_to_object} dest:#{dest_file_path}"
185
+ response = @region.s3.get_object(:bucket => @id,
186
+ :key => s3_path_to_object)
187
+ response.body.rewind
188
+ # I DO NOT KNOW what happens if the body is "too big". I didn't see a method in the
189
+ # API to chunk it out... but perhaps response.body does this already.
190
+ File.open(dest_file_path, 'wb') do |file|
191
+ response.body.each { |chunk| file.write chunk }
192
+ end
193
+ puts "s3 got " + `ls -l #{dest_file_path}`.strip
194
+ nil
195
+ end
196
+
197
+ def delete_object(options={})
198
+ # deletes from s3 an object in :bucket at :s3_path_to_object
199
+ s3_path_to_object = options[:s3_path_to_object]
200
+ puts "s3 delete #{s3_path_to_object}"
201
+ @region.s3.delete_object( :bucket => @id,
202
+ :key => s3_path_to_object)
203
+ puts "s3 deleted."
204
+ end
205
+
206
+ def delete_all_objects
207
+ response = @region.s3.list_objects({:bucket => @id})
208
+ response[:contents].each do |obj|
209
+ @region.s3.delete_object( :bucket => @id,
210
+ :key => obj[:key])
211
+ end
212
+ end
213
+ end
214
+ class AwsDbInstance
215
+ attr_accessor :id, :tags, :region, :endpoint
216
+ def initialize(region, options = {})
217
+ @region = region
218
+ opts = options[:opts]
219
+ if !options.has_key?(:instance)
220
+ @id = opts[:db_instance_identifier]
221
+ snapshot_name = options[:snapshot_name]
222
+ if 0 < @region.find_db_instances({:instance_id => @id}).length
223
+ puts "Error, instance: #{@id} already exists"
224
+ return
225
+ end
226
+ last = self.get_latest_db_snapshot({:snapshot_name => snapshot_name})
227
+ puts "Restoring: #{last.db_instance_identifier}, snapshot: #{last.db_instance_identifier} from : #{last.snapshot_create_time}"
228
+ opts[:db_snapshot_identifier] = last.db_snapshot_identifier
229
+ response = @region.rds.restore_db_instance_from_db_snapshot(opts)
230
+ @_instance = response[:db_instance]
231
+ @region.rds.add_tags_to_resource({:resource_name => "arn:aws:rds:#{@region.region}:#{@region.account_id}:db:#{@id}",
232
+ :tags => [{:key => "environment", :value => options[:environment]},
233
+ {:key => "purpose", :value => options[:purpose]}]})
234
+
235
+ self.wait
236
+
237
+ opts = { :db_instance_identifier => @id,
238
+ :vpc_security_group_ids => options[:vpc_security_group_ids]}
239
+ @region.rds.modify_db_instance(opts)
240
+ else
241
+ @_instance = options[:instance]
242
+ @id = @_instance[:db_instance_identifier]
243
+ end
244
+ @tags = {}
245
+ _tags = @region.rds.list_tags_for_resource({:resource_name => "arn:aws:rds:#{@region.region}:#{@region.account_id}:db:#{@id}"})
246
+ _tags[:tag_list].each do |t|
247
+ @tags[t[:key].to_sym] = t[:value]
248
+ end
249
+ @endpoint = @_instance.endpoint[:address]
250
+ end
251
+
252
+ def delete(options={})
253
+ puts "Deleting database: #{@id}"
254
+ opts = { :db_instance_identifier => @id,
255
+ :skip_final_snapshot => false,
256
+ :final_db_snapshot_identifier => "#{@id}-#{Time.now.strftime("%Y-%m-%d-%H-%M-%S")}" }
257
+ i = @region.rds.delete_db_instance(opts)
258
+ end
259
+
260
+ def purge_db_snapshots
261
+ latest = 0
262
+ @region.rds.describe_db_snapshots[:db_snapshots].each do |i|
263
+ if i.snapshot_type == "manual" and i.db_instance_identifier == @id
264
+ if i.snapshot_create_time.to_i > latest
265
+ latest = i.snapshot_create_time.to_i
266
+ end
267
+ end
268
+ end
269
+ @region.rds.describe_db_snapshots[:db_snapshots].each do |i|
270
+ if i.snapshot_type == "manual" and i.db_instance_identifier == @id
271
+ if i.snapshot_create_time.to_i != latest
272
+ puts "Removing snapshot: #{i.db_snapshot_identifier}/#{i.snapshot_create_time.to_s}"
273
+ begin
274
+ @region.rds.delete_db_snapshot({:db_snapshot_identifier => i.db_snapshot_identifier})
275
+ rescue
276
+ puts "Error removing snapshot: #{i.db_snapshot_identifier}/#{i.snapshot_create_time.to_s}"
277
+ end
278
+ else
279
+ puts "Keeping snapshot: #{i.db_snapshot_identifier}/#{i.snapshot_create_time.to_s}"
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ def wait(options = {:desired_status => "available",
286
+ :timeout => 600})
287
+ inst = @region.find_db_instances({:instance_id => @id})[0]
288
+ if !inst
289
+ puts "Error, instance: #{@id} not found"
290
+ return
291
+ end
292
+ t0 = Time.now.to_i
293
+ while inst.status != options[:desired_status]
294
+ inst = @region.find_db_instances({:instance_id => @id})[0]
295
+ puts "Database: #{@id} at #{@endpoint}. Current status: #{inst.status}"
296
+ if Time.now.to_i - t0 > options[:timeout]
297
+ puts "Timed out waiting for database: #{@id} at #{@endpoint} to move into status: #{options[:desired_status]}. Current status: #{inst.status}"
298
+ return
299
+ end
300
+ sleep 20
301
+ end
302
+ end
303
+
304
+ def status
305
+ @_instance.db_instance_status
306
+ end
307
+
308
+ def get_latest_db_snapshot(options={})
309
+ snapshot_name = options.has_key?(:snapshot_name) ? options[:snapshot_name] : @id
310
+
311
+ last = nil
312
+ last_t = 0
313
+ @region.rds.describe_db_snapshots[:db_snapshots].each do |i|
314
+ if i.db_instance_identifier == snapshot_name and (last.nil? or i.snapshot_create_time > last_t)
315
+ last = i
316
+ last_t = i.snapshot_create_time
317
+ end
318
+ end
319
+ last
320
+ end
321
+
322
+ end
323
+ class AwsInstance
324
+ attr_accessor :id, :tags, :region, :private_ip, :public_ip, :_instance
325
+ def initialize(region, options = {})
326
+ @region = region
327
+ if !options.has_key?(:instance)
328
+ resp = @region.ec2.run_instances(options)
329
+ raise "Error creating instance using options" if resp.nil? or resp[:instances].length <= 0
330
+ @_instance = resp[:instances][0]
331
+ else
332
+ @_instance = options[:instance]
333
+ end
334
+ @id = @_instance[:instance_id]
335
+ @tags = {}
336
+ @_instance.tags.each do |t|
337
+ @tags[t[:key].to_sym] = t[:value]
338
+ end
339
+ @public_ip = @_instance[:public_ip_address]
340
+ @private_ip = @_instance[:private_ip_address]
341
+ end
342
+ def state(use_cached_state=true)
343
+ if !use_cached_state
344
+ response = @region.ec2.describe_instances({instance_ids: [@id]})
345
+ response[:reservations].each do |res|
346
+ res[:instances].each do |inst|
347
+ if inst[:instance_id] == @id
348
+ return inst[:state][:name].strip()
349
+ end
350
+ end
351
+ end
352
+ return ""
353
+ else
354
+ @_instance.state[:name].strip()
355
+ end
356
+ end
357
+ def start(wait=false)
358
+ if self.state(use_cached_state = false) != "stopped"
359
+ puts "Instance cannot be started - #{@region.region}://#{@id} is in the state: #{self.state}"
360
+ return
361
+ end
362
+ puts "Starting instance: #{@region.region}://#{@id}"
363
+ @region.ec2.start_instances({:instance_ids => [@id]})
364
+ if wait
365
+ begin
366
+ sleep 10
367
+ puts "Starting instance: #{@region.region}://#{@id} - state: #{self.state}"
368
+ end while self.state(use_cached_state = false) != "running"
369
+ end
370
+ if @tags.has_key?("elastic_ip")
371
+ @region.ec2.associate_address({:instance_id => @id, :public_ip => @tags['elastic_ip']})
372
+ puts "Associated ip: #{@tags['elastic_ip']} with instance: #{@id}"
373
+ elsif @tags.has_key?("elastic_ip_allocation_id")
374
+ @region.ec2.associate_address({:instance_id => @id, :allocation_id => @tags['elastic_ip_allocation_id']})
375
+ puts "Associated allocation id: #{@tags['elastic_ip_allocation_id']} with instance: #{@id}"
376
+ end
377
+ if @tags.has_key?("elastic_lb")
378
+ self.add_to_lb(@tags["elastic_lb"])
379
+ puts "Adding instance: #{@id} to '#{@tags['elastic_lb']}' load balancer"
380
+ end
381
+ end
382
+ def set_security_groups(groups)
383
+ resp = @region.ec2.modify_instance_attribute({:instance_id => @id,
384
+ :groups => groups})
385
+ end
386
+ def add_tags(h_tags)
387
+ tags = []
388
+ h_tags.each do |k,v|
389
+ tags << {:key => k.to_s, :value => v}
390
+ end
391
+ resp = @region.ec2.create_tags({:resources => [@id],
392
+ :tags => tags})
393
+ end
394
+
395
+ def add_to_lb(lb_name)
396
+ @region.elb.register_instances_with_load_balancer({:load_balancer_name => lb_name,
397
+ :instances => [{:instance_id => @id}]})
398
+ end
399
+
400
+ def remove_from_lb(lb_name)
401
+ lb = @region.elb.describe_load_balancers({:load_balancer_names => [lb_name]})
402
+ if lb and lb[:load_balancer_descriptions].length > 0
403
+ lb[:load_balancer_descriptions][0][:instances].each do |lb_i|
404
+ if lb_i[:instance_id] == @id
405
+ @elb.deregister_instances_from_load_balancer({:load_balancer_name => lb_name,
406
+ :instances => [{:instance_id => @id}]})
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ def terminate()
413
+ @region.ec2.terminate_instances({:instance_ids => [@id]})
414
+ end
415
+
416
+ def archive_logs()
417
+ end
418
+
419
+ def stop(wait=false)
420
+ if self.state(use_cached_state = false) != "running"
421
+ puts "Instance cannot be stopped - #{@region.region}://#{@id} is in the state: #{self.state}"
422
+ return
423
+ end
424
+ if @tags.has_key?("elastic_lb")
425
+ puts "Removing instance: #{@id} from '#{@tags['elastic_lb']}' load balancer"
426
+ remove_from_lb(tags["elastic_lb"])
427
+ end
428
+ puts "Stopping instance: #{@region.region}://#{@id}"
429
+ @region.ec2.stop_instances({:instance_ids => [@id]})
430
+ while self.state(use_cached_state = false) != "stopped"
431
+ sleep 10
432
+ puts "Stopping instance: #{@region.region}://#{@id} - state: #{self.state}"
433
+ end if wait
434
+ if self.state(use_cached_state = false) == "stopped"
435
+ puts "Instance stopped: #{@region.region}://#{@id}"
436
+ end
437
+ end
438
+ def connect
439
+ if self.state(use_cached_state = false) != "running"
440
+ puts "Cannot connect, instance: #{@region.region}://#{@id} due to its state: #{self.state}"
441
+ return
442
+ end
443
+ ip = self.public_ip != "" ? self.public_ip : self.private_ip
444
+ #puts "Connecting: ssh -i ~/.ssh/ec2.#{@region.region}.pem #{@tags[:user]}@#{ip}"
445
+ #exec "ssh -i ~/.ssh/ec2.#{@region.region}.pem #{@tags[:user]}@#{ip}"
446
+ puts "Connecting: ssh #{@tags[:user]}@#{ip}"
447
+ exec "ssh #{@tags[:user]}@#{ip}"
448
+ end
449
+
450
+
451
+ end
452
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aws-tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Connors
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: A set of simple tools to bind some essential functions of aws resources
28
+ email: karmet@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/aws_region.rb
34
+ homepage: http://rubygems.org/gems/aws-tools
35
+ licenses:
36
+ - MIT
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.2.2
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: A set of simple tools to bind some essential functions of aws resources
58
+ test_files: []