auser-poolparty 0.0.9 → 0.1.0

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 (40) hide show
  1. data/CHANGELOG +3 -2
  2. data/Rakefile +61 -4
  3. data/bin/instance +3 -1
  4. data/bin/pool +6 -2
  5. data/config/sample-config.yml +4 -4
  6. data/lib/core/object.rb +3 -0
  7. data/lib/helpers/plugin_spec_helper.rb +59 -0
  8. data/lib/modules/ec2_wrapper.rb +3 -1
  9. data/lib/modules/file_writer.rb +1 -1
  10. data/lib/modules/vlad_override.rb +83 -83
  11. data/lib/poolparty.rb +31 -13
  12. data/lib/poolparty/application.rb +22 -15
  13. data/lib/poolparty/init.rb +1 -1
  14. data/lib/poolparty/master.rb +41 -28
  15. data/lib/poolparty/monitors.rb +1 -3
  16. data/lib/poolparty/monitors/cpu.rb +7 -3
  17. data/lib/poolparty/monitors/memory.rb +14 -7
  18. data/lib/poolparty/monitors/web.rb +11 -5
  19. data/lib/poolparty/provider/packages/essential.rb +1 -1
  20. data/lib/poolparty/provider/packages/heartbeat.rb +1 -1
  21. data/lib/poolparty/provider/packages/ruby.rb +1 -10
  22. data/lib/poolparty/remote_instance.rb +5 -18
  23. data/lib/poolparty/remoter.rb +55 -4
  24. data/lib/poolparty/scheduler.rb +15 -25
  25. data/lib/poolparty/thread_pool.rb +94 -0
  26. data/poolparty.gemspec +9 -6
  27. data/spec/application_spec.rb +32 -13
  28. data/spec/callback_spec.rb +20 -1
  29. data/spec/file_writer_spec.rb +1 -0
  30. data/spec/kernel_spec.rb +13 -0
  31. data/spec/master_spec.rb +50 -20
  32. data/spec/monitors/cpu_monitor_spec.rb +1 -1
  33. data/spec/plugin_manager_spec.rb +9 -17
  34. data/spec/plugin_spec.rb +34 -34
  35. data/spec/poolparty_spec.rb +41 -1
  36. data/spec/remote_instance_spec.rb +5 -18
  37. data/spec/scheduler_spec.rb +7 -6
  38. data/spec/spec_helper.rb +8 -18
  39. metadata +19 -6
  40. data/lib/poolparty/tasks/package.rake +0 -53
@@ -1,6 +1,7 @@
1
1
  =begin rdoc
2
2
  Handle the remoting aspects of the remote_instances
3
3
  =end
4
+ require 'open4'
4
5
  module PoolParty
5
6
  module Remoter
6
7
  module ClassMethods
@@ -66,10 +67,8 @@ module PoolParty
66
67
  run_array_of_tasks arr
67
68
  end
68
69
 
69
- def run_now command
70
- unless command.empty?
71
- Kernel.system "#{self.class.ssh_string} #{self.ip} #{command.runnable}"
72
- end
70
+ def run_now command
71
+ run command unless command.empty?
73
72
  end
74
73
 
75
74
  def ssh_tasks;@ssh_tasks ||= [];end
@@ -107,6 +106,58 @@ module PoolParty
107
106
  def set_hosts(c=nil)
108
107
  end
109
108
 
109
+ # Nearly Directly from vlad
110
+ def run command, on=self
111
+ cmd = [self.class.ssh_string, on.ip].compact
112
+ result = []
113
+
114
+ commander = cmd.join(" ") << " \"#{command.runnable}\""
115
+
116
+ pid, inn, out, err = Open4::popen4(commander)
117
+
118
+ inn.sync = true
119
+ streams = [out, err]
120
+ out_stream = {
121
+ out => $stdout,
122
+ err => $stderr,
123
+ }
124
+
125
+ # Handle process termination ourselves
126
+ status = nil
127
+ Thread.start do
128
+ status = Process.waitpid2(pid).last
129
+ end
130
+
131
+ until streams.empty? do
132
+ # don't busy loop
133
+ selected, = select streams, nil, nil, 0.1
134
+
135
+ next if selected.nil? or selected.empty?
136
+
137
+ selected.each do |stream|
138
+ if stream.eof? then
139
+ streams.delete stream if status # we've quit, so no more writing
140
+ next
141
+ end
142
+
143
+ data = stream.readpartial(1024)
144
+ out_stream[stream].write data
145
+
146
+ if stream == err and data =~ /^Password:/ then
147
+ inn.puts sudo_password
148
+ data << "\n"
149
+ $stderr.write "\n"
150
+ end
151
+
152
+ result << data
153
+ end
154
+ end
155
+
156
+ PoolParty.message "execution failed with status #{status.exitstatus}: #{cmd.join ' '}" unless status.success?
157
+
158
+ result.join
159
+ end
160
+
110
161
  end
