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.
- 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")
|