process_bot 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +52 -42
- data/README.md +4 -1
- data/exe/process_bot +6 -0
- data/lib/process_bot/capistrano/sidekiq_helpers.rb +26 -8
- data/lib/process_bot/client_socket.rb +13 -3
- data/lib/process_bot/control_socket.rb +44 -10
- data/lib/process_bot/logger.rb +5 -1
- data/lib/process_bot/options.rb +10 -0
- data/lib/process_bot/process/handlers/sidekiq.rb +95 -3
- data/lib/process_bot/process/runner.rb +1 -1
- data/lib/process_bot/process.rb +15 -63
- data/lib/process_bot/version.rb +1 -1
- metadata +5 -6
- data/process_bot.gemspec +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ba4b3227493fa167bf23dfa8338ab83baf626a444eb91ff98d8d7ce40056cc2
|
4
|
+
data.tar.gz: 605f53e2980a549cb945470e6010877ce2b4eed5f5adef5dd7c80f2f46976332
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf82581d6d23036f2bbd5a91587f752b89d45e5c24a045193acd24911eb7ef5e427991baa7d0c6bb9299207c0c67595b4b645fb13743737e5771fde19063edba
|
7
|
+
data.tar.gz: 49f2aeca472a0a5957b86f70db38be1516faf8503ba04b70108e6ef8bf91b40b1bd2cb241339e79b447ba47290e47a25c81568cf516fbfa057a0745742f7e061
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
process_bot (0.1.
|
4
|
+
process_bot (0.1.5)
|
5
5
|
knjrbfw (>= 0.0.116)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
ast (2.4.
|
10
|
+
ast (2.4.3)
|
11
11
|
coderay (1.1.3)
|
12
12
|
datet (0.0.25)
|
13
|
-
diff-lcs (1.5.
|
13
|
+
diff-lcs (1.5.1)
|
14
14
|
http2 (0.0.36)
|
15
15
|
string-cases (~> 0)
|
16
|
-
json (2.
|
16
|
+
json (2.10.2)
|
17
17
|
knjrbfw (0.0.116)
|
18
18
|
datet
|
19
19
|
http2
|
@@ -21,56 +21,63 @@ GEM
|
|
21
21
|
ruby_process
|
22
22
|
tsafe
|
23
23
|
wref (>= 0.0.8)
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
language_server-protocol (3.17.0.4)
|
25
|
+
lint_roller (1.1.0)
|
26
|
+
method_source (1.1.0)
|
27
|
+
parallel (1.26.3)
|
28
|
+
parser (3.3.7.4)
|
27
29
|
ast (~> 2.4.1)
|
30
|
+
racc
|
28
31
|
php4r (0.0.4)
|
29
32
|
datet
|
30
33
|
http2
|
31
34
|
string-strtr
|
32
|
-
|
35
|
+
prism (1.4.0)
|
36
|
+
pry (0.15.2)
|
33
37
|
coderay (~> 1.1)
|
34
38
|
method_source (~> 1.0)
|
39
|
+
racc (1.8.1)
|
35
40
|
rainbow (3.1.1)
|
36
|
-
rake (13.
|
37
|
-
|
38
|
-
|
39
|
-
rspec (3.
|
40
|
-
rspec-core (~> 3.
|
41
|
-
rspec-expectations (~> 3.
|
42
|
-
rspec-mocks (~> 3.
|
43
|
-
rspec-core (3.
|
44
|
-
rspec-support (~> 3.
|
45
|
-
rspec-expectations (3.
|
41
|
+
rake (13.2.1)
|
42
|
+
ref (2.0.0)
|
43
|
+
regexp_parser (2.10.0)
|
44
|
+
rspec (3.13.0)
|
45
|
+
rspec-core (~> 3.13.0)
|
46
|
+
rspec-expectations (~> 3.13.0)
|
47
|
+
rspec-mocks (~> 3.13.0)
|
48
|
+
rspec-core (3.13.0)
|
49
|
+
rspec-support (~> 3.13.0)
|
50
|
+
rspec-expectations (3.13.0)
|
46
51
|
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
-
rspec-support (~> 3.
|
48
|
-
rspec-mocks (3.
|
52
|
+
rspec-support (~> 3.13.0)
|
53
|
+
rspec-mocks (3.13.0)
|
49
54
|
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
-
rspec-support (~> 3.
|
51
|
-
rspec-support (3.
|
52
|
-
rubocop (1.
|
55
|
+
rspec-support (~> 3.13.0)
|
56
|
+
rspec-support (3.13.0)
|
57
|
+
rubocop (1.75.2)
|
53
58
|
json (~> 2.3)
|
59
|
+
language_server-protocol (~> 3.17.0.2)
|
60
|
+
lint_roller (~> 1.1.0)
|
54
61
|
parallel (~> 1.10)
|
55
|
-
parser (>= 3.
|
62
|
+
parser (>= 3.3.0.2)
|
56
63
|
rainbow (>= 2.2.2, < 4.0)
|
57
|
-
regexp_parser (>=
|
58
|
-
|
59
|
-
rubocop-ast (>= 1.28.0, < 2.0)
|
64
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
65
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
60
66
|
ruby-progressbar (~> 1.7)
|
61
|
-
unicode-display_width (>= 2.4.0, <
|
62
|
-
rubocop-ast (1.
|
63
|
-
parser (>= 3.
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
rubocop (>= 1.
|
68
|
-
rubocop-ast (>=
|
69
|
-
rubocop-rake (0.
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
68
|
+
rubocop-ast (1.44.0)
|
69
|
+
parser (>= 3.3.7.2)
|
70
|
+
prism (~> 1.4)
|
71
|
+
rubocop-performance (1.25.0)
|
72
|
+
lint_roller (~> 1.1)
|
73
|
+
rubocop (>= 1.75.0, < 2.0)
|
74
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
75
|
+
rubocop-rake (0.7.1)
|
76
|
+
lint_roller (~> 1.1)
|
77
|
+
rubocop (>= 1.72.1)
|
78
|
+
rubocop-rspec (3.5.0)
|
79
|
+
lint_roller (~> 1.1)
|
80
|
+
rubocop (~> 1.72, >= 1.72.1)
|
74
81
|
ruby-progressbar (1.13.0)
|
75
82
|
ruby_process (0.0.13)
|
76
83
|
string-cases
|
@@ -79,8 +86,11 @@ GEM
|
|
79
86
|
string-cases (0.0.4)
|
80
87
|
string-strtr (0.0.3)
|
81
88
|
tsafe (0.0.12)
|
82
|
-
unicode-display_width (
|
83
|
-
|
89
|
+
unicode-display_width (3.1.4)
|
90
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
91
|
+
unicode-emoji (4.0.4)
|
92
|
+
wref (0.0.11)
|
93
|
+
ref
|
84
94
|
|
85
95
|
PLATFORMS
|
86
96
|
ruby
|
data/README.md
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
|
3
3
|
Run your app through ProcessBot for automatic restart if crashing, but still support normal deployment through Capistrano.
|
4
4
|
|
5
|
-
|
5
|
+
Watch memory usage for Sidekiq and restart gracefully if it exceeds a given limit (to counter memory leaks).
|
6
|
+
|
7
|
+
When deploying can gracefully exit Sidekiq to let long running jobs finish on old version of code, and start new Sidekiq processes after finishing deploy (but still let the old ones finish gracefully so nothing gets interrupted).
|
8
|
+
This requires not to remove columns, rename columns or any other intrusive database changes.
|
6
9
|
|
7
10
|
## Installation
|
8
11
|
|
data/exe/process_bot
CHANGED
@@ -32,16 +32,34 @@ module ProcessBot::Capistrano::SidekiqHelpers # rubocop:disable Metrics/ModuleLe
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def process_bot_command(process_bot_data, command)
|
35
|
+
def process_bot_command(process_bot_data, command) # rubocop:disable Metrics/AbcSize
|
36
36
|
raise "No port in process bot data? #{process_bot_data}" unless process_bot_data["port"]
|
37
37
|
|
38
|
-
|
39
|
-
"#{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec process_bot " \
|
40
|
-
"--command #{command} " \
|
41
|
-
"--port #{process_bot_data.fetch("port")}"
|
38
|
+
mode = "exec"
|
42
39
|
|
43
|
-
if
|
44
|
-
|
40
|
+
if mode == "runner"
|
41
|
+
args = {command: command, port: process_bot_data.fetch("port")}
|
42
|
+
|
43
|
+
if command == :graceful && !fetch(:process_bot_wait_for_gracefully_stopped).nil?
|
44
|
+
args["wait_for_gracefully_stopped"] = fetch(:process_bot_wait_for_gracefully_stopped)
|
45
|
+
end
|
46
|
+
|
47
|
+
escaped_args = JSON.generate(args).gsub("\"", "\\\"")
|
48
|
+
rails_runner_command = "require 'process_bot'; ProcessBot::Process.new(ProcessBot::Options.from_args(#{escaped_args})).execute!"
|
49
|
+
|
50
|
+
backend_command = "cd #{release_path} && " \
|
51
|
+
"#{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec rails runner \"#{rails_runner_command}\""
|
52
|
+
elsif mode == "exec"
|
53
|
+
backend_command = "cd #{release_path} && " \
|
54
|
+
"#{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec process_bot " \
|
55
|
+
"--command #{command} " \
|
56
|
+
"--port #{process_bot_data.fetch("port")}"
|
57
|
+
|
58
|
+
if command == :graceful && !fetch(:process_bot_wait_for_gracefully_stopped).nil?
|
59
|
+
backend_command << " --wait-for-gracefully-stopped #{fetch(:process_bot_wait_for_gracefully_stopped)}"
|
60
|
+
end
|
61
|
+
else
|
62
|
+
raise "Unknown mode: #{mode}"
|
45
63
|
end
|
46
64
|
|
47
65
|
backend.execute backend_command
|
@@ -126,7 +144,7 @@ module ProcessBot::Capistrano::SidekiqHelpers # rubocop:disable Metrics/ModuleLe
|
|
126
144
|
screen_args = ["-dmS process-bot--sidekiq--#{idx}-#{latest_release_version}"]
|
127
145
|
|
128
146
|
if (process_bot_sidekiq_log = fetch(:process_bot_sidekig_log))
|
129
|
-
screen_args << "-L -Logfile #{process_bot_sidekiq_log}"
|
147
|
+
screen_args << "-L -Logfile #{process_bot_sidekiq_log}_#{latest_release_version}_#{idx}.log"
|
130
148
|
elsif fetch(:sidekiq_log)
|
131
149
|
screen_args << "-L -Logfile #{fetch(:sidekiq_log)}"
|
132
150
|
end
|
@@ -8,14 +8,19 @@ class ProcessBot::ClientSocket
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def client
|
11
|
-
@client ||=
|
11
|
+
@client ||= Socket.tcp("localhost", options.fetch(:port).to_i, connect_timeout: 2)
|
12
12
|
end
|
13
13
|
|
14
14
|
def close
|
15
15
|
client.close
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def logger
|
19
|
+
@logger ||= ProcessBot::Logger.new(options: options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def send_command(data) # rubocop:disable Metrics/AbcSize
|
23
|
+
logger.log "Sending: #{data}"
|
19
24
|
client.puts(JSON.generate(data))
|
20
25
|
response_raw = client.gets
|
21
26
|
|
@@ -26,6 +31,11 @@ class ProcessBot::ClientSocket
|
|
26
31
|
|
27
32
|
return :success if response.fetch("type") == "success"
|
28
33
|
|
29
|
-
|
34
|
+
if response.fetch("type") == "error"
|
35
|
+
error = RuntimeError.new("Command raised an error: #{response.fetch("message")}")
|
36
|
+
error.set_backtrace(response.fetch("backtrace") + Thread.current.backtrace)
|
37
|
+
|
38
|
+
raise error
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
@@ -1,24 +1,36 @@
|
|
1
1
|
require "socket"
|
2
2
|
|
3
3
|
class ProcessBot::ControlSocket
|
4
|
-
attr_reader :options, :process, :server
|
4
|
+
attr_reader :options, :port, :process, :server
|
5
5
|
|
6
6
|
def initialize(options:, process:)
|
7
7
|
@options = options
|
8
8
|
@process = process
|
9
|
+
@port = options.fetch(:port).to_i
|
9
10
|
end
|
10
11
|
|
11
|
-
def
|
12
|
-
|
12
|
+
def logger
|
13
|
+
@logger ||= ProcessBot::Logger.new(options: options)
|
13
14
|
end
|
14
15
|
|
15
16
|
def start
|
16
|
-
|
17
|
+
start_tcp_server
|
17
18
|
run_client_loop
|
19
|
+
logger.logs "TCPServer started"
|
20
|
+
options.events.call(:on_socket_opened, port: @port)
|
21
|
+
end
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
def start_tcp_server
|
24
|
+
tries ||= 0
|
25
|
+
tries += 1
|
26
|
+
@server = TCPServer.new("localhost", @port)
|
27
|
+
rescue Errno::EADDRNOTAVAIL => e
|
28
|
+
if tries <= 100
|
29
|
+
@port += 1
|
30
|
+
retry
|
31
|
+
else
|
32
|
+
raise e
|
33
|
+
end
|
22
34
|
end
|
23
35
|
|
24
36
|
def stop
|
@@ -45,16 +57,38 @@ class ProcessBot::ControlSocket
|
|
45
57
|
|
46
58
|
if command_type == "graceful" || command_type == "stop"
|
47
59
|
begin
|
48
|
-
|
60
|
+
command_options = if command["options"]
|
61
|
+
symbolize_keys(command.fetch("options"))
|
62
|
+
else
|
63
|
+
{}
|
64
|
+
end
|
65
|
+
|
66
|
+
logger.logs "Command #{command_type} with options #{command_options}"
|
67
|
+
|
68
|
+
process.__send__(command_type, **command_options)
|
49
69
|
client.puts(JSON.generate(type: "success"))
|
50
70
|
rescue => e # rubocop:disable Style/RescueStandardError
|
51
|
-
|
71
|
+
logger.logs e.message, type: :stderr
|
72
|
+
logger.logs e.backtrace, type: :stderr
|
73
|
+
|
74
|
+
client.puts(JSON.generate(type: "error", message: e.message, backtrace: e.backtrace))
|
52
75
|
|
53
76
|
raise e
|
54
77
|
end
|
55
78
|
else
|
56
|
-
client.puts(JSON.generate(type: "error", message: "Unknown command: #{command_type}"))
|
79
|
+
client.puts(JSON.generate(type: "error", message: "Unknown command: #{command_type}", backtrace: Thread.current.backtrace))
|
57
80
|
end
|
58
81
|
end
|
59
82
|
end
|
83
|
+
|
84
|
+
def symbolize_keys(hash)
|
85
|
+
new_hash = {}
|
86
|
+
hash.each do |key, value|
|
87
|
+
next if key == "port"
|
88
|
+
|
89
|
+
new_hash[key.to_sym] = value
|
90
|
+
end
|
91
|
+
|
92
|
+
new_hash
|
93
|
+
end
|
60
94
|
end
|
data/lib/process_bot/logger.rb
CHANGED
@@ -6,7 +6,7 @@ class ProcessBot::Logger
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def log(output, type: :stdout)
|
9
|
-
if type == :stdout
|
9
|
+
if type == :stdout || (type == :debug && options[:debug])
|
10
10
|
$stdout.print output
|
11
11
|
elsif type == :stderr
|
12
12
|
$stderr.print output
|
@@ -20,6 +20,10 @@ class ProcessBot::Logger
|
|
20
20
|
fp_log.flush
|
21
21
|
end
|
22
22
|
|
23
|
+
def logs(output, **args)
|
24
|
+
log("#{output}\n", **args)
|
25
|
+
end
|
26
|
+
|
23
27
|
def log_file_path
|
24
28
|
options.fetch(:log_file_path)
|
25
29
|
end
|
data/lib/process_bot/options.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
class ProcessBot::Options
|
2
2
|
attr_reader :options
|
3
3
|
|
4
|
+
def self.from_args(args)
|
5
|
+
options = ProcessBot::Options.new
|
6
|
+
|
7
|
+
args.each do |key, value|
|
8
|
+
options.set(key, value)
|
9
|
+
end
|
10
|
+
|
11
|
+
options
|
12
|
+
end
|
13
|
+
|
4
14
|
def initialize(options = {})
|
5
15
|
@options = options
|
6
16
|
end
|
@@ -1,14 +1,38 @@
|
|
1
1
|
class ProcessBot::Process::Handlers::Sidekiq
|
2
|
-
attr_reader :options
|
2
|
+
attr_reader :options, :process
|
3
3
|
|
4
|
-
def initialize(
|
5
|
-
@
|
4
|
+
def initialize(process)
|
5
|
+
@process = process
|
6
|
+
@options = process.options
|
7
|
+
end
|
8
|
+
|
9
|
+
def current_pid
|
10
|
+
process.current_pid
|
11
|
+
end
|
12
|
+
|
13
|
+
def daemonize
|
14
|
+
logger.logs "Daemonize!"
|
15
|
+
|
16
|
+
pid = Process.fork do
|
17
|
+
Process.daemon
|
18
|
+
yield
|
19
|
+
end
|
20
|
+
|
21
|
+
Process.detach(pid) if pid
|
22
|
+
end
|
23
|
+
|
24
|
+
def false_value?(value)
|
25
|
+
!value || value == "false"
|
6
26
|
end
|
7
27
|
|
8
28
|
def fetch(*args, **opts)
|
9
29
|
options.fetch(*args, **opts)
|
10
30
|
end
|
11
31
|
|
32
|
+
def logger
|
33
|
+
@logger ||= ProcessBot::Logger.new(options: options)
|
34
|
+
end
|
35
|
+
|
12
36
|
def set_option(key, value)
|
13
37
|
raise "Unknown option for Sidekiq handler: #{key}" unless options.key?(key)
|
14
38
|
|
@@ -42,4 +66,72 @@ class ProcessBot::Process::Handlers::Sidekiq
|
|
42
66
|
command << "'"
|
43
67
|
command
|
44
68
|
end
|
69
|
+
|
70
|
+
def graceful(**args)
|
71
|
+
wait_for_gracefully_stopped = args.fetch(:wait_for_gracefully_stopped, true)
|
72
|
+
process.set_stopped
|
73
|
+
|
74
|
+
unless current_pid
|
75
|
+
warn "Sidekiq not running with a PID"
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
Process.kill("TSTP", current_pid)
|
80
|
+
|
81
|
+
if false_value?(wait_for_gracefully_stopped)
|
82
|
+
logger.logs "Dont wait for gracefully stopped - doing that in fork..."
|
83
|
+
|
84
|
+
daemonize do
|
85
|
+
wait_for_no_jobs_and_stop_sidekiq
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
else
|
89
|
+
logger.logs "Wait for gracefully stopped..."
|
90
|
+
wait_for_no_jobs_and_stop_sidekiq
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def stop(**_args)
|
95
|
+
process.set_stopped
|
96
|
+
|
97
|
+
unless current_pid
|
98
|
+
warn "#{handler_name} not running with a PID"
|
99
|
+
return
|
100
|
+
end
|
101
|
+
|
102
|
+
Process.kill("TERM", current_pid)
|
103
|
+
end
|
104
|
+
|
105
|
+
def wait_for_no_jobs # rubocop:disable Metrics/AbcSize
|
106
|
+
loop do
|
107
|
+
found_process = false
|
108
|
+
|
109
|
+
Knj::Unix_proc.list("grep" => current_pid) do |process|
|
110
|
+
process_command = process.data.fetch("cmd")
|
111
|
+
process_pid = process.data.fetch("pid").to_i
|
112
|
+
next unless process_pid == current_pid
|
113
|
+
|
114
|
+
found_process = true
|
115
|
+
sidekiq_regex = /\Asidekiq (\d+).(\d+).(\d+) (#{options.possible_process_titles_joined_regex}) \[(\d+) of (\d+)(\]|) (.+?)(\]|)\Z/
|
116
|
+
match = process_command.match(sidekiq_regex)
|
117
|
+
raise "Couldnt match Sidekiq command: #{process_command} with Sidekiq regex: #{sidekiq_regex}" unless match
|
118
|
+
|
119
|
+
running_jobs = match[5].to_i
|
120
|
+
|
121
|
+
logger.logs "running_jobs: #{running_jobs}"
|
122
|
+
|
123
|
+
return if running_jobs.zero? # rubocop:disable Lint/NonLocalExitFromIterator
|
124
|
+
end
|
125
|
+
|
126
|
+
raise "Couldn't find running process with PID #{current_pid}" unless found_process
|
127
|
+
|
128
|
+
sleep 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def wait_for_no_jobs_and_stop_sidekiq
|
133
|
+
logger.logs "Wait for no jobs and Stop sidekiq"
|
134
|
+
wait_for_no_jobs
|
135
|
+
stop
|
136
|
+
end
|
45
137
|
end
|
@@ -27,7 +27,7 @@ class ProcessBot::Process::Runner
|
|
27
27
|
|
28
28
|
PTY.spawn(command, err: stderr_writer.fileno) do |stdout, _stdin, pid|
|
29
29
|
@subprocess_pid = pid
|
30
|
-
logger.
|
30
|
+
logger.logs "Command running with PID #{pid}: #{command}"
|
31
31
|
|
32
32
|
stdout_reader_thread = Thread.new do
|
33
33
|
stdout.each_char do |chunk|
|
data/lib/process_bot/process.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
require "forwardable"
|
1
2
|
require "json"
|
3
|
+
require "string-cases"
|
2
4
|
|
3
5
|
class ProcessBot::Process
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegator :handler_instance, :graceful
|
9
|
+
def_delegator :handler_instance, :stop
|
10
|
+
|
4
11
|
autoload :Handlers, "#{__dir__}/process/handlers"
|
5
12
|
autoload :Runner, "#{__dir__}/process/runner"
|
6
13
|
|
@@ -13,7 +20,7 @@ class ProcessBot::Process
|
|
13
20
|
options.events.connect(:on_process_started, &method(:on_process_started)) # rubocop:disable Performance/MethodObjectAsBlock
|
14
21
|
options.events.connect(:on_socket_opened, &method(:on_socket_opened)) # rubocop:disable Performance/MethodObjectAsBlock
|
15
22
|
|
16
|
-
logger.
|
23
|
+
logger.logs("ProcessBot 1 - Options: #{options.options}")
|
17
24
|
end
|
18
25
|
|
19
26
|
def execute!
|
@@ -22,7 +29,7 @@ class ProcessBot::Process
|
|
22
29
|
if command == "start"
|
23
30
|
start
|
24
31
|
elsif command == "graceful" || command == "stop"
|
25
|
-
client.send_command(command: command)
|
32
|
+
client.send_command(command: command, options: options.options)
|
26
33
|
else
|
27
34
|
raise "Unknown command: #{command}"
|
28
35
|
end
|
@@ -39,6 +46,10 @@ class ProcessBot::Process
|
|
39
46
|
end
|
40
47
|
end
|
41
48
|
|
49
|
+
def handler_instance
|
50
|
+
@handler_instance ||= handler_class.new(self)
|
51
|
+
end
|
52
|
+
|
42
53
|
def handler_name
|
43
54
|
@handler_name ||= options.fetch(:handler)
|
44
55
|
end
|
@@ -71,42 +82,17 @@ class ProcessBot::Process
|
|
71
82
|
if stopped
|
72
83
|
break
|
73
84
|
else
|
74
|
-
|
85
|
+
logger.logs "Process stopped - starting again after 1 sec"
|
75
86
|
sleep 1
|
76
87
|
end
|
77
88
|
end
|
78
89
|
end
|
79
90
|
|
80
|
-
def
|
81
|
-
@stopped = true
|
82
|
-
|
83
|
-
unless current_pid
|
84
|
-
warn "#{handler_name} not running with a PID"
|
85
|
-
return
|
86
|
-
end
|
87
|
-
|
88
|
-
Process.kill("TSTP", current_pid)
|
89
|
-
|
90
|
-
if options[:wait_for_gracefully_stopped] == "false"
|
91
|
-
Thread.new { wait_for_no_jobs_and_stop_sidekiq }
|
92
|
-
else
|
93
|
-
wait_for_no_jobs_and_stop_sidekiq
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def stop
|
91
|
+
def set_stopped
|
98
92
|
@stopped = true
|
99
|
-
|
100
|
-
unless current_pid
|
101
|
-
warn "#{handler_name} not running with a PID"
|
102
|
-
return
|
103
|
-
end
|
104
|
-
|
105
|
-
Process.kill("TERM", current_pid)
|
106
93
|
end
|
107
94
|
|
108
95
|
def run
|
109
|
-
handler_instance = handler_class.new(options)
|
110
96
|
runner = ProcessBot::Process::Runner.new(command: handler_instance.start_command, logger: logger, options: options)
|
111
97
|
runner.run
|
112
98
|
end
|
@@ -116,38 +102,4 @@ class ProcessBot::Process
|
|
116
102
|
@current_process_title = "ProcessBot #{JSON.generate(process_args)}"
|
117
103
|
Process.setproctitle(current_process_title)
|
118
104
|
end
|
119
|
-
|
120
|
-
def wait_for_no_jobs # rubocop:disable Metrics/AbcSize
|
121
|
-
loop do
|
122
|
-
found_process = false
|
123
|
-
|
124
|
-
Knj::Unix_proc.list("grep" => current_pid) do |process|
|
125
|
-
process_command = process.data.fetch("cmd")
|
126
|
-
process_pid = process.data.fetch("pid").to_i
|
127
|
-
next unless process_pid == current_pid
|
128
|
-
|
129
|
-
found_process = true
|
130
|
-
sidekiq_regex = /\Asidekiq (\d+).(\d+).(\d+) (#{options.possible_process_titles_joined_regex}) \[(\d+) of (\d+)(\]|) (.+?)(\]|)\Z/
|
131
|
-
match = process_command.match(sidekiq_regex)
|
132
|
-
raise "Couldnt match Sidekiq command: #{process_command} with Sidekiq regex: #{sidekiq_regex}" unless match
|
133
|
-
|
134
|
-
running_jobs = match[5].to_i
|
135
|
-
|
136
|
-
puts "running_jobs: #{running_jobs}"
|
137
|
-
|
138
|
-
return if running_jobs.zero? # rubocop:disable Lint/NonLocalExitFromIterator
|
139
|
-
end
|
140
|
-
|
141
|
-
raise "Couldn't find running process with PID #{current_pid}" unless found_process
|
142
|
-
|
143
|
-
sleep 1
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def wait_for_no_jobs_and_stop_sidekiq
|
148
|
-
puts "Wait for no jobs and Stop sidekiq"
|
149
|
-
|
150
|
-
wait_for_no_jobs
|
151
|
-
stop
|
152
|
-
end
|
153
105
|
end
|
data/lib/process_bot/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: process_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kaspernj
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: knjrbfw
|
@@ -60,7 +60,6 @@ files:
|
|
60
60
|
- lib/process_bot/process/runner.rb
|
61
61
|
- lib/process_bot/version.rb
|
62
62
|
- peak_flow.yml
|
63
|
-
- process_bot.gemspec
|
64
63
|
- sig/process_bot.rbs
|
65
64
|
homepage: https://github.com/kaspernj/process_bot
|
66
65
|
licenses:
|
@@ -71,7 +70,7 @@ metadata:
|
|
71
70
|
source_code_uri: https://github.com/kaspernj/process_bot
|
72
71
|
changelog_uri: https://github.com/kaspernj/process_bot/blob/master/CHANGELOG.md
|
73
72
|
rubygems_mfa_required: 'true'
|
74
|
-
post_install_message:
|
73
|
+
post_install_message:
|
75
74
|
rdoc_options: []
|
76
75
|
require_paths:
|
77
76
|
- lib
|
@@ -87,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
86
|
version: '0'
|
88
87
|
requirements: []
|
89
88
|
rubygems_version: 3.1.6
|
90
|
-
signing_key:
|
89
|
+
signing_key:
|
91
90
|
specification_version: 4
|
92
91
|
summary: Run and control processes.
|
93
92
|
test_files: []
|
data/process_bot.gemspec
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "lib/process_bot/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "process_bot"
|
7
|
-
spec.version = ProcessBot::VERSION
|
8
|
-
spec.authors = ["kaspernj"]
|
9
|
-
spec.email = ["k@spernj.org"]
|
10
|
-
|
11
|
-
spec.summary = "Run and control processes."
|
12
|
-
spec.description = "Run and control processes."
|
13
|
-
spec.homepage = "https://github.com/kaspernj/process_bot"
|
14
|
-
spec.license = "MIT"
|
15
|
-
spec.required_ruby_version = ">= 2.7.0"
|
16
|
-
|
17
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
|
-
|
19
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
-
spec.metadata["source_code_uri"] = "https://github.com/kaspernj/process_bot"
|
21
|
-
spec.metadata["changelog_uri"] = "https://github.com/kaspernj/process_bot/blob/master/CHANGELOG.md"
|
22
|
-
|
23
|
-
# Specify which files should be added to the gem when it is released.
|
24
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
-
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
28
|
-
end
|
29
|
-
end
|
30
|
-
spec.bindir = "exe"
|
31
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
-
spec.require_paths = ["lib"]
|
33
|
-
|
34
|
-
spec.add_dependency "knjrbfw", ">= 0.0.116"
|
35
|
-
|
36
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
37
|
-
end
|