chef 0.9.18 → 0.10.0.beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. data/README.rdoc +0 -3
  2. data/distro/arch/etc/rc.d/chef-server +0 -4
  3. data/distro/arch/etc/rc.d/chef-server-webui +0 -4
  4. data/distro/arch/etc/rc.d/chef-solr +0 -4
  5. data/distro/arch/etc/rc.d/chef-solr-indexer +0 -4
  6. data/lib/chef.rb +3 -3
  7. data/lib/chef/api_client.rb +1 -1
  8. data/lib/chef/application.rb +11 -1
  9. data/lib/chef/application/client.rb +18 -22
  10. data/lib/chef/application/knife.rb +28 -29
  11. data/lib/chef/application/solo.rb +14 -12
  12. data/lib/chef/client.rb +112 -54
  13. data/lib/chef/config.rb +4 -0
  14. data/lib/chef/cookbook/chefignore.rb +66 -0
  15. data/lib/chef/cookbook/cookbook_collection.rb +6 -5
  16. data/lib/chef/cookbook/cookbook_version_loader.rb +151 -0
  17. data/lib/chef/cookbook/file_system_file_vendor.rb +10 -8
  18. data/lib/chef/cookbook/metadata.rb +200 -108
  19. data/lib/chef/cookbook_loader.rb +39 -163
  20. data/lib/chef/cookbook_uploader.rb +100 -78
  21. data/lib/chef/cookbook_version.rb +92 -47
  22. data/lib/chef/cookbook_version_selector.rb +163 -0
  23. data/lib/chef/couchdb.rb +9 -1
  24. data/lib/chef/data_bag.rb +1 -1
  25. data/lib/chef/data_bag_item.rb +1 -1
  26. data/lib/chef/encrypted_data_bag_item.rb +126 -0
  27. data/lib/chef/environment.rb +386 -0
  28. data/lib/chef/exceptions.rb +82 -1
  29. data/lib/chef/index_queue/amqp_client.rb +15 -12
  30. data/lib/chef/index_queue/indexable.rb +38 -4
  31. data/lib/chef/json_compat.rb +3 -3
  32. data/lib/chef/knife.rb +97 -202
  33. data/lib/chef/knife/bootstrap.rb +27 -61
  34. data/lib/chef/knife/bootstrap/archlinux-gems.erb +4 -2
  35. data/lib/chef/knife/bootstrap/centos5-gems.erb +6 -15
  36. data/lib/chef/knife/bootstrap/fedora13-gems.erb +3 -4
  37. data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +2 -2
  38. data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +6 -5
  39. data/lib/chef/knife/client_bulk_delete.rb +6 -3
  40. data/lib/chef/knife/client_create.rb +13 -10
  41. data/lib/chef/knife/client_delete.rb +10 -7
  42. data/lib/chef/knife/client_edit.rb +9 -6
  43. data/lib/chef/knife/client_list.rb +8 -5
  44. data/lib/chef/knife/client_reregister.rb +9 -6
  45. data/lib/chef/knife/client_show.rb +9 -6
  46. data/lib/chef/knife/configure.rb +15 -19
  47. data/lib/chef/knife/configure_client.rb +4 -4
  48. data/lib/chef/knife/cookbook_bulk_delete.rb +11 -8
  49. data/lib/chef/knife/cookbook_create.rb +120 -55
  50. data/lib/chef/knife/cookbook_delete.rb +18 -12
  51. data/lib/chef/knife/cookbook_download.rb +10 -6
  52. data/lib/chef/knife/cookbook_list.rb +15 -6
  53. data/lib/chef/knife/cookbook_metadata.rb +41 -21
  54. data/lib/chef/knife/cookbook_metadata_from_file.rb +4 -0
  55. data/lib/chef/knife/cookbook_show.rb +16 -5
  56. data/lib/chef/knife/cookbook_site_download.rb +2 -2
  57. data/lib/chef/knife/cookbook_site_share.rb +18 -13
  58. data/lib/chef/knife/cookbook_site_unshare.rb +7 -4
  59. data/lib/chef/knife/cookbook_site_vendor.rb +21 -18
  60. data/lib/chef/knife/cookbook_test.rb +14 -14
  61. data/lib/chef/knife/cookbook_upload.rb +91 -40
  62. data/lib/chef/knife/data_bag_create.rb +41 -6
  63. data/lib/chef/knife/data_bag_delete.rb +5 -3
  64. data/lib/chef/knife/data_bag_edit.rb +55 -11
  65. data/lib/chef/knife/data_bag_from_file.rb +47 -7
  66. data/lib/chef/knife/data_bag_list.rb +4 -1
  67. data/lib/chef/knife/data_bag_show.rb +44 -4
  68. data/lib/chef/knife/environment_create.rb +53 -0
  69. data/lib/chef/knife/environment_delete.rb +45 -0
  70. data/lib/chef/knife/environment_edit.rb +45 -0
  71. data/lib/chef/knife/environment_from_file.rb +39 -0
  72. data/lib/chef/knife/environment_list.rb +42 -0
  73. data/lib/chef/knife/environment_show.rb +46 -0
  74. data/lib/chef/knife/exec.rb +1 -1
  75. data/lib/chef/knife/index_rebuild.rb +8 -9
  76. data/lib/chef/knife/node_bulk_delete.rb +9 -6
  77. data/lib/chef/knife/node_create.rb +9 -6
  78. data/lib/chef/knife/node_delete.rb +10 -7
  79. data/lib/chef/knife/node_edit.rb +129 -10
  80. data/lib/chef/knife/node_from_file.rb +10 -7
  81. data/lib/chef/knife/node_list.rb +11 -6
  82. data/lib/chef/knife/node_run_list_add.rb +10 -7
  83. data/lib/chef/knife/node_run_list_remove.rb +9 -6
  84. data/lib/chef/knife/node_show.rb +15 -7
  85. data/lib/chef/knife/recipe_list.rb +4 -3
  86. data/lib/chef/knife/role_bulk_delete.rb +9 -6
  87. data/lib/chef/knife/role_create.rb +9 -6
  88. data/lib/chef/knife/role_delete.rb +10 -7
  89. data/lib/chef/knife/role_edit.rb +11 -8
  90. data/lib/chef/knife/role_from_file.rb +10 -7
  91. data/lib/chef/knife/role_list.rb +8 -5
  92. data/lib/chef/knife/role_show.rb +11 -8
  93. data/lib/chef/knife/search.rb +33 -10
  94. data/lib/chef/knife/ssh.rb +33 -61
  95. data/lib/chef/knife/status.rb +7 -4
  96. data/lib/chef/knife/subcommand_loader.rb +101 -0
  97. data/lib/chef/knife/tag_create.rb +31 -0
  98. data/lib/chef/knife/tag_delete.rb +31 -0
  99. data/lib/chef/knife/tag_list.rb +29 -0
  100. data/lib/chef/knife/ui.rb +229 -0
  101. data/lib/chef/knife/windows_bootstrap.rb +8 -5
  102. data/lib/chef/log.rb +5 -59
  103. data/lib/chef/mash.rb +211 -0
  104. data/lib/chef/mixins.rb +1 -2
  105. data/lib/chef/nil_argument.rb +3 -0
  106. data/lib/chef/node.rb +96 -34
  107. data/lib/chef/platform.rb +27 -0
  108. data/lib/chef/provider/cookbook_file.rb +21 -20
  109. data/lib/chef/provider/deploy/revision.rb +3 -0
  110. data/lib/chef/provider/file.rb +20 -11
  111. data/lib/chef/provider/git.rb +26 -26
  112. data/lib/chef/provider/group/aix.rb +70 -0
  113. data/lib/chef/provider/group/groupadd.rb +7 -4
  114. data/lib/chef/provider/group/usermod.rb +1 -1
  115. data/lib/chef/provider/package.rb +28 -28
  116. data/lib/chef/provider/package/dpkg.rb +1 -1
  117. data/lib/chef/provider/package/portage.rb +50 -39
  118. data/lib/chef/provider/package/rubygems.rb +1 -1
  119. data/lib/chef/provider/package/zypper.rb +3 -20
  120. data/lib/chef/provider/remote_directory.rb +0 -2
  121. data/lib/chef/provider/remote_file.rb +2 -3
  122. data/lib/chef/provider/service/arch.rb +28 -35
  123. data/lib/chef/provider/service/simple.rb +1 -1
  124. data/lib/chef/provider/subversion.rb +22 -22
  125. data/lib/chef/providers.rb +1 -0
  126. data/lib/chef/recipe.rb +10 -12
  127. data/lib/chef/resource.rb +49 -42
  128. data/lib/chef/resource/gem_package.rb +7 -3
  129. data/lib/chef/resource/git.rb +5 -5
  130. data/lib/chef/resource/package.rb +7 -7
  131. data/lib/chef/resource/scm.rb +2 -1
  132. data/lib/chef/resource/solaris_package.rb +0 -1
  133. data/lib/chef/resource/yum_package.rb +0 -1
  134. data/lib/chef/rest.rb +7 -16
  135. data/lib/chef/rest/rest_request.rb +0 -16
  136. data/lib/chef/role.rb +67 -13
  137. data/lib/chef/run_context.rb +37 -21
  138. data/lib/chef/run_list.rb +30 -15
  139. data/lib/chef/run_list/run_list_expansion.rb +41 -20
  140. data/lib/chef/run_list/run_list_item.rb +20 -6
  141. data/lib/chef/run_list/versioned_recipe_list.rb +68 -0
  142. data/lib/chef/runner.rb +7 -15
  143. data/lib/chef/search/query.rb +12 -7
  144. data/lib/chef/shef.rb +6 -7
  145. data/lib/chef/shef/shef_session.rb +40 -35
  146. data/lib/chef/shell_out.rb +22 -201
  147. data/lib/chef/shell_out/unix.rb +224 -0
  148. data/lib/chef/shell_out/windows.rb +95 -0
  149. data/lib/chef/solr_query.rb +187 -0
  150. data/lib/chef/solr_query/lucene.treetop +145 -0
  151. data/lib/chef/solr_query/lucene_nodes.rb +285 -0
  152. data/lib/chef/solr_query/query_transform.rb +65 -0
  153. data/lib/chef/solr_query/solr_http_request.rb +118 -0
  154. data/lib/chef/version.rb +4 -2
  155. data/lib/chef/version_class.rb +70 -0
  156. data/lib/chef/version_constraint.rb +116 -0
  157. metadata +68 -37
  158. data/lib/chef/cookbook/metadata/version.rb +0 -87
  159. data/lib/chef/knife/bluebox_images_list.rb +0 -54
  160. data/lib/chef/knife/bluebox_server_create.rb +0 -157
  161. data/lib/chef/knife/bluebox_server_delete.rb +0 -63
  162. data/lib/chef/knife/bluebox_server_list.rb +0 -59
  163. data/lib/chef/knife/ec2_instance_data.rb +0 -46
  164. data/lib/chef/knife/ec2_server_create.rb +0 -218
  165. data/lib/chef/knife/ec2_server_delete.rb +0 -87
  166. data/lib/chef/knife/ec2_server_list.rb +0 -89
  167. data/lib/chef/knife/rackspace_server_create.rb +0 -184
  168. data/lib/chef/knife/rackspace_server_delete.rb +0 -57
  169. data/lib/chef/knife/rackspace_server_list.rb +0 -59
  170. data/lib/chef/knife/slicehost_images_list.rb +0 -53
  171. data/lib/chef/knife/slicehost_server_create.rb +0 -103
  172. data/lib/chef/knife/slicehost_server_delete.rb +0 -61
  173. data/lib/chef/knife/slicehost_server_list.rb +0 -64
  174. data/lib/chef/knife/terremark_server_create.rb +0 -152
  175. data/lib/chef/knife/terremark_server_delete.rb +0 -87
  176. data/lib/chef/knife/terremark_server_list.rb +0 -77
  177. data/lib/chef/mixin/find_preferred_file.rb +0 -92
