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.
Files changed (40) hide show
  1. data/.specification +57 -0
  2. data/README.rdoc +25 -21
  3. data/Rakefile +20 -4
  4. data/VERSION +1 -1
  5. data/awsymandias.gemspec +37 -12
  6. data/lib/awsymandias.rb +36 -331
  7. data/lib/awsymandias/addons/right_elb_interface.rb +375 -0
  8. data/lib/awsymandias/ec2.rb +49 -0
  9. data/lib/awsymandias/ec2/application_stack.rb +261 -0
  10. data/lib/awsymandias/extensions/class_extension.rb +18 -0
  11. data/lib/awsymandias/extensions/net_http_extension.rb +9 -0
  12. data/lib/awsymandias/instance.rb +149 -0
  13. data/lib/awsymandias/load_balancer.rb +157 -0
  14. data/lib/awsymandias/right_aws.rb +73 -0
  15. data/lib/awsymandias/right_elb.rb +16 -0
  16. data/lib/awsymandias/simple_db.rb +46 -0
  17. data/lib/awsymandias/snapshot.rb +33 -0
  18. data/lib/awsymandias/stack_definition.rb +60 -0
  19. data/lib/awsymandias/volume.rb +70 -0
  20. data/spec/integration/instance_spec.rb +37 -0
  21. data/spec/unit/addons/right_elb_interface_spec.rb +45 -0
  22. data/spec/unit/awsymandias_spec.rb +61 -0
  23. data/spec/unit/ec2/application_stack_spec.rb +634 -0
  24. data/spec/unit/instance_spec.rb +365 -0
  25. data/spec/unit/load_balancer_spec.rb +250 -0
  26. data/spec/unit/right_aws_spec.rb +90 -0
  27. data/spec/unit/simple_db_spec.rb +63 -0
  28. data/spec/unit/snapshot_spec.rb +39 -0
  29. data/spec/unit/stack_definition_spec.rb +113 -0
  30. data/tags +368 -0
  31. metadata +39 -13
  32. data/spec/awsymandias_spec.rb +0 -815
  33. data/vendor/aws-sdb/LICENSE +0 -19
  34. data/vendor/aws-sdb/README +0 -1
  35. data/vendor/aws-sdb/Rakefile +0 -20
  36. data/vendor/aws-sdb/lib/aws_sdb.rb +0 -3
  37. data/vendor/aws-sdb/lib/aws_sdb/error.rb +0 -42
  38. data/vendor/aws-sdb/lib/aws_sdb/service.rb +0 -191
  39. data/vendor/aws-sdb/spec/aws_sdb/service_spec.rb +0 -183
  40. data/vendor/aws-sdb/spec/spec_helper.rb +0 -4
