invoker 1.0.4 → 1.1.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 +4 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +30 -0
- data/.travis.yml +1 -0
- data/Gemfile +1 -0
- data/bin/invoker +4 -8
- data/invoker.gemspec +10 -11
- data/lib/invoker.rb +95 -21
- data/lib/invoker/cli.rb +126 -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 +28 -2
- data/lib/invoker/commander.rb +34 -236
- data/lib/invoker/config.rb +5 -0
- data/lib/invoker/daemon.rb +126 -0
- data/lib/invoker/dns_cache.rb +23 -0
- data/lib/invoker/errors.rb +1 -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 +16 -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 +33 -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/{command_listener → ipc}/server.rb +6 -11
- data/lib/invoker/ipc/tail_command.rb +11 -0
- data/lib/invoker/ipc/unix_client.rb +60 -0
- data/lib/invoker/parsers/config.rb +1 -0
- data/lib/invoker/power/balancer.rb +17 -7
- data/lib/invoker/power/config.rb +6 -3
- data/lib/invoker/power/dns.rb +22 -21
- data/lib/invoker/power/http_response.rb +1 -1
- data/lib/invoker/power/power.rb +3 -0
- data/lib/invoker/power/powerup.rb +3 -2
- data/lib/invoker/power/setup.rb +6 -4
- data/lib/invoker/process_manager.rb +187 -0
- data/lib/invoker/process_printer.rb +27 -38
- data/lib/invoker/reactor.rb +19 -38
- data/lib/invoker/reactor/reader.rb +53 -0
- data/lib/invoker/version.rb +1 -1
- data/readme.md +1 -1
- 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 +30 -0
- data/spec/invoker/commander_spec.rb +57 -127
- data/spec/invoker/config_spec.rb +21 -0
- data/spec/invoker/daemon_spec.rb +34 -0
- data/spec/invoker/invoker_spec.rb +31 -0
- data/spec/invoker/ipc/client_handler_spec.rb +44 -0
- data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
- data/spec/invoker/ipc/message/list_response_spec.rb +22 -0
- data/spec/invoker/ipc/message_spec.rb +45 -0
- data/spec/invoker/ipc/unix_client_spec.rb +29 -0
- data/spec/invoker/power/setup_spec.rb +1 -1
- data/spec/invoker/process_manager_spec.rb +98 -0
- data/spec/invoker/reactor_spec.rb +6 -0
- data/spec/spec_helper.rb +15 -24
- metadata +107 -77
- data/lib/invoker/command_listener/client.rb +0 -45
- data/lib/invoker/parsers/option_parser.rb +0 -106
- data/lib/invoker/power.rb +0 -7
- data/lib/invoker/runner.rb +0 -98
- data/spec/invoker/command_listener/client_spec.rb +0 -52
@@ -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)
|
20
|
+
@dns_mutex.synchronize { dns_data[name] = { 'port' => port } }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/invoker/errors.rb
CHANGED
data/lib/invoker/ipc.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require "invoker/ipc/base_command"
|
2
|
+
require 'invoker/ipc/message'
|
3
|
+
require 'invoker/ipc/add_command'
|
4
|
+
require 'invoker/ipc/add_http_command'
|
5
|
+
require 'invoker/ipc/client_handler'
|
6
|
+
require 'invoker/ipc/dns_check_command'
|
7
|
+
require 'invoker/ipc/list_command'
|
8
|
+
require 'invoker/ipc/remove_command'
|
9
|
+
require 'invoker/ipc/server'
|
10
|
+
require "invoker/ipc/reload_command"
|
11
|
+
require 'invoker/ipc/tail_command'
|
12
|
+
require 'invoker/ipc/unix_client'
|
13
|
+
require "invoker/ipc/ping_command"
|
14
|
+
|
15
|
+
module Invoker
|
16
|
+
module IPC
|
17
|
+
INITIAL_PACKET_SIZE = 9
|
18
|
+
def self.message_from_io(io)
|
19
|
+
json_size = io.read(INITIAL_PACKET_SIZE)
|
20
|
+
json_string = io.read(json_size.to_i)
|
21
|
+
ruby_object_hash = JSON.parse(json_string)
|
22
|
+
command_name = camelize(ruby_object_hash['type'])
|
23
|
+
command_klass = Invoker::IPC::Message.const_get(command_name)
|
24
|
+
command_klass.new(ruby_object_hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Taken from Rails without inflection support
|
28
|
+
def self.camelize(term)
|
29
|
+
string = term.to_s
|
30
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
31
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
32
|
+
string.gsub!('/', '::')
|
33
|
+
string
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.underscore(term)
|
37
|
+
word = term.to_s.gsub('::', '/')
|
38
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
39
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
40
|
+
word.tr!("-", "_")
|
41
|
+
word.downcase!
|
42
|
+
word
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Invoker
|
2
|
+
module IPC
|
3
|
+
class BaseCommand
|
4
|
+
attr_accessor :client_socket
|
5
|
+
def initialize(client_socket)
|
6
|
+
@client_socket = client_socket
|
7
|
+
end
|
8
|
+
|
9
|
+
def send_data(message_object)
|
10
|
+
client_socket.write(message_object.encoded_message)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Invoke the command that actual processes incoming message
|
14
|
+
# returning true from this message means, command has been processed
|
15
|
+
# and client socket can be closed. returning false means, it is a
|
16
|
+
# long running command and socket should not be closed immediately
|
17
|
+
# @param [Invoker::IPC::Message] incoming message
|
18
|
+
# @return [Boolean] true or false
|
19
|
+
def run_command(message_object)
|
20
|
+
raise "Not implemented"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Invoker
|
2
|
+
module IPC
|
3
|
+
class ClientHandler
|
4
|
+
attr_accessor :client_socket
|
5
|
+
def initialize(client_socket)
|
6
|
+
@client_socket = client_socket
|
7
|
+
end
|
8
|
+
|
9
|
+
def read_and_execute
|
10
|
+
client_handler, message_object = read_incoming_command
|
11
|
+
client_socket.close if client_handler.run_command(message_object)
|
12
|
+
rescue StandardError => error
|
13
|
+
Invoker::Logger.puts error.message
|
14
|
+
Invoker::Logger.puts error.backtrace
|
15
|
+
client_socket.close
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def read_incoming_command
|
21
|
+
message_object = Invoker::IPC.message_from_io(client_socket)
|
22
|
+
[message_object.command_handler_klass.new(client_socket), message_object]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Invoker
|
2
|
+
module IPC
|
3
|
+
class DnsCheckCommand < BaseCommand
|
4
|
+
def run_command(message_object)
|
5
|
+
process_detail = Invoker.dns_cache[message_object.process_name]
|
6
|
+
|
7
|
+
dns_check_response = Invoker::IPC::Message::DnsCheckResponse.new(
|
8
|
+
process_name: message_object.process_name,
|
9
|
+
port: process_detail ? process_detail['port'] : nil
|
10
|
+
)
|
11
|
+
send_data(dns_check_response)
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module Invoker
|
2
|
+
module IPC
|
3
|
+
module Message
|
4
|
+
module Serialization
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def as_json
|
10
|
+
attributes.merge(type: message_type)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_json
|
14
|
+
JSON.generate(as_json)
|
15
|
+
end
|
16
|
+
|
17
|
+
def message_attributes
|
18
|
+
self.class.message_attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
def encoded_message
|
22
|
+
json_data = to_json
|
23
|
+
json_size = json_data.length.to_s
|
24
|
+
length_str = json_size.rjust(Invoker::IPC::INITIAL_PACKET_SIZE, '0')
|
25
|
+
length_str + json_data
|
26
|
+
end
|
27
|
+
|
28
|
+
def eql?(other)
|
29
|
+
other.class == self.class &&
|
30
|
+
compare_attributes(other)
|
31
|
+
end
|
32
|
+
|
33
|
+
def attributes
|
34
|
+
message_attribute_keys = message_attributes || []
|
35
|
+
message_attribute_keys.reduce({}) do |mem, obj|
|
36
|
+
value = send(obj)
|
37
|
+
if value.is_a?(Array)
|
38
|
+
mem[obj] = serialize_array(value)
|
39
|
+
elsif value.is_a?(Hash)
|
40
|
+
mem[obj] = serialize_hash(value)
|
41
|
+
else
|
42
|
+
mem[obj] = value.respond_to?(:as_json) ? value.as_json : encode_as_utf(value)
|
43
|
+
end
|
44
|
+
mem
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def compare_attributes(other)
|
51
|
+
message_attributes.all? do |attribute_name|
|
52
|
+
send(attribute_name).eql?(other.send(attribute_name))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def encode_as_utf(value)
|
57
|
+
return value unless value.is_a?(String)
|
58
|
+
value.encode("utf-8", invalid: :replace, undef: :replace, replace: '_')
|
59
|
+
end
|
60
|
+
|
61
|
+
def serialize_array(attribute_array)
|
62
|
+
attribute_array.map do |x|
|
63
|
+
x.respond_to?(:as_json) ? x.as_json : encode_as_utf(x)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def serialize_hash(attribute_hash)
|
68
|
+
attribute_hash.inject({}) do |temp_mem, (temp_key, temp_value)|
|
69
|
+
if temp_value.respond_to?(:as_json)
|
70
|
+
temp_mem[temp_key] = temp_value.as_json
|
71
|
+
else
|
72
|
+
temp_mem[temp_key] = encode_as_utf(temp_value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module ClassMethods
|
78
|
+
def message_attributes(*incoming_attributes)
|
79
|
+
if incoming_attributes.empty? && defined?(@message_attributes)
|
80
|
+
@message_attributes
|
81
|
+
else
|
82
|
+
@message_attributes ||= []
|
83
|
+
new_attributes = incoming_attributes.flatten
|
84
|
+
@message_attributes += new_attributes
|
85
|
+
attr_accessor *new_attributes
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Base
|
92
|
+
def initialize(options)
|
93
|
+
options.each do |key, value|
|
94
|
+
if self.respond_to?("#{key}=")
|
95
|
+
send("#{key}=", value)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def message_type
|
101
|
+
Invoker::IPC.underscore(self.class.name).split("/").last
|
102
|
+
end
|
103
|
+
|
104
|
+
def command_handler_klass
|
105
|
+
Invoker::IPC.const_get("#{IPC.camelize(message_type)}Command")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class Add < Base
|
110
|
+
include Serialization
|
111
|
+
message_attributes :process_name
|
112
|
+
end
|
113
|
+
|
114
|
+
class Tail < Base
|
115
|
+
include Serialization
|
116
|
+
message_attributes :process_names
|
117
|
+
end
|
118
|
+
|
119
|
+
class AddHttp < Base
|
120
|
+
include Serialization
|
121
|
+
message_attributes :process_name, :port
|
122
|
+
end
|
123
|
+
|
124
|
+
class Reload < Base
|
125
|
+
include Serialization
|
126
|
+
message_attributes :process_name, :signal
|
127
|
+
|
128
|
+
def remove_message
|
129
|
+
Remove.new(process_name: process_name, signal: signal)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class List < Base
|
134
|
+
include Serialization
|
135
|
+
end
|
136
|
+
|
137
|
+
class Process < Base
|
138
|
+
include Serialization
|
139
|
+
message_attributes :process_name, :shell_command, :dir, :pid
|
140
|
+
end
|
141
|
+
|
142
|
+
class Remove < Base
|
143
|
+
include Serialization
|
144
|
+
message_attributes :process_name, :signal
|
145
|
+
end
|
146
|
+
|
147
|
+
class DnsCheck < Base
|
148
|
+
include Serialization
|
149
|
+
message_attributes :process_name
|
150
|
+
end
|
151
|
+
|
152
|
+
class DnsCheckResponse < Base
|
153
|
+
include Serialization
|
154
|
+
message_attributes :process_name, :port
|
155
|
+
end
|
156
|
+
|
157
|
+
class Ping < Base
|
158
|
+
include Serialization
|
159
|
+
end
|
160
|
+
|
161
|
+
class Pong < Base
|
162
|
+
include Serialization
|
163
|
+
message_attributes :status
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
require "invoker/ipc/message/list_response"
|
170
|
+
require "invoker/ipc/message/tail_response"
|
@@ -0,0 +1,33 @@
|
|
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, :process_name => process.label,
|
20
|
+
:dir => process.dir
|
21
|
+
}
|
22
|
+
if worker = workers[process.label]
|
23
|
+
worker_attrs.update(pid: worker.pid)
|
24
|
+
end
|
25
|
+
process_array << worker_attrs
|
26
|
+
end
|
27
|
+
|
28
|
+
new(processes: process_array)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,31 +1,26 @@
|
|
1
1
|
require "fileutils"
|
2
2
|
|
3
3
|
module Invoker
|
4
|
-
module
|
4
|
+
module IPC
|
5
5
|
class Server
|
6
6
|
SOCKET_PATH = "/tmp/invoker"
|
7
7
|
def initialize
|
8
8
|
@open_clients = []
|
9
|
-
Socket.unix_server_loop(SOCKET_PATH)
|
10
|
-
|
11
|
-
|
12
|
-
ensure
|
13
|
-
sock.close
|
14
|
-
end
|
15
|
-
}
|
9
|
+
Socket.unix_server_loop(SOCKET_PATH) do |sock, client_addrinfo|
|
10
|
+
Thread.new { process_client(sock) }
|
11
|
+
end
|
16
12
|
end
|
17
13
|
|
18
14
|
def clean_old_socket
|
19
|
-
if File.
|
15
|
+
if File.exist?(SOCKET_PATH)
|
20
16
|
FileUtils.rm(SOCKET_PATH, :force => true)
|
21
17
|
end
|
22
18
|
end
|
23
19
|
|
24
20
|
def process_client(client_socket)
|
25
|
-
client = Invoker::
|
21
|
+
client = Invoker::IPC::ClientHandler.new(client_socket)
|
26
22
|
client.read_and_execute
|
27
23
|
end
|
28
24
|
end
|
29
25
|
end
|
30
|
-
|
31
26
|
end
|