auser-poolparty 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -2
- data/Rakefile +61 -4
- data/bin/instance +3 -1
- data/bin/pool +6 -2
- data/config/sample-config.yml +4 -4
- data/lib/core/object.rb +3 -0
- data/lib/helpers/plugin_spec_helper.rb +59 -0
- data/lib/modules/ec2_wrapper.rb +3 -1
- data/lib/modules/file_writer.rb +1 -1
- data/lib/modules/vlad_override.rb +83 -83
- data/lib/poolparty.rb +31 -13
- data/lib/poolparty/application.rb +22 -15
- data/lib/poolparty/init.rb +1 -1
- data/lib/poolparty/master.rb +41 -28
- data/lib/poolparty/monitors.rb +1 -3
- data/lib/poolparty/monitors/cpu.rb +7 -3
- data/lib/poolparty/monitors/memory.rb +14 -7
- data/lib/poolparty/monitors/web.rb +11 -5
- data/lib/poolparty/provider/packages/essential.rb +1 -1
- data/lib/poolparty/provider/packages/heartbeat.rb +1 -1
- data/lib/poolparty/provider/packages/ruby.rb +1 -10
- data/lib/poolparty/remote_instance.rb +5 -18
- data/lib/poolparty/remoter.rb +55 -4
- data/lib/poolparty/scheduler.rb +15 -25
- data/lib/poolparty/thread_pool.rb +94 -0
- data/poolparty.gemspec +9 -6
- data/spec/application_spec.rb +32 -13
- data/spec/callback_spec.rb +20 -1
- data/spec/file_writer_spec.rb +1 -0
- data/spec/kernel_spec.rb +13 -0
- data/spec/master_spec.rb +50 -20
- data/spec/monitors/cpu_monitor_spec.rb +1 -1
- data/spec/plugin_manager_spec.rb +9 -17
- data/spec/plugin_spec.rb +34 -34
- data/spec/poolparty_spec.rb +41 -1
- data/spec/remote_instance_spec.rb +5 -18
- data/spec/scheduler_spec.rb +7 -6
- data/spec/spec_helper.rb +8 -18
- metadata +19 -6
- data/lib/poolparty/tasks/package.rake +0 -53
@@ -10,12 +10,12 @@ module PoolParty
|
|
10
10
|
# The application options
|
11
11
|
def options(opts={})
|
12
12
|
@options ||= make_options(opts)
|
13
|
-
end
|
13
|
+
end
|
14
14
|
# Make the options with the config_file overrides included
|
15
15
|
# Default config file assumed to be at config/config.yml
|
16
16
|
def make_options(opts={})
|
17
17
|
loading_options = opts.delete(:optsparse) || {}
|
18
|
-
loading_options.merge!(
|
18
|
+
loading_options.merge!( opts.delete(:argv) || {} )
|
19
19
|
|
20
20
|
config_file_location = (default_options[:config_file] || opts[:config_file])
|
21
21
|
# If the config_file options are specified and not empty
|
@@ -29,7 +29,7 @@ module PoolParty
|
|
29
29
|
|
30
30
|
default_options.merge!(opts)
|
31
31
|
load_options!(loading_options) # Load command-line options
|
32
|
-
default_options.merge!(local_user_data) unless local_user_data
|
32
|
+
default_options.merge!(local_user_data) unless local_user_data.nil?
|
33
33
|
|
34
34
|
OpenStruct.new(default_options)
|
35
35
|
end
|
@@ -41,10 +41,13 @@ module PoolParty
|
|
41
41
|
op.banner = opts[:banner] if opts[:banner]
|
42
42
|
op.on('-A key', '--access-key key', "Ec2 access key (ENV['ACCESS_KEY'])") { |key| default_options[:access_key] = key }
|
43
43
|
op.on('-S key', '--secret-access-key key', "Ec2 secret access key (ENV['SECRET_ACCESS_KEY'])") { |key| default_options[:secret_access_key] = key }
|
44
|
-
op.on('-I ami', '--image-id id', "AMI instance (default: 'ami-
|
44
|
+
op.on('-I ami', '--image-id id', "AMI instance (default: 'ami-40bc5829')") {|id| default_options[:ami] = id }
|
45
45
|
op.on('-k keypair', '--keypair name', "Keypair name (ENV['KEYPAIR_NAME'])") { |key| default_options[:keypair] = key }
|
46
|
-
op.on('-b bucket', '--bucket bucket', "Application bucket") { |bucket| default_options[:shared_bucket] = bucket }
|
47
|
-
|
46
|
+
op.on('-b bucket', '--bucket bucket', "Application bucket") { |bucket| default_options[:shared_bucket] = bucket }
|
47
|
+
# //THIS IS WHERE YOU LEFT OFF
|
48
|
+
op.on('-D working directory', '--dir dir', "Working directory") { |d| default_options[:working_directory] = d }
|
49
|
+
|
50
|
+
op.on('--ec2-dir dir', "Directory with ec2 data (default: '~/.ec2')") {|id| default_options[:ec2_dir] = id }
|
48
51
|
op.on('-r names', '--services names', "Monitored services (default: '')") {|id| default_options[:services] = id }
|
49
52
|
op.on('-c file', '--config-file file', "Config file (default: '')") {|file| default_options[:config_file] = file }
|
50
53
|
op.on('-l plugin_dir', '--plugin-dir dir', "Plugin directory (default: '')") {|file| default_options[:plugin_dir] = file }
|
@@ -106,13 +109,14 @@ module PoolParty
|
|
106
109
|
:username => "root",
|
107
110
|
:ec2_dir => ENV["EC2_HOME"],
|
108
111
|
:keypair => ENV["KEYPAIR_NAME"],
|
109
|
-
:ami => 'ami-
|
112
|
+
:ami => 'ami-44bd592d',
|
110
113
|
:shared_bucket => "",
|
111
|
-
:expand_when => "
|
114
|
+
:expand_when => "web < 1.5\n memory > 0.85",
|
112
115
|
:contract_when => "cpu < 0.20\n memory < 0.10",
|
113
116
|
:os => "ubuntu",
|
114
117
|
:plugin_dir => "vendor",
|
115
|
-
:install_on_load => false
|
118
|
+
:install_on_load => false,
|
119
|
+
:working_directory => Dir.pwd
|
116
120
|
}
|
117
121
|
end
|
118
122
|
# Services monitored by Heartbeat
|
@@ -126,16 +130,19 @@ module PoolParty
|
|
126
130
|
:secret_access_key => secret_access_key,
|
127
131
|
:user_data => user_data}.to_yaml
|
128
132
|
end
|
129
|
-
def local_user_data
|
130
|
-
@local_user_data ||=
|
133
|
+
def local_user_data
|
131
134
|
begin
|
132
|
-
@@timer.timeout(
|
133
|
-
YAML.load(open("http://169.254.169.254/latest/user-data").read)
|
135
|
+
@@timer.timeout(3.seconds) do
|
136
|
+
@local_user_data ||=YAML.load(open("http://169.254.169.254/latest/user-data").read)
|
134
137
|
end
|
135
138
|
rescue Exception => e
|
136
|
-
{}
|
139
|
+
@local_user_data = {}
|
137
140
|
end
|
138
141
|
end
|
142
|
+
# For testing purposes
|
143
|
+
def reset!
|
144
|
+
@local_user_data = nil
|
145
|
+
end
|
139
146
|
# Keypair path
|
140
147
|
# Idiom:
|
141
148
|
# /Users/username/.ec2/id_rsa-name
|
@@ -168,7 +175,7 @@ module PoolParty
|
|
168
175
|
end
|
169
176
|
end
|
170
177
|
def version
|
171
|
-
PoolParty::Version
|
178
|
+
PoolParty::Version.string
|
172
179
|
end
|
173
180
|
def install_on_load?(bool=false)
|
174
181
|
options.install_on_load == true || bool
|
data/lib/poolparty/init.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
=begin rdoc
|
2
2
|
Load the files in order
|
3
3
|
=end
|
4
|
-
%w(optioner application
|
4
|
+
%w(optioner application thread_pool scheduler provider remoter remoting remote_instance master monitors tasks plugin plugin_manager).each do |f|
|
5
5
|
require File.join(File.dirname(__FILE__), f)
|
6
6
|
end
|
data/lib/poolparty/master.rb
CHANGED
@@ -5,17 +5,16 @@ module PoolParty
|
|
5
5
|
class Master < Remoting
|
6
6
|
include Aska
|
7
7
|
include Callbacks
|
8
|
-
include Monitors
|
9
8
|
# ############################
|
10
9
|
include Remoter
|
11
10
|
# ############################
|
12
11
|
include FileWriter
|
13
12
|
|
14
13
|
def initialize
|
15
|
-
super
|
14
|
+
super
|
16
15
|
|
17
|
-
self.class.send :rules, :contract_when, Application.options.contract_when
|
18
|
-
self.class.send :rules, :expand_when, Application.options.expand_when
|
16
|
+
self.class.send :rules, :contract_when, Application.options.contract_when unless are_rules?(:contract_when)
|
17
|
+
self.class.send :rules, :expand_when, Application.options.expand_when unless are_rules?(:expand_when)
|
19
18
|
end
|
20
19
|
# Start the cloud
|
21
20
|
def start_cloud!
|
@@ -82,11 +81,16 @@ module PoolParty
|
|
82
81
|
sudo apt-get update --fix-missing
|
83
82
|
EOE
|
84
83
|
|
85
|
-
|
86
|
-
|
87
|
-
end
|
84
|
+
ssh(update_apt_string)
|
85
|
+
|
88
86
|
Provider.install_poolparty(cloud_ips)
|
89
87
|
Provider.install_userpackages(cloud_ips)
|
88
|
+
|
89
|
+
# For plugins
|
90
|
+
nodes.each do |node|
|
91
|
+
node.install
|
92
|
+
end
|
93
|
+
|
90
94
|
end
|
91
95
|
end
|
92
96
|
def cloud_ips
|
@@ -106,13 +110,14 @@ module PoolParty
|
|
106
110
|
end
|
107
111
|
# Daemonize only if we are not in the test environment
|
108
112
|
run_thread_loop(:daemonize => !Application.test?) do
|
109
|
-
add_task {
|
113
|
+
add_task {PoolParty.message "Checking cloud"}
|
114
|
+
add_task {launch_minimum_instances}
|
110
115
|
add_task {reconfigure_cloud_when_necessary}
|
111
116
|
add_task {scale_cloud!}
|
112
117
|
add_task {check_stats}
|
113
118
|
end
|
114
119
|
rescue Exception => e
|
115
|
-
|
120
|
+
Process.kill("HUP", Process.pid)
|
116
121
|
end
|
117
122
|
end
|
118
123
|
alias_method :start_monitor, :start_monitor!
|
@@ -120,6 +125,8 @@ module PoolParty
|
|
120
125
|
end
|
121
126
|
# Sole purpose to check the stats, mainly in a plugin
|
122
127
|
def check_stats
|
128
|
+
str = registered_monitors.collect {|m| "#{m}"}
|
129
|
+
PoolParty.message "Monitors: #{str.join(", ")}"
|
123
130
|
end
|
124
131
|
# Add an instance if the cloud needs one ore terminate one if necessary
|
125
132
|
def scale_cloud!
|
@@ -132,9 +139,11 @@ module PoolParty
|
|
132
139
|
# This is a basic check against the local store of the instances that have the
|
133
140
|
# stack installed.
|
134
141
|
def reconfigure_cloud_when_necessary
|
142
|
+
PoolParty.message "#{number_of_unconfigured_nodes} unconfigured nodes"
|
135
143
|
configure_cloud if number_of_unconfigured_nodes > 0
|
136
144
|
end
|
137
145
|
def number_of_unconfigured_nodes
|
146
|
+
# TODO: Find a better way to tell if the nodes are configured.
|
138
147
|
nodes.reject {|a| a.stack_installed? }.size
|
139
148
|
end
|
140
149
|
def grow_by(num=1)
|
@@ -147,15 +156,24 @@ module PoolParty
|
|
147
156
|
end
|
148
157
|
def shrink_by(num=1)
|
149
158
|
num.times do |i|
|
159
|
+
# Get the last node that is not the master
|
150
160
|
node = nodes.reject {|a| a.master? }[-1]
|
151
|
-
request_termination_of_instance(node.instance_id) if node
|
161
|
+
res = request_termination_of_instance(node.instance_id) if node
|
162
|
+
PoolParty.message "#{res ? "Could" : "Could not"} shutdown instance"
|
152
163
|
end
|
153
164
|
wait_for_all_instances_to_terminate
|
154
165
|
configure_cloud
|
155
166
|
end
|
156
|
-
|
167
|
+
def make_base_tmp_dir(c)
|
168
|
+
`mkdir #{base_tmp_dir}` unless File.directory?(base_tmp_dir)
|
169
|
+
end
|
170
|
+
before :build_and_send_config_files_in_temp_directory, :make_base_tmp_dir
|
157
171
|
def build_and_send_config_files_in_temp_directory
|
158
172
|
require 'ftools'
|
173
|
+
if File.directory?(Application.plugin_dir)
|
174
|
+
Kernel.system("tar -czf #{base_tmp_dir}/plugins.tar.gz #{File.basename(Application.plugin_dir)}")
|
175
|
+
end
|
176
|
+
|
159
177
|
File.copy(get_config_file_for("cloud_master_takeover"), "#{base_tmp_dir}/cloud_master_takeover")
|
160
178
|
File.copy(get_config_file_for("heartbeat.conf"), "#{base_tmp_dir}/ha.cf")
|
161
179
|
|
@@ -180,12 +198,12 @@ module PoolParty
|
|
180
198
|
build_heartbeat_config_file_for(node)
|
181
199
|
build_heartbeat_resources_file_for(node)
|
182
200
|
end
|
183
|
-
end
|
201
|
+
end
|
184
202
|
end
|
185
203
|
def cleanup_tmp_directory(c)
|
186
204
|
Dir["#{base_tmp_dir}/*"].each {|f| FileUtils.rm_rf f} if File.directory?("tmp/")
|
187
205
|
end
|
188
|
-
before :build_and_send_config_files_in_temp_directory, :cleanup_tmp_directory
|
206
|
+
before :build_and_send_config_files_in_temp_directory, :cleanup_tmp_directory
|
189
207
|
# Send the files to the nodes
|
190
208
|
def send_config_files_to_nodes(c)
|
191
209
|
run_array_of_tasks(rsync_tasks("#{base_tmp_dir}/*", "#{remote_base_tmp_dir}"))
|
@@ -205,12 +223,18 @@ chmod +x #{script_file}
|
|
205
223
|
end
|
206
224
|
# Add an instance if the load is high
|
207
225
|
def add_instance_if_load_is_high
|
208
|
-
|
226
|
+
if expand?
|
227
|
+
PoolParty.message "Cloud needs expansion"
|
228
|
+
grow_by(1)
|
229
|
+
end
|
209
230
|
end
|
210
231
|
alias_method :add_instance, :add_instance_if_load_is_high
|
211
232
|
# Teardown an instance if the load is pretty low
|
212
233
|
def terminate_instance_if_load_is_low
|
213
|
-
|
234
|
+
if contract?
|
235
|
+
PoolParty.message "Cloud to shrink"
|
236
|
+
shrink_by(1)
|
237
|
+
end
|
214
238
|
end
|
215
239
|
alias_method :terminate_instance, :terminate_instance_if_load_is_low
|
216
240
|
# FOR MONITORING
|
@@ -270,7 +294,7 @@ chmod +x #{script_file}
|
|
270
294
|
def get_config_file_for(name)
|
271
295
|
if File.exists?("#{user_dir}/config/#{name}")
|
272
296
|
"#{user_dir}/config/#{name}"
|
273
|
-
else
|
297
|
+
else
|
274
298
|
"#{root_dir}/config/#{name}"
|
275
299
|
end
|
276
300
|
end
|
@@ -356,6 +380,7 @@ chmod +x #{script_file}
|
|
356
380
|
|
357
381
|
class << self
|
358
382
|
include PoolParty
|
383
|
+
include FileWriter
|
359
384
|
|
360
385
|
def with_nodes(&block)
|
361
386
|
new.nodes.each &block
|
@@ -377,18 +402,6 @@ chmod +x #{script_file}
|
|
377
402
|
def get_next_node(node)
|
378
403
|
new.get_next_node(node)
|
379
404
|
end
|
380
|
-
# Build a heartbeat_config_file from the config file in the config directory and return a tempfile
|
381
|
-
def build_heartbeat_config_file_for(node)
|
382
|
-
return nil unless node
|
383
|
-
new.build_heartbeat_config_file_for(node)
|
384
|
-
end
|
385
|
-
# Build a heartbeat resources file from the config directory and return a tempfile
|
386
|
-
def build_heartbeat_resources_file_for(node)
|
387
|
-
return nil unless node && get_next_node(node)
|
388
|
-
new.write_to_file_for("haresources", node) do
|
389
|
-
"#{node.haproxy_resources_entry}\n#{get_next_node(node).haproxy_resources_entry}"
|
390
|
-
end
|
391
|
-
end
|
392
405
|
def set_hosts(c, remotetask=nil)
|
393
406
|
unless remotetask.nil?
|
394
407
|
rt = remotetask
|
data/lib/poolparty/monitors.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
=begin rdoc
|
2
2
|
Basic monitor on the cpu stats
|
3
3
|
=end
|
4
|
-
|
4
|
+
require "poolparty"
|
5
|
+
|
6
|
+
module Cpu
|
5
7
|
module Master
|
6
8
|
def cpu
|
7
9
|
nodes.size > 0 ? nodes.inject(0) {|i,a| i+=a.cpu } / nodes.size : 0.0
|
@@ -9,8 +11,10 @@ module Cpu
|
|
9
11
|
end
|
10
12
|
|
11
13
|
module Remote
|
12
|
-
def cpu
|
13
|
-
|
14
|
+
def cpu
|
15
|
+
str = run("uptime").split(/\s+/)[-3].to_f rescue 0.0
|
16
|
+
PoolParty.message "Cpu usage: #{str}"
|
17
|
+
str
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
=begin rdoc
|
2
2
|
Basic monitor on the cpu stats
|
3
3
|
=end
|
4
|
+
require "poolparty"
|
5
|
+
|
4
6
|
module Memory
|
5
7
|
module Master
|
6
8
|
# Get the average memory usage over the cloud
|
@@ -11,14 +13,19 @@ module Memory
|
|
11
13
|
|
12
14
|
module Remote
|
13
15
|
def memory
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
out = begin
|
17
|
+
str = run("free -m | grep -i mem")
|
18
|
+
|
19
|
+
total_memory = str.split[1].to_f
|
20
|
+
used_memory = str.split[2].to_f
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
used_memory / total_memory
|
23
|
+
rescue Exception => e
|
24
|
+
0.0
|
25
|
+
end
|
26
|
+
PoolParty.message "Memory: #{out}"
|
27
|
+
out
|
28
|
+
end
|
22
29
|
end
|
23
30
|
|
24
31
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
=begin rdoc
|
2
2
|
Basic monitor on the cpu stats
|
3
3
|
=end
|
4
|
+
require "poolparty"
|
5
|
+
|
4
6
|
module Web
|
5
7
|
module Master
|
6
8
|
# Get the average web request capabilities over the cloud
|
@@ -11,11 +13,15 @@ module Web
|
|
11
13
|
|
12
14
|
module Remote
|
13
15
|
def web
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
out = begin
|
17
|
+
str = run("httperf --server localhost --port #{Application.client_port} --num-conn 3 --timeout 5 | grep 'Request rate'")
|
18
|
+
str[/[.]* ([\d]*\.[\d]*) [.]*/, 0].chomp.to_f
|
19
|
+
rescue Exception => e
|
20
|
+
0.0
|
21
|
+
end
|
22
|
+
PoolParty.message "Web requests: #{out}"
|
23
|
+
out
|
24
|
+
end
|
19
25
|
end
|
20
26
|
|
21
27
|
end
|
@@ -22,16 +22,7 @@ package :rubygems do
|
|
22
22
|
requires :ruby
|
23
23
|
end
|
24
24
|
|
25
|
-
package :poolparty_required_gems do
|
26
|
-
description "required gems"
|
27
|
-
gems %w( SQS aws-s3 amazon-ec2 aska rake rcov auser-poolparty )
|
28
|
-
end
|
29
|
-
|
30
25
|
package :required_gems do
|
31
26
|
description "Pool party gem"
|
32
|
-
|
33
|
-
source 'http://gems.github.com -y'
|
34
|
-
end
|
35
|
-
|
36
|
-
required :poolparty_required_gems
|
27
|
+
gems %w( SQS aws-s3 amazon-ec2 auser-aska rake rcov auser-poolparty vlad --no-ri --no-rdoc)
|
37
28
|
end
|
@@ -22,7 +22,7 @@ module PoolParty
|
|
22
22
|
@number = obj[:number] || 0 # Defaults to the master
|
23
23
|
@status = obj[:status] || "running"
|
24
24
|
@launching_time = obj[:launching_time] || Time.now
|
25
|
-
@keypair = obj[:keypair] || Application.keypair
|
25
|
+
@keypair = obj[:keypair] || Application.keypair rescue ""
|
26
26
|
end
|
27
27
|
|
28
28
|
# Host entry for this instance
|
@@ -159,18 +159,9 @@ module PoolParty
|
|
159
159
|
mkdir -p /data && /usr/bin/s3fs #{Application.shared_bucket} -o accessKeyId=#{Application.access_key} -o secretAccessKey=#{Application.secret_access_key} -o nonempty /data
|
160
160
|
EOC
|
161
161
|
end
|
162
|
-
end
|
163
|
-
|
162
|
+
end
|
164
163
|
# Installs with one commandline and an scp, rather than 10
|
165
164
|
def install
|
166
|
-
# unless stack_installed?
|
167
|
-
# execute_tasks do
|
168
|
-
# # scp(base_install_script, "~/base_install.sh")
|
169
|
-
# ssh("chmod +x base_install.sh && /bin/sh base_install.sh && rm base_install.sh && echo 'installed!' ")
|
170
|
-
# end
|
171
|
-
# PoolParty.message "After install execute_tasks"
|
172
|
-
# mark_installed
|
173
|
-
# end
|
174
165
|
end
|
175
166
|
# Login to store the authenticity
|
176
167
|
def login_once
|
@@ -192,12 +183,8 @@ module PoolParty
|
|
192
183
|
def configure
|
193
184
|
end
|
194
185
|
def update_plugin_string
|
195
|
-
|
196
|
-
|
197
|
-
installed_plugins.each do |plugin_source|
|
198
|
-
str << "git clone #{plugin_source}\n"
|
199
|
-
end
|
200
|
-
str
|
186
|
+
dir = File.basename(Application.plugin_dir)
|
187
|
+
"if [[ -f plugins.tar.gz ]]; then mkdir -p #{dir} && tar -zxf plugins.tar.gz -C #{dir}; fi"
|
201
188
|
end
|
202
189
|
# Is this the master and if not, is the master running?
|
203
190
|
def is_not_master_and_master_is_not_running?
|
@@ -224,7 +211,7 @@ module PoolParty
|
|
224
211
|
end
|
225
212
|
end
|
226
213
|
def stack_installed?
|
227
|
-
@stack_installed ||=
|
214
|
+
@stack_installed ||= run("cat ~/.installed")
|
228
215
|
end
|
229
216
|
def mark_installed(caller=nil)
|
230
217
|
run_now("echo 'installed' > ~/.installed")
|