poolparty 1.3.8 → 1.3.13

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 (36) hide show
  1. data/VERSION.yml +1 -1
  2. data/bin/cloud-thrift +32 -18
  3. data/config/jeweler.rb +3 -2
  4. data/examples/thrift/thrift_example.rb +3 -1
  5. data/lib/cloud_providers/cloud_provider.rb +9 -0
  6. data/lib/cloud_providers/cloud_provider_instance.rb +7 -2
  7. data/lib/cloud_providers/ec2/ec2.rb +22 -7
  8. data/lib/cloud_providers/ec2/ec2_helpers.rb +60 -5
  9. data/lib/dependency_resolvers/base.rb +2 -9
  10. data/lib/poolparty/base.rb +2 -2
  11. data/lib/poolparty/cloud.rb +12 -2
  12. data/lib/poolparty/monitor.rb +25 -3
  13. data/lib/poolparty/plugins/hermes.rb +24 -11
  14. data/lib/poolparty/pool.rb +9 -10
  15. data/lib/poolparty/resource.rb +16 -13
  16. data/lib/poolparty/resources/file.rb +2 -2
  17. data/lib/poolparty/resources/link.rb +2 -1
  18. data/lib/proto/command_interface_handler.rb +27 -8
  19. data/lib/proto/command_query_handler.rb +19 -0
  20. data/lib/proto/poolparty.thrift +1 -0
  21. data/lib/provision/bootstrap_scripts/build_centos.sh +2 -0
  22. data/lib/provision/bootstrap_scripts/build_ubuntu.sh +1 -0
  23. data/tasks/poolparty.rake +24 -0
  24. data/test/lib/dependency_resolvers/base_test.rb +1 -1
  25. data/test/lib/dependency_resolvers/chef/resources/remote_directory_test.rb +2 -2
  26. data/test/lib/dependency_resolvers/chef/resources/remote_file_test.rb +1 -1
  27. data/test/lib/dependency_resolvers/chef_test.rb +3 -3
  28. data/test/lib/poolparty/cloud_test.rb +19 -3
  29. data/test/lib/poolparty/monitor_test.rb +28 -1
  30. data/test/lib/poolparty/resource_test.rb +14 -2
  31. data/test/lib/poolparty/resources/conditional_test.rb +1 -0
  32. data/test/lib/poolparty/resources/directory_test.rb +1 -1
  33. data/test/lib/poolparty/resources/file_test.rb +1 -1
  34. data/test/lib/poolparty/resources/user_test.rb +1 -1
  35. data/test/lib/proto/command_query_handler_test.rb +11 -0
  36. metadata +5 -3
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 3
3
- :patch: 8
3
+ :patch: 13
4
4
  :major: 1
data/bin/cloud-thrift CHANGED
@@ -22,9 +22,11 @@ Usage: #{$0} #{all_options_string}
22
22
  EOS
23
23
 
24
24
  short_desc "Start the cloud thrift interface"
25
- opt :port, "Start a port", :type => :integer, :default => 11223
25
+ opt :port, "Start on port", :type => :integer, :default => 11223
26
26
  opt :dir, "Pid directory", :type => :string, :default => "/tmp"
27
27
  opt :daemon, "Daemonize", :type => :bool, :default => true
28
+ # Query params
29
+ opt :host, "Query on host", :type => :string, :default => "localhost"
28
30
 
29
31
  run do |command|
30
32
 
@@ -33,12 +35,12 @@ EOS
33
35
  cmd = ARGV.shift
34
36
  cmd = ARGV.shift if cmd == "thrift"
35
37
 
36
- if %w(run start restart stop).include?(cmd)
38
+ if %w(run start restart stop query).include?(cmd)
37
39
  args = [cmd]
38
40
  args << ["--", command.argv] unless command.argv.empty?
39
41
  args.flatten!
40
42
  else
41
- puts "You must pass one of the following: #{%w(run start restart stop).join(", ")}"
43
+ puts "You must pass one of the following: #{%w(run start restart stop query).join(", ")}"
42
44
  args = ["--help"]
43
45
  end
44
46
 
@@ -55,22 +57,34 @@ EOS
55
57
  :monitor => true
56
58
  }.merge(command.opts)
57
59
 
58
- block = Proc.new do
59
- handler = CommandInterfaceHandler.new
60
- processor = CloudThrift::CommandInterface::Processor.new(handler)
61
- transport = Thrift::ServerSocket.new( options[:port] )
62
- transportFactory = Thrift::BufferedTransportFactory.new()
63
- server = Thrift::SimpleServer.new(processor, transport, transportFactory)
60
+ if cmd == "query"
61
+ require "command_query_handler"
64
62
 
65
- puts "Starting the cloud server..."
66
- server.serve()
67
- puts "done."
68
- end
69
-
70
- if command[:daemon]
71
- Daemons.run_proc("cloud-thrift", options, &block)
72
- else
73
- block.call
63
+ @loaded_clouds.each do |cld|
64
+ puts CommandQueryHandler.run_query(cld, ARGV.shift, ARGV, options)
65
+ end
66
+ else
67
+ block = Proc.new do
68
+ handler = CommandInterfaceHandler.new
69
+ processor = CloudThrift::CommandInterface::Processor.new(handler)
70
+ transport = Thrift::ServerSocket.new( options[:port] )
71
+ transportFactory = Thrift::BufferedTransportFactory.new()
72
+ server = Thrift::SimpleServer.new(processor, transport, transportFactory)
73
+
74
+ puts "Starting the cloud server..."
75
+ begin
76
+ server.serve()
77
+ rescue
78
+ exit 10
79
+ end
80
+ puts "done."
81
+ end
82
+
83
+ if command[:daemon]
84
+ Daemons.run_proc("cloud-thrift", options, &block)
85
+ else
86
+ block.call
87
+ end
74
88
  end
75
89
 
76
90
  end
data/config/jeweler.rb CHANGED
@@ -15,7 +15,7 @@ end
15
15
  s.description = "PoolParty: The easy, open-source, cross-cloud management solution"
