auser-poolparty 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/README.txt +10 -10
- data/Rakefile +30 -21
- data/{web/static/site/images → assets}/clouds.png +0 -0
- data/bin/instance +39 -34
- data/bin/pool +44 -29
- data/bin/poolnotify +34 -0
- data/config/haproxy.conf +1 -1
- data/config/heartbeat_authkeys.conf +1 -1
- data/config/monit/haproxy.monit.conf +2 -1
- data/config/nginx.conf +1 -1
- data/config/reconfigure_instances_script.sh +28 -9
- data/config/sample-config.yml +1 -1
- data/lib/core/string.rb +3 -0
- data/lib/modules/ec2_wrapper.rb +47 -22
- data/lib/modules/file_writer.rb +38 -0
- data/lib/modules/sprinkle_overrides.rb +32 -0
- data/lib/modules/vlad_override.rb +5 -4
- data/lib/poolparty.rb +14 -10
- data/lib/poolparty/application.rb +33 -19
- data/lib/poolparty/master.rb +227 -105
- data/lib/poolparty/optioner.rb +8 -4
- data/lib/poolparty/plugin.rb +34 -4
- data/lib/poolparty/provider/packages/haproxy.rb +0 -15
- data/lib/poolparty/provider/packages/heartbeat.rb +1 -1
- data/lib/poolparty/provider/packages/ruby.rb +6 -6
- data/lib/poolparty/provider/packages/s3fuse.rb +9 -2
- data/lib/poolparty/provider/provider.rb +65 -25
- data/lib/poolparty/remote_instance.rb +95 -74
- data/lib/poolparty/remoter.rb +48 -37
- data/lib/poolparty/remoting.rb +41 -17
- data/lib/poolparty/scheduler.rb +4 -4
- data/lib/poolparty/tasks.rb +1 -1
- data/lib/poolparty/tasks/package.rake +53 -0
- data/lib/poolparty/tasks/plugins.rake +1 -1
- data/poolparty.gemspec +50 -58
- data/spec/application_spec.rb +28 -0
- data/spec/core_spec.rb +9 -0
- data/spec/ec2_wrapper_spec.rb +87 -0
- data/spec/file_writer_spec.rb +73 -0
- data/spec/files/describe_response +37 -0
- data/spec/files/multi_describe_response +69 -0
- data/spec/files/remote_desc_response +37 -0
- data/spec/helpers/ec2_mock.rb +3 -0
- data/spec/master_spec.rb +302 -78
- data/spec/monitors/cpu_monitor_spec.rb +2 -1
- data/spec/monitors/memory_spec.rb +1 -0
- data/spec/monitors/misc_monitor_spec.rb +1 -0
- data/spec/monitors/web_spec.rb +1 -0
- data/spec/optioner_spec.rb +12 -0
- data/spec/plugin_manager_spec.rb +10 -10
- data/spec/plugin_spec.rb +6 -3
- data/spec/pool_binary_spec.rb +3 -0
- data/spec/poolparty_spec.rb +12 -7
- data/spec/provider_spec.rb +1 -0
- data/spec/remote_instance_spec.rb +18 -18
- data/spec/remoter_spec.rb +4 -2
- data/spec/remoting_spec.rb +10 -2
- data/spec/scheduler_spec.rb +0 -6
- data/spec/spec_helper.rb +13 -0
- metadata +83 -52
- data/Manifest +0 -115
- data/lib/poolparty/tmp.rb +0 -46
- data/misc/basics_tutorial.txt +0 -142
- data/web/static/conf/nginx.conf +0 -22
- data/web/static/site/images/balloon.png +0 -0
- data/web/static/site/images/cb.png +0 -0
- data/web/static/site/images/railsconf_preso_img.png +0 -0
- data/web/static/site/index.html +0 -71
- data/web/static/site/javascripts/application.js +0 -3
- data/web/static/site/javascripts/corner.js +0 -178
- data/web/static/site/javascripts/jquery-1.2.6.pack.js +0 -11
- data/web/static/site/misc.html +0 -42
- data/web/static/site/storage/pool_party_presentation.pdf +0 -0
- data/web/static/site/stylesheets/application.css +0 -100
- data/web/static/site/stylesheets/reset.css +0 -17
- data/web/static/src/layouts/application.haml +0 -25
- data/web/static/src/pages/index.haml +0 -25
- data/web/static/src/pages/misc.haml +0 -5
- data/web/static/src/stylesheets/application.sass +0 -100
data/spec/core_spec.rb
CHANGED
@@ -12,4 +12,13 @@ describe "Hash" do
|
|
12
12
|
a.safe_merge!(b)
|
13
13
|
a.should == {:a => "10", :b => "20", :c => "40"}
|
14
14
|
end
|
15
|
+
end
|
16
|
+
describe "String" do
|
17
|
+
it "should be able to convert a big string with \n to a runnable string" do
|
18
|
+
str =<<-EOS
|
19
|
+
echo 'hi'
|
20
|
+
puts 'hi'
|
21
|
+
EOS
|
22
|
+
str.runnable.should == "echo 'hi' && puts 'hi'"
|
23
|
+
end
|
15
24
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require File.dirname(__FILE__) + "/helpers/ec2_mock"
|
3
|
+
|
4
|
+
class EC2Test
|
5
|
+
include Ec2Wrapper
|
6
|
+
|
7
|
+
def get_non_empty_instance_description
|
8
|
+
@resp ||= EC2::Response.parse(:xml => read_file("describe_response"))
|
9
|
+
end
|
10
|
+
def get_non_empty_instances_description
|
11
|
+
@resp2 ||= EC2::Response.parse(:xml => read_file("multi_describe_response"))
|
12
|
+
end
|
13
|
+
def get_remote_inst_desc
|
14
|
+
@resp3 = EC2::Response.parse(:xml => read_file("remote_desc_response"))
|
15
|
+
end
|
16
|
+
def read_file(name)
|
17
|
+
open("#{File.dirname(__FILE__)}/files/#{name}").read
|
18
|
+
end
|
19
|
+
end
|
20
|
+
describe "EC2ResponseObject" do
|
21
|
+
before(:each) do
|
22
|
+
@test = EC2Test.new
|
23
|
+
@r = @test.get_non_empty_instance_description
|
24
|
+
@rs = @test.get_non_empty_instances_description
|
25
|
+
@rst = @test.get_remote_inst_desc
|
26
|
+
end
|
27
|
+
describe "single instance" do
|
28
|
+
it "should be able to get the response object from the query" do
|
29
|
+
EC2ResponseObject.get_response_from(@r).should_not be_nil
|
30
|
+
end
|
31
|
+
it "should return an EC2:Response from EC2ResponseObject.get_response_from" do
|
32
|
+
EC2ResponseObject.get_response_from(@r).class.should == EC2::Response
|
33
|
+
end
|
34
|
+
it "should be able to grab the keypair name from the response object" do
|
35
|
+
EC2ResponseObject.get_response_from(@r).instancesSet.item.instanceId.should == "i-60bc6a09"
|
36
|
+
end
|
37
|
+
it "should be able to list out the running instances" do
|
38
|
+
EC2ResponseObject.get_descriptions(@r)
|
39
|
+
end
|
40
|
+
it "should return an array of instances" do
|
41
|
+
EC2ResponseObject.get_descriptions(@r).class.should == Array
|
42
|
+
end
|
43
|
+
it "should be able to iterate through the instances" do
|
44
|
+
EC2ResponseObject.get_descriptions(@r).select {|a| a[:keypair] == "auser"}.should_not be_empty
|
45
|
+
end
|
46
|
+
end
|
47
|
+
describe "multiple responses" do
|
48
|
+
it "should be able to get the response object from the query" do
|
49
|
+
EC2ResponseObject.get_response_from(@r).should_not be_nil
|
50
|
+
end
|
51
|
+
it "should return an EC2:Response from EC2ResponseObject.get_response_from" do
|
52
|
+
EC2ResponseObject.get_response_from(@r).class.should == EC2::Response
|
53
|
+
end
|
54
|
+
it "should be able to grab the keypair name from the response object" do
|
55
|
+
EC2ResponseObject.get_response_from(@r).instancesSet.item.instanceId.should == "i-60bc6a09"
|
56
|
+
end
|
57
|
+
it "should be able to list out the running instances" do
|
58
|
+
EC2ResponseObject.get_descriptions(@r)
|
59
|
+
end
|
60
|
+
it "should return an array of instances" do
|
61
|
+
EC2ResponseObject.get_descriptions(@r).class.should == Array
|
62
|
+
end
|
63
|
+
it "should be able to iterate through the instances" do
|
64
|
+
EC2ResponseObject.get_descriptions(@r).select {|a| a[:keypair] == "auser"}.should_not be_empty
|
65
|
+
end
|
66
|
+
end
|
67
|
+
describe "multiple responses" do
|
68
|
+
it "should be able to get the response object from the query" do
|
69
|
+
EC2ResponseObject.get_response_from(@rst).should_not be_nil
|
70
|
+
end
|
71
|
+
it "should return an EC2:Response from EC2ResponseObject.get_response_from" do
|
72
|
+
EC2ResponseObject.get_response_from(@rst).class.should == EC2::Response
|
73
|
+
end
|
74
|
+
it "should be able to grab the keypair name from the response object" do
|
75
|
+
EC2ResponseObject.get_response_from(@rst).instancesSet.item.instanceId.should == "i-94f82efd"
|
76
|
+
end
|
77
|
+
it "should be able to list out the running instances" do
|
78
|
+
EC2ResponseObject.get_descriptions(@rst)
|
79
|
+
end
|
80
|
+
it "should return an array of instances" do
|
81
|
+
EC2ResponseObject.get_descriptions(@rst).class.should == Array
|
82
|
+
end
|
83
|
+
it "should be able to iterate through the instances" do
|
84
|
+
EC2ResponseObject.get_descriptions(@rst).select {|a| a[:keypair] == "auser"}.should_not be_empty
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class TestClass
|
4
|
+
include FileWriter
|
5
|
+
end
|
6
|
+
describe "FileWriter" do
|
7
|
+
before(:each) do
|
8
|
+
@instance = RemoteInstance.new
|
9
|
+
@instance.stub!(:name).and_return "node0"
|
10
|
+
@instance.stub!(:ip).and_return "127.0.0.1"
|
11
|
+
@test = TestClass.new
|
12
|
+
end
|
13
|
+
describe "writing to the temp directory with a string" do
|
14
|
+
before(:each) do
|
15
|
+
@test.write_to_file_for("haproxy", @instance, "topical")
|
16
|
+
@outfile = "#{@test.base_tmp_dir}/node0-haproxy"
|
17
|
+
end
|
18
|
+
after(:each) do
|
19
|
+
FileUtils.rm @outfile
|
20
|
+
end
|
21
|
+
it "should be able to write a file to the tmp directory with a string" do
|
22
|
+
File.file?(@outfile).should == true
|
23
|
+
end
|
24
|
+
it "should be able to write to the file with the string" do
|
25
|
+
open(@outfile).read.should == "topical"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "writing to the temp directory with a block" do
|
30
|
+
before(:each) do
|
31
|
+
@test.write_to_file_for("haproxy", @instance) do
|
32
|
+
"Hello topical"
|
33
|
+
end
|
34
|
+
@outfile = "#{@test.base_tmp_dir}/node0-haproxy"
|
35
|
+
end
|
36
|
+
after(:each) do
|
37
|
+
FileUtils.rm @outfile
|
38
|
+
end
|
39
|
+
it "should be able to write a file to the tmp directory with a string" do
|
40
|
+
File.file?(@outfile).should == true
|
41
|
+
end
|
42
|
+
it "should be able to write to the file with the string" do
|
43
|
+
open(@outfile).read.should == "Hello topical"
|
44
|
+
end
|
45
|
+
describe "for output of block" do
|
46
|
+
before(:each) do
|
47
|
+
@test.write_to_file_for("haproxy", @instance) do
|
48
|
+
"Fix and me"
|
49
|
+
"Hello topical"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
it "should just write the final output of the block to the file" do
|
53
|
+
open(@outfile).read.should == "Hello topical"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "without a node" do
|
59
|
+
before(:each) do
|
60
|
+
@test.write_to_file_for("haproxy") do
|
61
|
+
"Hello topical"
|
62
|
+
end
|
63
|
+
@outfile = "#{@test.base_tmp_dir}/haproxy"
|
64
|
+
end
|
65
|
+
after(:each) do
|
66
|
+
FileUtils.rm @outfile
|
67
|
+
end
|
68
|
+
it "should write to the master file without a node name" do
|
69
|
+
File.file?(@outfile).should == true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2008-02-01/">
|
3
|
+
<reservationSet>
|
4
|
+
<item>
|
5
|
+
<reservationId>r-6d539104</reservationId>
|
6
|
+
<ownerId>161964561164</ownerId>
|
7
|
+
<groupSet>
|
8
|
+
<item>
|
9
|
+
<groupId>default</groupId>
|
10
|
+
</item>
|
11
|
+
</groupSet>
|
12
|
+
<instancesSet>
|
13
|
+
<item>
|
14
|
+
<instanceId>i-60bc6a09</instanceId>
|
15
|
+
<imageId>ami-4bb05422</imageId>
|
16
|
+
<instanceState>
|
17
|
+
<code>16</code>
|
18
|
+
<name>running</name>
|
19
|
+
</instanceState>
|
20
|
+
<privateDnsName>domU-12-31-39-00-86-15.compute-1.internal</privateDnsName>
|
21
|
+
<dnsName>ec2-75-101-242-244.compute-1.amazonaws.com</dnsName>
|
22
|
+
<reason/>
|
23
|
+
<keyName>auser</keyName>
|
24
|
+
<amiLaunchIndex>0</amiLaunchIndex>
|
25
|
+
<productCodes/>
|
26
|
+
<instanceType>m1.small</instanceType>
|
27
|
+
<launchTime>2008-06-25T20:14:35.000Z</launchTime>
|
28
|
+
<placement>
|
29
|
+
<availabilityZone>us-east-1b</availabilityZone>
|
30
|
+
</placement>
|
31
|
+
<kernelId>aki-a71cf9ce</kernelId>
|
32
|
+
<ramdiskId>ari-a51cf9cc</ramdiskId>
|
33
|
+
</item>
|
34
|
+
</instancesSet>
|
35
|
+
</item>
|
36
|
+
</reservationSet>
|
37
|
+
</DescribeInstancesResponse>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2008-02-01/">
|
3
|
+
<reservationSet>
|
4
|
+
<item>
|
5
|
+
<reservationId>r-7678ba1f</reservationId>
|
6
|
+
<ownerId>802044448869</ownerId>
|
7
|
+
<groupSet>
|
8
|
+
<item>
|
9
|
+
<groupId>default</groupId>
|
10
|
+
</item>
|
11
|
+
</groupSet>
|
12
|
+
<instancesSet>
|
13
|
+
<item>
|
14
|
+
<instanceId>i-6df12704</instanceId>
|
15
|
+
<imageId>ami-3057b259</imageId>
|
16
|
+
<instanceState>
|
17
|
+
<code>16</code>
|
18
|
+
<name>running</name>
|
19
|
+
</instanceState>
|
20
|
+
<privateDnsName>ip-10-251-199-97.ec2.internal</privateDnsName>
|
21
|
+
<dnsName>ec2-72-44-39-61.compute-1.amazonaws.com</dnsName>
|
22
|
+
<reason/>
|
23
|
+
<keyName>pjc-keypair</keyName>
|
24
|
+
<amiLaunchIndex>0</amiLaunchIndex>
|
25
|
+
<productCodes/>
|
26
|
+
<instanceType>m1.small</instanceType>
|
27
|
+
<launchTime>2008-06-26T21:55:52.000Z</launchTime>
|
28
|
+
<placement>
|
29
|
+
<availabilityZone>us-east-1c</availabilityZone>
|
30
|
+
</placement>
|
31
|
+
<kernelId>aki-a71cf9ce</kernelId>
|
32
|
+
<ramdiskId>ari-a51cf9cc</ramdiskId>
|
33
|
+
</item>
|
34
|
+
</instancesSet>
|
35
|
+
</item>
|
36
|
+
<item>
|
37
|
+
<reservationId>r-4978ba20</reservationId>
|
38
|
+
<ownerId>802044448869</ownerId>
|
39
|
+
<groupSet>
|
40
|
+
<item>
|
41
|
+
<groupId>default</groupId>
|
42
|
+
</item>
|
43
|
+
</groupSet>
|
44
|
+
<instancesSet>
|
45
|
+
<item>
|
46
|
+
<instanceId>i-6cf12705</instanceId>
|
47
|
+
<imageId>ami-3057b259</imageId>
|
48
|
+
<instanceState>
|
49
|
+
<code>16</code>
|
50
|
+
<name>running</name>
|
51
|
+
</instanceState>
|
52
|
+
<privateDnsName>ip-10-251-198-230.ec2.internal</privateDnsName>
|
53
|
+
<dnsName>ec2-75-101-236-239.compute-1.amazonaws.com</dnsName>
|
54
|
+
<reason/>
|
55
|
+
<keyName>pjc-keypair</keyName>
|
56
|
+
<amiLaunchIndex>0</amiLaunchIndex>
|
57
|
+
<productCodes/>
|
58
|
+
<instanceType>m1.small</instanceType>
|
59
|
+
<launchTime>2008-06-26T21:55:55.000Z</launchTime>
|
60
|
+
<placement>
|
61
|
+
<availabilityZone>us-east-1c</availabilityZone>
|
62
|
+
</placement>
|
63
|
+
<kernelId>aki-a71cf9ce</kernelId>
|
64
|
+
<ramdiskId>ari-a51cf9cc</ramdiskId>
|
65
|
+
</item>
|
66
|
+
</instancesSet>
|
67
|
+
</item>
|
68
|
+
</reservationSet>
|
69
|
+
</DescribeInstancesResponse>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2008-02-01/">
|
3
|
+
<reservationSet>
|
4
|
+
<item>
|
5
|
+
<reservationId>r-ec8d4085</reservationId>
|
6
|
+
<ownerId>161964561164</ownerId>
|
7
|
+
<groupSet>
|
8
|
+
<item>
|
9
|
+
<groupId>default</groupId>
|
10
|
+
</item>
|
11
|
+
</groupSet>
|
12
|
+
<instancesSet>
|
13
|
+
<item>
|
14
|
+
<instanceId>i-94f82efd</instanceId>
|
15
|
+
<imageId>ami-4bb05422</imageId>
|
16
|
+
<instanceState>
|
17
|
+
<code>16</code>
|
18
|
+
<name>running</name>
|
19
|
+
</instanceState>
|
20
|
+
<privateDnsName>ip-10-251-203-148.ec2.internal</privateDnsName>
|
21
|
+
<dnsName>ec2-75-101-205-180.compute-1.amazonaws.com</dnsName>
|
22
|
+
<reason/>
|
23
|
+
<keyName>auser</keyName>
|
24
|
+
<amiLaunchIndex>0</amiLaunchIndex>
|
25
|
+
<productCodes/>
|
26
|
+
<instanceType>m1.small</instanceType>
|
27
|
+
<launchTime>2008-06-27T02:25:26.000Z</launchTime>
|
28
|
+
<placement>
|
29
|
+
<availabilityZone>us-east-1a</availabilityZone>
|
30
|
+
</placement>
|
31
|
+
<kernelId>aki-a71cf9ce</kernelId>
|
32
|
+
<ramdiskId>ari-a51cf9cc</ramdiskId>
|
33
|
+
</item>
|
34
|
+
</instancesSet>
|
35
|
+
</item>
|
36
|
+
</reservationSet>
|
37
|
+
</DescribeInstancesResponse>
|
data/spec/helpers/ec2_mock.rb
CHANGED
@@ -24,6 +24,9 @@ module PoolParty
|
|
24
24
|
def get_instances_description
|
25
25
|
instances
|
26
26
|
end
|
27
|
+
def get_non_empty_instances_description
|
28
|
+
EC2::Response.parse(:xml => open("#{File.dirname(__FILE__)}/../files/response.txt").read)
|
29
|
+
end
|
27
30
|
# Fake the ec2 connection
|
28
31
|
def ec2
|
29
32
|
@ec2 ||= EC2::Base.new(:access_key_id => "not a key", :secret_access_key => "not a key")
|
data/spec/master_spec.rb
CHANGED
@@ -2,14 +2,18 @@ require File.dirname(__FILE__) + '/spec_helper'
|
|
2
2
|
|
3
3
|
describe "Master" do
|
4
4
|
before(:each) do
|
5
|
+
stub_option_load
|
5
6
|
Kernel.stub!(:system).and_return true
|
6
7
|
Kernel.stub!(:exec).and_return true
|
7
|
-
Kernel.stub!(:sleep).and_return true # WHy wait
|
8
|
+
Kernel.stub!(:sleep).and_return true # WHy wait?, just do it
|
8
9
|
|
9
10
|
Application.options.stub!(:contract_when).and_return("web > 30.0\n cpu < 0.10")
|
10
11
|
Application.options.stub!(:expand_when).and_return("web < 3.0\n cpu > 0.80")
|
11
12
|
@master = Master.new
|
12
13
|
end
|
14
|
+
after(:all) do
|
15
|
+
@master.cleanup_tmp_directory(nil)
|
16
|
+
end
|
13
17
|
it "should launch the first instances and set the first as the master and the rest as slaves" do
|
14
18
|
Application.stub!(:minimum_instances).and_return(1)
|
15
19
|
Application.stub!(:verbose).and_return(false) # Hide messages
|
@@ -20,11 +24,11 @@ describe "Master" do
|
|
20
24
|
@master.stub!(:wait).and_return true
|
21
25
|
|
22
26
|
@master.should_receive(:launch_new_instance!).and_return(
|
23
|
-
{:instance_id => "i-5849ba", :ip => "
|
27
|
+
{:instance_id => "i-5849ba", :ip => "127.0.0.1", :status => "running"})
|
24
28
|
@master.stub!(:list_of_nonterminated_instances).and_return(
|
25
|
-
[{:instance_id => "i-5849ba", :ip => "
|
29
|
+
[{:instance_id => "i-5849ba", :ip => "127.0.0.1", :status => "running"}])
|
26
30
|
|
27
|
-
node = RemoteInstance.new({:instance_id => "i-5849ba", :ip => "
|
31
|
+
node = RemoteInstance.new({:instance_id => "i-5849ba", :ip => "127.0.0.1", :status => "running"})
|
28
32
|
node.stub!(:scp).and_return "true"
|
29
33
|
node.stub!(:ssh).and_return "true"
|
30
34
|
|
@@ -34,14 +38,36 @@ describe "Master" do
|
|
34
38
|
|
35
39
|
@master.nodes.first.instance_id.should == "i-5849ba"
|
36
40
|
end
|
41
|
+
describe "Singleton methods" do
|
42
|
+
before(:each) do
|
43
|
+
@master = Master.new
|
44
|
+
@instance = RemoteInstance.new
|
45
|
+
@blk = Proc.new {puts "new"}
|
46
|
+
Master.stub!(:new).once.and_return @master
|
47
|
+
end
|
48
|
+
it "should be able to run with_nodes" do
|
49
|
+
Master.should_receive(:new).once.and_return @master
|
50
|
+
@master.should_receive(:nodes).once.and_return []
|
51
|
+
Master.with_nodes &@blk
|
52
|
+
end
|
53
|
+
it "should run the block on each node" do
|
54
|
+
collection = [@instance]
|
55
|
+
@master.should_receive(:nodes).once.and_return collection
|
56
|
+
collection.should_receive(:each).once
|
57
|
+
Master.with_nodes &@blk
|
58
|
+
end
|
59
|
+
end
|
37
60
|
describe "with stubbed instances" do
|
38
61
|
before(:each) do
|
39
62
|
@master.stub!(:list_of_nonterminated_instances).and_return([
|
40
|
-
{:instance_id => "i-5849ba", :ip => "
|
41
|
-
{:instance_id => "i-5849bb", :ip => "
|
42
|
-
{:instance_id => "i-5849bc", :ip => "
|
63
|
+
{:instance_id => "i-5849ba", :ip => "127.0.0.1", :status => "running"},
|
64
|
+
{:instance_id => "i-5849bb", :ip => "127.0.0.2", :status => "running"},
|
65
|
+
{:instance_id => "i-5849bc", :ip => "127.0.0.3", :status => "pending"}
|
43
66
|
])
|
44
67
|
Kernel.stub!(:exec).and_return true
|
68
|
+
@instance = RemoteInstance.new
|
69
|
+
@instance.stub!(:ip).and_return("127.0.0.1")
|
70
|
+
@instance.stub!(:name).and_return("node0")
|
45
71
|
end
|
46
72
|
|
47
73
|
it "should be able to go through the instances and assign them numbers" do
|
@@ -61,72 +87,128 @@ describe "Master" do
|
|
61
87
|
@master.get_node(2).instance_id.should == "i-5849bc"
|
62
88
|
end
|
63
89
|
it "should be able to build a hosts file" do
|
64
|
-
open(@master.
|
90
|
+
open(@master.build_hosts_file_for(@instance).path).read.should == "127.0.0.1 node0\n127.0.0.1 localhost.localdomain localhost ubuntu\n127.0.0.2 node1\n127.0.0.3 node2"
|
65
91
|
end
|
66
92
|
it "should be able to build a hosts file for a specific instance" do
|
67
|
-
@master.build_hosts_file_for(@
|
93
|
+
open(@master.build_hosts_file_for(@instance).path).read.should =~ "127.0.0.1 node0"
|
68
94
|
end
|
69
95
|
it "should be able to build a haproxy file" do
|
70
|
-
@master.build_haproxy_file.should =~ "server node0
|
96
|
+
open(@master.build_haproxy_file.path).read.should =~ "server node0 127.0.0.1:#{Application.client_port}"
|
71
97
|
end
|
72
98
|
it "should be able to reconfigure the instances (working on two files a piece)" do
|
73
|
-
@master.
|
99
|
+
@master.should_receive(:remote_configure_instances).and_return true
|
74
100
|
@master.stub!(:number_of_unconfigured_nodes).and_return 1
|
75
|
-
@master.
|
101
|
+
@master.reconfigure_cloud_when_necessary
|
102
|
+
end
|
103
|
+
it "should return the number of unconfigured nodes when asked" do
|
104
|
+
@master.nodes.each {|node| node.stub!(:stack_installed?).and_return(true) unless node.master? }
|
105
|
+
@master.number_of_unconfigured_nodes.should == 1
|
106
|
+
end
|
107
|
+
it "should be able to return the size of the cloud" do
|
108
|
+
@master.nodes.size.should == 3
|
76
109
|
end
|
77
110
|
it "should be able to restart the running instances' services" do
|
78
111
|
@master.nodes.each {|a| a.should_receive(:restart_with_monit).and_return true }
|
79
112
|
@master.restart_running_instances_services
|
80
113
|
end
|
81
114
|
it "should be able to build a heartbeat auth file" do
|
82
|
-
open(@master.
|
115
|
+
open(@master.build_and_copy_heartbeat_authkeys_file.path).read.should =~ /1 md5/
|
83
116
|
end
|
84
|
-
describe "
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
117
|
+
describe "configuration" do
|
118
|
+
describe "sending configuration files" do
|
119
|
+
before(:each) do
|
120
|
+
Master.stub!(:new).and_return(@master)
|
121
|
+
end
|
122
|
+
it "should be able to build a heartbeat resources file for the specific node" do
|
123
|
+
open(Master.build_heartbeat_resources_file_for(@master.nodes.first).path).read.should =~ /node0 127/
|
124
|
+
end
|
125
|
+
it "should be able to build a heartbeat config file" do
|
126
|
+
open(Master.build_heartbeat_config_file_for(@master.nodes.first).path).read.should =~ /\nnode node0\nnode node1/
|
127
|
+
end
|
128
|
+
it "should be able to say if heartbeat is necessary with more than 1 server or not" do
|
129
|
+
Master.requires_heartbeat?.should == true
|
130
|
+
end
|
131
|
+
it "should be able to say that heartbeat is not necessary if there is 1 server" do
|
132
|
+
@master.stub!(:list_of_nonterminated_instances).and_return([
|
133
|
+
{:instance_id => "i-5849ba", :ip => "127.0.0.1", :status => "running"}
|
134
|
+
])
|
135
|
+
Master.requires_heartbeat?.should == false
|
136
|
+
end
|
137
|
+
it "should only install the stack on nodes that don't have it marked locally as installed" do
|
138
|
+
@master.nodes.each {|i| i.should_receive(:stack_installed?).and_return(true)}
|
139
|
+
@master.should_not_receive(:reconfigure_running_instances)
|
140
|
+
@master.reconfigure_cloud_when_necessary
|
141
|
+
end
|
142
|
+
it "should install the stack on all the nodes (because it needs reconfiguring) if there is any node that needs the stack" do
|
143
|
+
@master.nodes.first.should_receive(:stack_installed?).and_return(false)
|
144
|
+
@master.should_receive(:configure_cloud).once.and_return(true)
|
145
|
+
@master.reconfigure_cloud_when_necessary
|
146
|
+
end
|
147
|
+
describe "rsync'ing the files to the instances" do
|
148
|
+
it "should receive send_config_files_to_nodes after it builds the config files in the temp directory" do
|
149
|
+
@master.should_receive(:send_config_files_to_nodes).once
|
150
|
+
@master.build_and_send_config_files_in_temp_directory
|
151
|
+
end
|
152
|
+
it "should run_array_of_tasks(scp_tasks)" do
|
153
|
+
@master.should_receive(:run_array_of_tasks).and_return true
|
154
|
+
@master.build_and_send_config_files_in_temp_directory
|
155
|
+
end
|
156
|
+
it "should compile a list of files to rsync" do
|
157
|
+
@master.stub!(:run_array_of_tasks).and_return true
|
158
|
+
@master.rsync_tasks("#{@master.base_tmp_dir}", "tmp")[0].should =~ /rsync/
|
159
|
+
end
|
160
|
+
end
|
161
|
+
describe "remote configuration" do
|
162
|
+
before(:each) do
|
163
|
+
@master.stub!(:nodes).and_return [@instance]
|
164
|
+
end
|
165
|
+
it "should call remote_configure_instances when configuring" do
|
166
|
+
@master.should_receive(:remote_configure_instances).and_return true
|
167
|
+
@master.configure_cloud
|
168
|
+
end
|
169
|
+
it "should change the configuration script into an executable and run it" do
|
170
|
+
@master.should_receive(:run_array_of_tasks).and_return true
|
171
|
+
@master.remote_configure_instances
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
93
175
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
it "should only install the stack on nodes that don't have it marked locally as installed" do
|
104
|
-
@master.nodes.each {|i| i.should_receive(:stack_installed?).and_return(true)}
|
105
|
-
@master.should_not_receive(:reconfigure_running_instances)
|
106
|
-
@master.reconfigure_cloud_when_necessary
|
107
|
-
end
|
108
|
-
it "should install the stack on all the nodes (because it needs reconfiguring) if there is any node that needs the stack" do
|
109
|
-
@master.nodes.first.should_receive(:stack_installed?).and_return(false)
|
110
|
-
@master.should_receive(:reconfigure_running_instances).once.and_return(true)
|
111
|
-
@master.reconfigure_cloud_when_necessary
|
112
|
-
end
|
113
|
-
describe "with new configuration and installation (build scripts)" do
|
176
|
+
end
|
177
|
+
describe "installation" do
|
178
|
+
it "should not install on the instances if the application doesn't say it should" do
|
179
|
+
Application.stub!(:install_on_load?).and_return false
|
180
|
+
Provider.should_not_receive(:install_poolparty)
|
181
|
+
@master.install_cloud
|
182
|
+
end
|
183
|
+
describe "when asked" do
|
114
184
|
before(:each) do
|
115
|
-
|
185
|
+
Application.stub!(:install_on_load?).and_return true
|
186
|
+
Sprinkle::Script.stub!(:sprinkle).and_return true
|
187
|
+
@master.stub!(:execute_tasks).and_return true
|
188
|
+
end
|
189
|
+
it "should install on the instances if the application says it should" do
|
190
|
+
Provider.should_receive(:install_poolparty)
|
191
|
+
@master.install_cloud
|
116
192
|
end
|
117
|
-
it "should
|
118
|
-
@
|
119
|
-
|
193
|
+
it "should execute the remote tasks on all of the instances" do
|
194
|
+
@master.should_receive(:execute_tasks).and_return true
|
195
|
+
@master.install_cloud
|
120
196
|
end
|
121
|
-
|
122
|
-
|
197
|
+
describe "stubbing installation" do
|
198
|
+
before(:each) do
|
199
|
+
@master.stub!(:execute_tasks).and_return true
|
200
|
+
end
|
201
|
+
it "should install poolparty" do
|
202
|
+
Provider.should_receive(:install_poolparty).and_return true
|
203
|
+
Provider.should_receive(:install_userpackages).and_return true
|
204
|
+
@master.install_cloud
|
205
|
+
end
|
206
|
+
it "should install the user packages" do
|
207
|
+
Provider.should_receive(:install_poolparty).and_return true
|
208
|
+
Provider.should_receive(:install_userpackages).and_return true
|
209
|
+
@master.install_cloud
|
210
|
+
end
|
123
211
|
end
|
124
|
-
it "should be able to build_reconfigure_instances_script_for" do
|
125
|
-
str = open(Master.build_reconfigure_instances_script_for(@node)).read
|
126
|
-
str.should =~ /hostname -v node0/
|
127
|
-
str.should =~ /mkdir \/etc\/ha\.d\/resource\.d/
|
128
|
-
str.should =~ /pool\ maintain\ \-c \~\/\.config/
|
129
|
-
end
|
130
212
|
end
|
131
213
|
end
|
132
214
|
describe "displaying" do
|
@@ -134,6 +216,20 @@ describe "Master" do
|
|
134
216
|
@master.list.should =~ /CLOUD \(/
|
135
217
|
end
|
136
218
|
end
|
219
|
+
it "should be able to grab a list of the instances" do
|
220
|
+
@master.cloud_ips.should == %w(127.0.0.1 127.0.0.2 127.0.0.3)
|
221
|
+
end
|
222
|
+
describe "starting" do
|
223
|
+
before(:each) do
|
224
|
+
Kernel.stub!(:sleep).and_return true
|
225
|
+
end
|
226
|
+
it "should request to launch the minimum number of instances" do
|
227
|
+
Application.stub!(:minimum_instances).and_return 3
|
228
|
+
@master.stub!(:number_of_pending_and_running_instances).and_return 1
|
229
|
+
@master.should_receive(:request_launch_new_instances).with(2).and_return true
|
230
|
+
@master.launch_minimum_instances
|
231
|
+
end
|
232
|
+
end
|
137
233
|
describe "monitoring" do
|
138
234
|
it "should start the monitor when calling start_monitor!" do
|
139
235
|
@master.should_receive(:run_thread_loop).and_return(Proc.new {})
|
@@ -157,7 +253,7 @@ describe "Master" do
|
|
157
253
|
@master.contract?.should == false
|
158
254
|
end
|
159
255
|
it "should be able to say that it should contract" do
|
160
|
-
@master.stub!(:web).and_return(
|
256
|
+
@master.stub!(:web).and_return(31.2)
|
161
257
|
@master.stub!(:cpu).and_return(0.05)
|
162
258
|
|
163
259
|
@master.contract?.should == true
|
@@ -174,30 +270,158 @@ describe "Master" do
|
|
174
270
|
|
175
271
|
@master.expand?.should == true
|
176
272
|
end
|
273
|
+
describe "scaling" do
|
274
|
+
before(:each) do
|
275
|
+
Kernel.stub!(:sleep).and_return true
|
276
|
+
@master.stub!(:wait_for_all_instances_to_boot).and_return true
|
277
|
+
@master.stub!(:wait_for_all_instances_to_terminate).and_return true
|
278
|
+
end
|
279
|
+
it "should try to add a new instance" do
|
280
|
+
@master.should_receive(:add_instance_if_load_is_high).and_return true
|
281
|
+
@master.scale_cloud!
|
282
|
+
end
|
283
|
+
it "should try to terminate an instance" do
|
284
|
+
@master.should_receive(:terminate_instance_if_load_is_low).and_return true
|
285
|
+
@master.scale_cloud!
|
286
|
+
end
|
287
|
+
it "should try to grow the cloud by 1 node when asked" do
|
288
|
+
@master.should_receive(:request_launch_new_instances).once.and_return true
|
289
|
+
@master.should_receive(:configure_cloud).once.and_return true
|
290
|
+
@master.grow_by(1)
|
291
|
+
end
|
292
|
+
it "should try to shrink the cloud by 1 when asked" do
|
293
|
+
@master.should_receive(:request_termination_of_instance).and_return true
|
294
|
+
@master.should_receive(:configure_cloud).and_return true
|
295
|
+
@master.shrink_by(1)
|
296
|
+
end
|
297
|
+
end
|
177
298
|
end
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
299
|
+
describe "Configuration" do
|
300
|
+
before(:each) do
|
301
|
+
@instance = RemoteInstance.new
|
302
|
+
end
|
303
|
+
it "should be able to build the haproxy file" do
|
304
|
+
@master.build_haproxy_file
|
305
|
+
end
|
306
|
+
describe "by copying files to the poolpartytmp directory" do
|
307
|
+
it "should build and copy files to the tmp directory" do
|
308
|
+
@master.build_and_send_config_files_in_temp_directory
|
309
|
+
File.directory?(@master.base_tmp_dir).should == true
|
310
|
+
end
|
311
|
+
it "should copy the cloud_master_takeover script to the tmp directory" do
|
312
|
+
@master.should_receive(:get_config_file_for).at_least(1).and_return "true"
|
313
|
+
File.should_receive(:copy).exactly(3).and_return true
|
314
|
+
@master.build_and_send_config_files_in_temp_directory
|
315
|
+
end
|
316
|
+
describe "get configs" do
|
317
|
+
before(:each) do
|
318
|
+
@master.stub!(:user_dir).and_return("user")
|
319
|
+
@master.stub!(:root_dir).and_return("root")
|
320
|
+
end
|
321
|
+
it "should try to get the config file in the user directory before the root_dir" do
|
322
|
+
File.should_receive(:exists?).with("#{@master.user_dir}/config/config.yml").and_return true
|
323
|
+
@master.get_config_file_for("config.yml").should == "user/config/config.yml"
|
324
|
+
end
|
325
|
+
it "should try to get the config file in the root directory if it doesn't exist in the user directory" do
|
326
|
+
File.should_receive(:exists?).with("#{@master.user_dir}/config/config.yml").and_return false
|
327
|
+
@master.get_config_file_for("config.yml").should == "root/config/config.yml"
|
328
|
+
end
|
329
|
+
end
|
330
|
+
it "should copy the config file if it exists" do
|
331
|
+
Application.stub!(:config_file).and_return "config.yml"
|
332
|
+
File.stub!(:exists?).and_return true
|
333
|
+
File.should_receive(:copy).exactly(5).times.and_return true
|
334
|
+
@master.build_and_send_config_files_in_temp_directory
|
335
|
+
end
|
336
|
+
describe "with copy_config_files_in_directory_to_tmp_dir method" do
|
337
|
+
before(:each) do
|
338
|
+
@instance2 = RemoteInstance.new
|
339
|
+
@instance2.stub!(:ip).and_return "127.0.0.2"
|
340
|
+
Master.stub!(:new).and_return @master
|
341
|
+
@master.stub!(:nodes).and_return [@instance, @instance2]
|
342
|
+
end
|
343
|
+
it "should be able to clean up after itself" do
|
344
|
+
File.open("#{@master.base_tmp_dir}/test", "w+") {|f| f << "hello world"}
|
345
|
+
@master.cleanup_tmp_directory(nil)
|
346
|
+
File.file?("#{@master.base_tmp_dir}/test").should == false
|
347
|
+
end
|
348
|
+
it "should check to see if there is a directory in the user directory to grab the files from" do
|
349
|
+
File.stub!(:directory?).with("/Users/auser/Sites/work/citrusbyte/internal/gems/poolparty/pool/tmp/resource.d").and_return false
|
350
|
+
File.should_receive(:directory?).at_least(1).with("#{user_dir}/config/resource.d").at_least(1).and_return true
|
351
|
+
@master.copy_config_files_in_directory_to_tmp_dir("config/resource.d")
|
352
|
+
end
|
353
|
+
it "should copy all the files that are in the directory" do
|
354
|
+
Dir.stub!(:[]).and_return ["1","2","3"]
|
355
|
+
File.should_receive(:copy).exactly(3).times.and_return true
|
356
|
+
@master.copy_config_files_in_directory_to_tmp_dir("config/resource.d")
|
357
|
+
end
|
358
|
+
it "should copy all the resource.d files from the monit directory to the tmp directory" do
|
359
|
+
@master.stub!(:copy_config_files_in_directory_to_tmp_dir).with("config/resource.d").and_return true
|
360
|
+
@master.should_receive(:copy_config_files_in_directory_to_tmp_dir).at_least(1).with("config/monit.d").and_return true
|
361
|
+
@master.build_and_send_config_files_in_temp_directory
|
362
|
+
end
|
363
|
+
it "should build the authkeys file for haproxy" do
|
364
|
+
@master.should_receive(:build_and_copy_heartbeat_authkeys_file).and_return true
|
365
|
+
@master.build_and_send_config_files_in_temp_directory
|
366
|
+
end
|
367
|
+
it "should build the haproxy configuration file" do
|
368
|
+
@master.should_receive(:build_haproxy_file).and_return true
|
369
|
+
@master.build_and_send_config_files_in_temp_directory
|
370
|
+
end
|
371
|
+
it "should build the hosts file for nodes" do
|
372
|
+
@master.should_receive(:build_hosts_file_for).at_least(1).and_return true
|
373
|
+
@master.build_and_send_config_files_in_temp_directory
|
374
|
+
end
|
375
|
+
it "should build the ssh reconfigure script" do
|
376
|
+
@master.should_receive(:build_reconfigure_instances_script_for).at_least(1).and_return ""
|
377
|
+
@master.build_and_send_config_files_in_temp_directory
|
378
|
+
end
|
379
|
+
it "should be able to build the hosts file for the nodes" do
|
380
|
+
@master.build_and_send_config_files_in_temp_directory
|
381
|
+
end
|
382
|
+
it "should build global files" do
|
383
|
+
Master.should_receive(:build_user_global_files).once
|
384
|
+
@master.build_and_send_config_files_in_temp_directory
|
385
|
+
end
|
386
|
+
it "should build user node files" do
|
387
|
+
Master.should_receive(:build_user_node_files_for).at_least(1)
|
388
|
+
@master.build_and_send_config_files_in_temp_directory
|
389
|
+
end
|
390
|
+
describe "when the cloud requires heartbeat" do
|
391
|
+
before(:each) do
|
392
|
+
Master.stub!(:requires_heartbeat?).and_return true
|
393
|
+
end
|
394
|
+
it "should build the heartbeat configuration file" do
|
395
|
+
@master.should_receive(:build_heartbeat_config_file_for).at_least(1).and_return true
|
396
|
+
@master.build_and_send_config_files_in_temp_directory
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
describe "user define files" do
|
401
|
+
it "should have access to global_user_files as an array" do
|
402
|
+
Master.global_user_files.class.should == Array
|
403
|
+
end
|
404
|
+
it "should be able to add a global file to the array" do
|
405
|
+
Master.define_global_user_file(:box) {"box"}
|
406
|
+
Master.global_user_files.size.should == 1
|
407
|
+
end
|
408
|
+
it "should have access to user_node_files as an array" do
|
409
|
+
Master.user_node_files.class.should == Array
|
410
|
+
end
|
411
|
+
it "should be able to add a node file to the array" do
|
412
|
+
Master.define_node_user_file(:box) {|a| "#{a}.box"}
|
413
|
+
Master.user_node_files.size.should == 1
|
414
|
+
end
|
415
|
+
it "should write_to_file_for for each of the global_user_files" do
|
416
|
+
Master.should_receive(:write_to_file_for).once.and_return true
|
417
|
+
Master.build_user_global_files
|
418
|
+
end
|
419
|
+
it "should write_to_file_for for each of the user_node_files" do
|
420
|
+
Master.should_receive(:write_to_file_for).once.and_return true
|
421
|
+
Master.build_user_node_files_for(@instance)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
201
425
|
end
|
202
426
|
end
|
203
427
|
end
|