invoker_ruby34 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +374 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 00fd6d414bbd948a68c467db81fd0bf85aaeb06936095809422a6600efde13b0
|
4
|
+
data.tar.gz: 5a270e80b14fe388677487fc3c4cefc5cfa631e4412d7f909052f78f847fc317
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3d525fe1112da40b02a632488ab6eaa98eed9540769ca89fbd1d9d2fb8abedee7d455a90ee393fe6d5b106855beb0bbcf8704fec45f3e8400d72c183cc228f63
|
7
|
+
data.tar.gz: '011681d58c22f1b56c498eb07251faf9683e692424199a6cac8ba6856c377d35d6ed0bf6cdda282228e6308086d80d22e32e9028384aa69139b907b5cda279fe'
|
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
|