poolparty 1.3.8 → 1.3.13

Sign up to get free protection for your applications and to get access to all the features.
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