16
16
  s.summary = <<-EOM
17
17
  Self-healing, auto-scaling system administration, provisioning
18
- and maintaining tool that makes cloud computing easy and fun
18
+ and maintaining tool that makes cloud computing easier.
19
19
  EOM
20
20
 
21
21
  s.homepage = "http://poolpartyrb.com"
@@ -25,8 +25,9 @@ end
25
25
  s.test_files = Dir["test/**/test_*.rb"]
26
26
 
27
27
  s.files = (%w(Rakefile README.rdoc License.txt VERSION.yml) + Dir["{config,examples,lib,test,tasks,script,generators,bin,vendor}/**/*"])
28
+ s.files += ["vendor/erlang/hermes/ebin/*.tar.gz"]
28
29
 
29
- s.files.exclude 'vendor/erlang/*'
30
+ s.files.exclude 'vendor/erlang/hermes'
30
31
  s.files.exclude 'examples/thrift/**/*.beam'
31
32
  # s.files.exclude "**/*/erl_crash.dump"
32
33
 
@@ -15,6 +15,7 @@ require "poolparty_types"
15
15
  port = ARGV.pop || 11223
16
16
 
17
17
  transport = Thrift::BufferedTransport.new(Thrift::Socket.new('localhost', port))
18
+ # transport = Thrift::BufferedTransport.new(Thrift::Socket.new('192.168.2.31', port))
18
19
  # transport = Thrift::BufferedTransport.new(Thrift::Socket.new('vm', port))
19
20
  protocol = Thrift::BinaryProtocol.new(transport)
20
21
 
@@ -22,7 +23,8 @@ client = CloudThrift::CommandInterface::Client.new(protocol)
22
23
  transport.open()
23
24
 
24
25
  cld = CloudThrift::CloudQuery.new
25
- cld.name = 'monitored_app'
26
+ cld.name = 'pp2'
27
+ # cld.name = 'monitored_app'
26
28
  # cld.name = 'vmware'
27
29
 
28
30
  resp = client.run_command(cld, "name", [])
@@ -81,5 +81,14 @@ module CloudProviders
81
81
  results.select_with_hash(hsh)
82
82
  end
83
83
 
84
+ def before_compile(cld)
85
+ end
86
+
87
+ def after_compile(cld)
88
+ end
89
+
90
+
91
+
92
+
84
93
  end
85
94
  end
@@ -68,10 +68,15 @@ module CloudProviders
68
68
  raise StandardError.new("You must pass in a cloud to configure an instance") unless cloud
69
69
  cloud.compile(self)
70
70
 
71
- scp(:source => keypair.full_filepath,
72
- :destination => "/etc/poolparty/keys/#{keypair.basename}")
71
+ # scp(:source => keypair.full_filepath,
72
+ # :destination => "/etc/poolparty/keys/#{keypair.basename}")
73
73
 
74
74
  FileUtils.mkdir_p cloud.tmp_path/"etc"/"poolparty" unless File.directory?(cloud.tmp_path/"etc"/"poolparty")
75
+ FileUtils.mkdir_p cloud.tmp_path/"etc"/"poolparty"/"keys" unless File.directory?(cloud.tmp_path/"etc"/"poolparty"/"keys")
76
+
77
+ FileUtils.cp keypair.full_filepath, cloud.tmp_path/"etc"/"poolparty"/"keys"/keypair.basename
78
+ File.open(cloud.tmp_path/"etc"/"poolparty"/"cloud_name", "w") {|f| f << cloud.name }
79
+
75
80
  pack_clouds_dot_rb_and_expected_directories
76
81
 
77
82
  dputs("Rsyncing #{cloud.tmp_path/"*"}")
@@ -19,12 +19,15 @@ using :provider_name
19
19
  EOM
20
20
  end
21
21
 
22
+ require "#{File.dirname(__FILE__)}/ec2_helpers"
22
23
  require "#{File.dirname(__FILE__)}/ec2_response"
23
24
  require "#{File.dirname(__FILE__)}/ec2_instance"
24
25
 
25
26
  module CloudProviders
26
27
  class Ec2 < CloudProvider
27
28
 
29
+ include CloudProviders::Ec2Helpers
30
+
28
31
  # Set the aws keys from the environment, or load from /etc/poolparty/env.yml if the environment variable is not set
29
32
  def self.default_access_key
30
33
  ENV['EC2_ACCESS_KEY'] || load_keys_from_file[:access_key]
@@ -63,7 +66,7 @@ module CloudProviders
63
66
  return @aws_yml if @aws_yml && caching==true
64
67
  return {} unless File.exists?(filename)
65
68
  ddputs("Reading keys from file: #{filename}")
66
- @aws_yml = YAML::load( open(filename).read )
69
+ @aws_yml = YAML::load( open(filename).read ) || {}
67
70
  end
68
71
 
69
72
  default_options({
@@ -88,13 +91,13 @@ module CloudProviders
88
91
  :ramdisk_id => nil,
89
92
  :availability_zone => nil,
90
93
  :block_device_mappings => nil,
91
- :elastic_ips => nil, # An array of the elastic ips
92
- :ebs_volume_id => nil # The volume id of an ebs volume # TODO: ensure this is consistent with :block_device_mappings
94
+ :elastic_ips => [], # An array of the elastic ips
95
+ :ebs_volumes => [] # The volume id of an ebs volume # TODO: ensure this is consistent with :block_device_mappings
93
96
  })
94
97
 
95
98
 
96
99
  def ec2(o={})
97
- @ec2 ||= Rightscale::Ec2.new(access_key, secret_access_key, o.merge(:logger => PoolParty::PoolPartyLog, :default_host => ec2_url))
100
+ @ec2 ||= Rightscale::Ec2.new(access_key, secret_access_key, o.merge(:logger => PoolParty::PoolPartyLog, :endpoint_url => ec2_url))
98
101
  end
99
102
 
100
103
  # Start a new instance with the given options
@@ -114,9 +117,12 @@ module CloudProviders
114
117
  availability_zone,
115
118
  block_device_mappings
116
119
  )
