Class: ProcessBot::Process::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/process_bot/process/runner.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command:, handler_instance:, handler_name:, logger:, options:) ⇒ Runner

Returns a new instance of Runner.



6
7
8
9
10
11
12
13
# File 'lib/process_bot/process/runner.rb', line 6

def initialize(command:, handler_instance:, handler_name:, logger:, options:)
  @command = command
  @handler_instance = handler_instance
  @handler_name = handler_name
  @logger = logger
  @monitor = Monitor.new
  @options = options
end

Instance Attribute Details

#commandObject (readonly)

Returns the value of attribute command.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def command
  @command
end

#exit_statusObject (readonly)

Returns the value of attribute exit_status.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def exit_status
  @exit_status
end

#handler_instanceObject (readonly)

Returns the value of attribute handler_instance.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def handler_instance
  @handler_instance
end

#handler_nameObject (readonly)

Returns the value of attribute handler_name.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def handler_name
  @handler_name
end

#loggerObject (readonly)

Returns the value of attribute logger.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def logger
  @logger
end

#monitorObject (readonly)

Returns the value of attribute monitor.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def monitor
  @monitor
end

#optionsObject (readonly)

Returns the value of attribute options.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def options
  @options
end

#pidObject (readonly)

Returns the value of attribute pid.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def pid
  @pid
end

#stop_timeObject (readonly)

Returns the value of attribute stop_time.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def stop_time
  @stop_time
end

#subprocess_pidObject (readonly)

Returns the value of attribute subprocess_pid.



4
5
6
# File 'lib/process_bot/process/runner.rb', line 4

def subprocess_pid
  @subprocess_pid
end

Instance Method Details

#find_sidekiq_pidObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/process_bot/process/runner.rb', line 131

def find_sidekiq_pid
  Thread.new do
    while running? && !pid
      related_sidekiq_processes.each do |related_sidekiq_process| # rubocop:disable Lint/UnreachableLoop
        puts "FOUND PID: #{related_sidekiq_process.pid}"
        @pid = related_sidekiq_process.pid
        options.events.call(:on_process_started, pid: related_sidekiq_process.pid)

        break
      end

      unless pid
        puts "Waiting 1 second before trying to find Sidekiq PID again"
        sleep 1
      end
    end
  end
end

#output(output:, type:) ⇒ Object



15
16
17
# File 'lib/process_bot/process/runner.rb', line 15

def output(output:, type:)
  logger.log(output, type: type)
end


75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/process_bot/process/runner.rb', line 75

def related_processes
  related_processes = []

  Knj::Unix_proc.list do |process|
    begin
      process_pgid = Process.getpgid(process.pid)
    rescue Errno::ESRCH
      # Process no longer running
    end

    related_processes << process if subprocess_pgid == process_pgid
  end

  related_processes
end


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/process_bot/process/runner.rb', line 91

def related_sidekiq_processes
  related_sidekiq_processes = []

  Knj::Unix_proc.list("grep" => "sidekiq") do |process|
    cmd = process.data.fetch("cmd")

    if /sidekiq ([0-9]+\.[0-9]+\.[0-9]+) (#{options.possible_process_titles_joined_regex})/.match?(cmd)
      sidekiq_pid = process.data.fetch("pid").to_i

      begin
        sidekiq_pgid = Process.getpgid(sidekiq_pid)
      rescue Errno::ESRCH
        # Process no longer running
      end

      related_sidekiq_processes << process if subprocess_pgid == sidekiq_pgid
    end
  end

  related_sidekiq_processes
end

#runObject

rubocop:disable Metrics/AbcSize



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/process_bot/process/runner.rb', line 23

def run # rubocop:disable Metrics/AbcSize
  @start_time = Time.new
  stderr_reader, stderr_writer = IO.pipe

  require "pty"

  PTY.spawn(command, err: stderr_writer.fileno) do |stdout, _stdin, pid|
    @subprocess_pid = pid
    logger.logs "Command running with PID #{pid}: #{command}"

    stdout_reader_thread = Thread.new do
      stdout.each_char do |chunk|
        monitor.synchronize do
          output(type: :stdout, output: chunk)
        end
      end
    rescue Errno::EIO
      # Process done
    ensure
      status = Process::Status.wait(subprocess_pid, 0)

      @exit_status = status.exitstatus
      stderr_writer.close
    end

    stderr_reader_thread = Thread.new do
      stderr_reader.each_char do |chunk|
        monitor.synchronize do
          output(type: :stderr, output: chunk)
        end
      end
    end

    find_sidekiq_pid if handler_name == "sidekiq"

    stdout_reader_thread.join
    stderr_reader_thread.join

    logger.logs "Process stopped"

    @stop_time = Time.new
  end
end

#running?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/process_bot/process/runner.rb', line 19

def running?
  !stop_time
end

#sidekiq_app_nameObject



71
72
73
# File 'lib/process_bot/process/runner.rb', line 71

def sidekiq_app_name
  options.fetch(:application)
end


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/process_bot/process/runner.rb', line 113

def stop_related_processes
  loop do
    processes = related_processes

    if processes.length <= 0
      logger.logs "No related processes could be found"
      break
    else
      processes.each do |process|
        logger.logs "Terminating PID #{process.pid}: #{process.data.fetch("cmd")}"
        Process.kill("TERM", process.pid)
      end

      sleep 0.5
    end
  end
end

#subprocess_pgidObject



67
68
69
# File 'lib/process_bot/process/runner.rb', line 67

def subprocess_pgid
  @subprocess_pgid ||= Process.getpgid(subprocess_pid)
end