@@ -0,0 +1,365 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require File.expand_path(File.dirname(__FILE__) + "/../../lib/awsymandias")
4
+
5
+ module Awsymandias
6
+ module RightAws
7
+ describe Instance do
8
+ def stub_connection_with(requested, return_value)
9
+ Awsymandias::RightAws.stub!(:connection).and_return stub("a connection", requested.to_sym => return_value)
10
+ end
11
+
12
+ DESCRIBE_INSTANCES_NO_RESULTS_RESPONSE = []
13
+
14
+ DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE = [{:aws_instance_type=>"m1.large",
15
+ :ami_launch_index=>"0",
16
+ :aws_reason=>"",
17
+ :aws_launch_time=>"2009-04-20T01:30:35.000Z",
18
+ :aws_owner=>"423319072129",
19
+ :ssh_key_name=>"",
20
+ :aws_reservation_id=>"r-b9b6e2d0",
21
+ :aws_kernel_id=>"aki-b51cf9dc",
22
+ :aws_instance_id=>"i-pending-instance",
23
+ :aws_availability_zone=>"us-east-1b",
24
+ :aws_state=>"pending",
25
+ :aws_groups=>["default"],
26
+ :aws_ramdisk_id=>"ari-b31cf9da",
27
+ :aws_image_id=>"ami-890beae0",
28
+ :dns_name=>"",
29
+ :aws_state_code=>"0",
30
+ :aws_product_codes=>[],
31
+ :private_dns_name=>""}]
32
+
33
+ DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE = [{:aws_instance_type=>"m1.large",
34
+ :ami_launch_index=>"0",
35
+ :aws_reason=>"",
36
+ :aws_launch_time=>"2009-07-23T13:57:22.000Z",
37
+ :aws_owner=>"423319072129",
38
+ :ssh_key_name=>"gsg-keypair",
39
+ :aws_reservation_id=>"r-dd6733b4",
40
+ :aws_kernel_id=>"aki-b51cf9dc",
41
+ :aws_instance_id=>"i-single-running-instance",
42
+ :aws_availability_zone=>"us-east-1b",
43
+ :aws_state=>"running",
44
+ :aws_groups=>["default"],
45
+ :aws_ramdisk_id=>"ari-b31cf9da",
46
+ :aws_image_id=>"ami-some-image",
47
+ :dns_name=>"ec2-174-129-118-52.compute-1.amazonaws.com",
48
+ :aws_state_code=>"16",
49
+ :aws_product_codes=>[],
50
+ :private_dns_name=>"ip-10-244-226-239.ec2.internal"}]
51
+
52
+ DESCRIBE_INSTANCES_MULTIPLE_RESULTS_RUNNING_RESPONSE = [{:aws_instance_type=>"c1.xlarge",
53
+ :ami_launch_index=>"0",
54
+ :aws_reason=>"",
55
+ :aws_launch_time=>"2009-07-23T13:57:22.000Z",
56
+ :aws_owner=>"423319072129",
57
+ :ssh_key_name=>"",
58
+ :aws_reservation_id=>"r-dd6733b4",
59
+ :aws_kernel_id=>"aki-b51cf9dc",
60
+ :aws_instance_id=>"i-multiple_running_1",
61
+ :aws_availability_zone=>"us-east-1b",
62
+ :aws_state=>"running",
63
+ :aws_groups=>["default"],
64
+ :aws_ramdisk_id=>"ari-b31cf9da",
65
+ :aws_image_id=>"ami-890beae0",
66
+ :dns_name=>"ec2-174-129-124-195.compute-1.amazonaws.com",
67
+ :aws_state_code=>"16",
68
+ :aws_product_codes=>[],
69
+ :private_dns_name=>"ip-10-250-214-207.ec2.internal"},
70
+ {:aws_instance_type=>"m1.large",
71
+ :ami_launch_index=>"0",
72
+ :aws_reason=>"",
73
+ :aws_launch_time=>"2009-07-23T14:57:22.000Z",
74
+ :aws_owner=>"423319072129",
75
+ :ssh_key_name=>"",
76
+ :aws_reservation_id=>"r-dd6733b5",
77
+ :aws_kernel_id=>"aki-b51cf9dc",
78
+ :aws_instance_id=>"i-multiple_running_2",
79
+ :aws_availability_zone=>"us-east-1b",
80
+ :aws_state=>"running",
81
+ :aws_groups=>["default"],
82
+ :aws_ramdisk_id=>"ari-b31cf9da",
83
+ :aws_image_id=>"ami-890beae0",
84
+ :dns_name=>"ec2-174-129-124-196.compute-1.amazonaws.com",
85
+ :aws_state_code=>"16",
86
+ :aws_product_codes=>[],
87
+ :private_dns_name=>"ip-10-250-214-208.ec2.internal"}]
88
+
89
+ RUN_INSTANCES_SINGLE_RESULT_RESPONSE = [{:aws_image_id => "ami-e444444d",
90
+ :aws_reason => "",
91
+ :aws_state_code => "0",
92
+ :aws_owner => "000000000888",
93
+ :aws_instance_id => "i-123f1234",
94
+ :aws_reservation_id => "r-aabbccdd",
95
+ :aws_state => "pending",
96
+ :dns_name => "",
97
+ :ssh_key_name => "my_awesome_key",
98
+ :aws_groups => ["my_awesome_group"],
99
+ :private_dns_name => "",
100
+ :aws_instance_type => "m1.large",
101
+ :aws_launch_time => "2008-1-1T00:00:00.000Z",
102
+ :aws_ramdisk_id => "ari-8605e0ef",
103
+ :aws_kernel_id => "aki-9905e0f0",
104
+ :ami_launch_index => "0",
105
+ :aws_availability_zone => "us-east-1b"}]
106
+
107
+
108
+ TERMINATE_INSTANCES_SINGLE_RESULT_RESPONSE = [{:aws_shutdown_state => "shutting-down",
109
+ :aws_instance_id => "i-f222222d",
110
+ :aws_shutdown_state_code => 32,
111
+ :aws_prev_state => "running",
112
+ :aws_prev_state_code => 16}]
113
+
114
+ DESCRIBE_INSTANCES_SINGLE_RESULT_TERMINATED_RESPONSE = [{:aws_instance_type=>"c1.xlarge",
115
+ :ami_launch_index=>"0",
116
+ :aws_reason=>"User initiated ",
117
+ :aws_launch_time=>"2009-07-23T18:22:49.000Z",
118
+ :aws_owner=>"423319072129",
119
+ :ssh_key_name=>"",
120
+ :aws_reservation_id=>"r-b9b6e2d0",
121
+ :aws_kernel_id=>"aki-b51cf9dc",
122
+ :aws_instance_id=>"i-fb585992",
123
+ :aws_availability_zone=>"us-east-1b",
124
+ :aws_state=>"terminated",
125
+ :aws_groups=>["default"],
126
+ :aws_ramdisk_id=>"ari-b31cf9da",
127
+ :aws_image_id=>"ami-890beae0",
128
+ :dns_name=>"",
129
+ :aws_state_code=>"48",
130
+ :aws_product_codes=>[],
131
+ :private_dns_name=>""}]
132
+
133
+ describe "find" do
134
+ it "should raise ActiveResource::ResourceNotFound if the given instance ID is not found" do
135
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_NO_RESULTS_RESPONSE
136
+ lambda do
137
+ Instance.find("i-some-instance")
138
+ end.should raise_error(ActiveResource::ResourceNotFound)
139
+ end
140
+
141
+ it "should return an object with the appropriate instance ID when an instance with the given ID is found" do
142
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
143
+ Instance.find("i-single-running-instance").instance_id.should == "i-single-running-instance"
144
+ end
145
+
146
+ it "should return more than one object if multiple IDs are requested" do
147
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_MULTIPLE_RESULTS_RUNNING_RESPONSE
148
+ Instance.find(:all, :instance_ids => ["i-multiple_running_1", "i-multiple_running_2"]).map do |instance|
149
+ instance.instance_id
150
+ end.should == ["i-multiple_running_1", "i-multiple_running_2"]
151
+ end
152
+
153
+ it "should map camelized XML properties to Ruby-friendly underscored method names" do
154
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
155
+ instance = Instance.find("i-single-running-instance")
156
+ instance.aws_image_id.should == "ami-some-image"
157
+ instance.ssh_key_name.should == "gsg-keypair"
158
+ instance.aws_instance_type.should == Awsymandias::EC2.instance_types["m1.large"]
159
+ instance.aws_availability_zone.should == "us-east-1b"
160
+ end
161
+ end
162
+
163
+ describe "to_params" do
164
+ it "should be able to reproduce a reasonable set of its launch params as a hash" do
165
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
166
+ Instance.find("i-some-instance").to_params.should == {
167
+ :aws_image_id => "ami-some-image",
168
+ :ssh_key_name => "gsg-keypair",
169
+ :aws_instance_type => Awsymandias::EC2.instance_types["m1.large"],
170
+ :aws_availability_zone => "us-east-1b"
171
+ }
172
+ end
173
+ end
174
+
175
+ describe "running?" do
176
+ it "should return false if it contains an instances set with the given instance ID and its state is pending" do
177
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE
178
+ Instance.find("i-some-instance").should_not be_running
179
+ end
180
+
181
+ it "should return true if it contains an instances set with the given instance ID and its state is running" do
182
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
183
+ Instance.find("i-some-instance").should be_running
184
+ end
185
+ end
186
+
187
+ describe "reload" do
188
+ it "should reload an instance without replacing the object" do
189
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE
190
+ instance = Instance.find("i-some-instance")
191
+ instance.should_not be_running
192
+
193
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
194
+ instance.reload.should be_running
195
+ end
196
+ end
197
+
198
+ describe "launch" do
199
+ it "should launch a new instance given some values" do
200
+ mock_connection = mock("a connection")
201
+ mock_connection.should_receive(:run_instances).
202
+ with("an_id", 1, 1, "default", "gsg-keypair", nil, nil, "m1.small", nil, nil,
203
+ "us_east_1a", nil).and_return(RUN_INSTANCES_SINGLE_RESULT_RESPONSE)
204
+
205
+ mock_connection.should_receive(:describe_instances).and_return(DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE)
206
+
207
+ Awsymandias::RightAws.stub!(:connection).and_return mock_connection
208
+
209
+ Awsymandias::Instance.launch(
210
+ :image_id => "an_id",
211
+ :key_name => "gsg-keypair",
212
+ :instance_type => Awsymandias::EC2::InstanceTypes::M1_SMALL,
213
+ :availability_zone => Awsymandias::EC2::AvailabilityZones::US_EAST_1A
214
+ )
215
+ end
216
+
217
+ it "should convert the instance type it's given to a string as needed" do
218
+ mock_connection = mock("a connection")
219
+ mock_connection.should_receive(:run_instances).with(nil, 1, 1, "default", nil, nil, nil,
220
+ "m1.small", nil, nil, nil, nil).and_return(RUN_INSTANCES_SINGLE_RESULT_RESPONSE)
221
+ mock_connection.should_receive(:describe_instances).and_return(stub("response").as_null_object)
222
+ Awsymandias::RightAws.stub!(:connection).and_return mock_connection
223
+
224
+ Awsymandias::Instance.launch(:instance_type => Awsymandias::EC2::InstanceTypes::M1_SMALL)
225
+ end
226
+ end
227
+
228
+ describe "terminate!" do
229
+ it "should terminate a running instance" do
230
+ mock_connection = mock("a connection")
231
+ mock_connection.should_receive(:describe_instances).and_return(
232
+ DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE,
233
+ DESCRIBE_INSTANCES_SINGLE_RESULT_TERMINATED_RESPONSE
234
+ )
235
+ mock_connection.should_receive(:terminate_instances).and_return(
236
+ TERMINATE_INSTANCES_SINGLE_RESULT_RESPONSE
237
+ )
238
+
239
+ Awsymandias::RightAws.stub!(:connection).and_return mock_connection
240
+
241
+ instance = Awsymandias::Instance.find("a result id")
242
+ instance.should be_running
243
+ instance.terminate!
244
+ instance.should_not be_running
245
+ instance.should be_terminated
246
+ end
247
+ end
248
+
249
+ describe "instance_type" do
250
+ it "should return its instance_type attribute as an InstanceType object" do
251
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
252
+ Instance.find("i-some-instance").aws_instance_type.should == Awsymandias::EC2::InstanceTypes::M1_LARGE
253
+ end
254
+ end
255
+
256
+ describe "launch_time" do
257
+ it "should return its launch_time attribute as an instance of Time" do
258
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE
259
+ Awsymandias::Instance.find("i-some-instance").aws_launch_time.should == Time.parse("2009-04-20T01:30:35.000Z")
260
+ end
261
+ end
262
+
263
+ describe "uptime" do
264
+ it "should be zero seconds if it is not yet running" do
265
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE
266
+ Awsymandias::Instance.find("i-some-instance").uptime.should == 0.seconds
267
+ end
268
+
269
+ it "should be zero seconds if it is terminated" do
270
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE
271
+ Awsymandias::Instance.find("i-some-instance").uptime.should == 0.seconds
272
+ end
273
+
274
+ it "should calculate the uptime of a running instance in terms of its launch time" do
275
+ time_now = Time.now
276
+ Time.stub!(:now).and_return time_now
277
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
278
+ instance = Awsymandias::Instance.find("i-some-instance")
279
+ instance.uptime.should == (time_now - instance.aws_launch_time)
280
+ end
281
+ end
282
+
283
+ describe "public_dns" do
284
+ it "should return the public dns from the xml" do
285
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
286
+ Awsymandias::Instance.find("i-some-instance").public_dns.should == "ec2-174-129-118-52.compute-1.amazonaws.com"
287
+ end
288
+ end
289
+
290
+ describe "private_dns" do
291
+ it "should return the private dns from the xml" do
292
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
293
+ Awsymandias::Instance.find("i-some-instance").private_dns.should == "ip-10-244-226-239.ec2.internal"
294
+ end
295
+ end
296
+
297
+ describe "running_cost" do
298
+ it "should be zero if the instance has not yet been launched" do
299
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_PENDING_RESPONSE
300
+ Awsymandias::Instance.find("i-some-instance").running_cost.should == Money.new(0)
301
+ end
302
+
303
+ it "should be a single increment if the instance was launched 5 minutes ago" do
304
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
305
+ instance = Awsymandias::Instance.find("i-some-instance")
306
+ instance.attributes['aws_launch_time'] = 5.minutes.ago.to_s
307
+ expected_cost = instance.aws_instance_type.price_per_hour
308
+ instance.running_cost.should == expected_cost
309
+ end
310
+
311
+ it "should be a single increment if the instance was launched 59 minutes ago" do
312
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
313
+ instance = Awsymandias::Instance.find("i-some-instance")
314
+ instance.attributes['aws_launch_time'] = 59.minutes.ago.to_s
315
+ expected_cost = instance.aws_instance_type.price_per_hour
316
+ instance.running_cost.should == expected_cost
317
+ end
318
+
319
+ it "should be two increments if the instance was launched 61 minutes ago" do
320
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
321
+ instance = Awsymandias::Instance.find("i-some-instance")
322
+ instance.attributes['aws_launch_time'] = 61.minutes.ago.to_s
323
+ expected_cost = instance.aws_instance_type.price_per_hour * 2
324
+ instance.running_cost.should == expected_cost
325
+ end
326
+
327
+ it "should be three increments if the instance was launched 150 minutes ago" do
328
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
329
+ instance = Awsymandias::Instance.find("i-some-instance")
330
+ instance.attributes['aws_launch_time'] = 150.minutes.ago.to_s
331
+ expected_cost = instance.aws_instance_type.price_per_hour * 3
332
+ instance.running_cost.should == expected_cost
333
+ end
334
+ end
335
+
336
+ describe "port_open?" do
337
+ it "should return true if telnet does not raise" do
338
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
339
+ instance = Awsymandias::Instance.find("i-some-instance")
340
+ Net::Telnet.should_receive(:new).with("Timeout" => 5,
341
+ "Host" => "ec2-174-129-118-52.compute-1.amazonaws.com",
342
+ "Port" => 100).and_return(true)
343
+ instance.port_open?(100).should be_true
344
+ end
345
+
346
+ it "should return false if telnet does raise" do
347
+ stub_connection_with :describe_instances, DESCRIBE_INSTANCES_SINGLE_RESULT_RUNNING_RESPONSE
348
+ instance = Awsymandias::Instance.find("i-some-instance")
349
+ Net::Telnet.should_receive(:new).with("Timeout" => 5,
350
+ "Host" => "ec2-174-129-118-52.compute-1.amazonaws.com",
351
+ "Port" => 100).and_raise(Timeout::Error.new("error"))
352
+ instance.port_open?(100).should be_false
353
+ end
354
+ end
355
+
356
+ describe "dns_hostname" do
357
+ it "should return instance name with dashes instead of underscores" do
358
+ instance = Instance.new
359
+ instance.name = :db_1
360
+ instance.dns_hostname.should == "db-1"
361
+ end
362
+ end
363
+ end
364
+ end
365
+ end
@@ -0,0 +1,250 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require File.expand_path(File.dirname(__FILE__) + "/../../lib/awsymandias")
4
+
5
+ module Awsymandias
6
+ describe LoadBalancer do
7
+
8
+ before :each do
9
+ Awsymandias::RightElb.should_receive(:connection).any_number_of_times.and_return(@elb_connection = mock('connection'))
10
+ @elb_connection.should_receive(:configure_health_check).any_number_of_times.and_return(lambda { @lb.health_check })
11
+ end
12
+
13
+ def raises_error(message)
14
+ @error_raised = false
15
+ yield
16
+ rescue => error
17
+ @error_raised = true
18
+ fail "Expected an exception to be raised with message '#{message}' but got '#{error.message}'" if message != error.message
19
+ ensure
20
+ fail "Expected an exception to be raised with message '#{message}' but no exception was raised" unless @error_raised
21
+ end
22
+
23
+ DEFAULT_LB_ATTRIBUTES = {:aws_created_at => "Tue Aug 04 11:14:27 UTC 2009",
24
+ :availability_zones=>["us-east-1b"],
25
+ :dns_name=>"RobTest-883635706.us-east-1.elb.amazonaws.com",
26
+ :name=>"RobTest",
27
+ :instances=>["i-5752453e"],
28
+ :listeners=> [{:protocol=>"HTTP", :load_balancer_port=>80, :instance_port=>3080},
29
+ {:protocol=>"HTTP", :load_balancer_port=>8080, :instance_port=>3081}
30
+ ],
31
+ :health_check=> { :healthy_threshold=>10,
32
+ :unhealthy_threshold=>3,
33
+ :interval=>31,
34
+ :target=>"TCP:3081",
35
+ :timeout=>6
36
+ }
37
+ }
38
+
39
+ def populated_load_balancer(attribs = {})
40
+ @lb = LoadBalancer.new DEFAULT_LB_ATTRIBUTES.merge(attribs)
41
+ @lb
42
+ end
43
+
44
+ describe "valid_load_balancer_name" do
45
+ it "should return true for a valid load balancer name" do
46
+ LoadBalancer.valid_load_balancer_name?('balancer-1').should == true
47
+ end
48
+
49
+ it "should return false for a load balancer name containing an underscore" do
50
+ LoadBalancer.valid_load_balancer_name?('balancer_1').should == false
51
+ end
52
+
53
+ it "should return false for a load balancer name containing a period" do
54
+ LoadBalancer.valid_load_balancer_name?('balancer.1').should == false
55
+ end
56
+
57
+ it "should return false for a load balancer name containing a colon" do
58
+ LoadBalancer.valid_load_balancer_name?('balancer:1').should == false
59
+ end
60
+
61
+ it "should return false for a load balancer name containing a slash" do
62
+ LoadBalancer.valid_load_balancer_name?('balancer/1').should == false
63
+ end
64
+
65
+ it "should return false for an invalid load balancer name that is a symbol" do
66
+ LoadBalancer.valid_load_balancer_name?(:balancer_1).should == false
67
+ end
68
+ end
69
+
70
+ describe 'initialize' do
71
+ it "should initialize new HealthCheck and Listener objects" do
72
+ lb = populated_load_balancer
73
+ lb.health_check.is_a?(LoadBalancer::HealthCheck).should == true
74
+ lb.listeners.first.is_a?(LoadBalancer::Listener).should == true
75
+ end
76
+
77
+ it "should raise an error when trying to initialize a load balancer with an invalid name" do
78
+ raises_error("Load balancer name can only contain alphanumeric characters or a dash.") do
79
+ lb = populated_load_balancer :name => "invalid_name"
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "availability_zones=" do
85
+ it "should remove availability_zones that are not in the passed list but are enabled in the load balancer" do
86
+ lb = populated_load_balancer :availability_zones => [:zone_1, :zone_2]
87
+
88
+ desired_zones = [:zone_2]
89
+
90
+ @elb_connection.should_receive(:disable_availability_zones_for_lb).with(lb.name, [:zone_1]).and_return(desired_zones)
91
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:availability_zones => desired_zones}])
92
+ lb.availability_zones = desired_zones
93
+ end
94
+
95
+ it "should add availability_zones that are in the passed list but not already in the load balancer" do
96
+ lb = populated_load_balancer :availability_zones => [:zone_1]
97
+
98
+ desired_zones = [:zone_1, :zone_2]
99
+
100
+ @elb_connection.should_receive(:enable_availability_zones_for_lb).with(lb.name, [:zone_2]).and_return(desired_zones)
101
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:availability_zones => desired_zones}])
102
+
103
+ lb.availability_zones = desired_zones
104
+ end
105
+
106
+ it "should update the availability_zones attribute" do
107
+ lb = populated_load_balancer :availability_zones => [:zone_1]
108
+
109
+ desired_zones = [:zone_1, :zone_2]
110
+
111
+ @elb_connection.should_receive(:enable_availability_zones_for_lb).with(lb.name, [:zone_2]).and_return(desired_zones)
112
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:availability_zones => desired_zones}])
113
+
114
+ lb.availability_zones = desired_zones
115
+ lb.availability_zones.should == desired_zones
116
+ end
117
+ end
118
+
119
+ describe "find" do
120
+ # it "should return an array of Awsymandias::Snapshot objects." do
121
+ # connection = mock('connection')
122
+ # connection.should_receive(:describe_snapshots).and_return(
123
+ # [{:aws_id => :some_snapshot_id}, {:aws_id => :another_snapshot_id}]
124
+ # )
125
+ # Awsymandias::RightAws.should_receive(:connection).and_return(connection)
126
+ #
127
+ # snapshots = Snapshot.find
128
+ # snapshots.map(&:aws_id).should == [:some_snapshot_id, :another_snapshot_id]
129
+ # snapshots.map(&:class).uniq.should == [Awsymandias::Snapshot]
130
+ # end
131
+ end
132
+
133
+ describe "health_check=" do
134
+ it "should not call save if the load_balancer is not launched" do
135
+ health_check = LoadBalancer::HealthCheck.new populated_load_balancer
136
+ LoadBalancer::HealthCheck.should_receive(:new).and_return(health_check)
137
+ health_check.should_receive(:save).never
138
+
139
+ lb = populated_load_balancer :dns_name => nil
140
+ end
141
+
142
+ it "should try to configure health check with new parameters" do
143
+ custom_settings = {:healthy_threshold => :custom_healthy_threshold,
144
+ :unhealthy_threshold => :custom_unhealthy_threshold,
145
+ :interval => :custom_interval,
146
+ :target => :custom_target,
147
+ :timeout => :custom_timeout
148
+ }
149
+
150
+ lb = populated_load_balancer
151
+ health_check = LoadBalancer::HealthCheck.new lb, custom_settings
152
+
153
+ custom_settings.each_pair { |setting_name, value| health_check.send(setting_name).should == value }
154
+ end
155
+
156
+ it "should use defaults for attributes that are not passed in" do
157
+ lb = populated_load_balancer
158
+ health_check = LoadBalancer::HealthCheck.new lb
159
+
160
+ LoadBalancer::HealthCheck::DEFAULT_SETTINGS.each_pair { |setting_name, value| health_check.send(setting_name).should == value }
161
+ end
162
+ end
163
+
164
+ describe "instances=" do
165
+ it "should deregister instances that are not in the passed list but are registered with the load balancer" do
166
+ lb = populated_load_balancer :instances => [:instance_1, :instance_2]
167
+
168
+ desired_instances = [:instance_2]
169
+
170
+ @elb_connection.should_receive(:deregister_instances_from_lb).with(lb.name, [:instance_1]).and_return(desired_instances)
171
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:instances => desired_instances}])
172
+ lb.instances = desired_instances
173
+ end
174
+
175
+ it "should register instances that are in the passed list but are not registered with the load balancer" do
176
+ lb = populated_load_balancer :instances => [:instance_1]
177
+
178
+ desired_instances = [:instance_1, :instance_2]
179
+
180
+ @elb_connection.should_receive(:register_instances_with_lb).with(lb.name, [:instance_2]).and_return(desired_instances)
181
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:instances => desired_instances}])
182
+ lb.instances = desired_instances
183
+ end
184
+
185
+ it "should update the instances attribute" do
186
+ lb = populated_load_balancer :instances => [:instance_1]
187
+
188
+ desired_instances = [:instance_1, :instance_2]
189
+
190
+ @elb_connection.should_receive(:register_instances_with_lb).with(lb.name, [:instance_2]).and_return(desired_instances)
191
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:instances => desired_instances}])
192
+
193
+ lb.instances = desired_instances
194
+ lb.instances.should == desired_instances
195
+ end
196
+ end
197
+
198
+ describe "launch" do
199
+ it ", the class method, should instantiate a new load balancer and launch it" do
200
+ listener_hash = {:protocol => 'HTTP', :load_balancer_port => 80, :instance_port => 8080}
201
+ @elb_connection.should_receive(:create_lb).with(:lbname, [:some_availability_zones], [ listener_hash ])
202
+ LoadBalancer.launch({:name => :lbname,
203
+ :availability_zones => [:some_availability_zones],
204
+ :listeners => [ listener_hash ]})
205
+ end
206
+
207
+ it "should raise an error if you try to launch a load balancer with no availability zones" do
208
+ lb = populated_load_balancer :availability_zones => []
209
+ raises_error("Load balancers must have at least one availability zone defined.") do
210
+ lb.launch
211
+ end
212
+ end
213
+
214
+ it "should raise an error if you try to launch a load balancer with no listeners" do
215
+ lb = populated_load_balancer :listeners => []
216
+ raises_error("Load balancers must have at least one listener defined.") do
217
+ lb.launch.should
218
+ end
219
+ end
220
+
221
+ it "should set the dns_name when launched" do
222
+ lb = populated_load_balancer :dns_name => nil
223
+ @elb_connection.should_receive(:create_lb).and_return(:a_dns_name)
224
+ lb.launch
225
+ lb.dns_name.should == :a_dns_name
226
+ end
227
+ end
228
+
229
+ describe "reload" do
230
+ it "should not try to refresh if not launched" do
231
+ lb = populated_load_balancer :dns_name => nil
232
+ @elb_connection.should_receive(:describe_lbs).never
233
+ lb.reload
234
+ end
235
+
236
+ it "should refresh itself from AWS data" do
237
+ last_hour = Time.now - 1.hour
238
+ lb = populated_load_balancer :aws_created_at => last_hour.to_s
239
+
240
+ time_now = Time.now
241
+ expected_attributes = DEFAULT_LB_ATTRIBUTES.merge({ :aws_created_at => time_now.to_s })
242
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([expected_attributes])
243
+
244
+ lb.aws_created_at.to_s == last_hour.to_s
245
+ lb.reload
246
+ lb.aws_created_at.to_s.should == time_now.to_s
247
+ end
248
+ end
249
+ end
250
+ end