module OpenShift::Utils

Public Class Methods

oo_spawn(command, options = {}) click to toggle source

::oo_spawn(command, [, options]) -> [stdout, stderr, exit status]

spawn executes specified command and return its stdout, stderr and exit status. Or, raise exceptions if certain conditions are not met.

command: command line string which is passed to the standard shell

options: hash

:env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
:unsetenv_others => true   : clear environment variables except specified by :env
:chdir => path             : set current directory when running command
:expected_exitstatus       : An Integer value for the expected return code of command
                           : If not set spawn() returns exitstatus from command otherwise
                           : raise an error if exitstatus is not expected_exitstatus
:timeout                   : Maximum number of seconds to wait for command to finish. default: 3600
:uid                       : spawn command as given user in a SELinux context using runuser/runcon,
                           : stdin for the command is %rdev/ull
# File lib/openshift-origin-node/utils/shell_exec.rb, line 72
def self.oo_spawn(command, options = {})
  options[:env]         ||= {}
  options[:timeout]     ||= 3600
  options[:buffer_size] ||= 32768

  opts                   = {}
  opts[:unsetenv_others] = (options[:unsetenv_others] ||= false)
  opts[:close_others]    = true
  opts[:chdir] = options[:chdir] unless options[:chdir].nil?

  IO.pipe do |read_stderr, write_stderr|
    IO.pipe do |read_stdout, write_stdout|
      opts[:out] = write_stdout
      opts[:err] = write_stderr

      if options[:uid]
        # lazy init otherwise we end up with a cyclic require...
        require 'openshift-origin-node/model/unix_user'

        opts[:in] = '/dev/null'
        context   = %Q{unconfined_u:system_r:openshift_t:#{UnixUser.get_mcs_label(options[:uid])}}
        name      = Etc.getpwuid(options[:uid]).name
        command   = %Q{/sbin/runuser -m -s /bin/sh #{name} -c "exec /usr/bin/runcon '#{context}' /bin/sh -c \\"#{command}\\""}
      end

      NodeLogger.trace_logger.debug { "oo_spawn running #{command}" }
      pid = Kernel.spawn(options[:env], command, opts)

      unless pid
        raise OpenShift::Utils::ShellExecutionException.new(
                  "Kernel.spawn failed for command '#{command}'")
      end

      begin
        write_stdout.close
        write_stderr.close

        out, err, status = read_results(pid, read_stdout, read_stderr, options)
        NodeLogger.logger.debug { "Shell command '#{command}' ran. rc=#{status.exitstatus}" }

        if (!options[:expected_exitstatus].nil?) && (status.exitstatus != options[:expected_exitstatus])
          raise OpenShift::Utils::ShellExecutionException.new(
                    "Shell command '#{command}' returned an error. rc=#{status.exitstatus}",
                    status.exitstatus, out, err)
        end

        return [out, err, status.exitstatus]
      rescue TimeoutExceeded => e
        ShellExec.kill_process_tree(pid)
        raise OpenShift::Utils::ShellExecutionException.new(
                  "Shell command '#{command}'' exceeded timeout of #{e.seconds}", -1, out, err)
      end
    end
  end
end