diff options
author | Adam Jacob <adam@hjksolutions.com> | 2008-09-01 20:43:08 -0700 |
---|---|---|
committer | Adam Jacob <adam@hjksolutions.com> | 2008-09-01 20:43:08 -0700 |
commit | f826880c267755005fccb93bdac6105a3a8e24b2 (patch) | |
tree | 19570c8728f0b1c7130bce9ba6ca2b93b38eedcb /lib/chef | |
parent | 8fa13f5a5cbff073a39e69142fccb08f96b66dd9 (diff) | |
download | chef-f826880c267755005fccb93bdac6105a3a8e24b2.tar.gz |
Adding in execute support
Diffstat (limited to 'lib/chef')
-rw-r--r-- | lib/chef/exceptions.rb | 3 | ||||
-rw-r--r-- | lib/chef/mixin/command.rb | 204 | ||||
-rw-r--r-- | lib/chef/platform.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/execute.rb | 55 | ||||
-rw-r--r-- | lib/chef/resource/execute.rb | 133 |
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 |