itrg-invoker 1.6.0
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 +7 -0
- data/bin/invoker +7 -0
- data/lib/invoker/cli/pinger.rb +23 -0
- data/lib/invoker/cli/question.rb +15 -0
- data/lib/invoker/cli/tail.rb +34 -0
- data/lib/invoker/cli/tail_watcher.rb +34 -0
- data/lib/invoker/cli.rb +197 -0
- data/lib/invoker/command_worker.rb +64 -0
- data/lib/invoker/commander.rb +101 -0
- data/lib/invoker/daemon.rb +126 -0
- data/lib/invoker/dns_cache.rb +23 -0
- data/lib/invoker/errors.rb +17 -0
- data/lib/invoker/event/manager.rb +79 -0
- data/lib/invoker/ipc/add_command.rb +12 -0
- data/lib/invoker/ipc/add_http_command.rb +10 -0
- data/lib/invoker/ipc/base_command.rb +24 -0
- data/lib/invoker/ipc/client_handler.rb +26 -0
- data/lib/invoker/ipc/dns_check_command.rb +17 -0
- data/lib/invoker/ipc/list_command.rb +11 -0
- data/lib/invoker/ipc/message/list_response.rb +35 -0
- data/lib/invoker/ipc/message/tail_response.rb +10 -0
- data/lib/invoker/ipc/message.rb +170 -0
- data/lib/invoker/ipc/ping_command.rb +10 -0
- data/lib/invoker/ipc/reload_command.rb +12 -0
- data/lib/invoker/ipc/remove_command.rb +12 -0
- data/lib/invoker/ipc/server.rb +26 -0
- data/lib/invoker/ipc/tail_command.rb +11 -0
- data/lib/invoker/ipc/unix_client.rb +60 -0
- data/lib/invoker/ipc.rb +45 -0
- data/lib/invoker/logger.rb +13 -0
- data/lib/invoker/parsers/config.rb +184 -0
- data/lib/invoker/parsers/procfile.rb +86 -0
- data/lib/invoker/power/balancer.rb +133 -0
- data/lib/invoker/power/config.rb +77 -0
- data/lib/invoker/power/dns.rb +38 -0
- data/lib/invoker/power/http_parser.rb +68 -0
- data/lib/invoker/power/http_response.rb +81 -0
- data/lib/invoker/power/port_finder.rb +49 -0
- data/lib/invoker/power/power.rb +3 -0
- data/lib/invoker/power/powerup.rb +29 -0
- data/lib/invoker/power/setup/distro/arch.rb +15 -0
- data/lib/invoker/power/setup/distro/base.rb +80 -0
- data/lib/invoker/power/setup/distro/debian.rb +11 -0
- data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
- data/lib/invoker/power/setup/distro/redhat.rb +11 -0
- data/lib/invoker/power/setup/distro/ubuntu.rb +46 -0
- data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
- data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
- data/lib/invoker/power/setup/linux_setup.rb +97 -0
- data/lib/invoker/power/setup/osx_setup.rb +137 -0
- data/lib/invoker/power/setup.rb +93 -0
- data/lib/invoker/power/templates/400.html +40 -0
- data/lib/invoker/power/templates/404.html +40 -0
- data/lib/invoker/power/templates/503.html +40 -0
- data/lib/invoker/power/url_rewriter.rb +40 -0
- data/lib/invoker/process_manager.rb +201 -0
- data/lib/invoker/process_printer.rb +59 -0
- data/lib/invoker/reactor/reader.rb +65 -0
- data/lib/invoker/reactor.rb +37 -0
- data/lib/invoker/version.rb +47 -0
- data/lib/invoker.rb +151 -0
- data/spec/invoker/cli/pinger_spec.rb +22 -0
- data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
- data/spec/invoker/cli_spec.rb +27 -0
- data/spec/invoker/command_worker_spec.rb +45 -0
- data/spec/invoker/commander_spec.rb +152 -0
- data/spec/invoker/config_spec.rb +361 -0
- data/spec/invoker/daemon_spec.rb +34 -0
- data/spec/invoker/event/manager_spec.rb +67 -0
- data/spec/invoker/invoker_spec.rb +71 -0
- data/spec/invoker/ipc/client_handler_spec.rb +54 -0
- data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
- data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
- data/spec/invoker/ipc/message_spec.rb +49 -0
- data/spec/invoker/ipc/unix_client_spec.rb +29 -0
- data/spec/invoker/power/balancer_spec.rb +53 -0
- data/spec/invoker/power/config_spec.rb +18 -0
- data/spec/invoker/power/http_parser_spec.rb +32 -0
- data/spec/invoker/power/http_response_spec.rb +34 -0
- data/spec/invoker/power/port_finder_spec.rb +16 -0
- data/spec/invoker/power/setup/linux_setup_spec.rb +166 -0
- data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
- data/spec/invoker/power/setup_spec.rb +4 -0
- data/spec/invoker/power/url_rewriter_spec.rb +69 -0
- data/spec/invoker/power/web_sockets_spec.rb +61 -0
- data/spec/invoker/process_manager_spec.rb +130 -0
- data/spec/invoker/reactor_spec.rb +6 -0
- data/spec/spec_helper.rb +43 -0
- metadata +376 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e3d5b0bdacd47a5980cbcda55ef7834f36dd5dd9865b0449629407fd73a781ff
|
4
|
+
data.tar.gz: 82fd4361283d887768faa4b2c3595cf3ce13286fc11bfc2f7ad5e74843992766
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 85741205e13f285db961c8ddaa02d9fea88aa98027a2a5a518683a56234209bfa850fd7a896457d14a0e2cc33d1b3ccd1c148eaf6318fc0d1b48cffa17bdb609
|
7
|
+
data.tar.gz: ce60237af156b87c220e86a8efe002127652f4b2417d549f370c526df1cb45d367ad71278038f27e3231a64007f75f19ae71b9a2dcc466f9ac0bf5aabb9b1367
|
data/bin/invoker
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "timeout"
|
2
|
+
|
3
|
+
module Invoker
|
4
|
+
class CLI::Pinger
|
5
|
+
attr_accessor :unix_client
|
6
|
+
def initialize(unix_client)
|
7
|
+
@unix_client = unix_client
|
8
|
+
end
|
9
|
+
|
10
|
+
def invoker_running?
|
11
|
+
response = send_ping_and_read_response
|
12
|
+
response && response.status == 'pong'
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def send_ping_and_read_response
|
18
|
+
Timeout.timeout(2) { unix_client.send_and_receive('ping') }
|
19
|
+
rescue Timeout::Error
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Invoker
|
2
|
+
class CLI::Question
|
3
|
+
def self.agree(question_text)
|
4
|
+
$stdout.print(question_text)
|
5
|
+
answer = $stdin.gets
|
6
|
+
answer.strip!
|
7
|
+
if answer =~ /\Ay(?:es)?|no?\Z/i
|
8
|
+
answer =~ /\Ay(?:es)?\Z/i
|
9
|
+
else
|
10
|
+
$stdout.puts "Please enter 'yes' or 'no'."
|
11
|
+
agree(question_text)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Invoker
|
2
|
+
class CLI::Tail
|
3
|
+
attr_accessor :process_names
|
4
|
+
def initialize(process_names)
|
5
|
+
verify_process_name(process_names)
|
6
|
+
@process_names = process_names
|
7
|
+
@unix_socket = Invoker::IPC::UnixClient.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
socket = @unix_socket.send_and_wait('tail', process_names: process_names)
|
12
|
+
trap('INT') { socket.close }
|
13
|
+
loop do
|
14
|
+
message = read_next_line(socket)
|
15
|
+
break unless message
|
16
|
+
puts message.tail_line
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def verify_process_name(process_names)
|
23
|
+
if process_names.empty?
|
24
|
+
abort("Tail command requires one or more process name")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_next_line(socket)
|
29
|
+
Invoker::IPC.message_from_io(socket)
|
30
|
+
rescue
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Invoker
|
2
|
+
# This class defines sockets which are open for watching log files
|
3
|
+
class CLI::TailWatcher
|
4
|
+
attr_accessor :tail_watchers
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@tail_mutex = Mutex.new
|
8
|
+
@tail_watchers = Hash.new { |h, k| h[k] = [] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](process_name)
|
12
|
+
@tail_mutex.synchronize { tail_watchers[process_name] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(names, socket)
|
16
|
+
@tail_mutex.synchronize do
|
17
|
+
names.each { |name| tail_watchers[name] << socket }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove(name, socket)
|
22
|
+
@tail_mutex.synchronize do
|
23
|
+
client_list = tail_watchers[name]
|
24
|
+
client_list.delete(socket)
|
25
|
+
purge(name, socket) if client_list.empty?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def purge(name, socket)
|
30
|
+
tail_watchers.delete(name)
|
31
|
+
Invoker.close_socket(socket)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/invoker/cli.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "thor"
|
3
|
+
|
4
|
+
module Invoker
|
5
|
+
class CLI < Thor
|
6
|
+
def self.start(*args)
|
7
|
+
cli_args = args.flatten
|
8
|
+
# If it is not a valid task, it is probably file argument
|
9
|
+
if default_start_command?(cli_args)
|
10
|
+
args = [cli_args.unshift("start")]
|
11
|
+
end
|
12
|
+
super(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "setup", "Run Invoker setup"
|
16
|
+
option :tld,
|
17
|
+
type: :string,
|
18
|
+
banner: 'Configure invoker to use a different top level domain'
|
19
|
+
def setup
|
20
|
+
Invoker::Power::Setup.install(get_tld(options))
|
21
|
+
end
|
22
|
+
map install: :setup
|
23
|
+
|
24
|
+
desc "version", "Print Invoker version"
|
25
|
+
def version
|
26
|
+
Invoker::Logger.puts Invoker::VERSION
|
27
|
+
end
|
28
|
+
map %w(-v --version) => :version
|
29
|
+
|
30
|
+
desc "uninstall", "Uninstall Invoker and all installed files"
|
31
|
+
def uninstall
|
32
|
+
Invoker::Power::Setup.uninstall
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "start [CONFIG_FILE]", "Start Invoker Server"
|
36
|
+
option :port, type: :numeric, banner: "Port series to be used for starting rack servers"
|
37
|
+
option :daemon,
|
38
|
+
type: :boolean,
|
39
|
+
banner: "Daemonize the server into the background",
|
40
|
+
aliases: [:d]
|
41
|
+
option :nocolors,
|
42
|
+
type: :boolean,
|
43
|
+
banner: "Disable color in output",
|
44
|
+
aliases: [:nc]
|
45
|
+
option :certificate,
|
46
|
+
type: :string,
|
47
|
+
banner: "Path to certificate"
|
48
|
+
option :private_key,
|
49
|
+
type: :string,
|
50
|
+
banner: "Path to private key"
|
51
|
+
def start(file = nil)
|
52
|
+
Invoker.setup_config_location
|
53
|
+
port = options[:port] || 9000
|
54
|
+
Invoker.daemonize = options[:daemon]
|
55
|
+
Invoker.nocolors = options[:nocolors]
|
56
|
+
Invoker.certificate = options[:certificate]
|
57
|
+
Invoker.private_key = options[:private_key]
|
58
|
+
Invoker.load_invoker_config(file, port)
|
59
|
+
warn_about_notification
|
60
|
+
pinger = Invoker::CLI::Pinger.new(unix_socket)
|
61
|
+
abort("Invoker is already running".colorize(:red)) if pinger.invoker_running?
|
62
|
+
Invoker.commander.start_manager
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "add process", "Add a program to Invoker server"
|
66
|
+
def add(name)
|
67
|
+
unix_socket.send_command('add', process_name: name)
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "add_http process_name port [IP]", "Add an external http process to Invoker DNS server"
|
71
|
+
def add_http(name, port, ip = nil)
|
72
|
+
unix_socket.send_command('add_http', process_name: name, port: port, ip: ip)
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "tail process1 process2", "Tail a particular process"
|
76
|
+
def tail(*names)
|
77
|
+
tailer = Invoker::CLI::Tail.new(names)
|
78
|
+
tailer.run
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "log process1", "Get log of particular process"
|
82
|
+
def log(process_name)
|
83
|
+
system("egrep -a '^#{process_name}' #{Invoker.daemon.log_file}")
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "reload process", "Reload a process managed by Invoker"
|
87
|
+
option :signal,
|
88
|
+
banner: "Signal to send for killing the process, default is SIGINT",
|
89
|
+
aliases: [:s]
|
90
|
+
def reload(name)
|
91
|
+
signal = options[:signal] || 'INT'
|
92
|
+
unix_socket.send_command('reload', process_name: name, signal: signal)
|
93
|
+
end
|
94
|
+
map restart: :reload
|
95
|
+
|
96
|
+
desc "list", "List all running processes"
|
97
|
+
option :raw,
|
98
|
+
type: :boolean,
|
99
|
+
banner: "Print process list in raw text format",
|
100
|
+
aliases: [:r]
|
101
|
+
option :wait,
|
102
|
+
type: :boolean,
|
103
|
+
banner: "wait for update",
|
104
|
+
aliases: [:w]
|
105
|
+
def list
|
106
|
+
if options[:wait]
|
107
|
+
Signal.trap("INT") { exit(0) }
|
108
|
+
loop do
|
109
|
+
puts "\e[H\e[2J"
|
110
|
+
unix_socket.send_command('list') do |response_object|
|
111
|
+
Invoker::ProcessPrinter.new(response_object).tap { |printer| printer.print_table }
|
112
|
+
end
|
113
|
+
sleep(5)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
unix_socket.send_command('list') do |response_object|
|
117
|
+
if options[:raw]
|
118
|
+
Invoker::ProcessPrinter.new(response_object).tap { |printer| printer.print_raw_text }
|
119
|
+
else
|
120
|
+
Invoker::ProcessPrinter.new(response_object).tap { |printer| printer.print_table }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
desc "remove process", "Stop a process managed by Invoker"
|
127
|
+
option :signal,
|
128
|
+
banner: "Signal to send for killing the process, default is SIGINT",
|
129
|
+
aliases: [:s]
|
130
|
+
def remove(name)
|
131
|
+
signal = options[:signal] || 'INT'
|
132
|
+
unix_socket.send_command('remove', process_name: name, signal: signal)
|
133
|
+
end
|
134
|
+
|
135
|
+
desc "stop", "Stop Invoker daemon"
|
136
|
+
def stop
|
137
|
+
Invoker.daemon.stop
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def self.default_start_command?(args)
|
143
|
+
command_name = args.first
|
144
|
+
command_name &&
|
145
|
+
!command_name.match(/^-/) &&
|
146
|
+
!valid_tasks.include?(command_name)
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.valid_tasks
|
150
|
+
tasks.keys + %w(help install restart)
|
151
|
+
end
|
152
|
+
|
153
|
+
# TODO(kgrz): the default TLD option is duplicated in both this file and
|
154
|
+
# lib/invoker.rb May be assign this to a constant?
|
155
|
+
def get_tld(options)
|
156
|
+
if options[:tld] && !options[:tld].empty?
|
157
|
+
options[:tld]
|
158
|
+
else
|
159
|
+
'test'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def unix_socket
|
164
|
+
Invoker::IPC::UnixClient.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def warn_about_notification
|
168
|
+
if Invoker.darwin?
|
169
|
+
warn_about_terminal_notifier
|
170
|
+
else
|
171
|
+
warn_about_libnotify
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def warn_about_libnotify
|
176
|
+
require "libnotify"
|
177
|
+
rescue LoadError
|
178
|
+
Invoker::Logger.puts "You can install libnotify gem for Invoker notifications "\
|
179
|
+
"via system tray".colorize(:red)
|
180
|
+
end
|
181
|
+
|
182
|
+
def warn_about_terminal_notifier
|
183
|
+
if Invoker.darwin?
|
184
|
+
command_path = `which terminal-notifier`
|
185
|
+
if !command_path || command_path.empty?
|
186
|
+
Invoker::Logger.puts "You can enable OSX notification for processes "\
|
187
|
+
"by installing terminal-notifier gem".colorize(:red)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
require "invoker/cli/question"
|
195
|
+
require "invoker/cli/tail_watcher"
|
196
|
+
require "invoker/cli/tail"
|
197
|
+
require "invoker/cli/pinger"
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Invoker
|
2
|
+
class CommandWorker
|
3
|
+
attr_accessor :command_label, :pipe_end, :pid, :color
|
4
|
+
|
5
|
+
def initialize(command_label, pipe_end, pid, color)
|
6
|
+
@command_label = command_label
|
7
|
+
@pipe_end = pipe_end
|
8
|
+
@pid = pid
|
9
|
+
@color = color
|
10
|
+
end
|
11
|
+
|
12
|
+
# Copied verbatim from Eventmachine code
|
13
|
+
def receive_data data
|
14
|
+
(@buf ||= '') << data
|
15
|
+
|
16
|
+
while @buf.slice!(/(.*?)\r?\n/)
|
17
|
+
receive_line($1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def unbind
|
22
|
+
Invoker::Logger.print(".")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Print the lines received over the network
|
26
|
+
def receive_line(line)
|
27
|
+
tail_watchers = Invoker.tail_watchers[@command_label]
|
28
|
+
color_line = "#{@command_label.colorize(color)} : #{line}"
|
29
|
+
plain_line = "#{@command_label} : #{line}"
|
30
|
+
if Invoker.nocolors?
|
31
|
+
Invoker::Logger.puts plain_line
|
32
|
+
else
|
33
|
+
Invoker::Logger.puts color_line
|
34
|
+
end
|
35
|
+
if tail_watchers && !tail_watchers.empty?
|
36
|
+
json_encoded_tail_response = tail_response(color_line)
|
37
|
+
if json_encoded_tail_response
|
38
|
+
tail_watchers.each { |tail_socket| send_data(tail_socket, json_encoded_tail_response) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_h
|
44
|
+
{ command_label: command_label, pid: pid.to_s }
|
45
|
+
end
|
46
|
+
|
47
|
+
def send_data(socket, data)
|
48
|
+
socket.write(data)
|
49
|
+
rescue
|
50
|
+
Invoker::Logger.puts "Removing #{@command_label} watcher #{socket} from list"
|
51
|
+
Invoker.tail_watchers.remove(@command_label, socket)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Encode current line as json and send the response.
|
57
|
+
def tail_response(line)
|
58
|
+
tail_response = Invoker::IPC::Message::TailResponse.new(tail_line: line)
|
59
|
+
tail_response.encoded_message
|
60
|
+
rescue
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "io/console"
|
2
|
+
require 'pty'
|
3
|
+
require "json"
|
4
|
+
require "dotenv"
|
5
|
+
require "forwardable"
|
6
|
+
|
7
|
+
module Invoker
|
8
|
+
class Commander
|
9
|
+
attr_accessor :reactor, :process_manager
|
10
|
+
attr_accessor :event_manager, :runnables, :thread_group
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegators :@process_manager, :start_process_by_name, :stop_process
|
14
|
+
def_delegators :@process_manager, :restart_process, :get_worker_from_fd, :process_list
|
15
|
+
|
16
|
+
def_delegators :@event_manager, :schedule_event, :trigger
|
17
|
+
def_delegator :@reactor, :watch_for_read
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@thread_group = ThreadGroup.new
|
21
|
+
@runnable_mutex = Mutex.new
|
22
|
+
|
23
|
+
@event_manager = Invoker::Event::Manager.new
|
24
|
+
@runnables = []
|
25
|
+
|
26
|
+
@reactor = Invoker::Reactor.new
|
27
|
+
@process_manager = Invoker::ProcessManager.new
|
28
|
+
Thread.abort_on_exception = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Start the invoker process supervisor. This method starts a unix server
|
32
|
+
# in separate thread that listens for incoming commands.
|
33
|
+
def start_manager
|
34
|
+
verify_process_configuration
|
35
|
+
daemonize_app if Invoker.daemonize?
|
36
|
+
install_interrupt_handler
|
37
|
+
unix_server_thread = Thread.new { Invoker::IPC::Server.new }
|
38
|
+
@thread_group.add(unix_server_thread)
|
39
|
+
process_manager.run_power_server
|
40
|
+
Invoker.config.autorunnable_processes.each do |process_info|
|
41
|
+
process_manager.start_process(process_info)
|
42
|
+
Logger.puts("Starting process - #{process_info.label} waiting for #{process_info.sleep_duration} seconds...")
|
43
|
+
sleep(process_info.sleep_duration)
|
44
|
+
end
|
45
|
+
at_exit { process_manager.kill_workers }
|
46
|
+
start_event_loop
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_next_tick(*args, &block)
|
50
|
+
@runnable_mutex.synchronize do
|
51
|
+
@runnables << OpenStruct.new(:args => args, :block => block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def run_runnables
|
56
|
+
@runnables.each do |runnable|
|
57
|
+
instance_exec(*runnable.args, &runnable.block)
|
58
|
+
end
|
59
|
+
@runnables = []
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def verify_process_configuration
|
65
|
+
if !Invoker.config.processes || Invoker.config.processes.empty?
|
66
|
+
raise Invoker::Errors::InvalidConfig.new("No processes configured in config file")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def start_event_loop
|
71
|
+
loop do
|
72
|
+
reactor.monitor_for_fd_events
|
73
|
+
run_runnables
|
74
|
+
run_scheduled_events
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def run_scheduled_events
|
79
|
+
event_manager.run_scheduled_events do |event|
|
80
|
+
event.block.call
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def install_interrupt_handler
|
85
|
+
Signal.trap("INT") {
|
86
|
+
Invoker::Logger.puts("Stopping invoker")
|
87
|
+
process_manager.kill_workers
|
88
|
+
exit(0)
|
89
|
+
}
|
90
|
+
Signal.trap("TERM") {
|
91
|
+
Invoker::Logger.puts("Stopping invoker")
|
92
|
+
process_manager.kill_workers
|
93
|
+
exit(0)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def daemonize_app
|
98
|
+
Invoker.daemon.start
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Invoker
|
2
|
+
# rip off from borg
|
3
|
+
# https://github.com/code-mancers/borg/blob/master/lib/borg/borg_daemon.rb
|
4
|
+
class Daemon
|
5
|
+
attr_reader :process_name
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@process_name = 'invoker'
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
if running?
|
13
|
+
Invoker::Logger.puts "Invoker daemon is already running"
|
14
|
+
exit(0)
|
15
|
+
elsif dead?
|
16
|
+
File.delete(pid_file) if File.exist?(pid_file)
|
17
|
+
end
|
18
|
+
Invoker::Logger.puts "Running Invoker daemon"
|
19
|
+
daemonize
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
kill_process
|
24
|
+
end
|
25
|
+
|
26
|
+
def pid_file
|
27
|
+
File.join(Invoker.home, ".invoker", "#{process_name}.pid")
|
28
|
+
end
|
29
|
+
|
30
|
+
def pid
|
31
|
+
File.read(pid_file).strip.to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_file
|
35
|
+
File.join(Invoker.home, ".invoker", "#{process_name}.log")
|
36
|
+
end
|
37
|
+
|
38
|
+
def daemonize
|
39
|
+
if fork
|
40
|
+
sleep(2)
|
41
|
+
exit(0)
|
42
|
+
else
|
43
|
+
Process.setsid
|
44
|
+
File.open(pid_file, "w") do |file|
|
45
|
+
file.write(Process.pid.to_s)
|
46
|
+
end
|
47
|
+
Invoker::Logger.puts "Invoker daemon log is available at #{log_file}"
|
48
|
+
redirect_io(log_file)
|
49
|
+
$0 = process_name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def kill_process
|
54
|
+
pgid = Process.getpgid(pid)
|
55
|
+
Process.kill('-TERM', pgid)
|
56
|
+
File.delete(pid_file) if File.exist?(pid_file)
|
57
|
+
Invoker::Logger.puts "Stopped Invoker daemon"
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_running?
|
61
|
+
Process.kill(0, pid)
|
62
|
+
true
|
63
|
+
rescue Errno::ESRCH
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def status
|
68
|
+
@status ||= check_process_status
|
69
|
+
end
|
70
|
+
|
71
|
+
def pidfile_exists?
|
72
|
+
File.exist?(pid_file)
|
73
|
+
end
|
74
|
+
|
75
|
+
def running?
|
76
|
+
status == 0
|
77
|
+
end
|
78
|
+
|
79
|
+
# pidfile exists but process isn't running
|
80
|
+
def dead?
|
81
|
+
status == 1
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def check_process_status
|
87
|
+
if pidfile_exists? && process_running?
|
88
|
+
0
|
89
|
+
elsif pidfile_exists? # but not process_running
|
90
|
+
1
|
91
|
+
else
|
92
|
+
3
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def redirect_io(logfile_name = nil)
|
97
|
+
redirect_file_to_target($stdin)
|
98
|
+
redirect_stdout(logfile_name)
|
99
|
+
redirect_stderr
|
100
|
+
end
|
101
|
+
|
102
|
+
def redirect_stderr
|
103
|
+
redirect_file_to_target($stderr, $stdout)
|
104
|
+
$stderr.sync = true
|
105
|
+
end
|
106
|
+
|
107
|
+
def redirect_stdout(logfile_name)
|
108
|
+
if logfile_name
|
109
|
+
begin
|
110
|
+
$stdout.reopen logfile_name, "a"
|
111
|
+
$stdout.sync = true
|
112
|
+
rescue StandardError
|
113
|
+
redirect_file_to_target($stdout)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
redirect_file_to_target($stdout)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def redirect_file_to_target(file, target = "/dev/null")
|
121
|
+
begin
|
122
|
+
file.reopen(target)
|
123
|
+
rescue; end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Invoker
|
2
|
+
class DNSCache
|
3
|
+
attr_accessor :dns_data
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
self.dns_data = {}
|
7
|
+
@dns_mutex = Mutex.new
|
8
|
+
Invoker.config.processes.each do |process|
|
9
|
+
if process.port
|
10
|
+
dns_data[process.label] = { 'port' => process.port }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](process_name)
|
16
|
+
@dns_mutex.synchronize { dns_data[process_name] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(name, port, ip = nil)
|
20
|
+
@dns_mutex.synchronize { dns_data[name] = { 'port' => port, 'ip' => ip } }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Invoker
|
2
|
+
module Errors
|
3
|
+
class ToomanyOpenConnections < StandardError; end
|
4
|
+
class ProcessTerminated < StandardError
|
5
|
+
attr_accessor :message, :ready_fd
|
6
|
+
def initialize(ready_fd, message)
|
7
|
+
@ready_fd = ready_fd
|
8
|
+
@message = message
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class NoValidPortFound < StandardError; end
|
13
|
+
class InvalidConfig < StandardError; end
|
14
|
+
class InvalidFile < StandardError; end
|
15
|
+
class ClientDisconnected < StandardError; end
|
16
|
+
end
|
17
|
+
end
|