summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/chef/exceptions.rb3
-rw-r--r--lib/chef/mixin/command.rb204
-rw-r--r--lib/chef/platform.rb2
-rw-r--r--lib/chef/provider/execute.rb55
-rw-r--r--lib/chef/resource/execute.rb133
5 files changed, 396 insertions, 1 deletions
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index 94a6950414..2aad51096b 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -17,6 +17,7 @@
class Chef
class Exception
- class SearchIndex < RuntimeError; end
+ class SearchIndex < RuntimeError; end
+ class Exec < RuntimeError; end
end
end \ No newline at end of file
diff --git a/lib/chef/mixin/command.rb b/lib/chef/mixin/command.rb
new file mode 100644
index 0000000000..b513abb0eb
--- /dev/null
+++ b/lib/chef/mixin/command.rb
@@ -0,0 +1,204 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This is taken directly from Ara T Howard's Open4 library, and then
+# modified to suit the needs of Chef. Any bugs here are most likely
+# my own, and not Ara.
+#
+# The original appears in external/open4.rb in it's unmodified form.
+#
+# Thanks, Ara.
+
+require 'tmpdir'
+require 'fcntl'
+require 'etc'
+
+class Chef
+ module Mixin
+ module Command
+
+ def run_command(args={})
+ if args.has_key?(:creates)
+ if File.exists?(args[:creates])
+ Chef::Log.debug("Skipping #{args[:command_string]} - creates #{args[:creates]} exists.")
+ return false
+ end
+ end
+
+ if args.has_key?(:onlyif)
+ status = popen4(args[:onlyif]) { |p, i, o, e| }
+ if status.exitstatus != 0
+ Chef::Log.debug("Skipping #{args[:command_string]} - onlyif #{args[:onlyif]} returned #{status.exitstatus}")
+ return false
+ end
+ end
+
+ if args.has_key?(:unless)
+ status = popen4(args[:unless]) { |p, i, o, e| }
+ if status.exitstatus == 0
+ Chef::Log.debug("Skipping #{args[:command_string]} - unless #{args[:unless]} returned #{status.exitstatus}")
+ return false
+ end
+ end
+
+ exec_processing_block = lambda do |pid, stdin, stdout, stderr|
+ stdin.close
+
+ stdout_string = stdout.gets(nil)
+ if stdout_string
+ Chef::Log.debug("---- Begin #{args[:command_string]} STDOUT ----")
+ Chef::Log.debug(stdout_string.strip)
+ Chef::Log.debug("---- End #{args[:command_string]} STDOUT ----")
+ end
+ stderr_string = stderr.gets(nil)
+ if stderr_string
+ Chef::Log.debug("---- Begin #{args[:command_string]} STDERR ----")
+ Chef::Log.debug(stderr_string.strip)
+ Chef::Log.debug("---- End #{args[:command_string]} STDERR ----")
+ end
+ end
+
+ args[:cwd] ||= Dir.tmpdir
+ unless File.directory?(args[:cwd])
+ raise Chef::Exception::Exec, "#{args[:cwd]} does not exist or is not a directory"
+ end
+
+ status = nil
+ Dir.chdir(args[:cwd]) do
+ if args[:timeout]
+ begin
+ Timeout.timeout(args[:timeout]) do
+ status = popen4(args[:command], args, &exec_processing_block)
+ end
+ rescue Exception => e
+ Chef::Log.error("#{args[:command_string]} exceeded timeout #{args[:timeout]}")
+ raise(e)
+ end
+ else
+ status = popen4(args[:command], args, &exec_processing_block)
+ end
+
+ if status.exitstatus != args[:returns]
+ raise Chef::Exception::Exec, "#{args[:command_string]} returned #{status.exitstatus}, expected #{args[:returns]}"
+ else
+ Chef::Log.debug("Ran #{args[:command_string]} returned #{status.exitstatus}")
+ end
+ end
+ status
+ end
+
+ module_function :run_command
+
+ def popen4(cmd, args={}, &b)
+
+ args[:user] ||= nil
+ unless args[:user].kind_of?(Integer)
+ args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
+ end
+ args[:group] ||= nil
+ unless args[:group].kind_of?(Integer)
+ args[:group] = Etc.getgrnam(args[:group]).gid if args[:group]
+ end
+ args[:environment] ||= nil
+
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
+
+ verbose = $VERBOSE
+ begin
+ $VERBOSE = nil
+ ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
+
+ cid = fork {
+ pw.last.close
+ STDIN.reopen pw.first
+ pw.first.close
+
+ pr.first.close
+ STDOUT.reopen pr.last
+ pr.last.close
+
+ pe.first.close
+ STDERR.reopen pe.last
+ pe.last.close
+
+ STDOUT.sync = STDERR.sync = true
+
+ if args[:user]
+ Process.euid = args[:user]
+ Process.uid = args[:user]
+ end
+
+ if args[:group]
+ Process.egid = args[:group]
+ Process.gid = args[:group]
+ end
+
+ if args[:environment]
+ args[:environment].each do |key,value|
+ ENV[key] = value
+ end
+ end
+
+ begin
+ if cmd.kind_of?(Array)
+ exec(*cmd)
+ else
+ exec(cmd)
+ end
+ raise 'forty-two'
+ rescue Exception => e
+ Marshal.dump(e, ps.last)
+ ps.last.flush
+ end
+ ps.last.close unless (ps.last.closed?)
+ exit!
+ }
+ ensure
+ $VERBOSE = verbose
+ end
+
+ [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
+
+ begin
+ e = Marshal.load ps.first
+ raise(Exception === e ? e : "unknown failure!")
+ rescue EOFError # If we get an EOF error, then the exec was successful
+ 42
+ ensure
+ ps.first.close
+ end
+
+ pw.last.sync = true
+
+ pi = [pw.last, pr.first, pe.first]
+
+ if b
+ begin
+ b[cid, *pi]
+ Process.waitpid2(cid).last
+ ensure
+ pi.each{|fd| fd.close unless fd.closed?}
+ end
+ else
+ [cid, pw.last, pr.first, pe.first]
+ end
+ end
+
+ module_function :popen4
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/platform.rb b/lib/chef/platform.rb
index 3e7519c63f..e947328c9a 100644
--- a/lib/chef/platform.rb
+++ b/lib/chef/platform.rb
@@ -36,6 +36,8 @@ class Chef
:template => Chef::Provider::Template,
:remote_file => Chef::Provider::RemoteFile,
:remote_directory => Chef::Provider::RemoteDirectory,
+ :sysctl => Chef::Provider::Sysctl,
+ :execute => Chef::Provider::Execute
}
}
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
new file mode 100644
index 0000000000..82603fe370
--- /dev/null
+++ b/lib/chef/provider/execute.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require File.join(File.dirname(__FILE__), "..", "mixin", "command")
+
+class Chef
+ class Provider
+ class Execute < Chef::Provider
+
+ include Chef::Mixin::Command
+
+ def load_current_resource
+ true
+ end
+
+ def action_run
+ command_args = {
+ :command => @new_resource.command,
+ :command_string => @new_resource.to_s,
+ }
+ command_args[:creates] = @new_resource.creates if @new_resource.creates
+ command_args[:onlyif] = @new_resource.onlyif if @new_resource.onlyif
+ command_args[:unless] = @new_resource.unless if @new_resource.unless
+ command_args[:timeout] = @new_resource.timeout if @new_resource.timeout
+ command_args[:returns] = @new_resource.returns if @new_resource.returns
+ command_args[:environment] = @new_resource.environment if @new_resource.environment
+ command_args[:user] = @new_resource.user if @new_resource.user
+ command_args[:group] = @new_resource.group if @new_resource.group
+ command_args[:cwd] = @new_resource.cwd if @new_resource.cwd
+
+ status = run_command(command_args)
+ if status
+ @new_resource.updated = true
+ Chef::Log.info("Ran #{@new_resource} successfully")
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
new file mode 100644
index 0000000000..21f08e2a34
--- /dev/null
+++ b/lib/chef/resource/execute.rb
@@ -0,0 +1,133 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Resource
+ class Execute < Chef::Resource
+
+ def initialize(name, collection=nil, node=nil)
+ super(name, collection, node)
+ @resource_name = :execute
+ @command = name
+ @backup = 5
+ @action = "run"
+ @creates = nil
+ @cwd = nil
+ @environment = nil
+ @group = nil
+ @onlyif = nil
+ @path = nil
+ @notify_only = false
+ @returns = 0
+ @timeout = nil
+ @unless = nil
+ @user = nil
+ @allowed_actions.push(:run)
+ end
+
+ def command(arg=nil)
+ set_or_return(
+ :command,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ def creates(arg=nil)
+ set_or_return(
+ :creates,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ def cwd(arg=nil)
+ set_or_return(
+ :cwd,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ def environment(arg=nil)
+ set_or_return(
+ :environment,
+ arg,
+ :kind_of => [ Hash ]
+ )
+ end
+
+ def group(arg=nil)
+ set_or_return(
+ :group,
+ arg,
+ :kind_of => [ String, Integer ]
+ )
+ end
+
+ def onlyif(arg=nil)
+ set_or_return(
+ :onlyif,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ def path(arg=nil)
+ set_or_return(
+ :path,
+ arg,
+ :kind_of => [ Array ]
+ )
+ end
+
+ def returns(arg=nil)
+ set_or_return(
+ :returns,
+ arg,
+ :kind_of => [ Integer ]
+ )
+ end
+
+ def timeout(arg=nil)
+ set_or_return(
+ :timeout,
+ arg,
+ :kind_of => [ Integer ]
+ )
+ end
+
+ def unless(arg=nil)
+ set_or_return(
+ :unless,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ def user(arg=nil)
+ set_or_return(
+ :user,
+ arg,
+ :kind_of => [ String, Integer ]
+ )
+ end
+
+ end
+ end
+end \ No newline at end of file