117
- instances = response_array .collect do |aws_response_hash|
120
+ instances = response_array.collect do |aws_response_hash|
118
121
  Ec2Instance.new( Ec2Response.pp_format(aws_response_hash).merge(o) )
119
122
  end
123
+
124
+ after_run_instance(instances)
125
+
120
126
  #FIXME: This needs to deal with the case when an array is returned if max_instances > 1
121
127
  instances.first
122
128
  end
@@ -159,7 +165,15 @@ module CloudProviders
159
165
  end
160
166
 
161
167
  def after_compile(cld)
162
- save_aws_env_to_yml(cld.tmp_path/"etc"/"poolparty"/"env.yml")
168
+ save_aws_env_to_yml(cld.tmp_path/"etc"/"poolparty"/"env.yml") rescue nil
169
+ end
170
+
171
+ # Run after all the instances are run
172
+ def after_run_instance(instances_list)
173
+ instances_list.each do |inst|
174
+ associate_address(inst.instance_id) if next_unused_elastic_ip
175
+ attach_volume(inst.instance_id) if next_unused_volume
176
+ end
163
177
  end
164
178
 
165
179
  # Read yaml file and use it to set environment variables and local variables.
@@ -171,7 +185,8 @@ module CloudProviders
171
185
 
172
186
  # Save aws keys and env variables to a yaml file
173
187
  def save_aws_env_to_yml(filename='/etc/poolparty/env.yml')
174
- File.open(filename, 'w') {|f| f<<YAML::dump(aws_hash(dsl_options, "/etc/poolparty/ec2")) } rescue nil
188
+ hsh = aws_hash(default_options, "/etc/poolparty/ec2")
189
+ File.open(filename, 'w') {|f| f<<YAML::dump(hsh) }
175
190
  end
176
191
 
177
192
  # Return a hash of the aws keys and environment variables
@@ -1,12 +1,57 @@
1
1
  module CloudProviders
2
2
  module Ec2Helpers
3
3
 
4
+ # VOLUMES
5
+ def attach_volume(instance_id, volume_id=next_unused_volume, device="/dev/sdh")
6
+ ec2.attach_volume(volume_id, instance_id, device)
7
+ end
8
+
9
+ def next_unused_volume
10
+ if all_volumes.empty?
11
+ nil
12
+ else
13
+ available_volumes.first
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def all_volumes
20
+ ebs_volumes.empty? ? [] : ec2.describe_volumes.select {|v| ebs_volumes.include?(v[:aws_id]) }
21
+ end
22
+
23
+ def available_volumes
24
+ all_volumes.select {|v| v[:aws_status] == 'available' }
25
+ end
26
+
27
+ def unavailable_volumes
28
+ all_volumes.reject {|v| available_volumes.include?(v) }
29
+ end
30
+
31
+ public
32
+
33
+ # SECURITY GROUPS
34
+ def security_groups(list=[])
35
+ ec2.describe_security_groups(list)
36
+ end
37
+
38
+ public
39
+
40
+ # ELASTIC IPS
41
+
4
42
  # Associate an address with the instance using ec2
5
43
  # Get the next_unused_elastic_ip
6
44
  # and if there is one, associate the instance to the
7
45
  # public ip
8
- def associate_address()
9
- raise StandardError.new('Not Implemented Yet')
46
+ def associate_address(instance_id)
47
+ new_ip = next_unused_elastic_ip
48
+ ec2.associate_address(instance_id, new_ip)
49
+ loop do
50
+ if describe_instance(:instance_id => instance_id).public_ip == new_ip
51
+ return new_ip
52
+ end
53
+ sleep 1
54
+ end
10
55
  end
11
56
 
12
57
  # Get the next usable elastic ip
@@ -17,13 +62,23 @@ module CloudProviders
17
62
  # intersection of the unused ips and those, find the first one available
18
63
  # and return that.
19
64
  def next_unused_elastic_ip
20
- raise StandardError.new('Not Implemented Yet')
21
- if elastic_ips.empty?
65
+ if unusued_elastic_ips.empty?
22
66
  nil
23
67
  else
68
+ unusued_elastic_ips.first
24
69
  end
25
70
  end
26
-
71
+
72
+ private
73
+
74
+ def all_elastic_ips
75
+ elastic_ips.empty? ? [] : ec2.describe_addresses & elastic_ips
76
+ end
77
+
78
+ def unusued_elastic_ips
79
+ all_elastic_ips.select {|i| i[:instance_id] == nil }
80
+ end
81
+
27
82
  # Help create a keypair for the cloud
28
83
  # This is a helper to create the keypair and add them to the cloud for you
29
84
  # def create_keypair
@@ -89,14 +89,7 @@ module DependencyResolvers
89
89
  "#{obj.to_i}"
90
90
  end
91
91
  when String
92
- case obj
93
- when /^\d{4}$/
94
- "#{obj}"
95
- when /^\d{3}$/
96
- "0#{obj}"
97
- else
98
- "\"#{obj}\""
99
- end
92
+ "\"#{obj}\""
100
93
  when Proc
101
94
  obj.call # eh
102
95
  when Array
@@ -119,4 +112,4 @@ module DependencyResolvers
119
112
 
120
113
  end
121
114
 
122
- end
115
+ end
@@ -157,12 +157,12 @@ module PoolParty
157
157
  deps_array.each do |dep_name|
158
158
  dep = get_resource(dep_type, dep_name)
159
159
  raise PoolPartyError.create("ResourceNotFound", "A resource required for #{resource.has_method_name}(#{resource.name}) was not found: #{dep_type}(#{dep_name}). Please make sure you've specified this in your configuration.") unless dep
160
- result.add_edge!(dep, resource, dep.name) unless result.edge?(dep, resource)
160
+ result.add_edge!(dep, resource, dep.name) unless result.edge?(dep, resource) or result.edge?(resource, dep)
161
161
  end
162
162
  end
163
163
 
164
164
  if on
165
- result.add_edge!(resource, on, resource.name) unless result.edge?(resource, on)
165
+ result.add_edge!(resource, on, resource.name) unless result.edge?(resource, on) or result.edge?(on, resource)
166
166
  else
