auser-poolparty 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|