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
@@ -0,0 +1,40 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>Invoker</title>
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
margin: 0;
|
9
|
+
padding: 0;
|
10
|
+
background: #fff;
|
11
|
+
line-height: 18px;
|
12
|
+
}
|
13
|
+
div.page {
|
14
|
+
padding: 36px 90px;
|
15
|
+
}
|
16
|
+
h1, h2, p, li {
|
17
|
+
font-family: Helvetica, sans-serif;
|
18
|
+
font-size: 13px;
|
19
|
+
}
|
20
|
+
h1 {
|
21
|
+
line-height: 45px;
|
22
|
+
font-size: 36px;
|
23
|
+
margin: 0;
|
24
|
+
}
|
25
|
+
h2 {
|
26
|
+
line-height: 27px;
|
27
|
+
font-size: 18px;
|
28
|
+
font-weight: normal;
|
29
|
+
margin: 0;
|
30
|
+
}
|
31
|
+
</style>
|
32
|
+
</head>
|
33
|
+
<body class="">
|
34
|
+
<div class="page">
|
35
|
+
<h1>Application not found</h1>
|
36
|
+
<hr>
|
37
|
+
<h2>Invoker could not find the application. Please check the configuration file.<h2>
|
38
|
+
</div>
|
39
|
+
</body>
|
40
|
+
</html>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>Invoker</title>
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
margin: 0;
|
9
|
+
padding: 0;
|
10
|
+
background: #fff;
|
11
|
+
line-height: 18px;
|
12
|
+
}
|
13
|
+
div.page {
|
14
|
+
padding: 36px 90px;
|
15
|
+
}
|
16
|
+
h1, h2, p, li {
|
17
|
+
font-family: Helvetica, sans-serif;
|
18
|
+
font-size: 13px;
|
19
|
+
}
|
20
|
+
h1 {
|
21
|
+
line-height: 45px;
|
22
|
+
font-size: 36px;
|
23
|
+
margin: 0;
|
24
|
+
}
|
25
|
+
h2 {
|
26
|
+
line-height: 27px;
|
27
|
+
font-size: 18px;
|
28
|
+
font-weight: normal;
|
29
|
+
margin: 0;
|
30
|
+
}
|
31
|
+
</style>
|
32
|
+
</head>
|
33
|
+
<body class="">
|
34
|
+
<div class="page">
|
35
|
+
<h1>Application not running</h1>
|
36
|
+
<hr>
|
37
|
+
<h2>Invoker did not get any response. Please check if the application is running.<h2>
|
38
|
+
</div>
|
39
|
+
</body>
|
40
|
+
</html>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Invoker
|
2
|
+
module Power
|
3
|
+
class UrlRewriter
|
4
|
+
def select_backend_config(complete_path)
|
5
|
+
possible_matches = extract_host_from_domain(complete_path)
|
6
|
+
exact_match = nil
|
7
|
+
possible_matches.each do |match|
|
8
|
+
if match
|
9
|
+
exact_match = dns_check(process_name: match)
|
10
|
+
break if exact_match.port
|
11
|
+
end
|
12
|
+
end
|
13
|
+
exact_match
|
14
|
+
end
|
15
|
+
|
16
|
+
def extract_host_from_domain(complete_path)
|
17
|
+
matching_strings = []
|
18
|
+
tld_match_regex.map do |regexp|
|
19
|
+
if (match_result = complete_path.match(regexp))
|
20
|
+
matching_strings << match_result[1]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
matching_strings.uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def tld_match_regex
|
29
|
+
tld = Invoker.config.tld
|
30
|
+
[/([\w.-]+)\.#{tld}(\:\d+)?$/, /([\w-]+)\.#{tld}(\:\d+)?$/]
|
31
|
+
end
|
32
|
+
|
33
|
+
def dns_check(dns_args)
|
34
|
+
Invoker::IPC::UnixClient.send_command("dns_check", dns_args) do |dns_response|
|
35
|
+
dns_response
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module Invoker
|
2
|
+
# Class is responsible for managing all the processes Invoker is supposed
|
3
|
+
# to manage. Takes care of starting, stopping and restarting processes.
|
4
|
+
class ProcessManager
|
5
|
+
LABEL_COLORS = [:green, :yellow, :blue, :magenta, :cyan]
|
6
|
+
attr_accessor :open_pipes, :workers
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@open_pipes = {}
|
10
|
+
@workers = {}
|
11
|
+
@worker_mutex = Mutex.new
|
12
|
+
@thread_group = ThreadGroup.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_process(process_info)
|
16
|
+
m, s = PTY.open
|
17
|
+
s.raw! # disable newline conversion.
|
18
|
+
|
19
|
+
pid = run_command(process_info, s)
|
20
|
+
|
21
|
+
s.close
|
22
|
+
|
23
|
+
worker = CommandWorker.new(process_info.label, m, pid, select_color)
|
24
|
+
|
25
|
+
add_worker(worker)
|
26
|
+
wait_on_pid(process_info.label, pid)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Start a process given their name
|
30
|
+
# @param process_name [String] Command label of process specified in config file.
|
31
|
+
def start_process_by_name(process_name)
|
32
|
+
if process_running?(process_name)
|
33
|
+
Invoker::Logger.puts "\nProcess '#{process_name}' is already running".colorize(:red)
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
process_info = Invoker.config.process(process_name)
|
38
|
+
start_process(process_info) if process_info
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remove a process from list of processes managed by invoker supervisor.It also
|
42
|
+
# kills the process before removing it from the list.
|
43
|
+
#
|
44
|
+
# @param remove_message [Invoker::IPC::Message::Remove]
|
45
|
+
# @return [Boolean] if process existed and was removed else false
|
46
|
+
def stop_process(remove_message)
|
47
|
+
worker = workers[remove_message.process_name]
|
48
|
+
command_label = remove_message.process_name
|
49
|
+
return false unless worker
|
50
|
+
signal_to_use = remove_message.signal || 'INT'
|
51
|
+
|
52
|
+
Invoker::Logger.puts("Removing #{command_label} with signal #{signal_to_use}".colorize(:red))
|
53
|
+
kill_or_remove_process(worker.pid, signal_to_use, command_label)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Receive a message from user to restart a Process
|
57
|
+
# @param [Invoker::IPC::Message::Reload]
|
58
|
+
def restart_process(reload_message)
|
59
|
+
command_label = reload_message.process_name
|
60
|
+
if stop_process(reload_message.remove_message)
|
61
|
+
Invoker.commander.schedule_event(command_label, :worker_removed) do
|
62
|
+
start_process_by_name(command_label)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
start_process_by_name(command_label)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def run_power_server
|
70
|
+
return unless Invoker.can_run_balancer?(false)
|
71
|
+
|
72
|
+
powerup_id = Invoker::Power::Powerup.fork_and_start
|
73
|
+
wait_on_pid("powerup_manager", powerup_id)
|
74
|
+
at_exit do
|
75
|
+
begin
|
76
|
+
Process.kill("INT", powerup_id)
|
77
|
+
rescue Errno::ESRCH; end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Given a file descriptor returns the worker object
|
82
|
+
#
|
83
|
+
# @param fd [IO] an IO object with valid file descriptor
|
84
|
+
# @return [Invoker::CommandWorker] The worker object which is associated with this fd
|
85
|
+
def get_worker_from_fd(fd)
|
86
|
+
open_pipes[fd.fileno]
|
87
|
+
end
|
88
|
+
|
89
|
+
def load_env(directory = nil)
|
90
|
+
directory ||= ENV['PWD']
|
91
|
+
|
92
|
+
if !directory || directory.empty? || !Dir.exist?(directory)
|
93
|
+
return {}
|
94
|
+
end
|
95
|
+
|
96
|
+
default_env = File.join(directory, '.env')
|
97
|
+
local_env = File.join(directory, '.env.local')
|
98
|
+
env = {}
|
99
|
+
|
100
|
+
if File.exist?(default_env)
|
101
|
+
env.merge!(Dotenv::Environment.new(default_env))
|
102
|
+
end
|
103
|
+
|
104
|
+
if File.exist?(local_env)
|
105
|
+
env.merge!(Dotenv::Environment.new(local_env))
|
106
|
+
end
|
107
|
+
|
108
|
+
env
|
109
|
+
end
|
110
|
+
|
111
|
+
def kill_workers
|
112
|
+
@workers.each do |key, worker|
|
113
|
+
kill_or_remove_process(worker.pid, "INT", worker.command_label)
|
114
|
+
end
|
115
|
+
@workers = {}
|
116
|
+
end
|
117
|
+
|
118
|
+
# List currently running commands
|
119
|
+
def process_list
|
120
|
+
Invoker::IPC::Message::ListResponse.from_workers(workers)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def wait_on_pid(command_label, pid)
|
126
|
+
raise Invoker::Errors::ToomanyOpenConnections if @thread_group.enclosed?
|
127
|
+
|
128
|
+
thread = Thread.new do
|
129
|
+
Process.wait(pid)
|
130
|
+
message = "Process with command #{command_label} exited with status #{$?.exitstatus}"
|
131
|
+
Invoker::Logger.puts("\n#{message}".colorize(:red))
|
132
|
+
Invoker.notify_user(message)
|
133
|
+
Invoker.commander.trigger(command_label, :exit)
|
134
|
+
end
|
135
|
+
@thread_group.add(thread)
|
136
|
+
end
|
137
|
+
|
138
|
+
def select_color
|
139
|
+
selected_color = LABEL_COLORS.shift
|
140
|
+
LABEL_COLORS.push(selected_color)
|
141
|
+
selected_color
|
142
|
+
end
|
143
|
+
|
144
|
+
def process_running?(command_label)
|
145
|
+
!!workers[command_label]
|
146
|
+
end
|
147
|
+
|
148
|
+
def kill_or_remove_process(pid, signal_to_use, command_label)
|
149
|
+
process_kill(pid, signal_to_use)
|
150
|
+
true
|
151
|
+
rescue Errno::ESRCH
|
152
|
+
Invoker::Logger.puts("Killing process with #{pid} and name #{command_label} failed".colorize(:red))
|
153
|
+
remove_worker(command_label, false)
|
154
|
+
false
|
155
|
+
end
|
156
|
+
|
157
|
+
def process_kill(pid, signal_to_use)
|
158
|
+
if signal_to_use.to_i == 0
|
159
|
+
Process.kill(signal_to_use, -Process.getpgid(pid))
|
160
|
+
else
|
161
|
+
Process.kill(signal_to_use.to_i, -Process.getpgid(pid))
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Remove worker from all collections
|
166
|
+
def remove_worker(command_label, trigger_event = true)
|
167
|
+
worker = @workers[command_label]
|
168
|
+
if worker
|
169
|
+
@open_pipes.delete(worker.pipe_end.fileno)
|
170
|
+
@workers.delete(command_label)
|
171
|
+
# Move label color to front of array so it's reused first
|
172
|
+
LABEL_COLORS.delete(worker.color)
|
173
|
+
LABEL_COLORS.unshift(worker.color)
|
174
|
+
end
|
175
|
+
if trigger_event
|
176
|
+
Invoker.commander.trigger(command_label, :worker_removed)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# add worker to global collections
|
181
|
+
def add_worker(worker)
|
182
|
+
@open_pipes[worker.pipe_end.fileno] = worker
|
183
|
+
@workers[worker.command_label] = worker
|
184
|
+
Invoker.commander.watch_for_read(worker.pipe_end)
|
185
|
+
end
|
186
|
+
|
187
|
+
def run_command(process_info, write_pipe)
|
188
|
+
command_label = process_info.label
|
189
|
+
|
190
|
+
Invoker.commander.schedule_event(command_label, :exit) { remove_worker(command_label) }
|
191
|
+
|
192
|
+
env_options = load_env(process_info.dir)
|
193
|
+
|
194
|
+
spawn_options = {
|
195
|
+
:chdir => process_info.dir || ENV['PWD'], :out => write_pipe, :err => write_pipe,
|
196
|
+
:pgroup => true, :close_others => true, :in => :close
|
197
|
+
}
|
198
|
+
Invoker.run_without_bundler { spawn(env_options, process_info.cmd, spawn_options) }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Invoker
|
2
|
+
class ProcessPrinter
|
3
|
+
MAX_COLUMN_WIDTH = 40
|
4
|
+
attr_accessor :list_response
|
5
|
+
|
6
|
+
def initialize(list_response)
|
7
|
+
self.list_response = list_response
|
8
|
+
end
|
9
|
+
|
10
|
+
def print_table
|
11
|
+
hash_with_colors = []
|
12
|
+
list_response.processes.each do |process|
|
13
|
+
if process.pid
|
14
|
+
hash_with_colors << colorize_hash(process, "green")
|
15
|
+
else
|
16
|
+
hash_with_colors << colorize_hash(process, "light_black")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
Formatador.display_compact_table(hash_with_colors)
|
20
|
+
end
|
21
|
+
|
22
|
+
def print_raw_text
|
23
|
+
list_response.processes.each do |process|
|
24
|
+
Formatador.display_line("[bold]Process Name : #{process.process_name}[/]")
|
25
|
+
Formatador.indent {
|
26
|
+
Formatador.display_line("Dir : #{process.dir}")
|
27
|
+
if process.pid
|
28
|
+
Formatador.display_line("PID : #{process.pid}")
|
29
|
+
else
|
30
|
+
Formatador.display_line("PID : Not Running")
|
31
|
+
end
|
32
|
+
Formatador.display_line("Port : #{process.port}")
|
33
|
+
Formatador.display_line("Command : #{process.shell_command}")
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def colorize_hash(process, color)
|
41
|
+
hash_with_colors = {}
|
42
|
+
|
43
|
+
hash_with_colors['dir'] = colored_string(process.dir, color)
|
44
|
+
hash_with_colors['pid'] = colored_string(process.pid || 'Not Running', color)
|
45
|
+
hash_with_colors['port'] = colored_string(process.port, color)
|
46
|
+
hash_with_colors['shell_command'] = colored_string(process.shell_command, color)
|
47
|
+
hash_with_colors['process_name'] = colored_string(process.process_name, color)
|
48
|
+
hash_with_colors
|
49
|
+
end
|
50
|
+
|
51
|
+
def colored_string(string, color)
|
52
|
+
string = string.to_s
|
53
|
+
if string.length > MAX_COLUMN_WIDTH
|
54
|
+
string = "#{string[0..MAX_COLUMN_WIDTH]}.."
|
55
|
+
end
|
56
|
+
"[#{color}]#{string}[/]"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Invoker
|
2
|
+
class Reactor::Reader
|
3
|
+
attr_accessor :read_array
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@read_array = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def watch_for_read(socket)
|
10
|
+
@read_array << socket
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle_read_event(read_ready_fds)
|
14
|
+
ready_fds = read_ready_fds.flatten.compact
|
15
|
+
ready_fds.each { |ready_fd| process_read(ready_fd) }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def process_read(ready_fd)
|
21
|
+
command_worker = Invoker.commander.get_worker_from_fd(ready_fd)
|
22
|
+
begin
|
23
|
+
data = read_data(ready_fd)
|
24
|
+
send_data_to_worker(data, command_worker)
|
25
|
+
rescue Invoker::Errors::ProcessTerminated
|
26
|
+
remove_from_read_monitoring(command_worker, ready_fd)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def send_data_to_worker(data, command_worker)
|
31
|
+
if command_worker
|
32
|
+
command_worker.receive_data(data)
|
33
|
+
else
|
34
|
+
Invoker::Logger.puts("No reader found for incoming data")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_from_read_monitoring(command_worker, ready_fd)
|
39
|
+
if command_worker
|
40
|
+
read_array.delete(command_worker.pipe_end)
|
41
|
+
command_worker.unbind
|
42
|
+
else
|
43
|
+
read_array.delete(ready_fd)
|
44
|
+
end
|
45
|
+
rescue StandardError => error
|
46
|
+
Invoker::Logger.puts(error.message)
|
47
|
+
Invoker::Logger.puts(error.backtrace)
|
48
|
+
end
|
49
|
+
|
50
|
+
def read_data(ready_fd)
|
51
|
+
sock_data = []
|
52
|
+
begin
|
53
|
+
while(t_data = ready_fd.read_nonblock(64))
|
54
|
+
sock_data << t_data
|
55
|
+
end
|
56
|
+
rescue Errno::EAGAIN
|
57
|
+
return sock_data.join
|
58
|
+
rescue Errno::EWOULDBLOCK
|
59
|
+
return sock_data.join
|
60
|
+
rescue
|
61
|
+
raise Invoker::Errors::ProcessTerminated.new(ready_fd,sock_data.join)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Invoker
|
2
|
+
class Reactor
|
3
|
+
attr_accessor :reader
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@reader = Invoker::Reactor::Reader.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def watch_for_read(fd)
|
10
|
+
reader.watch_for_read(fd)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Writes data to client socket and raises error if errors
|
14
|
+
# while writing
|
15
|
+
def send_data(socket, data)
|
16
|
+
socket.write(data)
|
17
|
+
rescue
|
18
|
+
raise Invoker::Errors::ClientDisconnected
|
19
|
+
end
|
20
|
+
|
21
|
+
def monitor_for_fd_events
|
22
|
+
ready_read_fds, _ , _ = select(*options_for_select)
|
23
|
+
|
24
|
+
if ready_read_fds && !ready_read_fds.empty?
|
25
|
+
reader.handle_read_event(ready_read_fds)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def options_for_select
|
32
|
+
[reader.read_array, [], [], 0.05]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
require "invoker/reactor/reader"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Invoker
|
2
|
+
class Version
|
3
|
+
include Comparable
|
4
|
+
attr_reader :major, :minor, :patch
|
5
|
+
|
6
|
+
def initialize(number)
|
7
|
+
t_major, t_minor, t_patch = number.split('.')
|
8
|
+
@major = t_major.to_i
|
9
|
+
@minor = t_minor.to_i
|
10
|
+
@patch = t_patch.to_i
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_a
|
14
|
+
[major, minor, patch].compact
|
15
|
+
end
|
16
|
+
|
17
|
+
def <=>(version)
|
18
|
+
(major.to_i <=> version.major.to_i).nonzero? ||
|
19
|
+
(minor.to_i <=> version.minor.to_i).nonzero? ||
|
20
|
+
patch.to_i <=> version.patch.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def matches?(operator, number)
|
24
|
+
version = Version.new(number)
|
25
|
+
self == version
|
26
|
+
|
27
|
+
return self == version if operator == '='
|
28
|
+
return self > version if operator == '>'
|
29
|
+
return self < version if operator == '<'
|
30
|
+
return version <= self && version.next > self if operator == '~>'
|
31
|
+
end
|
32
|
+
|
33
|
+
def next
|
34
|
+
next_splits = to_a
|
35
|
+
|
36
|
+
if next_splits.length == 1
|
37
|
+
next_splits[0] += 1
|
38
|
+
else
|
39
|
+
next_splits[-2] += 1
|
40
|
+
next_splits[-1] = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
Version.new(next_splits.join('.'))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
VERSION = "1.6.0"
|
47
|
+
end
|
data/lib/invoker.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
$: << File.dirname(__FILE__) unless $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "formatador"
|
5
|
+
|
6
|
+
require "ostruct"
|
7
|
+
require "uuid"
|
8
|
+
require "json"
|
9
|
+
require "colorize"
|
10
|
+
require "etc"
|
11
|
+
|
12
|
+
require "invoker/version"
|
13
|
+
require "invoker/logger"
|
14
|
+
require "invoker/daemon"
|
15
|
+
require "invoker/cli"
|
16
|
+
require "invoker/dns_cache"
|
17
|
+
require "invoker/ipc"
|
18
|
+
require "invoker/power/config"
|
19
|
+
require "invoker/power/port_finder"
|
20
|
+
require "invoker/power/setup"
|
21
|
+
require "invoker/power/setup/linux_setup"
|
22
|
+
require "invoker/power/setup/osx_setup"
|
23
|
+
require "invoker/power/powerup"
|
24
|
+
require "invoker/errors"
|
25
|
+
require "invoker/parsers/procfile"
|
26
|
+
require "invoker/parsers/config"
|
27
|
+
require "invoker/commander"
|
28
|
+
require "invoker/process_manager"
|
29
|
+
require "invoker/command_worker"
|
30
|
+
require "invoker/reactor"
|
31
|
+
require "invoker/event/manager"
|
32
|
+
require "invoker/process_printer"
|
33
|
+
|
34
|
+
module Invoker
|
35
|
+
class << self
|
36
|
+
attr_accessor :config, :tail_watchers, :commander
|
37
|
+
attr_accessor :dns_cache, :daemonize, :nocolors, :certificate, :private_key
|
38
|
+
|
39
|
+
alias_method :daemonize?, :daemonize
|
40
|
+
alias_method :nocolors?, :nocolors
|
41
|
+
|
42
|
+
def darwin?
|
43
|
+
ruby_platform.downcase.include?("darwin")
|
44
|
+
end
|
45
|
+
|
46
|
+
def linux?
|
47
|
+
ruby_platform.downcase.include?("linux")
|
48
|
+
end
|
49
|
+
|
50
|
+
def ruby_platform
|
51
|
+
RUBY_PLATFORM
|
52
|
+
end
|
53
|
+
|
54
|
+
def load_invoker_config(file, port)
|
55
|
+
@config = Invoker::Parsers::Config.new(file, port)
|
56
|
+
@dns_cache = Invoker::DNSCache.new(@invoker_config)
|
57
|
+
@tail_watchers = Invoker::CLI::TailWatcher.new
|
58
|
+
@commander = Invoker::Commander.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def close_socket(socket)
|
62
|
+
socket.close
|
63
|
+
rescue StandardError => error
|
64
|
+
Invoker::Logger.puts "Error removing socket #{error}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def daemon
|
68
|
+
@daemon ||= Invoker::Daemon.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def can_run_balancer?(throw_warning = true)
|
72
|
+
return true if File.exist?(Invoker::Power::Config.config_file)
|
73
|
+
|
74
|
+
if throw_warning
|
75
|
+
Invoker::Logger.puts("Invoker has detected setup has not been run. Domain feature will not work without running setup command.".colorize(:red))
|
76
|
+
end
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup_config_location
|
81
|
+
config_dir = Invoker::Power::Config.config_dir
|
82
|
+
return config_dir if Dir.exist?(config_dir)
|
83
|
+
|
84
|
+
if File.exist?(config_dir)
|
85
|
+
old_config = File.read(config_dir)
|
86
|
+
FileUtils.rm_f(config_dir)
|
87
|
+
end
|
88
|
+
|
89
|
+
FileUtils.mkdir(config_dir)
|
90
|
+
|
91
|
+
migrate_old_config(old_config, config_dir) if old_config
|
92
|
+
config_dir
|
93
|
+
end
|
94
|
+
|
95
|
+
def run_without_bundler
|
96
|
+
if defined?(Bundler)
|
97
|
+
Bundler.with_clean_env do
|
98
|
+
yield
|
99
|
+
end
|
100
|
+
else
|
101
|
+
yield
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def notify_user(message)
|
106
|
+
if Invoker.darwin?
|
107
|
+
run_without_bundler { check_and_notify_with_terminal_notifier(message) }
|
108
|
+
elsif Invoker.linux?
|
109
|
+
notify_with_libnotify(message)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def check_and_notify_with_terminal_notifier(message)
|
114
|
+
command_path = `which terminal-notifier`
|
115
|
+
if command_path && !command_path.empty?
|
116
|
+
system("terminal-notifier -message '#{message}' -title Invoker")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def notify_with_libnotify(message)
|
121
|
+
begin
|
122
|
+
require "libnotify"
|
123
|
+
Libnotify.show(body: message, summary: "Invoker", timeout: 2.5)
|
124
|
+
rescue LoadError; end
|
125
|
+
end
|
126
|
+
|
127
|
+
def migrate_old_config(old_config, config_location)
|
128
|
+
new_config = File.join(config_location, 'config')
|
129
|
+
File.open(new_config, 'w') do |file|
|
130
|
+
file.write(old_config)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# On some platforms `Dir.home` or `ENV['HOME']` does not return home directory of user.
|
135
|
+
# this is especially true, after effective and real user id of process
|
136
|
+
# has been changed.
|
137
|
+
#
|
138
|
+
# @return [String] home directory of the user
|
139
|
+
def home
|
140
|
+
if File.writable?(Dir.home)
|
141
|
+
Dir.home
|
142
|
+
else
|
143
|
+
Etc.getpwuid(Process.uid).dir
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def default_tld
|
148
|
+
'test'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Invoker::CLI::Pinger do
|
4
|
+
let(:unix_client) { Invoker::IPC::UnixClient.new }
|
5
|
+
let(:pinger) { Invoker::CLI::Pinger.new(unix_client) }
|
6
|
+
let(:pong) { MM::Pong.new(status: 'pong') }
|
7
|
+
|
8
|
+
context "If Invoker is running" do
|
9
|
+
it "should return true" do
|
10
|
+
unix_client.expects(:send_and_receive).returns(pong)
|
11
|
+
expect(pinger.invoker_running?).to be_truthy
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "if Invoker is not running" do
|
16
|
+
it "should return false" do
|
17
|
+
unix_client.expects(:send_and_receive).returns(nil)
|
18
|
+
unix_client.expects(:abort).never
|
19
|
+
expect(pinger.invoker_running?).to be_falsey
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|