Class: Chutzen::Command

Inherits:
Object
  • Object
show all
Defined in:
lib/chutzen/command.rb,
lib/chutzen/command/execution_failed.rb

Overview

Holds a description for a command and executes it.

Defined Under Namespace

Classes: ExecutionFailed

Constant Summary collapse

ALLOW_EVAL =
/\A[\d<>=\s]+\z/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(description, dictionary:, work_path:) ⇒ Command

Creates a new command with a description hash, dictionary, and the current work path.

Command.new(
  { 'execute' => 'ls -al' },
  dictionary: dictionary,
  work_path: Dir.pwd
)


28
29
30
31
32
33
34
35
36
37
# File 'lib/chutzen/command.rb', line 28

def initialize(description, dictionary:, work_path:)
  defaults
  description.each do |name, value|
    instance_variable_set("@#{name}", value)
  end
  @dictionary = dictionary
  @work_path = work_path
  @stdout = StringIO.new
  @stderr = StringIO.new
end

Instance Attribute Details

#exit_statusObject (readonly)

Returns a Process::Status object after the command has run.



18
19
20
# File 'lib/chutzen/command.rb', line 18

def exit_status
  @exit_status
end

#stderrObject (readonly)

Returns a StringIO object with all data the command wrote to stderr.



16
17
18
# File 'lib/chutzen/command.rb', line 16

def stderr
  @stderr
end

#stdoutObject (readonly)

Returns a StringIO object with all data the command wrote to stdout.



14
15
16
# File 'lib/chutzen/command.rb', line 14

def stdout
  @stdout
end

Instance Method Details

#nameObject

Attempts to return the name of the binary that is being executed.



52
53
54
# File 'lib/chutzen/command.rb', line 52

def name
  to_s.split(' ')[0].split('/').last
end

#optional?Boolean

Return true when the command should be performed but does not need to exit successfully.

Returns:

  • (Boolean)


41
42
43
# File 'lib/chutzen/command.rb', line 41

def optional?
  @optional
end

#performObject

Runs the job based on its description. Returns the job itself for convenience.



58
59
60
61
62
63
64
# File 'lib/chutzen/command.rb', line 58

def perform
  return if skip?

  result = @execute ? perform_command : self
  merge_remember_into_dictionary
  result
end

#perform_commandObject



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/chutzen/command.rb', line 66

def perform_command
  trace_execution do
    # We use the STOP signal to instruct Sidekiq to stop reading from the queue and the TERM
    # signal to kill and re-queue running jobs. Unfortunately the command opened by popen would
    # also receive the signal and stop or terminate the command. When stopped the command would
    # never complete and when killed it would probably fail the job.
    #
    # Chutzen can't trap the signals because it runs in the same process. We don't want to wrap
    # the command in a runner because that's even more inconvenient.
    #
    # As a tradeoff we start the command in its own process group. Upside is that it ignores the
    # signals of its parent process. Downside is that it may orphan the process when Sidekiq
    # stops.
    Open3.popen3(to_s, chdir: @work_path, pgroup: true) do |stdin, stdout, stderr, thread|
      stdin.close_write
      monitor(stdout, stderr, thread)
      @exit_status = thread.value
    end
    verify_exit_status
    merge_output_into_dictionary
    self
  end
end

#perform_whenObject

Returns the value expressed by the perform_when section but with its variables instantiated.



110
111
112
113
114
# File 'lib/chutzen/command.rb', line 110

def perform_when
  return nil unless @perform_when

  Expression.new(Template.new(@perform_when, @dictionary).result, @dictionary)
end

#resultObject

Returns the result expressed by the result section but with its variables instantiated.



92
93
94
95
96
97
98
# File 'lib/chutzen/command.rb', line 92

def result
  return nil unless @result

  result = Dictionary.new
  Apply.new(result, @dictionary).merge(@result)
  result.to_hash
end

#skip_whenObject

Returns the value expressed by the skip_when section but with its variables instantiated.



102
103
104
105
106
# File 'lib/chutzen/command.rb', line 102

def skip_when
  return nil unless @skip_when

  Expression.new(Template.new(@skip_when, @dictionary).result, @dictionary)
end

#to_sObject

Joins all the arguments from the execute sections to build a command that can be executed by a shell.



47
48
49
# File 'lib/chutzen/command.rb', line 47

def to_s
  Template.new(Shell.join(@execute), @dictionary).result
end