dreadpiratepj-poolparty 0.0.8
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 +12 -0
- data/Manifest +115 -0
- data/README.txt +140 -0
- data/Rakefile +27 -0
- data/bin/instance +61 -0
- data/bin/pool +62 -0
- data/config/cloud_master_takeover +17 -0
- data/config/create_proxy_ami.sh +582 -0
- data/config/haproxy.conf +29 -0
- data/config/heartbeat.conf +8 -0
- data/config/heartbeat_authkeys.conf +2 -0
- data/config/installers/ubuntu_install.sh +77 -0
- data/config/monit.conf +9 -0
- data/config/monit/haproxy.monit.conf +7 -0
- data/config/monit/nginx.monit.conf +0 -0
- data/config/nginx.conf +24 -0
- data/config/reconfigure_instances_script.sh +18 -0
- data/config/sample-config.yml +23 -0
- data/config/scp_instances_script.sh +12 -0
- data/lib/core/array.rb +13 -0
- data/lib/core/exception.rb +9 -0
- data/lib/core/float.rb +13 -0
- data/lib/core/hash.rb +11 -0
- data/lib/core/kernel.rb +12 -0
- data/lib/core/module.rb +22 -0
- data/lib/core/object.rb +18 -0
- data/lib/core/proc.rb +15 -0
- data/lib/core/string.rb +49 -0
- data/lib/core/time.rb +41 -0
- data/lib/modules/callback.rb +133 -0
- data/lib/modules/ec2_wrapper.rb +82 -0
- data/lib/modules/safe_instance.rb +31 -0
- data/lib/modules/vlad_override.rb +82 -0
- data/lib/poolparty.rb +105 -0
- data/lib/poolparty/application.rb +170 -0
- data/lib/poolparty/init.rb +6 -0
- data/lib/poolparty/master.rb +329 -0
- data/lib/poolparty/monitors.rb +13 -0
- data/lib/poolparty/monitors/cpu.rb +19 -0
- data/lib/poolparty/monitors/memory.rb +26 -0
- data/lib/poolparty/monitors/web.rb +23 -0
- data/lib/poolparty/optioner.rb +16 -0
- data/lib/poolparty/plugin.rb +43 -0
- data/lib/poolparty/plugin_manager.rb +67 -0
- data/lib/poolparty/provider.rb +2 -0
- data/lib/poolparty/provider/packages/essential.rb +6 -0
- data/lib/poolparty/provider/packages/git.rb +4 -0
- data/lib/poolparty/provider/packages/haproxy.rb +20 -0
- data/lib/poolparty/provider/packages/heartbeat.rb +4 -0
- data/lib/poolparty/provider/packages/monit.rb +6 -0
- data/lib/poolparty/provider/packages/rsync.rb +4 -0
- data/lib/poolparty/provider/packages/ruby.rb +37 -0
- data/lib/poolparty/provider/packages/s3fuse.rb +11 -0
- data/lib/poolparty/provider/provider.rb +60 -0
- data/lib/poolparty/remote_instance.rb +216 -0
- data/lib/poolparty/remoter.rb +106 -0
- data/lib/poolparty/remoting.rb +112 -0
- data/lib/poolparty/scheduler.rb +103 -0
- data/lib/poolparty/tasks.rb +29 -0
- data/lib/poolparty/tasks/cloud.rake +57 -0
- data/lib/poolparty/tasks/development.rake +38 -0
- data/lib/poolparty/tasks/ec2.rake +20 -0
- data/lib/poolparty/tasks/instance.rake +63 -0
- data/lib/poolparty/tasks/plugins.rake +30 -0
- data/lib/poolparty/tasks/server.rake +42 -0
- data/lib/poolparty/tmp.rb +46 -0
- data/lib/s3/s3_object_store_folders.rb +44 -0
- data/misc/basics_tutorial.txt +142 -0
- data/poolparty.gemspec +72 -0
- data/spec/application_spec.rb +39 -0
- data/spec/callback_spec.rb +194 -0
- data/spec/core_spec.rb +15 -0
- data/spec/helpers/ec2_mock.rb +44 -0
- data/spec/kernel_spec.rb +11 -0
- data/spec/master_spec.rb +203 -0
- data/spec/monitors/cpu_monitor_spec.rb +38 -0
- data/spec/monitors/memory_spec.rb +50 -0
- data/spec/monitors/misc_monitor_spec.rb +50 -0
- data/spec/monitors/web_spec.rb +39 -0
- data/spec/optioner_spec.rb +22 -0
- data/spec/plugin_manager_spec.rb +31 -0
- data/spec/plugin_spec.rb +101 -0
- data/spec/pool_binary_spec.rb +10 -0
- data/spec/poolparty_spec.rb +15 -0
- data/spec/provider_spec.rb +17 -0
- data/spec/remote_instance_spec.rb +149 -0
- data/spec/remoter_spec.rb +65 -0
- data/spec/remoting_spec.rb +84 -0
- data/spec/scheduler_spec.rb +75 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/string_spec.rb +28 -0
- data/web/static/conf/nginx.conf +22 -0
- data/web/static/site/images/balloon.png +0 -0
- data/web/static/site/images/cb.png +0 -0
- data/web/static/site/images/clouds.png +0 -0
- data/web/static/site/images/railsconf_preso_img.png +0 -0
- data/web/static/site/index.html +71 -0
- data/web/static/site/javascripts/application.js +3 -0
- data/web/static/site/javascripts/corner.js +178 -0
- data/web/static/site/javascripts/jquery-1.2.6.pack.js +11 -0
- data/web/static/site/misc.html +42 -0
- data/web/static/site/storage/pool_party_presentation.pdf +0 -0
- data/web/static/site/stylesheets/application.css +100 -0
- data/web/static/site/stylesheets/reset.css +17 -0
- data/web/static/src/layouts/application.haml +25 -0
- data/web/static/src/pages/index.haml +25 -0
- data/web/static/src/pages/misc.haml +5 -0
- data/web/static/src/stylesheets/application.sass +100 -0
- metadata +260 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "Application" do
|
4
|
+
it "should be able to send options in the Application.options" do
|
5
|
+
options({:optparse => {:banner => "hi"}})
|
6
|
+
end
|
7
|
+
it "should have the root_dir defined" do
|
8
|
+
PoolParty.root_dir.should_not be_nil
|
9
|
+
end
|
10
|
+
it "should be able to call on the haproxy_config_file" do
|
11
|
+
Application.haproxy_config_file.should_not be_nil
|
12
|
+
end
|
13
|
+
it "should be able to find the client_port" do
|
14
|
+
Application.options.should_receive(:client_port).and_return(7788)
|
15
|
+
Application.client_port.should == 7788
|
16
|
+
end
|
17
|
+
it "should always have cloud_master_takeover in the managed services list" do
|
18
|
+
Application.master_managed_services.should =~ /cloud_master_takeover/
|
19
|
+
end
|
20
|
+
it "should be able to say it is in development mode if it is in dev mode" do
|
21
|
+
Application.stub!(:environment).and_return("development")
|
22
|
+
Application.development?.should == true
|
23
|
+
end
|
24
|
+
it "should be able to say it is in production if it is in production" do
|
25
|
+
Application.stub!(:environment).and_return("production")
|
26
|
+
Application.production?.should == true
|
27
|
+
end
|
28
|
+
it "should be able to say it's keypair path is in the $HOME/ directory" do
|
29
|
+
Application.stub!(:ec2_dir).and_return("~/.ec2")
|
30
|
+
Application.stub!(:keypair).and_return("poolparty")
|
31
|
+
Application.keypair_path.should == "~/.ec2/id_rsa-poolparty"
|
32
|
+
end
|
33
|
+
it "should be able to show the version of the gem" do
|
34
|
+
Application.version.should_not be_nil
|
35
|
+
end
|
36
|
+
it "should show the version as a string" do
|
37
|
+
Application.version.class.should == String
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class TestCallbacks
|
4
|
+
include Callbacks
|
5
|
+
attr_reader :str
|
6
|
+
|
7
|
+
before :world, :hello
|
8
|
+
after :world, :thanks
|
9
|
+
|
10
|
+
def hello(caller)
|
11
|
+
string << "hello "
|
12
|
+
end
|
13
|
+
def world
|
14
|
+
string << "world"
|
15
|
+
end
|
16
|
+
def thanks(caller)
|
17
|
+
string << ", thank you"
|
18
|
+
end
|
19
|
+
after :pop, :boom
|
20
|
+
def pop
|
21
|
+
string << "pop"
|
22
|
+
end
|
23
|
+
def boom(caller)
|
24
|
+
string << " goes boom"
|
25
|
+
end
|
26
|
+
def string
|
27
|
+
@str ||= String.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
describe "Callbacks" do
|
31
|
+
before(:each) do
|
32
|
+
@klass = TestCallbacks.new
|
33
|
+
end
|
34
|
+
it "should retain it's class identifier" do
|
35
|
+
@klass.class.should == TestCallbacks
|
36
|
+
end
|
37
|
+
it "should callback the method before the method runs" do
|
38
|
+
@klass.world.should == "hello world, thank you"
|
39
|
+
end
|
40
|
+
it "should callback the method before the method runs" do
|
41
|
+
@klass.pop.should == "pop goes boom"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
class TestMultipleCallbacks
|
45
|
+
include Callbacks
|
46
|
+
attr_reader :str
|
47
|
+
def hi(caller)
|
48
|
+
string << "hi, "
|
49
|
+
end
|
50
|
+
def hello(caller)
|
51
|
+
string << "hello "
|
52
|
+
end
|
53
|
+
def world
|
54
|
+
string << "world"
|
55
|
+
end
|
56
|
+
def string
|
57
|
+
@str ||= String.new
|
58
|
+
end
|
59
|
+
before :world, :hi, :hello
|
60
|
+
end
|
61
|
+
describe "Multiple callbacks" do
|
62
|
+
before(:each) do
|
63
|
+
@klass = TestMultipleCallbacks.new
|
64
|
+
end
|
65
|
+
it "should be able to have multiple callbacks on the same call" do
|
66
|
+
@klass.world.should == "hi, hello world"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
class OutsideClass
|
70
|
+
def self.hello(caller)
|
71
|
+
puts "hello"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
class TestOutsideClass
|
75
|
+
include Callbacks
|
76
|
+
before :world, {:hello => OutsideClass}
|
77
|
+
def world
|
78
|
+
"world"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
describe "Options" do
|
82
|
+
before(:each) do
|
83
|
+
@c = TestOutsideClass.new
|
84
|
+
end
|
85
|
+
it "should be able to pass external class options to the callback" do
|
86
|
+
OutsideClass.should_receive(:hello).and_return "hello"
|
87
|
+
@c.world
|
88
|
+
end
|
89
|
+
end
|
90
|
+
class BlockClass
|
91
|
+
include Callbacks
|
92
|
+
before :world do
|
93
|
+
string << "hello "
|
94
|
+
end
|
95
|
+
def world
|
96
|
+
string << "world"
|
97
|
+
end
|
98
|
+
def string
|
99
|
+
@string ||= ""
|
100
|
+
end
|
101
|
+
end
|
102
|
+
describe "Block callbacks" do
|
103
|
+
it "should call the block on the callback" do
|
104
|
+
BlockClass.new.world.should == "hello world"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
class BlockAndMethodClass
|
108
|
+
include Callbacks
|
109
|
+
before :world, :hi do
|
110
|
+
string << "hello "
|
111
|
+
end
|
112
|
+
def world
|
113
|
+
string << "world"
|
114
|
+
end
|
115
|
+
def hi(caller)
|
116
|
+
string << "hi, "
|
117
|
+
end
|
118
|
+
def string
|
119
|
+
@string ||= ""
|
120
|
+
end
|
121
|
+
end
|
122
|
+
describe "Block and method callbacks" do
|
123
|
+
it "should call the block on the callback and add the block" do
|
124
|
+
BlockAndMethodClass.new.world.should == "hi, hello world"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
class ExternalMethodCallClass
|
128
|
+
include Callbacks
|
129
|
+
before :world, :hello
|
130
|
+
after :hello, :peter
|
131
|
+
|
132
|
+
def world
|
133
|
+
string << "world"
|
134
|
+
end
|
135
|
+
def hello(caller)
|
136
|
+
string << "hello "
|
137
|
+
end
|
138
|
+
def peter(caller)
|
139
|
+
string << "peter "
|
140
|
+
end
|
141
|
+
def string
|
142
|
+
@string ||= ""
|
143
|
+
end
|
144
|
+
end
|
145
|
+
describe "External method callbacks inside a method" do
|
146
|
+
it "should call the block on the callback and add the " do
|
147
|
+
ExternalMethodCallClass.new.world.should == "hello peter world"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
class OutsideBindingClass
|
151
|
+
def hello(caller)
|
152
|
+
caller.string << "hello"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
class BindingClass
|
156
|
+
include Callbacks
|
157
|
+
before :world, :hello => "OutsideBindingClass"
|
158
|
+
def world
|
159
|
+
string << "#{@hello} world"
|
160
|
+
end
|
161
|
+
def string
|
162
|
+
@string ||= ""
|
163
|
+
end
|
164
|
+
end
|
165
|
+
describe "Methods" do
|
166
|
+
it "should have access to the local variables of the call" do
|
167
|
+
BindingClass.new.world.should == "hello world"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
class EvilOutsideClass
|
171
|
+
attr_reader :name
|
172
|
+
def get_name(caller)
|
173
|
+
@name = caller.hello
|
174
|
+
end
|
175
|
+
def show_name(caller)
|
176
|
+
@name
|
177
|
+
end
|
178
|
+
end
|
179
|
+
class BindingClass
|
180
|
+
include Callbacks
|
181
|
+
before :print, :get_name => "EvilOutsideClass"
|
182
|
+
def print
|
183
|
+
"hello"
|
184
|
+
end
|
185
|
+
def hello
|
186
|
+
"franke"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
describe "Variables on the plugin callbacker class" do
|
190
|
+
it "should have a new method of the class" do
|
191
|
+
BindingClass.new.print
|
192
|
+
BindingClass.new.methods.include?("eviloutsideclass").should == true
|
193
|
+
end
|
194
|
+
end
|
data/spec/core_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "Hash" do
|
4
|
+
it "should preserve the contents of the original hash when safe_merge'ing" do
|
5
|
+
a = {:a => "10", :b => "20"}
|
6
|
+
b = {:b => "30", :c => "40"}
|
7
|
+
a.safe_merge(b).should == {:a => "10", :b => "20", :c => "40"}
|
8
|
+
end
|
9
|
+
it "should preserve the contents of the original hash when safe_merge!'ing" do
|
10
|
+
a = {:a => "10", :b => "20"}
|
11
|
+
b = {:b => "30", :c => "40"}
|
12
|
+
a.safe_merge!(b)
|
13
|
+
a.should == {:a => "10", :b => "20", :c => "40"}
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PoolParty
|
2
|
+
class Master
|
3
|
+
def launch_new_instance!
|
4
|
+
letter = ("a".."z").to_a[instances.size] # For unique instance_ids
|
5
|
+
h = {:instance_id => "i-58ba56#{letter}", :ip => "ip-127-0-0-1.aws.amazonaws.com", :status => "pending", :launching_time => Time.now }
|
6
|
+
instances << h
|
7
|
+
Thread.new {wait 0.1;h[:status] = "running"} # Simulate the startup time
|
8
|
+
return h
|
9
|
+
end
|
10
|
+
# Shutdown the instance by instance_id
|
11
|
+
def terminate_instance!(instance_id)
|
12
|
+
instances.select {|a| a[:instance_id] == instance_id}[0][:status] = "terminating"
|
13
|
+
end
|
14
|
+
# Instance description
|
15
|
+
def describe_instance(id)
|
16
|
+
item = instances.select {|a| a[:instance_id] == id}[0]
|
17
|
+
EC2ResponseObject.get_hash_from_response(item)
|
18
|
+
end
|
19
|
+
# Get instance by id
|
20
|
+
def get_instance_by_id(id)
|
21
|
+
get_instances_description.select {|a| a.instance_id == id}[0] rescue nil
|
22
|
+
end
|
23
|
+
# Get the s3 description for the response in a hash format
|
24
|
+
def get_instances_description
|
25
|
+
instances
|
26
|
+
end
|
27
|
+
# Fake the ec2 connection
|
28
|
+
def ec2
|
29
|
+
@ec2 ||= EC2::Base.new(:access_key_id => "not a key", :secret_access_key => "not a key")
|
30
|
+
end
|
31
|
+
# Some basic instances, not totally necessary
|
32
|
+
def instances
|
33
|
+
@instances ||= []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
class RemoteInstance
|
37
|
+
def ssh(l="")
|
38
|
+
"true"
|
39
|
+
end
|
40
|
+
def scp(s,d,o={})
|
41
|
+
"true"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/kernel_spec.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "Kernel extensions" do
|
4
|
+
before(:each) do
|
5
|
+
@host = Master.new
|
6
|
+
end
|
7
|
+
it "should eval the string into time" do
|
8
|
+
@host.should_receive(:sleep).once.and_return true
|
9
|
+
@host.wait "10.seconds"
|
10
|
+
end
|
11
|
+
end
|
data/spec/master_spec.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "Master" do
|
4
|
+
before(:each) do
|
5
|
+
Kernel.stub!(:system).and_return true
|
6
|
+
Kernel.stub!(:exec).and_return true
|
7
|
+
Kernel.stub!(:sleep).and_return true # WHy wait?
|
8
|
+
|
9
|
+
Application.options.stub!(:contract_when).and_return("web > 30.0\n cpu < 0.10")
|
10
|
+
Application.options.stub!(:expand_when).and_return("web < 3.0\n cpu > 0.80")
|
11
|
+
@master = Master.new
|
12
|
+
end
|
13
|
+
it "should launch the first instances and set the first as the master and the rest as slaves" do
|
14
|
+
Application.stub!(:minimum_instances).and_return(1)
|
15
|
+
Application.stub!(:verbose).and_return(false) # Hide messages
|
16
|
+
Master.stub!(:new).and_return(@master)
|
17
|
+
|
18
|
+
@master.stub!(:number_of_running_instances).and_return(0);
|
19
|
+
@master.stub!(:number_of_pending_instances).and_return(0);
|
20
|
+
@master.stub!(:wait).and_return true
|
21
|
+
|
22
|
+
@master.should_receive(:launch_new_instance!).and_return(
|
23
|
+
{:instance_id => "i-5849ba", :ip => "ip-127-0-0-1.aws.amazon.com", :status => "running"})
|
24
|
+
@master.stub!(:list_of_nonterminated_instances).and_return(
|
25
|
+
[{:instance_id => "i-5849ba", :ip => "ip-127-0-0-1.aws.amazon.com", :status => "running"}])
|
26
|
+
|
27
|
+
node = RemoteInstance.new({:instance_id => "i-5849ba", :ip => "ip-127-0-0-1.aws.amazon.com", :status => "running"})
|
28
|
+
node.stub!(:scp).and_return "true"
|
29
|
+
node.stub!(:ssh).and_return "true"
|
30
|
+
|
31
|
+
@master.stub!(:number_of_pending_instances).and_return(0)
|
32
|
+
@master.stub!(:get_node).with(0).and_return node
|
33
|
+
@master.start_cloud!
|
34
|
+
|
35
|
+
@master.nodes.first.instance_id.should == "i-5849ba"
|
36
|
+
end
|
37
|
+
describe "with stubbed instances" do
|
38
|
+
before(:each) do
|
39
|
+
@master.stub!(:list_of_nonterminated_instances).and_return([
|
40
|
+
{:instance_id => "i-5849ba", :ip => "ip-127-0-0-1.aws.amazon.com", :status => "running"},
|
41
|
+
{:instance_id => "i-5849bb", :ip => "ip-127-0-0-2.aws.amazon.com", :status => "running"},
|
42
|
+
{:instance_id => "i-5849bc", :ip => "ip-127-0-0-3.aws.amazon.com", :status => "pending"}
|
43
|
+
])
|
44
|
+
Kernel.stub!(:exec).and_return true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be able to go through the instances and assign them numbers" do
|
48
|
+
i = 0
|
49
|
+
@master.nodes.each do |node|
|
50
|
+
node.number.should == i
|
51
|
+
i += 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
it "should be able to say that the master is the master" do
|
55
|
+
@master.nodes.first.master?.should == true
|
56
|
+
end
|
57
|
+
it "should be able to say that the slave is not a master" do
|
58
|
+
@master.nodes[1].master?.should == false
|
59
|
+
end
|
60
|
+
it "should be able to get a specific node in the nodes from the master" do
|
61
|
+
@master.get_node(2).instance_id.should == "i-5849bc"
|
62
|
+
end
|
63
|
+
it "should be able to build a hosts file" do
|
64
|
+
open(@master.build_hosts_file.path).read.should == "ip-127-0-0-1.aws.amazon.com node0\nip-127-0-0-2.aws.amazon.com node1\nip-127-0-0-3.aws.amazon.com node2"
|
65
|
+
end
|
66
|
+
it "should be able to build a hosts file for a specific instance" do
|
67
|
+
@master.build_hosts_file_for(@master.nodes.first).should =~ "127.0.0.1 node0"
|
68
|
+
end
|
69
|
+
it "should be able to build a haproxy file" do
|
70
|
+
@master.build_haproxy_file.should =~ "server node0 ip-127-0-0-1.aws.amazon.com:#{Application.client_port}"
|
71
|
+
end
|
72
|
+
it "should be able to reconfigure the instances (working on two files a piece)" do
|
73
|
+
@master.nodes[0].should_receive(:configure).and_return true if @master.nodes[0].status =~ /running/
|
74
|
+
@master.stub!(:number_of_unconfigured_nodes).and_return 1
|
75
|
+
@master.reconfigure_running_instances
|
76
|
+
end
|
77
|
+
it "should be able to restart the running instances' services" do
|
78
|
+
@master.nodes.each {|a| a.should_receive(:restart_with_monit).and_return true }
|
79
|
+
@master.restart_running_instances_services
|
80
|
+
end
|
81
|
+
it "should be able to build a heartbeat auth file" do
|
82
|
+
open(@master.build_heartbeat_authkeys_file).read.should =~ /1 md5/
|
83
|
+
end
|
84
|
+
describe "configuring" do
|
85
|
+
before(:each) do
|
86
|
+
Master.stub!(:new).and_return(@master)
|
87
|
+
end
|
88
|
+
it "should be able to build a heartbeat resources file for the specific node" do
|
89
|
+
Master.build_heartbeat_resources_file_for(@master.nodes.first).should =~ /node0 ip-127/
|
90
|
+
end
|
91
|
+
it "should be able to build a heartbeat config file" do
|
92
|
+
Master.build_heartbeat_config_file_for(@master.nodes.first).should =~ /\nnode node0\nnode node1/
|
93
|
+
end
|
94
|
+
it "should be able to say if heartbeat is necessary with more than 1 server or not" do
|
95
|
+
Master.requires_heartbeat?.should == true
|
96
|
+
end
|
97
|
+
it "should be able to say that heartbeat is not necessary if there is 1 server" do
|
98
|
+
@master.stub!(:list_of_nonterminated_instances).and_return([
|
99
|
+
{:instance_id => "i-5849ba", :ip => "ip-127-0-0-1.aws.amazon.com", :status => "running"}
|
100
|
+
])
|
101
|
+
Master.requires_heartbeat?.should == false
|
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
|
114
|
+
before(:each) do
|
115
|
+
@node = @master.nodes.first
|
116
|
+
end
|
117
|
+
it "should be able to build_scp_instances_script_for" do
|
118
|
+
@node.should_receive(:scp_string).exactly(10).times.and_return("true")
|
119
|
+
Master.build_scp_instances_script_for(@node)
|
120
|
+
end
|
121
|
+
it "should be able to build_scp_instances_script_for and contain scp 10 times" do
|
122
|
+
open(Master.build_scp_instances_script_for(@node)).read.scan(/scp/).size.should == 10
|
123
|
+
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
|
+
end
|
131
|
+
end
|
132
|
+
describe "displaying" do
|
133
|
+
it "should be able to list the cloud instances" do
|
134
|
+
@master.list.should =~ /CLOUD \(/
|
135
|
+
end
|
136
|
+
end
|
137
|
+
describe "monitoring" do
|
138
|
+
it "should start the monitor when calling start_monitor!" do
|
139
|
+
@master.should_receive(:run_thread_loop).and_return(Proc.new {})
|
140
|
+
@master.start_monitor!
|
141
|
+
end
|
142
|
+
it "should request to launch a new instance" do
|
143
|
+
@master.should_receive(:add_instance_if_load_is_high).and_return(true)
|
144
|
+
@master.add_instance_if_load_is_high
|
145
|
+
end
|
146
|
+
it "should request to terminate a non-master instance if the load" do
|
147
|
+
@master.should_receive(:contract?).and_return(true)
|
148
|
+
@master.should_receive(:request_termination_of_instance).and_return(true)
|
149
|
+
@master.terminate_instance_if_load_is_low
|
150
|
+
end
|
151
|
+
end
|
152
|
+
describe "expanding and contracting" do
|
153
|
+
it "should be able to say that it should not contract" do
|
154
|
+
@master.stub!(:web).and_return(10.2)
|
155
|
+
@master.stub!(:cpu).and_return(0.32)
|
156
|
+
|
157
|
+
@master.contract?.should == false
|
158
|
+
end
|
159
|
+
it "should be able to say that it should contract" do
|
160
|
+
@master.stub!(:web).and_return(30.2)
|
161
|
+
@master.stub!(:cpu).and_return(0.05)
|
162
|
+
|
163
|
+
@master.contract?.should == true
|
164
|
+
end
|
165
|
+
it "should be able to say that it should not expand if it shouldn't expand" do
|
166
|
+
@master.stub!(:web).and_return(30.2)
|
167
|
+
@master.stub!(:cpu).and_return(0.92)
|
168
|
+
|
169
|
+
@master.expand?.should == false
|
170
|
+
end
|
171
|
+
it "should be able to say that it should expand if it should expand" do
|
172
|
+
@master.stub!(:web).and_return(1.2)
|
173
|
+
@master.stub!(:cpu).and_return(0.92)
|
174
|
+
|
175
|
+
@master.expand?.should == true
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
describe "Configuration" do
|
180
|
+
it "should be able to build the haproxy file" do
|
181
|
+
@master.build_haproxy_file
|
182
|
+
end
|
183
|
+
end
|
184
|
+
describe "Singleton methods" do
|
185
|
+
before(:each) do
|
186
|
+
@master = Master.new
|
187
|
+
@instance = RemoteInstance.new
|
188
|
+
@blk = Proc.new {puts "new"}
|
189
|
+
Master.stub!(:new).once.and_return @master
|
190
|
+
end
|
191
|
+
it "should be able to run with_nodes" do
|
192
|
+
Master.should_receive(:new).once.and_return @master
|
193
|
+
@master.should_receive(:nodes).once.and_return []
|
194
|
+
Master.with_nodes &@blk
|
195
|
+
end
|
196
|
+
it "should run the block on each node" do
|
197
|
+
collection = [@instance]
|
198
|
+
@master.should_receive(:nodes).once.and_return collection
|
199
|
+
collection.should_receive(:each).once
|
200
|
+
Master.with_nodes &@blk
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|