chef 0.8.16 → 0.9.0.a3
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/bin/shef +1 -0
- data/distro/common/man/man1/chef-server-webui.1 +106 -0
- data/distro/common/man/man1/chef-server.1 +0 -1
- data/distro/common/man/man1/chef-solr-indexer.1 +55 -0
- data/distro/common/man/man1/chef-solr.1 +55 -0
- data/distro/common/man/man8/chef-client.8 +4 -2
- data/distro/common/man/man8/chef-solo.8 +1 -2
- data/distro/common/man/man8/chef-solr-rebuild.8 +37 -0
- data/distro/common/man/man8/knife.8 +668 -266
- data/distro/common/man/man8/shef.8 +45 -0
- data/distro/common/markdown/README +3 -0
- data/distro/common/markdown/knife.mkd +520 -0
- data/distro/debian/etc/default/chef-client +4 -0
- data/distro/debian/etc/default/chef-server +6 -0
- data/distro/debian/etc/default/chef-server-webui +6 -0
- data/distro/debian/etc/default/chef-solr +4 -0
- data/distro/debian/etc/default/chef-solr-indexer +4 -0
- data/distro/debian/etc/init.d/chef-client +41 -41
- data/distro/debian/etc/init.d/chef-server +10 -10
- data/distro/debian/etc/init.d/chef-server-webui +121 -0
- data/distro/debian/etc/init.d/chef-solr +177 -0
- data/distro/debian/etc/init.d/chef-solr-indexer +176 -0
- data/distro/redhat/etc/init.d/chef-client +76 -48
- data/distro/redhat/etc/init.d/chef-server +85 -51
- data/distro/redhat/etc/init.d/chef-server-webui +85 -51
- data/distro/redhat/etc/init.d/chef-solr +77 -49
- data/distro/redhat/etc/init.d/chef-solr-indexer +77 -48
- data/distro/redhat/etc/logrotate.d/chef-client +8 -0
- data/distro/redhat/etc/logrotate.d/chef-server +8 -0
- data/distro/redhat/etc/logrotate.d/chef-server-webui +8 -0
- data/distro/redhat/etc/logrotate.d/chef-solr +8 -0
- data/distro/redhat/etc/logrotate.d/chef-solr-indexer +8 -0
- data/distro/redhat/etc/sysconfig/chef-client +9 -4
- data/distro/redhat/etc/sysconfig/chef-server +10 -6
- data/distro/redhat/etc/sysconfig/chef-server-webui +10 -6
- data/distro/redhat/etc/sysconfig/chef-solr +3 -4
- data/distro/redhat/etc/sysconfig/chef-solr-indexer +3 -3
- data/lib/chef.rb +16 -5
- data/lib/chef/application/knife.rb +2 -2
- data/lib/chef/application/solo.rb +1 -7
- data/lib/chef/cache/checksum.rb +12 -5
- data/lib/chef/cache/file_cache_by_checksum.rb +52 -0
- data/lib/chef/checksum.rb +115 -0
- data/lib/chef/client.rb +193 -185
- data/lib/chef/config.rb +9 -1
- data/lib/chef/cookbook/cookbook_collection.rb +43 -0
- data/lib/chef/cookbook/file_system_file_vendor.rb +53 -0
- data/lib/chef/cookbook/file_vendor.rb +47 -0
- data/lib/chef/cookbook/metadata.rb +34 -35
- data/lib/chef/cookbook/metadata/version.rb +1 -1
- data/lib/chef/cookbook_loader.rb +70 -45
- data/lib/chef/cookbook_version.rb +760 -0
- data/lib/chef/couchdb.rb +8 -5
- data/lib/chef/data_bag_item.rb +5 -5
- data/lib/chef/exceptions.rb +10 -0
- data/lib/chef/file_access_control.rb +134 -0
- data/lib/chef/handler.rb +62 -0
- data/lib/chef/handler/json_file.rb +47 -0
- data/lib/chef/knife.rb +14 -2
- data/lib/chef/knife/bootstrap.rb +126 -0
- data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
- data/lib/chef/knife/cookbook_delete.rb +4 -4
- data/lib/chef/knife/cookbook_download.rb +57 -26
- data/lib/chef/knife/cookbook_metadata.rb +2 -2
- data/lib/chef/knife/cookbook_show.rb +30 -11
- data/lib/chef/knife/cookbook_upload.rb +113 -86
- data/lib/chef/knife/ec2_server_create.rb +146 -0
- data/lib/chef/knife/ec2_server_delete.rb +84 -0
- data/lib/chef/knife/ec2_server_list.rb +82 -0
- data/lib/chef/knife/status.rb +51 -0
- data/lib/chef/mixin/language_include_attribute.rb +16 -11
- data/lib/chef/mixin/language_include_recipe.rb +15 -16
- data/lib/chef/mixin/recipe_definition_dsl_core.rb +17 -20
- data/lib/chef/mixin/shell_out.rb +38 -0
- data/lib/chef/mixins.rb +1 -1
- data/lib/chef/node.rb +190 -63
- data/lib/chef/node/attribute.rb +92 -78
- data/lib/chef/platform.rb +24 -4
- data/lib/chef/provider.rb +28 -10
- data/lib/chef/provider/breakpoint.rb +2 -2
- data/lib/chef/provider/cookbook_file.rb +96 -0
- data/lib/chef/provider/cron.rb +2 -2
- data/lib/chef/provider/deploy.rb +12 -10
- data/lib/chef/provider/env.rb +152 -0
- data/lib/chef/provider/env/windows.rb +75 -0
- data/lib/chef/provider/file.rb +10 -14
- data/lib/chef/provider/group.rb +15 -2
- data/lib/chef/provider/group/dscl.rb +17 -25
- data/lib/chef/provider/group/gpasswd.rb +6 -3
- data/lib/chef/provider/group/pw.rb +3 -7
- data/lib/chef/provider/group/windows.rb +79 -0
- data/lib/chef/provider/link.rb +4 -5
- data/lib/chef/provider/mdadm.rb +25 -18
- data/lib/chef/provider/mount/mount.rb +28 -27
- data/lib/chef/provider/package.rb +35 -35
- data/lib/chef/provider/package/dpkg.rb +13 -10
- data/lib/chef/provider/package/easy_install.rb +6 -6
- data/lib/chef/provider/package/freebsd.rb +17 -51
- data/lib/chef/provider/package/rpm.rb +1 -1
- data/lib/chef/provider/package/rubygems.rb +391 -74
- data/lib/chef/provider/package/yum.rb +2 -2
- data/lib/chef/provider/package/zypper.rb +2 -1
- data/lib/chef/provider/remote_directory.rb +60 -83
- data/lib/chef/provider/remote_file.rb +17 -66
- data/lib/chef/provider/script.rb +20 -9
- data/lib/chef/provider/service.rb +23 -30
- data/lib/chef/provider/service/arch.rb +3 -3
- data/lib/chef/provider/service/debian.rb +22 -17
- data/lib/chef/provider/service/freebsd.rb +4 -4
- data/lib/chef/provider/service/init.rb +2 -2
- data/lib/chef/provider/service/redhat.rb +14 -16
- data/lib/chef/provider/service/simple.rb +7 -3
- data/lib/chef/provider/service/solaris.rb +85 -0
- data/lib/chef/provider/service/upstart.rb +12 -7
- data/lib/chef/provider/service/windows.rb +2 -2
- data/lib/chef/provider/template.rb +133 -118
- data/lib/chef/provider/user.rb +34 -17
- data/lib/chef/provider/user/dscl.rb +117 -114
- data/lib/chef/provider/user/windows.rb +124 -0
- data/lib/chef/providers.rb +7 -0
- data/lib/chef/recipe.rb +39 -20
- data/lib/chef/resource.rb +47 -52
- data/lib/chef/resource/apt_package.rb +4 -4
- data/lib/chef/resource/bash.rb +4 -4
- data/lib/chef/resource/cookbook_file.rb +45 -0
- data/lib/chef/resource/cron.rb +3 -3
- data/lib/chef/resource/csh.rb +4 -4
- data/lib/chef/resource/deploy.rb +3 -3
- data/lib/chef/resource/directory.rb +4 -4
- data/lib/chef/resource/dpkg_package.rb +4 -4
- data/lib/chef/resource/easy_install_package.rb +3 -3
- data/lib/chef/resource/env.rb +58 -0
- data/lib/chef/resource/erl_call.rb +3 -3
- data/lib/chef/resource/execute.rb +3 -3
- data/lib/chef/resource/file.rb +3 -3
- data/lib/chef/resource/freebsd_package.rb +3 -3
- data/lib/chef/resource/gem_package.rb +17 -9
- data/lib/chef/resource/git.rb +3 -3
- data/lib/chef/resource/group.rb +3 -3
- data/lib/chef/resource/http_request.rb +4 -4
- data/lib/chef/resource/ifconfig.rb +3 -3
- data/lib/chef/resource/link.rb +3 -3
- data/lib/chef/resource/log.rb +2 -2
- data/lib/chef/resource/macports_package.rb +2 -2
- data/lib/chef/resource/mdadm.rb +3 -3
- data/lib/chef/resource/mount.rb +2 -2
- data/lib/chef/resource/package.rb +4 -4
- data/lib/chef/resource/pacman_package.rb +4 -4
- data/lib/chef/resource/perl.rb +4 -4
- data/lib/chef/resource/portage_package.rb +4 -4
- data/lib/chef/resource/python.rb +4 -4
- data/lib/chef/resource/remote_directory.rb +3 -3
- data/lib/chef/resource/remote_file.rb +26 -3
- data/lib/chef/resource/route.rb +3 -3
- data/lib/chef/resource/ruby.rb +3 -3
- data/lib/chef/resource/ruby_block.rb +3 -2
- data/lib/chef/resource/scm.rb +7 -5
- data/lib/chef/resource/script.rb +4 -4
- data/lib/chef/resource/service.rb +3 -3
- data/lib/chef/resource/subversion.rb +4 -2
- data/lib/chef/resource/template.rb +3 -3
- data/lib/chef/resource/user.rb +3 -3
- data/lib/chef/resource/yum_package.rb +3 -3
- data/lib/chef/resource_collection.rb +9 -5
- data/lib/chef/resources.rb +2 -0
- data/lib/chef/rest.rb +4 -0
- data/lib/chef/role.rb +2 -0
- data/lib/chef/run_context.rb +108 -0
- data/lib/chef/run_list.rb +75 -98
- data/lib/chef/run_list/run_list_expansion.rb +156 -0
- data/lib/chef/run_list/run_list_item.rb +71 -0
- data/lib/chef/runner.rb +58 -61
- data/lib/chef/sandbox.rb +147 -0
- data/lib/chef/shef.rb +4 -3
- data/lib/chef/shef/ext.rb +12 -4
- data/lib/chef/shef/shef_session.rb +27 -23
- data/lib/chef/shell_out.rb +375 -0
- data/lib/chef/util/windows.rb +56 -0
- data/lib/chef/util/windows/net_group.rb +101 -0
- data/lib/chef/util/windows/net_user.rb +198 -0
- data/lib/chef/version.rb +20 -0
- metadata +112 -22
- data/lib/chef/compile.rb +0 -158
- data/lib/chef/cookbook.rb +0 -201
- data/lib/chef/mixin/generate_url.rb +0 -58
data/lib/chef/shef.rb
CHANGED
@@ -127,7 +127,7 @@ module Shef
|
|
127
127
|
|
128
128
|
def self.client_type
|
129
129
|
type = Shef::StandAloneSession
|
130
|
-
type = Shef::SoloSession if Chef::Config[:
|
130
|
+
type = Shef::SoloSession if Chef::Config[:shef_solo]
|
131
131
|
type = Shef::ClientSession if Chef::Config[:client]
|
132
132
|
type
|
133
133
|
end
|
@@ -161,11 +161,12 @@ module Shef
|
|
161
161
|
:default => true,
|
162
162
|
:boolean => true
|
163
163
|
|
164
|
-
option :
|
164
|
+
option :shef_solo,
|
165
165
|
:short => "-s",
|
166
166
|
:long => "--solo",
|
167
167
|
:description => "chef-solo shef session",
|
168
|
-
:boolean => true
|
168
|
+
:boolean => true,
|
169
|
+
:proc => proc {Chef::Config[:solo] = true}
|
169
170
|
|
170
171
|
option :client,
|
171
172
|
:short => "-z",
|
data/lib/chef/shef/ext.rb
CHANGED
@@ -61,6 +61,7 @@ module Shef
|
|
61
61
|
banner << "".ljust(80, "=")
|
62
62
|
banner << "| " + "Command".ljust(25) + "| " + "Description"
|
63
63
|
banner << "".ljust(80, "=")
|
64
|
+
|
64
65
|
self.class.all_help_descriptions.each do |cmd, description|
|
65
66
|
banner << "| " + cmd.ljust(25) + "| " + description
|
66
67
|
end
|
@@ -86,7 +87,7 @@ module Shef
|
|
86
87
|
end
|
87
88
|
|
88
89
|
def all_help_descriptions
|
89
|
-
if sc = superclass
|
90
|
+
if (sc = superclass) && superclass.respond_to?(:help_descriptions)
|
90
91
|
help_descriptions + sc.help_descriptions
|
91
92
|
else
|
92
93
|
help_descriptions
|
@@ -225,8 +226,7 @@ class Object
|
|
225
226
|
def run_chef
|
226
227
|
Chef::Log.level = :debug
|
227
228
|
session = Shef.session
|
228
|
-
session.
|
229
|
-
runrun = Chef::Runner.new(node, session.collection, session.definitions, session.cookbook_loader).converge
|
229
|
+
runrun = Chef::Runner.new(session.run_context).converge
|
230
230
|
Chef::Log.level = :info
|
231
231
|
runrun
|
232
232
|
end
|
@@ -275,6 +275,14 @@ class Object
|
|
275
275
|
|
276
276
|
end
|
277
277
|
|
278
|
+
class String
|
279
|
+
undef_method :version
|
280
|
+
end
|
281
|
+
|
282
|
+
class NilClass
|
283
|
+
undef_method :version
|
284
|
+
end
|
285
|
+
|
278
286
|
class Chef
|
279
287
|
class Recipe
|
280
288
|
|
@@ -287,7 +295,7 @@ class Chef
|
|
287
295
|
desc "list all the resources on the current recipe"
|
288
296
|
def resources(*args)
|
289
297
|
if args.empty?
|
290
|
-
pp
|
298
|
+
pp run_context.resource_collection.instance_variable_get(:@resources_by_name).keys
|
291
299
|
else
|
292
300
|
pp resources = original_resources(*args)
|
293
301
|
resources
|
@@ -19,9 +19,8 @@ module Shef
|
|
19
19
|
class ShefSession
|
20
20
|
include Singleton
|
21
21
|
|
22
|
-
attr_accessor :node, :compile, :recipe
|
22
|
+
attr_accessor :node, :compile, :recipe, :run_context
|
23
23
|
attr_reader :node_attributes, :client
|
24
|
-
|
25
24
|
def initialize
|
26
25
|
@node_built = false
|
27
26
|
end
|
@@ -34,9 +33,10 @@ module Shef
|
|
34
33
|
loading do
|
35
34
|
rebuild_node
|
36
35
|
@node = client.node
|
36
|
+
rebuild_context
|
37
37
|
node.consume_attributes(node_attributes) if node_attributes
|
38
38
|
shorten_node_inspect
|
39
|
-
@recipe = Chef::Recipe.new(nil, nil,
|
39
|
+
@recipe = Chef::Recipe.new(nil, nil, run_context)
|
40
40
|
@node_built = true
|
41
41
|
end
|
42
42
|
end
|
@@ -46,8 +46,12 @@ module Shef
|
|
46
46
|
@node.consume_attributes(@node_attributes)
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
50
|
-
|
49
|
+
def resource_collection
|
50
|
+
run_context.resource_collection
|
51
|
+
end
|
52
|
+
|
53
|
+
def run_context
|
54
|
+
@run_context || rebuild_context
|
51
55
|
end
|
52
56
|
|
53
57
|
def definitions
|
@@ -62,7 +66,7 @@ module Shef
|
|
62
66
|
raise "Not Supported! #{self.class.name} doesn't support #save_node, maybe you need to run shef in client mode?"
|
63
67
|
end
|
64
68
|
|
65
|
-
def
|
69
|
+
def rebuild_context
|
66
70
|
raise "Not Implemented! :rebuild_collection should be implemented by subclasses"
|
67
71
|
end
|
68
72
|
|
@@ -112,16 +116,18 @@ module Shef
|
|
112
116
|
|
113
117
|
class StandAloneSession < ShefSession
|
114
118
|
|
115
|
-
def
|
116
|
-
@
|
119
|
+
def rebuild_context
|
120
|
+
@run_context = Chef::RunContext.new(@node, {}) # no recipes
|
117
121
|
end
|
118
122
|
|
119
123
|
private
|
120
124
|
|
121
125
|
def rebuild_node
|
126
|
+
Chef::Config[:solo] = true
|
122
127
|
@client = Chef::Client.new
|
128
|
+
@client.run_ohai
|
123
129
|
@client.determine_node_name
|
124
|
-
@client.build_node(@client.node_name, true)
|
130
|
+
@client.build_node #(@client.node_name, true)
|
125
131
|
end
|
126
132
|
|
127
133
|
end
|
@@ -129,27 +135,22 @@ module Shef
|
|
129
135
|
class SoloSession < ShefSession
|
130
136
|
|
131
137
|
def definitions
|
132
|
-
@
|
138
|
+
@run_context.definitions
|
133
139
|
end
|
134
140
|
|
135
|
-
def
|
136
|
-
@
|
137
|
-
end
|
138
|
-
|
139
|
-
def rebuild_collection
|
140
|
-
@compile = Chef::Compile.new(@client.node)
|
141
|
-
|
142
|
-
@collection = @compile.collection
|
143
|
-
@collection << @recipe.collection.all_resources
|
144
|
-
@collection
|
141
|
+
def rebuild_context
|
142
|
+
@run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new(Chef::CookbookLoader.new))
|
145
143
|
end
|
146
144
|
|
147
145
|
private
|
148
146
|
|
149
147
|
def rebuild_node
|
148
|
+
# Tell the client we're chef solo so it won't try to contact the server
|
149
|
+
Chef::Config[:solo] = true
|
150
150
|
@client = Chef::Client.new
|
151
|
+
@client.run_ohai
|
151
152
|
@client.determine_node_name
|
152
|
-
@client.build_node(@client.node_name, true)
|
153
|
+
@client.build_node #(@client.node_name, true)
|
153
154
|
end
|
154
155
|
|
155
156
|
end
|
@@ -163,13 +164,16 @@ module Shef
|
|
163
164
|
private
|
164
165
|
|
165
166
|
def rebuild_node
|
167
|
+
# Make sure the client knows this is not chef solo
|
168
|
+
Chef::Config[:solo] = false
|
166
169
|
@client = Chef::Client.new
|
170
|
+
@client.run_ohai
|
167
171
|
@client.determine_node_name
|
168
172
|
@client.register
|
169
|
-
@client.build_node(@client.node_name, false)
|
173
|
+
@client.build_node #(@client.node_name, false)
|
170
174
|
|
171
175
|
@client.sync_cookbooks
|
172
176
|
end
|
173
177
|
|
174
178
|
end
|
175
|
-
end
|
179
|
+
end
|
@@ -0,0 +1,375 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2010 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 'etc'
|
20
|
+
require 'tmpdir'
|
21
|
+
require 'chef/log'
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
|
25
|
+
# Provides a simplified interface to shelling out yet still collecting both
|
26
|
+
# standard out and standard error and providing full control over environment,
|
27
|
+
# working directory, uid, gid, etc.
|
28
|
+
#
|
29
|
+
# No means for passing input to the subprocess is provided, nor is there any
|
30
|
+
# way to inspect the output of the command as it is being read. If you need
|
31
|
+
# to do that, you have to use Chef::Mixin::Command.popen4
|
32
|
+
#
|
33
|
+
# == Platform Support
|
34
|
+
# Chef::ShellOut uses Kernel.fork() and is therefore unsuitable for Windows
|
35
|
+
# or jruby.
|
36
|
+
class ShellOut
|
37
|
+
READ_WAIT_TIME = 0.01
|
38
|
+
DEFAULT_READ_TIMEOUT = 60
|
39
|
+
DEFAULT_ENVIRONMENT = {'LC_ALL' => 'C'}
|
40
|
+
|
41
|
+
attr_accessor :user, :group, :cwd, :valid_exit_codes
|
42
|
+
attr_reader :command, :umask, :environment
|
43
|
+
attr_writer :timeout
|
44
|
+
|
45
|
+
attr_reader :stdout, :stderr, :status
|
46
|
+
|
47
|
+
attr_reader :stdin_pipe, :stdout_pipe, :stderr_pipe, :process_status_pipe
|
48
|
+
|
49
|
+
# === Arguments:
|
50
|
+
# Takes a single command, or a list of command fragments. These are used
|
51
|
+
# as arguments to Kernel.exec. See the Kernel.exec documentation for more
|
52
|
+
# explanation of how arguments are evaluated. The last argument can be an
|
53
|
+
# options Hash.
|
54
|
+
# === Options:
|
55
|
+
# If the last argument is a Hash, it is removed from the list of args passed
|
56
|
+
# to exec and used as an options hash. The following options are available:
|
57
|
+
# * user: the user the commmand should run as. if an integer is given, it is
|
58
|
+
# used as a uid. A string is treated as a username and resolved to a uid
|
59
|
+
# with Etc.getpwnam
|
60
|
+
# * group: the group the command should run as. works similarly to +user+
|
61
|
+
# * cwd: the directory to chdir to before running the command
|
62
|
+
# * umask: a umask to set before running the command. If given as an Integer,
|
63
|
+
# be sure to use two leading zeros so it's parsed as Octal. A string will
|
64
|
+
# be treated as an octal integer
|
65
|
+
# * returns: one or more Integer values to use as valid exit codes for the
|
66
|
+
# subprocess. This only has an effect if you call +error!+ after
|
67
|
+
# +run_command+.
|
68
|
+
# * environment: a Hash of environment variables to set before the command
|
69
|
+
# is run. By default, the environment will *always* be set to 'LC_ALL' => 'C'
|
70
|
+
# to prevent issues with multibyte characters in Ruby 1.8. To avoid this,
|
71
|
+
# use :environment => nil for *no* extra environment settings, or
|
72
|
+
# :environment => {'LC_ALL'=>nil, ...} to set other environment settings
|
73
|
+
# without changing the locale.
|
74
|
+
def initialize(*command_args)
|
75
|
+
@stdout, @stderr = '', ''
|
76
|
+
@environment = DEFAULT_ENVIRONMENT
|
77
|
+
@cwd = Dir.tmpdir
|
78
|
+
@valid_exit_codes = [0]
|
79
|
+
|
80
|
+
if command_args.last.is_a?(Hash)
|
81
|
+
parse_options(command_args.pop)
|
82
|
+
end
|
83
|
+
|
84
|
+
@command = command_args.size == 1 ? command_args.first : command_args
|
85
|
+
end
|
86
|
+
|
87
|
+
def umask=(new_umask)
|
88
|
+
@umask = (new_umask.respond_to?(:oct) ? new_umask.oct : new_umask.to_i) & 007777
|
89
|
+
end
|
90
|
+
|
91
|
+
def uid
|
92
|
+
return nil unless user
|
93
|
+
user.kind_of?(Integer) ? user : Etc.getpwnam(user.to_s).uid
|
94
|
+
end
|
95
|
+
|
96
|
+
def gid
|
97
|
+
return nil unless group
|
98
|
+
group.kind_of?(Integer) ? group : Etc.getgrnam(group.to_s).gid
|
99
|
+
end
|
100
|
+
|
101
|
+
def timeout
|
102
|
+
@timeout || DEFAULT_READ_TIMEOUT
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_for_exception
|
106
|
+
msg = ""
|
107
|
+
msg << "---- Begin output of #{command} ----\n"
|
108
|
+
msg << "STDOUT: #{stdout.strip}\n"
|
109
|
+
msg << "STDERR: #{stderr.strip}\n"
|
110
|
+
msg << "---- End output of #{command} ----\n"
|
111
|
+
msg << "Ran #{command} returned #{status.exitstatus}" if status
|
112
|
+
msg
|
113
|
+
end
|
114
|
+
|
115
|
+
def exitstatus
|
116
|
+
@status && @status.exitstatus
|
117
|
+
end
|
118
|
+
|
119
|
+
# Run the command, writing the command's standard out and standard error
|
120
|
+
# to +stdout+ and +stderr+, and saving its exit status object to +status+
|
121
|
+
# === Returns
|
122
|
+
# returns +self+; +stdout+, +stderr+, +status+, and +exitstatus+ will be
|
123
|
+
# populated with results of the command
|
124
|
+
# === Raises
|
125
|
+
# * Errno::EACCES when you are not privileged to execute the command
|
126
|
+
# * Errno::ENOENT when the command is not available on the system (or not
|
127
|
+
# in the current $PATH)
|
128
|
+
# * Chef::Exceptions::CommandTimeout when the command does not complete
|
129
|
+
# within +timeout+ seconds (default: 60s)
|
130
|
+
def run_command
|
131
|
+
Chef::Log.debug("sh(#{@command})")
|
132
|
+
|
133
|
+
@child_pid = fork_subprocess
|
134
|
+
|
135
|
+
configure_parent_process_file_descriptors
|
136
|
+
propagate_pre_exec_failure
|
137
|
+
|
138
|
+
@result = nil
|
139
|
+
read_time = 0
|
140
|
+
|
141
|
+
until @status
|
142
|
+
ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
|
143
|
+
unless ready
|
144
|
+
read_time += READ_WAIT_TIME
|
145
|
+
if read_time >= timeout && !@result
|
146
|
+
raise Chef::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if ready && ready.first.include?(child_stdout)
|
151
|
+
read_stdout_to_buffer
|
152
|
+
end
|
153
|
+
if ready && ready.first.include?(child_stderr)
|
154
|
+
read_stderr_to_buffer
|
155
|
+
end
|
156
|
+
|
157
|
+
unless @status
|
158
|
+
# make one more pass to get the last of the output after the
|
159
|
+
# child process dies
|
160
|
+
if results = Process.waitpid2(@child_pid, Process::WNOHANG)
|
161
|
+
@status = results.last
|
162
|
+
redo
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
self
|
167
|
+
rescue Exception
|
168
|
+
# do our best to kill zombies
|
169
|
+
Process.waitpid2(@child_pid, Process::WNOHANG) rescue nil
|
170
|
+
raise
|
171
|
+
ensure
|
172
|
+
close_all_pipes
|
173
|
+
end
|
174
|
+
|
175
|
+
def error!
|
176
|
+
unless Array(valid_exit_codes).include?(exitstatus)
|
177
|
+
invalid!("Expected process to exit 0, but it exited with #{exitstatus}")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Raises a Chef::Exceptions::ShellCommandFailed exception, appending the
|
182
|
+
# command's stdout, stderr, and exitstatus to the exception message.
|
183
|
+
# === Arguments
|
184
|
+
# +msg+ A String to use as the basis of the exception message. The
|
185
|
+
# default explanation is very generic, providing a more
|
186
|
+
# informative message is highly encouraged.
|
187
|
+
# === Raises
|
188
|
+
# Chef::Exceptions::ShellCommandFailed always
|
189
|
+
def invalid!(msg=nil)
|
190
|
+
msg ||= "Command produced unexpected results"
|
191
|
+
raise Chef::Exceptions::ShellCommandFailed, msg + "\n" + format_for_exception
|
192
|
+
end
|
193
|
+
|
194
|
+
def inspect
|
195
|
+
"<#{self.class.name}##{object_id}: command: '#@command' process_status: #{@status.inspect} " +
|
196
|
+
"stdout: '#{stdout.strip}' stderr: '#{stderr.strip}' child_pid: #{@child_pid.inspect} " +
|
197
|
+
"environment: #{@environment.inspect} timeout: #{timeout} user: #@user group: #@group working_dir: #@cwd >"
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def parse_options(opts)
|
203
|
+
opts.each do |option, setting|
|
204
|
+
case option.to_s
|
205
|
+
when 'cwd'
|
206
|
+
self.cwd = setting
|
207
|
+
when 'user'
|
208
|
+
self.user = setting
|
209
|
+
when 'group'
|
210
|
+
self.group = setting
|
211
|
+
when 'umask'
|
212
|
+
self.umask = setting
|
213
|
+
when 'timeout'
|
214
|
+
self.timeout = setting
|
215
|
+
when 'returns'
|
216
|
+
self.valid_exit_codes = Array(setting)
|
217
|
+
when 'environment', 'env'
|
218
|
+
# passing :environment => nil means don't set any new ENV vars
|
219
|
+
setting.nil? ? @environment = {} : @environment.merge!(setting)
|
220
|
+
else
|
221
|
+
raise Chef::Exceptions::InvalidCommandOption, "option '#{option.inspect}' is not a valid option for #{self.class.name}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def set_user
|
227
|
+
if user
|
228
|
+
Process.euid = uid
|
229
|
+
Process.uid = uid
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def set_group
|
234
|
+
if group
|
235
|
+
Process.egid = gid
|
236
|
+
Process.gid = gid
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def set_environment
|
241
|
+
environment.each do |env_var,value|
|
242
|
+
ENV[env_var] = value
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def set_umask
|
247
|
+
File.umask(umask) if umask
|
248
|
+
end
|
249
|
+
|
250
|
+
def initialize_ipc
|
251
|
+
@stdout_pipe, @stderr_pipe, @process_status_pipe = IO.pipe, IO.pipe, IO.pipe
|
252
|
+
@process_status_pipe.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
253
|
+
end
|
254
|
+
|
255
|
+
def child_stdout
|
256
|
+
@stdout_pipe[0]
|
257
|
+
end
|
258
|
+
|
259
|
+
def child_stderr
|
260
|
+
@stderr_pipe[0]
|
261
|
+
end
|
262
|
+
|
263
|
+
def child_process_status
|
264
|
+
@process_status_pipe[0]
|
265
|
+
end
|
266
|
+
|
267
|
+
def close_all_pipes
|
268
|
+
child_stdout.close unless child_stdout.closed?
|
269
|
+
child_stderr.close unless child_stderr.closed?
|
270
|
+
child_process_status.close unless child_process_status.closed?
|
271
|
+
#rescue NoMethodError # we blew up before IPC was setup
|
272
|
+
end
|
273
|
+
|
274
|
+
# replace stdout, and stderr with pipes to the parent, and close the
|
275
|
+
# reader side of the error marshaling side channel. Close STDIN so when we
|
276
|
+
# exec, the new program will no it's never getting input ever.
|
277
|
+
def configure_subprocess_file_descriptors
|
278
|
+
process_status_pipe.first.close
|
279
|
+
|
280
|
+
# HACK: for some reason, just STDIN.close isn't good enough when running
|
281
|
+
# under ruby 1.9.2, so make it good enough:
|
282
|
+
stdin_reader, stdin_writer = IO.pipe
|
283
|
+
stdin_writer.close
|
284
|
+
STDIN.reopen stdin_reader
|
285
|
+
stdin_reader.close
|
286
|
+
|
287
|
+
stdout_pipe.first.close
|
288
|
+
STDOUT.reopen stdout_pipe.last
|
289
|
+
stdout_pipe.last.close
|
290
|
+
|
291
|
+
stderr_pipe.first.close
|
292
|
+
STDERR.reopen stderr_pipe.last
|
293
|
+
stderr_pipe.last.close
|
294
|
+
|
295
|
+
STDOUT.sync = STDERR.sync = true
|
296
|
+
end
|
297
|
+
|
298
|
+
def configure_parent_process_file_descriptors
|
299
|
+
# Close the sides of the pipes we don't care about
|
300
|
+
stdout_pipe.last.close
|
301
|
+
stderr_pipe.last.close
|
302
|
+
process_status_pipe.last.close
|
303
|
+
# Get output as it happens rather than buffered
|
304
|
+
child_stdout.sync = true
|
305
|
+
child_stderr.sync = true
|
306
|
+
# Set file descriptors to non-blocking IO. man(2) fcntl
|
307
|
+
child_stdout.fcntl(Fcntl::F_SETFL, child_stdout.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
|
308
|
+
child_stderr.fcntl(Fcntl::F_SETFL, child_stderr.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
|
309
|
+
true
|
310
|
+
end
|
311
|
+
|
312
|
+
# Some patch levels of ruby in wide use (in particular the ruby 1.8.6 on OSX)
|
313
|
+
# segfault when you IO.select a pipe that's reached eof. Weak sauce.
|
314
|
+
def open_pipes
|
315
|
+
@open_pipes ||= [child_stdout, child_stderr]
|
316
|
+
end
|
317
|
+
|
318
|
+
def read_stdout_to_buffer
|
319
|
+
while chunk = child_stdout.read(16 * 1024)
|
320
|
+
@stdout << chunk
|
321
|
+
end
|
322
|
+
rescue Errno::EAGAIN
|
323
|
+
rescue EOFError
|
324
|
+
open_pipes.delete_at(0)
|
325
|
+
end
|
326
|
+
|
327
|
+
def read_stderr_to_buffer
|
328
|
+
while chunk = child_stderr.read(16 * 1024)
|
329
|
+
@stderr << chunk
|
330
|
+
end
|
331
|
+
rescue Errno::EAGAIN
|
332
|
+
rescue EOFError
|
333
|
+
open_pipes.delete_at(1)
|
334
|
+
end
|
335
|
+
|
336
|
+
def fork_subprocess
|
337
|
+
initialize_ipc
|
338
|
+
|
339
|
+
fork do
|
340
|
+
configure_subprocess_file_descriptors
|
341
|
+
|
342
|
+
set_user
|
343
|
+
set_group
|
344
|
+
set_environment
|
345
|
+
set_umask
|
346
|
+
|
347
|
+
begin
|
348
|
+
command.kind_of?(Array) ? exec(*command) : exec(command)
|
349
|
+
|
350
|
+
raise 'forty-two' # Should never get here
|
351
|
+
rescue Exception => e
|
352
|
+
Marshal.dump(e, process_status_pipe.last)
|
353
|
+
process_status_pipe.last.flush
|
354
|
+
end
|
355
|
+
process_status_pipe.last.close unless (process_status_pipe.last.closed?)
|
356
|
+
exit!
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Attempt to get a Marshaled error from the side-channel.
|
361
|
+
# If it's there, un-marshal it and raise. If it's not there,
|
362
|
+
# assume everything went well.
|
363
|
+
def propagate_pre_exec_failure
|
364
|
+
begin
|
365
|
+
e = Marshal.load child_process_status
|
366
|
+
raise(Exception === e ? e : "unknown failure: #{e.inspect}")
|
367
|
+
rescue EOFError # If we get an EOF error, then the exec was successful
|
368
|
+
true
|
369
|
+
ensure
|
370
|
+
child_process_status.close
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
375
|
+
end
|