111
162
 
112
163
  def self.included(receiver)
@@ -9,28 +9,21 @@ module PoolParty
9
9
  end
10
10
  # Synchronize the running threaded tasks
11
11
  def run
12
- unless tasks.empty?
13
- self.class.synchronize do
14
- tasks.reject!{|a|
15
- begin
16
- a.run
17
- a.join
18
- rescue Exception => e
19
- puts "There was an error in the task: #{e} #{e.backtrace.join("\n")}"
20
- end
21
- true
22
- }
12
+ unless tasks.empty? && !running?
13
+ running = true
14
+ pool = ThreadPool.new(10)
15
+ tasks.reject! do |task|
16
+ pool.process {task.call}
23
17
  end
18
+ pool.join() # When all the tasks are done
19
+ running = false
24
20
  end
25
21
  end
22
+ def running?;@running == true;end
23
+ def running;@running ||= false;end
26
24
  # Add a task in a new thread
27
25
  def <<(a, *args)
28
- thread = Thread.new(a) do |task|
29
- Thread.stop rescue ""
30
- Thread.current[:callee] = task
31
- a.call args
32
- end
33
- tasks << thread
26
+ tasks << a
34
27
  end
35
28
  alias_method :push, :<<
36
29
  # In the ThreadSafeInstance
@@ -45,7 +38,7 @@ module PoolParty
45
38
  end
46
39
  # Add a task to the new threaded block
47
40
  def add_task(&blk)
48
- _tasker.push proc{blk.call}
41
+ _tasker.push blk
49
42
  end
50
43
  # Grab the polling_time
51
44
  def interval
@@ -74,16 +67,17 @@ module PoolParty
74
67
  end
75
68
  # Run the loop and wait the amount of time between running the tasks
76
69
  # You can send it daemonize => true and it will daemonize
77
- def run_thread_loop(opts={}, &block)
70
+ def run_thread_loop(opts={}, &blk)
78
71
  block = lambda {
79
72
  loop do
80
73
  begin
81
74
  yield if block_given?
75
+ add_task { Signal.trap("INT") { exit } }
82
76
  run_thread_list
77
+ PoolParty.message "Waiting: #{interval}"
83
78
  wait interval
84
- reset!
85
79
  rescue Exception => e
86
- puts "There was an error in the run_thread_loop: #{e}"
80
+ Process.kill("INT", Process.pid)
87
81
  end
88
82
  end
89
83
  }
@@ -94,10 +88,6 @@ module PoolParty
94
88
  def run_thread_list
95
89
  run_threads
96
90
  end
97
- # Reset
98
- def reset!
99
- cached_variables.each {|cached| cached = nil }
100
- end
101
91
 
102
92
  end
103
93
  end
@@ -0,0 +1,94 @@
1
+ module PoolParty
2
+ class ThreadPool
3
+ class Worker
4
+ def initialize
5
+ @mutex = Mutex.new
6
+ @thread = Thread.new do
7
+ while true
8
+ sleep 0.001
9
+ block = get_block
10
+ if block
11
+ block.call
12
+ reset_block
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ def get_block
19
+ @mutex.synchronize {@block}
20
+ end
21
+
22
+ def set_block(block)
23
+ @mutex.synchronize do
24
+ raise RuntimeError, "Thread already busy." if @block
25
+ @block = block
26
+ end
27
+ end
28
+
29
+ def reset_block
30
+ @mutex.synchronize {@block = nil}
31
+ end
32
+
33
+ def busy?
34
+ @mutex.synchronize {!@block.nil?}
35
+ end
36
+ end
37
+
38
+ attr_accessor :max_size
39
+ attr_reader :workers
40
+
41
+ def initialize(max_size = 10)
42
+ @max_size = max_size
43
+ @workers = []
44
+ @mutex = Mutex.new
45
+ end
46
+
47
+ def size
48
+ @mutex.synchronize {@workers.size}
49
+ end
50
+
51
+ def busy?
52
+ @mutex.synchronize {@workers.any? {|w| w.busy?}}
53
+ end
54
+
55
+ def join
56
+ sleep 0.01 while busy?
57
+ end
58
+
59
+ def process(&block)
60
+ while true
61
+ @mutex.synchronize do
62
+ worker = find_available_worker
63
+ if worker
64
+ return worker.set_block(block)
65
+ end
66
+ end
67
+ sleep 0.01
68
+ end
69
+ end
70
+
71
+ def find_available_worker
72
+ free_worker || create_worker
73
+ end
74
+
75
+ def wait_for_worker
76
+ while true
77
+ worker = find_available_worker
78
+ return worker if worker
79
+ sleep 0.01
80
+ end
81
+ end
82
+
83
+ def free_worker
84
+ @workers.each {|w| return w unless w.busy?}; nil
85
+ end
86
+
87
+ def create_worker
88
+ return nil if @workers.size >= @max_size
89
+ worker = Worker.new
90
+ @workers << worker
91
+ worker
92
+ end
93
+ end
94
+ end
@@ -1,21 +1,21 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{poolparty}
3
- s.version = "0.0.9"
3
+ s.version = "0.1.0"
4
4
 
