awful 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7bedcb9861ea2075c3ea043bd651ebf56edd2668
4
+ data.tar.gz: aa9e8729e5772fef666e9334ad6405bf8977c816
5
+ SHA512:
6
+ metadata.gz: b9af606fdddd26cb7455a485690da1e62b7bc02efdc5d2d2ee494c092957cfdd47b8adb5cae71e98188a0d3f16ecedb26fd44420734de7b4bc128f2fe4e0eec3
7
+ data.tar.gz: 6c6218d4692d44c435aad729be0346d166e6421cc4cf9f6d2ec686b6866179b6f11fbf4ccb2315c391db7740480b545533ddf78155bcf7aff5abc53b36f68ee3
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in awful.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 TODO: Write your name
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Awful
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'awful'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install awful
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/awful/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/awful.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'awful/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "awful"
8
+ spec.version = Awful::VERSION
9
+ spec.authors = ["Ric Lister"]
10
+ spec.email = ["rlister+gh@gmail.com"]
11
+ spec.summary = %q{Simple AWS command-line tool.}
12
+ spec.description = %q{AWS cmdline and yaml loader.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_dependency('aws-sdk', '~> 2')
25
+ spec.add_dependency('thor')
26
+ end
data/bin/asg ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+ #-*- mode: ruby; -*-
3
+
4
+ require 'awful'
5
+ require 'awful/auto_scaling'
6
+
7
+ Awful::AutoScaling.start(ARGV)
data/bin/ec2 ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+ #-*- mode: ruby; -*-
3
+
4
+ require 'awful'
5
+ require 'awful/ec2'
6
+
7
+ Awful::Ec2.start(ARGV)
data/bin/elb ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+ #-*- mode: ruby; -*-
3
+
4
+ require 'awful'
5
+ require 'awful/elb'
6
+
7
+ Awful::Elb.start(ARGV)
data/bin/lc ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+ #-*- mode: ruby; -*-
3
+
4
+ require 'awful'
5
+ require 'awful/launch_config'
6
+
7
+ Awful::LaunchConfig.start(ARGV)
data/bin/subnet ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+ #-*- mode: ruby; -*-
3
+
4
+ require 'awful'
5
+ require 'awful/subnet'
6
+
7
+ Awful::Subnet.start(ARGV)
data/bin/vpc ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+ #-*- mode: ruby; -*-
3
+
4
+ require 'awful'
5
+ require 'awful/vpc'
6
+
7
+ Awful::Vpc.start(ARGV)
data/lib/awful.rb ADDED
@@ -0,0 +1,87 @@
1
+ require "awful/version"
2
+
3
+ require 'aws-sdk'
4
+ require 'thor'
5
+ require 'yaml'
6
+
7
+ module Awful
8
+
9
+ def ec2
10
+ @ec2 ||= Aws::EC2::Client.new
11
+ end
12
+
13
+ def autoscaling
14
+ @autoscaling ||= Aws::AutoScaling::Client.new
15
+ end
16
+
17
+ def elb
18
+ @elb ||= Aws::ElasticLoadBalancing::Client.new
19
+ end
20
+
21
+ def symbolize_keys(thing)
22
+ if thing.is_a?(Hash)
23
+ Hash[ thing.map { |k,v| [ k.to_sym, symbolize_keys(v) ] } ]
24
+ elsif thing.respond_to?(:map)
25
+ thing.map { |v| symbolize_keys(v) }
26
+ else
27
+ thing
28
+ end
29
+ end
30
+
31
+ def stringify_keys(thing)
32
+ if thing.is_a?(Hash)
33
+ Hash[ thing.map { |k,v| [ k.to_s, stringify_keys(v) ] } ]
34
+ elsif thing.respond_to?(:map)
35
+ thing.map { |v| stringify_keys(v) }
36
+ else
37
+ thing
38
+ end
39
+ end
40
+
41
+ def load_cfg(options = {})
42
+ cfg = $stdin.tty? ? {} : symbolize_keys(YAML.load($stdin.read))
43
+ cfg.merge(symbolize_keys(options.reject{ |_,v| v.nil? }))
44
+ end
45
+
46
+ def only_keys_matching(hash, keylist)
47
+ hash.select do |key,_|
48
+ keylist.include?(key)
49
+ end
50
+ end
51
+
52
+ def remove_empty_strings(hash)
53
+ hash.reject do |_,value|
54
+ value.respond_to?(:empty?) and value.empty?
55
+ end
56
+ end
57
+
58
+ def tag_name(thing)
59
+ tn = thing.tags.find { |tag| tag.key == 'Name' }
60
+ tn && tn.value
61
+ end
62
+
63
+ ## return id for instance by name
64
+ def find_instance(name)
65
+ if name .nil?
66
+ nil?
67
+ elsif name.match(/^i-[\d[a-f]]{8}$/)
68
+ name
69
+ else
70
+ ec2.describe_instances.map(&:reservations).flatten.map(&:instances).flatten.find do |instance|
71
+ tag_name(instance) == name
72
+ end.instance_id
73
+ end
74
+ end
75
+
76
+ ## return id for subnet by name
77
+ def find_subnet(name)
78
+ if name.match(/^subnet-[\d[a-f]]{8}$/)
79
+ name
80
+ else
81
+ ec2.describe_subnets.map(&:subnets).flatten.find do |subnet|
82
+ tag_name(subnet) == name
83
+ end.subnet_id
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,135 @@
1
+ module Awful
2
+
3
+ class AutoScaling < Thor
4
+ include Awful
5
+
6
+ desc 'ls [PATTERN]', 'list autoscaling groups with name matching PATTERN'
7
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
8
+ def ls(name = /./)
9
+ fields = if options[:long]
10
+ ->(a) { [
11
+ a.auto_scaling_group_name,
12
+ a.launch_configuration_name,
13
+ "#{a.instances.length}/#{a.desired_capacity}",
14
+ "#{a.min_size}-#{a.max_size}",
15
+ a.availability_zones.sort.join(','),
16
+ a.created_time
17
+ ] }
18
+ else
19
+ ->(a) { [ a.auto_scaling_group_name ] }
20
+ end
21
+
22
+ autoscaling.describe_auto_scaling_groups.map(&:auto_scaling_groups).flatten.select do |asg|
23
+ asg.auto_scaling_group_name.match(name)
24
+ end.map do |asg|
25
+ fields.call(asg)
26
+ end.tap do |list|
27
+ print_table list
28
+ end
29
+ end
30
+
31
+ desc 'delete NAME', 'delete autoscaling group'
32
+ def delete(name)
33
+ if yes? "Really delete auto-scaling group #{name}?", :yellow
34
+ autoscaling.delete_auto_scaling_group(auto_scaling_group_name: name)
35
+ end
36
+ end
37
+
38
+ desc 'instances', 'list instance IDs for instances in groups matching NAME'
39
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
40
+ def instances(name)
41
+ fields = options[:long] ? %i[instance_id auto_scaling_group_name availability_zone lifecycle_state health_status launch_configuration_name] : %i[instance_id]
42
+
43
+ autoscaling.describe_auto_scaling_instances.map(&:auto_scaling_instances).flatten.select do |instance|
44
+ instance.auto_scaling_group_name.match(name)
45
+ end.map do |instance|
46
+ fields.map { |field| instance.send(field) }
47
+ end.tap do |list|
48
+ print_table list
49
+ end
50
+
51
+ end
52
+
53
+ desc 'ips NAME', 'list IPs for instances in groups matching NAME'
54
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
55
+ def ips(name)
56
+ fields = options[:long] ? %i[public_ip_address private_ip_address instance_id image_id instance_type launch_time] : %i[ public_ip_address ]
57
+
58
+ instance_ids = autoscaling.describe_auto_scaling_instances.map(&:auto_scaling_instances).flatten.select do |instance|
59
+ instance.auto_scaling_group_name.match(name)
60
+ end.map(&:instance_id)
61
+
62
+ ec2 = Aws::EC2::Client.new
63
+ ec2.describe_instances(instance_ids: instance_ids).map(&:reservations).flatten.map(&:instances).flatten.map do |instance|
64
+ fields.map { |field| instance.send(field) }
65
+ end.tap do |list|
66
+ print_table list
67
+ end
68
+ end
69
+
70
+ desc 'dump NAME', 'dump existing autoscaling group as yaml'
71
+ def dump(name)
72
+ asg = autoscaling.describe_auto_scaling_groups(auto_scaling_group_names: Array(name)).map(&:auto_scaling_groups).flatten.first.to_hash
73
+ puts YAML.dump(stringify_keys(asg))
74
+ end
75
+
76
+ desc 'create NAME', 'create a new auto-scaling group'
77
+ def create(name)
78
+ opt = load_cfg
79
+ whitelist = %i[auto_scaling_group_name launch_configuration_name instance_id min_size max_size desired_capacity default_cooldown availability_zones
80
+ load_balancer_names health_check_type health_check_grace_period placement_group vpc_zone_identifier termination_policies tags ]
81
+
82
+ opt[:auto_scaling_group_name] = name
83
+ opt = remove_empty_strings(opt)
84
+ opt = only_keys_matching(opt, whitelist)
85
+
86
+ ## scrub aws-provided keys from tags
87
+ opt[:tags] = opt.has_key?(:tags) ? opt[:tags].map { |tag| only_keys_matching(tag, %i[key value propagate_at_launch]) } : []
88
+
89
+ autoscaling.create_auto_scaling_group(opt)
90
+ end
91
+
92
+ desc 'update NAME', 'update existing auto-scaling group'
93
+ method_option :desired_capacity, aliases: '-d', default: nil, desc: 'Set desired capacity'
94
+ method_option :min_size, aliases: '-m', default: nil, desc: 'Set minimum capacity'
95
+ method_option :max_size, aliases: '-M', default: nil, desc: 'Set maximum capacity'
96
+ def update(name)
97
+ opt = load_cfg(options)
98
+ whitelist = %i[auto_scaling_group_name launch_configuration_name min_size max_size desired_capacity default_cooldown availability_zones
99
+ health_check_type health_check_grace_period placement_group vpc_zone_identifier termination_policies ]
100
+
101
+ ## cleanup the group options
102
+ opt[:auto_scaling_group_name] = name
103
+ opt = remove_empty_strings(opt)
104
+
105
+ ## update the group
106
+ autoscaling.update_auto_scaling_group(only_keys_matching(opt, whitelist))
107
+
108
+ ## update any tags
109
+ if opt[:tags]
110
+ tags = opt[:tags].map { |tag| tag.merge(resource_id: name, resource_type: 'auto-scaling-group') }
111
+ autoscaling.create_or_update_tags(tags: tags)
112
+ end
113
+ end
114
+
115
+ desc 'terminate NAME', 'terminate instances in group NAME'
116
+ method_option :decrement, aliases: '-d', default: false, desc: 'Decrement desired capacity for each terminated instance'
117
+ def terminate(name, num = 1)
118
+ instance_ids = autoscaling.describe_auto_scaling_instances.map(&:auto_scaling_instances).flatten.select do |instance|
119
+ instance.auto_scaling_group_name == name
120
+ end.map(&:instance_id)
121
+
122
+ instances = ec2.describe_instances(instance_ids: instance_ids).map(&:reservations).flatten.map(&:instances).flatten.sort_by(&:launch_time)
123
+ instances.first(num.to_i).map(&:instance_id).tap do |ids|
124
+ if yes? "Really terminate #{num} instances: #{ids.join(',')}?", :yellow
125
+ ids.each do |id|
126
+ autoscaling.terminate_instance_in_auto_scaling_group(instance_id: id, should_decrement_desired_capacity: options[:decrement] && true)
127
+ end
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ end
134
+
135
+ end
data/lib/awful/ec2.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'base64'
2
+
3
+ module Awful
4
+
5
+ class Ec2 < Thor
6
+ include Awful
7
+
8
+ desc 'ls [PATTERN]', 'list EC2 instances [with id or tags matching PATTERN]'
9
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
10
+ def ls(name = /./)
11
+ fields = options[:long] ?
12
+ ->(i) { [ tag_name(i) || '-', i.instance_id, i.instance_type, i.virtualization_type, i.placement.availability_zone, i.state.name,
13
+ i.security_groups.map(&:group_name).join(','), i.private_ip_address, i.public_ip_address ] } :
14
+ ->(i) { [ tag_name(i) || i.instance_id ] }
15
+
16
+ ec2.describe_instances.map(&:reservations).flatten.map(&:instances).flatten.select do |instance|
17
+ instance.instance_id.match(name) or instance.tags.any? { |tag| tag.value.match(name) }
18
+ end.map do |instance|
19
+ fields.call(instance)
20
+ end.tap do |list|
21
+ print_table list.sort
22
+ end
23
+ end
24
+
25
+ desc 'dump NAME', 'dump EC2 instance with id or tag NAME as yaml'
26
+ def dump(name)
27
+ ec2.describe_instances.map(&:reservations).flatten.map(&:instances).flatten.find do |instance|
28
+ instance.instance_id == name or tag_name(instance) == name
29
+ end.tap do |instance|
30
+ puts YAML.dump(stringify_keys(instance.to_hash))
31
+ end
32
+ end
33
+
34
+ desc 'create NAME', 'run new EC2 instance'
35
+ method_option :subnet, :aliases => '-s', :default => nil, :desc => 'VPC subnet to use; default nil (classic)'
36
+ method_option :public_ip, :aliases => '-p', :default => true, :desc => 'Assign public IP to VPC instances'
37
+ method_option :elastic_ip, :aliases => '-e', :default => true, :desc => 'Assign new elastic IP to instances'
38
+ def create(name)
39
+ opt = load_cfg.merge(symbolize_keys(options))
40
+ whitelist = %i[image_id min_count max_count key_name security_group_ids user_data instance_type kernel_id
41
+ ramdisk_id monitoring subnet_id disable_api_termination instance_initiated_shutdown_behavior
42
+ additional_info iam_instance_profile ebs_optimized network_interfaces]
43
+
44
+ opt[:min_count] ||= 1
45
+ opt[:max_count] ||= 1
46
+ opt[:monitoring] = {enabled: opt.fetch(:monitoring, {}).fetch(:state, '') == 'enabled'}
47
+
48
+ ## set subnet from human-readable name, either for network interface, or instance-level
49
+ if opt[:subnet]
50
+ subnet = find_subnet(opt[:subnet])
51
+ opt[:network_interfaces] ? (opt[:network_interfaces][0][:subnet_id] = subnet) : (opt[:subnet_id] = subnet)
52
+ end
53
+
54
+ (opt[:tags] = opt.fetch(:tags, [])).find_index { |t| t[:key] == 'Name' }.tap do |index|
55
+ opt[:tags][index || 0] = {key: 'Name', value: name}
56
+ end
57
+
58
+ ## TODO: block_device_mappings
59
+ ## TODO: placement
60
+
61
+ opt[:security_group_ids] = opt.fetch(:security_groups, []).map { |sg| sg[:group_id] }
62
+ opt[:user_data] = Base64.strict_encode64(opt[:user_data]) if opt[:user_data]
63
+
64
+ # scrub unwanted fields from a copied instance dump
65
+ opt = remove_empty_strings(opt)
66
+
67
+ ## start instance
68
+ response = ec2.run_instances(only_keys_matching(opt, whitelist))
69
+ ids = response.instances.map(&:instance_id)
70
+ ec2.create_tags(resources: ids, tags: opt[:tags]) # tag instances
71
+ puts ids # report new instance ids
72
+
73
+ ## wait for instance to enter running state
74
+ puts 'running instance ...'
75
+ ec2.wait_until(:instance_running, instance_ids: ids)
76
+
77
+ ## allocate and associate new elastic IPs
78
+ ids.map { |id| associate(id, allocate.allocation_id) } if opt[:elastic_ip]
79
+
80
+ ## report DNS or IP for instance
81
+ ec2.describe_instances(instance_ids: ids).map(&:reservations).flatten.map(&:instances).flatten.map do |instance|
82
+ instance.public_dns_name or instance.public_ip_address or instance.private_ip_address
83
+ end.tap do |list|
84
+ puts list
85
+ end
86
+ end
87
+
88
+ desc 'allocate', 'allocate a new elastic IP address'
89
+ def allocate
90
+ ec2.allocate_address(domain: 'vpc').first.tap do |eip|
91
+ puts eip.allocation_id, eip.public_ip
92
+ end
93
+ end
94
+
95
+ desc 'associate NAME IP', 'associate a public ip with an instance'
96
+ def associate(name, eip)
97
+ ec2.associate_address(instance_id: find_instance(name), allocation_id: eip).map(&:association_id).tap do |id|
98
+ puts id
99
+ end
100
+ end
101
+
102
+ desc 'addresses', 'list elastic IP addresses'
103
+ def addresses
104
+ ec2.describe_addresses.map(&:addresses).flatten.map do |ip|
105
+ [ ip.allocation_id, ip.public_ip, ip.instance_id, ip.domain ]
106
+ end.tap do |list|
107
+ print_table list
108
+ end
109
+ end
110
+
111
+ desc 'dns NAME', 'get public DNS for named instance'
112
+ def dns(name)
113
+ ec2.describe_instances.map(&:reservations).flatten.map(&:instances).flatten.find do |instance|
114
+ instance.instance_id == name or (n = tag_name(instance) and n.match(name))
115
+ end.public_dns_name.tap do |dns|
116
+ puts dns
117
+ end
118
+ end
119
+
120
+ desc 'delete NAME', 'terminate a running instance'
121
+ def delete(name)
122
+ id =
123
+ if name.match(/^i-[\d[a-f]]{8}$/)
124
+ name
125
+ else
126
+ ec2.describe_instances.map(&:reservations).flatten.map(&:instances).flatten.find do |instance|
127
+ tag_name(instance) == name and not %w[terminated shutting-down].include?(instance.state.name)
128
+ end.instance_id
129
+ end
130
+ if yes? "Really terminate instance #{name} (#{id})?", :yellow
131
+ ec2.terminate_instances(instance_ids: Array(id))
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ end
data/lib/awful/elb.rb ADDED
@@ -0,0 +1,86 @@
1
+ module Awful
2
+
3
+ class Elb < Thor
4
+ include Awful
5
+
6
+ desc 'ls [PATTERN]', 'list vpcs [with any tags matching PATTERN]'
7
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
8
+ def ls(name = /./)
9
+ fields = options[:long] ?
10
+ ->(e) { [e.load_balancer_name, e.instances.length, e.availability_zones.join(','), e.dns_name] } :
11
+ ->(e) { [e.load_balancer_name] }
12
+
13
+ elb.describe_load_balancers.map(&:load_balancer_descriptions).flatten.select do |elb|
14
+ elb.load_balancer_name.match(name)
15
+ end.map do |elb|
16
+ fields.call(elb)
17
+ end.tap do |list|
18
+ print_table list
19
+ end
20
+ end
21
+
22
+ desc 'instances NAME', 'list instances and states for elb NAME'
23
+ def instances(name)
24
+ instances = elb.describe_instance_health(load_balancer_name: name).map(&:instance_states).flatten
25
+ instances_by_id = instances.inject({}) { |hash,instance| hash[instance.instance_id] = instance; hash }
26
+
27
+ ec2.describe_instances(instance_ids: instances_by_id.keys).map(&:reservations).flatten.map(&:instances).flatten.map do |instance|
28
+ health = instances_by_id[instance.instance_id]
29
+ [ instance.tags.map(&:value).sort.join(','), instance.public_ip_address, health.state, health.reason_code, health.description ]
30
+ end.tap do |list|
31
+ print_table list
32
+ end
33
+ end
34
+
35
+ desc 'dump NAME', 'dump VPC with id or tag NAME as yaml'
36
+ def dump(name)
37
+ lb = elb.describe_load_balancers(load_balancer_names: Array(name)).map(&:load_balancer_descriptions).flatten.first.to_hash
38
+ puts YAML.dump(stringify_keys(lb))
39
+ end
40
+
41
+ desc 'dns NAME', 'get DNS name for load-balancers matching NAME'
42
+ def dns(name)
43
+ elb.describe_load_balancers.map(&:load_balancer_descriptions).flatten.select do |elb|
44
+ elb.load_balancer_name.match(name)
45
+ end.map(&:dns_name).tap do |dns|
46
+ puts dns
47
+ end
48
+ end
49
+
50
+ desc 'create NAME', 'create new load-balancer'
51
+ def create(name)
52
+ whitelist = %i[load_balancer_name listeners availability_zones subnets security_groups scheme tags]
53
+ opt = load_cfg
54
+ opt[:load_balancer_name] = name
55
+ opt[:listeners] = opt.fetch(:listener_descriptions, []).map { |l| l[:listener] }
56
+ opt.delete(:availability_zones) unless opt.fetch(:subnets, []).empty?
57
+ opt = remove_empty_strings(opt)
58
+ opt = only_keys_matching(opt, whitelist)
59
+ elb.create_load_balancer(opt).map(&:dns_name).flatten.tap { |dns| puts dns }
60
+ health_check(name) if opt[:health_check]
61
+ end
62
+
63
+ desc 'health_check NAME', 'set health-check'
64
+ method_option :target, aliases: '-t', default: nil, desc: 'Health check target'
65
+ method_option :interval, aliases: '-i', default: nil, desc: 'Check interval'
66
+ method_option :timeout, aliases: '-o', default: nil, desc: 'Check timeout'
67
+ method_option :unhealthy_threshold, aliases: '-u', default: nil, desc: 'Unhealthy threshold'
68
+ method_option :healthy_threshold, aliases: '-h', default: nil, desc: 'Healthy threshold'
69
+ def health_check(name)
70
+ opt = load_cfg.merge(options.reject(&:nil?))
71
+ hc = elb.configure_health_check(load_balancer_name: name, health_check: opt[:health_check])
72
+ hc.map(&:health_check).flatten.first.tap do |h|
73
+ print_table h.to_hash
74
+ end
75
+ end
76
+
77
+ desc 'delete NAME', 'delete load-balancer'
78
+ def delete(name)
79
+ if yes? "really delete ELB #{name}?", :yellow
80
+ elb.delete_load_balancer(load_balancer_name: name)
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,49 @@
1
+ require 'base64'
2
+
3
+ module Awful
4
+
5
+ class LaunchConfig < Thor
6
+ include Awful
7
+
8
+ desc 'ls [PATTERN]', 'list launch configs with name matching PATTERN'
9
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
10
+ def ls(name = /./)
11
+ fields = options[:long] ? %i[launch_configuration_name image_id instance_type created_time] : %i[launch_configuration_name]
12
+ autoscaling.describe_launch_configurations.map(&:launch_configurations).flatten.select do |lc|
13
+ lc.launch_configuration_name.match(name)
14
+ end.map do |lc|
15
+ fields.map { |field| lc.send(field) }
16
+ end.tap do |list|
17
+ print_table list
18
+ end
19
+ end
20
+
21
+ desc 'delete NAME', 'delete launch configuration'
22
+ def delete(name)
23
+ autoscaling.delete_launch_configuration(launch_configuration_name: name)
24
+ end
25
+
26
+ desc 'dump NAME', 'dump existing launch_configuration as yaml'
27
+ def dump(name)
28
+ lc = autoscaling.describe_launch_configurations(launch_configuration_names: Array(name)).map(&:launch_configurations).flatten.first.to_hash
29
+ lc[:user_data] = Base64.decode64(lc[:user_data])
30
+ puts YAML.dump(stringify_keys(lc))
31
+ end
32
+
33
+ desc 'create [NAME]', 'create a new launch configuration'
34
+ def create(name)
35
+ opt = load_cfg
36
+ whitelist = %i[launch_configuration_name image_id key_name security_groups classic_link_vpc_id classic_link_vpc_security_groups user_data
37
+ instance_id instance_type kernel_id ramdisk_id block_device_mappings instance_monitoring spot_price iam_instance_profile
38
+ ebs_optimized associate_public_ip_address placement_tenancy]
39
+ opt[:launch_configuration_name] = "#{name}-#{Time.now.utc.strftime('%Y%m%d%H%M%S')}"
40
+ opt[:user_data] = Base64.encode64(opt[:user_data]) # encode user data
41
+ opt = remove_empty_strings(opt)
42
+ opt = only_keys_matching(opt, whitelist)
43
+ autoscaling.create_launch_configuration(opt)
44
+ puts opt[:launch_configuration_name]
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,32 @@
1
+ module Awful
2
+
3
+ class Subnet < Thor
4
+ include Awful
5
+
6
+ desc 'ls [PATTERN]', 'list subnets [with any tags matching PATTERN]'
7
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
8
+ def ls(name = /./)
9
+ fields = options[:long] ?
10
+ ->(s) { [s.tags.map{ |t| t.value }.join(','), s.subnet_id, s.state, s.cidr_block, s.available_ip_address_count, s.availability_zone] } :
11
+ ->(s) { [s.subnet_id] }
12
+ ec2.describe_subnets.map(&:subnets).flatten.select do |subnet|
13
+ subnet.tags.any? { |tag| tag.value.match(name) }
14
+ end.map do |subnet|
15
+ fields.call(subnet)
16
+ end.tap do |list|
17
+ print_table list.sort
18
+ end
19
+ end
20
+
21
+ desc 'dump NAME', 'dump subnet with id or tag NAME as yaml'
22
+ def dump(name)
23
+ ec2.describe_subnets.map(&:subnets).flatten.find do |subnet|
24
+ subnet.subnet_id == name or subnet.tags.any? { |tag| tag.value == name }
25
+ end.tap do |subnet|
26
+ puts YAML.dump(stringify_keys(subnet.to_hash))
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,3 @@
1
+ module Awful
2
+ VERSION = "0.0.1"
3
+ end
data/lib/awful/vpc.rb ADDED
@@ -0,0 +1,32 @@
1
+ module Awful
2
+
3
+ class Vpc < Thor
4
+ include Awful
5
+
6
+ desc 'ls [PATTERN]', 'list vpcs [with any tags matching PATTERN]'
7
+ method_option :long, aliases: '-l', default: false, desc: 'Long listing'
8
+ def ls(name = /./)
9
+ fields = options[:long] ?
10
+ ->(v) { [v.tags.map{ |t| t.value }.join(','), v.vpc_id, v.state, v.cidr_block] } :
11
+ ->(v) { [v.vpc_id] }
12
+ ec2.describe_vpcs.map(&:vpcs).flatten.select do |vpc|
13
+ vpc.tags.any? { |tag| tag.value.match(name) }
14
+ end.map do |vpc|
15
+ fields.call(vpc)
16
+ end.tap do |list|
17
+ print_table list
18
+ end
19
+ end
20
+
21
+ desc 'dump NAME', 'dump VPC with id or tag NAME as yaml'
22
+ def dump(name)
23
+ ec2.describe_vpcs.map(&:vpcs).flatten.find do |vpc|
24
+ vpc.vpc_id == name or vpc.tags.any? { |tag| tag.value == name }
25
+ end.tap do |vpc|
26
+ puts YAML.dump(stringify_keys(vpc.to_hash))
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: awful
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ric Lister
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: AWS cmdline and yaml loader.
70
+ email:
71
+ - rlister+gh@gmail.com
72
+ executables:
73
+ - asg
74
+ - ec2
75
+ - elb
76
+ - lc
77
+ - subnet
78
+ - vpc
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - ".gitignore"
83
+ - Gemfile
84
+ - LICENSE.txt
85
+ - README.md
86
+ - Rakefile
87
+ - awful.gemspec
88
+ - bin/asg
89
+ - bin/ec2
90
+ - bin/elb
91
+ - bin/lc
92
+ - bin/subnet
93
+ - bin/vpc
94
+ - lib/awful.rb
95
+ - lib/awful/auto_scaling.rb
96
+ - lib/awful/ec2.rb
97
+ - lib/awful/elb.rb
98
+ - lib/awful/launch_config.rb
99
+ - lib/awful/subnet.rb
100
+ - lib/awful/version.rb
101
+ - lib/awful/vpc.rb
102
+ homepage: ''
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.2.2
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Simple AWS command-line tool.
126
+ test_files: []