awsborn 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.mdown CHANGED
@@ -77,16 +77,16 @@ equally well be added as options to the specific servers.
77
77
 
78
78
  ### Defining a cluster
79
79
 
80
- The `cluster` method accepts three commands, `domain`, `load_balancer` and `server`. `domain` is
81
- optional and is just a way to avoid repetition in the `server` commands. `load_balancer` is also optional and take a name and a hash. `server`
82
- takes a name (which can be used as a key in the cluster, e.g. `log_servers[:log_a]`)
80
+ The `cluster` method accepts three commands, `domain`, `load_balancer` and `server`.
81
+ `domain` is optional and is just a way to avoid repetition in the `server` commands.
82
+ `load_balancer` is also optional and take a name and a hash. `server` takes a name
83
+ (which can be used as a key in the cluster, e.g. `log_servers[:log_a]`)
83
84
  and a hash:
84
85
 
85
86
  Mandatory keys for server:
86
87
 
87
- * `:zone` -- the availability zone for the server. One of `:us_east_1a`, `:us_east_1b`,
88
- `:us_east_1c`, `:us_east_1d`, `:us_west_1a`, `:us_west_1b`, `:eu_west_1a`, `:eu_west_1b`,
89
- `:ap-southeast-1a`, `:ap-southeast-1b`.
88
+ * `:zone` -- the availability zone for the server. Expressed as symbols, e.g.
89
+ `:us_east_1d`, `:us_west_1a`, `:eu_west_1c`, `:ap-southeast-1a`, `:ap-northeast-1b`.
90
90
  * `:disk` -- a hash of `device => volume-id`. Awsborn uses the disks to tell if a server
91
91
  is running or not (see *Launching a cluster*). `volume-id` can also be an array
92
92
  `[volume-id, :format]`, in which case `the_server.format_disk_on_device?(device)`
@@ -108,25 +108,39 @@ Keys that override server type settings:
108
108
 
109
109
  Mandatory keys for load_balancer:
110
110
 
111
- * `:region` -- the region covered by the load balancer. One of `:us_east_1`, `:us_west_1`, `:eu_west_1`, `:ap_southeast_1`
111
+ * `:region` -- the region covered by the load balancer. One of `:us_east_1`, `:us_west_1`,
112
+ `:eu_west_1`, `:ap_southeast_1`, `:ap_northeast_1`
112
113
 
113
114
  Optional keys for load_balancer:
114
115
 
115
- * `:only` -- A list of server names to which the load balancer is
116
- restricted
117
- * `:except` -- A list of servers that the load balancer should ignore
118
- * `:listerners` -- A list of listeners definitions for the load
119
- balancer. For instance: `{ :protocol => :http, :load_balancer_port => 80, :instance_port => 80 }`
120
- * `:sticky_cookies` -- A list of sticky cookies policies that the load balancer
121
- should take care of. There are three kinds of available policies: `:disabled`, `:app_generated` (the cookie is generated by the app) or `:lb_generated` (the cookie is generated by the load balancer).
116
+ * `:only` -- a list of server names to which the load balancer is
117
+ restricted.
118
+ * `:except` -- a list of servers that the load balancer should ignore.
119
+ * `:health_check` -- a hash containing full or partial health check
120
+ configuration. Health check can contain `:healthy_threshold`,
121
+ `:unhealthy_threshold`, `:target`, `:timeout` and `:interval`
122
+ * `:listeners` -- a list of listener definitions for the load balancer. For instance:
123
+ `{ :protocol => :http, :load_balancer_port => 80, :instance_port => 80 }`
124
+ * `:sticky_cookies` -- a list of ports/cookie policies that the load balancer
125
+ should take care of. There are three kinds of available policies:
126
+ `:disabled`,
127
+ `:app_generated` (the cookie is generated by the app) or
128
+ `:lb_generated` (the cookie is generated by the load balancer).
122
129
 
123
130
  Example:
124
131
 
125
132
  cluster "test" do
126
- domain 'yourdimain.net'
133
+ domain 'yourdomain.net'
127
134
  load_balancer 'test-balancer',