5
5
  s.required_rubygems_version = Gem::Requirement.new("= 1.2") if s.respond_to? :required_rubygems_version=
6
6
  s.authors = ["Ari Lerner"]
7
7
  s.cert_chain = nil
8
- s.date = %q{2008-06-28}
8
+ s.date = %q{2008-07-02}
9
9
  s.description = %q{Run your entire application off EC2, managed and auto-scaling}
10
10
  s.email = %q{ari.lerner@citrusbyte.com}
11
11
  s.executables = ["instance", "pool", "poolnotify"]
12
- s.extra_rdoc_files = ["CHANGELOG", "README.txt", "bin", "bin/instance", "bin/pool", "bin/poolnotify", "lib", "lib/core", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/float.rb", "lib/core/hash.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/proc.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/modules", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/file_writer.rb", "lib/modules/safe_instance.rb", "lib/modules/sprinkle_overrides.rb", "lib/modules/vlad_override.rb", "lib/poolparty", "lib/poolparty.rb", "lib/poolparty/application.rb", "lib/poolparty/init.rb", "lib/poolparty/master.rb", "lib/poolparty/monitors", "lib/poolparty/monitors.rb", "lib/poolparty/monitors/cpu.rb", "lib/poolparty/monitors/memory.rb", "lib/poolparty/monitors/web.rb", "lib/poolparty/optioner.rb", "lib/poolparty/plugin.rb", "lib/poolparty/plugin_manager.rb", "lib/poolparty/provider", "lib/poolparty/provider.rb", "lib/poolparty/provider/packages", "lib/poolparty/provider/packages/essential.rb", "lib/poolparty/provider/packages/git.rb", "lib/poolparty/provider/packages/haproxy.rb", "lib/poolparty/provider/packages/heartbeat.rb", "lib/poolparty/provider/packages/monit.rb", "lib/poolparty/provider/packages/rsync.rb", "lib/poolparty/provider/packages/ruby.rb", "lib/poolparty/provider/packages/s3fuse.rb", "lib/poolparty/provider/provider.rb", "lib/poolparty/remote_instance.rb", "lib/poolparty/remoter.rb", "lib/poolparty/remoting.rb", "lib/poolparty/scheduler.rb", "lib/poolparty/tasks", "lib/poolparty/tasks.rb", "lib/poolparty/tasks/cloud.rake", "lib/poolparty/tasks/development.rake", "lib/poolparty/tasks/ec2.rake", "lib/poolparty/tasks/instance.rake", "lib/poolparty/tasks/package.rake", "lib/poolparty/tasks/plugins.rake", "lib/poolparty/tasks/server.rake", "lib/s3", "lib/s3/s3_object_store_folders.rb"]
13
- s.files = ["CHANGELOG", "README.txt", "Rakefile", "assets", "assets/clouds.png", "bin", "bin/instance", "bin/pool", "bin/poolnotify", "config", "config/cloud_master_takeover", "config/create_proxy_ami.sh", "config/haproxy.conf", "config/heartbeat.conf", "config/heartbeat_authkeys.conf", "config/installers", "config/installers/ubuntu_install.sh", "config/monit", "config/monit.conf", "config/monit/haproxy.monit.conf", "config/monit/nginx.monit.conf", "config/nginx.conf", "config/reconfigure_instances_script.sh", "config/sample-config.yml", "config/scp_instances_script.sh", "lib", "lib/core", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/float.rb", "lib/core/hash.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/proc.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/modules", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/file_writer.rb", "lib/modules/safe_instance.rb", "lib/modules/sprinkle_overrides.rb", "lib/modules/vlad_override.rb", "lib/poolparty", "lib/poolparty.rb", "lib/poolparty/application.rb", "lib/poolparty/init.rb", "lib/poolparty/master.rb", "lib/poolparty/monitors", "lib/poolparty/monitors.rb", "lib/poolparty/monitors/cpu.rb", "lib/poolparty/monitors/memory.rb", "lib/poolparty/monitors/web.rb", "lib/poolparty/optioner.rb", "lib/poolparty/plugin.rb", "lib/poolparty/plugin_manager.rb", "lib/poolparty/provider", "lib/poolparty/provider.rb", "lib/poolparty/provider/packages", "lib/poolparty/provider/packages/essential.rb", "lib/poolparty/provider/packages/git.rb", "lib/poolparty/provider/packages/haproxy.rb", "lib/poolparty/provider/packages/heartbeat.rb", "lib/poolparty/provider/packages/monit.rb", "lib/poolparty/provider/packages/rsync.rb", "lib/poolparty/provider/packages/ruby.rb", "lib/poolparty/provider/packages/s3fuse.rb", "lib/poolparty/provider/provider.rb", "lib/poolparty/remote_instance.rb", "lib/poolparty/remoter.rb", "lib/poolparty/remoting.rb", "lib/poolparty/scheduler.rb", "lib/poolparty/tasks", "lib/poolparty/tasks.rb", "lib/poolparty/tasks/cloud.rake", "lib/poolparty/tasks/development.rake", "lib/poolparty/tasks/ec2.rake", "lib/poolparty/tasks/instance.rake", "lib/poolparty/tasks/package.rake", "lib/poolparty/tasks/plugins.rake", "lib/poolparty/tasks/server.rake", "lib/s3", "lib/s3/s3_object_store_folders.rb", "spec", "spec/application_spec.rb", "spec/callback_spec.rb", "spec/core_spec.rb", "spec/ec2_wrapper_spec.rb", "spec/file_writer_spec.rb", "spec/files", "spec/files/describe_response", "spec/files/multi_describe_response", "spec/files/remote_desc_response", "spec/helpers", "spec/helpers/ec2_mock.rb", "spec/kernel_spec.rb", "spec/master_spec.rb", "spec/monitors", "spec/monitors/cpu_monitor_spec.rb", "spec/monitors/memory_spec.rb", "spec/monitors/misc_monitor_spec.rb", "spec/monitors/web_spec.rb", "spec/optioner_spec.rb", "spec/plugin_manager_spec.rb", "spec/plugin_spec.rb", "spec/pool_binary_spec.rb", "spec/poolparty_spec.rb", "spec/provider_spec.rb", "spec/remote_instance_spec.rb", "spec/remoter_spec.rb", "spec/remoting_spec.rb", "spec/scheduler_spec.rb", "spec/spec_helper.rb", "spec/string_spec.rb", "poolparty.gemspec"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "README.txt", "bin", "bin/instance", "bin/pool", "bin/poolnotify", "lib", "lib/core", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/float.rb", "lib/core/hash.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/proc.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/helpers", "lib/helpers/plugin_spec_helper.rb", "lib/modules", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/file_writer.rb", "lib/modules/safe_instance.rb", "lib/modules/sprinkle_overrides.rb", "lib/modules/vlad_override.rb", "lib/poolparty", "lib/poolparty.rb", "lib/poolparty/application.rb", "lib/poolparty/init.rb", "lib/poolparty/master.rb", "lib/poolparty/monitors", "lib/poolparty/monitors.rb", "lib/poolparty/monitors/cpu.rb", "lib/poolparty/monitors/memory.rb", "lib/poolparty/monitors/web.rb", "lib/poolparty/optioner.rb", "lib/poolparty/plugin.rb", "lib/poolparty/plugin_manager.rb", "lib/poolparty/provider", "lib/poolparty/provider.rb", "lib/poolparty/provider/packages", "lib/poolparty/provider/packages/essential.rb", "lib/poolparty/provider/packages/git.rb", "lib/poolparty/provider/packages/haproxy.rb", "lib/poolparty/provider/packages/heartbeat.rb", "lib/poolparty/provider/packages/monit.rb", "lib/poolparty/provider/packages/rsync.rb", "lib/poolparty/provider/packages/ruby.rb", "lib/poolparty/provider/packages/s3fuse.rb", "lib/poolparty/provider/provider.rb", "lib/poolparty/remote_instance.rb", "lib/poolparty/remoter.rb", "lib/poolparty/remoting.rb", "lib/poolparty/scheduler.rb", "lib/poolparty/tasks", "lib/poolparty/tasks.rb", "lib/poolparty/tasks/cloud.rake", "lib/poolparty/tasks/development.rake", "lib/poolparty/tasks/ec2.rake", "lib/poolparty/tasks/instance.rake", "lib/poolparty/tasks/plugins.rake", "lib/poolparty/tasks/server.rake", "lib/poolparty/thread_pool.rb", "lib/s3", "lib/s3/s3_object_store_folders.rb"]
13
+ s.files = ["CHANGELOG", "README.txt", "Rakefile", "assets", "assets/clouds.png", "bin", "bin/instance", "bin/pool", "bin/poolnotify", "config", "config/cloud_master_takeover", "config/create_proxy_ami.sh", "config/haproxy.conf", "config/heartbeat.conf", "config/heartbeat_authkeys.conf", "config/installers", "config/installers/ubuntu_install.sh", "config/monit", "config/monit.conf", "config/monit/haproxy.monit.conf", "config/monit/nginx.monit.conf", "config/nginx.conf", "config/reconfigure_instances_script.sh", "config/sample-config.yml", "config/scp_instances_script.sh", "lib", "lib/core", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/float.rb", "lib/core/hash.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/proc.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/helpers", "lib/helpers/plugin_spec_helper.rb", "lib/modules", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/file_writer.rb", "lib/modules/safe_instance.rb", "lib/modules/sprinkle_overrides.rb", "lib/modules/vlad_override.rb", "lib/poolparty", "lib/poolparty.rb", "lib/poolparty/application.rb", "lib/poolparty/init.rb", "lib/poolparty/master.rb", "lib/poolparty/monitors", "lib/poolparty/monitors.rb", "lib/poolparty/monitors/cpu.rb", "lib/poolparty/monitors/memory.rb", "lib/poolparty/monitors/web.rb", "lib/poolparty/optioner.rb", "lib/poolparty/plugin.rb", "lib/poolparty/plugin_manager.rb", "lib/poolparty/provider", "lib/poolparty/provider.rb", "lib/poolparty/provider/packages", "lib/poolparty/provider/packages/essential.rb", "lib/poolparty/provider/packages/git.rb", "lib/poolparty/provider/packages/haproxy.rb", "lib/poolparty/provider/packages/heartbeat.rb", "lib/poolparty/provider/packages/monit.rb", "lib/poolparty/provider/packages/rsync.rb", "lib/poolparty/provider/packages/ruby.rb", "lib/poolparty/provider/packages/s3fuse.rb", "lib/poolparty/provider/provider.rb", "lib/poolparty/remote_instance.rb", "lib/poolparty/remoter.rb", "lib/poolparty/remoting.rb", "lib/poolparty/scheduler.rb", "lib/poolparty/tasks", "lib/poolparty/tasks.rb", "lib/poolparty/tasks/cloud.rake", "lib/poolparty/tasks/development.rake", "lib/poolparty/tasks/ec2.rake", "lib/poolparty/tasks/instance.rake", "lib/poolparty/tasks/plugins.rake", "lib/poolparty/tasks/server.rake", "lib/poolparty/thread_pool.rb", "lib/s3", "lib/s3/s3_object_store_folders.rb", "spec", "spec/application_spec.rb", "spec/callback_spec.rb", "spec/core_spec.rb", "spec/ec2_wrapper_spec.rb", "spec/file_writer_spec.rb", "spec/files", "spec/files/describe_response", "spec/files/multi_describe_response", "spec/files/remote_desc_response", "spec/helpers", "spec/helpers/ec2_mock.rb", "spec/kernel_spec.rb", "spec/master_spec.rb", "spec/monitors", "spec/monitors/cpu_monitor_spec.rb", "spec/monitors/memory_spec.rb", "spec/monitors/misc_monitor_spec.rb", "spec/monitors/web_spec.rb", "spec/optioner_spec.rb", "spec/plugin_manager_spec.rb", "spec/plugin_spec.rb", "spec/pool_binary_spec.rb", "spec/poolparty_spec.rb", "spec/provider_spec.rb", "spec/remote_instance_spec.rb", "spec/remoter_spec.rb", "spec/remoting_spec.rb", "spec/scheduler_spec.rb", "spec/spec_helper.rb", "spec/string_spec.rb", "poolparty.gemspec"]
14
14
  s.has_rdoc = true
