bguthrie-awsymandias 0.2.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.specification +57 -0
- data/README.rdoc +25 -21
- data/Rakefile +20 -4
- data/VERSION +1 -1
- data/awsymandias.gemspec +37 -12
- data/lib/awsymandias.rb +36 -331
- data/lib/awsymandias/addons/right_elb_interface.rb +375 -0
- data/lib/awsymandias/ec2.rb +49 -0
- data/lib/awsymandias/ec2/application_stack.rb +261 -0
- data/lib/awsymandias/extensions/class_extension.rb +18 -0
- data/lib/awsymandias/extensions/net_http_extension.rb +9 -0
- data/lib/awsymandias/instance.rb +149 -0
- data/lib/awsymandias/load_balancer.rb +157 -0
- data/lib/awsymandias/right_aws.rb +73 -0
- data/lib/awsymandias/right_elb.rb +16 -0
- data/lib/awsymandias/simple_db.rb +46 -0
- data/lib/awsymandias/snapshot.rb +33 -0
- data/lib/awsymandias/stack_definition.rb +60 -0
- data/lib/awsymandias/volume.rb +70 -0
- data/spec/integration/instance_spec.rb +37 -0
- data/spec/unit/addons/right_elb_interface_spec.rb +45 -0
- data/spec/unit/awsymandias_spec.rb +61 -0
- data/spec/unit/ec2/application_stack_spec.rb +634 -0
- data/spec/unit/instance_spec.rb +365 -0
- data/spec/unit/load_balancer_spec.rb +250 -0
- data/spec/unit/right_aws_spec.rb +90 -0
- data/spec/unit/simple_db_spec.rb +63 -0
- data/spec/unit/snapshot_spec.rb +39 -0
- data/spec/unit/stack_definition_spec.rb +113 -0
- data/tags +368 -0
- metadata +39 -13
- data/spec/awsymandias_spec.rb +0 -815
- data/vendor/aws-sdb/LICENSE +0 -19
- data/vendor/aws-sdb/README +0 -1
- data/vendor/aws-sdb/Rakefile +0 -20
- data/vendor/aws-sdb/lib/aws_sdb.rb +0 -3
- data/vendor/aws-sdb/lib/aws_sdb/error.rb +0 -42
- data/vendor/aws-sdb/lib/aws_sdb/service.rb +0 -191
- data/vendor/aws-sdb/spec/aws_sdb/service_spec.rb +0 -183
- data/vendor/aws-sdb/spec/spec_helper.rb +0 -4
data/.specification
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: onyx-awsymandias
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors: []
|
7
|
+
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-14 04:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email:
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib
|
26
|
+
- lib/awsymandias.rb
|
27
|
+
has_rdoc: false
|
28
|
+
homepage:
|
29
|
+
licenses: []
|
30
|
+
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
|
34
|
+
require_paths:
|
35
|
+
- bin
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: "0"
|
42
|
+
version:
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
requirements: []
|
50
|
+
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.3.2
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary:
|
56
|
+
test_files: []
|
57
|
+
|
data/README.rdoc
CHANGED
@@ -4,34 +4,18 @@
|
|
4
4
|
|
5
5
|
Awsymandias is a library that helps you set up and tear down complicated AWS environments. In addition to offering a clean, fluent domain model for working with instances and groups of instances it allows you to persist role-to-instance-id mappings to SimpleDB, allowing you to manage your stack from multiple different machines. Eventually it will allow you to add arbitrary instance metadata and help you track instance running costs over time.
|
6
6
|
|
7
|
-
I met a hacker from an antique land
|
8
|
-
Who said: Two tall and heavy mounts of steel
|
9
|
-
Lie in a basement. Near them on a stand,
|
10
|
-
Recessed, a dark CRT lies, whose peel’d
|
11
|
-
Cracked shell of dullest beige, and blinkenlights,
|
12
|
-
Tell that its fact’ry well those old specs read
|
13
|
-
Which yet survive, inked on the lifeless thing,
|
14
|
-
The die that stamp’d them and the power that fed.
|
15
|
-
And on the burned-in screen these words appear:
|
16
|
-
“My name is Awsymandias, king of kings:
|
17
|
-
Look on my racks, ye Mighty, and despair!”
|
18
|
-
No bits at all remain. Not far away
|
19
|
-
A data center waits, its humming air
|
20
|
-
Host to a boundless cloud by th’hour to pay.
|
21
|
-
|
22
|
-
(Apologies to Shelley’s original sonnet. Die-hards will please note that it scans and attempts to follow the original meter and rhyme scheme wherever possible.)
|
23
|
-
|
24
7
|
== Example
|
25
8
|
|
26
9
|
# Give the stack a name, and describe its members.
|
27
|
-
stack = Awsymandias::EC2::ApplicationStack.
|
28
|
-
s.
|
29
|
-
s.
|
10
|
+
stack = Awsymandias::EC2::ApplicationStack.define("test") do |s|
|
11
|
+
s.instance :db, :instance_type => "m1.large", ...
|
12
|
+
s.instance :app, :instance_type => "c1.xlarge", ...
|
13
|
+
s.volume :data, :volume_id => "vol-12345", :instance => :db, :unix_device => "/dev/sdj"
|
30
14
|
end
|
31
15
|
|
32
16
|
# Check if we're running by pulling stack description from SDB; if not, launch asynchronously.
|
33
17
|
stack.launch unless stack.launched? || stack.running?
|
34
|
-
until stack.running?
|
18
|
+
until stack.reload.running?
|
35
19
|
sleep(5)
|
36
20
|
end
|
37
21
|
|
@@ -48,6 +32,26 @@ This should allow you to re-launch and deploy that AWS stack from any one of sev
|
|
48
32
|
* {Paul Gross}[http://www.pgrs.net] {github}[http://github.com/pgr0ss] {email}[mailto:pgross@gmail.com]
|
49
33
|
* Rob Sweet (rob@ldg.net)
|
50
34
|
* Kris Hicks (krishicks at gmail dot com)
|
35
|
+
* {Tony Pitluga}[http://tonypitluga.blogspot.com/] {github}[http://github.com/pitluga] {email}[mailto:tony.pitluga@gmail.com]
|
36
|
+
|
37
|
+
== Brief Expository Sonnet
|
38
|
+
|
39
|
+
I met a hacker from an antique land
|
40
|
+
Who said: Two tall and heavy mounts of steel
|
41
|
+
Lie in a basement. Near them on a stand,
|
42
|
+
Recessed, a dark CRT lies, whose peel’d
|
43
|
+
Cracked shell of dullest beige, and blinkenlights,
|
44
|
+
Tell that its fact’ry well those old specs read
|
45
|
+
Which yet survive, inked on the lifeless thing,
|
46
|
+
The die that stamp’d them and the power that fed.
|
47
|
+
And on the burned-in screen these words appear:
|
48
|
+
“My name is Awsymandias, king of kings:
|
49
|
+
Look on my racks, ye Mighty, and despair!”
|
50
|
+
No bits at all remain. Not far away
|
51
|
+
A data center waits, its humming air
|
52
|
+
Host to a boundless cloud by th’hour to pay.
|
53
|
+
|
54
|
+
(Apologies to Shelley’s original. Diehards will please note that it scans and attempts to follow the original meter and rhyme scheme wherever possible.)
|
51
55
|
|
52
56
|
== License
|
53
57
|
|
data/Rakefile
CHANGED
@@ -3,12 +3,28 @@ require 'spec/rake/spectask'
|
|
3
3
|
require 'rake/gempackagetask'
|
4
4
|
require 'jeweler'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
|
7
|
+
namespace :spec do
|
8
|
+
|
9
|
+
desc "runs all the unit specs"
|
10
|
+
Spec::Rake::SpecTask.new(:unit) do |t|
|
11
|
+
t.rcov = true
|
12
|
+
t.rcov_opts = ["--text-summary", "--include-file lib/awsymandias", "--exclude gems,spec"]
|
13
|
+
t.spec_files = FileList['spec/unit/**/*_spec.rb']
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "runs all the integration specs (requires AMAZON_ACCESS_KEY_ID and AMAZON_SECRET_ACCESS_KEY env variables to be set)"
|
17
|
+
Spec::Rake::SpecTask.new(:integration) do |t|
|
18
|
+
t.rcov = true
|
19
|
+
t.rcov_opts = ["--text-summary", "--include-file lib/awsymandias", "--exclude gems,spec"]
|
20
|
+
t.spec_files = FileList['spec/integration/**/*_spec.rb']
|
21
|
+
end
|
22
|
+
|
10
23
|
end
|
11
24
|
|
25
|
+
desc "runs all the specs"
|
26
|
+
task :spec => [:'spec:unit', :'spec:integration']
|
27
|
+
|
12
28
|
task :default => [:spec]
|
13
29
|
|
14
30
|
desc "Open an irb session preloaded with this library"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.1
|
data/awsymandias.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{awsymandias}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.3.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Brian Guthrie"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-08-11}
|
10
10
|
s.description = %q{A library for helping you set up, track, and tear down complicated deployment configurations in Amazon EC2.}
|
11
11
|
s.email = %q{btguthrie@gmail.com}
|
12
12
|
s.extra_rdoc_files = [
|
@@ -14,20 +14,36 @@ Gem::Specification.new do |s|
|
|
14
14
|
]
|
15
15
|
s.files = [
|
16
16
|
".gitignore",
|
17
|
+
".specification",
|
17
18
|
"README.rdoc",
|
18
19
|
"Rakefile",
|
19
20
|
"VERSION",
|
20
21
|
"awsymandias.gemspec",
|
21
22
|
"lib/awsymandias.rb",
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
25
|
-
"
|
26
|
-
"
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"
|
23
|
+
"lib/awsymandias/addons/right_elb_interface.rb",
|
24
|
+
"lib/awsymandias/ec2.rb",
|
25
|
+
"lib/awsymandias/ec2/application_stack.rb",
|
26
|
+
"lib/awsymandias/extensions/class_extension.rb",
|
27
|
+
"lib/awsymandias/extensions/net_http_extension.rb",
|
28
|
+
"lib/awsymandias/instance.rb",
|
29
|
+
"lib/awsymandias/load_balancer.rb",
|
30
|
+
"lib/awsymandias/right_aws.rb",
|
31
|
+
"lib/awsymandias/right_elb.rb",
|
32
|
+
"lib/awsymandias/simple_db.rb",
|
33
|
+
"lib/awsymandias/snapshot.rb",
|
34
|
+
"lib/awsymandias/stack_definition.rb",
|
35
|
+
"lib/awsymandias/volume.rb",
|
36
|
+
"spec/integration/instance_spec.rb",
|
37
|
+
"spec/unit/addons/right_elb_interface_spec.rb",
|
38
|
+
"spec/unit/awsymandias_spec.rb",
|
39
|
+
"spec/unit/ec2/application_stack_spec.rb",
|
40
|
+
"spec/unit/instance_spec.rb",
|
41
|
+
"spec/unit/load_balancer_spec.rb",
|
42
|
+
"spec/unit/right_aws_spec.rb",
|
43
|
+
"spec/unit/simple_db_spec.rb",
|
44
|
+
"spec/unit/snapshot_spec.rb",
|
45
|
+
"spec/unit/stack_definition_spec.rb",
|
46
|
+
"tags"
|
31
47
|
]
|
32
48
|
s.has_rdoc = true
|
33
49
|
s.homepage = %q{http://github.com/bguthrie/awsymandias}
|
@@ -36,7 +52,16 @@ Gem::Specification.new do |s|
|
|
36
52
|
s.rubygems_version = %q{1.3.1}
|
37
53
|
s.summary = %q{A library for helping you set up, track, and tear down complicated deployment configurations in Amazon EC2.}
|
38
54
|
s.test_files = [
|
39
|
-
"spec/
|
55
|
+
"spec/integration/instance_spec.rb",
|
56
|
+
"spec/unit/addons/right_elb_interface_spec.rb",
|
57
|
+
"spec/unit/awsymandias_spec.rb",
|
58
|
+
"spec/unit/ec2/application_stack_spec.rb",
|
59
|
+
"spec/unit/instance_spec.rb",
|
60
|
+
"spec/unit/load_balancer_spec.rb",
|
61
|
+
"spec/unit/right_aws_spec.rb",
|
62
|
+
"spec/unit/simple_db_spec.rb",
|
63
|
+
"spec/unit/snapshot_spec.rb",
|
64
|
+
"spec/unit/stack_definition_spec.rb"
|
40
65
|
]
|
41
66
|
|
42
67
|
if s.respond_to? :specification_version then
|
data/lib/awsymandias.rb
CHANGED
@@ -1,352 +1,57 @@
|
|
1
|
-
|
1
|
+
unless defined?(Awsymandias)
|
2
|
+
Dir[File.dirname(__FILE__) + "/../vendor/**/lib"].each { |dir| $: << dir }
|
3
|
+
$: << File.dirname(__FILE__)
|
4
|
+
|
5
|
+
require 'right_aws'
|
6
|
+
require "sdb/right_sdb_interface"
|
7
|
+
require 'money'
|
8
|
+
require 'activesupport'
|
9
|
+
require 'activeresource'
|
10
|
+
require 'net/telnet'
|
2
11
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require 'money'
|
6
|
-
require 'activesupport'
|
7
|
-
require 'activeresource'
|
8
|
-
require 'net/telnet'
|
12
|
+
Dir[File.dirname(__FILE__) + "/awsymandias/extensions/**/*.rb"].each { |file| require file }
|
13
|
+
Dir[File.dirname(__FILE__) + "/awsymandias/**/*.rb"].each { |file| require file }
|
9
14
|
|
10
|
-
module Awsymandias
|
11
|
-
class << self
|
12
|
-
attr_writer :access_key_id, :secret_access_key
|
13
|
-
|
14
|
-
def access_key_id
|
15
|
-
@access_key_id || AMAZON_ACCESS_KEY_ID || ENV['AMAZON_ACCESS_KEY_ID']
|
16
|
-
end
|
17
|
-
|
18
|
-
def secret_access_key
|
19
|
-
@secret_access_key || AMAZON_SECRET_ACCESS_KEY || ENV['AMAZON_SECRET_ACCESS_KEY']
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module Support
|
24
|
-
module Hash
|
25
|
-
# Ganked from ActiveResource 2.3.2.
|
26
|
-
def reformat_incoming_param_data(params)
|
27
|
-
case params.class.to_s
|
28
|
-
when "Hash"
|
29
|
-
params.inject({}) do |h,(k,v)|
|
30
|
-
h[k.to_s.underscore.tr("-", "_")] = reformat_incoming_param_data(v)
|
31
|
-
h
|
32
|
-
end
|
33
|
-
when "Array"
|
34
|
-
params.map { |v| reformat_incoming_param_data(v) }
|
35
|
-
else
|
36
|
-
params
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
15
|
+
module Awsymandias
|
41
16
|
|
42
|
-
module EC2
|
43
17
|
class << self
|
44
|
-
|
45
|
-
|
46
|
-
def connection
|
47
|
-
@connection ||= ::EC2::Base.new(
|
48
|
-
:access_key_id => Awsymandias.access_key_id || ENV['AMAZON_ACCESS_KEY_ID'],
|
49
|
-
:secret_access_key => Awsymandias.secret_access_key || ENV['AMAZON_SECRET_ACCESS_KEY']
|
50
|
-
)
|
51
|
-
end
|
52
|
-
|
53
|
-
def instance_types
|
54
|
-
[
|
55
|
-
Awsymandias::EC2::InstanceTypes::M1_SMALL,
|
56
|
-
Awsymandias::EC2::InstanceTypes::M1_LARGE,
|
57
|
-
Awsymandias::EC2::InstanceTypes::M1_XLARGE,
|
58
|
-
Awsymandias::EC2::InstanceTypes::C1_MEDIUM,
|
59
|
-
Awsymandias::EC2::InstanceTypes::C1_XLARGE
|
60
|
-
].index_by(&:name)
|
61
|
-
end
|
62
|
-
end
|
18
|
+
attr_writer :access_key_id, :secret_access_key
|
19
|
+
attr_accessor :verbose
|
63
20
|
|
64
|
-
|
21
|
+
Awsymandias.verbose = false
|
65
22
|
|
66
|
-
|
67
|
-
|
68
|
-
module InstanceTypes
|
69
|
-
M1_SMALL = InstanceType.new("m1.small", Money.new(10))
|
70
|
-
M1_LARGE = InstanceType.new("m1.large", Money.new(40))
|
71
|
-
M1_XLARGE = InstanceType.new("m1.xlarge", Money.new(80))
|
72
|
-
|
73
|
-
C1_MEDIUM = InstanceType.new("c1.medium", Money.new(20))
|
74
|
-
C1_XLARGE = InstanceType.new("c1.xlarge", Money.new(80))
|
75
|
-
end
|
76
|
-
|
77
|
-
# All currently availability zones.
|
78
|
-
# TODO Generate dynamically.
|
79
|
-
module AvailabilityZones
|
80
|
-
US_EAST_1A = "us_east_1a"
|
81
|
-
US_EAST_1B = "us_east_1b"
|
82
|
-
US_EAST_1C = "us_east_1c"
|
83
|
-
|
84
|
-
EU_WEST_1A = "eu_west_1a"
|
85
|
-
EU_WEST_1B = "eu_west_1b"
|
86
|
-
end
|
87
|
-
|
88
|
-
# An instance represents an AWS instance as derived from a call to EC2's describe-instances methods.
|
89
|
-
# It wraps the simple hash structures returned by the EC2 gem with a domain model.
|
90
|
-
# It inherits from ARes::B in order to provide simple XML <-> domain model mapping.
|
91
|
-
class Instance < ActiveResource::Base
|
92
|
-
include Awsymandias::Support::Hash
|
93
|
-
extend Awsymandias::Support::Hash # reformat_incoming_param_data
|
94
|
-
|
95
|
-
self.site = "mu"
|
96
|
-
|
97
|
-
def id; instance_id; end
|
98
|
-
def public_dns; dns_name; end
|
99
|
-
def private_dns; private_dns_name; end
|
100
|
-
|
101
|
-
def pending?
|
102
|
-
instance_state.name == "pending"
|
23
|
+
def access_key_id
|
24
|
+
@access_key_id || AMAZON_ACCESS_KEY_ID || ENV['AMAZON_ACCESS_KEY_ID']
|
103
25
|
end
|
104
26
|
|
105
|
-
def
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
def port_open?(port)
|
110
|
-
Net::Telnet.new("Host" => public_dns, "Port" => port) && true rescue false
|
111
|
-
end
|
112
|
-
|
113
|
-
def terminated?
|
114
|
-
instance_state.name == "terminated"
|
27
|
+
def secret_access_key
|
28
|
+
@secret_access_key || AMAZON_SECRET_ACCESS_KEY || ENV['AMAZON_SECRET_ACCESS_KEY']
|
115
29
|
end
|
116
30
|
|
117
|
-
def
|
118
|
-
Awsymandias::
|
119
|
-
reload
|
31
|
+
def stack_names
|
32
|
+
Awsymandias::SimpleDB.query('application-stack', "").flatten.select { |stack_name| !stack_name.blank? }
|
120
33
|
end
|
121
34
|
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
))
|
128
|
-
end
|
129
|
-
|
130
|
-
def to_params
|
131
|
-
{
|
132
|
-
:image_id => self.image_id,
|
133
|
-
:key_name => self.key_name,
|
134
|
-
:instance_type => self.instance_type,
|
135
|
-
:availability_zone => self.placement.availability_zone
|
136
|
-
}
|
137
|
-
end
|
138
|
-
|
139
|
-
def instance_type
|
140
|
-
Awsymandias::EC2.instance_types[@attributes['instance_type']]
|
141
|
-
end
|
142
|
-
|
143
|
-
def launch_time
|
144
|
-
Time.parse(@attributes['launch_time'])
|
145
|
-
end
|
146
|
-
|
147
|
-
def uptime
|
148
|
-
return 0.seconds if pending?
|
149
|
-
Time.now - self.launch_time
|
150
|
-
end
|
151
|
-
|
152
|
-
def running_cost
|
153
|
-
return Money.new(0) if pending?
|
154
|
-
instance_type.price_per_hour * (uptime / 1.hour).ceil
|
155
|
-
end
|
156
|
-
|
157
|
-
class << self
|
158
|
-
def find(*args)
|
159
|
-
opts = args.extract_options!
|
160
|
-
what = args.first
|
161
|
-
|
162
|
-
if what == :all
|
163
|
-
find_all(opts[:instance_ids], opts)
|
164
|
-
else
|
165
|
-
find_one(what, opts)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def find_all(ids, opts={})
|
170
|
-
reservation_set = EC2.connection.describe_instances(:instance_id => ids)["reservationSet"]
|
171
|
-
if reservation_set.nil?
|
172
|
-
[]
|
173
|
-
else
|
174
|
-
reservation_set["item"].sum([]) do |item_set|
|
175
|
-
item_set["instancesSet"]["item"].map do |item|
|
176
|
-
instantiate_record(reformat_incoming_param_data(item))
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
35
|
+
def wait_for(message, refresh_seconds, &block)
|
36
|
+
print "Waiting for #{message}.." if Awsymandias.verbose
|
37
|
+
while !block.call
|
38
|
+
print "." if Awsymandias.verbose
|
39
|
+
sleep(refresh_seconds)
|
180
40
|
end
|
181
|
-
|
182
|
-
def find_one(id, opts={})
|
183
|
-
reservation_set = EC2.connection.describe_instances(:instance_id => [ id ])["reservationSet"]
|
184
|
-
if reservation_set.nil?
|
185
|
-
raise ActiveResource::ResourceNotFound, "not found: #{id}"
|
186
|
-
else
|
187
|
-
reservation_set["item"].first["instancesSet"]["item"].map do |item|
|
188
|
-
instantiate_record(reformat_incoming_param_data(item))
|
189
|
-
end.first
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def launch(opts={})
|
194
|
-
opts.assert_valid_keys! :image_id, :key_name, :instance_type, :availability_zone, :user_data
|
195
|
-
|
196
|
-
opts[:instance_type] = opts[:instance_type].name if opts[:instance_type].is_a?(Awsymandias::EC2::InstanceType)
|
197
|
-
|
198
|
-
response = Awsymandias::EC2.connection.run_instances opts
|
199
|
-
instance_id = response["instancesSet"]["item"].map {|h| h["instanceId"]}.first
|
200
|
-
find(instance_id)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
# Goal:
|
206
|
-
# stack = EC2::ApplicationStack.new do |stack|
|
207
|
-
# stack.role "db", :instance_type => EC2::InstanceTypes::C1_XLARGE, :image_id => "ami-3576915c"
|
208
|
-
# stack.role "app1", "app2", "app3", :instance_type => EC2::InstanceType::M1_XLARGE, :image_id => "ami-dc789fb5"
|
209
|
-
# stack.role "memcache", :instance_type => EC2::InstanceType::C1_LARGE, :image_id => "ami-dc789fb5"
|
210
|
-
# end
|
211
|
-
# stack.app1.running?
|
212
|
-
class ApplicationStack
|
213
|
-
attr_reader :name, :roles, :sdb_domain
|
214
|
-
|
215
|
-
DEFAULT_SDB_DOMAIN = "application-stack"
|
216
|
-
|
217
|
-
class << self
|
218
|
-
def find(name)
|
219
|
-
returning(new(name)) do |stack|
|
220
|
-
return nil unless stack.launched?
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
def launch(name, opts={})
|
225
|
-
returning(new(name, opts)) do |stack|
|
226
|
-
stack.launch
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
def initialize(name, opts={})
|
232
|
-
opts.assert_valid_keys! :roles
|
233
|
-
|
234
|
-
@name = name
|
235
|
-
@roles = opts[:roles] || {}
|
236
|
-
@sdb_domain = opts[:sdb_domain] || DEFAULT_SDB_DOMAIN
|
237
|
-
@instances = {}
|
238
|
-
yield self if block_given?
|
41
|
+
verbose_output "OK!"
|
239
42
|
end
|
240
43
|
|
241
|
-
def
|
242
|
-
|
243
|
-
names.each do |name|
|
244
|
-
@roles[name] = opts
|
245
|
-
self.metaclass.send(:define_method, name) { @instances[name] }
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
def launch
|
250
|
-
@roles.each do |name, params| # TODO Optimize this for a single remote call.
|
251
|
-
@instances[name] = Awsymandias::EC2::Instance.launch(params)
|
252
|
-
end
|
253
|
-
store_role_to_instance_id_mapping!
|
254
|
-
self
|
255
|
-
end
|
256
|
-
|
257
|
-
def reload
|
258
|
-
raise "Can't reload unless launched" unless launched?
|
259
|
-
@instances.values.each(&:reload) # TODO Optimize this for a single remote call.
|
260
|
-
self
|
261
|
-
end
|
262
|
-
|
263
|
-
def terminate!
|
264
|
-
@instances.values.each(&:terminate!) # TODO Optimize this for a single remote call.
|
265
|
-
remove_role_to_instance_id_mapping!
|
266
|
-
self
|
267
|
-
end
|
268
|
-
|
269
|
-
def launched?
|
270
|
-
@instances.any? || ( @instances = retrieve_role_to_instance_id_mapping ).any?
|
271
|
-
end
|
272
|
-
|
273
|
-
def running?
|
274
|
-
launched? && @instances.values.all?(&:running?)
|
275
|
-
end
|
276
|
-
|
277
|
-
def port_open?(port)
|
278
|
-
@instances.values.all? { |instance| instance.port_open?(port) }
|
279
|
-
end
|
280
|
-
|
281
|
-
def running_cost
|
282
|
-
return Money.new(0) unless launched?
|
283
|
-
@instances.values.sum { |instance| instance.running_cost }
|
284
|
-
end
|
285
|
-
|
286
|
-
def inspect
|
287
|
-
( [ "Environment #{@name}, running? #{running?}" ] + roles.map do |role_name, opts|
|
288
|
-
"** #{role_name}: #{opts.inspect}"
|
289
|
-
end ).join("\n")
|
44
|
+
def verbose_output(message)
|
45
|
+
puts message if Awsymandias.verbose
|
290
46
|
end
|
291
47
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
end )
|
48
|
+
def describe_stacks
|
49
|
+
Awsymandias.stack_names.each do |stack_name|
|
50
|
+
stack = EC2::ApplicationStack.find(stack_name)
|
51
|
+
puts stack.summarize if stack
|
52
|
+
puts ""
|
298
53
|
end
|
299
|
-
|
300
|
-
def remove_role_to_instance_id_mapping!
|
301
|
-
Awsymandias::SimpleDB.delete @sdb_domain, @name
|
302
|
-
end
|
303
|
-
|
304
|
-
def retrieve_role_to_instance_id_mapping
|
305
|
-
returning(Awsymandias::SimpleDB.get(@sdb_domain, @name)) do |mapping|
|
306
|
-
unless mapping.empty?
|
307
|
-
live_instances = Awsymandias::EC2::Instance.find(:all, :instance_ids => mapping.values.flatten).index_by(&:instance_id)
|
308
|
-
mapping.each do |role_name, instance_id|
|
309
|
-
mapping[role_name] = live_instances[instance_id.first]
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
54
|
+
end
|
315
55
|
end
|
316
56
|
end
|
317
|
-
|
318
|
-
# TODO Locate a nicer SimpleDB API and get out of the business of maintaining this one.
|
319
|
-
module SimpleDB # :nodoc
|
320
|
-
class << self
|
321
|
-
def connection(opts={})
|
322
|
-
@connection ||= ::AwsSdb::Service.new({
|
323
|
-
:access_key_id => Awsymandias.access_key_id || ENV['AMAZON_ACCESS_KEY_ID'],
|
324
|
-
:secret_access_key => Awsymandias.secret_access_key || ENV['AMAZON_SECRET_ACCESS_KEY'],
|
325
|
-
:logger => Logger.new("/dev/null")
|
326
|
-
}.merge(opts))
|
327
|
-
end
|
328
|
-
|
329
|
-
def put(domain, name, stuff)
|
330
|
-
connection.put_attributes handle_domain(domain), name, stuff
|
331
|
-
end
|
332
|
-
|
333
|
-
def get(domain, name)
|
334
|
-
connection.get_attributes(handle_domain(domain), name) || {}
|
335
|
-
end
|
336
|
-
|
337
|
-
def delete(domain, name)
|
338
|
-
connection.delete_attributes handle_domain(domain), name
|
339
|
-
end
|
340
|
-
|
341
|
-
private
|
342
|
-
|
343
|
-
def domain_exists?(domain)
|
344
|
-
Awsymandias::SimpleDB.connection.list_domains[0].include?(domain)
|
345
|
-
end
|
346
|
-
|
347
|
-
def handle_domain(domain)
|
348
|
-
returning(domain) { connection.create_domain(domain) unless domain_exists?(domain) }
|
349
|
-
end
|
350
|
-
end
|
351
|
-
end
|
352
57
|
end
|