chef 0.9.18 → 0.10.0.beta.0

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