15
- s.homepage = %q{http://blog.citrusbyte.com}
15
+ s.homepage = %q{http://poolpartyrb.com}
16
16
  s.post_install_message = %q{
17
17
 
18
- Get ready to jump in the pool, you just installed PoolParty! (Updated at 05:15PM, 06/28/08)
18
+ Get ready to jump in the pool, you just installed PoolParty! (Updated at 02:12PM, 07/02/08)
19
19
 
20
20
  Please check out the documentation for any questions or check out the google groups at
21
21
  http://groups.google.com/group/poolpartyrb
@@ -45,6 +45,7 @@ Gem::Specification.new do |s|
45
45
  s.add_runtime_dependency(%q<git>, [">= 0"])
46
46
  s.add_runtime_dependency(%q<crafterm-sprinkle>, [">= 0"])
47
47
  s.add_runtime_dependency(%q<SystemTimer>, [">= 0"])
48
+ s.add_development_dependency(%q<echoe>, [">= 0"])
48
49
  else
49
50
  s.add_dependency(%q<aws-s3>, [">= 0"])
50
51
  s.add_dependency(%q<amazon-ec2>, [">= 0"])
@@ -52,6 +53,7 @@ Gem::Specification.new do |s|
52
53
  s.add_dependency(%q<git>, [">= 0"])
53
54
  s.add_dependency(%q<crafterm-sprinkle>, [">= 0"])
54
55
  s.add_dependency(%q<SystemTimer>, [">= 0"])
56
+ s.add_dependency(%q<echoe>, [">= 0"])
55
57
  end
56
58
  else
57
59
  s.add_dependency(%q<aws-s3>, [">= 0"])
@@ -60,5 +62,6 @@ Gem::Specification.new do |s|
60
62
  s.add_dependency(%q<git>, [">= 0"])
61
63
  s.add_dependency(%q<crafterm-sprinkle>, [">= 0"])
62
64
  s.add_dependency(%q<SystemTimer>, [">= 0"])
65
+ s.add_dependency(%q<echoe>, [">= 0"])
63
66
  end
64
67
  end
@@ -3,6 +3,7 @@ require File.dirname(__FILE__) + '/spec_helper'
3
3
  describe "Application" do
4
4
  before(:each) do
5
5
  stub_option_load
6
+ Application.reset!
6
7
  end
7
8
  it "should be able to send options in the Application.options" do
8
9
  options({:optparse => {:banner => "hi"}})
@@ -39,29 +40,47 @@ describe "Application" do
39
40
  it "should show the version as a string" do
40
41
  Application.version.class.should == String
41
42
  end
42
- it "should be able to start instances with the the key access information on the user-data" do
43
- Application.launching_user_data.should =~ /:access_key/
44
- Application.launching_user_data.should =~ /:secret_access_key/
45
- end
46
- it "should be able to pull out the access_key from the user data" do
47
- Application.local_user_data[:access_key].should == 3.14159
48
- end
49
- it "should overwrite the default_options when passing in to the instance data" do
50
- Application.stub!(:default_options).and_return({:access_key => 42})
51
- Application.options.access_key.should == 3.14159
43
+ describe "User data" do
44
+ before(:each) do
45
+ @str = ":access_key: 3.14159\n:secret_access_key: pi"
46
+ Application.options = nil
47
+ Application.stub!(:open).with("http://169.254.169.254/latest/user-data").and_return(@str)
48
+ @str.stub!(:read).and_return ":access_key: 3.14159\n:secret_access_key: pi"
49
+ Application.default_options.stub!(:merge!).with({})
50
+ Application.default_options.stub!(:merge!).with({:access_key => 3.14159, :secret_access_key => "pi"})
51
+ end
52
+ it "should try to load the user data into a yaml hash" do
53
+ YAML.should_receive(:load).with(":access_key: 3.14159\n:secret_access_key: pi")
54
+ Application.local_user_data
55
+ end
56
+ it "should be able to start instances with the the key access information on the user-data" do
57
+ Application.launching_user_data.should =~ /:access_key/
58
+ Application.launching_user_data.should =~ /:secret_access_key/
59
+ end
60
+ it "should be able to pull out the access_key from the user data" do
61
+ Application.local_user_data[:access_key].should == 3.14159
62
+ end
63
+ it "should be able tp pull out the secret_access_key from the user-data" do
64
+ Application.local_user_data[:secret_access_key].should == "pi"
65
+ end
66
+ it "should overwrite the default_options when passing in to the instance data" do
67
+ Application.stub!(:default_options).and_return({:access_key => 42})
68
+ Application.local_user_data
69
+ Application.options.access_key.should == 3.14159
70
+ end
52
71
  end
53
72
  it "should parse and use a config file if it is given for the options" do
54
- YAML.should_receive(:load).and_return({:config_file => "config/sample-config.yml"})
73
+ YAML.should_receive(:load).at_least(1).and_return({:config_file => "config/sample-config.yml"})
55
74
  Application.make_options(:config_file => "config/sample-config.yml")
56
75
  end
57
76
  it "should not read the config file if it is not passed and doesn't exist" do
58
77
  File.stub!(:file?).and_return false
59
- YAML.should_not_receive(:load)
78
+ YAML.should_not_receive(:load).with("config/config.yml")
60
79
  Application.make_options
61
80
  end
62
81
  it "should not read the config file if it is passed and doesn't exist" do
63
82
  File.stub!(:file?).and_return false
64
- YAML.should_not_receive(:load)
83
+ YAML.should_not_receive(:load).with("config/config.yml")
65
84
  Application.make_options(:config_file => "ted")
66
85
  end
67
86
  end
@@ -68,7 +68,7 @@ describe "Multiple callbacks" do
68
68
  end
69
69
  class OutsideClass
70
70
  def self.hello(caller)
71
- puts "hello"
71
+ "hello"
72
72
  end
73
73
  end
74
74
  class TestOutsideClass
@@ -191,4 +191,23 @@ describe "Variables on the plugin callbacker class" do
191
191
  BindingClass.new.print
192
192
  BindingClass.new.methods.include?("eviloutsideclass").should == true
193
193
  end
194
+ end
195
+ class DoubleClass
196
+ include Callbacks
197
+ before :print, :hello => "OutsideBindingClass"
198
+ after :print, :hello => "OutsideBindingClass"
199
+
200
+ def print
201
+ string
202
+ end
203
+ def string
204
+ @string ||= ""
205
+ end
206
+ end
207
+ describe "Chaining" do
208
+ it "should be able to call both a before and an after spec" do
209
+ @d = DoubleClass.new
210
+ @d.print
211
+ @d.string.should == "hellohello"
212
+ end
194
213
  end
@@ -5,6 +5,7 @@ class TestClass
5
5
  end
6
6
  describe "FileWriter" do
7
7
  before(:each) do
8
+ Application.reset!
8
9
  @instance = RemoteInstance.new
9
10
  @instance.stub!(:name).and_return "node0"
10
11
  @instance.stub!(:ip).and_return "127.0.0.1"
@@ -8,4 +8,17 @@ describe "Kernel extensions" do
8
8
  @host.should_receive(:sleep).once.and_return true
9
9
  @host.wait "10.seconds"
10
10
  end
11
+ end
12
+ describe "Object extensions" do
13
+ before(:each) do
14
+ @klass = Object.new
15
+ @klass.instance_eval <<-EOE
16
+ def hello
17
+ puts "hello"
18
+ end
19
+ EOE
20
+ end
21
+ it "should be able to get a list of the defined methods on the object" do
22
+ @klass.my_methods.should == ["hello"]
23
+ end
11
24
  end
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/helpers/ec2_mock'
2
3
 
3
4
  describe "Master" do
4
5
  before(:each) do
@@ -86,22 +87,25 @@ describe "Master" do
86
87
  it "should be able to get a specific node in the nodes from the master" do
87
88
  @master.get_node(2).instance_id.should == "i-5849bc"
88
89
  end
90
+ it "should be able to get the next node" do
91
+ @master.get_next_node(@instance).instance_id.should == "i-5849bb"
92
+ end
89
93
  it "should be able to build a hosts file" do
90
94
  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"
91
95
  end
92
96
  it "should be able to build a hosts file for a specific instance" do
93
- open(@master.build_hosts_file_for(@instance).path).read.should =~ "127.0.0.1 node0"
97
+ open(@master.build_hosts_file_for(@instance).path).read.should =~ /127\.0\.0\.1 node0/
94
98
  end
95
99
  it "should be able to build a haproxy file" do
96
- open(@master.build_haproxy_file.path).read.should =~ "server node0 127.0.0.1:#{Application.client_port}"
100
+ open(@master.build_haproxy_file.path).read.should =~ /server node0 127\.0\.0\.1:#{Application.client_port}/
97
101
  end
98
102
  it "should be able to reconfigure the instances (working on two files a piece)" do
99
103
  @master.should_receive(:remote_configure_instances).and_return true
100
104
  @master.stub!(:number_of_unconfigured_nodes).and_return 1
101
105
  @master.reconfigure_cloud_when_necessary
102
106
  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? }
107
+ it "should return the number of unconfigured nodes when asked" do
108
+ @master.nodes.each {|node| node.stub!(:stack_installed?).and_return(node.master? ? false : true) }
105
109
  @master.number_of_unconfigured_nodes.should == 1
106
110
  end
107
111
  it "should be able to return the size of the cloud" do
@@ -118,39 +122,50 @@ describe "Master" do
118
122
  describe "sending configuration files" do
119
123
  before(:each) do
120
124
  Master.stub!(:new).and_return(@master)
125
+ @master.stub!(:ssh)
126
+ @master.stub!(:scp)
127
+ @master.nodes.each do |node|
128
+ node.stub!(:ssh)
129
+ node.stub!(:scp)
130
+ node.stub!(:stack_installed?).and_return true
131
+ end
121
132
  end
122
133
  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/
134
+ open(@master.build_heartbeat_resources_file_for(@master.nodes.first).path).read.should == "node0 127.0.0.1\nnode1 127.0.0.2"
124
135
  end
125
136
  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/
137
+ open(@master.build_heartbeat_config_file_for(@master.nodes.first).path).read.should =~ /\nnode node0\nnode node1/
127
138
  end
128
139
  it "should be able to say if heartbeat is necessary with more than 1 server or not" do
129
140
  Master.requires_heartbeat?.should == true
130
141
  end
131
142
  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([
143
+ @master.stub!(:nodes).and_return([
133
144
  {:instance_id => "i-5849ba", :ip => "127.0.0.1", :status => "running"}
134
145
  ])
135
146
  Master.requires_heartbeat?.should == false
136
147
  end
137
148
  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)}
149
+ @master.nodes.each {|i| i.should_receive(:stack_installed?).at_least(1).and_return(true)}
139
150
  @master.should_not_receive(:reconfigure_running_instances)
140
151
  @master.reconfigure_cloud_when_necessary
141
152
  end
142
153
  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)
154
+ @master.nodes.first.should_receive(:stack_installed?).at_least(1).and_return(false)
144
155
  @master.should_receive(:configure_cloud).once.and_return(true)
145
156
  @master.reconfigure_cloud_when_necessary
146
157
  end
147
158
  describe "rsync'ing the files to the instances" do
159
+ it "should cleanup the tmp directory before sending configuration to the nodes" do
160
+ @master.should_receive(:cleanup_tmp_directory).once
161
+ @master.build_and_send_config_files_in_temp_directory
162
+ end
148
163
  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
164
+ @master.should_receive(:send_config_files_to_nodes).at_least(1)
150
165
  @master.build_and_send_config_files_in_temp_directory
151
166
  end
152
167
  it "should run_array_of_tasks(scp_tasks)" do
153
- @master.should_receive(:run_array_of_tasks).and_return true
168
+ @master.should_receive(:run_array_of_tasks).at_least(1).and_return true
154
169
  @master.build_and_send_config_files_in_temp_directory
155
170
  end
156
171
  it "should compile a list of files to rsync" do
@@ -184,14 +199,18 @@ describe "Master" do
184
199
  before(:each) do
185
200
  Application.stub!(:install_on_load?).and_return true
186
201
  Sprinkle::Script.stub!(:sprinkle).and_return true
187
- @master.stub!(:execute_tasks).and_return true
202
+ @master.stub!(:ssh).and_return true
203
+ @master.nodes.each do |node|
204
+ node.stub!(:run_now).and_return true
205
+ end
188
206
  end
189
207
  it "should install on the instances if the application says it should" do
208
+ Provider.stub!(:install_userpackages)
190
209
  Provider.should_receive(:install_poolparty)
191
210
  @master.install_cloud
192
211
  end
193
212
  it "should execute the remote tasks on all of the instances" do
194
- @master.should_receive(:execute_tasks).and_return true
213
+ @master.should_receive(:ssh).and_return true
195
214
  @master.install_cloud
196
215
  end
197
216
  describe "stubbing installation" do
@@ -244,30 +263,35 @@ describe "Master" do
244
263
  @master.should_receive(:request_termination_of_instance).and_return(true)
245
264
  @master.terminate_instance_if_load_is_low
246
265
  end
266
+ it "should launch the minimum_instances when the minimum aren't launched"
267
+ it "should reconfigure the cloud if it's necessary to do so"
268
+ it "should try to scale the cloud when monitoring"
269
+ it "should check the stats of the cloud"
247
270
  end
248
- describe "expanding and contracting" do
271
+ describe "expanding and contracting" do
249
272
  it "should be able to say that it should not contract" do
250
273
  @master.stub!(:web).and_return(10.2)
251
274
  @master.stub!(:cpu).and_return(0.32)
252
275
 
253
276
  @master.contract?.should == false
254
277
  end
255
- it "should be able to say that it should contract" do
256
- @master.stub!(:web).and_return(31.2)
257
- @master.stub!(:cpu).and_return(0.05)
258
-
278
+ it "should be able to say that it should contract" do
279
+ @master.should_receive(:cpu).once.and_return(0.05)
280
+ @master.should_receive(:web).once.and_return(35.2)
281
+
259
282
  @master.contract?.should == true
260
283
  end
261
284
  it "should be able to say that it should not expand if it shouldn't expand" do
262
285
  @master.stub!(:web).and_return(30.2)
263
286
  @master.stub!(:cpu).and_return(0.92)
264
-
287
+
265
288
  @master.expand?.should == false
266
289
  end
267
290
  it "should be able to say that it should expand if it should expand" do
268
291
  @master.stub!(:web).and_return(1.2)
269
292
  @master.stub!(:cpu).and_return(0.92)
270
-
293
+
294
+ @master.should_receive(:web).once.and_return(1.2)
271
295
  @master.expand?.should == true
272
296
  end
273
297
  describe "scaling" do
@@ -313,6 +337,12 @@ describe "Master" do
313
337
  File.should_receive(:copy).exactly(3).and_return true
314
338
  @master.build_and_send_config_files_in_temp_directory
315
339
  end
340
+ it "should tar the plugin_dir into the tmp directory" do
341
+ FileUtils.mkdir_p Application.plugin_dir rescue ""
342
+
343
+ Kernel.should_receive(:system).with("tar -czf #{@master.base_tmp_dir}/plugins.tar.gz #{File.basename(Application.plugin_dir)}").and_return true
344
+ @master.build_and_send_config_files_in_temp_directory
345
+ end
316
346
  describe "get configs" do
317
347
  before(:each) do
318
348
  @master.stub!(:user_dir).and_return("user")