meepo 1.5.2
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/.coveralls.yml +1 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.rubocop.yml +29 -0
- data/.travis.yml +10 -0
- data/Dockerfile +7 -0
- data/Gemfile +13 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +15 -0
- data/TODO +5 -0
- data/bin/invoker +7 -0
- data/contrib/completion/invoker-completion.bash +70 -0
- data/contrib/completion/invoker-completion.zsh +62 -0
- data/examples/hello_sinatra.rb +26 -0
- data/examples/sample.ini +3 -0
- data/invoker.gemspec +43 -0
- data/lib/invoker.rb +152 -0
- data/lib/invoker/cli.rb +159 -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/command_worker.rb +60 -0
- data/lib/invoker/commander.rb +95 -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.rb +45 -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.rb +170 -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/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/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 +131 -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/pf_migrate.rb +64 -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.rb +90 -0
- data/lib/invoker/power/setup/distro/arch.rb +15 -0
- data/lib/invoker/power/setup/distro/base.rb +57 -0
- data/lib/invoker/power/setup/distro/debian.rb +11 -0
- data/lib/invoker/power/setup/distro/mint.rb +10 -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 +10 -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 +105 -0
- data/lib/invoker/power/setup/osx_setup.rb +137 -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 +198 -0
- data/lib/invoker/process_printer.rb +43 -0
- data/lib/invoker/reactor.rb +37 -0
- data/lib/invoker/reactor/reader.rb +54 -0
- data/lib/invoker/version.rb +47 -0
- data/readme.md +25 -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 +22 -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/pf_migrate_spec.rb +87 -0
- data/spec/invoker/power/port_finder_spec.rb +16 -0
- data/spec/invoker/power/setup/linux_setup_spec.rb +103 -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 +70 -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 +389 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
module Invoker
|
|
2
|
+
module Power
|
|
3
|
+
class OsxSetup < Setup
|
|
4
|
+
FIREWALL_PLIST_FILE = "/Library/LaunchDaemons/com.codemancers.invoker.firewall.plist"
|
|
5
|
+
RESOLVER_DIR = "/etc/resolver"
|
|
6
|
+
|
|
7
|
+
def resolver_file
|
|
8
|
+
File.join(RESOLVER_DIR, tld)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def setup_invoker
|
|
12
|
+
if setup_resolver_file
|
|
13
|
+
find_open_ports
|
|
14
|
+
install_resolver(port_finder.dns_port)
|
|
15
|
+
install_firewall(port_finder.http_port, port_finder.https_port)
|
|
16
|
+
# Before writing the config file, drop down to a normal user
|
|
17
|
+
drop_to_normal_user
|
|
18
|
+
create_config_file
|
|
19
|
+
else
|
|
20
|
+
Invoker::Logger.puts("Invoker is not configured to serve from subdomains".color(:red))
|
|
21
|
+
end
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def uninstall_invoker
|
|
26
|
+
uninstall_invoker_flag = Invoker::CLI::Question.agree("Are you sure you want to uninstall firewall rules created by setup (y/n) : ")
|
|
27
|
+
|
|
28
|
+
if uninstall_invoker_flag
|
|
29
|
+
remove_resolver_file
|
|
30
|
+
unload_firewall_rule(true)
|
|
31
|
+
Invoker::Power::Config.delete
|
|
32
|
+
Invoker::Logger.puts("Firewall rules were removed")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build_power_config
|
|
37
|
+
config = super
|
|
38
|
+
config[:dns_port] = port_finder.dns_port
|
|
39
|
+
config
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def install_resolver(dns_port)
|
|
43
|
+
open_resolver_for_write { |fl| fl.write(resolve_string(dns_port)) }
|
|
44
|
+
rescue Errno::EACCES
|
|
45
|
+
Invoker::Logger.puts("Running setup requires root access, please rerun it with sudo".color(:red))
|
|
46
|
+
raise
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def install_firewall(http_port, https_port)
|
|
50
|
+
File.open(FIREWALL_PLIST_FILE, "w") { |fl|
|
|
51
|
+
fl.write(plist_string(http_port, https_port))
|
|
52
|
+
}
|
|
53
|
+
unload_firewall_rule
|
|
54
|
+
load_firewall_rule
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def load_firewall_rule
|
|
58
|
+
system("launchctl load -Fw #{FIREWALL_PLIST_FILE} 2>/dev/null")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def unload_firewall_rule(remove = false)
|
|
62
|
+
system("pfctl -a com.apple/250.InvokerFirewall -F nat 2>/dev/null")
|
|
63
|
+
system("launchctl unload -w #{FIREWALL_PLIST_FILE} 2>/dev/null")
|
|
64
|
+
system("rm -rf #{FIREWALL_PLIST_FILE}") if remove
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Ripped from POW code
|
|
68
|
+
def plist_string(http_port, https_port)
|
|
69
|
+
plist =<<-EOD
|
|
70
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
71
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
72
|
+
<plist version="1.0">
|
|
73
|
+
<dict>
|
|
74
|
+
<key>Label</key>
|
|
75
|
+
<string>com.codemancers.invoker</string>
|
|
76
|
+
<key>ProgramArguments</key>
|
|
77
|
+
<array>
|
|
78
|
+
<string>sh</string>
|
|
79
|
+
<string>-c</string>
|
|
80
|
+
<string>#{firewall_command(http_port, https_port)}</string>
|
|
81
|
+
</array>
|
|
82
|
+
<key>RunAtLoad</key>
|
|
83
|
+
<true/>
|
|
84
|
+
<key>UserName</key>
|
|
85
|
+
<string>root</string>
|
|
86
|
+
</dict>
|
|
87
|
+
</plist>
|
|
88
|
+
EOD
|
|
89
|
+
plist
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def resolve_string(dns_port)
|
|
93
|
+
string =<<-EOD
|
|
94
|
+
nameserver 127.0.0.1
|
|
95
|
+
port #{dns_port}
|
|
96
|
+
EOD
|
|
97
|
+
string
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Ripped from Pow code
|
|
101
|
+
def firewall_command(http_port, https_port)
|
|
102
|
+
rules = [
|
|
103
|
+
"rdr pass on lo0 inet proto tcp from any to any port 80 -> 127.0.0.1 port #{http_port}",
|
|
104
|
+
"rdr pass on lo0 inet proto tcp from any to any port 443 -> 127.0.0.1 port #{https_port}"
|
|
105
|
+
].join("\n")
|
|
106
|
+
"echo \"#{rules}\" | pfctl -a 'com.apple/250.InvokerFirewall' -f - -E"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def setup_resolver_file
|
|
110
|
+
return true unless File.exist?(resolver_file)
|
|
111
|
+
|
|
112
|
+
Invoker::Logger.puts "Invoker has detected an existing Pow installation. We recommend "\
|
|
113
|
+
"that you uninstall pow and rerun this setup.".color(:red)
|
|
114
|
+
Invoker::Logger.puts "If you have already uninstalled Pow, proceed with installation"\
|
|
115
|
+
" by pressing y/n."
|
|
116
|
+
replace_resolver_flag = Invoker::CLI::Question.agree("Replace Pow configuration (y/n) : ")
|
|
117
|
+
|
|
118
|
+
if replace_resolver_flag
|
|
119
|
+
Invoker::Logger.puts "Invoker has overwritten one or more files created by Pow. "\
|
|
120
|
+
"If .#{tld} domains still don't resolve locally, try turning off the wi-fi"\
|
|
121
|
+
" and turning it on. It'll force OS X to reload network configuration".color(:green)
|
|
122
|
+
end
|
|
123
|
+
replace_resolver_flag
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def open_resolver_for_write
|
|
129
|
+
FileUtils.mkdir(RESOLVER_DIR) unless Dir.exist?(RESOLVER_DIR)
|
|
130
|
+
fl = File.open(resolver_file, "w")
|
|
131
|
+
yield fl
|
|
132
|
+
ensure
|
|
133
|
+
fl && fl.close
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -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>Bad request</h1>
|
|
36
|
+
<hr>
|
|
37
|
+
<h2>Invoker couldn't understand the request</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 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,198 @@
|
|
|
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".color(: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}".color(: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}".color(: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".color(: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
|
+
end
|
|
172
|
+
if trigger_event
|
|
173
|
+
Invoker.commander.trigger(command_label, :worker_removed)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# add worker to global collections
|
|
178
|
+
def add_worker(worker)
|
|
179
|
+
@open_pipes[worker.pipe_end.fileno] = worker
|
|
180
|
+
@workers[worker.command_label] = worker
|
|
181
|
+
Invoker.commander.watch_for_read(worker.pipe_end)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def run_command(process_info, write_pipe)
|
|
185
|
+
command_label = process_info.label
|
|
186
|
+
|
|
187
|
+
Invoker.commander.schedule_event(command_label, :exit) { remove_worker(command_label) }
|
|
188
|
+
|
|
189
|
+
env_options = load_env(process_info.dir)
|
|
190
|
+
|
|
191
|
+
spawn_options = {
|
|
192
|
+
:chdir => process_info.dir || ENV['PWD'], :out => write_pipe, :err => write_pipe,
|
|
193
|
+
:pgroup => true, :close_others => true, :in => :close
|
|
194
|
+
}
|
|
195
|
+
Invoker.run_without_bundler { spawn(env_options, process_info.cmd, spawn_options) }
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|