128
135
  :region => :eu_west_1,
129
136
  :only => [:cluster_test_1, :cluster_test_2],
137
+ :health_check => {
138
+ :target => 'HTTP:80/index.html',
139
+ :timeout => 6,
140
+ :interval => 31,
141
+ :unhealthy_threshold => 3,
142
+ :healthy_threshold => 9
143
+ },
130
144
  :listeners => [
131
145
  { :protocol => :http, :load_balancer_port => 80, :instance_port => 80},
132
146
  { :protocol => :tcp, :load_balancer_port => 443, :instance_port => 443}
@@ -311,6 +325,14 @@ Other rake tasks include:
311
325
 
312
326
  ## Changes
313
327
 
328
+ ### New in 0.8.1
329
+
330
+ * Specify health_check for load balancers.
331
+
332
+ ### New in 0.8.0
333
+
334
+ * Server cluster can have load balancers.
335
+
314
336
  ### New in 0.7.0
315
337
 
316
338
  * Allow several security groups per server.
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ begin
9
9
  gem.summary = %Q{Awsborn defines servers as instances with a certain disk volume, which makes it easy to restart missing servers.}
10
10
  gem.email = "david@icehouse.se"
11
11
  gem.homepage = "http://github.com/icehouse/awsborn"
12
- gem.authors = ["David Vrensk"]
12
+ gem.authors = ["David Vrensk", "Jean-Louis Giordano"]
13
13
  gem.add_dependency "right_aws", ">= 2.1.0"
14
14
  gem.add_dependency "json_pure", ">= 1.2.3"
15
15
  gem.add_dependency "rake"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.0
1
+ 0.8.1
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.0"
8
+ s.version = "0.8.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["David Vrensk"]
12
- s.date = %q{2011-08-11}
11
+ s.authors = ["David Vrensk", "Jean-Louis Giordano"]
12
+ s.date = %q{2011-08-16}
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 = [
@@ -3,8 +3,8 @@ module Awsborn
3
3
 
4
4
  AVAILABILITY_ZONES = %w[
5
5
  us-east-1a us-east-1b us-east-1c us-east-1d
6
- us-west-1a us-west-1b
7
- eu-west-1a eu-west-1b
6
+ us-west-1a us-west-1b us-west-1c
7
+ eu-west-1a eu-west-1b eu-west-1c
8
8
  ap-southeast-1a ap-southeast-1b
9
9
  ap-northeast-1a ap-northeast-1b
10
10
  ]
@@ -15,14 +15,14 @@ module Awsborn
15
15
  SYMBOL_CONSTANT_MAP = (AVAILABILITY_ZONES + INSTANCE_TYPES).inject({}) { |memo,str| memo[str.tr('-.','_').to_sym] = str; memo }
16
16
 
17
17
  def endpoint_for_zone_and_service (zone, service)
18
- region = zone_to_awz_region(zone)
18
+ region = zone_to_aws_region(zone)
19
19
  case service
20
20
  when :ec2 then "https://#{region}.ec2.amazonaws.com"
21
21
  when :elb then "https://#{region}.elasticloadbalancing.amazonaws.com"
22
22
  end
23
23
  end
24
24
 
25
- def zone_to_awz_region (zone)
25
+ def zone_to_aws_region (zone)
26
26
  region = zone.to_s.sub(/[a-z]$/,'').tr('_','-')
27
27
  raise UnknownConstantError, "Unknown region: #{region} for zone: #{zone}" unless REGIONS.include? region
28
28
  region
@@ -50,7 +50,7 @@ module Awsborn
50
50
  type.to_s.tr('.','_').to_sym
51
51
  end
52
52
 
53
- def awz_constant (symbol)
53
+ def aws_constant (symbol)
54
54
  SYMBOL_CONSTANT_MAP[symbol] || raise(UnknownConstantError, "Unknown constant: #{symbol}")
55
55
  end
56
56
  end
data/lib/awsborn/elb.rb CHANGED
@@ -23,7 +23,7 @@ module Awsborn
23
23
  end
24
24
 
25
25
  def initialize (zone)
26
- @region = zone_to_awz_region(zone)
26
+ @region = zone_to_aws_region(zone)
27
27
  @endpoint = endpoint_for_zone_and_service(@region, :elb)
28
28
  end
29
29
 
@@ -112,6 +112,15 @@ module Awsborn
112
112
  connection.disable_availability_zones_for_load_balancer(balancer_name, *zones)
113
113
  end
114
114
 
115
+ def configure_health_check (balancer_name, health_check)
116
+ logger.debug "Setting health check on load balancer #{balancer_name}"
117
+ connection.configure_health_check(balancer_name, health_check)
118
+ end
119
+
120
+ def health_status (balancer_name)
121
+ connection.describe_instance_health(balancer_name, *instances(balancer_name))
122
+ end
123
+
115
124
  protected
116
125
 
117
126
  def existing_app_cookie_policies (balancer_name)
@@ -2,17 +2,24 @@ module Awsborn
2
2
  class LoadBalancer
3
3
 
4
4
  include Awsborn::AwsConstants
5
- attr_accessor :name, :only, :except, :region, :listeners, :sticky_cookies
5
+ attr_accessor :name, :only, :except, :region, :listeners, :sticky_cookies, :health_check_config
6
6
 
7
7
  DEFAULT_LISTENERS = [ { :protocol => :http, :load_balancer_port => 80, :instance_port => 80} ]
8
+ DEFAULT_HEALTH_CONFIG = {
9
+ :healthy_threshold => 10,
10
+ :unhealthy_threshold => 2,
11
+ :target => "TCP:80",
12
+ :timeout => 5,
13
+ :interval => 30
14
+ }
8
15
  def initialize (name, options={})
9
16
  @name = name
10
17
  @only = options[:only] || []
11
18
  @except = options[:except] || []
12
- @region = zone_to_awz_region(options[:region])
19
+ @region = zone_to_aws_region(options[:region])
13
20
  @listeners = options[:listeners] || DEFAULT_LISTENERS
14
21
  @sticky_cookies = options[:sticky_cookies] || []
15
- launch unless running?
22
+ @health_check_config = DEFAULT_HEALTH_CONFIG.merge(options[:health_check] || {})
16
23
  end
17
24
 
18
25
  def elb
@@ -57,6 +64,9 @@ module Awsborn
57
64
  elb.create_load_balancer(@name)
58
65
  end
59
66
 
67
+ def health_status
68
+ elb.health_status(@name)
69
+ end
60
70
 
61
71
  def update_listeners
62
72
  elb.set_load_balancer_listeners(@name, @listeners)
@@ -80,7 +90,13 @@ module Awsborn
80
90
  end
81
91
  end
82
92
 
93
+ def update_health_config
94
+ elb.configure_health_check(@name, @health_check_config)
95
+ end
96
+
83
97
  def update_with (new_servers)
98
+ launch unless running?
99
+
84
100
  servers_to_be_balanced = new_servers
85
101
  servers_to_be_balanced =
86
102
  servers_to_be_balanced.select{|s| @only.include?(s.name)} unless @only.empty?
@@ -92,6 +108,7 @@ module Awsborn
92
108
 
93
109
  update_listeners
94
110
  update_sticky_cookies
111
+ update_health_config
95
112
 
96
113
  self.description
97
114
  end
@@ -15,19 +15,19 @@ describe Awsborn::AwsConstants do
15
15
  end
16
16
  end
17
17
 
18
- describe "zone_to_awz_region" do
18
+ describe "zone_to_aws_region" do
19
19
  it "accepts a zone symbol and returns its region" do
20
- zone_to_awz_region(:eu_west_1a).should == 'eu-west-1'
20
+ zone_to_aws_region(:eu_west_1a).should == 'eu-west-1'
21
21
  end
22
22
  it "accepts an aws zone symbol and returns its region" do
23
- zone_to_awz_region('eu-west-1a').should == 'eu-west-1'
23
+ zone_to_aws_region('eu-west-1a').should == 'eu-west-1'
24
24
  end
25
25
  it "raise an error if no region found" do
26
- expect{zone_to_awz_region('santa-northpole-2b')}.to raise_error(Awsborn::UnknownConstantError)
26
+ expect{zone_to_aws_region('santa-northpole-2b')}.to raise_error(Awsborn::UnknownConstantError)
27
27
  end
28
28
  it "returns a region even when a region is given" do
29
- zone_to_awz_region('eu-west-1').should == 'eu-west-1'
30
- zone_to_awz_region(:eu_west_1).should == 'eu-west-1'
29
+ zone_to_aws_region('eu-west-1').should == 'eu-west-1'
30
+ zone_to_aws_region(:eu_west_1).should == 'eu-west-1'
31
31
  end
32
32
  end
33
33
 
@@ -67,15 +67,15 @@ describe Awsborn::AwsConstants do
67
67
  end
68
68
  end
69
69
 
70
- describe "awz_constant" do
70
+ describe "aws_constant" do
71
71
  it "should look up an availability zone" do
72
- awz_constant(:eu_west_1a).should == "eu-west-1a"
72
+ aws_constant(:eu_west_1a).should == "eu-west-1a"
73
73
  end
74
74
  it "should look up an instance type" do
75
- awz_constant(:m1_large).should == "m1.large"
75
+ aws_constant(:m1_large).should == "m1.large"
76
76
  end
77
77
  it "should raise an error if the symbol is unknown" do
78
- expect{awz_constant(:unknown_constant)}.to raise_error(Awsborn::UnknownConstantError)
78
+ expect{aws_constant(:unknown_constant)}.to raise_error(Awsborn::UnknownConstantError)
79
79
  end
80
80
  end
81
81
 
data/spec/elb_spec.rb CHANGED
@@ -251,6 +251,21 @@ describe Awsborn::Elb do
251
251
  @elb.disable_zones('some-name', ['eu-west-1a', 'eu-west-1b'])
252
252
  end
253
253
  end
254
+
255
+ describe "configure_health_check" do
256
+ it "forwards to ElbInterface" do
257
+ @mock_interface.should_receive(:configure_health_check).with('some-name', 'health-check-config')
258
+ @elb.configure_health_check('some-name', 'health-check-config')
259
+ end
260
+ end
261
+
262
+ describe "health_status" do
263
+ it "returns the health status of each instance of the load balancer" do
264
+ @mock_interface.should_receive(:describe_load_balancers).with('some-name').and_return([{:instances => 'instances'}])
265
+ @mock_interface.should_receive(:describe_instance_health).with('some-name', 'instances').and_return("health_status")
266
+ @elb.health_status('some-name')
267
+ end
268
+ end
254
269
  end
255
270
  end
256
271
 
@@ -5,9 +5,23 @@ describe Awsborn::LoadBalancer do
5
5
  @mocked_elb = mock(:elb,
6
6
  :running? => false,
7
7
  :remove_all_cookie_policies => true,
8
- :create_load_balancer => {})
8
+ :create_load_balancer => {},
9
+ :instances => [],
10
+ :zones => [],
11
+ :register_instances => true,
12
+ :enable_zones => true,
13
+ :set_load_balancer_listeners => true,
14
+ :describe_load_balancer => "description",
15
+ :configure_health_check => true)
9
16
  @listener_fixture = [ { :protocol => :tcp, :load_balancer_port => 123, :instance_port => 123} ]
10
17
  @cookies_fixture = [ { :ports => [123], :policy => :disabled } ]
18
+ @health_check_fixture = {
19
+ :healthy_threshold => 9,
20
+ :unhealthy_threshold => 3,
21
+ :target => "TCP:433",
22
+ :timeout => 6,
23
+ :interval => 31
24
+ }
11
25
  Awsborn::Elb.stub!(:new).and_return(@mocked_elb)
12
26
  end
13
27
  describe "initialize" do
@@ -15,22 +29,6 @@ describe Awsborn::LoadBalancer do
15
29
  expect { Awsborn::LoadBalancer.new('some-name') }.to raise_error
16
30
  expect { Awsborn::LoadBalancer.new('some-name', :region => :blabla) }.to raise_error
17
31
  end
18
- it "launches the load balancer if not running" do
19
- @mocked_elb.should_receive(:running?).with('some-name').and_return(false)
20
- @mocked_elb.should_receive(:create_load_balancer).with('some-name').and_return(nil)
21
- @balancer = Awsborn::LoadBalancer.new(
22
- 'some-name',
23
- :region => :eu_west_1
24
- )
25
- end
26
- it "does not launch the load balancer if running" do
27
- @mocked_elb.should_receive(:running?).with('some-name').and_return(true)
28
- @mocked_elb.should_not_receive(:create_load_balancer)
29
- @balancer = Awsborn::LoadBalancer.new(
30
- 'some-name',
31
- :region => :eu_west_1
32
- )
33
- end
34
32
  describe "sets all attributes properly" do
35
33
  subject do
36
34
  @balancer = Awsborn::LoadBalancer.new(
@@ -39,7 +37,8 @@ describe Awsborn::LoadBalancer do
39
37
  :only => [:server1, :server2],
40
38
  :except => [:server2],
41
39
  :listeners => @listener_fixture,
42
- :sticky_cookies => @cookies_fixture
40
+ :sticky_cookies => @cookies_fixture,
41
+ :health_check => @health_check_fixture
43
42
  )
44
43
  end
45
44
  its(:name) { should == 'some-name' }
@@ -48,6 +47,7 @@ describe Awsborn::LoadBalancer do
48
47
  its(:except) { should == [:server2] }
49
48
  its(:listeners) { should == @listener_fixture }
50
49
  its(:sticky_cookies) { should == @cookies_fixture }
50
+ its(:health_check_config) { should == @health_check_fixture }
51
51
  end
52
52
  describe "sets proper default values" do
53
53
  subject do
@@ -60,6 +60,19 @@ describe Awsborn::LoadBalancer do
60
60
  its(:except) { should == [] }
61
61
  its(:listeners) { should == Awsborn::LoadBalancer::DEFAULT_LISTENERS }
62
62
  its(:sticky_cookies) { should == [] }
63
+ its(:health_check_config) { should == Awsborn::LoadBalancer::DEFAULT_HEALTH_CONFIG }
64
+ end
65
+ it "accepts partial health config" do
66
+ balancer = Awsborn::LoadBalancer.new(
67
+ 'some-name',
68
+ :region => :eu_west_1,
69
+ :health_check => { :target => 'HTTP:80/test.html' }
70
+ )
71
+ balancer.health_check_config[:target].should == 'HTTP:80/test.html'
72
+ [:healthy_threshold, :unhealthy_threshold, :timeout, :interval].each do |other_attribute|
73
+ balancer.health_check_config[other_attribute].should ==
74
+ Awsborn::LoadBalancer::DEFAULT_HEALTH_CONFIG[other_attribute]
75
+ end
63
76
  end
64
77
  end
65
78
 
@@ -180,6 +193,27 @@ describe Awsborn::LoadBalancer do
180
193
  end
181
194
  end
182
195
 
196
+ describe "health_status" do
197
+ it "delegates to elb" do
198
+ @mocked_elb.should_receive(:health_status).with('some-name').and_return('health_status')
199
+ @balancer = Awsborn::LoadBalancer.new(
200
+ 'some-name',
201
+ :region => :eu_west_1
202
+ ).health_status.should == 'health_status'
203
+ end
204
+ end
205
+
206
+ describe "update_health_config" do
207
+ it "delegates to elb" do
208
+ @mocked_elb.should_receive(:configure_health_check).with('some-name', @health_check_fixture)
209
+ @balancer = Awsborn::LoadBalancer.new(
210
+ 'some-name',
211
+ :region => :eu_west_1,
212
+ :health_check => @health_check_fixture
213
+ ).update_health_config
214
+ end
215
+ end
216
+
183
217
  describe "update_listeners" do
184
218
  it "delegates to elb" do
185
219
  @mocked_elb.should_receive('set_load_balancer_listeners').with('some-name', @listener_fixture)
@@ -254,11 +288,22 @@ describe Awsborn::LoadBalancer do
254
288
  mock(:server1, :name => :server2, :instance_id => 'i-00000002', :zone => :eu_west_1b)
255
289
  ]