@@ -15,15 +15,14 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
- require "singleton"
19
- require "pp"
20
- require "etc"
21
- require "mixlib/cli"
18
+ require 'singleton'
19
+ require 'pp'
20
+ require 'etc'
21
+ require 'mixlib/cli'
22
22
 
23
- require 'chef'
24
23
  require 'chef/version'
25
- require "chef/client"
26
- require "chef/config"
24
+ require 'chef/client'
25
+ require 'chef/config'
27
26
 
28
27
  require 'chef/shef/shef_session'
29
28
  require 'chef/shef/ext'
@@ -1,14 +1,16 @@
1
1
  #--
2
2
  # Author:: Daniel DeLeo (<dan@kallistec.com>)
3
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
3
4
  # Copyright:: Copyright (c) 2009 Daniel DeLeo
5
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
6
  # License:: Apache License, Version 2.0
5
7
  #
6
8
  # Licensed under the Apache License, Version 2.0 (the "License");
7
9
  # you may not use this file except in compliance with the License.
8
10
  # You may obtain a copy of the License at
9
- #
11
+ #
10
12
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
13
+ #
12
14
  # Unless required by applicable law or agreed to in writing, software
13
15
  # distributed under the License is distributed on an "AS IS" BASIS,
14
16
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,11 +24,12 @@ require 'chef/config'
22
24
  require 'chef/client'
