auser-poolparty 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/CHANGELOG +3 -2
  2. data/Rakefile +61 -4
  3. data/bin/instance +3 -1
  4. data/bin/pool +6 -2
  5. data/config/sample-config.yml +4 -4
  6. data/lib/core/object.rb +3 -0
  7. data/lib/helpers/plugin_spec_helper.rb +59 -0
  8. data/lib/modules/ec2_wrapper.rb +3 -1
  9. data/lib/modules/file_writer.rb +1 -1
  10. data/lib/modules/vlad_override.rb +83 -83
  11. data/lib/poolparty.rb +31 -13
  12. data/lib/poolparty/application.rb +22 -15
  13. data/lib/poolparty/init.rb +1 -1
  14. data/lib/poolparty/master.rb +41 -28
  15. data/lib/poolparty/monitors.rb +1 -3
  16. data/lib/poolparty/monitors/cpu.rb +7 -3
  17. data/lib/poolparty/monitors/memory.rb +14 -7
  18. data/lib/poolparty/monitors/web.rb +11 -5
  19. data/lib/poolparty/provider/packages/essential.rb +1 -1
  20. data/lib/poolparty/provider/packages/heartbeat.rb +1 -1
  21. data/lib/poolparty/provider/packages/ruby.rb +1 -10
  22. data/lib/poolparty/remote_instance.rb +5 -18
  23. data/lib/poolparty/remoter.rb +55 -4
  24. data/lib/poolparty/scheduler.rb +15 -25
  25. data/lib/poolparty/thread_pool.rb +94 -0
  26. data/poolparty.gemspec +9 -6
  27. data/spec/application_spec.rb +32 -13
  28. data/spec/callback_spec.rb +20 -1
  29. data/spec/file_writer_spec.rb +1 -0
  30. data/spec/kernel_spec.rb +13 -0
  31. data/spec/master_spec.rb +50 -20
  32. data/spec/monitors/cpu_monitor_spec.rb +1 -1
  33. data/spec/plugin_manager_spec.rb +9 -17
  34. data/spec/plugin_spec.rb +34 -34
  35. data/spec/poolparty_spec.rb +41 -1
  36. data/spec/remote_instance_spec.rb +5 -18
  37. data/spec/scheduler_spec.rb +7 -6
  38. data/spec/spec_helper.rb +8 -18
  39. metadata +19 -6
  40. data/lib/poolparty/tasks/package.rake +0 -53
@@ -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!( {:argv => opts.delete(:argv)} )
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-4a46a323')") {|id| default_options[:ami] = id }
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
- op.on('-D ec2 directory', '--ec2-dir dir', "Directory with ec2 data (default: '~/.ec2')") {|id| default_options[:ec2_dir] = id }
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-4a46a323',
112
+ :ami => 'ami-44bd592d',
110
113
  :shared_bucket => "",
111
- :expand_when => "web_usage < 1.5\n memory > 0.85",
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(5.seconds) do
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::STRING
178
+ PoolParty::Version.string
172
179
  end
173
180
  def install_on_load?(bool=false)
174
181
  options.install_on_load == true || bool
@@ -1,6 +1,6 @@
1
1
  =begin rdoc
2
2
  Load the files in order
3
3
  =end
4
- %w(optioner application monitors scheduler provider remoter remoting remote_instance master tasks plugin plugin_manager).each do |f|
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
@@ -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
- execute_tasks do
86
- ssh(update_apt_string)
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 {launch_minimum_instances} # If the base instances go down...
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
- puts "There was an error: #{e.nice_message}"
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
- grow_by(1) if expand?
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
- shrink_by(1) if contract?
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
@@ -8,6 +8,4 @@ module PoolParty
8
8
  module Remote
9
9
  end
10
10
  end
11
- end
12
-
13
- Dir["monitors/*"].each {|f| require f}
11
+ end
@@ -1,7 +1,9 @@
1
1
  =begin rdoc
2
2
  Basic monitor on the cpu stats
3
3
  =end
4
- module Cpu
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
- ssh("uptime").split(/\s+/)[-3].to_f rescue 0.0
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
- str = ssh("free -m | grep -i mem")
15
- total_memory = str.split[1].to_f
16
- used_memory = str.split[2].to_f
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
- used_memory / total_memory
19
- rescue
20
- 0.0
21
- end
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
- str = ssh("httperf --server localhost --port #{Application.client_port} --num-conn 3 --timeout 5 | grep 'Request rate'")
15
- str[/[.]* ([\d]*\.[\d]*) [.]*/, 0].chomp.to_f
16
- rescue
17
- 0.0
18
- end
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  package :build_essential do
4
4
  description 'Build tools'
5
- apt %w(build-essential)
5
+ apt %w( build-essential )
6
6
  end
@@ -1,4 +1,4 @@
1
1
  package :heartbeat, :provides => :failover do
2
2
  description "Heartbeat Linux HA project"
3
- apt %w(heartbeat-2)
3
+ apt %w( heartbeat-2 )
4
4
  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
- gem "poolparty" do
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
- reset!
196
- str = "mkdir -p #{Application.plugin_dir} && cd #{Application.plugin_dir}\n"
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 ||= false
214
+ @stack_installed ||= run("cat ~/.installed")
228
215
  end
229
216
  def mark_installed(caller=nil)
230
217
  run_now("echo 'installed' > ~/.installed")