256
290
  end
257
- it "sets new instances, sets new zones, updates listeners and updates sticky cookies" do
291
+ it "launches the load balancer if not running" do
292
+ @mocked_elb.should_receive(:running?).with('some-name').and_return(false)
293
+ @mocked_elb.should_receive(:create_load_balancer).with('some-name').and_return(nil)
294
+ @balancer.update_with(@new_servers)
295
+ end
296
+ it "does not launch the load balancer if running" do
297
+ @mocked_elb.should_receive(:running?).with('some-name').and_return(true)
298
+ @mocked_elb.should_not_receive(:create_load_balancer)
299
+ @balancer.update_with(@new_servers)
300
+ end
301
+ it "sets instances and new zones and updates listeners, sticky cookies and health config" do
258
302
  @balancer.should_receive(:instances=).with(['i-00000001', 'i-00000002'])
259
303
  @balancer.should_receive(:zones=).with(['eu-west-1a', 'eu-west-1b'])
260
304
  @balancer.should_receive(:update_listeners)
261
305
  @balancer.should_receive(:update_sticky_cookies)
306
+ @balancer.should_receive(:update_health_config)
262
307
  @balancer.should_receive(:description).and_return('description')
263
308
  @balancer.update_with(@new_servers).should == 'description'
264
309
  end