167
167
  result.add_vertex!(resource)
168
168
  end
@@ -196,12 +196,14 @@ module PoolParty
196
196
  # the defined (or the default dependency_resolver, chef)
197
197
  def compile(caller=nil)
198
198
  callback :before_compile
199
+ cloud_provider.before_compile(self)
199
200
  FileUtils.mkdir_p tmp_path unless File.directory?(tmp_path)
200
201
  ddputs <<-EOE
201
202
  Compiling cloud #{self.name} to #{tmp_path/"etc"/"#{dependency_resolver_name}"}
202
203
  number of resources: #{ordered_resources.size}
203
204
  EOE
204
205
  out = dependency_resolver.compile_to(ordered_resources, tmp_path/"etc"/"#{dependency_resolver_name}", caller)
206
+ cloud_provider.after_compile(self)
205
207
  callback :after_compile
206
208
  out
207
209
  end
@@ -230,14 +232,14 @@ Compiling cloud #{self.name} to #{tmp_path/"etc"/"#{dependency_resolver_name}"}
230
232
  # vote_for(:expand) if v > 0.8
231
233
  # end
232
234
  def monitor(monitor_symbol, &block)
233
- monitors[monitor_symbol] ||= PoolParty::Monitor.new(monitor_symbol, &block)
235
+ monitors[monitor_symbol.to_sym] ||= PoolParty::Monitor.new(monitor_symbol, &block)
234
236
  end
235
237
 
236
238
  # Run the monitor logic
237
239
  def run_monitor(monitor_name, value)
238
240
  mon = monitors[monitor_name.to_sym]
239
241
  if mon
240
- mon.run(value.to_f)
242
+ mon.run(value)
241
243
  else
242
244
  "unhandled monitor"
243
245
  end
@@ -248,6 +250,14 @@ Compiling cloud #{self.name} to #{tmp_path/"etc"/"#{dependency_resolver_name}"}
248
250
  @monitors ||= {}
249
251
  end
250
252
 
253
+ def monitor_format(mon_name, meth=nil, &block)
254
+ if monitors.has_key?(mon_name.to_sym)
255
+ monitors[mon_name.to_sym].format(meth, &block)
256
+ else
257
+ raise PoolPartyError.create("MonitorsFormattingError", "You created a monitor format for an unknown monitor. Please check and try again!")
258
+ end
259
+ end
260
+
251
261
  ##### Internal methods #####
252
262
  # Methods that only the cloud itself will use
253
263
  # and thus are private
@@ -15,7 +15,7 @@
15
15
  module PoolParty
16
16
  class Monitor
17
17
 
18
- attr_reader :name, :monitor_block
18
+ attr_reader :name, :monitor_block, :value_format
19
19
 
20
20
  def initialize(monitor_name, &block)
21
21
  msg =<<-EOE
@@ -27,7 +27,7 @@ You must pass a block with your monitor
27
27
  end
28
28
  EOE
29
29
  raise PoolPartyError.create("MonitorDefinitionError", msg) unless block
30
- @name = monitor_name
30
+ @name = monitor_name.to_sym
31
31
  @monitor_block = block
32
32
  end
33
33
 
@@ -37,11 +37,33 @@ You must pass a block with your monitor
37
37
  # retrieved and return the methods available.
38
38
  def run(val)
39
39
  @methods = nil
40
- instance_exec val, &monitor_block
40
+ instance_exec format_value(val), &monitor_block
41
41
  methods
42
42
  end
43
43
 
44
+ # Format the monitor values
45
+ # Set the monitor format here.
46
+ # The default will be to turn the value into a float
47
+ # but to allow other formats, call the value here, for instance:
48
+ # mon.format :to_s
49
+ # Blocks are also permitted
50
+ def format(meth=nil, &block)
51
+ @value_format ||= (meth ? meth : block)
52
+ end
53
+
44
54
  private
55
+
56
+ # Format the value of the monitor
57
+ def format_value(value)
58
+ case value_format
59
+ when Proc
60
+ value_format.call(value)
61
+ when nil
62
+ value.to_f
63
+ else
64
+ value.send value_format
65
+ end
66
+ end
45
67
 
46
68
  # We don't want the methods actually executing since we are executing the methods
47
69
  # in a cloud, we just want to store the output values of the
@@ -11,22 +11,28 @@ module PoolParty
11
11
  )
12
12
 
13
13
  def after_loaded
14
+ run_dependencies
15
+ build_rsync_directory
14
16
  add_unpack
17
+ run_dependencies
15
18
  run_if_needed
16
19
  end
17
20
 
18
21
  def after_compile
19
- run_dependencies
20
- build_rsync_directory
21
22
  end
22
23
 
23
24
  def run_dependencies
24
- case cloud.platform
25
+ install_packages = case cloud.platform
25
26
  when false
26
27
  else
27
- has_package "erlang-nox"
28
- has_package "erlang-dev"
29
- has_package "rrdtool"
28
+ ["erlang-nox", "erlang-dev"]
29
+ end
30
+ has_package "rrdtool"
31
+ has_exec "install_erlang" do
32
+ command "echo ''"
33
+ install_packages.each do |pkg|
34
+ has_package pkg
35
+ end
30
36
  end
31
37
  end
32
38
 
@@ -43,18 +49,25 @@ module PoolParty
43
49
  etc_poolparty = cloud.tmp_path + "/etc/poolparty"
44
50
  FileUtils.mkdir_p(etc_poolparty)
45
51
  node_names = cloud.nodes.collect{|n| n.internal_ip || n.dns_name}.compact.collect{|n| "hermes@#{n}"}
46
- contents = node_names.collect{|n| %Q{"#{n}".}}.join("\n")
52
+ contents = node_names.collect{|n| %Q{'#{n}'.}}.join("\n")
47
53
  File.open(etc_poolparty + "/seeds.conf", "w") {|f| f.puts contents}
48
54
  end
49
55
 
50
56
  def add_unpack