23
25
  require 'chef/cookbook/cookbook_collection'
24
26
  require 'chef/cookbook_loader'
27
+ require 'chef/run_list/run_list_expansion'
25
28
 
26
29
  module Shef
27
30
  class ShefSession
28
31
  include Singleton
29
-
32
+
30
33
  def self.session_type(type=nil)
31
34
  @session_type = type if type
32
35
  @session_type
@@ -37,13 +40,13 @@ module Shef
37
40
  def initialize
38
41
  @node_built = false
39
42
  end
40
-
43
+
41
44
  def node_built?
42
45
  !!@node_built
43
46
  end
44
-
47
+
45
48
  def reset!
46
- loading do
49
+ loading do
47
50
  rebuild_node
48
51
  @node = client.node
49
52
  shorten_node_inspect
@@ -55,12 +58,12 @@ module Shef
55
58
  @node_built = true
56
59
  end
57
60
  end
58
-
61
+
59
62
  def node_attributes=(attrs)
60
63
  @node_attributes = attrs
61
64
  @node.consume_attributes(@node_attributes)
62
65
  end
63
-
66
+
64
67
  def resource_collection
65
68
  run_context.resource_collection
66
69
  end
@@ -68,25 +71,25 @@ module Shef
68
71
  def run_context
69
72
  @run_context ||= rebuild_context