@@ -269,6 +314,7 @@ describe Awsborn::LoadBalancer do
269
314
  @balancer.should_receive(:zones=).with(['eu-west-1a'])
270
315
  @balancer.should_receive(:update_listeners)
271
316
  @balancer.should_receive(:update_sticky_cookies)
317
+ @balancer.should_receive(:update_health_config)
272
318
  @balancer.should_receive(:description).and_return('description')
273
319
  @balancer.update_with(@new_servers).should == 'description'
274
320
  end
@@ -279,6 +325,7 @@ describe Awsborn::LoadBalancer do
279
325
  @balancer.should_receive(:zones=).with(['eu-west-1b'])
280
326
  @balancer.should_receive(:update_listeners)
281
327
  @balancer.should_receive(:update_sticky_cookies)
328
+ @balancer.should_receive(:update_health_config)
282
329
  @balancer.should_receive(:description).and_return('description')
283
330
  @balancer.update_with(@new_servers).should == 'description'
284
331
  end
data/spec/server_spec.rb CHANGED
@@ -17,15 +17,15 @@ describe Awsborn::Server do
17
17
  @server = SampleServer.new :sample, :zone => :eu_west_1a, :disk => {:sdf => "vol-aaaaaaaa"}
18
18
  end
19
19
 
20
- describe "awz_constant" do
20
+ describe "aws_constant" do
21
21
  it "should look up an availability zone" do
22
- @server.awz_constant(:eu_west_1a).should == "eu-west-1a"
22
+ @server.aws_constant(:eu_west_1a).should == "eu-west-1a"
23
23
  end
24
24
  it "should look up an instance type" do
25
- @server.awz_constant(:m1_large).should == "m1.large"
25
+ @server.aws_constant(:m1_large).should == "m1.large"
26
26
  end
27
27
  it "should raise an error if the symbol is unknown" do
28
- expect{@server.awz_constant(:unknown_constant)}.to raise_error(Awsborn::UnknownConstantError)
28
+ expect{@server.aws_constant(:unknown_constant)}.to raise_error(Awsborn::UnknownConstantError)
29
29
  end
30
30
  end
31
31
 
metadata CHANGED
@@ -1,21 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: awsborn
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
4
+ hash: 61
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 8
9
- - 0
10
- version: 0.8.0
9
+ - 1
10
+ version: 0.8.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - David Vrensk
14
+ - Jean-Louis Giordano
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-08-11 00:00:00 +02:00
19
+ date: 2011-08-16 00:00:00 +02:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency