maws 0.8.0
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.
- data/bin/maws +10 -0
- data/lib/maws/chunk_source.rb +41 -0
- data/lib/maws/command.rb +62 -0
- data/lib/maws/command_loader.rb +28 -0
- data/lib/maws/command_options_parser.rb +37 -0
- data/lib/maws/commands/configure.rb +287 -0
- data/lib/maws/commands/console.rb +38 -0
- data/lib/maws/commands/create.rb +25 -0
- data/lib/maws/commands/describe.rb +15 -0
- data/lib/maws/commands/destroy.rb +11 -0
- data/lib/maws/commands/elb-add.rb +17 -0
- data/lib/maws/commands/elb-describe.rb +23 -0
- data/lib/maws/commands/elb-disable-zones.rb +17 -0
- data/lib/maws/commands/elb-enable-zones.rb +18 -0
- data/lib/maws/commands/elb-remove.rb +16 -0
- data/lib/maws/commands/set-prefix.rb +24 -0
- data/lib/maws/commands/set-security-groups.rb +442 -0
- data/lib/maws/commands/start.rb +11 -0
- data/lib/maws/commands/status.rb +25 -0
- data/lib/maws/commands/stop.rb +11 -0
- data/lib/maws/commands/teardown.rb +11 -0
- data/lib/maws/commands/volumes-cleanup.rb +22 -0
- data/lib/maws/commands/volumes-status.rb +43 -0
- data/lib/maws/commands/wait.rb +61 -0
- data/lib/maws/connection.rb +121 -0
- data/lib/maws/core_ext/object.rb +5 -0
- data/lib/maws/description/ebs.rb +40 -0
- data/lib/maws/description/ec2.rb +72 -0
- data/lib/maws/description/elb.rb +52 -0
- data/lib/maws/description/rds.rb +47 -0
- data/lib/maws/description.rb +78 -0
- data/lib/maws/instance/ebs.rb +45 -0
- data/lib/maws/instance/ec2.rb +144 -0
- data/lib/maws/instance/elb.rb +92 -0
- data/lib/maws/instance/rds.rb +84 -0
- data/lib/maws/instance.rb +167 -0
- data/lib/maws/instance_collection.rb +98 -0
- data/lib/maws/instance_display.rb +84 -0
- data/lib/maws/instance_matcher.rb +27 -0
- data/lib/maws/loader.rb +173 -0
- data/lib/maws/logger.rb +66 -0
- data/lib/maws/mash.rb +9 -0
- data/lib/maws/maws.rb +102 -0
- data/lib/maws/profile_loader.rb +92 -0
- data/lib/maws/specification.rb +127 -0
- data/lib/maws/ssh.rb +7 -0
- data/lib/maws/trollop.rb +782 -0
- data/lib/maws/volumes_command.rb +29 -0
- data/lib/maws.rb +25 -0
- metadata +115 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'right_aws'
|
2
|
+
require 'maws/mash'
|
3
|
+
require 'maws/description'
|
4
|
+
|
5
|
+
class Connection
|
6
|
+
attr_reader :ec2, :rds, :elb
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
|
11
|
+
@access_key_id = @config.aws_key.key_id
|
12
|
+
@secret_key = @config.aws_key.secret_key
|
13
|
+
|
14
|
+
@params = {:region => @config.region, :logger => $right_aws_logger}
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect(services)
|
18
|
+
# The right_aws gem parses the EC2_URL environment variable if it is set. The EC2 CLI tools also use that variable
|
19
|
+
# but expect the hostname to be region-specific (e.g., us-east-1.ec2.amazonaws.com) instead of generic
|
20
|
+
# (e.g., ec2.amazonaws.com). To avoid conflicts, unset the variable here and use the right_aws default value.
|
21
|
+
ENV["EC2_URL"] = nil
|
22
|
+
|
23
|
+
# always connect to ec2
|
24
|
+
@ec2 = RightAws::Ec2.new(@access_key_id, @secret_key, @params.dup)
|
25
|
+
|
26
|
+
if services.include?(:rds)
|
27
|
+
@rds = RightAws::RdsInterface.new(@access_key_id, @secret_key, @params.dup)
|
28
|
+
end
|
29
|
+
|
30
|
+
if services.include?(:elb)
|
31
|
+
@elb = RightAws::ElbInterface.new(@access_key_id, @secret_key, @params.dup)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def available_zones
|
36
|
+
ec2.describe_availability_zones.
|
37
|
+
find_all{ |zone_description| zone_description[:zone_state] == "available"}.
|
38
|
+
map { |zone_description| zone_description[:zone_name][/\w$/]}
|
39
|
+
end
|
40
|
+
|
41
|
+
def image_id_for_image_name(image_name)
|
42
|
+
return if image_name.nil? || image_name.empty?
|
43
|
+
images = @ec2.describe_images(:filters => { 'tag:Name' => image_name})
|
44
|
+
if images.empty?
|
45
|
+
error "No AMI with name '#{image_name}'"
|
46
|
+
elsif images.count > 1
|
47
|
+
error "Ambigous AMI name: '#{image_name}'. Several AMIs match it #{images.collect{|i| i[:aws_id]}.join(', ')}"
|
48
|
+
else
|
49
|
+
images.first[:aws_id]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def descriptions(services = nil)
|
54
|
+
services ||= :all
|
55
|
+
descriptions = []
|
56
|
+
|
57
|
+
descriptions += ec2_descriptions if services == :all or services.include?(:ec2)
|
58
|
+
descriptions += rds_descriptions if services == :all or services.include?(:rds)
|
59
|
+
descriptions += elb_descriptions if services == :all or services.include?(:elb)
|
60
|
+
|
61
|
+
descriptions
|
62
|
+
end
|
63
|
+
|
64
|
+
def ec2_descriptions
|
65
|
+
# convert aws description to Description
|
66
|
+
descriptions = descriptions = ec2.describe_instances.map {|description|
|
67
|
+
description[:service] = :ec2
|
68
|
+
Description.create(description)
|
69
|
+
}
|
70
|
+
|
71
|
+
# filter out terminated when same name exists as a living one and terminated
|
72
|
+
by_name = descriptions.group_by { |d| d.name }
|
73
|
+
|
74
|
+
# if there is one than more for the same name: delete terminated descriptions, take the last one (trust AWS sorting)
|
75
|
+
by_name.map {|name, descriptions|
|
76
|
+
if descriptions.count > 1
|
77
|
+
descriptions.delete_if {|d| d.status == "terminated"}
|
78
|
+
descriptions.replace([descriptions.last]).compact!
|
79
|
+
end
|
80
|
+
}
|
81
|
+
|
82
|
+
filter_current_profile_prefix(by_name.values.flatten)
|
83
|
+
end
|
84
|
+
|
85
|
+
def rds_descriptions
|
86
|
+
return [] unless rds
|
87
|
+
|
88
|
+
descriptions = rds.describe_db_instances.map { |description|
|
89
|
+
description[:service] = :rds
|
90
|
+
Description.create(description)
|
91
|
+
}
|
92
|
+
|
93
|
+
filter_current_profile_prefix(descriptions)
|
94
|
+
end
|
95
|
+
|
96
|
+
def elb_descriptions
|
97
|
+
return [] unless elb
|
98
|
+
|
99
|
+
descriptions = elb.describe_load_balancers.map { |description|
|
100
|
+
description[:service] = :elb
|
101
|
+
Description.create(description)
|
102
|
+
}
|
103
|
+
|
104
|
+
filter_current_profile_prefix(descriptions)
|
105
|
+
end
|
106
|
+
|
107
|
+
def ebs_descriptions
|
108
|
+
descriptions = ec2.describe_volumes.map { |description|
|
109
|
+
description[:service] = :ebs
|
110
|
+
Description.create(description)
|
111
|
+
}
|
112
|
+
|
113
|
+
filter_current_profile_prefix(descriptions)
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def filter_current_profile_prefix(descriptions)
|
119
|
+
descriptions.delete_if {|d| d.profile != @config.profile.name || d.prefix != @config.prefix}
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Description::EBS < Description
|
2
|
+
def initialize(description)
|
3
|
+
description[:service] ||= :ebs
|
4
|
+
@description = description
|
5
|
+
end
|
6
|
+
|
7
|
+
def name
|
8
|
+
(tags && tags["Name"]) || aws_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def status
|
12
|
+
aws_status
|
13
|
+
end
|
14
|
+
|
15
|
+
def region_zone
|
16
|
+
description[:zone]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# example EBS volume descriptions
|
21
|
+
# [{:snapshot_id=>"snap-a536ecc0",
|
22
|
+
# :service => :ebs, # this is set by maws
|
23
|
+
# :aws_id=>"vol-85fe67e8",
|
24
|
+
# :aws_status=>"available",
|
25
|
+
# :aws_created_at=>"2011-12-15T18:47:26.000Z",
|
26
|
+
# :zone=>"us-east-1c",
|
27
|
+
# :tags=>{},
|
28
|
+
# :aws_size=>100},
|
29
|
+
# {:aws_device=>"/dev/sda",
|
30
|
+
# :snapshot_id=>"snap-35446157",
|
31
|
+
# :aws_id=>"vol-6ce63401",
|
32
|
+
# :aws_status=>"in-use",
|
33
|
+
# :aws_created_at=>"2011-11-19T22:45:03.000Z",
|
34
|
+
# :aws_attachment_status=>"attached",
|
35
|
+
# :zone=>"us-east-1c",
|
36
|
+
# :tags=>{"Name"=>"foo-e2e-control-1"},
|
37
|
+
# :aws_size=>6,
|
38
|
+
# :aws_attached_at=>"2011-11-19T22:45:24.000Z",
|
39
|
+
# :aws_instance_id=>"i-fd126f9e",
|
40
|
+
# :delete_on_termination=>true},
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class Description::EC2 < Description
|
2
|
+
def initialize(description)
|
3
|
+
description[:service] ||= :ec2
|
4
|
+
@description = description
|
5
|
+
end
|
6
|
+
|
7
|
+
def name
|
8
|
+
(tags && tags["Name"]) || aws_instance_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def aws_id
|
12
|
+
aws_instance_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def status
|
16
|
+
aws_state
|
17
|
+
end
|
18
|
+
|
19
|
+
def region_zone
|
20
|
+
aws_availability_zone
|
21
|
+
end
|
22
|
+
|
23
|
+
def ebs_volumes
|
24
|
+
block_device_mappings || []
|
25
|
+
end
|
26
|
+
|
27
|
+
def ebs_volume_ids
|
28
|
+
ebs_volumes.map {|v| v[:ebs_volume_id]}
|
29
|
+
end
|
30
|
+
|
31
|
+
def attached_ebs_volume_ids
|
32
|
+
ebs_volumes.find_all{|v| v[:ebs_status] == 'attached'}.map {|v| v[:ebs_volume_id]}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# example ec2 description
|
38
|
+
# {:private_ip_address=>"10.240.7.99",
|
39
|
+
# :service => :ec2, # this is set by maws
|
40
|
+
# :aws_image_id=>"ami-c2a3f5d4",
|
41
|
+
# :ip_address=>"174.129.134.109",
|
42
|
+
# :dns_name=>"ec2-174-129-134-109.compute-1.amazonaws.com",
|
43
|
+
# :aws_instance_type=>"m1.small",
|
44
|
+
# :aws_owner=>"826693181925",
|
45
|
+
# :root_device_name=>"/dev/sda1",
|
46
|
+
# :instance_class=>"elastic",
|
47
|
+
# :aws_state=>"running",
|
48
|
+
# :private_dns_name=>"domU-12-31-39-04-00-95.compute-1.internal",
|
49
|
+
# :aws_reason=>"",
|
50
|
+
# :aws_launch_time=>"2009-11-18T14:03:25.000Z",
|
51
|
+
# :aws_reservation_id=>"r-54d38542",
|
52
|
+
# :aws_state_code=>16,
|
53
|
+
# :ami_launch_index=>"0",
|
54
|
+
# :aws_availability_zone=>"us-east-1a",
|
55
|
+
# :aws_groups=>["default"],
|
56
|
+
# :monitoring_state=>"disabled",
|
57
|
+
# :aws_product_codes=>[],
|
58
|
+
# :tags => {"Name" => "foo-test-web-01"}
|
59
|
+
# :ssh_key_name=>"",
|
60
|
+
# :block_device_mappings=>
|
61
|
+
# [{:ebs_status=>"attached",
|
62
|
+
# :ebs_delete_on_termination=>true,
|
63
|
+
# :ebs_attach_time=>"2009-11-18T14:03:34.000Z",
|
64
|
+
# :device_name=>"/dev/sda1",
|
65
|
+
# :ebs_volume_id=>"vol-e600f98f"},
|
66
|
+
# {:ebs_status=>"attached",
|
67
|
+
# :ebs_delete_on_termination=>true,
|
68
|
+
# :ebs_attach_time=>"2009-11-18T14:03:34.000Z",
|
69
|
+
# :device_name=>"/dev/sdk",
|
70
|
+
# :ebs_volume_id=>"vol-f900f990"}],
|
71
|
+
# :aws_instance_id=>"i-8ce84ae4"}
|
72
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Description::ELB < Description
|
2
|
+
def initialize(description)
|
3
|
+
description[:service] ||= :elb
|
4
|
+
@description = description
|
5
|
+
end
|
6
|
+
|
7
|
+
def region_zone
|
8
|
+
# take from AZs it supports, ELBs always have at least one AZ
|
9
|
+
availability_zones.first
|
10
|
+
end
|
11
|
+
|
12
|
+
def physical_zone
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
description[:load_balancer_name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def aws_id
|
21
|
+
description[:load_balancer_name]
|
22
|
+
end
|
23
|
+
|
24
|
+
def status
|
25
|
+
'available' if description[:load_balancer_name]
|
26
|
+
end
|
27
|
+
|
28
|
+
def enabled_zones
|
29
|
+
availability_zones.map {|z| z[-1,1]}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# example elb description
|
34
|
+
# {:availability_zones=>["us-east-1c"],
|
35
|
+
# :service => :elb, # this is set by maws
|
36
|
+
# :dns_name=>"foo-lb-110916812.us-east-1.elb.amazonaws.com",
|
37
|
+
# :created_time=>"2011-07-06T23:50:06.040Z",
|
38
|
+
# :health_check=>
|
39
|
+
# {:timeout=>5,
|
40
|
+
# :target=>"HTTP:80/",
|
41
|
+
# :interval=>30,
|
42
|
+
# :healthy_threshold=>6,
|
43
|
+
# :unhealthy_threshold=>2},
|
44
|
+
# :instances=>["i-0941bf68", "i-a182d6c0"],
|
45
|
+
# :load_balancer_name=>"foo-lb",
|
46
|
+
# :app_cookie_stickiness_policies=>[],
|
47
|
+
# :lb_cookie_stickiness_policies=>[],
|
48
|
+
# :listeners=>
|
49
|
+
# [{:instance_port=>"80",
|
50
|
+
# :protocol=>"HTTP",
|
51
|
+
# :policy_names=>[],
|
52
|
+
# :load_balancer_port=>"80"}]}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Description::RDS < Description
|
2
|
+
def initialize(description)
|
3
|
+
description[:service] ||= :rds
|
4
|
+
@description = description
|
5
|
+
end
|
6
|
+
|
7
|
+
def name
|
8
|
+
aws_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def region_zone
|
12
|
+
availability_zone
|
13
|
+
end
|
14
|
+
|
15
|
+
def logical_zone
|
16
|
+
# parse from name if not multi_az, otherwise nil
|
17
|
+
super unless multi_az
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# example rds description
|
23
|
+
# {:aws_id=>"foo-test-masterdb-1",
|
24
|
+
# :service => :rds, # this is set by maws
|
25
|
+
# :endpoint_port=>3306,
|
26
|
+
# :status=>"available",
|
27
|
+
# :multi_az=>true,
|
28
|
+
# :db_parameter_group=>{:status=>"in-sync", :name=>"default.mysql5.1"},
|
29
|
+
# :latest_restorable_time=>"2011-09-12T20:50:58.853Z",
|
30
|
+
# :master_username=>"root",
|
31
|
+
# :license_model=>"general-public-license",
|
32
|
+
# :engine=>"mysql",
|
33
|
+
# :pending_modified_values=>{},
|
34
|
+
# :db_security_groups=>[{:status=>"active", :name=>"default"}],
|
35
|
+
# :engine_version=>"5.1.57",
|
36
|
+
# :read_replica_db_instance_identifiers=>[],
|
37
|
+
# :availability_zone=>"us-east-1a",
|
38
|
+
# :backup_retention_period=>1,
|
39
|
+
# :create_time=>"2011-09-12T20:47:08.521Z",
|
40
|
+
# :auto_minor_version_upgrade=>true,
|
41
|
+
# :preferred_backup_window=>"07:30-08:00",
|
42
|
+
# :allocated_storage=>6,
|
43
|
+
# :instance_class=>"db.m1.small",
|
44
|
+
# :preferred_maintenance_window=>"mon:10:00-mon:10:30",
|
45
|
+
# :db_name=>"foo",
|
46
|
+
# :endpoint_address=>
|
47
|
+
# "foo-test-masterdb-1.ck6iyjop7iqg.us-east-1.rds.amazonaws.com"}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class Description
|
2
|
+
attr_reader :description
|
3
|
+
|
4
|
+
def self.create(description)
|
5
|
+
service = description[:service]
|
6
|
+
klass = Description.const_get("#{service.to_s.upcase}")
|
7
|
+
klass.new(description)
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
description[key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method_name, *args, &block)
|
15
|
+
description[method_name.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(description)
|
19
|
+
raise "use #{self.class}::create instead"
|
20
|
+
end
|
21
|
+
|
22
|
+
# logical zone (what the zone is according to maws categorization)
|
23
|
+
def zone
|
24
|
+
# us-east-1a => a
|
25
|
+
logical_zone
|
26
|
+
end
|
27
|
+
|
28
|
+
# zone where the instance really lives
|
29
|
+
# this has meaning for RDS with multi-zone = true and for EC2 with region scope
|
30
|
+
def physical_zone
|
31
|
+
region_zone[-1,1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def region
|
35
|
+
# us-east-1a => us-east-1
|
36
|
+
region_zone[0...-1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def name_re
|
40
|
+
/^(.+\.)?(.+)-(.+)-(\d+)(\w)?$/
|
41
|
+
end
|
42
|
+
|
43
|
+
def prefix
|
44
|
+
# old.foo-test-app-1z => old
|
45
|
+
md = name.match(name_re)
|
46
|
+
md[1].to_s[0...-1] if md
|
47
|
+
end
|
48
|
+
|
49
|
+
def profile
|
50
|
+
# foo-test-app-1z => foo-test
|
51
|
+
md = name.match(name_re)
|
52
|
+
md[2] if md
|
53
|
+
end
|
54
|
+
|
55
|
+
def role
|
56
|
+
md = name.match(name_re)
|
57
|
+
md[3] if md
|
58
|
+
end
|
59
|
+
|
60
|
+
def index
|
61
|
+
md = name.match(name_re)
|
62
|
+
md[4].to_i if md
|
63
|
+
end
|
64
|
+
|
65
|
+
def logical_zone
|
66
|
+
md = name.match(name_re)
|
67
|
+
md[5] if md
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_instance(maws, config)
|
71
|
+
instance = Instance.create(maws, config, prefix, zone, role, index, {:service => service, :name => name, :region => region })
|
72
|
+
instance.description = self
|
73
|
+
instance
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# load all description files
|
78
|
+
Dir.glob(File.dirname(__FILE__) + '/description/*.rb') {|file| require file}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'maws/instance'
|
2
|
+
|
3
|
+
# for EC2 instances aws_id is a random id
|
4
|
+
# name is a value of 'Name' tag
|
5
|
+
class Instance::EBS < Instance
|
6
|
+
|
7
|
+
def destroy
|
8
|
+
connection.ec2.delete_volume(aws_id)
|
9
|
+
info "destroying EBS volume #{name}:#{device} (#{aws_id})"
|
10
|
+
end
|
11
|
+
|
12
|
+
def attached?
|
13
|
+
aws_attachment_status == 'attached'
|
14
|
+
end
|
15
|
+
|
16
|
+
def attached_to_instance_id
|
17
|
+
return "" unless attached_instance_name
|
18
|
+
device[:aws_instance_id]
|
19
|
+
end
|
20
|
+
|
21
|
+
def display_fields
|
22
|
+
if attached?
|
23
|
+
super + [:device, :aws_id, :attachment_status, :aws_instance_id]
|
24
|
+
else
|
25
|
+
super + [:aws_id]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def service
|
30
|
+
:ebs
|
31
|
+
end
|
32
|
+
|
33
|
+
def physical_zone
|
34
|
+
super || @config.available_zones.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def device
|
38
|
+
description.aws_device
|
39
|
+
end
|
40
|
+
|
41
|
+
def attachment_status
|
42
|
+
description.aws_attachment_status
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'maws/instance'
|
2
|
+
require 'maws/ssh'
|
3
|
+
|
4
|
+
# for EC2 instances aws_id is a random id
|
5
|
+
# name is a value of 'Name' tag
|
6
|
+
class Instance::EC2 < Instance
|
7
|
+
def create
|
8
|
+
return if alive?
|
9
|
+
info "creating EC2 #{name}..."
|
10
|
+
image_id = config(:image_id) || connection.image_id_for_image_name(config(:image_name))
|
11
|
+
if image_id.nil?
|
12
|
+
info "no AMI id found with name '#{config(:image_name)}'"
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
results = connection.ec2.launch_instances(image_id,
|
17
|
+
:availability_zone => region_physical_zone,
|
18
|
+
:key_name => config(:keypair),
|
19
|
+
:min_count => 1,
|
20
|
+
:max_count => 1,
|
21
|
+
:group_names => security_groups,
|
22
|
+
:user_data => config(:user_data),
|
23
|
+
:monitoring_enabled => config(:monitoring_enabled),
|
24
|
+
:instance_type => config(:instance_type))
|
25
|
+
|
26
|
+
|
27
|
+
self.description = Description::EC2.new(results.first)
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_prefix(prefix)
|
31
|
+
@prefix = prefix
|
32
|
+
old_name = @name
|
33
|
+
@name = self.class.name_for(@config, @prefix, @zone, @role, @index)
|
34
|
+
|
35
|
+
info "renaming #{old_name} to #{@name}"
|
36
|
+
create_tags
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_tags
|
40
|
+
tag_instance_name
|
41
|
+
tag_volumes_names
|
42
|
+
|
43
|
+
info "...done (#{name} or #{aws_id} is ready)"
|
44
|
+
end
|
45
|
+
|
46
|
+
def tag_instance_name
|
47
|
+
retries_left = 20
|
48
|
+
loop do
|
49
|
+
begin
|
50
|
+
connection.ec2.create_tags(aws_id, {'Name' => name})
|
51
|
+
info "tagged EC2 instance #{aws_id} as #{name}"
|
52
|
+
return
|
53
|
+
rescue RightAws::AwsError => error
|
54
|
+
if error.message =~ /^InvalidInstanceID.NotFound/
|
55
|
+
info "TAGGING FAILED. RETRYING..."
|
56
|
+
else
|
57
|
+
raise error
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
retries_left -= 1
|
62
|
+
retries_left > 0 ? sleep(1) : break
|
63
|
+
end
|
64
|
+
error "Couldn't not tag #{aws_id} with name #{name}. It might not exist on AWS"
|
65
|
+
end
|
66
|
+
|
67
|
+
def tag_volumes_names
|
68
|
+
# wait a while before volume_ids are made available on the aws ec2 description
|
69
|
+
retries_left = 30
|
70
|
+
loop do
|
71
|
+
if volume_ids.count > 0
|
72
|
+
# tag and return
|
73
|
+
volume_ids.each {|vid|
|
74
|
+
connection.ec2.create_tags(vid, {'Name' => name})
|
75
|
+
info "tagged EBS volume #{vid} as #{name}"
|
76
|
+
}
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
# resync all descriptions (volume ids should appear if missing)
|
81
|
+
@maws.resync_instances
|
82
|
+
|
83
|
+
retries_left -= 1
|
84
|
+
retries_left > 0 ? sleep(1) : break
|
85
|
+
end
|
86
|
+
error "No volumes found for #{name} (#{aws_id})"
|
87
|
+
end
|
88
|
+
|
89
|
+
def destroy
|
90
|
+
return unless alive?
|
91
|
+
connection.ec2.terminate_instances(aws_id)
|
92
|
+
info "destroying EC2 #{name} (#{aws_id})"
|
93
|
+
end
|
94
|
+
|
95
|
+
def stop
|
96
|
+
return unless alive?
|
97
|
+
if status == 'running'
|
98
|
+
connection.ec2.stop_instances(aws_id)
|
99
|
+
info "stopping EC2 #{name} (#{aws_id})"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def start
|
104
|
+
return unless alive?
|
105
|
+
if status == 'stopped'
|
106
|
+
connection.ec2.start_instances(aws_id)
|
107
|
+
info "starting EC2 #{name} (#{aws_id})"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def ssh_available?
|
112
|
+
return false unless alive? && self.dns_name && !self.dns_name.empty?
|
113
|
+
|
114
|
+
begin
|
115
|
+
3.times { # retry on host unreachable errors
|
116
|
+
begin
|
117
|
+
Net::SSH.start(dns_name, "phoneyuser", {:auth_methods => ["publickey"], :timeout => 1, :keys_only => true })
|
118
|
+
rescue Errno::EHOSTUNREACH
|
119
|
+
sleep 2
|
120
|
+
end
|
121
|
+
}
|
122
|
+
rescue Net::SSH::AuthenticationFailed
|
123
|
+
return true
|
124
|
+
rescue Object
|
125
|
+
return false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def volume_ids
|
130
|
+
description.ebs_volume_ids
|
131
|
+
end
|
132
|
+
|
133
|
+
def attached_volumes
|
134
|
+
description.attached_ebs_volume_ids
|
135
|
+
end
|
136
|
+
|
137
|
+
def service
|
138
|
+
:ec2
|
139
|
+
end
|
140
|
+
|
141
|
+
def display_fields
|
142
|
+
super + [:dns_name, :aws_instance_id, :aws_image_id]
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'maws/instance'
|
2
|
+
|
3
|
+
# for ELBs aws_id is the same as their names
|
4
|
+
class Instance::ELB < Instance
|
5
|
+
def create
|
6
|
+
return if alive?
|
7
|
+
info "creating ELB #{name}..."
|
8
|
+
|
9
|
+
listeners = config(:listeners).dup || []
|
10
|
+
|
11
|
+
server = connection.elb.create_load_balancer(name, @connection.availability_zones, listeners)
|
12
|
+
connection.elb.configure_health_check(name, config(:health_check))
|
13
|
+
|
14
|
+
description = server ? {:load_balancer_name => name} : {}
|
15
|
+
sync_from_description(description)
|
16
|
+
|
17
|
+
info "...done (ELB #{name} is ready)\n\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def destroy
|
21
|
+
return unless alive?
|
22
|
+
connection.elb.delete_load_balancer(aws_id)
|
23
|
+
info "destroying ELB #{name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_instances(instances)
|
27
|
+
names = instances.map{|i| i.name}
|
28
|
+
info "adding instances to ELB #{aws_id}: #{names.join(', ')}"
|
29
|
+
connection.elb.register_instances_with_load_balancer(aws_id, instances.map{|i| i.aws_id})
|
30
|
+
info "...done"
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_instances(instances)
|
34
|
+
names = instances.map{|i| i.name}
|
35
|
+
info "removing instances to ELB #{aws_id}: #{names.join(', ')}"
|
36
|
+
connection.elb.deregister_instances_with_load_balancer(aws_id, instances.map{|i| i.aws_id})
|
37
|
+
info "...done"
|
38
|
+
end
|
39
|
+
|
40
|
+
def attached_instances
|
41
|
+
instance_ids = @aws_description[:instances]
|
42
|
+
@profile.defined_instances.select {|i| instance_ids.include?(i.aws_id)}
|
43
|
+
end
|
44
|
+
|
45
|
+
def enable_zones(zones)
|
46
|
+
full_zones = zones.map {|z| command_options.region + z}
|
47
|
+
info "enabling zones #{full_zones.join(', ')} for ELB #{aws_id}..."
|
48
|
+
connection.elb.enable_availability_zones_for_load_balancer(aws_id, full_zones)
|
49
|
+
info "...done"
|
50
|
+
end
|
51
|
+
|
52
|
+
def disable_zones(zones)
|
53
|
+
full_zones = zones.map {|z| command_options.region + z}
|
54
|
+
info "disabling zones #{full_zones.join(', ')} for ELB #{aws_id}"
|
55
|
+
|
56
|
+
if enabled_availability_zones.size <= 1
|
57
|
+
info "can't remove last remaining zone: #{enabled_availability_zones.first}"
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
connection.elb.disable_availability_zones_for_load_balancer(aws_id, full_zones)
|
62
|
+
info "...done"
|
63
|
+
end
|
64
|
+
|
65
|
+
def service
|
66
|
+
:elb
|
67
|
+
end
|
68
|
+
|
69
|
+
def physical_zone
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def enabled_availability_zones
|
74
|
+
description.availability_zones
|
75
|
+
end
|
76
|
+
|
77
|
+
def zones_list
|
78
|
+
(description.enabled_zones || []).join(', ')
|
79
|
+
end
|
80
|
+
|
81
|
+
def display_fields
|
82
|
+
[:name, :status, :zones_list, :first_listener_info]
|
83
|
+
end
|
84
|
+
|
85
|
+
def first_listener_info
|
86
|
+
return "" unless alive?
|
87
|
+
(description.listeners || [{}]).first.to_hash.collect do |key, val|
|
88
|
+
"#{key}=#{val}"
|
89
|
+
end.join("; ")
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|