70
73
  end
71
-
74
+
72
75
  def definitions
73
76
  nil
74
77
  end
75
-
78
+
76
79
  def cookbook_loader
77
80
  nil
78
81
  end
79
-
82
+
80
83
  def save_node
81
84
  raise "Not Supported! #{self.class.name} doesn't support #save_node, maybe you need to run shef in client mode?"
82
85
  end
83
-
86
+
84
87
  def rebuild_context
85
88
  raise "Not Implemented! :rebuild_collection should be implemented by subclasses"
86
89
  end
87
-
90
+
88
91
  private
89
-
92
+
90
93
  def loading
91
94
  show_loading_progress
92
95
  begin
@@ -98,7 +101,7 @@ module Shef
98
101
  loading_complete(true)
99
102
  end
100
103
  end
101
-
104
+
102
105
  def show_loading_progress
103
106
  print "Loading"
104
107
  @loading = true
@@ -109,61 +112,63 @@ module Shef
109
112
  end
110
113
  end
111
114
  end
112
-
115
+
113
116
  def loading_complete(success)
114
117
  @loading = false
115
118
  @dot_printer.join
116
119
  msg = success ? "done.\n\n" : "epic fail!\n\n"
117
120
  print msg
118
121
  end
119
-
122
+
120
123
  def shorten_node_inspect