51
- has_exec "cd /tmp/hermes && escript target_system install hermes-#{hermes_release_version} #{remote_hermes_deployed_dir}",
52
- :creates => "#{remote_hermes_deployed_dir}/releases/#{hermes_release_version}"
57
+ has_exec "install_hermes",
58
+ :command => "cd /tmp/hermes && escript target_system install hermes-#{hermes_release_version} #{remote_hermes_deployed_dir}",
59
+ :creates => "#{remote_hermes_deployed_dir}/releases/#{hermes_release_version}",
60
+ :requires => get_package("erlang-dev")
61
+
62
+ has_link :name => "collectd_dir",
63
+ :to => "/var/lib/collectd/rrd/\#{`hostname -f`.chomp}", :source => "/var/lib/collectd/localhost",
64
+ :requires => [get_package("collectd")]
53
65
  end
54
66
 
55
67
  def run_if_needed
56
- has_exec "env GEN_CLUSTER_SEED_CONFIG=/etc/poolparty/seeds.conf #{remote_hermes_deployed_dir}/bin/erl -boot #{remote_hermes_deployed_dir}/releases/#{hermes_release_version}/start -noshell -detached",
57
- :not_if => "ps aux | grep -v grep | grep hermes | grep beam"
68
+ has_exec "env GEN_CLUSTER_SEED_CONFIG=/etc/poolparty/seeds.conf HERMES_RRD_DIRECTORY=/var/lib/collectd/localhost #{remote_hermes_deployed_dir}/bin/erl -boot #{remote_hermes_deployed_dir}/releases/#{hermes_release_version}/start -noshell -detached",
69
+ :not_if => "ps aux | grep -v grep | grep hermes | grep beam",
70
+ :requires => [get_exec("install_hermes"), get_link("collectd_dir")]
58
71
  end
59
72
 
60
73
  private
@@ -97,7 +97,7 @@ module PoolParty
97
97
  end
98
98
 
99
99
  def self.clouds_dot_rb_dir(n=nil)
100
- File.dirname(self.clouds_dot_rb_file) if self.clouds_dot_rb_file
100
+ self.clouds_dot_rb_file ? File.dirname(self.clouds_dot_rb_file) : "./"
101
101
  end
102
102
 
103
103
  # Load the default clouds.rb file
@@ -151,17 +151,16 @@ module PoolParty
151
151
  # + calls the resource define_resource_methods to define the resource methods
152
152
  # + sets up the log
153
153
  def self.before_file_load(filepath)
154
- $:.unshift(::File.dirname(filepath))
155
- Dir["#{ ::File.dirname(filepath)}/{plugins,lib}/**/*"].each do |plugin_path|
154
+ $:.unshift(File.dirname(filepath))
155
+ $:.unshift("#{File.dirname(filepath)}/lib")
156
+ $:.unshift("#{File.dirname(filepath)}/plugins")
157
+
158
+ Dir["#{File.dirname(filepath)}/lib/*"].each {|lib_path| require lib_path }
159
+ Dir["#{File.dirname(filepath)}/plugins/*"].each do |plugin_path|
156
160
  if File.directory?(plugin_path)
157
161
  $:.unshift(plugin_path)
158
-
159
- ["#{plugin_path}/#{File.basename(plugin_path)}", "#{plugin_path}/lib/#{File.basename(plugin_path)}"].each do |potential|
160
- require potential if File.exists?(potential)
161
- end
162
-
163
- elsif File.file?(plugin_path) && plugin_path.match(/.rb$/)
164
- require plugin_path
162
+ else
163
+ require plugin_path if File.file?(plugin_path) && plugin_path.match(/.rb$/)
165
164
  end
166
165
  end
167
166
  end
@@ -15,6 +15,7 @@ module PoolParty
15
15
  def initialize(opts={}, extra_opts={}, exists=true, &block)
16
16
  @exists ||= exists
17
17
  super(opts, extra_opts, &block)
18
+ after_loaded_requires_parent
18
19
  valid?
19
20
  end
20
21
 
@@ -69,6 +70,11 @@ module PoolParty
69
70
  other_resources_obj.each do |obj|
70
71
  requires(obj)
71
72
  end
73
+ else
74
+ # When is an object
75
+ # k = other_resources_obj.has_method_name
76
+ # dependencies[k] ||= []
77
+ # dependencies[k] << other_resources_obj.name
72
78
  end
73
79
  end
74
80
 
@@ -114,7 +120,7 @@ module PoolParty
114
120
  def after_compile
115
121
  end
116
122
 
117
- def after_loaded
123
+ def after_loaded_requires_parent
118
124
  requires parent if parent && !parent.is_a?(PoolParty::Cloud) && !parent.is_a?(PoolParty::Pool)
119
125
  end
120
126
 
@@ -162,8 +168,8 @@ module PoolParty
162
168
  def self.define_resource_methods
163
169
  defined_resources.each do |res|
164
170
  next if res.method_defined?
165
- ddputs "Defining resource: #{res} as #{res.has_method_name}"
166
- define_resource(res)
171
+ ddputs "Defining resource: #{res} as #{res.has_method_name} on #{self}"
172
+ define_resource(res, is_base_resource_class? ? Base : self)
167
173
  res.method_defined!
168
174
  unless res.defined_resources.empty?
169
175
  res.define_resource_methods
@@ -171,10 +177,14 @@ module PoolParty
171
177
  end
172
178
  end
173
179
 
180
+ def self.is_base_resource_class?
181
+ self.to_s == PoolParty::Resource.to_s
182
+ end
183
+
174
184
  # Define the resource on the base class so it's available across all
175
185
  # PoolParty classes that use Base
176
- def self.define_resource(res)
177
- Base.class_eval <<-EOE
186
+ def self.define_resource(res, base_klass=Base)
187
+ base_klass.class_eval <<-EOE
178
188
  def has_#{res.has_method_name}(a={},b={},e=true, &block)
179
189
  obj = #{res}.new(a,b,e,&block)
180
190
  resources << obj
@@ -252,14 +262,7 @@ module PoolParty
252
262
  "#{obj.to_i}"
