auser-poolparty 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +12 -0
- data/Manifest +115 -0
- data/README.txt +140 -0
- data/Rakefile +27 -0
- data/bin/instance +61 -0
- data/bin/pool +62 -0
- data/config/cloud_master_takeover +17 -0
- data/config/create_proxy_ami.sh +582 -0
- data/config/haproxy.conf +29 -0
- data/config/heartbeat.conf +8 -0
- data/config/heartbeat_authkeys.conf +2 -0
- data/config/installers/ubuntu_install.sh +77 -0
- data/config/monit/haproxy.monit.conf +7 -0
- data/config/monit/nginx.monit.conf +0 -0
- data/config/monit.conf +9 -0
- data/config/nginx.conf +24 -0
- data/config/reconfigure_instances_script.sh +18 -0
- data/config/sample-config.yml +23 -0
- data/config/scp_instances_script.sh +12 -0
- data/lib/core/array.rb +13 -0
- data/lib/core/exception.rb +9 -0
- data/lib/core/float.rb +13 -0
- data/lib/core/hash.rb +11 -0
- data/lib/core/kernel.rb +12 -0
- data/lib/core/module.rb +22 -0
- data/lib/core/object.rb +18 -0
- data/lib/core/proc.rb +15 -0
- data/lib/core/string.rb +49 -0
- data/lib/core/time.rb +41 -0
- data/lib/modules/callback.rb +133 -0
- data/lib/modules/ec2_wrapper.rb +82 -0
- data/lib/modules/safe_instance.rb +31 -0
- data/lib/modules/vlad_override.rb +82 -0
- data/lib/poolparty/application.rb +170 -0
- data/lib/poolparty/init.rb +6 -0
- data/lib/poolparty/master.rb +329 -0
- data/lib/poolparty/monitors/cpu.rb +19 -0
- data/lib/poolparty/monitors/memory.rb +26 -0
- data/lib/poolparty/monitors/web.rb +23 -0
- data/lib/poolparty/monitors.rb +13 -0
- data/lib/poolparty/optioner.rb +16 -0
- data/lib/poolparty/plugin.rb +43 -0
- data/lib/poolparty/plugin_manager.rb +67 -0
- data/lib/poolparty/provider/packages/essential.rb +6 -0
- data/lib/poolparty/provider/packages/git.rb +4 -0
- data/lib/poolparty/provider/packages/haproxy.rb +20 -0
- data/lib/poolparty/provider/packages/heartbeat.rb +4 -0
- data/lib/poolparty/provider/packages/monit.rb +6 -0
- data/lib/poolparty/provider/packages/rsync.rb +4 -0
- data/lib/poolparty/provider/packages/ruby.rb +37 -0
- data/lib/poolparty/provider/packages/s3fuse.rb +11 -0
- data/lib/poolparty/provider/provider.rb +60 -0
- data/lib/poolparty/provider.rb +2 -0
- data/lib/poolparty/remote_instance.rb +216 -0
- data/lib/poolparty/remoter.rb +106 -0
- data/lib/poolparty/remoting.rb +112 -0
- data/lib/poolparty/scheduler.rb +103 -0
- data/lib/poolparty/tasks/cloud.rake +57 -0
- data/lib/poolparty/tasks/development.rake +38 -0
- data/lib/poolparty/tasks/ec2.rake +20 -0
- data/lib/poolparty/tasks/instance.rake +63 -0
- data/lib/poolparty/tasks/plugins.rake +30 -0
- data/lib/poolparty/tasks/server.rake +42 -0
- data/lib/poolparty/tasks.rb +29 -0
- data/lib/poolparty/tmp.rb +46 -0
- data/lib/poolparty.rb +105 -0
- data/lib/s3/s3_object_store_folders.rb +44 -0
- data/misc/basics_tutorial.txt +142 -0
- data/poolparty.gemspec +72 -0
- data/spec/application_spec.rb +39 -0
- data/spec/callback_spec.rb +194 -0
- data/spec/core_spec.rb +15 -0
- data/spec/helpers/ec2_mock.rb +44 -0
- data/spec/kernel_spec.rb +11 -0
- data/spec/master_spec.rb +203 -0
- data/spec/monitors/cpu_monitor_spec.rb +38 -0
- data/spec/monitors/memory_spec.rb +50 -0
- data/spec/monitors/misc_monitor_spec.rb +50 -0
- data/spec/monitors/web_spec.rb +39 -0
- data/spec/optioner_spec.rb +22 -0
- data/spec/plugin_manager_spec.rb +31 -0
- data/spec/plugin_spec.rb +101 -0
- data/spec/pool_binary_spec.rb +10 -0
- data/spec/poolparty_spec.rb +15 -0
- data/spec/provider_spec.rb +17 -0
- data/spec/remote_instance_spec.rb +149 -0
- data/spec/remoter_spec.rb +65 -0
- data/spec/remoting_spec.rb +84 -0
- data/spec/scheduler_spec.rb +75 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/string_spec.rb +28 -0
- data/web/static/conf/nginx.conf +22 -0
- data/web/static/site/images/balloon.png +0 -0
- data/web/static/site/images/cb.png +0 -0
- data/web/static/site/images/clouds.png +0 -0
- data/web/static/site/images/railsconf_preso_img.png +0 -0
- data/web/static/site/index.html +71 -0
- data/web/static/site/javascripts/application.js +3 -0
- data/web/static/site/javascripts/corner.js +178 -0
- data/web/static/site/javascripts/jquery-1.2.6.pack.js +11 -0
- data/web/static/site/misc.html +42 -0
- data/web/static/site/storage/pool_party_presentation.pdf +0 -0
- data/web/static/site/stylesheets/application.css +100 -0
- data/web/static/site/stylesheets/reset.css +17 -0
- data/web/static/src/layouts/application.haml +25 -0
- data/web/static/src/pages/index.haml +25 -0
- data/web/static/src/pages/misc.haml +5 -0
- data/web/static/src/stylesheets/application.sass +100 -0
- metadata +260 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
extend self
|
|
3
|
+
|
|
4
|
+
class Remoting
|
|
5
|
+
include PoolParty
|
|
6
|
+
include Ec2Wrapper
|
|
7
|
+
include Scheduler
|
|
8
|
+
|
|
9
|
+
# == GENERAL METHODS
|
|
10
|
+
# == LISTING
|
|
11
|
+
# List all the running instances associated with this account
|
|
12
|
+
def list_of_running_instances
|
|
13
|
+
list_of_nonterminated_instances.select {|a| a[:status] =~ /running/}
|
|
14
|
+
end
|
|
15
|
+
# Get a list of the pending instances
|
|
16
|
+
def list_of_pending_instances
|
|
17
|
+
list_of_nonterminated_instances.select {|a| a[:status] =~ /pending/}
|
|
18
|
+
end
|
|
19
|
+
# list of shutting down instances
|
|
20
|
+
def list_of_terminating_instances
|
|
21
|
+
list_of_nonterminated_instances.select {|a| a[:status] =~ /shutting/}
|
|
22
|
+
end
|
|
23
|
+
# list all the nonterminated instances
|
|
24
|
+
def list_of_nonterminated_instances
|
|
25
|
+
list_of_instances.reject {|a| a[:status] =~ /terminated/}
|
|
26
|
+
end
|
|
27
|
+
# List the instances, regardless of their states
|
|
28
|
+
def list_of_instances
|
|
29
|
+
get_instances_description
|
|
30
|
+
end
|
|
31
|
+
# Get number of pending instances
|
|
32
|
+
def number_of_pending_instances
|
|
33
|
+
list_of_pending_instances.size
|
|
34
|
+
end
|
|
35
|
+
# get the number of running instances
|
|
36
|
+
def number_of_running_instances
|
|
37
|
+
list_of_running_instances.size
|
|
38
|
+
end
|
|
39
|
+
# get the number of pending and running instances
|
|
40
|
+
def number_of_pending_and_running_instances
|
|
41
|
+
number_of_running_instances + number_of_pending_instances
|
|
42
|
+
end
|
|
43
|
+
# == LAUNCHING
|
|
44
|
+
# Request to launch a new instance
|
|
45
|
+
def request_launch_new_instance
|
|
46
|
+
if can_start_a_new_instance?
|
|
47
|
+
request_launch_one_instance_at_a_time
|
|
48
|
+
return true
|
|
49
|
+
else
|
|
50
|
+
return false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
# Can we start a new instance?
|
|
54
|
+
def can_start_a_new_instance?
|
|
55
|
+
maximum_number_of_instances_are_not_running?
|
|
56
|
+
end
|
|
57
|
+
# Are the maximum number of instances running?
|
|
58
|
+
def maximum_number_of_instances_are_not_running?
|
|
59
|
+
list_of_running_instances.size < Application.maximum_instances
|
|
60
|
+
end
|
|
61
|
+
# Request to launch a number of instances
|
|
62
|
+
def request_launch_new_instances(num=1)
|
|
63
|
+
out = []
|
|
64
|
+
num.times {out << request_launch_one_instance_at_a_time}
|
|
65
|
+
out
|
|
66
|
+
end
|
|
67
|
+
# Launch one instance at a time
|
|
68
|
+
def request_launch_one_instance_at_a_time
|
|
69
|
+
reset!
|
|
70
|
+
while !number_of_pending_instances.zero?
|
|
71
|
+
wait "5.seconds"
|
|
72
|
+
reset!
|
|
73
|
+
end
|
|
74
|
+
return launch_new_instance!
|
|
75
|
+
end
|
|
76
|
+
# == SHUTDOWN
|
|
77
|
+
# Terminate all running instances
|
|
78
|
+
def request_termination_of_running_instances
|
|
79
|
+
list_of_running_instances.each {|a| terminate_instance!(a[:instance_id])}
|
|
80
|
+
end
|
|
81
|
+
# Request termination of all instances regardless of their state (includes pending instances)
|
|
82
|
+
def request_termination_of_all_instances
|
|
83
|
+
get_instances_description.each {|a| terminate_instance!(a[:instance_id])}
|
|
84
|
+
end
|
|
85
|
+
# Terminate instance by id
|
|
86
|
+
def request_termination_of_instance(id)
|
|
87
|
+
if can_shutdown_an_instance?
|
|
88
|
+
terminate_instance! id
|
|
89
|
+
return true
|
|
90
|
+
else
|
|
91
|
+
return false
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
# Can we shutdown an instance?
|
|
95
|
+
def can_shutdown_an_instance?
|
|
96
|
+
minimum_number_of_instances_are_running?
|
|
97
|
+
end
|
|
98
|
+
# Are the minimum number of instances running?
|
|
99
|
+
def minimum_number_of_instances_are_running?
|
|
100
|
+
list_of_running_instances.size > Application.minimum_instances
|
|
101
|
+
end
|
|
102
|
+
# Get the cached running_instances
|
|
103
|
+
def running_instances
|
|
104
|
+
@running_instances ||= update_instance_values
|
|
105
|
+
end
|
|
106
|
+
# Update the instance values
|
|
107
|
+
def update_instance_values
|
|
108
|
+
@running_instances = list_of_running_instances.collect {|a| RemoteInstance.new(a) }.sort
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
extend self
|
|
3
|
+
# Schedule tasks container
|
|
4
|
+
class ScheduleTasks
|
|
5
|
+
include ThreadSafeInstance
|
|
6
|
+
# Initialize tasks array and run
|
|
7
|
+
def tasks
|
|
8
|
+
@_tasks ||= []
|
|
9
|
+
end
|
|
10
|
+
# Synchronize the running threaded tasks
|
|
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
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
# Add a task in a new thread
|
|
27
|
+
def <<(a, *args)
|
|
28
|
+
thread = Thread.new(a) do |task|
|
|
29
|
+
Thread.stop
|
|
30
|
+
Thread.current[:callee] = task
|
|
31
|
+
a.call args
|
|
32
|
+
end
|
|
33
|
+
tasks << thread
|
|
34
|
+
end
|
|
35
|
+
alias_method :push, :<<
|
|
36
|
+
# In the ThreadSafeInstance
|
|
37
|
+
make_safe :<<
|
|
38
|
+
end
|
|
39
|
+
# Scheduler class
|
|
40
|
+
module Scheduler
|
|
41
|
+
attr_reader :tasker
|
|
42
|
+
# Get the tasks or ScheduleTasks
|
|
43
|
+
def _tasker
|
|
44
|
+
@_tasker ||= ScheduleTasks.new
|
|
45
|
+
end
|
|
46
|
+
# Add a task to the new threaded block
|
|
47
|
+
def add_task(&blk)
|
|
48
|
+
_tasker.push proc{blk.call}
|
|
49
|
+
end
|
|
50
|
+
# Grab the polling_time
|
|
51
|
+
def interval
|
|
52
|
+
@interval ||= Application.polling_time
|
|
53
|
+
end
|
|
54
|
+
# Run the threads
|
|
55
|
+
def run_threads
|
|
56
|
+
_tasker.run
|
|
57
|
+
end
|
|
58
|
+
alias_method :run_tasks, :run_threads
|
|
59
|
+
# Daemonize the process
|
|
60
|
+
def daemonize
|
|
61
|
+
PoolParty.message "Daemonizing..."
|
|
62
|
+
|
|
63
|
+
pid = fork do
|
|
64
|
+
Signal.trap('HUP', 'IGNORE') # Don't die upon logout
|
|
65
|
+
File.open("/dev/null", "r+") do |devnull|
|
|
66
|
+
$stdout.reopen(devnull)
|
|
67
|
+
$stderr.reopen(devnull)
|
|
68
|
+
$stdin.reopen(devnull) unless @use_stdin
|
|
69
|
+
end
|
|
70
|
+
yield if block_given?
|
|
71
|
+
end
|
|
72
|
+
Process.detach(pid)
|
|
73
|
+
pid
|
|
74
|
+
end
|
|
75
|
+
# Run the loop and wait the amount of time between running the tasks
|
|
76
|
+
# You can send it daemonize => true and it will daemonize
|
|
77
|
+
def run_thread_loop(opts={}, &block)
|
|
78
|
+
block = lambda {
|
|
79
|
+
loop do
|
|
80
|
+
begin
|
|
81
|
+
run_thread_list(&block)
|
|
82
|
+
wait interval
|
|
83
|
+
reset!
|
|
84
|
+
rescue Exception => e
|
|
85
|
+
puts "There was an error in the run_thread_loop: #{e}"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
}
|
|
89
|
+
# Run the tasks
|
|
90
|
+
opts[:daemonize] ? daemonize(&block) : block.call
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def run_thread_list
|
|
94
|
+
yield if block_given?
|
|
95
|
+
run_threads
|
|
96
|
+
end
|
|
97
|
+
# Reset
|
|
98
|
+
def reset!
|
|
99
|
+
cached_variables.each {|cached| cached = nil }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Cloud tasks
|
|
2
|
+
namespace(:cloud) do
|
|
3
|
+
# Setup
|
|
4
|
+
task :init do
|
|
5
|
+
setup_application
|
|
6
|
+
raise Exception.new("You must specify your access_key and secret_access_key") unless Application.access_key && Application.secret_access_key
|
|
7
|
+
end
|
|
8
|
+
# Install the stack on all of the nodes
|
|
9
|
+
desc "Prepare all servers"
|
|
10
|
+
task :prepare => :init do
|
|
11
|
+
PoolParty::Master.new.nodes.each do |node|
|
|
12
|
+
node.install
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
# Start the cloud
|
|
16
|
+
desc "Start the cloud"
|
|
17
|
+
task :start => :init do
|
|
18
|
+
PoolParty::Master.new.start_cloud!
|
|
19
|
+
end
|
|
20
|
+
# Reload the cloud with the new updated data
|
|
21
|
+
desc "Reload all instances with updated data"
|
|
22
|
+
task :reload => :init do
|
|
23
|
+
PoolParty::Master.new.nodes.each do |node|
|
|
24
|
+
node.configure
|
|
25
|
+
node.restart_with_monit
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
# List the cloud
|
|
29
|
+
desc "List cloud"
|
|
30
|
+
task :list => :init do
|
|
31
|
+
PoolParty::Master.new.list
|
|
32
|
+
end
|
|
33
|
+
# Shutdown the cloud
|
|
34
|
+
desc "Shutdown the entire cloud"
|
|
35
|
+
task :shutdown => :init do
|
|
36
|
+
PoolParty::Master.new.request_termination_of_all_instances
|
|
37
|
+
end
|
|
38
|
+
# Watch the cloud and scale it if necessary
|
|
39
|
+
desc "Watch the cloud and maintain it"
|
|
40
|
+
task :scale => :init do
|
|
41
|
+
begin
|
|
42
|
+
PoolParty::Master.new.scale_cloud!
|
|
43
|
+
rescue Exception => e
|
|
44
|
+
puts "There was an error scaling the cloud: #{e}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
# Maintain the cloud in a background process
|
|
49
|
+
desc "Maintain the cloud (run on the master)"
|
|
50
|
+
task :maintain => :init do
|
|
51
|
+
begin
|
|
52
|
+
PoolParty::Master.new.start_monitor!
|
|
53
|
+
rescue Exception => e
|
|
54
|
+
puts "There was an error starting the monitor: #{e}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
namespace(:dev) do
|
|
2
|
+
task :init do
|
|
3
|
+
setup_application
|
|
4
|
+
run "mkdir ~/.ec2 >/dev/null 2>/dev/null" unless File.directory?("~/.ec2")
|
|
5
|
+
end
|
|
6
|
+
# Setup a basic development environment for the user
|
|
7
|
+
desc "Setup development environment specify the config_file"
|
|
8
|
+
task :setup => [:init] do
|
|
9
|
+
keyfilename = ".#{Application.keypair}_pool_keys"
|
|
10
|
+
run <<-EOR
|
|
11
|
+
echo 'export AWS_ACCESS_KEY_ID=\"#{Application.access_key}\"' > $HOME/#{keyfilename}
|
|
12
|
+
echo 'export AWS_SECRET_ACCESS_ID=\"#{Application.secret_access_key}\"' >> $HOME/#{keyfilename}
|
|
13
|
+
echo 'export EC2_HOME=\"#{Application.ec2_dir}\"' >> $HOME/#{keyfilename}
|
|
14
|
+
echo 'export KEYPAIR_NAME=\"#{Application.keypair}\"' >> $HOME/#{keyfilename}
|
|
15
|
+
echo 'export CONFIG_FILE=\"#{Application.config_file}\"' >> $HOME/#{keyfilename}
|
|
16
|
+
echo 'export EC2_PRIVATE_KEY=`ls ~/.ec2/#{Application.keypair}/pk-*.pem`;' >> $HOME/#{keyfilename}
|
|
17
|
+
echo 'export EC2_CERT=`ls ~/.ec2/#{Application.keypair}/cert-*.pem`;' >> $HOME/#{keyfilename}
|
|
18
|
+
source $HOME/#{keyfilename}
|
|
19
|
+
EOR
|
|
20
|
+
end
|
|
21
|
+
desc "Generate a keypair"
|
|
22
|
+
task :setup_keypair => :init do
|
|
23
|
+
unless File.file?(Application.keypair_path)
|
|
24
|
+
Application.keypair = "cloud"
|
|
25
|
+
run <<-EOR
|
|
26
|
+
ec2-add-keypair cloud > #{Application.keypair_path}
|
|
27
|
+
chmod 600 #{Application.keypair_path}
|
|
28
|
+
EOR
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
desc "Authorize base ports for application"
|
|
32
|
+
task :authorize_ports => :init do
|
|
33
|
+
run <<-EOR
|
|
34
|
+
ec2-authorize -p 22 default
|
|
35
|
+
ec2-authorize -p 80 default
|
|
36
|
+
EOR
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
namespace(:ec2) do
|
|
2
|
+
task :init do
|
|
3
|
+
Application.options
|
|
4
|
+
end
|
|
5
|
+
# Start a new instance in the cloud
|
|
6
|
+
desc "Add and start an instance to the pool"
|
|
7
|
+
task :start_new_instance => [:init] do
|
|
8
|
+
puts PoolParty::Remoting.new.launch_new_instance!
|
|
9
|
+
end
|
|
10
|
+
# Stop all the instances via command-line
|
|
11
|
+
desc "Stop all running instances"
|
|
12
|
+
task :stop_running_instances => [:init] do
|
|
13
|
+
Thread.new {`ec2-describe-instances | grep INSTANCE | grep running | awk '{print $2}' | xargs ec2-terminate-instances`}
|
|
14
|
+
end
|
|
15
|
+
# Reboot the instances via commandline
|
|
16
|
+
desc "Restart all running instances"
|
|
17
|
+
task :restart_running_instances => [:init] do
|
|
18
|
+
Thread.new {`ec2-describe-instances | grep INSTANCE | grep running | awk '{print $2}' | xargs ec2-reboot-instances`}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
namespace(:instance) do
|
|
2
|
+
# Find the instance we want to deal with
|
|
3
|
+
# interface can be: num=0, i=0, inst=0, 0
|
|
4
|
+
# defaults to the master instance (0)
|
|
5
|
+
task :init do
|
|
6
|
+
num = (ENV['num'] || ENV["i"] || ENV["inst"] || ARGV.shift || 0).to_i
|
|
7
|
+
raise Exception.new("Please set the number of the instance (i.e. num=1, i=1, or as an argument)") unless num
|
|
8
|
+
@node = PoolParty::Master.new.get_node(num)
|
|
9
|
+
end
|
|
10
|
+
# Ssh into the node
|
|
11
|
+
desc "Remotely login to the remote instance"
|
|
12
|
+
task :ssh => [:init] do
|
|
13
|
+
@node.ssh
|
|
14
|
+
end
|
|
15
|
+
desc "Send a file to the remote instance"
|
|
16
|
+
task :exec => :init do
|
|
17
|
+
@node.ssh ENV['cmd']
|
|
18
|
+
end
|
|
19
|
+
# Send a file to the remote instance
|
|
20
|
+
# as designated by src='' and dest=''
|
|
21
|
+
desc "Send a file to the remote instance"
|
|
22
|
+
task :scp => :init do
|
|
23
|
+
@node.scp ENV['src'], ENV['dest']
|
|
24
|
+
end
|
|
25
|
+
# Execute a command on the remote instance as designated
|
|
26
|
+
# by cmd=''
|
|
27
|
+
desc "Execute cmd on a remote instance"
|
|
28
|
+
task :exec => [:init] do
|
|
29
|
+
cmd = ENV['cmd'] || "ls -l"
|
|
30
|
+
puts @node.ssh(cmd.runnable)
|
|
31
|
+
end
|
|
32
|
+
# Restart all the services monitored by monit
|
|
33
|
+
desc "Restart all the services"
|
|
34
|
+
task :reload => [:init] do
|
|
35
|
+
@node.restart_with_monit
|
|
36
|
+
end
|
|
37
|
+
# Start all the services monitored by monit
|
|
38
|
+
desc "Start all services"
|
|
39
|
+
task :load => [:init] do
|
|
40
|
+
@node.start_with_monit
|
|
41
|
+
end
|
|
42
|
+
# Stop the services monitored by monit
|
|
43
|
+
desc "Stop all services"
|
|
44
|
+
task :stop => [:init] do
|
|
45
|
+
@node.stop_with_monit
|
|
46
|
+
end
|
|
47
|
+
# Install the required services on this node
|
|
48
|
+
desc "Install stack on this node"
|
|
49
|
+
task :install => :init do
|
|
50
|
+
@node.install
|
|
51
|
+
end
|
|
52
|
+
# Turnoff this instance
|
|
53
|
+
desc "Teardown instance"
|
|
54
|
+
task :shutdown => :init do
|
|
55
|
+
`ec2-terminate-instances #{@node.instance_id}`
|
|
56
|
+
end
|
|
57
|
+
# Configure this node and start the services
|
|
58
|
+
desc "Configure the stack on this node"
|
|
59
|
+
task :configure => :init do
|
|
60
|
+
@node.configure
|
|
61
|
+
@node.restart_with_monit
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
namespace(:plugin) do
|
|
2
|
+
task :init do
|
|
3
|
+
@command = ARGV.shift # Get rid of the command
|
|
4
|
+
@name = (ENV['location'] || ENV["l"] || ARGV.shift)
|
|
5
|
+
unless @name
|
|
6
|
+
puts <<-EOM
|
|
7
|
+
Usage:
|
|
8
|
+
rake #{@command} location
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
rake #{@command} git://github.com/auser/pool-party-plugins.git
|
|
12
|
+
|
|
13
|
+
Check the help does for more information how to install a plugin
|
|
14
|
+
http://poolpartyrb.com
|
|
15
|
+
|
|
16
|
+
EOM
|
|
17
|
+
exit
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
desc "Install a plugin from a git repository"
|
|
21
|
+
task :install => :init do |command|
|
|
22
|
+
PoolParty::PluginManager.install_plugin @name
|
|
23
|
+
end
|
|
24
|
+
desc "Remove an installed plugin"
|
|
25
|
+
task :remove => :init do |command|
|
|
26
|
+
PoolParty::PluginManager.remove_plugin @name
|
|
27
|
+
end
|
|
28
|
+
rule "" do |t|
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Tasks to be run on the server
|
|
2
|
+
namespace(:server) do
|
|
3
|
+
task :init do
|
|
4
|
+
PoolParty::Coordinator.init(false)
|
|
5
|
+
end
|
|
6
|
+
# bundle, upload and register your bundle on the server
|
|
7
|
+
desc "Bundle, upload and register your ami"
|
|
8
|
+
task :all => [:bundle, :upload, :register] do
|
|
9
|
+
puts "== your ami is ready"
|
|
10
|
+
end
|
|
11
|
+
# Cleanup the /mnt directory
|
|
12
|
+
desc "Clean the /mnt directory"
|
|
13
|
+
task :clean_mnt do
|
|
14
|
+
`rm -rf /mnt/image* img*`
|
|
15
|
+
end
|
|
16
|
+
# Before we can bundle, we have to make sure we have the cert and pk files
|
|
17
|
+
desc "Ensure the required bundle files are present in /mnt"
|
|
18
|
+
task :check_bundle_files do
|
|
19
|
+
raise Exception.new("You must have a private key in your /mnt directory") unless File.exists?("/mnt/pk-*.pem")
|
|
20
|
+
raise Exception.new("You must have your access key in your /mnt directory") unless File.exists?("/mnt/cert-*.pem")
|
|
21
|
+
end
|
|
22
|
+
# Bundle the image
|
|
23
|
+
desc "Bundle this image into the /mnt directory"
|
|
24
|
+
task :bundle => [:clean_mnt, :check_bundle_files] do
|
|
25
|
+
puts `ec2-bundle-vol -k /mnt/pk-*.pem -u '#{Planner.user_id}' -d /mnt -c /mnt/cert-*.pem -r i386`
|
|
26
|
+
end
|
|
27
|
+
# Upload the bundle into the app_name bucket
|
|
28
|
+
desc "Upload the bundle to your bucket with a unique name: deletes old ami"
|
|
29
|
+
task :upload => [:init, :delete_bucket] do
|
|
30
|
+
puts `ec2-upload-bundle -b #{Planner.app_name} -m /mnt/image.manifest.xml -a #{Planner.access_key} -s #{Planner.secret_access_key}`
|
|
31
|
+
end
|
|
32
|
+
# Register the bucket with amazon and get back an ami
|
|
33
|
+
desc "Register the bundle with amazon"
|
|
34
|
+
task :register do
|
|
35
|
+
puts `ec2-register -K /mnt/pk-*.pem -C /mnt/cert-*.pem #{Planner.app_name}/image.manifest.xml`
|
|
36
|
+
end
|
|
37
|
+
# Delete the bucket
|
|
38
|
+
desc "Delete the bucket with the bundle under tha app name"
|
|
39
|
+
task :delete_bucket do
|
|
40
|
+
Planner.app_name.delete_bucket
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
class Tasks
|
|
3
|
+
include Callbacks
|
|
4
|
+
|
|
5
|
+
# Setup and define all the tasks
|
|
6
|
+
def initialize
|
|
7
|
+
yield self if block_given?
|
|
8
|
+
end
|
|
9
|
+
# Define the tasks in the rakefile
|
|
10
|
+
# From the rakefile
|
|
11
|
+
def define_tasks
|
|
12
|
+
# Run the command on the local system
|
|
13
|
+
def run(cmd)
|
|
14
|
+
system(cmd.runnable)
|
|
15
|
+
end
|
|
16
|
+
# Basic setup action
|
|
17
|
+
def setup_application
|
|
18
|
+
PoolParty.options({:config_file => (ENV["CONFIG_FILE"] || ENV["config"]) })
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Require the poolparty specific tasks
|
|
22
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].each { |t| eval open(t).read }
|
|
23
|
+
|
|
24
|
+
Dir["#{PoolParty.plugin_dir}/*/Rakefile"].each {|t| load "#{t}" }
|
|
25
|
+
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
|
2
|
+
require "backcall"
|
|
3
|
+
require "remoter"
|
|
4
|
+
class Test
|
|
5
|
+
include PoolParty::Remoter
|
|
6
|
+
include Callbacks
|
|
7
|
+
|
|
8
|
+
after :initialize, :set_hosts
|
|
9
|
+
def rt
|
|
10
|
+
@rt ||= Rake::RemoteTask
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def set_hosts(c)
|
|
14
|
+
rt.host "myslice", :app, :db
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def rtask(name, *args, &block)
|
|
18
|
+
rt.remote_task(name.to_sym => args, &block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def scp local, remote
|
|
22
|
+
require "tempfile"
|
|
23
|
+
rtask(:scp) do
|
|
24
|
+
put remote do
|
|
25
|
+
open(local).read
|
|
26
|
+
end
|
|
27
|
+
end.execute
|
|
28
|
+
end
|
|
29
|
+
before :scp, :set_hosts
|
|
30
|
+
def ssh command=nil, &block
|
|
31
|
+
block = Proc.new do
|
|
32
|
+
run command
|
|
33
|
+
end
|
|
34
|
+
rtask(:ssh, &block).execute
|
|
35
|
+
end
|
|
36
|
+
before :ssh, :set_hosts
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
t = Test.new
|
|
40
|
+
t.scp("/Users/auser/Sites/work/citrusbyte/internal/gems/pool-party/pool/CHANGELOG", "ho")
|
|
41
|
+
t.ssh("ls -l")
|
|
42
|
+
t.ssh <<-EOE
|
|
43
|
+
ls -l
|
|
44
|
+
mv ho CHANGELOG
|
|
45
|
+
cat CHANGELOG
|
|
46
|
+
EOE
|
data/lib/poolparty.rb
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
=begin rdoc
|
|
2
|
+
The main file, contains the client and the server application methods
|
|
3
|
+
=end
|
|
4
|
+
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
|
5
|
+
|
|
6
|
+
# rubygems
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require "aws/s3"
|
|
9
|
+
require "sqs"
|
|
10
|
+
require "EC2"
|
|
11
|
+
require 'thread'
|
|
12
|
+
require "pp"
|
|
13
|
+
require "tempfile"
|
|
14
|
+
require "aska"
|
|
15
|
+
require "vlad"
|
|
16
|
+
begin
|
|
17
|
+
require 'fastthread'
|
|
18
|
+
require 'thin'
|
|
19
|
+
require 'system_timer'
|
|
20
|
+
Timer = SystemTimer
|
|
21
|
+
rescue LoadError
|
|
22
|
+
require 'timeout'
|
|
23
|
+
Timer = Timeout
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
## Load PoolParty
|
|
27
|
+
pwd = File.dirname(__FILE__)
|
|
28
|
+
|
|
29
|
+
# Load the required files
|
|
30
|
+
# If there is an init file, load that, otherwise
|
|
31
|
+
# require all the files in each directory
|
|
32
|
+
%w(core modules s3 poolparty).each do |dir|
|
|
33
|
+
Dir["#{pwd}/#{dir}"].each do |dir|
|
|
34
|
+
begin
|
|
35
|
+
require File.join(dir, "init")
|
|
36
|
+
rescue LoadError => e
|
|
37
|
+
Dir["#{pwd}/#{File.basename(dir)}/**"].each {|file| require File.join(dir, File.basename(file))}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module PoolParty
|
|
43
|
+
module Version #:nodoc:
|
|
44
|
+
MAJOR = 0
|
|
45
|
+
MINOR = 0
|
|
46
|
+
TINY = 8
|
|
47
|
+
|
|
48
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
|
49
|
+
end
|
|
50
|
+
# PoolParty options
|
|
51
|
+
def options(opts={})
|
|
52
|
+
Application.options(opts)
|
|
53
|
+
end
|
|
54
|
+
# Are we working in verbose-mode
|
|
55
|
+
def verbose?
|
|
56
|
+
options.verbose == true
|
|
57
|
+
end
|
|
58
|
+
# Send a message if we are in verbose-mode
|
|
59
|
+
def message(msg="")
|
|
60
|
+
puts "-- #{msg}" if verbose?
|
|
61
|
+
end
|
|
62
|
+
# Root directory of the application
|
|
63
|
+
def root_dir
|
|
64
|
+
File.expand_path(File.dirname(__FILE__) + "/..")
|
|
65
|
+
end
|
|
66
|
+
# User directory
|
|
67
|
+
def user_dir
|
|
68
|
+
Dir.pwd
|
|
69
|
+
end
|
|
70
|
+
# Write string to a tempfile
|
|
71
|
+
def write_to_temp_file(str="")
|
|
72
|
+
tempfile = Tempfile.new("rand#{rand(1000)}-#{rand(1000)}")
|
|
73
|
+
tempfile.print(str)
|
|
74
|
+
tempfile.flush
|
|
75
|
+
tempfile
|
|
76
|
+
end
|
|
77
|
+
def register_monitor(*names)
|
|
78
|
+
names.each do |name|
|
|
79
|
+
PoolParty::Monitors.extend name
|
|
80
|
+
|
|
81
|
+
PoolParty::Master.send :include, name::Master
|
|
82
|
+
PoolParty::RemoteInstance.send :include, name::Remote
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
def load_plugins
|
|
86
|
+
Dir["#{plugin_dir}/**/init.rb"].each {|a| require a}
|
|
87
|
+
end
|
|
88
|
+
def reset!
|
|
89
|
+
@@installed_plugins = nil
|
|
90
|
+
Application.options = nil
|
|
91
|
+
end
|
|
92
|
+
def plugin_dir
|
|
93
|
+
"#{user_dir}/vendor"
|
|
94
|
+
end
|
|
95
|
+
def read_config_file(filename)
|
|
96
|
+
return {} unless filename
|
|
97
|
+
YAML.load(open(filename).read)
|
|
98
|
+
end
|
|
99
|
+
def include_cloud_tasks
|
|
100
|
+
Tasks.new.define_tasks
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
alias_method :tasks, :include_cloud_tasks
|
|
104
|
+
alias_method :include_tasks, :include_cloud_tasks
|
|
105
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
=begin rdoc
|
|
2
|
+
S3 overloads
|
|
3
|
+
=end
|
|
4
|
+
module AWS
|
|
5
|
+
module S3
|
|
6
|
+
class S3Object
|
|
7
|
+
class << self
|
|
8
|
+
|
|
9
|
+
alias :original_store :store
|
|
10
|
+
def store(key, data, bucket = nil, options = {})
|
|
11
|
+
store_folders(key, bucket, options) if options[:use_virtual_directories]
|
|
12
|
+
original_store(key, data, bucket, options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def streamed_store(key, filepath, bucket = nil, options = {})
|
|
16
|
+
store_folders(key, bucket, options) if options[:use_virtual_directories]
|
|
17
|
+
store(key,File.open(filepath), bucket)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def store_directory(directory, bucket, options = {})
|
|
21
|
+
Dir[File.join(directory, "*")].each do |file|
|
|
22
|
+
streamed_store("#{File.basename(File.dirname(file))}/#{File.basename(file)}", file, bucket, options.update(:use_virtual_directories => true))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def store_folders(key, bucket = nil, options = {})
|
|
27
|
+
folders = key.split("/")
|
|
28
|
+
folders.slice!(0)
|
|
29
|
+
folders.pop
|
|
30
|
+
current_folder = "/"
|
|
31
|
+
folders.each {|folder|
|
|
32
|
+
current_folder += folder
|
|
33
|
+
store_folder(current_folder, bucket, options)
|
|
34
|
+
current_folder += "/"
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def store_folder(key, bucket = nil, options = {})
|
|
39
|
+
original_store(key + "_$folder$", "", bucket, options) # store the magic entry that emulates a folder
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|