121
124
  def @node.inspect
122
125
  "<Chef::Node:0x#{self.object_id.to_s(16)} @name=\"#{self.name}\">"
123
126
  end
124
127
  end
125
-
128
+
126
129
  def rebuild_node
127
130
  raise "Not Implemented! :rebuild_node should be implemented by subclasses"
128
131
  end
129
-
132
+
130
133
  end
131
-
134
+
132
135
  class StandAloneSession < ShefSession
133
136
 
134
137
  session_type :standalone
135
-
138
+
136
139
  def rebuild_context
137
140
  @run_context = Chef::RunContext.new(@node, {}) # no recipes
141
+ @run_context.load(Chef::RunList::RunListExpansionFromDisk.new("_default", [])) # empty recipe list
138
142
  end
139
-
143
+
140
144
  private
141
-
145
+
142
146
  def rebuild_node
143
147
  Chef::Config[:solo] = true
144
148
  @client = Chef::Client.new
145
149
  @client.run_ohai
146
150
  @client.build_node
147
151
  end
148
-
152
+
149
153
  end
150
-
154
+
151
155
  class SoloSession < ShefSession
152
156
 
153
157
  session_type :solo
154
-
158
+
155
159
  def definitions
156
160
  @run_context.definitions
157
161
  end
158
-
162
+
159
163
  def rebuild_context
160
- Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest) }
161
- @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new(Chef::CookbookLoader.new))
164
+ Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, Chef::Config[:cookbook_path]) }
165
+ @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new(Chef::CookbookLoader.new(Chef::Config[:cookbook_path])))
166
+ @run_context.load(Chef::RunList::RunListExpansionFromDisk.new("_default", []))
162
167
  run_status.run_context = run_context
163
168
  end
164
-
169
+
165
170
  private
166
-
171
+
167
172
  def rebuild_node
168
173
  # Tell the client we're chef solo so it won't try to contact the server
169
174
  Chef::Config[:solo] = true
@@ -171,13 +176,13 @@ module Shef
171
176
  @client.run_ohai
172
177
  @client.build_node
173
178
  end
174
-
179
+
175
180
  end
176
-
181
+
177
182
  class ClientSession < SoloSession
178
183
 
179
184
  session_type :client
180
-
185
+
181
186
  def save_node
182
187
  @client.save_node
183
188
  end
@@ -187,6 +192,7 @@ module Shef
187
192
  Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, Chef::REST.new(Chef::Config[:server_url])) }
188
193
  cookbook_hash = @client.sync_cookbooks
189
194
  @run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new(cookbook_hash))
195
+ @run_context.load(Chef::RunList::RunListExpansionFromAPI.new("_default", []))
190
196
  @run_status.run_context = run_context
191
197
  end
192
198
 
@@ -228,7 +234,6 @@ module Shef
228
234
  ohai_data = @ohai.data.merge(@node.automatic_attrs)
229
235
 
230
236
  @node.consume_external_attrs(ohai_data,nil)
231
- @node.reset_defaults_and_overrides
232
237
 
233
238
  @node
234
239
  end
@@ -21,6 +21,7 @@ require 'tmpdir'
21
21
  require 'chef/log'
22
22
  require 'fcntl'
23
23
  require 'chef/exceptions'
24
+ require 'chef/shell_out/unix'
24
25
 
25
26
  class Chef
26
27
 
@@ -42,7 +43,20 @@ class Chef
42
43
  DEFAULT_READ_TIMEOUT = 60
43
44
  DEFAULT_ENVIRONMENT = {'LC_ALL' => 'C'}
44
45
 
45
- attr_accessor :user, :group, :cwd, :valid_exit_codes
46
+ if RUBY_PLATFORM =~ /mswin|mingw32|windows/
47
+ require 'chef/shell_out/windows'
48
+ include ShellOut::Windows
49
+ else
50
+ require 'chef/shell_out/unix'
51
+ include ShellOut::Unix
52
+ end
53
+
54
+ attr_accessor :user
55
+ attr_accessor :group
56
+ attr_accessor :cwd
57
+ attr_accessor :valid_exit_codes
58
+ attr_accessor :live_stream
59
+
46
60
  attr_reader :command, :umask, :environment
