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.
- data/.specification +57 -0
- data/README.rdoc +25 -21
- data/Rakefile +20 -4
- data/VERSION +1 -1
- data/awsymandias.gemspec +37 -12
- data/lib/awsymandias.rb +36 -331
- data/lib/awsymandias/addons/right_elb_interface.rb +375 -0
- data/lib/awsymandias/ec2.rb +49 -0
- data/lib/awsymandias/ec2/application_stack.rb +261 -0
- data/lib/awsymandias/extensions/class_extension.rb +18 -0
- data/lib/awsymandias/extensions/net_http_extension.rb +9 -0
- data/lib/awsymandias/instance.rb +149 -0
- data/lib/awsymandias/load_balancer.rb +157 -0
- data/lib/awsymandias/right_aws.rb +73 -0
- data/lib/awsymandias/right_elb.rb +16 -0
- data/lib/awsymandias/simple_db.rb +46 -0
- data/lib/awsymandias/snapshot.rb +33 -0
- data/lib/awsymandias/stack_definition.rb +60 -0
- data/lib/awsymandias/volume.rb +70 -0
- data/spec/integration/instance_spec.rb +37 -0
- data/spec/unit/addons/right_elb_interface_spec.rb +45 -0
- data/spec/unit/awsymandias_spec.rb +61 -0
- data/spec/unit/ec2/application_stack_spec.rb +634 -0
- data/spec/unit/instance_spec.rb +365 -0
- data/spec/unit/load_balancer_spec.rb +250 -0
- data/spec/unit/right_aws_spec.rb +90 -0
- data/spec/unit/simple_db_spec.rb +63 -0
- data/spec/unit/snapshot_spec.rb +39 -0
- data/spec/unit/stack_definition_spec.rb +113 -0
- data/tags +368 -0
- metadata +39 -13
- data/spec/awsymandias_spec.rb +0 -815
- data/vendor/aws-sdb/LICENSE +0 -19
- data/vendor/aws-sdb/README +0 -1
- data/vendor/aws-sdb/Rakefile +0 -20
- data/vendor/aws-sdb/lib/aws_sdb.rb +0 -3
- data/vendor/aws-sdb/lib/aws_sdb/error.rb +0 -42
- data/vendor/aws-sdb/lib/aws_sdb/service.rb +0 -191
- data/vendor/aws-sdb/spec/aws_sdb/service_spec.rb +0 -183
- 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
|