chef 0.7.10

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.

Files changed (120) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +135 -0
  3. data/bin/chef-client +26 -0
  4. data/bin/chef-solo +26 -0
  5. data/lib/chef.rb +49 -0
  6. data/lib/chef/application.rb +98 -0
  7. data/lib/chef/application/agent.rb +18 -0
  8. data/lib/chef/application/client.rb +209 -0
  9. data/lib/chef/application/indexer.rb +141 -0
  10. data/lib/chef/application/server.rb +18 -0
  11. data/lib/chef/application/solo.rb +214 -0
  12. data/lib/chef/client.rb +396 -0
  13. data/lib/chef/compile.rb +138 -0
  14. data/lib/chef/config.rb +141 -0
  15. data/lib/chef/cookbook.rb +144 -0
  16. data/lib/chef/cookbook/metadata.rb +407 -0
  17. data/lib/chef/cookbook/metadata/version.rb +87 -0
  18. data/lib/chef/cookbook_loader.rb +168 -0
  19. data/lib/chef/couchdb.rb +172 -0
  20. data/lib/chef/daemon.rb +170 -0
  21. data/lib/chef/exceptions.rb +36 -0
  22. data/lib/chef/file_cache.rb +205 -0
  23. data/lib/chef/log.rb +39 -0
  24. data/lib/chef/mixin/check_helper.rb +31 -0
  25. data/lib/chef/mixin/checksum.rb +37 -0
  26. data/lib/chef/mixin/command.rb +351 -0
  27. data/lib/chef/mixin/create_path.rb +56 -0
  28. data/lib/chef/mixin/deep_merge.rb +36 -0
  29. data/lib/chef/mixin/find_preferred_file.rb +99 -0
  30. data/lib/chef/mixin/from_file.rb +36 -0
  31. data/lib/chef/mixin/generate_url.rb +48 -0
  32. data/lib/chef/mixin/language.rb +79 -0
  33. data/lib/chef/mixin/params_validate.rb +197 -0
  34. data/lib/chef/mixin/template.rb +84 -0
  35. data/lib/chef/node.rb +406 -0
  36. data/lib/chef/node/attribute.rb +412 -0
  37. data/lib/chef/openid_registration.rb +181 -0
  38. data/lib/chef/platform.rb +253 -0
  39. data/lib/chef/provider.rb +40 -0
  40. data/lib/chef/provider/cron.rb +137 -0
  41. data/lib/chef/provider/directory.rb +72 -0
  42. data/lib/chef/provider/execute.rb +58 -0
  43. data/lib/chef/provider/file.rb +191 -0
  44. data/lib/chef/provider/group.rb +120 -0
  45. data/lib/chef/provider/group/groupadd.rb +92 -0
  46. data/lib/chef/provider/group/pw.rb +88 -0
  47. data/lib/chef/provider/http_request.rb +102 -0
  48. data/lib/chef/provider/ifconfig.rb +131 -0
  49. data/lib/chef/provider/link.rb +157 -0
  50. data/lib/chef/provider/mount.rb +121 -0
  51. data/lib/chef/provider/mount/mount.rb +208 -0
  52. data/lib/chef/provider/package.rb +160 -0
  53. data/lib/chef/provider/package/apt.rb +110 -0
  54. data/lib/chef/provider/package/dpkg.rb +113 -0
  55. data/lib/chef/provider/package/freebsd.rb +153 -0
  56. data/lib/chef/provider/package/macports.rb +105 -0
  57. data/lib/chef/provider/package/portage.rb +124 -0
  58. data/lib/chef/provider/package/rpm.rb +99 -0
  59. data/lib/chef/provider/package/rubygems.rb +130 -0
  60. data/lib/chef/provider/package/yum-dump.py +104 -0
  61. data/lib/chef/provider/package/yum.rb +175 -0
  62. data/lib/chef/provider/remote_directory.rb +126 -0
  63. data/lib/chef/provider/remote_file.rb +134 -0
  64. data/lib/chef/provider/route.rb +118 -0
  65. data/lib/chef/provider/ruby_block.rb +15 -0
  66. data/lib/chef/provider/script.rb +42 -0
  67. data/lib/chef/provider/service.rb +129 -0
  68. data/lib/chef/provider/service/debian.rb +64 -0
  69. data/lib/chef/provider/service/freebsd.rb +157 -0
  70. data/lib/chef/provider/service/gentoo.rb +54 -0
  71. data/lib/chef/provider/service/init.rb +126 -0
  72. data/lib/chef/provider/service/redhat.rb +62 -0
  73. data/lib/chef/provider/template.rb +141 -0
  74. data/lib/chef/provider/user.rb +170 -0
  75. data/lib/chef/provider/user/pw.rb +113 -0
  76. data/lib/chef/provider/user/useradd.rb +107 -0
  77. data/lib/chef/queue.rb +145 -0
  78. data/lib/chef/recipe.rb +210 -0
  79. data/lib/chef/resource.rb +256 -0
  80. data/lib/chef/resource/apt_package.rb +34 -0
  81. data/lib/chef/resource/bash.rb +33 -0
  82. data/lib/chef/resource/cron.rb +143 -0
  83. data/lib/chef/resource/csh.rb +33 -0
  84. data/lib/chef/resource/directory.rb +76 -0
  85. data/lib/chef/resource/dpkg_package.rb +34 -0
  86. data/lib/chef/resource/execute.rb +127 -0
  87. data/lib/chef/resource/file.rb +84 -0
  88. data/lib/chef/resource/gem_package.rb +41 -0
  89. data/lib/chef/resource/group.rb +68 -0
  90. data/lib/chef/resource/http_request.rb +52 -0
  91. data/lib/chef/resource/ifconfig.rb +134 -0
  92. data/lib/chef/resource/link.rb +78 -0
  93. data/lib/chef/resource/macports_package.rb +29 -0
  94. data/lib/chef/resource/mount.rb +135 -0
  95. data/lib/chef/resource/package.rb +80 -0
  96. data/lib/chef/resource/perl.rb +33 -0
  97. data/lib/chef/resource/portage_package.rb +33 -0
  98. data/lib/chef/resource/python.rb +33 -0
  99. data/lib/chef/resource/remote_directory.rb +91 -0
  100. data/lib/chef/resource/remote_file.rb +60 -0
  101. data/lib/chef/resource/route.rb +135 -0
  102. data/lib/chef/resource/ruby.rb +33 -0
  103. data/lib/chef/resource/ruby_block.rb +20 -0
  104. data/lib/chef/resource/script.rb +51 -0
  105. data/lib/chef/resource/service.rb +134 -0
  106. data/lib/chef/resource/template.rb +60 -0
  107. data/lib/chef/resource/user.rb +98 -0
  108. data/lib/chef/resource_collection.rb +176 -0
  109. data/lib/chef/resource_definition.rb +67 -0
  110. data/lib/chef/rest.rb +238 -0
  111. data/lib/chef/role.rb +231 -0
  112. data/lib/chef/run_list.rb +156 -0
  113. data/lib/chef/runner.rb +123 -0
  114. data/lib/chef/search.rb +88 -0
  115. data/lib/chef/search/result.rb +64 -0
  116. data/lib/chef/search_index.rb +77 -0
  117. data/lib/chef/tasks/chef_repo.rake +345 -0
  118. data/lib/chef/util/file_edit.rb +125 -0
  119. data/lib/chef/util/fileedit.rb +121 -0
  120. metadata +262 -0