47
61
  attr_writer :timeout
48
62
  attr_reader :execution_time
@@ -96,6 +110,7 @@ class Chef
96
110
  # cmd.run_command # etc.
97
111
  def initialize(*command_args)
98
112
  @stdout, @stderr = '', ''
113
+ @live_stream = nil
99
114
  @environment = DEFAULT_ENVIRONMENT
100
115
  @cwd = nil
101
116
  @valid_exit_codes = [0]
@@ -154,56 +169,9 @@ class Chef
154
169
  # * Chef::Exceptions::CommandTimeout when the command does not complete
155
170
  # within +timeout+ seconds (default: 60s)
156
171
  def run_command
157
- Chef::Log.debug("sh(#{@command})")
158
- @child_pid = fork_subprocess
159
-
160
- configure_parent_process_file_descriptors
161
- propagate_pre_exec_failure
162
-
163
- @result = nil
164
- @execution_time = 0
165
-
166
- # Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
167
- # when calling IO.select and IO#read. Some OS Vendors are not interested
168
- # in updating their ruby packages (Apple, *cough*) and we *have to*
169
- # make it work. So I give you this epic hack:
170
- GC.disable
171
- until @status
172
- ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
173
- unless ready
174
- @execution_time += READ_WAIT_TIME
175
- if @execution_time >= timeout && !@result
176
- raise Chef::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
177
- end
178
- end
179
-
180
- if ready && ready.first.include?(child_stdout)
181
- read_stdout_to_buffer
182
- end
183
- if ready && ready.first.include?(child_stderr)
184
- read_stderr_to_buffer
185
- end
186
-
187
- unless @status
188
- # make one more pass to get the last of the output after the
189
- # child process dies
190
- if results = Process.waitpid2(@child_pid, Process::WNOHANG)
191
- @status = results.last
192
- redo
193
- end
194
- end
195
- end
196
- self
197
- rescue Exception
198
- # do our best to kill zombies
199
- Process.waitpid2(@child_pid, Process::WNOHANG) rescue nil
200
- raise
201
- ensure
202
- # no matter what happens, turn the GC back on, and hope whatever busted
203
- # version of ruby we're on doesn't allocate some objects during the next
204
- # GC run.
205
- GC.enable
206
- close_all_pipes
172
+ # Implemented by unix/windows backend module, we just want to define it
173
+ # here for documentation purposes
174
+ super
207
175
  end
208
176
 
209
177
  # Checks the +exitstatus+ against the set of +valid_exit_codes+. If
@@ -255,164 +223,17 @@ class Chef
255
223
  self.timeout = setting
256
224
  when 'returns'
257
225
  self.valid_exit_codes = Array(setting)
226
+ when 'live_stream'
227
+ self.live_stream = setting
258
228
  when 'environment', 'env'
259
229
  # passing :environment => nil means don't set any new ENV vars
260
- setting.nil? ? @environment = {} : @environment.merge!(setting)
230
+ @environment = setting.nil? ? {} : @environment.dup.merge!(setting)
261
231
  else
262
232
  raise Chef::Exceptions::InvalidCommandOption, "option '#{option.inspect}' is not a valid option for #{self.class.name}"
263
233
  end
264
234
  end
265
235
  end
266
236
 
