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.
Files changed (80) hide show
  1. data/CHANGELOG +8 -0
  2. data/README.txt +10 -10
  3. data/Rakefile +30 -21
  4. data/{web/static/site/images → assets}/clouds.png +0 -0
  5. data/bin/instance +39 -34
  6. data/bin/pool +44 -29
  7. data/bin/poolnotify +34 -0
  8. data/config/haproxy.conf +1 -1
  9. data/config/heartbeat_authkeys.conf +1 -1
  10. data/config/monit/haproxy.monit.conf +2 -1
  11. data/config/nginx.conf +1 -1
  12. data/config/reconfigure_instances_script.sh +28 -9
  13. data/config/sample-config.yml +1 -1
  14. data/lib/core/string.rb +3 -0
  15. data/lib/modules/ec2_wrapper.rb +47 -22
  16. data/lib/modules/file_writer.rb +38 -0
  17. data/lib/modules/sprinkle_overrides.rb +32 -0
  18. data/lib/modules/vlad_override.rb +5 -4
  19. data/lib/poolparty.rb +14 -10
  20. data/lib/poolparty/application.rb +33 -19
  21. data/lib/poolparty/master.rb +227 -105
  22. data/lib/poolparty/optioner.rb +8 -4
  23. data/lib/poolparty/plugin.rb +34 -4
  24. data/lib/poolparty/provider/packages/haproxy.rb +0 -15
  25. data/lib/poolparty/provider/packages/heartbeat.rb +1 -1
  26. data/lib/poolparty/provider/packages/ruby.rb +6 -6
  27. data/lib/poolparty/provider/packages/s3fuse.rb +9 -2
  28. data/lib/poolparty/provider/provider.rb +65 -25
  29. data/lib/poolparty/remote_instance.rb +95 -74
  30. data/lib/poolparty/remoter.rb +48 -37
  31. data/lib/poolparty/remoting.rb +41 -17
  32. data/lib/poolparty/scheduler.rb +4 -4
  33. data/lib/poolparty/tasks.rb +1 -1
  34. data/lib/poolparty/tasks/package.rake +53 -0
  35. data/lib/poolparty/tasks/plugins.rake +1 -1
  36. data/poolparty.gemspec +50 -58
  37. data/spec/application_spec.rb +28 -0
  38. data/spec/core_spec.rb +9 -0
  39. data/spec/ec2_wrapper_spec.rb +87 -0
  40. data/spec/file_writer_spec.rb +73 -0
  41. data/spec/files/describe_response +37 -0
  42. data/spec/files/multi_describe_response +69 -0
  43. data/spec/files/remote_desc_response +37 -0
  44. data/spec/helpers/ec2_mock.rb +3 -0
  45. data/spec/master_spec.rb +302 -78
  46. data/spec/monitors/cpu_monitor_spec.rb +2 -1
  47. data/spec/monitors/memory_spec.rb +1 -0
  48. data/spec/monitors/misc_monitor_spec.rb +1 -0
  49. data/spec/monitors/web_spec.rb +1 -0
  50. data/spec/optioner_spec.rb +12 -0
  51. data/spec/plugin_manager_spec.rb +10 -10
  52. data/spec/plugin_spec.rb +6 -3
  53. data/spec/pool_binary_spec.rb +3 -0
  54. data/spec/poolparty_spec.rb +12 -7
  55. data/spec/provider_spec.rb +1 -0
  56. data/spec/remote_instance_spec.rb +18 -18
  57. data/spec/remoter_spec.rb +4 -2
  58. data/spec/remoting_spec.rb +10 -2
  59. data/spec/scheduler_spec.rb +0 -6
  60. data/spec/spec_helper.rb +13 -0
  61. metadata +83 -52
  62. data/Manifest +0 -115
  63. data/lib/poolparty/tmp.rb +0 -46
  64. data/misc/basics_tutorial.txt +0 -142
  65. data/web/static/conf/nginx.conf +0 -22
  66. data/web/static/site/images/balloon.png +0 -0
  67. data/web/static/site/images/cb.png +0 -0
  68. data/web/static/site/images/railsconf_preso_img.png +0 -0
  69. data/web/static/site/index.html +0 -71
  70. data/web/static/site/javascripts/application.js +0 -3
  71. data/web/static/site/javascripts/corner.js +0 -178
  72. data/web/static/site/javascripts/jquery-1.2.6.pack.js +0 -11
  73. data/web/static/site/misc.html +0 -42
  74. data/web/static/site/storage/pool_party_presentation.pdf +0 -0
  75. data/web/static/site/stylesheets/application.css +0 -100
  76. data/web/static/site/stylesheets/reset.css +0 -17
  77. data/web/static/src/layouts/application.haml +0 -25
  78. data/web/static/src/pages/index.haml +0 -25
  79. data/web/static/src/pages/misc.haml +0 -5
  80. 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>
@@ -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 => "ip-127-0-0-1.aws.amazon.com", :status => "running"})
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 => "ip-127-0-0-1.aws.amazon.com", :status => "running"}])
29
+ [{:instance_id => "i-5849ba", :ip => "127.0.0.1", :status => "running"}])
26
30
 
27
- node = RemoteInstance.new({:instance_id => "i-5849ba", :ip => "ip-127-0-0-1.aws.amazon.com", :status => "running"})
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 => "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"}
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.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"
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(@master.nodes.first).should =~ "127.0.0.1 node0"
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 ip-127-0-0-1.aws.amazon.com:#{Application.client_port}"
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.nodes[0].should_receive(:configure).and_return true if @master.nodes[0].status =~ /running/
99
+ @master.should_receive(:remote_configure_instances).and_return true
74
100
  @master.stub!(:number_of_unconfigured_nodes).and_return 1
75
- @master.reconfigure_running_instances
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.build_heartbeat_authkeys_file).read.should =~ /1 md5/
115
+ open(@master.build_and_copy_heartbeat_authkeys_file.path).read.should =~ /1 md5/
83
116
  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/
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
- 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
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
- @node = @master.nodes.first
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 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)
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
- 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
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(30.2)
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
- 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
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