meepo 1.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|