267
- def set_user
268
- if user
269
- Process.euid = uid
270
- Process.uid = uid
271
- end
272
- end
273
-
274
- def set_group
275
- if group
276
- Process.egid = gid
277
- Process.gid = gid
278
- end
279
- end
280
-
281
- def set_environment
282
- environment.each do |env_var,value|
283
- ENV[env_var] = value
284
- end
285
- end
286
-
287
- def set_umask
288
- File.umask(umask) if umask
289
- end
290
-
291
- def set_cwd
292
- Dir.chdir(cwd) if cwd
293
- end
294
-
295
- def initialize_ipc
296
- @stdout_pipe, @stderr_pipe, @process_status_pipe = IO.pipe, IO.pipe, IO.pipe
297
- @process_status_pipe.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
298
- end
299
-
300
- def child_stdout
301
- @stdout_pipe[0]
302
- end
303
-
304
- def child_stderr
305
- @stderr_pipe[0]
306
- end
307
-
308
- def child_process_status
309
- @process_status_pipe[0]
310
- end
311
-
312
- def close_all_pipes
313
- child_stdout.close unless child_stdout.closed?
314
- child_stderr.close unless child_stderr.closed?
315
- child_process_status.close unless child_process_status.closed?
316
- end
317
-
318
- # replace stdout, and stderr with pipes to the parent, and close the
319
- # reader side of the error marshaling side channel. Close STDIN so when we
320
- # exec, the new program will know it's never getting input ever.
321
- def configure_subprocess_file_descriptors
322
- process_status_pipe.first.close
323
-
324
- # HACK: for some reason, just STDIN.close isn't good enough when running
325
- # under ruby 1.9.2, so make it good enough:
326
- stdin_reader, stdin_writer = IO.pipe
327
- stdin_writer.close
328
- STDIN.reopen stdin_reader
329
- stdin_reader.close
330
-
331
- stdout_pipe.first.close
332
- STDOUT.reopen stdout_pipe.last
333
- stdout_pipe.last.close
334
-
335
- stderr_pipe.first.close
336
- STDERR.reopen stderr_pipe.last
337
- stderr_pipe.last.close
338
-
339
- STDOUT.sync = STDERR.sync = true
340
- end
341
-
342
- def configure_parent_process_file_descriptors
343
- # Close the sides of the pipes we don't care about
344
- stdout_pipe.last.close
345
- stderr_pipe.last.close
346
- process_status_pipe.last.close
347
- # Get output as it happens rather than buffered
348
- child_stdout.sync = true
349
- child_stderr.sync = true
350
-
351
- true
352
- end
353
-
354
- # Some patch levels of ruby in wide use (in particular the ruby 1.8.6 on OSX)
355
- # segfault when you IO.select a pipe that's reached eof. Weak sauce.
356
- def open_pipes
357
- @open_pipes ||= [child_stdout, child_stderr]
358
- end
359
-
360
- def read_stdout_to_buffer
361
- while chunk = child_stdout.read_nonblock(READ_SIZE)
362
- @stdout << chunk
363
- end
364
- rescue Errno::EAGAIN
365
- rescue EOFError
366
- open_pipes.delete_at(0)
367
- end
368
-
369
- def read_stderr_to_buffer
370
- while chunk = child_stderr.read_nonblock(READ_SIZE)
371
- @stderr << chunk
372
- end
373
- rescue Errno::EAGAIN
374
- rescue EOFError
375
- open_pipes.delete_at(1)
376
- end
377
-
378
- def fork_subprocess
379
- initialize_ipc
380
-
381
- fork do
382
- configure_subprocess_file_descriptors
383
-
384
- set_user
385
- set_group
386
- set_environment
387
- set_umask
388
- set_cwd
389
-
390
- begin
391
- command.kind_of?(Array) ? exec(*command) : exec(command)
392
-
393
- raise 'forty-two' # Should never get here
394
- rescue Exception => e
395
- Marshal.dump(e, process_status_pipe.last)
396
- process_status_pipe.last.flush
397
- end
398
- process_status_pipe.last.close unless (process_status_pipe.last.closed?)
399
- exit!
400
- end
401
- end
402
-
403
- # Attempt to get a Marshaled error from the side-channel.
404
- # If it's there, un-marshal it and raise. If it's not there,
405
- # assume everything went well.
406
- def propagate_pre_exec_failure
407
- begin
408
- e = Marshal.load child_process_status
409
- raise(Exception === e ? e : "unknown failure: #{e.inspect}")
410
- rescue EOFError # If we get an EOF error, then the exec was successful
411
- true
412
- ensure
413
- child_process_status.close
414
- end
415
- end
416
237
 
417
238
  end
418
239
  end