Class: Rundoc::CodeCommand::Background::ProcessSpawn
- Inherits:
-
Object
- Object
- Rundoc::CodeCommand::Background::ProcessSpawn
- Defined in:
- lib/rundoc/code_command/background/process_spawn.rb
Overview
This class is responsible for running processes in the background
By default it logs output to a file. This can be used to “wait” for a specific output before continuing:
server = ProcessSpawn("rails server")
server.wait("Use Ctrl-C to stop")
The process can be queried for it’s status to check if it is still booted or not. the process can also be manually stopped:
server = ProcessSpawn("rails server")
server.alive? # => true
server.stop
server.alive? # => false
There are class level methods that can be used to “name” and record background processes. They can be used like this:
server = ProcessSpawn("rails server")
ProcessSpawn.add("muh_server", server)
ProcessSpawn.find("muh_server") # => <# ProcessSpawn instance >
ProcessSpawn.find("foo") # => RuntimeError "Could not find task with name 'foo', ..."
Class Attribute Summary collapse
-
.tasks ⇒ Object
readonly
Returns the value of attribute tasks.
Instance Attribute Summary collapse
-
#command ⇒ Object
readonly
Returns the value of attribute command.
-
#log ⇒ Object
readonly
Returns the value of attribute log.
-
#pid ⇒ Object
readonly
Returns the value of attribute pid.
Class Method Summary collapse
Instance Method Summary collapse
- #alive? ⇒ Boolean
- #check_alive! ⇒ Object
-
#initialize(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1") ⇒ ProcessSpawn
constructor
A new instance of ProcessSpawn.
-
#stdin_write(contents, ending: $/, timeout: timeout_value, wait: nil) ⇒ Object
Writes the contents along with an optional ending character to the STDIN of the backtround process.
- #stop ⇒ Object
-
#wait(wait_value = nil, timeout_value = @timeout_value, file: @log) ⇒ Object
Wait until a given string is found in the logs.
Constructor Details
#initialize(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1") ⇒ ProcessSpawn
Returns a new instance of ProcessSpawn.
47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 47 def initialize(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1") @command = command @timeout_value = timeout @log_reference = log # https://twitter.com/schneems/status/1285289971083907075 @log = Pathname.new(log) @log.dirname.mkpath FileUtils.touch(@log) @pipe_output, @pipe_input = IO.pipe @command = "/usr/bin/env bash -c #{@command.shellescape} >> #{@log} #{out}" @pid = nil end |
Class Attribute Details
.tasks ⇒ Object (readonly)
Returns the value of attribute tasks.
31 32 33 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 31 def tasks @tasks end |
Instance Attribute Details
#command ⇒ Object (readonly)
Returns the value of attribute command.
45 46 47 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 45 def command @command end |
#log ⇒ Object (readonly)
Returns the value of attribute log.
45 46 47 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 45 def log @log end |
#pid ⇒ Object (readonly)
Returns the value of attribute pid.
45 46 47 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 45 def pid @pid end |
Class Method Details
.add(name, value) ⇒ Object
35 36 37 38 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 35 def self.add(name, value) raise "Task named #{name.inspect} is already started, choose a different name" if @tasks[name] @tasks[name] = value end |
.find(name) ⇒ Object
40 41 42 43 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 40 def self.find(name) raise "Could not find task with name #{name.inspect}, known task names: #{@tasks.keys.inspect}" unless @tasks[name] @tasks[name] end |
Instance Method Details
#alive? ⇒ Boolean
87 88 89 90 91 92 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 87 def alive? return false unless @pid Process.kill(0, @pid) rescue Errno::ESRCH, Errno::EPERM false end |
#check_alive! ⇒ Object
132 133 134 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 132 def check_alive! raise "#{@original_command} has exited unexpectedly: #{@log.read}" unless alive? end |
#stdin_write(contents, ending: $/, timeout: timeout_value, wait: nil) ⇒ Object
Writes the contents along with an optional ending character to the STDIN of the backtround process
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 105 def stdin_write(contents, ending: $/, timeout: timeout_value, wait: nil) log_file = File.new(@log) before_write_bytes = log_file.size begin Timeout.timeout(Integer(timeout)) do @pipe_input.print(contents + ending) @pipe_input.flush end rescue Timeout::Error raise "Timeout (#{timeout}s) waiting to write #{contents} to stdin. Log contents:\n'#{log.read}'" end # Ignore bytes written before we sent the STDIN message log_file.seek(before_write_bytes) wait(wait, timeout, file: log_file) contents end |
#stop ⇒ Object
123 124 125 126 127 128 129 130 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 123 def stop return unless alive? @pipe_input.close Process.kill("TERM", -Process.getpgid(@pid)) Process.wait(@pid) rescue Errno::ESRCH => e puts "Error stopping process (command: #{command}): #{e}" end |
#wait(wait_value = nil, timeout_value = @timeout_value, file: @log) ⇒ Object
Wait until a given string is found in the logs
If the string is not found within the timeout, a Timeout::Error is raised
Caution: The logs will not be cleared before waiting, so if the string is already present from a prior operation, then it will not wait at all.
To ensure you’re waiting for a brand new string, call ‘log.truncate(0)` first. which is accessible via `:::– background.log.clear` in rundoc syntax.
74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 74 def wait(wait_value = nil, timeout_value = @timeout_value, file: @log) call return unless wait_value Timeout.timeout(Integer(timeout_value)) do until file.read.include?(wait_value) sleep 0.01 end end rescue Timeout::Error raise "Timeout (#{timeout_value}s) waiting for #{@command.inspect} to find a match using #{wait_value.inspect} in \n'#{log.read}'" end |