awsborn 0.8.7 → 0.9.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/.gitignore +3 -0
- data/Gemfile +60 -0
- data/Gemfile.lock +40 -0
- data/README.mdown +27 -0
- data/VERSION +1 -1
- data/awsborn.gemspec +4 -2
- data/lib/awsborn/ec2.rb +5 -0
- data/lib/awsborn/server.rb +38 -6
- data/lib/awsborn/server_cluster.rb +2 -1
- data/spec/ec2_spec.rb +18 -0
- data/spec/server_spec.rb +77 -0
- metadata +7 -5
data/.gitignore
CHANGED
data/Gemfile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# A sample Gemfile
|
2
|
+
source "http://rubygems.org"
|
3
|
+
|
4
|
+
gem 'jeweler', '1.4.0'
|
5
|
+
gem 'icehouse-right_aws', '2.2.0'
|
6
|
+
#gem 'gemcutter', '0.6.1'
|
7
|
+
gem 'rake', '0.8.7'
|
8
|
+
gem 'rspec', '2.6.0'
|
9
|
+
gem 'webmock', '1.6.4'
|
10
|
+
|
11
|
+
# gem 'addressable', '2.2.6, 2.2.1)
|
12
|
+
# gem 'awsborn', '0.8.7, 0.8.6, 0.8.1, 0.8.0, 0.7.0, 0.6.0)
|
13
|
+
# gem 'bundler', '1.0.0)
|
14
|
+
# gem 'crack', '0.1.8)
|
15
|
+
# gem 'diff-lcs', '1.1.2)
|
16
|
+
# gem 'extlib', '0.9.15)
|
17
|
+
# gem 'git', '1.2.5)
|
18
|
+
# gem 'json', '1.4.6)
|
19
|
+
# gem 'json_pure', '1.5.2, 1.5.1, 1.4.6)
|
20
|
+
# gem 'launchy', '0.4.0)
|
21
|
+
# gem 'mixlib-cli', '1.2.0)
|
22
|
+
# gem 'mixlib-config', '1.1.2)
|
23
|
+
# gem 'mixlib-log', '1.2.0)
|
24
|
+
# gem 'ohai', '0.5.8)
|
25
|
+
# gem 'right_aws', '2.1.0)
|
26
|
+
# gem 'right_http_connection', '1.3.0, 1.2.4)
|
27
|
+
# gem 'rspec-core', '2.6.4)
|
28
|
+
# gem 'rspec-expectations', '2.6.0)
|
29
|
+
# gem 'rspec-mocks', '2.6.0)
|
30
|
+
# gem 'rubyforge', '2.0.4)
|
31
|
+
# gem 'systemu', '1.2.0)
|
32
|
+
|
33
|
+
|
34
|
+
# addressable (2.2.6, 2.2.1)
|
35
|
+
# awsborn (0.8.7, 0.8.6, 0.8.1, 0.8.0, 0.7.0, 0.6.0)
|
36
|
+
# bundler (1.0.0)
|
37
|
+
# crack (0.1.8)
|
38
|
+
# diff-lcs (1.1.2)
|
39
|
+
# extlib (0.9.15)
|
40
|
+
# gemcutter (0.6.1)
|
41
|
+
# git (1.2.5)
|
42
|
+
# icehouse-right_aws (2.2.0)
|
43
|
+
# jeweler (1.4.0)
|
44
|
+
# json (1.4.6)
|
45
|
+
# json_pure (1.5.2, 1.5.1, 1.4.6)
|
46
|
+
# launchy (0.4.0)
|
47
|
+
# mixlib-cli (1.2.0)
|
48
|
+
# mixlib-config (1.1.2)
|
49
|
+
# mixlib-log (1.2.0)
|
50
|
+
# ohai (0.5.8)
|
51
|
+
# rake (0.8.7)
|
52
|
+
# right_aws (2.1.0)
|
53
|
+
# right_http_connection (1.3.0, 1.2.4)
|
54
|
+
# rspec (2.6.0)
|
55
|
+
# rspec-core (2.6.4)
|
56
|
+
# rspec-expectations (2.6.0)
|
57
|
+
# rspec-mocks (2.6.0)
|
58
|
+
# rubyforge (2.0.4)
|
59
|
+
# systemu (1.2.0)
|
60
|
+
# webmock (1.6.4, 1.3.5)
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.2.6)
|
5
|
+
crack (0.3.1)
|
6
|
+
diff-lcs (1.1.3)
|
7
|
+
gemcutter (0.7.0)
|
8
|
+
git (1.2.5)
|
9
|
+
icehouse-right_aws (2.2.0)
|
10
|
+
right_http_connection (>= 1.2.5)
|
11
|
+
jeweler (1.4.0)
|
12
|
+
gemcutter (>= 0.1.0)
|
13
|
+
git (>= 1.2.5)
|
14
|
+
rubyforge (>= 2.0.0)
|
15
|
+
json_pure (1.6.1)
|
16
|
+
rake (0.8.7)
|
17
|
+
right_http_connection (1.3.0)
|
18
|
+
rspec (2.6.0)
|
19
|
+
rspec-core (~> 2.6.0)
|
20
|
+
rspec-expectations (~> 2.6.0)
|
21
|
+
rspec-mocks (~> 2.6.0)
|
22
|
+
rspec-core (2.6.4)
|
23
|
+
rspec-expectations (2.6.0)
|
24
|
+
diff-lcs (~> 1.1.2)
|
25
|
+
rspec-mocks (2.6.0)
|
26
|
+
rubyforge (2.0.4)
|
27
|
+
json_pure (>= 1.1.7)
|
28
|
+
webmock (1.6.4)
|
29
|
+
addressable (~> 2.2, > 2.2.5)
|
30
|
+
crack (>= 0.1.7)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
icehouse-right_aws (= 2.2.0)
|
37
|
+
jeweler (= 1.4.0)
|
38
|
+
rake (= 0.8.7)
|
39
|
+
rspec (= 2.6.0)
|
40
|
+
webmock (= 1.6.4)
|
data/README.mdown
CHANGED
@@ -184,6 +184,33 @@ which does the following:
|
|
184
184
|
The `key_pair` is a temporary key pair that is used only for launching this cluster.
|
185
185
|
In case of a failed launch, the private key is available as `/tmp/temp_key_*`.
|
186
186
|
|
187
|
+
### Supplying user-data
|
188
|
+
|
189
|
+
Instances can be supplied user-data at launch, which can be used to integrate
|
190
|
+
with e.g. cloud-init or similar. See http://docs.amazonwebservices.com/AWSEC2/2007-03-01/DeveloperGuide/AESDG-chapter-instancedata.html
|
191
|
+
|
192
|
+
In Awsborn, you can supply user-data by defining a user_data instance method on the
|
193
|
+
Server class which must return a string.
|
194
|
+
|
195
|
+
class LogServer < Awsborn::Server
|
196
|
+
instance_type :m1_small
|
197
|
+
image_id 'ami-2fc2e95b', :sudo_user => 'ubuntu'
|
198
|
+
security_group 'Basic web'
|
199
|
+
keys '../keys/*'
|
200
|
+
bootstrap_script 'chef-bootstrap.sh'
|
201
|
+
monitor true
|
202
|
+
|
203
|
+
cluster do
|
204
|
+
domain 'releware.net'
|
205
|
+
server :log_a, :zone => :eu_west_1a, :disk => {:sdf => "vol-a857b8c1"}, :ip => 'log-a'
|
206
|
+
server :log_b, :zone => :eu_west_1b, :disk => {:sdf => "vol-aa57b8c3"}, :ip => 'log-b'
|
207
|
+
end
|
208
|
+
|
209
|
+
def user_data
|
210
|
+
"hostname=#{name}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
187
214
|
## A bootstrapping script
|
188
215
|
|
189
216
|
This is what we use with the AMI above:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/awsborn.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{awsborn}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.9.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David Vrensk", "Jean-Louis Giordano"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-11-11}
|
13
13
|
s.description = %q{Awsborn lets you define and launch a server cluster on Amazon EC2.}
|
14
14
|
s.email = %q{david@icehouse.se}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -19,6 +19,8 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
21
|
".gitignore",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
22
24
|
"LICENSE",
|
23
25
|
"README.mdown",
|
24
26
|
"Rakefile",
|
data/lib/awsborn/ec2.rb
CHANGED
data/lib/awsborn/server.rb
CHANGED
@@ -59,14 +59,36 @@ module Awsborn
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
62
|
+
def find_instance_id_by_name
|
63
|
+
instances = ec2.connection.describe_instances(
|
64
|
+
:filters => {'tag:Name' => full_name,
|
65
|
+
'instance-state-name' => ['pending', 'running']}
|
66
|
+
)
|
67
|
+
if instances.count > 1
|
68
|
+
raise ServerError, "Found multiple instances with full_name = #{full_name}."
|
69
|
+
end
|
70
|
+
instances.empty? ? nil : instances.first[:aws_instance_id]
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_instance_id_by_volume
|
63
74
|
map = {}
|
64
75
|
disk_volume_ids.each { |vol_id| map[vol_id] = ec2.instance_id_for_volume(vol_id) }
|
65
76
|
ids = map.values.uniq
|
66
77
|
if ids.size > 1
|
67
78
|
raise ServerError, "Volumes for #{self.class.name}:#{name} are connected to several instances: #{map.inspect}"
|
68
79
|
end
|
69
|
-
|
80
|
+
ids.first
|
81
|
+
end
|
82
|
+
|
83
|
+
def running?
|
84
|
+
name_id = find_instance_id_by_name
|
85
|
+
volume_id = find_instance_id_by_volume
|
86
|
+
|
87
|
+
if name_id && volume_id && name_id != volume_id
|
88
|
+
raise ServerError, "Volumes #{disk_volume_ids} are attached to #{volume_id}, not to instance #{name_id} with name #{full_name}"
|
89
|
+
else
|
90
|
+
ec2.instance_id = name_id || volume_id
|
91
|
+
end
|
70
92
|
end
|
71
93
|
|
72
94
|
def refresh
|
@@ -111,10 +133,13 @@ module Awsborn
|
|
111
133
|
:availability_zone => symbol_to_aws_zone(zone),
|
112
134
|
:key_name => key_pair.name,
|
113
135
|
:group_ids => security_group,
|
114
|
-
:monitoring_enabled => monitor
|
136
|
+
:monitoring_enabled => monitor,
|
137
|
+
:user_data => user_data
|
115
138
|
)
|
116
139
|
logger.debug @launch_response
|
117
140
|
|
141
|
+
ec2.set_instance_name full_name
|
142
|
+
|
118
143
|
Awsborn.wait_for("instance #{instance_id} (#{name}) to start", 10) { instance_running? }
|
119
144
|
self.host_name = aws_dns_name
|
120
145
|
end
|
@@ -162,7 +187,7 @@ module Awsborn
|
|
162
187
|
end
|
163
188
|
|
164
189
|
def attach_volumes
|
165
|
-
logger.debug "Attaching volumes #{disk.values.join(', ')} to #{name}"
|
190
|
+
logger.debug "Attaching volumes #{disk.values.join(', ')} to #{name}" unless disk.empty?
|
166
191
|
disk.each_pair do |device, str_or_ary|
|
167
192
|
volume = str_or_ary.is_a?(Array) ? str_or_ary.first : str_or_ary
|
168
193
|
device = "/dev/#{device}" if device.is_a?(Symbol) || ! device.match('/')
|
@@ -218,7 +243,7 @@ module Awsborn
|
|
218
243
|
@options[:zone]
|
219
244
|
end
|
220
245
|
def disk
|
221
|
-
@options[:disk]
|
246
|
+
@options[:disk] || {}
|
222
247
|
end
|
223
248
|
def disk_volume_ids
|
224
249
|
disk.values.map { |str_or_ary| str_or_ary.is_a?(Array) ? str_or_ary.first : str_or_ary }
|
@@ -292,7 +317,14 @@ module Awsborn
|
|
292
317
|
@describe_instance ||= ec2.describe_instance
|
293
318
|
end
|
294
319
|
def cluster_name
|
295
|
-
ServerCluster.cluster_for(self)
|
320
|
+
cluster = ServerCluster.cluster_for(self)
|
321
|
+
cluster ? cluster.name : "<unknown>"
|
322
|
+
end
|
323
|
+
def full_name
|
324
|
+
"#{self.class.name.downcase}-#{cluster_name}-#{name}"
|
325
|
+
end
|
326
|
+
def user_data
|
327
|
+
'' # Likely overridden in subclass
|
296
328
|
end
|
297
329
|
end
|
298
330
|
|
@@ -18,13 +18,14 @@ module Awsborn
|
|
18
18
|
clusters.each do |cluster|
|
19
19
|
return cluster if cluster.detect { |i| i == instance }
|
20
20
|
end
|
21
|
+
nil
|
21
22
|
end
|
22
23
|
|
23
24
|
def self.next_name
|
24
25
|
@next_name_counter ||= 1
|
25
26
|
old_names = clusters.map { |c| c.name }
|
26
27
|
begin
|
27
|
-
next_name = "cluster
|
28
|
+
next_name = "cluster#{@next_name_counter}"
|
28
29
|
@next_name_counter += 1
|
29
30
|
end while old_names.include?(next_name)
|
30
31
|
next_name
|
data/spec/ec2_spec.rb
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe Awsborn::Ec2 do
|
4
|
+
before do
|
5
|
+
@ec2 = Awsborn::Ec2.new :eu_west_1
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#set_instance_name" do
|
9
|
+
it "sets the name tag of the instance (if launched)" do
|
10
|
+
@ec2.stub!(:instance_id).and_return("i-123")
|
11
|
+
connection = mock("connection")
|
12
|
+
@ec2.stub!(:connection).and_return(connection)
|
13
|
+
|
14
|
+
connection.should_receive(:create_tags).with("i-123", {"Name" => "foo"})
|
15
|
+
@ec2.set_instance_name "foo"
|
16
|
+
end
|
17
|
+
it "raises an exception if the instance hasn't been launched" do
|
18
|
+
@ec2.stub!(:instance_id).and_return nil
|
19
|
+
expect {@ec2.set_instance_name "foo"}.to raise_error()
|
20
|
+
end
|
21
|
+
end
|
4
22
|
end
|
data/spec/server_spec.rb
CHANGED
@@ -47,6 +47,82 @@ describe Awsborn::Server do
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
describe "running?" do
|
51
|
+
it "first tries to set instance_id using find_instance_id_by_name" do
|
52
|
+
@server.stub!(:find_instance_id_by_name).and_return("i-1234")
|
53
|
+
@server.stub!(:find_instance_id_by_volume).and_return(nil)
|
54
|
+
@server.ec2.should_receive(:instance_id=).with("i-1234")
|
55
|
+
@server.running?
|
56
|
+
end
|
57
|
+
it "defaults back to using find_instance_by_volume" do
|
58
|
+
@server.stub!(:find_instance_id_by_name).and_return(nil)
|
59
|
+
@server.stub!(:find_instance_id_by_volume).and_return("i-2345")
|
60
|
+
@server.ec2.should_receive(:instance_id=).with("i-2345")
|
61
|
+
@server.running?
|
62
|
+
end
|
63
|
+
it "returns the value that instance_id was set to" do
|
64
|
+
@server.stub!(:find_instance_id_by_name).and_return("i-1234")
|
65
|
+
@server.stub!(:find_instance_id_by_volume).and_return(nil)
|
66
|
+
@server.running?.should == "i-1234"
|
67
|
+
end
|
68
|
+
it "raises a ServerError if find_instance_id_by_name and find_instance_id_by_volume doesn't match" do
|
69
|
+
@server.stub!(:find_instance_id_by_name).and_return("i-1234")
|
70
|
+
@server.stub!(:find_instance_id_by_volume).and_return("i-2345")
|
71
|
+
expect {@server.running?}.to raise_error(Awsborn::ServerError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#find_instance_id_by_volume" do
|
76
|
+
it "returns the instance id of the (hopefully unique) instance to which all defined disks are attached" do
|
77
|
+
@server.stub!(:disk_volume_ids).and_return(["volume_id_1", "volume_id_2"])
|
78
|
+
@server.ec2.stub!(:instance_id_for_volume).and_return("i-123")
|
79
|
+
@server.find_instance_id_by_volume.should == "i-123"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns nil if no instance was found" do
|
83
|
+
@server.stub!(:disk_volume_ids).and_return([])
|
84
|
+
@server.find_instance_id_by_volume.should be_nil
|
85
|
+
end
|
86
|
+
|
87
|
+
it "raises a ServerError if multiple instances were found" do
|
88
|
+
@server.stub!(:disk_volume_ids).and_return(["volume_id_1", "volume_id_2"])
|
89
|
+
@server.ec2.stub!(:instance_id_for_volume).and_return do |vol_id|
|
90
|
+
{"volume_id_1" => "i-1",
|
91
|
+
"volume_id_2" => "i-2"}[vol_id]
|
92
|
+
end
|
93
|
+
expect {@server.find_instance_id_by_volume}.to raise_error(Awsborn::ServerError)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#find_instance_id_by_name" do
|
98
|
+
before do
|
99
|
+
@connection = mock("connection")
|
100
|
+
@server.stub!(:full_name).and_return "fyllenamn"
|
101
|
+
@server.ec2.stub!(:connection).and_return @connection
|
102
|
+
end
|
103
|
+
|
104
|
+
it "returns the instance id of the (hopefully unique) pending or running instance with the correct name" do
|
105
|
+
instance = {:aws_instance_id => "i-1234"}
|
106
|
+
@connection.should_receive(:describe_instances).
|
107
|
+
with(:filters => {
|
108
|
+
'tag:Name' => "fyllenamn",
|
109
|
+
'instance-state-name' => ['pending', 'running']
|
110
|
+
}).
|
111
|
+
and_return [instance]
|
112
|
+
@server.find_instance_id_by_name.should == "i-1234"
|
113
|
+
end
|
114
|
+
|
115
|
+
it "returns nil if no instance was found" do
|
116
|
+
@connection.stub!(:describe_instances).and_return []
|
117
|
+
@server.find_instance_id_by_name.should be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
it "raises an exception if too many instances were found" do
|
121
|
+
@connection.stub!(:describe_instances).and_return ["too", "many"]
|
122
|
+
expect {@server.find_instance_id_by_name}.to raise_error(Awsborn::ServerError)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
50
126
|
# TODO
|
51
127
|
# context "first of all" do
|
52
128
|
# it "should have a connection to the EU service point"
|
@@ -79,6 +155,7 @@ describe Awsborn::Server do
|
|
79
155
|
image_id.should == 'ami-2fc2e95b'
|
80
156
|
options[:group_ids].should == ['Common', 'Other', 'SecureServer sample']
|
81
157
|
end
|
158
|
+
ec2.stub!(:set_instance_name)
|
82
159
|
ec2.should_receive(:create_security_group_if_missing).exactly(3).times
|
83
160
|
@server.stub(:ec2).and_return(ec2)
|
84
161
|
@server.should_receive(:instance_running?).and_return(true)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: awsborn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- David Vrensk
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2011-
|
19
|
+
date: 2011-11-11 00:00:00 +01:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -109,6 +109,8 @@ extra_rdoc_files:
|
|
109
109
|
files:
|
110
110
|
- .document
|
111
111
|
- .gitignore
|
112
|
+
- Gemfile
|
113
|
+
- Gemfile.lock
|
112
114
|
- LICENSE
|
113
115
|
- README.mdown
|
114
116
|
- Rakefile
|