poolparty 0.0.4
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 +4 -0
- data/Manifest +55 -0
- data/README.txt +113 -0
- data/Rakefile +18 -0
- data/bin/instance +54 -0
- data/bin/pool +33 -0
- data/config/config.yml +23 -0
- data/config/create_proxy_ami.sh +582 -0
- data/config/haproxy.conf +29 -0
- data/config/heartbeat.conf +9 -0
- data/config/heartbeat_authkeys.conf +2 -0
- data/config/monit/haproxy.monit.conf +7 -0
- data/config/monit/nginx.monit.conf +0 -0
- data/config/monit.conf +8 -0
- data/config/nginx.conf +24 -0
- data/lib/core/array.rb +10 -0
- data/lib/core/exception.rb +9 -0
- data/lib/core/kernel.rb +9 -0
- data/lib/core/module.rb +22 -0
- data/lib/core/object.rb +14 -0
- data/lib/core/string.rb +49 -0
- data/lib/core/time.rb +41 -0
- data/lib/modules/callback.rb +55 -0
- data/lib/modules/ec2_wrapper.rb +74 -0
- data/lib/modules/safe_instance.rb +31 -0
- data/lib/pool_party/application.rb +133 -0
- data/lib/pool_party/init.rb +4 -0
- data/lib/pool_party/master.rb +189 -0
- data/lib/pool_party/monitors/cpu.rb +18 -0
- data/lib/pool_party/monitors/memory.rb +21 -0
- data/lib/pool_party/monitors/web.rb +18 -0
- data/lib/pool_party/monitors.rb +13 -0
- data/lib/pool_party/optioner.rb +16 -0
- data/lib/pool_party/os/ubuntu.rb +78 -0
- data/lib/pool_party/os.rb +11 -0
- data/lib/pool_party/remote_instance.rb +180 -0
- data/lib/pool_party/remoting.rb +112 -0
- data/lib/pool_party/scheduler.rb +93 -0
- data/lib/pool_party/tasks.rb +220 -0
- data/lib/pool_party.rb +69 -0
- data/lib/s3/s3_object_store_folders.rb +44 -0
- data/poolparty.gemspec +55 -0
- data/spec/application_spec.rb +32 -0
- data/spec/callback_spec.rb +65 -0
- data/spec/helpers/ec2_mock.rb +56 -0
- data/spec/helpers/remote_instance_mock.rb +11 -0
- data/spec/kernel_spec.rb +11 -0
- data/spec/master_spec.rb +147 -0
- data/spec/monitor_spec.rb +16 -0
- data/spec/optioner_spec.rb +22 -0
- data/spec/poolparty_spec.rb +8 -0
- data/spec/remote_instance_spec.rb +29 -0
- data/spec/remoting_spec.rb +75 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/string_spec.rb +28 -0
- data/test/test_pool_party.rb +0 -0
- metadata +171 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
module TaskCommands
|
|
3
|
+
# Run the command on the local system
|
|
4
|
+
def run(cmd)
|
|
5
|
+
system(cmd.runnable)
|
|
6
|
+
end
|
|
7
|
+
# Basic setup action
|
|
8
|
+
def setup_application
|
|
9
|
+
Application.options({:config_file => (ENV["CONFIG_FILE"] || ENV["config"]) })
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
class Tasks
|
|
13
|
+
include TaskCommands
|
|
14
|
+
# Setup and define all the tasks
|
|
15
|
+
def initialize
|
|
16
|
+
yield self if block_given?
|
|
17
|
+
define_tasks!
|
|
18
|
+
end
|
|
19
|
+
# Define the tasks in the rakefile
|
|
20
|
+
def define_tasks!
|
|
21
|
+
# Tasks dealing with only an instance
|
|
22
|
+
namespace(:instance) do
|
|
23
|
+
# Find the instance we want to deal with
|
|
24
|
+
# interface can be: num=0, i=0, inst=0, 0
|
|
25
|
+
# defaults to the master instance (0)
|
|
26
|
+
task :init do
|
|
27
|
+
num = (ENV['num'] || ENV["i"] || ENV["inst"] || ARGV.shift || 0).to_i
|
|
28
|
+
raise Exception.new("Please set the number of the instance (i.e. num=1, i=1, or as an argument)") unless num
|
|
29
|
+
@node = PoolParty::Master.new.get_node(num)
|
|
30
|
+
end
|
|
31
|
+
# Ssh into the node
|
|
32
|
+
desc "Remotely login to the remote instance"
|
|
33
|
+
task :ssh => [:init] do
|
|
34
|
+
@node.ssh
|
|
35
|
+
end
|
|
36
|
+
# Send a file to the remote instance
|
|
37
|
+
# as designated by src='' and dest=''
|
|
38
|
+
desc "Send a file to the remote instance"
|
|
39
|
+
task :scp => [:init] do
|
|
40
|
+
@node.scp ENV['src'], ENV['dest']
|
|
41
|
+
end
|
|
42
|
+
# Execute a command on the remote instance as designated
|
|
43
|
+
# by cmd=''
|
|
44
|
+
desc "Execute cmd on a remote instance"
|
|
45
|
+
task :exec => [:init] do
|
|
46
|
+
cmd = ENV['cmd'] || "ls -l"
|
|
47
|
+
puts @node.ssh(cmd.runnable)
|
|
48
|
+
end
|
|
49
|
+
# Restart all the services monitored by monit
|
|
50
|
+
desc "Restart all the services"
|
|
51
|
+
task :reload => [:init] do
|
|
52
|
+
@node.restart_with_monit
|
|
53
|
+
end
|
|
54
|
+
# Start all the services monitored by monit
|
|
55
|
+
desc "Start all services"
|
|
56
|
+
task :load => [:init] do
|
|
57
|
+
@node.start_with_monit
|
|
58
|
+
end
|
|
59
|
+
# Stop the services monitored by monit
|
|
60
|
+
desc "Stop all services"
|
|
61
|
+
task :stop => [:init] do
|
|
62
|
+
@node.stop_with_monit
|
|
63
|
+
end
|
|
64
|
+
# Install the required services on this node
|
|
65
|
+
desc "Install stack on this node"
|
|
66
|
+
task :install => :init do
|
|
67
|
+
@node.install_stack
|
|
68
|
+
end
|
|
69
|
+
# Turnoff this instance
|
|
70
|
+
desc "Teardown instance"
|
|
71
|
+
task :shutdown => :init do
|
|
72
|
+
`ec2-terminate-instances #{@node.instance_id}`
|
|
73
|
+
end
|
|
74
|
+
# Configure this node and start the services
|
|
75
|
+
desc "Configure the stack on this node"
|
|
76
|
+
task :configure => :init do
|
|
77
|
+
@node.configure
|
|
78
|
+
@node.restart_with_monit
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
namespace(:dev) do
|
|
83
|
+
task :init do
|
|
84
|
+
setup_application
|
|
85
|
+
end
|
|
86
|
+
# Setup a basic development environment for the user
|
|
87
|
+
desc "Setup development environment specify the config_file"
|
|
88
|
+
task :setup => :init do
|
|
89
|
+
keyfilename = ".#{Application.keypair}_pool_keys"
|
|
90
|
+
run <<-EOR
|
|
91
|
+
echo 'export ACCESS_KEY=\"#{Application.access_key}\"' > $HOME/#{keyfilename}
|
|
92
|
+
echo 'export SECRET_ACCESS_KEY=\"#{Application.secret_access_key}\"' >> $HOME/#{keyfilename}
|
|
93
|
+
echo 'export EC2_HOME=\"#{Application.ec2_dir}\"' >> $HOME/#{keyfilename}
|
|
94
|
+
echo 'export KEYPAIR_NAME=\"#{Application.keypair}\"' >> $HOME/#{keyfilename}
|
|
95
|
+
echo 'export CONFIG_FILE=\"#{Application.config_file}\"' >> $HOME/#{keyfilename}
|
|
96
|
+
EOR
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
# Cloud tasks
|
|
100
|
+
namespace(:cloud) do
|
|
101
|
+
# Setup
|
|
102
|
+
task :init do
|
|
103
|
+
setup_application
|
|
104
|
+
raise Exception.new("You must specify your access_key and secret_access_key") unless Application.access_key && Application.secret_access_key
|
|
105
|
+
end
|
|
106
|
+
# Install the stack on all of the nodes
|
|
107
|
+
desc "Prepare all servers"
|
|
108
|
+
task :prepare => :init do
|
|
109
|
+
PoolParty::Master.new.nodes.each do |node|
|
|
110
|
+
node.install_stack
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
# Start the cloud
|
|
114
|
+
desc "Start the cloud"
|
|
115
|
+
task :start => :init do
|
|
116
|
+
PoolParty::Master.new.start_cloud!
|
|
117
|
+
end
|
|
118
|
+
# Reload the cloud with the new updated data
|
|
119
|
+
desc "Reload all instances with updated data"
|
|
120
|
+
task :reload => :init do
|
|
121
|
+
PoolParty::Master.new.nodes.each do |node|
|
|
122
|
+
node.configure
|
|
123
|
+
node.restart_with_monit
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
# List the cloud
|
|
127
|
+
desc "List cloud"
|
|
128
|
+
task :list => :init do
|
|
129
|
+
PoolParty::Master.new.list
|
|
130
|
+
end
|
|
131
|
+
# Shutdown the cloud
|
|
132
|
+
desc "Shutdown the entire cloud"
|
|
133
|
+
task :shutdown => :init do
|
|
134
|
+
PoolParty::Master.new.request_termination_of_all_instances
|
|
135
|
+
end
|
|
136
|
+
# Maintain the cloud in a background process
|
|
137
|
+
desc "Maintain the cloud (run on the master)"
|
|
138
|
+
task :maintain => :init do
|
|
139
|
+
begin
|
|
140
|
+
PoolParty::Master.new.start_monitor!
|
|
141
|
+
rescue Exception => e
|
|
142
|
+
puts "There was an error starting the monitor: #{e}"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
# Deploy task.
|
|
146
|
+
# TODO: Find a beautiful way of updating the user-defined configuration
|
|
147
|
+
# data
|
|
148
|
+
desc "Deploy web application from production git repos specified in config file"
|
|
149
|
+
task :deploy => :init do
|
|
150
|
+
puts "Deploying web app on nginx"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Nearly antiquated tasks
|
|
155
|
+
namespace(:ec2) do
|
|
156
|
+
task :init do
|
|
157
|
+
Application.options
|
|
158
|
+
end
|
|
159
|
+
# Start a new instance in the cloud
|
|
160
|
+
desc "Add and start an instance to the pool"
|
|
161
|
+
task :start_new_instance => [:init] do
|
|
162
|
+
puts PoolParty::Remoting.new.launch_new_instance!
|
|
163
|
+
end
|
|
164
|
+
# Stop all the instances via command-line
|
|
165
|
+
desc "Stop all running instances"
|
|
166
|
+
task :stop_running_instances => [:init] do
|
|
167
|
+
Thread.new {`ec2-describe-instances | grep INSTANCE | grep running | awk '{print $2}' | xargs ec2-terminate-instances`}
|
|
168
|
+
end
|
|
169
|
+
# Reboot the instances via commandline
|
|
170
|
+
desc "Restart all running instances"
|
|
171
|
+
task :restart_running_instances => [:init] do
|
|
172
|
+
Thread.new {`ec2-describe-instances | grep INSTANCE | grep running | awk '{print $2}' | xargs ec2-reboot-instances`}
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
# Tasks to be run on the server
|
|
176
|
+
namespace(:server) do
|
|
177
|
+
task :init do
|
|
178
|
+
PoolParty::Coordinator.init(false)
|
|
179
|
+
end
|
|
180
|
+
# bundle, upload and register your bundle on the server
|
|
181
|
+
desc "Bundle, upload and register your ami"
|
|
182
|
+
task :all => [:bundle, :upload, :register] do
|
|
183
|
+
puts "== your ami is ready"
|
|
184
|
+
end
|
|
185
|
+
# Cleanup the /mnt directory
|
|
186
|
+
desc "Clean the /mnt directory"
|
|
187
|
+
task :clean_mnt do
|
|
188
|
+
`rm -rf /mnt/image* img*`
|
|
189
|
+
end
|
|
190
|
+
# Before we can bundle, we have to make sure we have the cert and pk files
|
|
191
|
+
desc "Ensure the required bundle files are present in /mnt"
|
|
192
|
+
task :check_bundle_files do
|
|
193
|
+
raise Exception.new("You must have a private key in your /mnt directory") unless File.exists?("/mnt/pk-*.pem")
|
|
194
|
+
raise Exception.new("You must have your access key in your /mnt directory") unless File.exists?("/mnt/cert-*.pem")
|
|
195
|
+
end
|
|
196
|
+
# Bundle the image
|
|
197
|
+
desc "Bundle this image into the /mnt directory"
|
|
198
|
+
task :bundle => [:clean_mnt, :check_bundle_files] do
|
|
199
|
+
puts `ec2-bundle-vol -k /mnt/pk-*.pem -u '#{Planner.user_id}' -d /mnt -c /mnt/cert-*.pem -r i386`
|
|
200
|
+
end
|
|
201
|
+
# Upload the bundle into the app_name bucket
|
|
202
|
+
desc "Upload the bundle to your bucket with a unique name: deletes old ami"
|
|
203
|
+
task :upload => [:init, :delete_bucket] do
|
|
204
|
+
puts `ec2-upload-bundle -b #{Planner.app_name} -m /mnt/image.manifest.xml -a #{Planner.access_key} -s #{Planner.secret_access_key}`
|
|
205
|
+
end
|
|
206
|
+
# Register the bucket with amazon and get back an ami
|
|
207
|
+
desc "Register the bundle with amazon"
|
|
208
|
+
task :register do
|
|
209
|
+
puts `ec2-register -K /mnt/pk-*.pem -C /mnt/cert-*.pem #{Planner.app_name}/image.manifest.xml`
|
|
210
|
+
end
|
|
211
|
+
# Delete the bucket
|
|
212
|
+
desc "Delete the bucket with the bundle under tha app name"
|
|
213
|
+
task :delete_bucket do
|
|
214
|
+
Planner.app_name.delete_bucket
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
end
|
|
220
|
+
end
|
data/lib/pool_party.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
begin
|
|
16
|
+
require 'fastthread'
|
|
17
|
+
require 'thin'
|
|
18
|
+
rescue LoadError
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
## Load PoolParty
|
|
22
|
+
pwd = File.dirname(__FILE__)
|
|
23
|
+
|
|
24
|
+
# Load the required files
|
|
25
|
+
# If there is an init file, load that, otherwise
|
|
26
|
+
# require all the files in each directory
|
|
27
|
+
%w(core modules s3 pool_party).each do |dir|
|
|
28
|
+
Dir["#{pwd}/#{dir}"].each do |dir|
|
|
29
|
+
begin
|
|
30
|
+
require File.join(dir, "init")
|
|
31
|
+
rescue LoadError => e
|
|
32
|
+
Dir["#{pwd}/#{File.basename(dir)}/**"].each {|file| require File.join(dir, File.basename(file))}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module PoolParty
|
|
38
|
+
module Version
|
|
39
|
+
MAJOR = '0'
|
|
40
|
+
MINOR = '0'
|
|
41
|
+
REVISION = '4'
|
|
42
|
+
def self.combined
|
|
43
|
+
[MAJOR, MINOR, REVISION].join('.')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
# PoolParty options
|
|
47
|
+
def options(opts={})
|
|
48
|
+
Application.options(opts)
|
|
49
|
+
end
|
|
50
|
+
# Are we working in verbose-mode
|
|
51
|
+
def verbose?
|
|
52
|
+
Application.verbose == true
|
|
53
|
+
end
|
|
54
|
+
# Send a message if we are in verbose-mode
|
|
55
|
+
def message(msg="")
|
|
56
|
+
pp "-- #{msg}" if verbose?
|
|
57
|
+
end
|
|
58
|
+
# Root directory of the application
|
|
59
|
+
def root_dir
|
|
60
|
+
File.dirname(__FILE__)
|
|
61
|
+
end
|
|
62
|
+
# Write string to a tempfile
|
|
63
|
+
def write_to_temp_file(str="")
|
|
64
|
+
tempfile = Tempfile.new("rand#{rand(1000)}-#{rand(1000)}")
|
|
65
|
+
tempfile.print(str)
|
|
66
|
+
tempfile.flush
|
|
67
|
+
tempfile
|
|
68
|
+
end
|
|
69
|
+
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
|
data/poolparty.gemspec
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
# Gem::Specification for Poolparty-0.0.4
|
|
3
|
+
# Originally generated by Echoe
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = %q{poolparty}
|
|
7
|
+
s.version = "0.0.4"
|
|
8
|
+
|
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
|
10
|
+
|
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
12
|
+
s.authors = ["Ari Lerner"]
|
|
13
|
+
s.date = %q{2008-05-28}
|
|
14
|
+
s.description = %q{Run your entire application off EC2, managed and auto-scaling}
|
|
15
|
+
s.email = %q{ari.lerner@citrusbyte.com}
|
|
16
|
+
s.executables = ["instance", "pool"]
|
|
17
|
+
s.extra_rdoc_files = ["bin/instance", "bin/pool", "CHANGELOG", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/safe_instance.rb", "lib/pool_party/application.rb", "lib/pool_party/init.rb", "lib/pool_party/master.rb", "lib/pool_party/monitors/cpu.rb", "lib/pool_party/monitors/memory.rb", "lib/pool_party/monitors/web.rb", "lib/pool_party/monitors.rb", "lib/pool_party/optioner.rb", "lib/pool_party/os/ubuntu.rb", "lib/pool_party/os.rb", "lib/pool_party/remote_instance.rb", "lib/pool_party/remoting.rb", "lib/pool_party/scheduler.rb", "lib/pool_party/tasks.rb", "lib/pool_party.rb", "lib/s3/s3_object_store_folders.rb", "README.txt"]
|
|
18
|
+
s.files = ["bin/instance", "bin/pool", "CHANGELOG", "config/config.yml", "config/create_proxy_ami.sh", "config/haproxy.conf", "config/heartbeat.conf", "config/heartbeat_authkeys.conf", "config/monit/haproxy.monit.conf", "config/monit/nginx.monit.conf", "config/monit.conf", "config/nginx.conf", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/safe_instance.rb", "lib/pool_party/application.rb", "lib/pool_party/init.rb", "lib/pool_party/master.rb", "lib/pool_party/monitors/cpu.rb", "lib/pool_party/monitors/memory.rb", "lib/pool_party/monitors/web.rb", "lib/pool_party/monitors.rb", "lib/pool_party/optioner.rb", "lib/pool_party/os/ubuntu.rb", "lib/pool_party/os.rb", "lib/pool_party/remote_instance.rb", "lib/pool_party/remoting.rb", "lib/pool_party/scheduler.rb", "lib/pool_party/tasks.rb", "lib/pool_party.rb", "lib/s3/s3_object_store_folders.rb", "Manifest", "Rakefile", "README.txt", "spec/application_spec.rb", "spec/callback_spec.rb", "spec/helpers/ec2_mock.rb", "spec/helpers/remote_instance_mock.rb", "spec/kernel_spec.rb", "spec/master_spec.rb", "spec/monitor_spec.rb", "spec/optioner_spec.rb", "spec/poolparty_spec.rb", "spec/remote_instance_spec.rb", "spec/remoting_spec.rb", "spec/spec_helper.rb", "spec/string_spec.rb", "test/test_pool_party.rb", "poolparty.gemspec"]
|
|
19
|
+
s.has_rdoc = true
|
|
20
|
+
s.homepage = %q{http://blog.citrusbyte.com}
|
|
21
|
+
s.post_install_message = %q{For more information, check http://poolpartyrb.com
|
|
22
|
+
*** Ari Lerner @ <ari.lerner@citrusbyte.com> ***}
|
|
23
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Poolparty", "--main", "README.txt"]
|
|
24
|
+
s.require_paths = ["lib"]
|
|
25
|
+
s.rubyforge_project = %q{poolparty}
|
|
26
|
+
s.rubygems_version = %q{1.0.1}
|
|
27
|
+
s.summary = %q{Run your entire application off EC2, managed and auto-scaling}
|
|
28
|
+
s.test_files = ["test/test_pool_party.rb"]
|
|
29
|
+
|
|
30
|
+
s.add_dependency(%q<aws-s3>, [">= 0"])
|
|
31
|
+
s.add_dependency(%q<amazon-ec2>, [">= 0"])
|
|
32
|
+
s.add_dependency(%q<aska>, [">= 0"])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# # Original Rakefile source (requires the Echoe gem):
|
|
37
|
+
#
|
|
38
|
+
# require 'rubygems'
|
|
39
|
+
# require 'echoe'
|
|
40
|
+
# require 'lib/pool_party'
|
|
41
|
+
#
|
|
42
|
+
# task :default => :test
|
|
43
|
+
#
|
|
44
|
+
# Echoe.new("poolparty") do |p|
|
|
45
|
+
# p.author = "Ari Lerner"
|
|
46
|
+
# p.email = "ari.lerner@citrusbyte.com"
|
|
47
|
+
# p.summary = "Run your entire application off EC2, managed and auto-scaling"
|
|
48
|
+
# p.url = "http://blog.citrusbyte.com"
|
|
49
|
+
# p.docs_host = "www.poolpartyrb.com"
|
|
50
|
+
# p.dependencies = %w(aws-s3 amazon-ec2 aska)
|
|
51
|
+
# p.install_message = "For more information, check http://poolpartyrb.com\n*** Ari Lerner @ <ari.lerner@citrusbyte.com> ***"
|
|
52
|
+
# p.include_rakefile = true
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# PoolParty::Tasks.new
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Application" do
|
|
4
|
+
before(:each) do
|
|
5
|
+
end
|
|
6
|
+
it "should have the root_dir defined" do
|
|
7
|
+
Application.root_dir.should_not be_nil
|
|
8
|
+
end
|
|
9
|
+
it "should be able to call on the haproxy_config_file" do
|
|
10
|
+
Application.haproxy_config_file.should_not be_nil
|
|
11
|
+
end
|
|
12
|
+
it "should be able to find the client_port" do
|
|
13
|
+
Application.options.should_receive(:client_port).and_return(7788)
|
|
14
|
+
Application.client_port.should == 7788
|
|
15
|
+
end
|
|
16
|
+
it "should always have haproxy in the managed services list" do
|
|
17
|
+
Application.managed_services =~ /haproxy/
|
|
18
|
+
end
|
|
19
|
+
it "should be able to say it is in development mode if it is in dev mode" do
|
|
20
|
+
Application.stub!(:environment).and_return("development")
|
|
21
|
+
Application.development?.should == true
|
|
22
|
+
end
|
|
23
|
+
it "should be able to say it is in production if it is in production" do
|
|
24
|
+
Application.stub!(:environment).and_return("production")
|
|
25
|
+
Application.production?.should == true
|
|
26
|
+
end
|
|
27
|
+
it "should be able to say it's keypair path is in the $HOME/ directory" do
|
|
28
|
+
Application.stub!(:ec2_dir).and_return("~/.ec2")
|
|
29
|
+
Application.stub!(:keypair).and_return("poolparty")
|
|
30
|
+
Application.keypair_path.should == "~/.ec2/id_rsa-poolparty"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
|
2
|
+
|
|
3
|
+
class TestCallbacks
|
|
4
|
+
include Callbacks
|
|
5
|
+
attr_reader :str
|
|
6
|
+
def hello
|
|
7
|
+
string << "hello "
|
|
8
|
+
end
|
|
9
|
+
def world
|
|
10
|
+
string << "world"
|
|
11
|
+
end
|
|
12
|
+
def thanks
|
|
13
|
+
string << ", thank you"
|
|
14
|
+
end
|
|
15
|
+
before :world, :hello
|
|
16
|
+
after :world, :thanks
|
|
17
|
+
def pop
|
|
18
|
+
string << "pop"
|
|
19
|
+
end
|
|
20
|
+
def boom
|
|
21
|
+
string << " goes boom"
|
|
22
|
+
end
|
|
23
|
+
after :pop, :boom
|
|
24
|
+
def string
|
|
25
|
+
@str ||= String.new
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
describe "Callbacks" do
|
|
29
|
+
before(:each) do
|
|
30
|
+
@klass = TestCallbacks.new
|
|
31
|
+
end
|
|
32
|
+
it "should retain it's class identifier" do
|
|
33
|
+
@klass.class.should == TestCallbacks
|
|
34
|
+
end
|
|
35
|
+
it "should callback the method before the method runs" do
|
|
36
|
+
@klass.world.should == "hello world, thank you"
|
|
37
|
+
end
|
|
38
|
+
it "should callback the method before the method runs" do
|
|
39
|
+
@klass.pop.should == "pop goes boom"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
class TestMultipleCallbacks
|
|
43
|
+
include Callbacks
|
|
44
|
+
attr_reader :str
|
|
45
|
+
def hi
|
|
46
|
+
string << "hi, "
|
|
47
|
+
end
|
|
48
|
+
def hello
|
|
49
|
+
string << "hello "
|
|
50
|
+
end
|
|
51
|
+
def world
|
|
52
|
+
string << "world"
|
|
53
|
+
end
|
|
54
|
+
def string
|
|
55
|
+
@str ||= String.new
|
|
56
|
+
end
|
|
57
|
+
before :world, :hi
|
|
58
|
+
before :world, :hello
|
|
59
|
+
end
|
|
60
|
+
describe "Multiple callbacks" do
|
|
61
|
+
before(:each) do
|
|
62
|
+
@klass = TestMultipleCallbacks.new
|
|
63
|
+
end
|
|
64
|
+
it "should be able to have multiple callbacks on the same call"
|
|
65
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
module EC2Mock
|
|
3
|
+
def launch_new_instance!
|
|
4
|
+
letter = ("a".."z").to_a[instances.size] # For unique instance_ids
|
|
5
|
+
h = {:instance_id => "i-58ba56#{letter}", :ip => "ip-127-0-0-1.aws.amazonaws.com", :status => "pending", :launching_time => Time.now }
|
|
6
|
+
instances << h
|
|
7
|
+
Thread.new {wait 0.1;h[:status] = "running"} # Simulate the startup time
|
|
8
|
+
return h
|
|
9
|
+
end
|
|
10
|
+
# Shutdown the instance by instance_id
|
|
11
|
+
def terminate_instance!(instance_id)
|
|
12
|
+
instances.select {|a| a[:instance_id] == instance_id}[0][:status] = "terminating"
|
|
13
|
+
end
|
|
14
|
+
# Instance description
|
|
15
|
+
def describe_instance(id)
|
|
16
|
+
item = instances.select {|a| a[:instance_id] == id}[0]
|
|
17
|
+
EC2ResponseObject.get_hash_from_response(item)
|
|
18
|
+
end
|
|
19
|
+
# Get instance by id
|
|
20
|
+
def get_instance_by_id(id)
|
|
21
|
+
get_instances_description.select {|a| a.instance_id == id}[0] rescue nil
|
|
22
|
+
end
|
|
23
|
+
# Get the s3 description for the response in a hash format
|
|
24
|
+
def get_instances_description
|
|
25
|
+
instances
|
|
26
|
+
end
|
|
27
|
+
# Fake the ec2 connection
|
|
28
|
+
def ec2
|
|
29
|
+
@ec2 ||= EC2::Base.new(:access_key_id => "not a key", :secret_access_key => "not a key")
|
|
30
|
+
end
|
|
31
|
+
# Some basic instances, not totally necessary
|
|
32
|
+
def instances
|
|
33
|
+
@instances ||= []
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
class EC2ResponseObject
|
|
37
|
+
def self.get_descriptions(resp)
|
|
38
|
+
rs = resp.DescribeInstancesResponse.reservationSet.item
|
|
39
|
+
rs = rs.respond_to?(:instancesSet) ? rs.instancesSet : rs
|
|
40
|
+
out = begin
|
|
41
|
+
rs.reject {|a| a.empty? }.collect {|r| EC2ResponseObject.get_hash_from_response(r.instancesSet.item)}.reject {|a| a.nil? }
|
|
42
|
+
rescue Exception => e
|
|
43
|
+
begin
|
|
44
|
+
# Really weird bug with amazon's ec2 gem
|
|
45
|
+
rs.reject {|a| a.empty? }.collect {|r| EC2ResponseObject.get_hash_from_response(r)}.reject {|a| a.nil? }
|
|
46
|
+
rescue Exception => e
|
|
47
|
+
[]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
out
|
|
51
|
+
end
|
|
52
|
+
def self.get_hash_from_response(resp)
|
|
53
|
+
{:instance_id => resp.instanceId, :ip => resp.dnsName, :status => resp.instanceState.name, :launching_time => resp.launchTime} rescue nil
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/spec/kernel_spec.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Kernel extensions" do
|
|
4
|
+
before(:each) do
|
|
5
|
+
@host = Master.new
|
|
6
|
+
end
|
|
7
|
+
it "should eval the string into time" do
|
|
8
|
+
@host.should_receive(:sleep).once.and_return true
|
|
9
|
+
@host.wait "10.seconds"
|
|
10
|
+
end
|
|
11
|
+
end
|