253
263
  end
254
264
  when String
255
- case obj
256
- when /^\d{4}$/
257
- "#{obj}"
258
- when /^\d{3}$/
259
- "0#{obj}"
260
- else
261
- "\"#{obj}\""
262
- end
265
+ "\"#{obj}\""
263
266
  when Proc
264
267
  obj.call # eh
265
268
  when Array
@@ -66,8 +66,8 @@ end
66
66
  file = arg.first
67
67
  @template = if File.file?(b = File.expand_path(file))
68
68
  b
69
- elsif File.file?(c = File.expand_path(File.join(clouds_dot_rb_dir, file)))
70
- c
69
+ elsif File.file?(d = File.expand_path(File.join(clouds_dot_rb_dir, file)))
70
+ d
71
71
  elsif f = search_in_known_locations(file)
72
72
  f
73
73
  else
@@ -30,12 +30,13 @@ module PoolParty
30
30
 
31
31
  default_options(
32
32
  :link_type => :symbolic,
33
+ :source => nil,
33
34
  :to => nil
34
35
  )
35
36
 
36
37
  def print_to_chef
37
38
  <<-EOE
38
- link "<%= name %>" do
39
+ link "<%= source || name %>" do
39
40
  link_type <%= print_variable(link_type) %>
40
41
  action :<%= exists? ? :create : :delete %>
41
42
  to <%= print_variable(to) %>
@@ -4,7 +4,31 @@ class CommandInterfaceHandler
4
4
  cr = CloudThrift::CloudResponse.new
5
5
  cr.name = cld.name
6
6
  cr.command = command
7
- resp = begin
7
+
8
+ cr.response = format_response(get_response(cld, command, args))
9
+
10
+ return cr
11
+ end
12
+
13
+ def cast_command(cld, command, args)
14
+
15
+ cr = CloudThrift::CloudResponse.new
16
+ cr.name = cld.name
17
+ cr.command = command
18
+ cr.response = format_response("Running command: #{command}(#{args})")
19
+
20
+ fork do
21
+ get_response(cld, command, args)
22
+ end
23
+
24
+ return cr
25
+ end
26
+
27
+
28
+ private
29
+
30
+ def get_response(cld, command, args)
31
+ begin
8
32
  the_cloud = clouds[cld.name]
9
33
  if the_cloud
10
34
  if command.include?(".")
@@ -25,15 +49,9 @@ class CommandInterfaceHandler
25
49
  end
26
50
  rescue Exception => e
27
51
  cr.response = "Error: #{e.inspect}"
28
- end
29
-
30
- cr.response = format_response(resp)
31
-
32
- return cr
52
+ end
33
53
  end
34
54
 
35
- private
36
-
37
55
  def format_response(resp)
38
56
  case resp
39
57
  when Array
@@ -44,4 +62,5 @@ class CommandInterfaceHandler
44
62
  [resp]
45
63
  end.map {|ele| ele.to_s }
46
64
  end
65
+
47
66
  end
