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 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