awsborn 0.8.7 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -13,6 +13,9 @@ tmtags
13
13
  ## VIM
14
14
  *.swp
15
15
 
16
+ ## RUBYMINE
17
+ .idea/
18
+
16
19
  ## PROJECT::GENERAL
17
20
  coverage
18
21
  rdoc
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.8.7
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.7"
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-10-06}
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
@@ -95,5 +95,10 @@ module Awsborn
95
95
  end
96
96
  end
97
97
 
98
+ def set_instance_name (name)
99
+ raise "Instance hasn't been launched yet" unless instance_id
100
+ connection.create_tags(instance_id, {"Name" => name})
101
+ end
102
+
98
103
  end
99
104
  end
@@ -59,14 +59,36 @@ module Awsborn
59
59
  end
60
60
  end
61
61
 
62
- def running?
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
- ec2.instance_id = ids.first
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).name
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 #{@next_name_counter}"
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: 49
4
+ hash: 59
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 8
9
- - 7
10
- version: 0.8.7
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-10-06 00:00:00 +02:00
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