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,35 @@
|
|
|
1
|
+
module Invoker
|
|
2
|
+
module IPC
|
|
3
|
+
module Message
|
|
4
|
+
class ListResponse < Base
|
|
5
|
+
include Serialization
|
|
6
|
+
message_attributes :processes
|
|
7
|
+
def initialize(options)
|
|
8
|
+
self.processes = []
|
|
9
|
+
process_array = options[:processes] || options['processes']
|
|
10
|
+
process_array.each do |process_hash|
|
|
11
|
+
processes << Process.new(process_hash)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.from_workers(workers)
|
|
16
|
+
process_array = []
|
|
17
|
+
Invoker.config.processes.each do |process|
|
|
18
|
+
worker_attrs = {
|
|
19
|
+
shell_command: process.cmd,
|
|
20
|
+
process_name: process.label,
|
|
21
|
+
dir: process.dir,
|
|
22
|
+
port: process.port
|
|
23
|
+
}
|
|
24
|
+
if worker = workers[process.label]
|
|
25
|
+
worker_attrs.update(pid: worker.pid)
|
|
26
|
+
end
|
|
27
|
+
process_array << worker_attrs
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
new(processes: process_array)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
module Invoker
|
|
4
|
+
module IPC
|
|
5
|
+
class Server
|
|
6
|
+
SOCKET_PATH = "/tmp/invoker"
|
|
7
|
+
def initialize
|
|
8
|
+
@open_clients = []
|
|
9
|
+
Socket.unix_server_loop(SOCKET_PATH) do |sock, client_addrinfo|
|
|
10
|
+
Thread.new { process_client(sock) }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def clean_old_socket
|
|
15
|
+
if File.exist?(SOCKET_PATH)
|
|
16
|
+
FileUtils.rm(SOCKET_PATH, :force => true)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def process_client(client_socket)
|
|
21
|
+
client = Invoker::IPC::ClientHandler.new(client_socket)
|
|
22
|
+
client.read_and_execute
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Invoker
|
|
2
|
+
module IPC
|
|
3
|
+
class TailCommand < BaseCommand
|
|
4
|
+
def run_command(message_object)
|
|
5
|
+
Invoker::Logger.puts("Adding #{message_object.process_names.inspect}")
|
|
6
|
+
Invoker.tail_watchers.add(message_object.process_names, client_socket)
|
|
7
|
+
false
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Invoker
|
|
2
|
+
module IPC
|
|
3
|
+
class UnixClient
|
|
4
|
+
def send_command(command, message = {})
|
|
5
|
+
message_object = get_message_object(command, message)
|
|
6
|
+
open_client_socket do |socket|
|
|
7
|
+
send_json_message(socket, message_object)
|
|
8
|
+
socket.flush
|
|
9
|
+
if block_given?
|
|
10
|
+
response_object = Invoker::IPC.message_from_io(socket)
|
|
11
|
+
yield response_object
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def send_and_receive(command, message = {})
|
|
17
|
+
response = nil
|
|
18
|
+
message_object = get_message_object(command, message)
|
|
19
|
+
open_client_socket(false) do |socket|
|
|
20
|
+
send_json_message(socket, message_object)
|
|
21
|
+
socket.flush
|
|
22
|
+
response = Invoker::IPC.message_from_io(socket)
|
|
23
|
+
end
|
|
24
|
+
response
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def send_and_wait(command, message = {})
|
|
28
|
+
begin
|
|
29
|
+
socket = Socket.unix(Invoker::IPC::Server::SOCKET_PATH)
|
|
30
|
+
rescue
|
|
31
|
+
abort("Invoker does not seem to be running".color(:red))
|
|
32
|
+
end
|
|
33
|
+
message_object = get_message_object(command, message)
|
|
34
|
+
send_json_message(socket, message_object)
|
|
35
|
+
socket.flush
|
|
36
|
+
socket
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.send_command(command, message_arguments = {}, &block)
|
|
40
|
+
new.send_command(command, message_arguments, &block)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def get_message_object(command, message_arguments)
|
|
46
|
+
Invoker::IPC::Message.const_get(Invoker::IPC.camelize(command)).new(message_arguments)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def open_client_socket(abort_if_not_running = true)
|
|
50
|
+
Socket.unix(Invoker::IPC::Server::SOCKET_PATH) { |socket| yield socket }
|
|
51
|
+
rescue
|
|
52
|
+
abort_if_not_running && abort("Invoker does not seem to be running".color(:red))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def send_json_message(socket, message_object)
|
|
56
|
+
socket.write(message_object.encoded_message)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
require 'iniparse'
|
|
2
|
+
|
|
3
|
+
module Invoker
|
|
4
|
+
module Parsers
|
|
5
|
+
class Config
|
|
6
|
+
PORT_REGEX = /\$PORT/
|
|
7
|
+
|
|
8
|
+
attr_accessor :processes, :power_config
|
|
9
|
+
attr_reader :filename
|
|
10
|
+
|
|
11
|
+
# initialize takes a port form cli and decrements it by 1 and sets the
|
|
12
|
+
# instance variable @port. This port value is used as the environment
|
|
13
|
+
# variable $PORT mentioned inside invoker.ini. When method pick_port gets
|
|
14
|
+
# fired it increments the value of port by 1, subsequently when pick_port
|
|
15
|
+
# again gets fired, for another command, it will again increment port
|
|
16
|
+
# value by 1, that way generating different ports for different commands.
|
|
17
|
+
def initialize(filename, port)
|
|
18
|
+
@filename = filename || autodetect_config_file
|
|
19
|
+
print_message_and_abort if invalid_config_file?
|
|
20
|
+
|
|
21
|
+
@port = port - 1
|
|
22
|
+
@processes = load_config
|
|
23
|
+
if Invoker.can_run_balancer?
|
|
24
|
+
@power_config = Invoker::Power::Config.load_config()
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def http_port
|
|
29
|
+
power_config && power_config.http_port
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def dns_port
|
|
33
|
+
power_config && power_config.dns_port
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def https_port
|
|
37
|
+
power_config && power_config.https_port
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def tld
|
|
41
|
+
power_config && power_config.tld
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def autorunnable_processes
|
|
45
|
+
process_to_run = processes.reject(&:disable_autorun)
|
|
46
|
+
process_to_run.sort_by { |process| process.index }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def process(label)
|
|
50
|
+
processes.detect { |pconfig| pconfig.label == label }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def autodetect_config_file
|
|
56
|
+
Dir.glob("{invoker.ini,Procfile}").first
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def invalid_config_file?
|
|
60
|
+
@filename.nil?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def load_config
|
|
64
|
+
@filename = to_global_file if is_global?
|
|
65
|
+
|
|
66
|
+
if is_ini?
|
|
67
|
+
process_ini
|
|
68
|
+
elsif is_procfile?
|
|
69
|
+
process_procfile
|
|
70
|
+
else
|
|
71
|
+
print_message_and_abort
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def process_ini
|
|
76
|
+
ini_content = File.read(@filename)
|
|
77
|
+
document = IniParse.parse(ini_content)
|
|
78
|
+
document.map do |section|
|
|
79
|
+
check_directory(section["directory"])
|
|
80
|
+
process_command_from_section(section)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def process_procfile
|
|
85
|
+
procfile = Invoker::Parsers::Procfile.new(@filename)
|
|
86
|
+
procfile.entries.map do |name, command|
|
|
87
|
+
section = { "label" => name, "command" => command }
|
|
88
|
+
process_command_from_section(section)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def print_message_and_abort
|
|
93
|
+
Invoker::Logger.puts("\n Invalid config file. Invoker requires an ini or a Procfile.".color(:red))
|
|
94
|
+
abort
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def process_command_from_section(section)
|
|
98
|
+
if supports_subdomain?(section)
|
|
99
|
+
port = pick_port(section)
|
|
100
|
+
if port
|
|
101
|
+
command = replace_port_in_command(section['command'], port)
|
|
102
|
+
section['port'], section['command'] = port, command
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
make_pconfig(section)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def pick_port(section)
|
|
110
|
+
if section['command'] =~ PORT_REGEX
|
|
111
|
+
@port += 1
|
|
112
|
+
elsif section['port']
|
|
113
|
+
section['port']
|
|
114
|
+
else
|
|
115
|
+
nil
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def make_pconfig(section)
|
|
120
|
+
pconfig = {
|
|
121
|
+
label: section["label"] || section.key,
|
|
122
|
+
dir: expand_directory(section["directory"]),
|
|
123
|
+
cmd: section["command"]
|
|
124
|
+
}
|
|
125
|
+
pconfig['port'] = section['port'] if section['port']
|
|
126
|
+
pconfig['disable_autorun'] = section['disable_autorun'] if section['disable_autorun']
|
|
127
|
+
pconfig['index'] = section['index'].to_i if section['index']
|
|
128
|
+
section_index = pconfig['index'].to_i
|
|
129
|
+
if section_index
|
|
130
|
+
pconfig['index'] = section_index
|
|
131
|
+
else
|
|
132
|
+
pconfig['index'] = 0
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
sleep_duration = section['sleep'].to_i
|
|
136
|
+
if sleep_duration > 0
|
|
137
|
+
pconfig['sleep_duration'] = sleep_duration
|
|
138
|
+
else
|
|
139
|
+
pconfig['sleep_duration'] = 1
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
OpenStruct.new(pconfig)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def supports_subdomain?(section)
|
|
146
|
+
(section['command'] =~ PORT_REGEX) || section['port']
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def check_directory(app_dir)
|
|
150
|
+
if app_dir && !app_dir.empty? && !File.directory?(expand_directory(app_dir))
|
|
151
|
+
raise Invoker::Errors::InvalidConfig.new("Invalid directory #{app_dir}")
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def expand_directory(app_dir)
|
|
156
|
+
File.expand_path(app_dir) if app_dir
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def replace_port_in_command(command, port)
|
|
160
|
+
if command =~ PORT_REGEX
|
|
161
|
+
command.gsub(PORT_REGEX, port.to_s)
|
|
162
|
+
else
|
|
163
|
+
command
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def is_ini?
|
|
168
|
+
File.extname(@filename) == '.ini'
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def is_procfile?
|
|
172
|
+
@filename =~ /Procfile/
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def to_global_file
|
|
176
|
+
File.join(Invoker::Power::Config.config_dir, "#{@filename}.ini")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def is_global?
|
|
180
|
+
@filename =~ /^\w+$/ && File.exist?(to_global_file)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Invoker
|
|
2
|
+
module Parsers
|
|
3
|
+
# rip off from foreman
|
|
4
|
+
class Procfile
|
|
5
|
+
# Initialize a Procfile
|
|
6
|
+
#
|
|
7
|
+
# @param [String] filename (nil) An optional filename to read from
|
|
8
|
+
#
|
|
9
|
+
def initialize(filename=nil)
|
|
10
|
+
@entries = []
|
|
11
|
+
load(filename) if filename
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Yield each +Procfile+ entry in order
|
|
15
|
+
#
|
|
16
|
+
def entries
|
|
17
|
+
return @entries unless block_given?
|
|
18
|
+
@entries.each do |(name, command)|
|
|
19
|
+
yield name, command
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Retrieve a +Procfile+ command by name
|
|
24
|
+
#
|
|
25
|
+
# @param [String] name The name of the Procfile entry to retrieve
|
|
26
|
+
#
|
|
27
|
+
def [](name)
|
|
28
|
+
@entries.detect { |n,c| name == n }.last
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Create a +Procfile+ entry
|
|
32
|
+
#
|
|
33
|
+
# @param [String] name The name of the +Procfile+ entry to create
|
|
34
|
+
# @param [String] command The command of the +Procfile+ entry to create
|
|
35
|
+
#
|
|
36
|
+
def []=(name, command)
|
|
37
|
+
delete name
|
|
38
|
+
@entries << [name, command]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Remove a +Procfile+ entry
|
|
42
|
+
#
|
|
43
|
+
# @param [String] name The name of the +Procfile+ entry to remove
|
|
44
|
+
#
|
|
45
|
+
def delete(name)
|
|
46
|
+
@entries.reject! { |n,c| name == n }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Load a Procfile from a file
|
|
50
|
+
#
|
|
51
|
+
# @param [String] filename The filename of the +Procfile+ to load
|
|
52
|
+
#
|
|
53
|
+
def load(filename)
|
|
54
|
+
@entries.replace parse(filename)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Save a Procfile to a file
|
|
58
|
+
#
|
|
59
|
+
# @param [String] filename Save the +Procfile+ to this file
|
|
60
|
+
#
|
|
61
|
+
def save(filename)
|
|
62
|
+
File.open(filename, 'w') do |file|
|
|
63
|
+
file.puts self.to_s
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Get the +Procfile+ as a +String+
|
|
68
|
+
#
|
|
69
|
+
def to_s
|
|
70
|
+
@entries.map do |name, command|
|
|
71
|
+
[ name, command ].join(": ")
|
|
72
|
+
end.join("\n")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def parse(filename)
|
|
78
|
+
File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
|
|
79
|
+
if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/
|
|
80
|
+
[$1, $2]
|
|
81
|
+
end
|
|
82
|
+
end.compact
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|