@@ -0,0 +1,19 @@
1
+ class CommandQueryHandler
2
+ def self.run_query(on_cloud, command, args, opts={})
3
+ port = opts[:port] || 11223
4
+ host = opts[:host] || "localhost"
5
+
6
+ transport = Thrift::BufferedTransport.new(Thrift::Socket.new(host, port))
7
+ protocol = Thrift::BinaryProtocol.new(transport)
8
+
9
+ client = CloudThrift::CommandInterface::Client.new(protocol)
10
+ transport.open()
11
+
12
+ cld = CloudThrift::CloudQuery.new
13
+ cld.name = on_cloud.name
14
+
15
+ ddputs("Running command: #{command} on #{cld.name} at #{host}:#{port}")
16
+ resp = client.run_command(cld, command, args)
17
+ resp.response
18
+ end
19
+ end
@@ -14,4 +14,5 @@ struct CloudResponse {
14
14
 
15
15
  service CommandInterface {
16
16
  CloudResponse run_command(1:CloudQuery cld, 2:string command, 3:list<string> arglist)
17
+ CloudResponse cast_command(1:CloudQuery cld, 2:string command, 3:list<string> arglist)
17
18
  }
@@ -4,6 +4,8 @@
4
4
  # Make the /etc/poolparty directory to hold poolparty configuration
5
5
  mkdir -p /etc/poolparty
6
6
  mkdir -p /var/log/poolparty
7
+ mkdir -p /etc/poolparty/keys
8
+
7
9
 
8
10
  # Build the user groups
9
11
  if [ ! "$(egrep "poolparty" /etc/group)" ]; then
@@ -3,6 +3,7 @@
3
3
  # Build the directories
4
4
  # Make the /etc/poolparty directory to hold poolparty configuration
5
5
  mkdir -p /etc/poolparty
6
+ mkdir -p /etc/poolparty/keys
6
7
  mkdir -p /var/log/poolparty
7
8
 
8
9
  # Build the user groups
data/tasks/poolparty.rake CHANGED
@@ -22,6 +22,30 @@ task :thrift do
22
22
  end
23
23
  end
24
24
 
25
+ namespace(:hermes) do
26
+
27
+ desc "Pack for distribution"
28
+ task :target_system do
29
+ erl_dir = File.dirname(__FILE__) + "/../vendor/erlang"
30
+ hermes_dir = File.join(erl_dir, "hermes")
31
+
32
+ puts `cd #{hermes_dir} && make target_system`
33
+ end
34
+ desc "Update hermes code"
35
+ task :update do
36
+ erl_dir = File.dirname(__FILE__) + "/../vendor/erlang"
37
+ hermes_dir = File.join(erl_dir, "hermes")
38
+
39
+ if File.directory?(hermes_dir)
40
+ `cd #{hermes_dir} && git pull origin master`
41
+ else
42
+ FileUtils.mkdir_p erl_dir
43
+ r = "git clone git://github.com/auser/hermes.git #{hermes_dir}"
44
+ Kernel.system r
45
+ end
46
+ end
47
+ end
48
+
25
49
  namespace(:pp) do
26
50
  task :build_gem => ["poolparty:vendor:setup", "poolparty:vendor:update", :gemspec, :build]
27
51
 
@@ -30,7 +30,7 @@ template "/etc/motd" do
30
30
  source "/etc/motd.erb"
31
31
  action :create
32
32
  backup 5
33
- mode 0644
33
+ mode "0644"
34
34
  owner "root"
35
35
  end
36
36
  EOE
@@ -26,10 +26,10 @@ class RemoteDirectoryResourceTest < Test::Unit::TestCase
26
26
  remote_directory "/tmp/remote_something" do
27
27
  source "something"
28
28
  files_backup 10
29
- files_mode 0644
29
+ files_mode "0644"
30
30
  action :create
31
31
  recursive false
32
- mode 0755
32
+ mode "0755"
33
33
  owner "nobody"
34
34
  group "nobody"
35
35
  files_owner "root"
@@ -22,7 +22,7 @@ remote_file "/tmp/testfile" do
22
22
  source "http://www.example.com/tempfiles/testfile"
23
23
  action :create
24
24
  backup 5
25
- mode 0644
25
+ mode "0644"
26
26
  owner "root"
27
27
  checksum "08da002l"
28
28
  end
@@ -54,7 +54,7 @@ class ChefTest < Test::Unit::TestCase
54
54
 
55
55
  def test_compile_to_the_recipes
56
56
  @base.compile_to(@resources[:files], test_dir)
57
- assert_equal "template \"/etc/motd\" do\n source \"/etc/motd.erb\"\n action :create\n backup 5\n mode 0644\n owner \"root\"\nend\n", open(@cookboox_directory/"recipes"/"default.rb").read
57
+ assert_equal "template \"/etc/motd\" do\n source \"/etc/motd.erb\"\n action :create\n backup 5\n mode \"0644\"\n owner \"root\"\nend\n", open(@cookboox_directory/"recipes"/"default.rb").read
58
58
  end
59
59
 
60
60
  def test_compile_the_recipes
@@ -78,14 +78,14 @@ template "/etc/motd" do
78
78
  source "/etc/motd.erb"
79
79
  action :create
80
80
  backup 5
81
- mode 0644
81
+ mode "0644"
82
82
  owner "root"
83
83
  end
84
84
 
85
85
  directory "/etc/poolparty" do
86
86
  action :create
87
87
  recursive true
88
- mode 0644
88
+ mode "0644"
89
89
  owner "root"
90
90
  group "root"
91
91
  end
@@ -134,7 +134,6 @@ class CloudTest < Test::Unit::TestCase
134
134
  assert_equal 22, clouds["noneity"].ssh_port
135
135
  end
136
136
 
137
-
138
137
  def test_children_getting_parent_options
139
138
  clear!
140
139
  pool "outside" do
@@ -159,13 +158,24 @@ class CloudTest < Test::Unit::TestCase
159
158
  configure if v < 0.2
160
159
  vote_for(:expand) if v > 1.1
161
160
  end
161
+
162
+ monitor :load do |a|
163
+ # [0.42 0.43 0.37]
164
+ vote_for(:expand) if a[0] > 0.8
165
+ end
166
+
167
+ monitor_format :load do |d|
168
+ d.split(",").map {|ele| ele.to_f }
169
+ end
170
+
162
171
  end
163
172
  end
164
173
 
165
- assert_equal 1, clouds["monitor_app"].monitors.size
166
- assert_equal [:cpu], clouds["monitor_app"].monitors.map {|m,v| v.name }
174
+ assert_equal 2, clouds["monitor_app"].monitors.size
175
+ assert_equal [:cpu, :load], clouds["monitor_app"].monitors.map {|m,v| v.name }
167
176
  assert_equal({:configure => []}, clouds["monitor_app"].run_monitor("cpu", "0.1"))
168
177
  assert_equal({:vote_for => [:expand]}, clouds["monitor_app"].run_monitor("cpu", "1.4"))
178
+ assert_equal({:vote_for => [:expand]}, clouds["monitor_app"].run_monitor("load", "0.98, 0.23, 0.1"))
169
179
  end
170
180
 
171
181
  def test_add_monitoring_stack_if_needed
@@ -183,5 +193,11 @@ class CloudTest < Test::Unit::TestCase
183
193
  assert_equal 1, clouds["app_cloud"].monitors.size
184
194
 
185
195
  clouds["app_cloud"].compile
196
+
197
+ compile_dir = clouds["app_cloud"].tmp_path/"etc"/"chef"/"cookbooks"/"poolparty"
198
+ recipe_file = compile_dir/"recipes"/"default.rb"
199
+ recipe_contents = open(recipe_file).read
200
+
201
+ assert_match /install hermes/, recipe_contents
186
202
  end
187
203
  end
@@ -3,7 +3,7 @@ require "#{File.dirname(__FILE__)}/../../test_helper"
3
3
  class MonitorTest < Test::Unit::TestCase
4
4
 
5
5
  def setup
6
- @mon = PoolParty::Monitor.new(:'cpu-idle') do |c|
6
+ @mon = PoolParty::Monitor.new("cpu-idle") do |c|
7
7
  vote_for(:expand) if c > 0.8
8
8
  configure if c < 0.1
9
9
  end
@@ -27,4 +27,31 @@ class MonitorTest < Test::Unit::TestCase
27
27
  end
28
28
  end
29
29
 
30
+ def test_formatting_input
31
+ mon = PoolParty::Monitor.new("memory-used") {|c| long if c.length > 2}
32
+ mon.format(:to_s)
33
+ assert_equal({:long => []}, mon.run("hellllllllooooo world"))
34
+
35
+ mon = PoolParty::Monitor.new("memory-used") do |c|
36
+ long if c.length > 2
37
+ short if c.length < 2
38
+ end
39
+ mon.format(:to_a)
40
+ assert_equal({:long => []}, mon.run(%w(1 2 3 4)))
41
+ assert_equal({:short => []}, mon.run(%w(1)))
42
+
43
+ mon = PoolParty::Monitor.new("memory-used") do |saying, to|
44
+ if saying == "hello"
45
+ hello
46
+ else
47
+ goodbye
48
+ end
49
+ end
50
+
51
+ mon.format {|d|return *d.split(",")}
52
+
53
+ assert_equal({:hello => []}, mon.run("hello, world"))
54
+ assert_equal({:short => []}, mon.run("good day"))
55
+ end
56
+
30
57
  end
@@ -73,8 +73,6 @@ class ResourceTest < Test::Unit::TestCase
73
73
  assert_equal @inst.print_variable(:a), ":a"
74
74
  assert_equal @inst.print_variable({:a => "a"}), ":a => \"a\""
75
75
  assert_equal @inst.print_variable(644), "644"
76
- assert_equal @inst.print_variable("0755"), "0755"
77
- assert_equal @inst.print_variable("755"), "0755"
78
76
  assert_equal @inst.print_variable(@inst), @inst.to_s
79
77
  assert_equal @inst.print_variable(nil), nil
80
78
  end
@@ -147,4 +145,18 @@ class ResourceTest < Test::Unit::TestCase
147
145
  end
148
146
  end
149
147
 
148
+ def test_a_subclassed_resource_has_the_method_of_the_subclassed_resource
149
+ pool "oblong" do
150
+ cloud "piece" do
151
+ fake_plugin do
152
+ has_subclassed "box"
153
+ end
154
+ end
155
+ end
156
+
157
+ assert_equal PoolParty::Resources::FakePlugin, clouds["piece"].resources.first.class
158
+ assert_equal PoolParty::Resources::FakeSubclassedPlugin, clouds["piece"].resources.first.resources[0].class
159
+ assert_equal 1, clouds["piece"].resources.first.resources[0].resources.size
160
+ end
161
+
150
162
  end
@@ -27,6 +27,7 @@ end'
27
27
  pool "conditional_cloud" do
28
28
  cloud "test" do
29
29
 
30
+ keypair "test_key"
30
31
  has_case "os" do
31
32
  when_is :ubuntu, "git-core"
32
33
  else_is "git"
@@ -12,7 +12,7 @@ class DirectoryResourceTest < Test::Unit::TestCase
12
12
  end
13
13
 
14
14
  should "have the directory method denoted by has_method_name" do
15
- str = "directory \"/etc/poolparty\" do\n action :create\n recursive true\n mode 0644\n owner \"root\"\n group \"root\"\nend\n"
15
+ str = "directory \"/etc/poolparty\" do\n action :create\n recursive true\n mode \"0644\"\n owner \"root\"\n group \"root\"\nend\n"
16
16
  assert_equal str, @base.compile(@res)
17
17
  end
18
18
 
@@ -13,7 +13,7 @@ class DirectoryResourceTest < Test::Unit::TestCase
13
13
  end
14
14
 
15
15
  should "have the template method denoted by has_method_name" do
16
- str = "template \"/etc/poolparty/lyrics\" do\n source \"/etc/poolparty/lyrics.erb\"\n action :create\n backup 5\n mode 0644\n owner \"root\"\nend\n"
16
+ str = "template \"/etc/poolparty/lyrics\" do\n source \"/etc/poolparty/lyrics.erb\"\n action :create\n backup 5\n mode \"0644\"\n owner \"root\"\nend\n"
17
17
 
18
18
  assert_equal str, @base.compile(@res)
19
19
  assert_equal "I'm just a file, a lonely little file in the world", open(@cookboox_directory/"templates"/"default"/"etc"/"poolparty"/"lyrics.erb").read
@@ -16,7 +16,7 @@ str = <<-EOS
16
16
  user "random" do
17
17
  action :create
18
18
  comment "Random User"
19
- uid 1000
19
+ uid \"1000\"
20
20
  gid "users"
21
21
  home "/home/random"
22
22
  shell "/bin/zsh"
@@ -0,0 +1,11 @@
1
+ require "#{File.dirname(__FILE__)}/../../test_helper"
2
+ $:.unshift("#{File.dirname(__FILE__)}/../../../lib/proto")
3
+ require "command_query_handler"
4
+
5
+ class CommandQueryHandlerTest < Test::Unit::TestCase
6
+
7
+ def test_has_run_query_command
8
+ assert CommandQueryHandler.respond_to?(:run_query)
9
+ end
10
+
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poolparty
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.8
4
+ version: 1.3.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ari Lerner
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2009-09-02 00:00:00 -07:00
14
+ date: 2009-09-04 00:00:00 -07:00
15
15
  default_executable:
16
16
  dependencies: []
17
17
 
@@ -208,6 +208,7 @@ files:
208
208
  - lib/poolparty/resources/user.rb
209
209
  - lib/poolparty/resources/variable.rb
210
210
  - lib/proto/command_interface_handler.rb
211
+ - lib/proto/command_query_handler.rb
211
212
  - lib/proto/gen-erl/commandInterface_thrift.erl
212
213
  - lib/proto/gen-erl/commandInterface_thrift.hrl
213
214
  - lib/proto/gen-erl/poolparty_constants.hrl
@@ -335,6 +336,7 @@ files:
335
336
  - test/lib/poolparty/resources/service_test.rb
336
337
  - test/lib/poolparty/resources/user_test.rb
337
338
  - test/lib/poolparty/resources/variable_test.rb
339
+ - test/lib/proto/command_query_handler_test.rb
338
340
  - test/lib/provision/base_test.rb
339
341
  - test/lib/provision/bootstrapper_test.rb
340
342
  - test/test_helper.rb
@@ -1142,7 +1144,7 @@ rubyforge_project:
1142
1144
  rubygems_version: 1.3.4
1143
1145
  signing_key:
1144
1146
  specification_version: 3
1145
- summary: Self-healing, auto-scaling system administration, provisioning and maintaining tool that makes cloud computing easy and fun
1147
+ summary: Self-healing, auto-scaling system administration, provisioning and maintaining tool that makes cloud computing easier.
1146
1148
  test_files:
1147
1149
  - test/test_helper.rb
1148
1150
  - test/test_methods.rb