poolparty 1.6.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +1 -0
- data/VERSION.yml +1 -1
- data/bin/cloud +4 -0
- data/examples/chef_cloud.rb +7 -5
- data/lib/cloud_providers/connections.rb +8 -2
- data/lib/cloud_providers/ec2/ec2.rb +31 -16
- data/lib/cloud_providers/ec2/ec2_instance.rb +4 -0
- data/lib/cloud_providers/ec2/helpers/elastic_block_store.rb +20 -7
- data/lib/cloud_providers/ec2/helpers/elastic_block_store_group.rb +8 -2
- data/lib/cloud_providers/remote_instance.rb +6 -17
- data/lib/poolparty/chef.rb +80 -0
- data/lib/poolparty/chef_attribute.rb +1 -1
- data/lib/poolparty/chef_client.rb +63 -0
- data/lib/poolparty/chef_solo.rb +70 -0
- data/lib/poolparty/cloud.rb +7 -109
- data/lib/poolparty.rb +4 -1
- metadata +5 -2
data/README.rdoc
CHANGED
|
@@ -51,6 +51,7 @@ There are a number of commands PoolParty offers to interact with your cloud. The
|
|
|
51
51
|
* <tt>cloud list</tt>
|
|
52
52
|
* <tt>cloud show</tt>
|
|
53
53
|
* <tt>cloud ssh</tt>
|
|
54
|
+
* <tt>cloud run</tt>
|
|
54
55
|
|
|
55
56
|
Clouds are distinguished by security groups. If a security group is not specified in your cloud block, one will be created based on the naming convention poolname-cloudname.
|
|
56
57
|
|
data/VERSION.yml
CHANGED
data/bin/cloud
CHANGED
|
@@ -40,6 +40,10 @@ EOS
|
|
|
40
40
|
|
|
41
41
|
@loaded_pool = pool
|
|
42
42
|
@loaded_clouds = command[:name] ? [pool.clouds[command[:name]]] : pool.clouds.map {|name,cld|cld}
|
|
43
|
+
if @loaded_clouds.count == 0
|
|
44
|
+
puts "No clouds loaded. Check your clouds.rb or -n option"
|
|
45
|
+
exit
|
|
46
|
+
end
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
run do |command|
|
data/examples/chef_cloud.rb
CHANGED
|
@@ -4,11 +4,13 @@ pool "poolparty" do
|
|
|
4
4
|
cloud "chef" do
|
|
5
5
|
instances 1
|
|
6
6
|
using :ec2
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
chef :solo do
|
|
8
|
+
repo File.dirname(__FILE__)+"/chef_cloud/chef_repo"
|
|
9
|
+
recipe "apache2"
|
|
10
|
+
recipe "rsyslog::server"
|
|
11
|
+
recipe "collectd"
|
|
12
|
+
attributes :apache2 => {:listen_ports => ["80", "8080"]}
|
|
13
|
+
end
|
|
12
14
|
user_data open(File.dirname(__FILE__)+"/chef_cloud/user_data").read
|
|
13
15
|
security_group do
|
|
14
16
|
authorize :from_port => "22", :to_port => "22"
|
|
@@ -21,7 +21,7 @@ module CloudProviders
|
|
|
21
21
|
sleep(2)
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
|
-
|
|
24
|
+
connected
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def run(commands, o={})
|
|
@@ -38,6 +38,8 @@ module CloudProviders
|
|
|
38
38
|
# Get the environment hash out of
|
|
39
39
|
# the extra_ssh_ops and then delete
|
|
40
40
|
# the element
|
|
41
|
+
ssh_error_msg="SSH is not available for this node. perhaps you need to authorize it?"
|
|
42
|
+
raise PoolParty::PoolPartyError.create("SSHError", ssh_error_msg) unless ssh_available?
|
|
41
43
|
env = extra_ssh_ops[:env] || {}
|
|
42
44
|
extra_ssh_ops.delete :env
|
|
43
45
|
|
|
@@ -47,6 +49,7 @@ module CloudProviders
|
|
|
47
49
|
do_sudo = extra_ssh_ops[:do_sudo]
|
|
48
50
|
extra_ssh_ops.delete :do_sudo
|
|
49
51
|
end
|
|
52
|
+
do_sudo=user!="root"
|
|
50
53
|
|
|
51
54
|
envstring = env.collect {|k,v| "#{k}=#{v}"}.join ' && '
|
|
52
55
|
envstring += " && " unless envstring.size == 0
|
|
@@ -86,9 +89,12 @@ module CloudProviders
|
|
|
86
89
|
|
|
87
90
|
def rsync( opts={} )
|
|
88
91
|
raise StandardError.new("You must pass a :source=>uri option to rsync") unless opts[:source]
|
|
92
|
+
ssh_error_msg="SSH is not available for this node. perhaps you need to authorize it?"
|
|
93
|
+
raise PoolParty::PoolPartyError.create("SSHError", ssh_error_msg) unless ssh_available?
|
|
89
94
|
destination_path = opts[:destination] || opts[:source]
|
|
90
95
|
rsync_opts = opts[:rsync_opts] || '-va'
|
|
91
|
-
rsync_opts += %q% --rsync-path="sudo rsync"
|
|
96
|
+
rsync_opts += %q% --rsync-path="sudo rsync"% unless user=="root"
|
|
97
|
+
rsync_opts += %q% --exclude=.svn --exclude=.git --exclude=.cvs %
|
|
92
98
|
cmd_string = "rsync -L -e 'ssh #{ssh_options}' #{rsync_opts} #{opts[:source]} #{user}@#{host}:#{destination_path}"
|
|
93
99
|
out = system_run(cmd_string)
|
|
94
100
|
out
|
|
@@ -59,7 +59,7 @@ module CloudProviders
|
|
|
59
59
|
|
|
60
60
|
# Load credentials from file
|
|
61
61
|
def self.load_keys_from_credential_file(filename=default_credential_file, caching=true)
|
|
62
|
-
return {:access_key => @access_key, :secret_access_key => @secret_access_key} if @access_key and @secret_access_key
|
|
62
|
+
return {:access_key => @access_key, :secret_access_key => @secret_access_key} if @access_key and @secret_access_key and caching
|
|
63
63
|
return {} if filename.nil? or not File.exists?(filename)
|
|
64
64
|
puts("Reading keys from file: #{filename}")
|
|
65
65
|
File.open(filename).each_line { |line|
|
|
@@ -230,6 +230,10 @@ module CloudProviders
|
|
|
230
230
|
end
|
|
231
231
|
|
|
232
232
|
def bootstrap_nodes!(tmp_path=nil)
|
|
233
|
+
unless security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22)
|
|
234
|
+
warn "Cloud security_groups are not authorized for ssh. Cannot bootstrap."
|
|
235
|
+
return
|
|
236
|
+
end
|
|
233
237
|
tmp_path ||= cloud.tmp_path
|
|
234
238
|
nodes.each do |node|
|
|
235
239
|
next unless node.in_service?
|
|
@@ -241,6 +245,10 @@ module CloudProviders
|
|
|
241
245
|
end
|
|
242
246
|
|
|
243
247
|
def configure_nodes!(tmp_path=nil)
|
|
248
|
+
unless security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22)
|
|
249
|
+
warn "Cloud security_groups are not authorized for ssh. Cannot configure."
|
|
250
|
+
return
|
|
251
|
+
end
|
|
244
252
|
tmp_path ||= cloud.tmp_path
|
|
245
253
|
nodes.each do |node|
|
|
246
254
|
next unless node.in_service?
|
|
@@ -248,6 +256,9 @@ module CloudProviders
|
|
|
248
256
|
node.rsync_dir(tmp_path) if tmp_path
|
|
249
257
|
node.run_chef!
|
|
250
258
|
end
|
|
259
|
+
ebs_volume_groups.each do |vol_grp|
|
|
260
|
+
vol_grp.verify_attachments nodes
|
|
261
|
+
end
|
|
251
262
|
end
|
|
252
263
|
|
|
253
264
|
def assign_elastic_ips
|
|
@@ -384,7 +395,7 @@ module CloudProviders
|
|
|
384
395
|
# size 200
|
|
385
396
|
# end
|
|
386
397
|
def ebs_volumes(name=nil, &block)
|
|
387
|
-
ebs_volume_groups << ElasticBlockStoreGroup.new(sub_opts,&block)
|
|
398
|
+
ebs_volume_groups << ElasticBlockStoreGroup.new(sub_opts,&block) if block
|
|
388
399
|
end
|
|
389
400
|
|
|
390
401
|
def assign_ebs_volumes
|
|
@@ -404,25 +415,29 @@ module CloudProviders
|
|
|
404
415
|
# The function will return volumes matching *all* filters. A volume is a filter match if *any* one of the filter values equals the volume parameter value.
|
|
405
416
|
def list_ec2_volumes(filters=nil)
|
|
406
417
|
@volumes_on_ec2=ec2.describe_volumes.volumeSet.item unless @volumes_on_ec2
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
418
|
+
(if filters.nil? # no filter to check, so return at once
|
|
419
|
+
@volumes_on_ec2
|
|
420
|
+
else
|
|
421
|
+
@volumes_on_ec2.select{|vol| # select volumes for which no filter failed
|
|
422
|
+
not filters.map {|filter_key, filter_val|
|
|
423
|
+
filter_key=filter_key.to_s if filter_key.is_a?(Symbol) # filter_key may be given as a symbol
|
|
424
|
+
raise ArgumentError, "Filter key #{filter_key} is invalid" unless vol.has_key?(filter_key)
|
|
425
|
+
if filter_val.is_a?(Array) # Deal with multiple filter values
|
|
426
|
+
filter_val.map{|val| val.is_a?(String) ? val : val.to_s}.member?(vol[filter_key]) # make sure fiter_val array values are Strings before checking for match
|
|
427
|
+
else
|
|
428
|
+
(filter_val.is_a?(String) ? filter_val : filter_val.to_s)==vol[filter_key] # make sure fiter_val is a String before comparing
|
|
429
|
+
end
|
|
430
|
+
}.member?(false) # Check if a filter failed, the 'not' statement at the beginning of the map block negates this so 'select' will choose only when no filter failed
|
|
431
|
+
}.compact # remove nil results from volume set.
|
|
432
|
+
end
|
|
433
|
+
).map{|vol| ElasticBlockStore.new(vol,:cloud => cloud)}
|
|
419
434
|
end
|
|
420
435
|
|
|
421
436
|
# Read credentials from credential_file if one exists
|
|
422
437
|
def credential_file(file=nil)
|
|
423
438
|
unless file.nil?
|
|
424
|
-
|
|
425
|
-
|
|
439
|
+
dsl_options[:credential_file]=file
|
|
440
|
+
dsl_options.merge!(Ec2.load_keys_from_credential_file(file))
|
|
426
441
|
else
|
|
427
442
|
fetch(:credential_file)
|
|
428
443
|
end
|
|
@@ -41,6 +41,10 @@ module CloudProviders
|
|
|
41
41
|
def reachable?
|
|
42
42
|
ping_port self.public_ip, 22
|
|
43
43
|
end
|
|
44
|
+
|
|
45
|
+
def ssh_available?
|
|
46
|
+
cloud.security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22) and reachable? and in_service?
|
|
47
|
+
end
|
|
44
48
|
|
|
45
49
|
def in_service?
|
|
46
50
|
running?
|
|
@@ -2,13 +2,14 @@ module CloudProviders
|
|
|
2
2
|
class ElasticBlockStore < Ec2Helper
|
|
3
3
|
|
|
4
4
|
# instance methods
|
|
5
|
-
|
|
6
|
-
attr_reader :createTime
|
|
5
|
+
attr_reader :volumeId, :size, :snapshotId, :status, :attachments, :device, :availabilityZone, :instanceId
|
|
6
|
+
attr_reader :createTime
|
|
7
7
|
|
|
8
8
|
alias :volume_id :volumeId
|
|
9
9
|
alias :snapshot_id :snapshotId
|
|
10
10
|
alias :availability_zone :availabilityZone
|
|
11
11
|
alias :create_time :createTime
|
|
12
|
+
alias :instance_id :instanceId
|
|
12
13
|
|
|
13
14
|
def createTime(create_time)
|
|
14
15
|
unless create_time.class==DateTime
|
|
@@ -24,10 +25,14 @@ module CloudProviders
|
|
|
24
25
|
|
|
25
26
|
def parse_raw_response(raw_response)
|
|
26
27
|
@raw_respons = raw_response
|
|
27
|
-
raw_response.each{|k,v|
|
|
28
|
-
|
|
28
|
+
raw_response.each{|k,v| instance_variable_set("@"+k,v) if respond_to?(k) }
|
|
29
|
+
unless raw_response.attachmentSet.nil?
|
|
29
30
|
@attachments=raw_response.attachmentSet.item
|
|
30
|
-
@attachments.each{|attch|
|
|
31
|
+
@attachments.each{|attch| if attch.status=="attached" or attch.status=="attaching"
|
|
32
|
+
@instanceId=attch.instanceId
|
|
33
|
+
@device=attch.device
|
|
34
|
+
end
|
|
35
|
+
}
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
|
|
@@ -43,11 +48,19 @@ module CloudProviders
|
|
|
43
48
|
end
|
|
44
49
|
|
|
45
50
|
def attach(ec2_instance,device)
|
|
46
|
-
ec2.attach_volume(:volume_id => volume_id, :instance_id => ec2_instance.instance_id, :device => device).return=="true"
|
|
51
|
+
if ec2.attach_volume(:volume_id => volume_id, :instance_id => ec2_instance.instance_id, :device => device).return=="true"
|
|
52
|
+
update!
|
|
53
|
+
return true
|
|
54
|
+
end
|
|
55
|
+
false
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
def detach
|
|
50
|
-
ec2.detach_volume(:volume_id => volume_id).return=="true"
|
|
59
|
+
if ec2.detach_volume(:volume_id => volume_id).return=="true"
|
|
60
|
+
update!
|
|
61
|
+
return true
|
|
62
|
+
end
|
|
63
|
+
false
|
|
51
64
|
end
|
|
52
65
|
|
|
53
66
|
def detach!
|
|
@@ -57,17 +57,23 @@ module CloudProviders
|
|
|
57
57
|
# Check no volumes are attached to node on device
|
|
58
58
|
skip_node=false
|
|
59
59
|
cloud.list_ec2_volumes.each{|vol|
|
|
60
|
-
if vol.attached?(node.instance_id)and vol.device == device
|
|
60
|
+
if vol.attached?(node.instance_id) and vol.device == device
|
|
61
61
|
warn "A volume is allready attached to device #{device} of instance #{node.instance_id}"
|
|
62
62
|
skip_node = true
|
|
63
63
|
end
|
|
64
64
|
}
|
|
65
65
|
unless skip_node
|
|
66
66
|
vol=get_free_volume(node.zone)
|
|
67
|
-
vol.device=device
|
|
68
67
|
vol.attach(node,device)
|
|
69
68
|
end
|
|
70
69
|
}
|
|
71
70
|
end
|
|
71
|
+
|
|
72
|
+
def verify_attachments(nodes)
|
|
73
|
+
nodes_without_volume=nodes.select do |node|
|
|
74
|
+
volumes_attached_to(node.id).size=0
|
|
75
|
+
end
|
|
76
|
+
attach nodes_without_volume if nodes_without_volume.any?
|
|
77
|
+
end
|
|
72
78
|
end
|
|
73
79
|
end
|
|
@@ -38,29 +38,15 @@ module CloudProviders
|
|
|
38
38
|
|
|
39
39
|
def chef_bootstrapped?
|
|
40
40
|
# do_sudo is false cause we want to capture the return code of the call
|
|
41
|
-
@chef_bootstrapped ||=
|
|
41
|
+
@chef_bootstrapped ||= cloud.chef.node_bootsrapped?(self)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def bootstrap_chef!
|
|
45
|
-
unless chef_bootstrapped?
|
|
46
|
-
ssh([
|
|
47
|
-
'apt-get update',
|
|
48
|
-
'apt-get autoremove -y',
|
|
49
|
-
'apt-get install -y ruby ruby-dev rubygems git-core libopenssl-ruby',
|
|
50
|
-
'gem sources -a http://gems.opscode.com',
|
|
51
|
-
'gem install chef ohai --no-rdoc --no-ri' ])
|
|
52
|
-
end
|
|
53
|
-
ssh(bootstrap_gems.collect { |gem| "gem install #{gem} --no-rdoc --no-ri" } )
|
|
45
|
+
cloud.chef.node_bootstrap(self) unless chef_bootstrapped?
|
|
54
46
|
end
|
|
55
47
|
|
|
56
48
|
def run_chef!
|
|
57
|
-
|
|
58
|
-
$GEM_BIN/chef-solo -j /etc/chef/dna.json -c /etc/chef/solo.rb
|
|
59
|
-
CMD
|
|
60
|
-
envhash = {
|
|
61
|
-
:GEM_BIN => %q%$(gem env | grep "EXECUTABLE DIRECTORY" | awk "{print \\$4}")%
|
|
62
|
-
}
|
|
63
|
-
ssh([chef_solo_cmd.strip.squeeze(' ')], :env => envhash )
|
|
49
|
+
cloud.chef.node_run!(self)
|
|
64
50
|
end
|
|
65
51
|
|
|
66
52
|
def run
|
|
@@ -127,6 +113,9 @@ module CloudProviders
|
|
|
127
113
|
Time.now - Time.parse(launch_time)
|
|
128
114
|
end
|
|
129
115
|
|
|
116
|
+
def ssh_available?
|
|
117
|
+
warn "Implemented in cloudprovider instance class. something is wrong"
|
|
118
|
+
end
|
|
130
119
|
# def to_s
|
|
131
120
|
# (cloud ? to_hash.merge(:cloud=>cloud.name) : to_hash)
|
|
132
121
|
# end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
class Chef < Base
|
|
3
|
+
def self.types
|
|
4
|
+
return [:solo,:client]
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.get_chef(type,cloud,&block)
|
|
8
|
+
("Chef" + type.to_s.capitalize).constantize(PoolParty).send(:new,type,:cloud => cloud,&block)
|
|
9
|
+
end
|
|
10
|
+
# Chef
|
|
11
|
+
|
|
12
|
+
def attributes(hsh={}, &block)
|
|
13
|
+
@attributes ||= ChefAttribute.new(hsh, &block)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def override_attributes(hsh={}, &block)
|
|
17
|
+
@override_attributes ||= ChefAttribute.new(hsh, &block)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Adds a chef recipe to the cloud
|
|
21
|
+
#
|
|
22
|
+
# The hsh parameter is inserted into the override_attributes.
|
|
23
|
+
# The insertion is performed as follows. If
|
|
24
|
+
# the recipe name = "foo::bar" then effectively the call is
|
|
25
|
+
#
|
|
26
|
+
# override_attributes.merge! { :foo => { :bar => hsh } }
|
|
27
|
+
def recipe(recipe_name, hsh={})
|
|
28
|
+
_recipes << recipe_name unless _recipes.include?(recipe_name)
|
|
29
|
+
|
|
30
|
+
head = {}
|
|
31
|
+
tail = head
|
|
32
|
+
recipe_name.split("::").each do |key|
|
|
33
|
+
unless key == "default"
|
|
34
|
+
n = {}
|
|
35
|
+
tail[key] = n
|
|
36
|
+
tail = n
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
tail.replace hsh
|
|
40
|
+
|
|
41
|
+
override_attributes.merge!(head) unless hsh.empty?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def recipes(*recipes)
|
|
45
|
+
recipes.each do |r|
|
|
46
|
+
recipe(r)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def node_run!(remote_instance)
|
|
51
|
+
envhash = {
|
|
52
|
+
:GEM_BIN => %q%$(gem env | grep "EXECUTABLE DIRECTORY" | awk "{print \\$4}")%
|
|
53
|
+
}
|
|
54
|
+
remote_instance.ssh([chef_cmd.strip.squeeze(' ')], :env => envhash )
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def node_bootsrapped?(remote_instance)
|
|
58
|
+
remote_instance.ssh(["(gem list; dpkg -l chef) | grep -q chef && echo 'chef installed'"], :do_sudo => false).empty?
|
|
59
|
+
end
|
|
60
|
+
def node_bootstrap!(remote_instance)
|
|
61
|
+
remote_instance.ssh([
|
|
62
|
+
'apt-get update',
|
|
63
|
+
'apt-get autoremove -y',
|
|
64
|
+
'apt-get install -y ruby ruby-dev rubygems git-core libopenssl-ruby',
|
|
65
|
+
'gem sources -a http://gems.opscode.com',
|
|
66
|
+
'gem install chef ohai --no-rdoc --no-ri' ])
|
|
67
|
+
remote_instance.ssh(remote_instance.bootstrap_gems.collect { |gem| "gem install #{gem} --no-rdoc --no-ri" } )
|
|
68
|
+
end
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def _recipes
|
|
72
|
+
@_recipes ||= []
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def method_missing(m,*args,&block)
|
|
76
|
+
cloud.send(m,*args,&block) if cloud.respond_to?(m)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
# Chef class bootstrapping chef-client.
|
|
3
|
+
class ChefClient < Chef
|
|
4
|
+
dsl_methods :server_url,:validation_token
|
|
5
|
+
|
|
6
|
+
def openid_url(url=nil)
|
|
7
|
+
if url.nil?
|
|
8
|
+
return @openid_url||= (u=URI.parse(server_url)
|
|
9
|
+
u.port=4001
|
|
10
|
+
openid_url u.to_s)
|
|
11
|
+
else
|
|
12
|
+
@openid_url=url
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def roles(*roles)
|
|
17
|
+
return @_roles||=[cloud.name] if roles.empty?
|
|
18
|
+
@_roles=roles
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def compile!
|
|
22
|
+
build_tmp_dir
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
def after_initialized
|
|
27
|
+
raise PoolPartyError.create("ChefArgumentMissing", "server_url must be specified!") unless server_url
|
|
28
|
+
end
|
|
29
|
+
def chef_cmd
|
|
30
|
+
return <<-CMD
|
|
31
|
+
PATH="$PATH:$GEM_BIN" chef-client -j /etc/chef/dna.json -c /etc/chef/client.rb -d -i 1800 -s 20
|
|
32
|
+
CMD
|
|
33
|
+
end
|
|
34
|
+
# The NEW actual chef resolver.
|
|
35
|
+
def build_tmp_dir
|
|
36
|
+
base_directory = tmp_path/"etc"/"chef"
|
|
37
|
+
FileUtils.rm_rf base_directory
|
|
38
|
+
FileUtils.mkdir_p base_directory
|
|
39
|
+
puts "Creating the dna.json"
|
|
40
|
+
attributes.to_dna [], base_directory/"dna.json", {:run_list => roles.map{|r| "role[#{r}]"} + _recipes.map{|r| "recipe[#{r}]"}}.merge(attributes.init_opts)
|
|
41
|
+
write_client_dot_rb
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def write_client_dot_rb(to=tmp_path/"etc"/"chef"/"client.rb")
|
|
45
|
+
content = <<-EOE
|
|
46
|
+
log_level :info
|
|
47
|
+
log_location "/var/log/chef/client.log"
|
|
48
|
+
ssl_verify_mode :verify_none
|
|
49
|
+
file_cache_path "/var/cache/chef"
|
|
50
|
+
pid_file "/var/run/chef/client.pid"
|
|
51
|
+
Chef::Log::Formatter.show_time = true
|
|
52
|
+
openid_url "#{openid_url}"
|
|
53
|
+
EOE
|
|
54
|
+
%w(search_url role_url remotefile_url template_url registration_url).each{|url|
|
|
55
|
+
content+="#{url} \"#{server_url}\"\n"
|
|
56
|
+
}
|
|
57
|
+
content+="validation_token \"#{validation_token}\"\n" if validation_token
|
|
58
|
+
File.open(to, "w") do |f|
|
|
59
|
+
f << content
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module PoolParty
|
|
2
|
+
class ChefSolo < Chef
|
|
3
|
+
dsl_methods :repo
|
|
4
|
+
def compile!
|
|
5
|
+
build_tmp_dir
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def chef_cmd
|
|
10
|
+
return <<-CMD
|
|
11
|
+
PATH="$PATH:$GEM_BIN" chef-solo -j /etc/chef/dna.json -c /etc/chef/solo.rb
|
|
12
|
+
CMD
|
|
13
|
+
end
|
|
14
|
+
# The NEW actual chef resolver.
|
|
15
|
+
def build_tmp_dir
|
|
16
|
+
base_directory = tmp_path/"etc"/"chef"
|
|
17
|
+
FileUtils.rm_rf base_directory
|
|
18
|
+
puts "Copying the chef-repo into the base directory from #{repo}"
|
|
19
|
+
FileUtils.mkdir_p base_directory/"roles"
|
|
20
|
+
if File.directory?(repo)
|
|
21
|
+
if File.exist?(base_directory)
|
|
22
|
+
# First remove the directory
|
|
23
|
+
FileUtils.remove_entry base_directory, :force => true
|
|
24
|
+
end
|
|
25
|
+
FileUtils.cp_r "#{repo}/.", base_directory
|
|
26
|
+
else
|
|
27
|
+
raise "#{repo} chef repo directory does not exist"
|
|
28
|
+
end
|
|
29
|
+
puts "Creating the dna.json"
|
|
30
|
+
attributes.to_dna [], base_directory/"dna.json", {:run_list => ["role[#{cloud.name}]"]}
|
|
31
|
+
write_solo_dot_rb
|
|
32
|
+
write_chef_role_json tmp_path/"etc"/"chef"/"roles/#{cloud.name}.json"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def write_solo_dot_rb(to=tmp_path/"etc"/"chef"/"solo.rb")
|
|
36
|
+
content = <<-EOE
|
|
37
|
+
cookbook_path ["/etc/chef/site-cookbooks", "/etc/chef/cookbooks"]
|
|
38
|
+
role_path "/etc/chef/roles"
|
|
39
|
+
log_level :info
|
|
40
|
+
EOE
|
|
41
|
+
|
|
42
|
+
File.open(to, "w") do |f|
|
|
43
|
+
f << content
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def write_chef_role_json(to=tmp_path/"etc"/"chef"/"dna.json")
|
|
48
|
+
|
|
49
|
+
# Add the parent name and the name of the cloud to
|
|
50
|
+
# the role for easy access in recipes.
|
|
51
|
+
pp = {
|
|
52
|
+
:poolparty => {
|
|
53
|
+
:parent_name => cloud.parent.name,
|
|
54
|
+
:name => cloud.name,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override_attributes.merge! pp
|
|
59
|
+
ca = ChefAttribute.new({
|
|
60
|
+
:name => cloud.name,
|
|
61
|
+
:json_class => "Chef::Role",
|
|
62
|
+
:chef_type => "role",
|
|
63
|
+
:default_attributes => attributes.init_opts,
|
|
64
|
+
:override_attributes => override_attributes.init_opts,
|
|
65
|
+
:description => description
|
|
66
|
+
})
|
|
67
|
+
ca.to_dna _recipes.map {|a| File.basename(a) }, to
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/poolparty/cloud.rb
CHANGED
|
@@ -55,112 +55,6 @@ You did not specify a cloud provider in your clouds.rb. Make sure you have a blo
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
# Chef
|
|
59
|
-
def chef_repo(filepath=nil)
|
|
60
|
-
return @chef_repo if @chef_repo
|
|
61
|
-
@chef_repo = filepath.nil? ? nil : File.expand_path(filepath)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def chef_attributes(hsh={}, &block)
|
|
65
|
-
@chef_attributes ||= ChefAttribute.new(hsh, &block)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def chef_override_attributes(hsh={}, &block)
|
|
69
|
-
@chef_override_attributes ||= ChefAttribute.new(hsh, &block)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Adds a chef recipe to the cloud
|
|
73
|
-
#
|
|
74
|
-
# The hsh parameter is inserted into the chef_override_attributes.
|
|
75
|
-
# The insertion is performed as follows. If
|
|
76
|
-
# the recipe name = "foo::bar" then effectively the call is
|
|
77
|
-
#
|
|
78
|
-
# chef_override_attributes.merge! { :foo => { :bar => hsh } }
|
|
79
|
-
def recipe(recipe_name, hsh={})
|
|
80
|
-
_recipes << recipe_name unless _recipes.include?(recipe_name)
|
|
81
|
-
|
|
82
|
-
head = {}
|
|
83
|
-
tail = head
|
|
84
|
-
recipe_name.split("::").each do |key|
|
|
85
|
-
unless key == "default"
|
|
86
|
-
n = {}
|
|
87
|
-
tail[key] = n
|
|
88
|
-
tail = n
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
tail.replace hsh
|
|
92
|
-
|
|
93
|
-
chef_override_attributes.merge!(head) unless hsh.empty?
|
|
94
|
-
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def recipes(*recipes)
|
|
98
|
-
recipes.each do |r|
|
|
99
|
-
recipe(r)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
private
|
|
104
|
-
|
|
105
|
-
def _recipes
|
|
106
|
-
@_recipes ||= []
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# The NEW actual chef resolver.
|
|
110
|
-
def build_tmp_dir
|
|
111
|
-
base_directory = tmp_path/"etc"/"chef"
|
|
112
|
-
FileUtils.rm_rf base_directory
|
|
113
|
-
puts "Copying the chef-repo into the base directory from #{chef_repo}"
|
|
114
|
-
FileUtils.mkdir_p base_directory/"roles"
|
|
115
|
-
if File.directory?(chef_repo)
|
|
116
|
-
if File.exist?(base_directory)
|
|
117
|
-
# First remove the directory
|
|
118
|
-
FileUtils.remove_entry base_directory, :force => true
|
|
119
|
-
end
|
|
120
|
-
FileUtils.cp_r "#{chef_repo}/.", base_directory
|
|
121
|
-
else
|
|
122
|
-
raise "#{chef_repo} chef repo directory does not exist"
|
|
123
|
-
end
|
|
124
|
-
puts "Creating the dna.json"
|
|
125
|
-
chef_attributes.to_dna [], base_directory/"dna.json", {:run_list => ["role[#{name}]"]}
|
|
126
|
-
write_solo_dot_rb
|
|
127
|
-
write_chef_role_json tmp_path/"etc"/"chef"/"roles/#{name}.json"
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def write_solo_dot_rb(to=tmp_path/"etc"/"chef"/"solo.rb")
|
|
131
|
-
content = <<-EOE
|
|
132
|
-
cookbook_path ["/etc/chef/site-cookbooks", "/etc/chef/cookbooks"]
|
|
133
|
-
role_path "/etc/chef/roles"
|
|
134
|
-
log_level :info
|
|
135
|
-
EOE
|
|
136
|
-
|
|
137
|
-
File.open(to, "w") do |f|
|
|
138
|
-
f << content
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def write_chef_role_json(to=tmp_path/"etc"/"chef"/"dna.json")
|
|
143
|
-
|
|
144
|
-
# Add the parent name and the name of the cloud to
|
|
145
|
-
# the role for easy access in recipes.
|
|
146
|
-
pp = {
|
|
147
|
-
:poolparty => {
|
|
148
|
-
:parent_name => parent.name,
|
|
149
|
-
:name => name,
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
chef_override_attributes.merge! pp
|
|
154
|
-
ca = ChefAttribute.new({
|
|
155
|
-
:name => name,
|
|
156
|
-
:json_class => "Chef::Role",
|
|
157
|
-
:chef_type => "role",
|
|
158
|
-
:default_attributes => chef_attributes.init_opts,
|
|
159
|
-
:override_attributes => chef_override_attributes.init_opts,
|
|
160
|
-
:description => description
|
|
161
|
-
})
|
|
162
|
-
ca.to_dna _recipes.map {|a| File.basename(a) }, to
|
|
163
|
-
end
|
|
164
58
|
|
|
165
59
|
# The pool can either be the parent (the context where the object is declared)
|
|
166
60
|
# or the global pool object
|
|
@@ -191,12 +85,16 @@ log_level :info
|
|
|
191
85
|
end
|
|
192
86
|
end
|
|
193
87
|
end
|
|
194
|
-
|
|
88
|
+
|
|
89
|
+
def chef(chef_type=:solo, &block)
|
|
90
|
+
raise ArgumentError, "Chef type must be one of #{Chef.types.map{|v| ":" + v.to_s}.join(",")}." unless Chef.types.include?(chef_type)
|
|
91
|
+
@chef||=Chef.get_chef(chef_type,self,&block)
|
|
92
|
+
end
|
|
195
93
|
# compile the cloud spec and execute the compiled system and remote calls
|
|
196
94
|
def run
|
|
197
95
|
puts " running on #{cloud_provider.class}"
|
|
198
96
|
cloud_provider.run
|
|
199
|
-
unless
|
|
97
|
+
unless @chef.nil?
|
|
200
98
|
compile!
|
|
201
99
|
bootstrap!
|
|
202
100
|
end
|
|
@@ -280,7 +178,7 @@ No autoscalers defined
|
|
|
280
178
|
end
|
|
281
179
|
|
|
282
180
|
def compile!
|
|
283
|
-
|
|
181
|
+
@chef.compile! unless @chef.nil?
|
|
284
182
|
end
|
|
285
183
|
|
|
286
184
|
def bootstrap!
|
data/lib/poolparty.rb
CHANGED
|
@@ -45,6 +45,9 @@ require "keypair"
|
|
|
45
45
|
$LOAD_PATH.unshift(File.dirname(__FILE__)/"poolparty")
|
|
46
46
|
%w( base
|
|
47
47
|
chef_attribute
|
|
48
|
+
chef
|
|
49
|
+
chef_solo
|
|
50
|
+
chef_client
|
|
48
51
|
cloud pool
|
|
49
52
|
).each do |lib|
|
|
50
53
|
require "poolparty/#{lib}"
|
|
@@ -52,4 +55,4 @@ end
|
|
|
52
55
|
|
|
53
56
|
require 'cloud_providers'
|
|
54
57
|
|
|
55
|
-
puts "PoolParty core loadtime: #{Time.now-t}"
|
|
58
|
+
puts "PoolParty core loadtime: #{Time.now-t}"
|
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.6.
|
|
4
|
+
version: 1.6.2
|
|
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: 2010-
|
|
14
|
+
date: 2010-03-09 00:00:00 -08:00
|
|
15
15
|
default_executable:
|
|
16
16
|
dependencies: []
|
|
17
17
|
|
|
@@ -269,7 +269,10 @@ files:
|
|
|
269
269
|
- lib/keypair.rb
|
|
270
270
|
- lib/poolparty.rb
|
|
271
271
|
- lib/poolparty/base.rb
|
|
272
|
+
- lib/poolparty/chef.rb
|
|
272
273
|
- lib/poolparty/chef_attribute.rb
|
|
274
|
+
- lib/poolparty/chef_client.rb
|
|
275
|
+
- lib/poolparty/chef_solo.rb
|
|
273
276
|
- lib/poolparty/cloud.rb
|
|
274
277
|
- lib/poolparty/pool.rb
|
|
275
278
|
- lib/poolparty/pool_party_error.rb
|