@@ -0,0 +1,36 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 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
+ class Chef
19
+ class Exceptions
20
+ class Application < RuntimeError; end
21
+ class Cron < RuntimeError; end
22
+ class Exec < RuntimeError; end
23
+ class FileNotFound < RuntimeError; end
24
+ class Package < RuntimeError; end
25
+ class Service < RuntimeError; end
26
+ class Route < RuntimeError; end
27
+ class SearchIndex < RuntimeError; end
28
+ class Override < RuntimeError; end
29
+ class UnsupportedAction < RuntimeError; end
30
+ class MissingLibrary < RuntimeError; end
31
+ class User < RuntimeError; end
32
+ class Group < RuntimeError; end
33
+ class Link < RuntimeError; end
34
+ class Mount < RuntimeError; end
35
+ end
36
+ end
@@ -0,0 +1,205 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 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
+ require 'chef/mixin/params_validate'
19
+ require 'chef/mixin/create_path'
20
+ require 'chef/exceptions'
21
+ require 'json'
22
+ require 'fileutils'
23
+
24
+ class Chef
25
+ class FileCache
26
+ class << self
27
+ include Chef::Mixin::ParamsValidate
28
+ include Chef::Mixin::CreatePath
29
+
30
+ # Write a file to the File Cache.
31
+ #
32
+ # === Parameters
33
+ # path<String>:: The path to the file you want to put in the cache - should
34
+ # be relative to Chef::Config[:file_cache_path]
35
+ # contents<String>:: A string with the contents you want written to the file
36
+ #
37
+ # === Returns
38
+ # true
39
+ def store(path, contents)
40
+ validate(
41
+ {
42
+ :path => path,
43
+ :contents => contents
44
+ },
45
+ {
46
+ :path => { :kind_of => String },
47
+ :contents => { :kind_of => String },
48
+ }
49
+ )
50
+
51
+ file_path_array = File.split(path)
52
+ file_name = file_path_array.pop
53
+ cache_path = create_cache_path(File.join(file_path_array))
54
+ io = File.open(File.join(cache_path, file_name), "w")
55
+ io.print(contents)
56
+ io.close
57
+ true
58
+ end
59
+
60
+ # Move a file in to the cache. Useful with the REST raw file output.
61
+ #
62
+ # === Parameteres
63
+ # file<String>:: The path to the file you want in the cache
64
+ # path<String>:: The relative name you want the new file to use
65
+ def move_to(file, path)
66
+ validate(
67
+ {
68
+ :file => file,
69
+ :path => path
70
+ },
71
+ {
72
+ :file => { :kind_of => String },
73
+ :path => { :kind_of => String },
74
+ }
75
+ )
76
+
77
+ file_path_array = File.split(path)
78
+ file_name = file_path_array.pop
79
+ if File.exists?(file) && File.writable?(file)
80
+ FileUtils.mv(
81
+ file,
82
+ File.join(create_cache_path(File.join(file_path_array), true), file_name)
83
+ )
84
+ else
85
+ raise RuntimeError, "Cannot move #{file} to #{path}!"
86
+ end
87
+ end
88
+
89
+ # Read a file from the File Cache
90
+ #
91
+ # === Parameters
92
+ # path<String>:: The path to the file you want to load - should
93
+ # be relative to Chef::Config[:file_cache_path]
94
+ # read<True/False>:: Whether to return the file contents, or the path.
95
+ # Defaults to true.
96
+ #
97
+ # === Returns
98
+ # String:: A string with the file contents, or the path to the file.
99
+ #
100
+ # === Raises
101
+ # Chef::Exceptions::FileNotFound:: If it cannot find the file in the cache
102
+ def load(path, read=true)
103
+ validate(
104
+ {
105
+ :path => path
106
+ },
107
+ {
108
+ :path => { :kind_of => String }
109
+ }
110
+ )
111
+ cache_path = create_cache_path(path, false)
112
+ raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exists?(cache_path)
113
+ if read
114
+ File.read(cache_path)
115
+ else
116
+ cache_path
117
+ end
118
+ end
119
+
120
+ # Delete a file from the File Cache
121
+ #
122
+ # === Parameters
123
+ # path<String>:: The path to the file you want to delete - should
124
+ # be relative to Chef::Config[:file_cache_path]
125
+ #
126
+ # === Returns
127
+ # true
128
+ def delete(path)
129
+ validate(
130
+ {
131
+ :path => path
132
+ },
133
+ {
134
+ :path => { :kind_of => String },
135
+ }
136
+ )
137
+ cache_path = create_cache_path(path, false)
138
+ if File.exists?(cache_path)
139
+ File.unlink(cache_path)
140
+ end
141
+ true
142
+ end
143
+
144
+ # List all the files in the Cache
145
+ #
146
+ # === Returns
147
+ # Array:: An array of files in the cache, suitable for use with load, delete and store
148
+ def list()
149
+ keys = Array.new
150
+ Dir[File.join(Chef::Config[:file_cache_path], '**', '*')].each do |f|
151
+ if File.file?(f)
152
+ path = f.match("^#{Chef::Config[:file_cache_path]}\/(.+)")[1]
153
+ keys << path
154
+ end
155
+ end
156
+ keys
157
+ end
158
+
159
+ # Whether or not this file exists in the Cache
160
+ #
161
+ # === Parameters
162
+ # path:: The path to the file you want to check - is relative
163
+ # to Chef::Config[:file_cache_path]
164
+ #
165
+ # === Returns
166
+ # True:: If the file exists
167
+ # False:: If it does not
168
+ def has_key?(path)
169
+ validate(
170
+ {
171
+ :path => path
172
+ },
173
+ {
174
+ :path => { :kind_of => String },
175
+ }
176
+ )
177
+ full_path = create_cache_path(path, false)
178
+ if File.exists?(full_path)
179
+ true
180
+ else
181
+ false
182
+ end
183
+ end
184
+
185
+ # Create a full path to a given file in the cache. By default,
186
+ # also creates the path if it does not exist.
187
+ #
188
+ # === Parameters
189
+ # path:: The path to create, relative to Chef::Config[:file_cache_path]
190
+ # create_if_missing:: True by default - whether to create the path if it does not exist
191
+ #
192
+ # === Returns
193
+ # String:: The fully expanded path
194
+ def create_cache_path(path, create_if_missing=true)
195
+ cache_dir = File.expand_path(File.join(Chef::Config[:file_cache_path], path))
196
+ if create_if_missing
197
+ create_path(cache_dir)
198
+ else
199
+ cache_dir
200
+ end
201
+ end
202
+
203
+ end
204
+ end
205
+ end
data/lib/chef/log.rb ADDED
@@ -0,0 +1,39 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: AJ Christensen (<@aj@opsocde.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require 'chef'
20
+ require 'mixlib/log'
21
+
22
+ class Chef
23
+ class Log
24
+ extend Mixlib::Log
25
+
26
+ # This is here for compatability, before we moved to
27
+ # Mixlib::Log.
28
+ class Formatter
29
+ def self.show_time=(arg)
30
+ Mixlib::Log::Formatter.show_time = arg
31
+ end
32
+
33
+ def self.show_time
34
+ Mixlib::Log::Formatter.show_time
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,31 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 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
+ class Chef
19
+ module Mixin
20
+ module CheckHelper
21
+ def set_if_args(thing, arguments)
22
+ raise ArgumentError, "Must call set_if_args with a block!" unless Kernel.block_given?
23
+ if arguments != nil
24
+ yield(arguments)
25
+ else
26
+ thing
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 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 'digest/sha2'
20
+
21
+ class Chef
22
+ module Mixin
23
+ module Checksum
24
+
25
+ def checksum(file)
26
+ digest = Digest::SHA256.new
27
+ fh = ::File.open(file)
28
+ fh.each do |line|
29
+ digest.update(line)
30
+ end
31
+ fh.close
32
+ digest.hexdigest
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,351 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 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/log'
20
+ require 'chef/exceptions'
21
+ require 'tmpdir'
22
+ require 'fcntl'
23
+ require 'etc'
24
+ require 'io/wait'
25
+
26
+ class Chef
27
+ module Mixin
28
+ module Command
29
+
30
+ # If command is a block, returns true if the block returns true, false if it returns false.
31
+ # ("Only run this resource if the block is true")
32
+ #
33
+ # If the command is not a block, executes the command. If it returns any status other than
34
+ # 0, it returns false (clearly, a 0 status code is true)
35
+ #
36
+ # === Parameters
37
+ # command<Block>, <String>:: A block to check, or a string to execute
38
+ #
39
+ # === Returns
40
+ # true:: Returns true if the block is true, or if the command returns 0
41
+ # false:: Returns false if the block is false, or if the command returns a non-zero exit code.
42
+ def only_if(command)
43
+ if command.kind_of?(Proc)
44
+ res = command.call
45
+ unless res
46
+ return false
47
+ end
48
+ else
49
+ status = run_command(:command => command, :ignore_failure => true)
50
+ if status.exitstatus != 0
51
+ return false
52
+ end
53
+ end
54
+ true
55
+ end
56
+
57
+ module_function :only_if
58
+
59
+ # If command is a block, returns false if the block returns true, true if it returns false.
60
+ # ("Do not run this resource if the block is true")
61
+ #
62
+ # If the command is not a block, executes the command. If it returns a 0 exitstatus, returns false.
63
+ # ("Do not run this resource if the command returns 0")
64
+ #
65
+ # === Parameters
66
+ # command<Block>, <String>:: A block to check, or a string to execute
67
+ #
68
+ # === Returns
69
+ # true:: Returns true if the block is false, or if the command returns a non-zero exit status.
70
+ # false:: Returns false if the block is true, or if the command returns a 0 exit status.
71
+ def not_if(command)
72
+ if command.kind_of?(Proc)
73
+ res = command.call
74
+ if res
75
+ return false
76
+ end
77
+ else
78
+ status = run_command(:command => command, :ignore_failure => true)
79
+ if status.exitstatus == 0
80
+ return false
81
+ end
82
+ end
83
+ true
84
+ end
85
+
86
+ module_function :not_if
87
+
88
+ # === Parameters
89
+ # args<Hash>: A number of required and optional arguments
90
+ # command<String>, <Array>: A complete command with options to execute or a command and options as an Array
91
+ # creates<String>: The absolute path to a file that prevents the command from running if it exists
92
+ # cwd<String>: Working directory to execute command in, defaults to Dir.tmpdir
93
+ # timeout<String>: How many seconds to wait for the command to execute before timing out
94
+ # returns<String>: The single exit value command is expected to return, otherwise causes an exception
95
+ # ignore_failure<Boolean>: Whether to raise an exception on failure, or just return the status
96
+ #
97
+ # user<String>: The UID or user name of the user to execute the command as
98
+ # group<String>: The GID or group name of the group to execute the command as
99
+ # environment<Hash>: Pairs of environment variable names and their values to set before execution
100
+ #
101
+ # === Returns
102
+ # Returns the exit status of args[:command]
103
+ def run_command(args={})
104
+ command_output = ""
105
+
106
+ args[:ignore_failure] ||= false
107
+
108
+ if args.has_key?(:creates)
109
+ if File.exists?(args[:creates])
110
+ Chef::Log.debug("Skipping #{args[:command]} - creates #{args[:creates]} exists.")
111
+ return false
112
+ end
113
+ end
114
+
115
+ exec_processing_block = lambda do |pid, stdin, stdout, stderr|
116
+ Chef::Log.debug("---- Begin output of #{args[:command]} ----")
117
+ Chef::Log.debug("STDOUT: #{stdout.string.chomp}")
118
+ Chef::Log.debug("STDERR: #{stderr.string.chomp}")
119
+ command_output << "STDOUT: #{stdout.string.chomp}"
120
+ command_output << "STDERR: #{stderr.string.chomp}"
121
+ Chef::Log.debug("---- End output of #{args[:command]} ----")
122
+ end
123
+
124
+ args[:cwd] ||= Dir.tmpdir
125
+ unless File.directory?(args[:cwd])
126
+ raise Chef::Exceptions::Exec, "#{args[:cwd]} does not exist or is not a directory"
127
+ end
128
+
129
+ Chef::Log.debug("Executing #{args[:command]}")
130
+
131
+ status = nil
132
+
133
+ Dir.chdir(args[:cwd]) do
134
+ if args[:timeout]
135
+ begin
136
+ Timeout.timeout(args[:timeout]) do
137
+ status = popen4(args[:command], args, &exec_processing_block)
138
+ end
139
+ rescue Timeout::Error => e
140
+ Chef::Log.error("#{args[:command]} exceeded timeout #{args[:timeout]}")
141
+ raise(e)
142
+ end
143
+ else
144
+ status = popen4(args[:command], args, &exec_processing_block)
145
+ end
146
+
147
+ unless args[:ignore_failure]
148
+ args[:returns] ||= 0
149
+ if status.exitstatus != args[:returns]
150
+ # if the log level is not debug, through output of command when we fail
151
+ output = ""
152
+ if Chef::Log.logger.level > 0
153
+ output << "\n---- Begin output of #{args[:command]} ----\n"
154
+ output << "#{command_output}"
155
+ output << "---- End output of #{args[:command]} ----\n"
156
+ end
157
+ raise Chef::Exceptions::Exec, "#{args[:command]} returned #{status.exitstatus}, expected #{args[:returns]}#{output}"
158
+ end
159
+ end
160
+ Chef::Log.debug("Ran #{args[:command]} returned #{status.exitstatus}")
161
+ end
162
+ status
163
+ end
164
+
165
+ module_function :run_command
166
+
167
+ # This is taken directly from Ara T Howard's Open4 library, and then
168
+ # modified to suit the needs of Chef. Any bugs here are most likely
169
+ # my own, and not Ara's.
170
+ #
171
+ # The original appears in external/open4.rb in its unmodified form.
172
+ #
173
+ # Thanks Ara!
174
+ def popen4(cmd, args={}, &b)
175
+
176
+ # Waitlast - this is magic.
177
+ #
178
+ # Do we wait for the child process to die before we yield
179
+ # to the block, or after? That is the magic of waitlast.
180
+ #
181
+ # By default, we are waiting before we yield the block.
182
+ args[:waitlast] ||= false
183
+
184
+ args[:user] ||= nil
185
+ unless args[:user].kind_of?(Integer)
186
+ args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
187
+ end
188
+ args[:group] ||= nil
189
+ unless args[:group].kind_of?(Integer)
190
+ args[:group] = Etc.getgrnam(args[:group]).gid if args[:group]
191
+ end
192
+ args[:environment] ||= nil
193
+
194
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
195
+
196
+ verbose = $VERBOSE
197
+ begin
198
+ $VERBOSE = nil
199
+ ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
200
+
201
+ cid = fork {
202
+ pw.last.close
203
+ STDIN.reopen pw.first
204
+ pw.first.close
205
+
206
+ pr.first.close
207
+ STDOUT.reopen pr.last
208
+ pr.last.close
209
+
210
+ pe.first.close
211
+ STDERR.reopen pe.last
212
+ pe.last.close
213
+
214
+ STDOUT.sync = STDERR.sync = true
215
+
216
+ if args[:group]
217
+ Process.egid = args[:group]
218
+ Process.gid = args[:group]
219
+ end
220
+
221
+ if args[:user]
222
+ Process.euid = args[:user]
223
+ Process.uid = args[:user]
224
+ end
225
+
226
+ if args[:environment]
227
+ args[:environment].each do |key,value|
228
+ ENV[key] = value
229
+ end
230
+ end
231
+
232
+ if args[:umask]
233
+ umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777)
234
+ File.umask(umask)
235
+ end
236
+
237
+ begin
238
+ if cmd.kind_of?(Array)
239
+ exec(*cmd)
240
+ else
241
+ exec(cmd)
242
+ end
243
+ raise 'forty-two'
244
+ rescue Exception => e
245
+ Marshal.dump(e, ps.last)
246
+ ps.last.flush
247
+ end
248
+ ps.last.close unless (ps.last.closed?)
249
+ exit!
250
+ }
251
+ ensure
252
+ $VERBOSE = verbose
253
+ end
254
+
255
+ [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
256
+
257
+ begin
258
+ e = Marshal.load ps.first
259
+ raise(Exception === e ? e : "unknown failure!")
260
+ rescue EOFError # If we get an EOF error, then the exec was successful
261
+ 42
262
+ ensure
263
+ ps.first.close
264
+ end
265
+
266
+ pw.last.sync = true
267
+
268
+ pi = [pw.last, pr.first, pe.first]
269
+
270
+ if b
271
+ begin
272
+ if args[:waitlast]
273
+ b[cid, *pi]
274
+ Process.waitpid2(cid).last
275
+ else
276
+ # This took some doing.
277
+ # The trick here is to close STDIN
278
+ # Then set our end of the childs pipes to be O_NONBLOCK
279
+ # Then wait for the child to die, which means any IO it
280
+ # wants to do must be done - it's dead. If it isn't,
281
+ # it's because something totally skanky is happening,
282
+ # and we don't care.
283
+ o = StringIO.new
284
+ e = StringIO.new
285
+
286
+ pi[0].close
287
+
288
+ stdout = pi[1]
289
+ stderr = pi[2]
290
+
291
+ stdout.sync = true
292
+ stderr.sync = true
293
+
294
+ stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
295
+ stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
296
+
297
+ stdout_finished = false
298
+ stderr_finished = false
299
+
300
+ results = nil
301
+
302
+ while !stdout_finished || !stderr_finished
303
+ begin
304
+ channels_to_watch = []
305
+ channels_to_watch << stdout if !stdout_finished
306
+ channels_to_watch << stderr if !stderr_finished
307
+ ready = IO.select(channels_to_watch, nil, nil, 1.0)
308
+ rescue Errno::EAGAIN
309
+ results = Process.waitpid2(cid, Process::WNOHANG)
310
+ if results
311
+ stdout_finished = true
312
+ stderr_finished = true
313
+ end
314
+ end
315
+
316
+ if ready && ready.first.include?(stdout)
317
+ line = results ? stdout.gets(nil) : stdout.gets
318
+ if line
319
+ o.write(line)
320
+ else
321
+ stdout_finished = true
322
+ end
323
+ end
324
+ if ready && ready.first.include?(stderr)
325
+ line = results ? stderr.gets(nil) : stderr.gets
326
+ if line
327
+ e.write(line)
328
+ else
329
+ stderr_finished = true
330
+ end
331
+ end
332
+ end
333
+ results = Process.waitpid2(cid).last unless results
334
+ o.rewind
335
+ e.rewind
336
+ b[cid, pi[0], o, e]
337
+ results
338
+ end
339
+ ensure
340
+ pi.each{|fd| fd.close unless fd.closed?}
341
+ end
342
+ else
343
+ [cid, pw.last, pr.first, pe.first]
344
+ end
345
+ end
346
+
347
+ module_function :popen4
348
+
349
+ end
350
+ end
351
+ end