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.
Files changed (109) hide show
  1. data/CHANGELOG +12 -0
  2. data/Manifest +115 -0
  3. data/README.txt +140 -0
  4. data/Rakefile +27 -0
  5. data/bin/instance +61 -0
  6. data/bin/pool +62 -0
  7. data/config/cloud_master_takeover +17 -0
  8. data/config/create_proxy_ami.sh +582 -0
  9. data/config/haproxy.conf +29 -0
  10. data/config/heartbeat.conf +8 -0
  11. data/config/heartbeat_authkeys.conf +2 -0
  12. data/config/installers/ubuntu_install.sh +77 -0
  13. data/config/monit.conf +9 -0
  14. data/config/monit/haproxy.monit.conf +7 -0
  15. data/config/monit/nginx.monit.conf +0 -0
  16. data/config/nginx.conf +24 -0
  17. data/config/reconfigure_instances_script.sh +18 -0
  18. data/config/sample-config.yml +23 -0
  19. data/config/scp_instances_script.sh +12 -0
  20. data/lib/core/array.rb +13 -0
  21. data/lib/core/exception.rb +9 -0
  22. data/lib/core/float.rb +13 -0
  23. data/lib/core/hash.rb +11 -0
  24. data/lib/core/kernel.rb +12 -0
  25. data/lib/core/module.rb +22 -0
  26. data/lib/core/object.rb +18 -0
  27. data/lib/core/proc.rb +15 -0
  28. data/lib/core/string.rb +49 -0
  29. data/lib/core/time.rb +41 -0
  30. data/lib/modules/callback.rb +133 -0
  31. data/lib/modules/ec2_wrapper.rb +82 -0
  32. data/lib/modules/safe_instance.rb +31 -0
  33. data/lib/modules/vlad_override.rb +82 -0
  34. data/lib/poolparty.rb +105 -0
  35. data/lib/poolparty/application.rb +170 -0
  36. data/lib/poolparty/init.rb +6 -0
  37. data/lib/poolparty/master.rb +329 -0
  38. data/lib/poolparty/monitors.rb +13 -0
  39. data/lib/poolparty/monitors/cpu.rb +19 -0
  40. data/lib/poolparty/monitors/memory.rb +26 -0
  41. data/lib/poolparty/monitors/web.rb +23 -0
  42. data/lib/poolparty/optioner.rb +16 -0
  43. data/lib/poolparty/plugin.rb +43 -0
  44. data/lib/poolparty/plugin_manager.rb +67 -0
  45. data/lib/poolparty/provider.rb +2 -0
  46. data/lib/poolparty/provider/packages/essential.rb +6 -0
  47. data/lib/poolparty/provider/packages/git.rb +4 -0
  48. data/lib/poolparty/provider/packages/haproxy.rb +20 -0
  49. data/lib/poolparty/provider/packages/heartbeat.rb +4 -0
  50. data/lib/poolparty/provider/packages/monit.rb +6 -0
  51. data/lib/poolparty/provider/packages/rsync.rb +4 -0
  52. data/lib/poolparty/provider/packages/ruby.rb +37 -0
  53. data/lib/poolparty/provider/packages/s3fuse.rb +11 -0
  54. data/lib/poolparty/provider/provider.rb +60 -0
  55. data/lib/poolparty/remote_instance.rb +216 -0
  56. data/lib/poolparty/remoter.rb +106 -0
  57. data/lib/poolparty/remoting.rb +112 -0
  58. data/lib/poolparty/scheduler.rb +103 -0
  59. data/lib/poolparty/tasks.rb +29 -0
  60. data/lib/poolparty/tasks/cloud.rake +57 -0
  61. data/lib/poolparty/tasks/development.rake +38 -0
  62. data/lib/poolparty/tasks/ec2.rake +20 -0
  63. data/lib/poolparty/tasks/instance.rake +63 -0
  64. data/lib/poolparty/tasks/plugins.rake +30 -0
  65. data/lib/poolparty/tasks/server.rake +42 -0
  66. data/lib/poolparty/tmp.rb +46 -0
  67. data/lib/s3/s3_object_store_folders.rb +44 -0
  68. data/misc/basics_tutorial.txt +142 -0
  69. data/poolparty.gemspec +72 -0
  70. data/spec/application_spec.rb +39 -0
  71. data/spec/callback_spec.rb +194 -0
  72. data/spec/core_spec.rb +15 -0
  73. data/spec/helpers/ec2_mock.rb +44 -0
  74. data/spec/kernel_spec.rb +11 -0
  75. data/spec/master_spec.rb +203 -0
  76. data/spec/monitors/cpu_monitor_spec.rb +38 -0
  77. data/spec/monitors/memory_spec.rb +50 -0
  78. data/spec/monitors/misc_monitor_spec.rb +50 -0
  79. data/spec/monitors/web_spec.rb +39 -0
  80. data/spec/optioner_spec.rb +22 -0
  81. data/spec/plugin_manager_spec.rb +31 -0
  82. data/spec/plugin_spec.rb +101 -0
  83. data/spec/pool_binary_spec.rb +10 -0
  84. data/spec/poolparty_spec.rb +15 -0
  85. data/spec/provider_spec.rb +17 -0
  86. data/spec/remote_instance_spec.rb +149 -0
  87. data/spec/remoter_spec.rb +65 -0
  88. data/spec/remoting_spec.rb +84 -0
  89. data/spec/scheduler_spec.rb +75 -0
  90. data/spec/spec_helper.rb +39 -0
  91. data/spec/string_spec.rb +28 -0
  92. data/web/static/conf/nginx.conf +22 -0
  93. data/web/static/site/images/balloon.png +0 -0
  94. data/web/static/site/images/cb.png +0 -0
  95. data/web/static/site/images/clouds.png +0 -0
  96. data/web/static/site/images/railsconf_preso_img.png +0 -0
  97. data/web/static/site/index.html +71 -0
  98. data/web/static/site/javascripts/application.js +3 -0
  99. data/web/static/site/javascripts/corner.js +178 -0
  100. data/web/static/site/javascripts/jquery-1.2.6.pack.js +11 -0
  101. data/web/static/site/misc.html +42 -0
  102. data/web/static/site/storage/pool_party_presentation.pdf +0 -0
  103. data/web/static/site/stylesheets/application.css +100 -0
  104. data/web/static/site/stylesheets/reset.css +17 -0
  105. data/web/static/src/layouts/application.haml +25 -0
  106. data/web/static/src/pages/index.haml +25 -0
  107. data/web/static/src/pages/misc.haml +5 -0
  108. data/web/static/src/stylesheets/application.sass +100 -0
  109. 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
@@ -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
@@ -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