class Pups::ExecCommand

Attributes

background[RW]
cd[R]
commands[R]
raise_on_fail[RW]
stdin[RW]
stop_signal[RW]

Public Class Methods

from_hash(hash, params) click to toggle source
# File lib/pups/exec_command.rb, line 44
def self.from_hash(hash, params)
  cmd = new(params, hash['cd'])

  case c = hash['cmd']
  when String then cmd.add(c)
  when Array then c.each { |i| cmd.add(i) }
  end

  cmd.background = hash['background']
  cmd.stop_signal = hash['stop_signal'] || 'TERM'
  cmd.raise_on_fail = hash['raise_on_fail'] if hash.key? 'raise_on_fail'
  cmd.stdin = interpolate_params(hash['stdin'], params)

  cmd
end
from_str(str, params) click to toggle source
# File lib/pups/exec_command.rb, line 60
def self.from_str(str, params)
  cmd = new(params)
  cmd.add(str)
  cmd
end
new(params, cd = nil) click to toggle source
# File lib/pups/exec_command.rb, line 66
def initialize(params, cd = nil)
  @commands = []
  @params = params
  @cd = interpolate_params(cd)
  @raise_on_fail = true
end
terminate_async(opts = {}) click to toggle source
# File lib/pups/exec_command.rb, line 11
def self.terminate_async(opts = {})
  return unless defined? @@asyncs

  Pups.log.info('Terminating async processes')

  @@asyncs.each do |async|
    Pups.log.info("Sending #{async[:stop_signal]} to #{async[:command]} pid: #{async[:pid]}")
    begin
      Process.kill(async[:stop_signal], async[:pid])
    rescue StandardError
      nil
    end
  end

  @@asyncs.map do |async|
    Thread.new do
      Timeout.timeout(opts[:wait] || 10) do
        Process.wait(async[:pid])
      rescue StandardError
        nil
      end
    rescue Timeout::Error
      Pups.log.info("#{async[:command]} pid:#{async[:pid]} did not terminate cleanly, forcing termination!")
      begin
        Process.kill('KILL', async[:pid])
        Process.wait(async[:pid])
      rescue Errno::ESRCH
      rescue Errno::ECHILD
      end
    end
  end.each(&:join)
end

Public Instance Methods

add(cmd) click to toggle source
# File lib/pups/exec_command.rb, line 73
def add(cmd)
  @commands << process_params(cmd)
end
process_params(cmd) click to toggle source
# File lib/pups/exec_command.rb, line 123
def process_params(cmd)
  processed = interpolate_params(cmd)
  @cd ? "cd #{cd} && #{processed}" : processed
end
run() click to toggle source
# File lib/pups/exec_command.rb, line 77
def run
  commands.each do |command|
    Pups.log.info("> #{command}")
    pid = spawn(command)
    Pups.log.info(@result.readlines.join("\n")) if @result
    pid
  end
rescue StandardError
  raise if @raise_on_fail
end
spawn(command) click to toggle source
# File lib/pups/exec_command.rb, line 88
def spawn(command)
  if background
    pid = Process.spawn(command)
    (@@asyncs ||= []) << { pid: pid, command: command, stop_signal: (stop_signal || 'TERM') }
    Thread.new do
      begin
        Process.wait(pid)
      rescue Errno::ECHILD
        # already exited so skip
      end
      @@asyncs.delete_if { |async| async[:pid] == pid }
    end
    return pid
  end

  IO.popen(command, 'w+') do |f|
    if stdin
      # need a way to get stdout without blocking
      Pups.log.info(stdin)
      f.write stdin
      f.close
    else
      Pups.log.info(f.readlines.join)
    end
  end

  unless $CHILD_STATUS == 0
    err = Pups::ExecError.new("#{command} failed with return #{$CHILD_STATUS.inspect}")
    err.exit_code = $CHILD_STATUS.exitstatus
    raise err
  end

  nil
end