chef 0.7.16 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of chef might be problematic. Click here for more details.
- data/README.rdoc +11 -10
- data/bin/chef-client +2 -2
- data/bin/chef-solo +1 -1
- data/bin/knife +27 -0
- data/bin/shef +49 -0
- data/distro/README +2 -0
- data/distro/{debian → common}/man/man1/chef-indexer.1 +0 -0
- data/distro/{debian → common}/man/man1/chef-server.1 +0 -0
- data/distro/{debian → common}/man/man8/chef-client.8 +0 -0
- data/distro/{debian → common}/man/man8/chef-solo.8 +0 -0
- data/distro/common/man/man8/knife.8 +375 -0
- data/distro/redhat/etc/init.d/chef-client +8 -4
- data/distro/redhat/etc/init.d/chef-server +16 -15
- data/distro/redhat/etc/init.d/chef-server-webui +78 -0
- data/distro/redhat/etc/init.d/chef-solr +76 -0
- data/distro/redhat/etc/init.d/chef-solr-indexer +75 -0
- data/distro/redhat/etc/sysconfig/chef-client +10 -0
- data/distro/redhat/etc/sysconfig/chef-server +10 -0
- data/distro/redhat/etc/sysconfig/chef-server-webui +10 -0
- data/distro/redhat/etc/sysconfig/chef-solr +9 -0
- data/distro/redhat/etc/sysconfig/chef-solr-indexer +7 -0
- data/distro/suse/etc/init.d/chef-client +121 -0
- data/lib/chef.rb +1 -1
- data/lib/chef/api_client.rb +263 -0
- data/lib/chef/application.rb +1 -1
- data/lib/chef/application/client.rb +21 -3
- data/lib/chef/application/knife.rb +144 -0
- data/lib/chef/application/server.rb +2 -1
- data/lib/chef/application/solo.rb +9 -2
- data/lib/chef/cache.rb +61 -0
- data/lib/chef/cache/checksum.rb +70 -0
- data/lib/chef/certificate.rb +154 -0
- data/lib/chef/client.rb +123 -220
- data/lib/chef/compile.rb +9 -21
- data/lib/chef/config.rb +67 -10
- data/lib/chef/cookbook.rb +49 -22
- data/lib/chef/cookbook/metadata.rb +85 -5
- data/lib/chef/cookbook_loader.rb +4 -4
- data/lib/chef/couchdb.rb +99 -30
- data/lib/chef/daemon.rb +1 -1
- data/lib/chef/data_bag.rb +215 -0
- data/lib/chef/data_bag_item.rb +219 -0
- data/lib/chef/exceptions.rb +3 -0
- data/lib/chef/index_queue.rb +29 -0
- data/lib/chef/index_queue/amqp_client.rb +106 -0
- data/lib/chef/index_queue/consumer.rb +76 -0
- data/lib/chef/index_queue/indexable.rb +74 -0
- data/lib/chef/knife.rb +309 -0
- data/lib/chef/knife/client_bulk_delete.rb +40 -0
- data/lib/chef/knife/client_create.rb +62 -0
- data/lib/chef/knife/client_delete.rb +37 -0
- data/lib/chef/knife/client_edit.rb +37 -0
- data/lib/chef/knife/client_list.rb +40 -0
- data/lib/chef/knife/client_reregister.rb +48 -0
- data/lib/chef/knife/client_show.rb +42 -0
- data/lib/chef/knife/configure.rb +123 -0
- data/lib/chef/knife/cookbook_bulk_delete.rb +46 -0
- data/lib/chef/knife/cookbook_delete.rb +41 -0
- data/lib/chef/knife/cookbook_download.rb +57 -0
- data/lib/chef/knife/cookbook_list.rb +41 -0
- data/lib/chef/knife/cookbook_metadata.rb +87 -0
- data/lib/chef/knife/cookbook_show.rb +75 -0
- data/lib/chef/knife/cookbook_upload.rb +179 -0
- data/lib/chef/knife/data_bag_create.rb +43 -0
- data/lib/chef/knife/data_bag_delete.rb +43 -0
- data/lib/chef/knife/data_bag_edit.rb +49 -0
- data/lib/chef/knife/data_bag_list.rb +42 -0
- data/lib/chef/knife/data_bag_show.rb +40 -0
- data/lib/chef/knife/ec2_instance_data.rb +46 -0
- data/lib/chef/knife/index_rebuild.rb +51 -0
- data/lib/chef/knife/node_bulk_delete.rb +43 -0
- data/lib/chef/knife/node_create.rb +39 -0
- data/lib/chef/knife/node_delete.rb +36 -0
- data/lib/chef/knife/node_edit.rb +36 -0
- data/lib/chef/knife/node_from_file.rb +42 -0
- data/lib/chef/knife/node_list.rb +41 -0
- data/lib/chef/knife/node_run_list_add.rb +64 -0
- data/lib/chef/knife/node_run_list_remove.rb +45 -0
- data/lib/chef/knife/node_show.rb +46 -0
- data/lib/chef/knife/role_bulk_delete.rb +44 -0
- data/lib/chef/knife/role_create.rb +44 -0
- data/lib/chef/knife/role_delete.rb +36 -0
- data/lib/chef/knife/role_edit.rb +37 -0
- data/lib/chef/knife/role_from_file.rb +46 -0
- data/lib/chef/knife/role_list.rb +40 -0
- data/lib/chef/knife/role_show.rb +43 -0
- data/lib/chef/knife/search.rb +94 -0
- data/lib/chef/knife/ssh.rb +170 -0
- data/lib/chef/log.rb +30 -8
- data/lib/chef/mixin/checksum.rb +2 -7
- data/lib/chef/mixin/command.rb +32 -13
- data/lib/chef/mixin/convert_to_class_name.rb +15 -0
- data/lib/chef/mixin/deep_merge.rb +199 -11
- data/lib/chef/mixin/generate_url.rb +18 -9
- data/lib/chef/mixin/language.rb +29 -1
- data/lib/chef/mixin/language_include_attribute.rb +56 -0
- data/lib/chef/mixin/language_include_recipe.rb +53 -0
- data/lib/chef/mixin/params_validate.rb +25 -12
- data/lib/chef/mixin/recipe_definition_dsl_core.rb +2 -0
- data/lib/chef/mixin/template.rb +11 -1
- data/lib/chef/mixin/xml_escape.rb +87 -0
- data/lib/chef/node.rb +144 -122
- data/lib/chef/openid_registration.rb +12 -5
- data/lib/chef/platform.rb +89 -47
- data/lib/chef/provider/breakpoint.rb +36 -0
- data/lib/chef/provider/cron.rb +5 -6
- data/lib/chef/provider/deploy.rb +43 -10
- data/lib/chef/provider/deploy/revision.rb +2 -3
- data/lib/chef/provider/erl_call.rb +72 -0
- data/lib/chef/provider/file.rb +8 -4
- data/lib/chef/provider/git.rb +10 -5
- data/lib/chef/provider/group/dscl.rb +128 -0
- data/lib/chef/provider/http_request.rb +6 -2
- data/lib/chef/provider/ifconfig.rb +1 -0
- data/lib/chef/provider/link.rb +1 -1
- data/lib/chef/provider/log.rb +53 -0
- data/lib/chef/provider/mdadm.rb +88 -0
- data/lib/chef/provider/mount/mount.rb +1 -1
- data/lib/chef/provider/package.rb +1 -1
- data/lib/chef/provider/package/easy_install.rb +106 -0
- data/lib/chef/provider/package/pacman.rb +101 -0
- data/lib/chef/provider/package/portage.rb +1 -1
- data/lib/chef/provider/package/rpm.rb +10 -8
- data/lib/chef/provider/package/yum-dump.py +22 -3
- data/lib/chef/provider/package/yum.rb +32 -8
- data/lib/chef/provider/package/zypper.rb +132 -0
- data/lib/chef/provider/remote_directory.rb +58 -49
- data/lib/chef/provider/remote_file.rb +1 -1
- data/lib/chef/provider/route.rb +136 -80
- data/lib/chef/provider/ruby_block.rb +18 -1
- data/lib/chef/provider/service/arch.rb +109 -0
- data/lib/chef/provider/service/freebsd.rb +0 -1
- data/lib/chef/provider/service/simple.rb +2 -3
- data/lib/chef/provider/service/upstart.rb +191 -0
- data/lib/chef/provider/subversion.rb +12 -4
- data/lib/chef/provider/template.rb +85 -53
- data/lib/chef/provider/user.rb +1 -1
- data/lib/chef/provider/user/dscl.rb +277 -0
- data/lib/chef/provider/user/useradd.rb +1 -0
- data/lib/chef/recipe.rb +2 -41
- data/lib/chef/resource.rb +9 -3
- data/lib/chef/resource/breakpoint.rb +35 -0
- data/lib/chef/resource/deploy.rb +16 -2
- data/lib/chef/resource/easy_install_package.rb +41 -0
- data/lib/chef/resource/erl_call.rb +83 -0
- data/lib/chef/resource/freebsd_package.rb +35 -0
- data/lib/chef/resource/log.rb +62 -0
- data/lib/chef/resource/mdadm.rb +82 -0
- data/lib/chef/resource/pacman_package.rb +33 -0
- data/lib/chef/resource/ruby_block.rb +21 -2
- data/lib/chef/resource/scm.rb +8 -0
- data/lib/chef/resource/subversion.rb +1 -0
- data/lib/chef/resource/user.rb +5 -2
- data/lib/chef/resource/yum_package.rb +36 -0
- data/lib/chef/resource_collection.rb +17 -9
- data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
- data/lib/chef/rest.rb +166 -81
- data/lib/chef/role.rb +114 -38
- data/lib/chef/run_list.rb +15 -6
- data/lib/chef/runner.rb +13 -11
- data/lib/chef/search/query.rb +60 -0
- data/lib/chef/shef.rb +220 -0
- data/lib/chef/shef/ext.rb +297 -0
- data/lib/chef/shef/shef_session.rb +175 -0
- data/lib/chef/streaming_cookbook_uploader.rb +187 -0
- data/lib/chef/tasks/chef_repo.rake +53 -155
- data/lib/chef/util/file_edit.rb +94 -96
- data/lib/chef/webui_user.rb +233 -0
- metadata +219 -63
- data/distro/debian/etc/init.d/chef-indexer +0 -175
- data/distro/redhat/etc/chef/client.rb +0 -16
- data/distro/redhat/etc/chef/indexer.rb +0 -10
- data/distro/redhat/etc/chef/server.rb +0 -22
- data/distro/redhat/etc/init.d/chef-indexer +0 -76
- data/lib/chef/application/indexer.rb +0 -141
- data/lib/chef/queue.rb +0 -145
- data/lib/chef/search.rb +0 -88
- data/lib/chef/search/result.rb +0 -64
- data/lib/chef/search_index.rb +0 -77
- data/lib/chef/util/fileedit.rb +0 -121
@@ -0,0 +1,170 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2009 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/knife'
|
20
|
+
require 'chef/data_bag_item'
|
21
|
+
|
22
|
+
class Chef
|
23
|
+
class Knife
|
24
|
+
class Ssh < Knife
|
25
|
+
|
26
|
+
banner "Sub-Command: ssh QUERY COMMAND (options)"
|
27
|
+
|
28
|
+
option :concurrency,
|
29
|
+
:short => "-C NUM",
|
30
|
+
:long => "--concurrency NUM",
|
31
|
+
:description => "The number of concurrent connections",
|
32
|
+
:default => nil
|
33
|
+
|
34
|
+
option :attribute,
|
35
|
+
:short => "-a ATTR",
|
36
|
+
:long => "--attribute ATTR",
|
37
|
+
:description => "The attribute to use for opening the connection - default is fqdn",
|
38
|
+
:default => "fqdn"
|
39
|
+
|
40
|
+
def session
|
41
|
+
@session ||= Net::SSH::Multi.start(:concurrent_connections => config[:concurrency])
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def h
|
46
|
+
@highline ||= HighLine.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def configure_session
|
50
|
+
q = Chef::Search::Query.new
|
51
|
+
q.search(:node, @name_args[0]) do |item|
|
52
|
+
data = format_for_display(item)
|
53
|
+
Chef::Log.debug("Adding #{data[config[:attribute]]}")
|
54
|
+
session.use data[config[:attribute]]
|
55
|
+
@longest = data[config[:attribute]].length if data[config[:attribute]].length > @longest
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def fixup_sudo(command)
|
60
|
+
command.sub(/^sudo/, 'sudo -p \'knife sudo password: \'')
|
61
|
+
end
|
62
|
+
|
63
|
+
def print_data(host, data)
|
64
|
+
if data =~ /\n/
|
65
|
+
data.split(/\n/).each { |d| print_data(host, d) }
|
66
|
+
else
|
67
|
+
padding = @longest - host.length
|
68
|
+
print h.color(host, :cyan)
|
69
|
+
padding.downto(0) { print " " }
|
70
|
+
puts data
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def ssh_command(command, subsession=nil)
|
75
|
+
subsession ||= session
|
76
|
+
command = fixup_sudo(command)
|
77
|
+
subsession.open_channel do |ch|
|
78
|
+
ch.request_pty
|
79
|
+
ch.exec command do |ch, success|
|
80
|
+
raise ArgumentError, "Cannot execute #{command}" unless success
|
81
|
+
ch.on_data do |ichannel, data|
|
82
|
+
print_data(ichannel[:host], data)
|
83
|
+
if data =~ /^knife sudo password: /
|
84
|
+
ichannel.send_data("#{get_password}\n")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
session.loop
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_password
|
93
|
+
@password ||= h.ask("Enter your password: ") { |q| q.echo = false }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Present the prompt and read a single line from the console. It also
|
97
|
+
# detects ^D and returns "exit" in that case. Adds the input to the
|
98
|
+
# history, unless the input is empty. Loops repeatedly until a non-empty
|
99
|
+
# line is input.
|
100
|
+
def read_line
|
101
|
+
loop do
|
102
|
+
command = reader.readline("#{h.color('knife-ssh>', :bold)} ", true)
|
103
|
+
|
104
|
+
if command.nil?
|
105
|
+
command = "exit"
|
106
|
+
puts(command)
|
107
|
+
else
|
108
|
+
command.strip!
|
109
|
+
end
|
110
|
+
|
111
|
+
unless command.empty?
|
112
|
+
return command
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def reader
|
118
|
+
Readline
|
119
|
+
end
|
120
|
+
|
121
|
+
def interactive
|
122
|
+
puts "Connected to #{h.list(session.servers_for.collect { |s| h.color(s.host, :cyan) }, :inline, " and ")}"
|
123
|
+
puts
|
124
|
+
puts "To run a command on a list of servers, do:"
|
125
|
+
puts " on SERVER1 SERVER2 SERVER3; COMMAND"
|
126
|
+
puts " Example: on latte foamy; echo foobar"
|
127
|
+
puts
|
128
|
+
puts "To exit interactive mode, use 'quit!'"
|
129
|
+
puts
|
130
|
+
while 1
|
131
|
+
command = read_line
|
132
|
+
case command
|
133
|
+
when 'quit!'
|
134
|
+
puts 'Bye!'
|
135
|
+
break
|
136
|
+
when /^on (.+?); (.+)$/
|
137
|
+
raw_list = $1.split(" ")
|
138
|
+
server_list = Array.new
|
139
|
+
session.servers.each do |session_server|
|
140
|
+
server_list << session_server if raw_list.include?(session_server.host)
|
141
|
+
end
|
142
|
+
command = $2
|
143
|
+
ssh_command(command, session.on(*server_list))
|
144
|
+
else
|
145
|
+
ssh_command(command)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def run
|
151
|
+
@longest = 0
|
152
|
+
|
153
|
+
require 'net/ssh/multi'
|
154
|
+
require 'readline'
|
155
|
+
require 'highline'
|
156
|
+
|
157
|
+
configure_session
|
158
|
+
|
159
|
+
if @name_args[1] == "interactive"
|
160
|
+
interactive
|
161
|
+
else
|
162
|
+
ssh_command(@name_args[1..-1].join(" "))
|
163
|
+
end
|
164
|
+
|
165
|
+
session.close
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
data/lib/chef/log.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
-
# Author:: AJ Christensen (<@aj@
|
3
|
+
# Author:: AJ Christensen (<@aj@opscode.com>)
|
4
|
+
# Author:: Christopher Brown (<cb@opscode.com>)
|
4
5
|
# Copyright:: Copyright (c) 2008 Opscode, Inc.
|
5
6
|
# License:: Apache License, Version 2.0
|
6
7
|
#
|
@@ -22,18 +23,39 @@ require 'mixlib/log'
|
|
22
23
|
class Chef
|
23
24
|
class Log
|
24
25
|
extend Mixlib::Log
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_accessor :verbose
|
29
|
+
attr_reader :verbose_logger
|
30
|
+
protected :verbose_logger
|
31
|
+
|
32
|
+
def verbose
|
33
|
+
!(@verbose_logger.nil?)
|
34
|
+
end
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
36
|
+
def verbose=(value)
|
37
|
+
if value
|
38
|
+
@verbose_logger ||= Logger.new(STDOUT)
|
39
|
+
@verbose_logger.level = self.logger.level
|
40
|
+
@verbose_logger.formatter = self.logger.formatter
|
41
|
+
else
|
42
|
+
@verbose_logger = nil
|
43
|
+
end
|
44
|
+
self.verbose
|
31
45
|
end
|
46
|
+
|
47
|
+
def method_missing(method_symbol, *args)
|
48
|
+
self.verbose_logger.send(method_symbol, *args) if self.verbose
|
49
|
+
logger.send(method_symbol, *args)
|
50
|
+
end
|
51
|
+
end
|
32
52
|
|
33
|
-
|
34
|
-
|
53
|
+
class Formatter
|
54
|
+
def self.show_time=(*args)
|
55
|
+
Mixlib::Log::Formatter.show_time = *args
|
35
56
|
end
|
36
57
|
end
|
58
|
+
|
37
59
|
end
|
38
60
|
end
|
39
61
|
|
data/lib/chef/mixin/checksum.rb
CHANGED
@@ -17,19 +17,14 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require 'digest/sha2'
|
20
|
+
require 'chef/cache/checksum'
|
20
21
|
|
21
22
|
class Chef
|
22
23
|
module Mixin
|
23
24
|
module Checksum
|
24
25
|
|
25
26
|
def checksum(file)
|
26
|
-
|
27
|
-
fh = ::File.open(file)
|
28
|
-
fh.each do |line|
|
29
|
-
digest.update(line)
|
30
|
-
end
|
31
|
-
fh.close
|
32
|
-
digest.hexdigest
|
27
|
+
Chef::Cache::Checksum.checksum_for_file(file)
|
33
28
|
end
|
34
29
|
|
35
30
|
end
|
data/lib/chef/mixin/command.rb
CHANGED
@@ -39,14 +39,16 @@ class Chef
|
|
39
39
|
# === Returns
|
40
40
|
# true:: Returns true if the block is true, or if the command returns 0
|
41
41
|
# false:: Returns false if the block is false, or if the command returns a non-zero exit code.
|
42
|
-
def only_if(command)
|
42
|
+
def only_if(command, args = {})
|
43
43
|
if command.kind_of?(Proc)
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
chdir_or_tmpdir(args[:cwd]) do
|
45
|
+
res = command.call
|
46
|
+
unless res
|
47
|
+
return false
|
48
|
+
end
|
47
49
|
end
|
48
50
|
else
|
49
|
-
status = run_command(:command => command, :ignore_failure => true)
|
51
|
+
status = run_command({:command => command, :ignore_failure => true}.merge(args))
|
50
52
|
if status.exitstatus != 0
|
51
53
|
return false
|
52
54
|
end
|
@@ -68,14 +70,16 @@ class Chef
|
|
68
70
|
# === Returns
|
69
71
|
# true:: Returns true if the block is false, or if the command returns a non-zero exit status.
|
70
72
|
# false:: Returns false if the block is true, or if the command returns a 0 exit status.
|
71
|
-
def not_if(command)
|
73
|
+
def not_if(command, args = {})
|
72
74
|
if command.kind_of?(Proc)
|
73
|
-
|
74
|
-
|
75
|
-
|
75
|
+
chdir_or_tmpdir(args[:cwd]) do
|
76
|
+
res = command.call
|
77
|
+
if res
|
78
|
+
return false
|
79
|
+
end
|
76
80
|
end
|
77
81
|
else
|
78
|
-
status = run_command(:command => command, :ignore_failure => true)
|
82
|
+
status = run_command({:command => command, :ignore_failure => true}.merge(args))
|
79
83
|
if status.exitstatus == 0
|
80
84
|
return false
|
81
85
|
end
|
@@ -130,7 +134,7 @@ class Chef
|
|
130
134
|
stdout_string, stderr_string = stdout.string.chomp, stderr.string.chomp
|
131
135
|
end
|
132
136
|
|
133
|
-
args[:cwd] ||= Dir.tmpdir
|
137
|
+
args[:cwd] ||= Dir.tmpdir
|
134
138
|
unless File.directory?(args[:cwd])
|
135
139
|
raise Chef::Exceptions::Exec, "#{args[:cwd]} does not exist or is not a directory"
|
136
140
|
end
|
@@ -306,6 +310,9 @@ class Chef
|
|
306
310
|
begin
|
307
311
|
if args[:waitlast]
|
308
312
|
b[cid, *pi]
|
313
|
+
# send EOF so that if the child process is reading from STDIN
|
314
|
+
# it will actually finish up and exit
|
315
|
+
pi[0].close_write
|
309
316
|
Process.waitpid2(cid).last
|
310
317
|
else
|
311
318
|
# This took some doing.
|
@@ -341,6 +348,7 @@ class Chef
|
|
341
348
|
channels_to_watch << stderr if !stderr_finished
|
342
349
|
ready = IO.select(channels_to_watch, nil, nil, 1.0)
|
343
350
|
rescue Errno::EAGAIN
|
351
|
+
ensure
|
344
352
|
results = Process.waitpid2(cid, Process::WNOHANG)
|
345
353
|
if results
|
346
354
|
stdout_finished = true
|
@@ -365,11 +373,11 @@ class Chef
|
|
365
373
|
end
|
366
374
|
end
|
367
375
|
end
|
368
|
-
results = Process.waitpid2(cid)
|
376
|
+
results = Process.waitpid2(cid) unless results
|
369
377
|
o.rewind
|
370
378
|
e.rewind
|
371
379
|
b[cid, pi[0], o, e]
|
372
|
-
results
|
380
|
+
results.last
|
373
381
|
end
|
374
382
|
ensure
|
375
383
|
pi.each{|fd| fd.close unless fd.closed?}
|
@@ -381,6 +389,17 @@ class Chef
|
|
381
389
|
|
382
390
|
module_function :popen4
|
383
391
|
|
392
|
+
def chdir_or_tmpdir(dir, &block)
|
393
|
+
dir ||= Dir.tmpdir
|
394
|
+
unless File.directory?(dir)
|
395
|
+
raise Chef::Exceptions::Exec, "#{dir} does not exist or is not a directory"
|
396
|
+
end
|
397
|
+
Dir.chdir(dir) do
|
398
|
+
block.call
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
module_function :chdir_or_tmpdir
|
384
403
|
end
|
385
404
|
end
|
386
405
|
end
|
@@ -20,6 +20,7 @@
|
|
20
20
|
class Chef
|
21
21
|
module Mixin
|
22
22
|
module ConvertToClassName
|
23
|
+
extend self
|
23
24
|
|
24
25
|
def convert_to_class_name(str)
|
25
26
|
rname = nil
|
@@ -38,6 +39,20 @@ class Chef
|
|
38
39
|
rname
|
39
40
|
end
|
40
41
|
|
42
|
+
def convert_to_snake_case(str, namespace=nil)
|
43
|
+
str = str.dup
|
44
|
+
str.sub!(/^#{namespace}(\:\:)?/, '') if namespace
|
45
|
+
str.gsub!(/[A-Z]/) {|s| "_" + s}
|
46
|
+
str.downcase!
|
47
|
+
str.sub!(/^\_/, "")
|
48
|
+
str
|
49
|
+
end
|
50
|
+
|
51
|
+
def snake_case_basename(str)
|
52
|
+
with_namespace = convert_to_snake_case(str)
|
53
|
+
with_namespace.split("::").last.sub(/^_/, '')
|
54
|
+
end
|
55
|
+
|
41
56
|
def filename_to_qualified_string(base, filename)
|
42
57
|
file_base = File.basename(filename, ".rb")
|
43
58
|
base.to_s + (file_base == 'default' ? '' : "_#{file_base}")
|
@@ -1,6 +1,8 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Author:: Steve Midgley (http://www.misuse.org/science)
|
3
4
|
# Copyright:: Copyright (c) 2009 Opscode, Inc.
|
5
|
+
# Copyright:: Copyright (c) 2008 Steve Midgley
|
4
6
|
# License:: Apache License, Version 2.0
|
5
7
|
#
|
6
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,22 +17,208 @@
|
|
15
17
|
# See the License for the specific language governing permissions and
|
16
18
|
# limitations under the License.
|
17
19
|
|
20
|
+
# Notice:
|
21
|
+
# This code is imported from deep_merge by Steve Midgley. deep_merge is
|
22
|
+
# available under the MIT license from
|
23
|
+
# http://trac.misuse.org/science/wiki/DeepMerge
|
24
|
+
|
18
25
|
class Chef
|
19
26
|
module Mixin
|
20
|
-
|
27
|
+
module DeepMerge
|
21
28
|
def self.merge(first, second)
|
22
|
-
first
|
23
|
-
|
24
|
-
second = Mash.new(second).to_hash unless second.kind_of?(Mash)
|
25
|
-
second = second.to_hash
|
26
|
-
# Originally From: http://www.ruby-forum.com/topic/142809
|
27
|
-
# Author: Stefan Rusterholz
|
28
|
-
merger = proc do |key,v1,v2|
|
29
|
-
v1.respond_to?(:keys) && v2.respond_to?(:keys) ? v1.merge(v2, &merger) : v2
|
30
|
-
end
|
29
|
+
first = Mash.new(first) unless first.kind_of?(Mash)
|
30
|
+
second = Mash.new(second) unless second.kind_of?(Mash)
|
31
31
|
|
32
|
-
|
32
|
+
DeepMerge.deep_merge!(second, first, {:knockout_prefix => "!merge:", :preserve_unmergeables => false})
|
33
|
+
end
|
34
|
+
|
35
|
+
class InvalidParameter < StandardError; end
|
36
|
+
|
37
|
+
DEFAULT_FIELD_KNOCKOUT_PREFIX = '--' unless defined?(DEFAULT_FIELD_KNOCKOUT_PREFIX)
|
38
|
+
|
39
|
+
# Deep Merge core documentation.
|
40
|
+
# deep_merge! method permits merging of arbitrary child elements. The two top level
|
41
|
+
# elements must be hashes. These hashes can contain unlimited (to stack limit) levels
|
42
|
+
# of child elements. These child elements to not have to be of the same types.
|
43
|
+
# Where child elements are of the same type, deep_merge will attempt to merge them together.
|
44
|
+
# Where child elements are not of the same type, deep_merge will skip or optionally overwrite
|
45
|
+
# the destination element with the contents of the source element at that level.
|
46
|
+
# So if you have two hashes like this:
|
47
|
+
# source = {:x => [1,2,3], :y => 2}
|
48
|
+
# dest = {:x => [4,5,'6'], :y => [7,8,9]}
|
49
|
+
# dest.deep_merge!(source)
|
50
|
+
# Results: {:x => [1,2,3,4,5,'6'], :y => 2}
|
51
|
+
# By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
|
52
|
+
# To avoid this, use "deep_merge" (no bang/exclamation mark)
|
53
|
+
#
|
54
|
+
# Options:
|
55
|
+
# Options are specified in the last parameter passed, which should be in hash format:
|
56
|
+
# hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
|
57
|
+
# :preserve_unmergeables DEFAULT: false
|
58
|
+
# Set to true to skip any unmergeable elements from source
|
59
|
+
# :knockout_prefix DEFAULT: nil
|
60
|
+
# Set to string value to signify prefix which deletes elements from existing element
|
61
|
+
# :sort_merged_arrays DEFAULT: false
|
62
|
+
# Set to true to sort all arrays that are merged together
|
63
|
+
# :unpack_arrays DEFAULT: nil
|
64
|
+
# Set to string value to run "Array::join" then "String::split" against all arrays
|
65
|
+
# :merge_debug DEFAULT: false
|
66
|
+
# Set to true to get console output of merge process for debugging
|
67
|
+
#
|
68
|
+
# Selected Options Details:
|
69
|
+
# :knockout_prefix => The purpose of this is to provide a way to remove elements
|
70
|
+
# from existing Hash by specifying them in a special way in incoming hash
|
71
|
+
# source = {:x => ['--1', '2']}
|
72
|
+
# dest = {:x => ['1', '3']}
|
73
|
+
# dest.ko_deep_merge!(source)
|
74
|
+
# Results: {:x => ['2','3']}
|
75
|
+
# Additionally, if the knockout_prefix is passed alone as a string, it will cause
|
76
|
+
# the entire element to be removed:
|
77
|
+
# source = {:x => '--'}
|
78
|
+
# dest = {:x => [1,2,3]}
|
79
|
+
# dest.ko_deep_merge!(source)
|
80
|
+
# Results: {:x => ""}
|
81
|
+
# :unpack_arrays => The purpose of this is to permit compound elements to be passed
|
82
|
+
# in as strings and to be converted into discrete array elements
|
83
|
+
# irsource = {:x => ['1,2,3', '4']}
|
84
|
+
# dest = {:x => ['5','6','7,8']}
|
85
|
+
# dest.deep_merge!(source, {:unpack_arrays => ','})
|
86
|
+
# Results: {:x => ['1','2','3','4','5','6','7','8'}
|
87
|
+
# Why: If receiving data from an HTML form, this makes it easy for a checkbox
|
88
|
+
# to pass multiple values from within a single HTML element
|
89
|
+
#
|
90
|
+
# There are many tests for this library - and you can learn more about the features
|
91
|
+
# and usages of deep_merge! by just browsing the test examples
|
92
|
+
def self.deep_merge!(source, dest, options = {})
|
93
|
+
# turn on this line for stdout debugging text
|
94
|
+
merge_debug = options[:merge_debug] || false
|
95
|
+
overwrite_unmergeable = !options[:preserve_unmergeables]
|
96
|
+
knockout_prefix = options[:knockout_prefix] || nil
|
97
|
+
raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!" if knockout_prefix == ""
|
98
|
+
raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!" if knockout_prefix && !overwrite_unmergeable
|
99
|
+
# if present: we will split and join arrays on this char before merging
|
100
|
+
array_split_char = options[:unpack_arrays] || false
|
101
|
+
# request that we sort together any arrays when they are merged
|
102
|
+
sort_merged_arrays = options[:sort_merged_arrays] || false
|
103
|
+
di = options[:debug_indent] || ''
|
104
|
+
# do nothing if source is nil
|
105
|
+
return dest if source.nil?
|
106
|
+
# if dest doesn't exist, then simply copy source to it
|
107
|
+
if dest.nil? && overwrite_unmergeable
|
108
|
+
dest = source; return dest
|
109
|
+
end
|
110
|
+
|
111
|
+
puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
|
112
|
+
if source.kind_of?(Hash)
|
113
|
+
puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
114
|
+
source.each do |src_key, src_value|
|
115
|
+
if dest.kind_of?(Hash)
|
116
|
+
puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
|
117
|
+
if dest[src_key]
|
118
|
+
puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
|
119
|
+
dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' '))
|
120
|
+
else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
|
121
|
+
puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
|
122
|
+
# note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
|
123
|
+
begin
|
124
|
+
src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
|
125
|
+
rescue TypeError
|
126
|
+
src_dup = src_value
|
127
|
+
end
|
128
|
+
dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' '))
|
129
|
+
end
|
130
|
+
else # dest isn't a hash, so we overwrite it completely (if permitted)
|
131
|
+
if overwrite_unmergeable
|
132
|
+
puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug
|
133
|
+
dest = overwrite_unmergeables(source, dest, options)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
elsif source.kind_of?(Array)
|
138
|
+
puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
139
|
+
# if we are instructed, join/split any source arrays before processing
|
140
|
+
if array_split_char
|
141
|
+
puts "#{di} split/join on source: #{source.inspect}" if merge_debug
|
142
|
+
source = source.join(array_split_char).split(array_split_char)
|
143
|
+
if dest.kind_of?(Array)
|
144
|
+
dest = dest.join(array_split_char).split(array_split_char)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
# if there's a naked knockout_prefix in source, that means we are to truncate dest
|
148
|
+
if source.index(knockout_prefix)
|
149
|
+
dest = clear_or_nil(dest); source.delete(knockout_prefix)
|
150
|
+
end
|
151
|
+
if dest.kind_of?(Array)
|
152
|
+
if knockout_prefix
|
153
|
+
print "#{di} knocking out: " if merge_debug
|
154
|
+
# remove knockout prefix items from both source and dest
|
155
|
+
source.delete_if do |ko_item|
|
156
|
+
retval = false
|
157
|
+
item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
|
158
|
+
if item != ko_item
|
159
|
+
print "#{ko_item} - " if merge_debug
|
160
|
+
dest.delete(item)
|
161
|
+
dest.delete(ko_item)
|
162
|
+
retval = true
|
163
|
+
end
|
164
|
+
retval
|
165
|
+
end
|
166
|
+
puts if merge_debug
|
167
|
+
end
|
168
|
+
puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
169
|
+
dest = dest | source
|
170
|
+
dest.sort! if sort_merged_arrays
|
171
|
+
elsif overwrite_unmergeable
|
172
|
+
puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
|
173
|
+
dest = overwrite_unmergeables(source, dest, options)
|
174
|
+
end
|
175
|
+
else # src_hash is not an array or hash, so we'll have to overwrite dest
|
176
|
+
puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
177
|
+
dest = overwrite_unmergeables(source, dest, options)
|
178
|
+
end
|
179
|
+
puts "#{di}Returning #{dest.inspect}" if merge_debug
|
180
|
+
dest
|
181
|
+
end # deep_merge!
|
182
|
+
|
183
|
+
# allows deep_merge! to uniformly handle overwriting of unmergeable entities
|
184
|
+
def self.overwrite_unmergeables(source, dest, options)
|
185
|
+
merge_debug = options[:merge_debug] || false
|
186
|
+
overwrite_unmergeable = !options[:preserve_unmergeables]
|
187
|
+
knockout_prefix = options[:knockout_prefix] || false
|
188
|
+
di = options[:debug_indent] || ''
|
189
|
+
if knockout_prefix && overwrite_unmergeable
|
190
|
+
if source.kind_of?(String) # remove knockout string from source before overwriting dest
|
191
|
+
src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
|
192
|
+
elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
|
193
|
+
src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
|
194
|
+
else
|
195
|
+
src_tmp = source
|
196
|
+
end
|
197
|
+
if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
|
198
|
+
puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
|
199
|
+
dest = src_tmp
|
200
|
+
else # if we do find a knockout_prefix, then we just delete dest
|
201
|
+
puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
|
202
|
+
dest = ""
|
203
|
+
end
|
204
|
+
elsif overwrite_unmergeable
|
205
|
+
dest = source
|
206
|
+
end
|
207
|
+
dest
|
33
208
|
end
|
209
|
+
|
210
|
+
def self.clear_or_nil(obj)
|
211
|
+
if obj.respond_to?(:clear)
|
212
|
+
obj.clear
|
213
|
+
else
|
214
|
+
obj = nil
|
215
|
+
end
|
216
|
+
obj
|
217
|
+
end
|
218
|
+
|
34
219
|
end
|
220
|
+
|
35
221
|
end
|
36
222
|
end
|
223
|
+
|
224
|
+
|