invoker 0.0.3 → 0.1.1.pre
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.
- data/.travis.yml +1 -0
- data/invoker.gemspec +1 -0
- data/lib/invoker/command_listener/client.rb +18 -7
- data/lib/invoker/command_listener/server.rb +6 -6
- data/lib/invoker/command_worker.rb +6 -2
- data/lib/invoker/commander.rb +108 -26
- data/lib/invoker/event/manager.rb +79 -0
- data/lib/invoker/logger.rb +13 -0
- data/lib/invoker/parsers/config.rb +29 -0
- data/lib/invoker/parsers/option_parser.rb +86 -0
- data/lib/invoker/process_printer.rb +53 -0
- data/lib/invoker/reactor.rb +1 -6
- data/lib/invoker/runner.rb +29 -54
- data/lib/invoker/version.rb +1 -1
- data/lib/invoker.rb +15 -13
- data/readme.md +11 -3
- data/spec/invoker/command_listener/client_spec.rb +8 -13
- data/spec/invoker/command_worker_spec.rb +11 -0
- data/spec/invoker/commander_spec.rb +45 -3
- data/spec/invoker/config_spec.rb +1 -1
- data/spec/invoker/event/manager_spec.rb +72 -0
- data/spec/spec_helper.rb +1 -0
- metadata +63 -27
- checksums.yaml +0 -15
- data/lib/invoker/command_listener.rb +0 -3
- data/lib/invoker/config.rb +0 -28
data/.travis.yml
CHANGED
data/invoker.gemspec
CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.add_dependency("slop")
|
30
30
|
s.add_dependency("iniparse")
|
31
31
|
s.add_dependency("colored")
|
32
|
+
s.add_dependency("formatador")
|
32
33
|
s.add_development_dependency("bacon")
|
33
34
|
s.add_development_dependency("mocha")
|
34
35
|
s.add_development_dependency("mocha-on-bacon")
|
@@ -7,26 +7,37 @@ module Invoker
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def read_and_execute
|
10
|
-
command_info = client_socket.
|
10
|
+
command_info = client_socket.gets()
|
11
|
+
command_info && command_info.strip!
|
12
|
+
|
11
13
|
if command_info && !command_info.empty?
|
12
14
|
worker_command, command_label, rest_args = command_info.strip.split(" ")
|
13
|
-
|
15
|
+
worker_command.strip!
|
16
|
+
if worker_command
|
14
17
|
run_command(worker_command, command_label, rest_args)
|
15
18
|
end
|
16
19
|
end
|
17
|
-
client_socket.close()
|
18
20
|
end
|
19
21
|
|
20
22
|
def run_command(worker_command, command_label, rest_args = nil)
|
21
23
|
case worker_command
|
22
24
|
when 'add'
|
23
|
-
Invoker::COMMANDER.
|
25
|
+
Invoker::COMMANDER.on_next_tick(command_label) { |b_command_label|
|
26
|
+
add_command_by_label(b_command_label)
|
27
|
+
}
|
28
|
+
when 'list'
|
29
|
+
json = Invoker::COMMANDER.list_commands()
|
30
|
+
client_socket.puts(json)
|
24
31
|
when 'remove'
|
25
|
-
Invoker::COMMANDER.
|
32
|
+
Invoker::COMMANDER.on_next_tick(command_label, rest_args) { |b_command_label,b_rest_args|
|
33
|
+
remove_command(b_command_label, b_rest_args)
|
34
|
+
}
|
26
35
|
when 'reload'
|
27
|
-
Invoker::COMMANDER.
|
36
|
+
Invoker::COMMANDER.on_next_tick(command_label, rest_args) { |b_command_label, b_rest_args|
|
37
|
+
reload_command(b_command_label, b_rest_args)
|
38
|
+
}
|
28
39
|
else
|
29
|
-
|
40
|
+
Invoker::Logger.puts("\n Invalid command".red)
|
30
41
|
end
|
31
42
|
end
|
32
43
|
end
|
@@ -6,13 +6,13 @@ module Invoker
|
|
6
6
|
SOCKET_PATH = "/tmp/invoker"
|
7
7
|
def initialize
|
8
8
|
@open_clients = []
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
Socket.unix_server_loop(SOCKET_PATH) {|sock, client_addrinfo|
|
10
|
+
begin
|
11
|
+
process_client(sock)
|
12
|
+
ensure
|
13
|
+
sock.close
|
14
14
|
end
|
15
|
-
|
15
|
+
}
|
16
16
|
end
|
17
17
|
|
18
18
|
def clean_old_socket
|
@@ -19,12 +19,16 @@ module Invoker
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def unbind
|
22
|
-
|
22
|
+
Invoker::Logger.print(".")
|
23
23
|
end
|
24
24
|
|
25
25
|
# Print the lines received over the network
|
26
26
|
def receive_line(line)
|
27
|
-
|
27
|
+
Invoker::Logger.puts "#{@command_label.send(color)} : #{line}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h
|
31
|
+
{:command_label => command_label, :pid => pid.to_s}
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
data/lib/invoker/commander.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require "io/console"
|
2
2
|
require 'pty'
|
3
|
+
require "json"
|
3
4
|
|
4
5
|
module Invoker
|
5
6
|
class Commander
|
6
7
|
MAX_PROCESS_COUNT = 10
|
7
8
|
LABEL_COLORS = ['green', 'yellow', 'blue', 'magenta', 'cyan']
|
8
9
|
attr_accessor :reactor, :workers, :thread_group, :open_pipes
|
10
|
+
attr_accessor :event_manager, :runnables
|
9
11
|
|
10
12
|
def initialize
|
11
13
|
# mapping between open pipes and worker classes
|
@@ -16,10 +18,16 @@ module Invoker
|
|
16
18
|
|
17
19
|
@thread_group = ThreadGroup.new()
|
18
20
|
@worker_mutex = Mutex.new()
|
21
|
+
|
22
|
+
@event_manager = Invoker::Event::Manager.new()
|
23
|
+
@runnables = []
|
24
|
+
|
19
25
|
@reactor = Invoker::Reactor.new
|
20
26
|
Thread.abort_on_exception = true
|
21
27
|
end
|
22
28
|
|
29
|
+
# Start the invoker process supervisor. This method starts a unix server
|
30
|
+
# in separate thread that listens for incoming commands.
|
23
31
|
def start_manager
|
24
32
|
if !Invoker::CONFIG.processes || Invoker::CONFIG.processes.empty?
|
25
33
|
raise Invoker::Errors::InvalidConfig.new("No processes configured in config file")
|
@@ -28,9 +36,12 @@ module Invoker
|
|
28
36
|
unix_server_thread = Thread.new { Invoker::CommandListener::Server.new() }
|
29
37
|
thread_group.add(unix_server_thread)
|
30
38
|
Invoker::CONFIG.processes.each { |process_info| add_command(process_info) }
|
31
|
-
|
39
|
+
start_event_loop()
|
32
40
|
end
|
33
41
|
|
42
|
+
# Start given command and start a background thread to wait on the process
|
43
|
+
#
|
44
|
+
# @param process_info [OpenStruct(command, directory)]
|
34
45
|
def add_command(process_info)
|
35
46
|
m, s = PTY.open
|
36
47
|
s.raw! # disable newline conversion.
|
@@ -39,14 +50,20 @@ module Invoker
|
|
39
50
|
|
40
51
|
s.close()
|
41
52
|
|
42
|
-
|
43
|
-
LABEL_COLORS.push(selected_color)
|
44
|
-
worker = Invoker::CommandWorker.new(process_info.label, m, pid, selected_color)
|
53
|
+
worker = Invoker::CommandWorker.new(process_info.label, m, pid, select_color())
|
45
54
|
|
46
55
|
add_worker(worker)
|
47
56
|
wait_on_pid(process_info.label,pid)
|
48
57
|
end
|
49
58
|
|
59
|
+
# List currently running commands
|
60
|
+
def list_commands
|
61
|
+
Invoker::ProcessPrinter.to_json(workers)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Start executing given command by their label name.
|
65
|
+
#
|
66
|
+
# @param command_label [String] Command label of process specified in config file.
|
50
67
|
def add_command_by_label(command_label)
|
51
68
|
process_info = Invoker::CONFIG.processes.detect {|pconfig|
|
52
69
|
pconfig.label == command_label
|
@@ -56,30 +73,86 @@ module Invoker
|
|
56
73
|
end
|
57
74
|
end
|
58
75
|
|
59
|
-
|
60
|
-
|
61
|
-
|
76
|
+
# Reload a process given by command label
|
77
|
+
#
|
78
|
+
# @params command_label [String] Command label of process specified in config file.
|
79
|
+
def reload_command(command_label, rest_args)
|
80
|
+
if remove_command(command_label, rest_args)
|
81
|
+
event_manager.schedule_event(command_label, :worker_removed) {
|
82
|
+
add_command_by_label(command_label)
|
83
|
+
}
|
84
|
+
else
|
85
|
+
add_command_by_label(command_label)
|
86
|
+
end
|
62
87
|
end
|
63
88
|
|
89
|
+
# Remove a process from list of processes managed by invoker supervisor.It also
|
90
|
+
# kills the process before removing it from the list.
|
91
|
+
#
|
92
|
+
# @param command_label [String] Command label of process specified in config file
|
93
|
+
# @param rest_args [Array] Additional option arguments, such as signal that can be used.
|
94
|
+
# @return [Boolean] if process existed and was removed else false
|
64
95
|
def remove_command(command_label, rest_args)
|
65
96
|
worker = workers[command_label]
|
97
|
+
return false unless worker
|
66
98
|
signal_to_use = rest_args ? Array(rest_args).first : 'INT'
|
67
99
|
|
68
|
-
|
69
|
-
|
70
|
-
process_kill(worker.pid, signal_to_use)
|
71
|
-
end
|
100
|
+
Invoker::Logger.puts("Removing #{command_label} with signal #{signal_to_use}".red)
|
101
|
+
kill_or_remove_process(worker.pid, signal_to_use, command_label)
|
72
102
|
end
|
73
103
|
|
104
|
+
# Given a file descriptor returns the worker object
|
105
|
+
#
|
106
|
+
# @param fd [IO] an IO object with valid file descriptor
|
107
|
+
# @return [Invoker::CommandWorker] The worker object which is associated with this fd
|
74
108
|
def get_worker_from_fd(fd)
|
75
109
|
open_pipes[fd.fileno]
|
76
110
|
end
|
77
111
|
|
112
|
+
# Given a command label returns the associated worker object
|
113
|
+
#
|
114
|
+
# @param label [String] Command label of the command
|
115
|
+
# @return [Invoker::CommandWorker] The worker object which is associated with this command
|
78
116
|
def get_worker_from_label(label)
|
79
117
|
workers[label]
|
80
118
|
end
|
119
|
+
|
120
|
+
def on_next_tick(*args, &block)
|
121
|
+
@worker_mutex.synchronize do
|
122
|
+
@runnables << OpenStruct.new(:args => args, :block => block)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def run_runnables
|
127
|
+
@runnables.each do |runnable|
|
128
|
+
instance_exec(*runnable.args, &runnable.block)
|
129
|
+
end
|
130
|
+
@runnables = []
|
131
|
+
end
|
81
132
|
|
82
133
|
private
|
134
|
+
def start_event_loop
|
135
|
+
loop do
|
136
|
+
reactor.watch_on_pipe()
|
137
|
+
run_runnables()
|
138
|
+
run_scheduled_events()
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def run_scheduled_events
|
143
|
+
event_manager.run_scheduled_events do |event|
|
144
|
+
event.block.call()
|
145
|
+
end
|
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
|
+
remove_worker(command_label, false)
|
153
|
+
false
|
154
|
+
end
|
155
|
+
|
83
156
|
def process_kill(pid, signal_to_use)
|
84
157
|
if signal_to_use.to_i == 0
|
85
158
|
Process.kill(signal_to_use, pid)
|
@@ -87,29 +160,37 @@ module Invoker
|
|
87
160
|
Process.kill(signal_to_use.to_i, pid)
|
88
161
|
end
|
89
162
|
end
|
163
|
+
|
164
|
+
def select_color
|
165
|
+
selected_color = LABEL_COLORS.shift()
|
166
|
+
LABEL_COLORS.push(selected_color)
|
167
|
+
selected_color
|
168
|
+
end
|
90
169
|
|
91
170
|
# Remove worker from all collections
|
92
|
-
def remove_worker(command_label)
|
93
|
-
@
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
171
|
+
def remove_worker(command_label, trigger_event = true)
|
172
|
+
worker = @workers[command_label]
|
173
|
+
if worker
|
174
|
+
@open_pipes.delete(worker.pipe_end.fileno)
|
175
|
+
@workers.delete(command_label)
|
176
|
+
end
|
177
|
+
if trigger_event
|
178
|
+
event_manager.trigger(command_label, :worker_removed)
|
100
179
|
end
|
101
180
|
end
|
102
181
|
|
103
182
|
# add worker to global collections
|
104
183
|
def add_worker(worker)
|
105
|
-
@
|
106
|
-
|
107
|
-
|
108
|
-
@reactor.add_to_monitor(worker.pipe_end)
|
109
|
-
end
|
184
|
+
@open_pipes[worker.pipe_end.fileno] = worker
|
185
|
+
@workers[worker.command_label] = worker
|
186
|
+
@reactor.add_to_monitor(worker.pipe_end)
|
110
187
|
end
|
111
188
|
|
112
189
|
def run_command(process_info, write_pipe)
|
190
|
+
command_label = process_info.label
|
191
|
+
|
192
|
+
event_manager.schedule_event(command_label, :exit) { remove_worker(command_label) }
|
193
|
+
|
113
194
|
if defined?(Bundler)
|
114
195
|
Bundler.with_clean_env do
|
115
196
|
spawn(process_info.cmd,
|
@@ -125,12 +206,13 @@ module Invoker
|
|
125
206
|
|
126
207
|
def wait_on_pid(command_label,pid)
|
127
208
|
raise Invoker::Errors::ToomanyOpenConnections if @thread_group.enclosed?
|
209
|
+
|
128
210
|
thread = Thread.new do
|
129
211
|
Process.wait(pid)
|
130
212
|
message = "Process with command #{command_label} exited with status #{$?.exitstatus}"
|
131
|
-
|
213
|
+
Invoker::Logger.puts("\n#{message}".red)
|
132
214
|
notify_user(message)
|
133
|
-
|
215
|
+
event_manager.trigger(command_label, :exit)
|
134
216
|
end
|
135
217
|
@thread_group.add(thread)
|
136
218
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Invoker
|
2
|
+
module Event
|
3
|
+
class Manager
|
4
|
+
attr_accessor :scheduled_events, :triggered_events
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@scheduled_events = Hash.new {|h,k| h[k] = [] }
|
8
|
+
@triggered_events = []
|
9
|
+
@trigger_mutex = Mutex.new()
|
10
|
+
end
|
11
|
+
|
12
|
+
# Trigger an event. The event is not triggered immediately, but is just scheduled to be
|
13
|
+
# triggered.
|
14
|
+
#
|
15
|
+
# @param command_label [String] Command for which event should be triggered
|
16
|
+
# @param event_name [Symbol, nil] The optional event name
|
17
|
+
def trigger(command_label, event_name = nil)
|
18
|
+
@trigger_mutex.synchronize do
|
19
|
+
triggered_events << OpenStruct.new(
|
20
|
+
:command_label => command_label,
|
21
|
+
:event_name => event_name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Schedule an Event. The event will only trigger when a scheduled event matches
|
26
|
+
# a triggered event.
|
27
|
+
#
|
28
|
+
# @param command_label [String] Command for which the event should be triggered
|
29
|
+
# @param event_name [String, nil] Optional event name
|
30
|
+
# @param block The block to execute when event actually triggers
|
31
|
+
def schedule_event(command_label, event_name = nil, &block)
|
32
|
+
@trigger_mutex.synchronize do
|
33
|
+
scheduled_events[command_label] << OpenStruct.new(:event_name => event_name, :block => block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# On next iteration of event loop, this method is called and we try to match
|
38
|
+
# scheduled events with events that were triggered.
|
39
|
+
def run_scheduled_events
|
40
|
+
filtered_events_by_name_and_command = []
|
41
|
+
|
42
|
+
triggered_events.each_with_index do |triggered_event, index|
|
43
|
+
matched_events = scheduled_events[triggered_event.command_label]
|
44
|
+
if matched_events && !matched_events.empty?
|
45
|
+
filtered_events_by_name_and_command, unmatched_events =
|
46
|
+
filter_matched_events(matched_events, triggered_event)
|
47
|
+
triggered_events[index] = nil
|
48
|
+
remove_scheduled_event(unmatched_events, triggered_event.command_label)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
triggered_events.compact!
|
52
|
+
|
53
|
+
filtered_events_by_name_and_command.each {|event| yield event }
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def filter_matched_events(matched_events, event)
|
58
|
+
matched_filtered_events = []
|
59
|
+
|
60
|
+
matched_events.each_with_index do |matched_event, index|
|
61
|
+
if !event.event_name || event.event_name == matched_event.event_name
|
62
|
+
matched_filtered_events << matched_event
|
63
|
+
matched_events[index] = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
[matched_filtered_events, matched_events.compact]
|
67
|
+
end
|
68
|
+
|
69
|
+
def remove_scheduled_event(matched_events, command_label)
|
70
|
+
if !matched_events || matched_events.empty?
|
71
|
+
scheduled_events.delete(command_label)
|
72
|
+
else
|
73
|
+
scheduled_events[command_label] = matched_events
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'iniparse'
|
2
|
+
|
3
|
+
module Invoker
|
4
|
+
module Parsers
|
5
|
+
class Config
|
6
|
+
attr_accessor :processes
|
7
|
+
def initialize(filename)
|
8
|
+
@ini_content = File.read(filename)
|
9
|
+
@processes = process_ini(@ini_content)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def process_ini(ini_content)
|
14
|
+
document = IniParse.parse(ini_content)
|
15
|
+
document.map do |section|
|
16
|
+
check_directory(section["directory"])
|
17
|
+
OpenStruct.new(label: section.key, dir: section["directory"], cmd: section["command"])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def check_directory(app_dir)
|
22
|
+
if app_dir && !app_dir.empty? && !File.directory?(app_dir)
|
23
|
+
raise Invoker::Errors::InvalidConfig.new("Invalid directory #{app_dir}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "slop"
|
2
|
+
require "ostruct"
|
3
|
+
|
4
|
+
module Invoker
|
5
|
+
module Parsers
|
6
|
+
class OptionParser
|
7
|
+
def self.parse(args)
|
8
|
+
selected_command = nil
|
9
|
+
|
10
|
+
opts = Slop.parse(args, help: true) do
|
11
|
+
on :v, "Print the version" do
|
12
|
+
Invoker::Logger.puts Invoker::VERSION
|
13
|
+
end
|
14
|
+
|
15
|
+
command 'start' do
|
16
|
+
banner "Usage : invoker start config.ini \n Start Invoker Process Manager"
|
17
|
+
run do |cmd_opts, cmd_args|
|
18
|
+
selected_command = OpenStruct.new(:command => 'start', :file => cmd_args.first)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
command 'list' do
|
23
|
+
banner "Usage : invoker list\n List processes managed by invoker"
|
24
|
+
run do |cmd_opts, cmd_args|
|
25
|
+
selected_command = OpenStruct.new(:command => 'list')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
command 'add' do
|
30
|
+
banner "Usage : invoker add process_label \n Start the process with given process_label"
|
31
|
+
run do |cmd_opts, cmd_args|
|
32
|
+
selected_command = OpenStruct.new(:command => 'add', :command_key => cmd_args.first)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
command 'remove' do
|
37
|
+
banner "Usage : invoker remove process_label \n Stop the process with given label"
|
38
|
+
on :s, :signal=, "Signal to send for killing the process, default is SIGINT", as: String
|
39
|
+
|
40
|
+
run do |cmd_opts, cmd_args|
|
41
|
+
signal_to_use = cmd_opts.to_hash[:signal] || 'INT'
|
42
|
+
selected_command = OpenStruct.new(
|
43
|
+
:command => 'remove',
|
44
|
+
:command_key => cmd_args.first,
|
45
|
+
:signal => signal_to_use
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
command 'reload' do
|
51
|
+
banner "Usage : invoker reload process_label \n Restart the process with given label"
|
52
|
+
on :s, :signal=, "Signal to send for killing the process, default is SIGINT", as: String
|
53
|
+
|
54
|
+
run do |cmd_opts, cmd_args|
|
55
|
+
signal_to_use = cmd_opts.to_hash[:signal] || 'INT'
|
56
|
+
selected_command = OpenStruct.new(
|
57
|
+
:command => 'reload',
|
58
|
+
:command_key => cmd_args.first,
|
59
|
+
:signal => signal_to_use
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
selected_command || create_default_command(args, opts)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
# If user specifies no command either show help message or start the invoker
|
70
|
+
# process supervisor.
|
71
|
+
#
|
72
|
+
# @param args [Array] command line arguments
|
73
|
+
# @param opts [Slop::Options] Processed slop options
|
74
|
+
# @return [OpenStruct, false] returns default command or nil
|
75
|
+
def self.create_default_command(args, opts)
|
76
|
+
if args.first && File.exists?(args.first) && File.file?(args.first)
|
77
|
+
OpenStruct.new(:command => "start", :file => args.first)
|
78
|
+
else
|
79
|
+
Invoker::Logger.puts opts.inspect
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Invoker
|
2
|
+
class ProcessPrinter
|
3
|
+
MAX_COLUMN_WIDTH = 40
|
4
|
+
def self.to_json(workers)
|
5
|
+
final_json = []
|
6
|
+
Invoker::CONFIG.processes.each do |process|
|
7
|
+
if worker = workers[process.label]
|
8
|
+
final_json << {
|
9
|
+
:command => process.cmd, :command_label => process.label,
|
10
|
+
:dir => process.dir, :pid => worker.pid
|
11
|
+
}
|
12
|
+
else
|
13
|
+
final_json << {
|
14
|
+
:command => process.cmd, :command_label => process.label,
|
15
|
+
:dir => process.dir
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
final_json.to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.print_table(json_data)
|
24
|
+
final_json = JSON.parse(json_data)
|
25
|
+
if final_json && !final_json.empty?
|
26
|
+
json_for_printing = []
|
27
|
+
final_json.each do |json_row|
|
28
|
+
if json_row["pid"]
|
29
|
+
json_for_printing << colorize_hash(json_row, "green")
|
30
|
+
else
|
31
|
+
json_row["pid"] = "[light_black]not running[/]"
|
32
|
+
json_for_printing << colorize_hash(json_row, "light_black")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
Formatador.display_compact_table(json_for_printing)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def self.colorize_hash(hash, color)
|
41
|
+
hash.inject({}) do |mem,(key,obj)|
|
42
|
+
if obj.to_s.length > MAX_COLUMN_WIDTH
|
43
|
+
short_command = "#{obj.to_s[0..MAX_COLUMN_WIDTH]}.."
|
44
|
+
mem[key] = "[#{color}]#{short_command}[/]"
|
45
|
+
else
|
46
|
+
mem[key] = "[#{color}]#{obj}[/]"
|
47
|
+
end
|
48
|
+
mem
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/invoker/reactor.rb
CHANGED
@@ -13,12 +13,6 @@ module Invoker
|
|
13
13
|
@monitored_fds.delete(fd)
|
14
14
|
end
|
15
15
|
|
16
|
-
def start
|
17
|
-
loop do
|
18
|
-
watch_on_pipe
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
16
|
def watch_on_pipe
|
23
17
|
ready_read_fds,ready_write_fds,read_error_fds = select(monitored_fds,[],[],0.05)
|
24
18
|
|
@@ -38,6 +32,7 @@ module Invoker
|
|
38
32
|
data = read_data(ready_fd)
|
39
33
|
command_worker.receive_data(data)
|
40
34
|
rescue Invoker::Errors::ProcessTerminated
|
35
|
+
remove_from_monitoring(command_worker.pipe_end)
|
41
36
|
command_worker.unbind()
|
42
37
|
end
|
43
38
|
end
|
data/lib/invoker/runner.rb
CHANGED
@@ -5,45 +5,8 @@ require "socket"
|
|
5
5
|
module Invoker
|
6
6
|
class Runner
|
7
7
|
def self.run(args)
|
8
|
-
|
9
|
-
selected_command
|
10
|
-
|
11
|
-
opts = Slop.parse(args, help: true) do
|
12
|
-
on :v, "Print the version" do
|
13
|
-
$stdout.puts Invoker::VERSION
|
14
|
-
end
|
15
|
-
|
16
|
-
command 'start' do
|
17
|
-
banner "Usage : invoker start config.ini \n Start Invoker Process Manager"
|
18
|
-
run do |cmd_opts, cmd_args|
|
19
|
-
selected_command = OpenStruct.new(:command => 'start', :file => cmd_args.first)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
command 'add' do
|
24
|
-
banner "Usage : invoker add process_label \n Start the process with given process_label"
|
25
|
-
run do |cmd_opts, cmd_args|
|
26
|
-
selected_command = OpenStruct.new(:command => 'add', :command_key => cmd_args.first)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
command 'remove' do
|
31
|
-
banner "Usage : invoker remove process_label \n Stop the process with given label"
|
32
|
-
on :s, :signal=, "Signal to send for killing the process, default is SIGINT", as: String
|
33
|
-
|
34
|
-
run do |cmd_opts, cmd_args|
|
35
|
-
signal_to_use = cmd_opts.to_hash[:signal] || 'INT'
|
36
|
-
selected_command = OpenStruct.new(
|
37
|
-
:command => 'remove',
|
38
|
-
:command_key => cmd_args.first,
|
39
|
-
:signal => signal_to_use
|
40
|
-
)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
unless selected_command
|
45
|
-
$stdout.puts opts.inspect
|
46
|
-
else
|
8
|
+
selected_command = Invoker::Parsers::OptionParser.parse(args)
|
9
|
+
if selected_command
|
47
10
|
run_command(selected_command)
|
48
11
|
end
|
49
12
|
end
|
@@ -55,15 +18,19 @@ module Invoker
|
|
55
18
|
start_server(selected_command)
|
56
19
|
when 'add'
|
57
20
|
add_command(selected_command)
|
21
|
+
when 'reload'
|
22
|
+
refresh_command(selected_command)
|
23
|
+
when 'list'
|
24
|
+
list_commands(selected_command)
|
58
25
|
when 'remove'
|
59
26
|
remove_command(selected_command)
|
60
27
|
else
|
61
|
-
|
28
|
+
Invoker::Logger.puts "Invalid command"
|
62
29
|
end
|
63
30
|
end
|
64
31
|
|
65
32
|
def self.start_server(selected_command)
|
66
|
-
config = Invoker::Config.new(selected_command.file)
|
33
|
+
config = Invoker::Parsers::Config.new(selected_command.file)
|
67
34
|
Invoker.const_set(:CONFIG, config)
|
68
35
|
warn_about_terminal_notifier()
|
69
36
|
commander = Invoker::Commander.new()
|
@@ -72,31 +39,39 @@ module Invoker
|
|
72
39
|
end
|
73
40
|
|
74
41
|
def self.add_command(selected_command)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
42
|
+
Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) do |socket|
|
43
|
+
socket.puts("add #{selected_command.command_key}")
|
44
|
+
socket.flush()
|
45
|
+
end
|
79
46
|
end
|
80
47
|
|
81
48
|
def self.remove_command(selected_command)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
49
|
+
Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) do |socket|
|
50
|
+
socket.puts("remove #{selected_command.command_key} #{selected_command.signal}")
|
51
|
+
socket.flush()
|
52
|
+
end
|
86
53
|
end
|
87
54
|
|
88
55
|
def self.refresh_command(selected_command)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
56
|
+
Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) do |socket|
|
57
|
+
socket.puts("reload #{selected_command.command_key} #{selected_command.signal}")
|
58
|
+
socket.flush()
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.list_commands(selected_command)
|
63
|
+
Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) {|sock|
|
64
|
+
sock.puts("list")
|
65
|
+
data = sock.gets()
|
66
|
+
Invoker::ProcessPrinter.print_table(data)
|
67
|
+
}
|
93
68
|
end
|
94
69
|
|
95
70
|
def self.warn_about_terminal_notifier
|
96
71
|
if RUBY_PLATFORM.downcase.include?("darwin")
|
97
72
|
command_path = `which terminal-notifier`
|
98
73
|
if !command_path || command_path.empty?
|
99
|
-
|
74
|
+
Invoker::Logger.puts("You can enable OSX notification for processes by installing terminal-notifier gem".red)
|
100
75
|
end
|
101
76
|
end
|
102
77
|
end
|
data/lib/invoker/version.rb
CHANGED
data/lib/invoker.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
$: << File.dirname(__FILE__) unless $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
2
|
|
3
3
|
require "colored"
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
4
|
+
require "formatador"
|
5
|
+
require "ostruct"
|
6
|
+
require "invoker/version"
|
7
|
+
require "invoker/logger"
|
8
|
+
require "invoker/runner"
|
9
|
+
require "invoker/command_listener/server"
|
10
|
+
require "invoker/command_listener/client"
|
11
|
+
require "invoker/errors"
|
12
|
+
require "invoker/parsers/config"
|
13
|
+
require "invoker/parsers/option_parser"
|
14
|
+
require "invoker/commander"
|
15
|
+
require "invoker/command_worker"
|
16
|
+
require "invoker/reactor"
|
17
|
+
require "invoker/event/manager"
|
18
|
+
require "invoker/process_printer"
|
data/readme.md
CHANGED
@@ -22,7 +22,6 @@ You need to start by creating a `ini` file which will define processes you want
|
|
22
22
|
directory = /home/gnufied/god_particle
|
23
23
|
command = zsh -c 'bundle exec ruby script/delayed_job'
|
24
24
|
|
25
|
-
|
26
25
|
[events]
|
27
26
|
directory = /home/gnufied/god_particle
|
28
27
|
command = zsh -c 'bundle exec ruby script/event_server'
|
@@ -44,7 +43,16 @@ Now additionally you can control individual process by,
|
|
44
43
|
# add and start running
|
45
44
|
~> invoker add dj
|
46
45
|
|
47
|
-
|
46
|
+
# List currently running processes managed by invoker
|
47
|
+
~> invoker list
|
48
|
+
|
49
|
+
# Restart process given by command Label
|
50
|
+
~> invoker reload dj
|
51
|
+
|
52
|
+
# Restart process given by command label using specific signal for killing
|
53
|
+
~> invoker reload dj -s 9
|
54
|
+
|
55
|
+
You can also enable OSX notifications for crashed processes by installing `terminal-notifier` gem. It is not a dependency, but can be useful if something crashed and you weren't paying attention.
|
48
56
|
|
49
57
|
## Using with rbenv or rvm ##
|
50
58
|
|
@@ -58,7 +66,7 @@ If you are running `invoker` with Ruby version x, but your application requires
|
|
58
66
|
* Unless version of Ruby using which you are running `invoker` command and version of Ruby you are using in the application is same, you almost always will want to use
|
59
67
|
`zsh -c` or `bash -c`. `RVM` in particular requires a login shell and hence sometimes you may have to use `bash -lc`. For example:
|
60
68
|
|
61
|
-
command = bash -lc "rvm
|
69
|
+
command = bash -lc "rvm 2.0.0-p0 do bundle exec rails s"
|
62
70
|
|
63
71
|
|
64
72
|
## Bug reports and Feature requests
|
@@ -8,9 +8,8 @@ describe Invoker::CommandListener::Client do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should run if read from socket" do
|
11
|
-
invoker_commander.expects(:
|
12
|
-
@client_socket.expects(:
|
13
|
-
@client_socket.expects(:close)
|
11
|
+
invoker_commander.expects(:on_next_tick).with("foo")
|
12
|
+
@client_socket.expects(:gets).returns("add foo\n")
|
14
13
|
|
15
14
|
@client.read_and_execute()
|
16
15
|
end
|
@@ -23,17 +22,15 @@ describe Invoker::CommandListener::Client do
|
|
23
22
|
end
|
24
23
|
|
25
24
|
it "with specific signal" do
|
26
|
-
invoker_commander.expects(:
|
27
|
-
@client_socket.expects(:
|
28
|
-
@client_socket.expects(:close)
|
25
|
+
invoker_commander.expects(:on_next_tick).with("foo", "9")
|
26
|
+
@client_socket.expects(:gets).returns("remove foo 9\n")
|
29
27
|
|
30
28
|
@client.read_and_execute()
|
31
29
|
end
|
32
30
|
|
33
31
|
it "with default signal" do
|
34
|
-
invoker_commander.expects(:
|
35
|
-
@client_socket.expects(:
|
36
|
-
@client_socket.expects(:close)
|
32
|
+
invoker_commander.expects(:on_next_tick).with("foo",nil)
|
33
|
+
@client_socket.expects(:gets).returns("remove foo\n")
|
37
34
|
|
38
35
|
@client.read_and_execute()
|
39
36
|
end
|
@@ -46,10 +43,8 @@ describe Invoker::CommandListener::Client do
|
|
46
43
|
end
|
47
44
|
|
48
45
|
it "should print error if read from socket" do
|
49
|
-
invoker_commander.expects(:
|
50
|
-
|
51
|
-
@client_socket.expects(:read).returns("eugh foo\n")
|
52
|
-
@client_socket.expects(:close)
|
46
|
+
invoker_commander.expects(:on_next_tick).never()
|
47
|
+
@client_socket.expects(:gets).returns("eugh foo\n")
|
53
48
|
|
54
49
|
@client.read_and_execute
|
55
50
|
end
|
@@ -1,4 +1,15 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe "Command Worker" do
|
4
|
+
describe "converting workers hash to json" do
|
5
|
+
before do
|
6
|
+
@workers = {}
|
7
|
+
@workers["foo"] = Invoker::CommandWorker.new("foo", 89, 1023, "red")
|
8
|
+
@workers["bar"] = Invoker::CommandWorker.new("bar", 99, 1024, "blue")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should print json" do
|
12
|
+
@workers.values.map {|worker| worker.to_h }.to_json.should.not.be.empty
|
13
|
+
end
|
14
|
+
end
|
4
15
|
end
|
@@ -39,16 +39,27 @@ describe "Invoker::Commander" do
|
|
39
39
|
describe "if a signal is specified" do
|
40
40
|
it "should use that signal to kill the worker" do
|
41
41
|
@commander.expects(:process_kill).with("bogus", "HUP").returns(true)
|
42
|
-
@commander.remove_command("resque", "HUP")
|
42
|
+
@commander.remove_command("resque", "HUP").should.be.true
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
describe "if no signal is specified" do
|
47
47
|
it "should use INT signal" do
|
48
48
|
@commander.expects(:process_kill).with("bogus", "INT").returns(true)
|
49
|
-
@commander.remove_command("resque", nil)
|
49
|
+
@commander.remove_command("resque", nil).should.be.true
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
# describe "when a worker is not found" do
|
54
|
+
# before do
|
55
|
+
# @commander = Invoker::Commander.new()
|
56
|
+
# @commander.workers.expects(:[]).returns(OpenStruct.new(:pid => "bogus"))
|
57
|
+
# end
|
58
|
+
|
59
|
+
# it "should return false" do
|
60
|
+
|
61
|
+
# end
|
62
|
+
# end
|
52
63
|
end
|
53
64
|
|
54
65
|
describe "when no worker is found" do
|
@@ -69,10 +80,15 @@ describe "Invoker::Commander" do
|
|
69
80
|
before do
|
70
81
|
invoker_config.stubs(:processes).returns([OpenStruct.new(:label => "sleep", :cmd => "sleep 4", :dir => ENV['HOME'])])
|
71
82
|
@commander = Invoker::Commander.new()
|
83
|
+
Invoker.const_set(:COMMANDER, @commander)
|
84
|
+
end
|
85
|
+
|
86
|
+
after do
|
87
|
+
Invoker.send(:remove_const,:COMMANDER)
|
72
88
|
end
|
73
89
|
|
74
90
|
it "should populate workers and open_pipes" do
|
75
|
-
@commander.
|
91
|
+
@commander.expects(:start_event_loop)
|
76
92
|
@commander.start_manager()
|
77
93
|
@commander.open_pipes.should.not.be.empty
|
78
94
|
@commander.workers.should.not.be.empty
|
@@ -89,4 +105,30 @@ describe "Invoker::Commander" do
|
|
89
105
|
end
|
90
106
|
end
|
91
107
|
|
108
|
+
describe "#runnables" do
|
109
|
+
before do
|
110
|
+
@commander = Invoker::Commander.new()
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should run runnables in reactor tick with one argument" do
|
114
|
+
@commander.on_next_tick("foo") { |cmd| add_command_by_label(cmd) }
|
115
|
+
@commander.expects(:add_command_by_label).returns(true)
|
116
|
+
@commander.run_runnables()
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should run runnables with multiple args" do
|
120
|
+
@commander.on_next_tick("foo", "bar", "baz") { |t1,*rest|
|
121
|
+
remove_command(t1, rest)
|
122
|
+
}
|
123
|
+
@commander.expects(:remove_command).with("foo", ["bar", "baz"]).returns(true)
|
124
|
+
@commander.run_runnables()
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should run runnable with no args" do
|
128
|
+
@commander.on_next_tick() { hello() }
|
129
|
+
@commander.expects(:hello).returns(true)
|
130
|
+
@commander.run_runnables()
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
92
134
|
end
|
data/spec/invoker/config_spec.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Invoker::Event::Manager do
|
4
|
+
describe "Run scheduled events" do
|
5
|
+
before do
|
6
|
+
@event_manager = Invoker::Event::Manager.new()
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should run matched events" do
|
10
|
+
@event_manager.schedule_event("foo", :exit) { 'exit foo' }
|
11
|
+
@event_manager.trigger("foo", :exit)
|
12
|
+
|
13
|
+
@event_manager.run_scheduled_events do |event|
|
14
|
+
event.block.call.should.equal("exit foo")
|
15
|
+
end
|
16
|
+
|
17
|
+
@event_manager.scheduled_events.should.be.empty
|
18
|
+
@event_manager.triggered_events.should.be.empty
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should remove triggrered and scheduld events on run" do
|
22
|
+
@event_manager.schedule_event("foo", :exit) { 'exit foo' }
|
23
|
+
@event_manager.schedule_event("bar", :entry) { "entry bar"}
|
24
|
+
@event_manager.trigger("foo", :exit)
|
25
|
+
@event_manager.trigger("baz", :exit)
|
26
|
+
|
27
|
+
@event_manager.run_scheduled_events do |event|
|
28
|
+
event.block.call.should.equal("exit foo")
|
29
|
+
end
|
30
|
+
|
31
|
+
@event_manager.scheduled_events.should.not.be.empty
|
32
|
+
@event_manager.triggered_events.should.not.be.empty
|
33
|
+
|
34
|
+
baz_containing_event = lambda do |events|
|
35
|
+
events.detect {|event| event.command_label == "baz" }
|
36
|
+
end
|
37
|
+
|
38
|
+
bar_containing_scheduled_event = lambda do |events|
|
39
|
+
events.keys.detect {|event_key| event_key == "bar" }
|
40
|
+
end
|
41
|
+
|
42
|
+
@event_manager.triggered_events.should.be.a baz_containing_event
|
43
|
+
@event_manager.scheduled_events.should.be.a bar_containing_scheduled_event
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should handle multiple events for same command" do
|
47
|
+
@event_manager.schedule_event("foo", :exit) { 'exit foo' }
|
48
|
+
@event_manager.schedule_event("foo", :entry) { "entry bar"}
|
49
|
+
@event_manager.trigger("foo", :exit)
|
50
|
+
|
51
|
+
@event_manager.run_scheduled_events { |event| }
|
52
|
+
|
53
|
+
|
54
|
+
@event_manager.schedule_event("foo", :exit) { 'exit foo' }
|
55
|
+
@event_manager.trigger("foo", :exit)
|
56
|
+
|
57
|
+
@event_manager.scheduled_events.should.not.be.empty
|
58
|
+
@event_manager.triggered_events.should.not.be.empty
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not run unmatched events" do
|
62
|
+
@event_manager.schedule_event("bar", :entry) { "entry bar"}
|
63
|
+
@event_manager.trigger("foo", :exit)
|
64
|
+
|
65
|
+
events_ran = false
|
66
|
+
@event_manager.run_scheduled_events do |event|
|
67
|
+
events_ran = true
|
68
|
+
end
|
69
|
+
events_ran.should.be.false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: invoker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1.pre
|
5
|
+
prerelease: 6
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Hemant Kumar
|
@@ -11,99 +12,129 @@ cert_chain: []
|
|
11
12
|
date: 2013-05-04 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
|
-
|
15
|
+
name: slop
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
15
18
|
requirements:
|
16
19
|
- - ! '>='
|
17
20
|
- !ruby/object:Gem::Version
|
18
21
|
version: '0'
|
19
22
|
type: :runtime
|
20
23
|
prerelease: false
|
21
|
-
|
22
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
23
26
|
requirements:
|
24
27
|
- - ! '>='
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '0'
|
27
30
|
- !ruby/object:Gem::Dependency
|
28
|
-
|
31
|
+
name: iniparse
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
31
36
|
- !ruby/object:Gem::Version
|
32
37
|
version: '0'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
|
36
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
37
42
|
requirements:
|
38
43
|
- - ! '>='
|
39
44
|
- !ruby/object:Gem::Version
|
40
45
|
version: '0'
|
41
46
|
- !ruby/object:Gem::Dependency
|
42
|
-
|
47
|
+
name: colored
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
43
50
|
requirements:
|
44
51
|
- - ! '>='
|
45
52
|
- !ruby/object:Gem::Version
|
46
53
|
version: '0'
|
47
54
|
type: :runtime
|
48
55
|
prerelease: false
|
49
|
-
|
50
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
51
58
|
requirements:
|
52
59
|
- - ! '>='
|
53
60
|
- !ruby/object:Gem::Version
|
54
61
|
version: '0'
|
55
62
|
- !ruby/object:Gem::Dependency
|
56
|
-
|
63
|
+
name: formatador
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
57
66
|
requirements:
|
58
67
|
- - ! '>='
|
59
68
|
- !ruby/object:Gem::Version
|
60
69
|
version: '0'
|
61
|
-
type: :
|
70
|
+
type: :runtime
|
62
71
|
prerelease: false
|
63
|
-
|
64
|
-
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
65
74
|
requirements:
|
66
75
|
- - ! '>='
|
67
76
|
- !ruby/object:Gem::Version
|
68
77
|
version: '0'
|
69
78
|
- !ruby/object:Gem::Dependency
|
70
|
-
|
79
|
+
name: bacon
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
71
82
|
requirements:
|
72
83
|
- - ! '>='
|
73
84
|
- !ruby/object:Gem::Version
|
74
85
|
version: '0'
|
75
86
|
type: :development
|
76
87
|
prerelease: false
|
77
|
-
|
78
|
-
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
79
90
|
requirements:
|
80
91
|
- - ! '>='
|
81
92
|
- !ruby/object:Gem::Version
|
82
93
|
version: '0'
|
83
94
|
- !ruby/object:Gem::Dependency
|
84
|
-
|
95
|
+
name: mocha
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
85
98
|
requirements:
|
86
99
|
- - ! '>='
|
87
100
|
- !ruby/object:Gem::Version
|
88
101
|
version: '0'
|
89
102
|
type: :development
|
90
103
|
prerelease: false
|
91
|
-
|
92
|
-
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
93
106
|
requirements:
|
94
107
|
- - ! '>='
|
95
108
|
- !ruby/object:Gem::Version
|
96
109
|
version: '0'
|
97
110
|
- !ruby/object:Gem::Dependency
|
98
|
-
|
111
|
+
name: mocha-on-bacon
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
99
114
|
requirements:
|
100
115
|
- - ! '>='
|
101
116
|
- !ruby/object:Gem::Version
|
102
117
|
version: '0'
|
103
118
|
type: :development
|
104
119
|
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
105
127
|
name: rake
|
106
128
|
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
107
138
|
requirements:
|
108
139
|
- - ! '>='
|
109
140
|
- !ruby/object:Gem::Version
|
@@ -123,13 +154,16 @@ files:
|
|
123
154
|
- bin/invoker
|
124
155
|
- invoker.gemspec
|
125
156
|
- lib/invoker.rb
|
126
|
-
- lib/invoker/command_listener.rb
|
127
157
|
- lib/invoker/command_listener/client.rb
|
128
158
|
- lib/invoker/command_listener/server.rb
|
129
159
|
- lib/invoker/command_worker.rb
|
130
160
|
- lib/invoker/commander.rb
|
131
|
-
- lib/invoker/config.rb
|
132
161
|
- lib/invoker/errors.rb
|
162
|
+
- lib/invoker/event/manager.rb
|
163
|
+
- lib/invoker/logger.rb
|
164
|
+
- lib/invoker/parsers/config.rb
|
165
|
+
- lib/invoker/parsers/option_parser.rb
|
166
|
+
- lib/invoker/process_printer.rb
|
133
167
|
- lib/invoker/reactor.rb
|
134
168
|
- lib/invoker/runner.rb
|
135
169
|
- lib/invoker/version.rb
|
@@ -138,35 +172,37 @@ files:
|
|
138
172
|
- spec/invoker/command_worker_spec.rb
|
139
173
|
- spec/invoker/commander_spec.rb
|
140
174
|
- spec/invoker/config_spec.rb
|
175
|
+
- spec/invoker/event/manager_spec.rb
|
141
176
|
- spec/spec_helper.rb
|
142
177
|
homepage: http://github.com/code-mancers/invoker
|
143
178
|
licenses:
|
144
179
|
- MIT
|
145
|
-
metadata: {}
|
146
180
|
post_install_message:
|
147
181
|
rdoc_options: []
|
148
182
|
require_paths:
|
149
183
|
- lib
|
150
184
|
required_ruby_version: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
151
186
|
requirements:
|
152
187
|
- - ! '>='
|
153
188
|
- !ruby/object:Gem::Version
|
154
189
|
version: '0'
|
155
190
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
+
none: false
|
156
192
|
requirements:
|
157
193
|
- - ! '>='
|
158
194
|
- !ruby/object:Gem::Version
|
159
195
|
version: '0'
|
160
196
|
requirements: []
|
161
197
|
rubyforge_project:
|
162
|
-
rubygems_version:
|
198
|
+
rubygems_version: 1.8.23
|
163
199
|
signing_key:
|
164
|
-
specification_version:
|
200
|
+
specification_version: 3
|
165
201
|
summary: Something small for Process management
|
166
202
|
test_files:
|
167
203
|
- spec/invoker/command_listener/client_spec.rb
|
168
204
|
- spec/invoker/command_worker_spec.rb
|
169
205
|
- spec/invoker/commander_spec.rb
|
170
206
|
- spec/invoker/config_spec.rb
|
207
|
+
- spec/invoker/event/manager_spec.rb
|
171
208
|
- spec/spec_helper.rb
|
172
|
-
has_rdoc:
|
checksums.yaml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
---
|
2
|
-
!binary "U0hBMQ==":
|
3
|
-
metadata.gz: !binary |-
|
4
|
-
MGY0NmQ0YjY1MzZiOWNiYjJkNzE3NjI1YTQxOTI3YTEwMGQxM2FhZQ==
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NGNiZDNlMTgxZjliOWVmMTdlNjI0YmFkNjUyODcwNjViNGQ5MTJlMA==
|
7
|
-
!binary "U0hBNTEy":
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
MGQxYzdhY2MxYWRhMjExM2NlNDdiYTM0OTc1YjAxZmIyYTFjMjM2YWJiMmUx
|
10
|
-
NTE3OWFiMWNiZDEwOGE0NTA2ZmUyOGI1NGYwYTAxYzYwNjM2ZTQzMzU2ODkw
|
11
|
-
Yjc5MGQxZDg4ZmQ2MTRlYWJjMTE3ZDkwODIzODYxOTkyYjM2ZmE=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
NmIxY2Q3NzkzM2Y0OTYxMTk4ZmJmZTIxMmI2NGRjZTkyZjdkOGU2NGZiZjhh
|
14
|
-
YmNlZDZmODdmYjE4OWIxMzY5ZDUyNDhjODU2NmIwYTA0ZjQ5YmQxNzQxZTM2
|
15
|
-
MjUyMjI1MTAyYTZlZGVjZjRkYmEwMjRjOTgwNWE4ZjBmYjIwYTE=
|
data/lib/invoker/config.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require "yaml"
|
2
|
-
require 'iniparse'
|
3
|
-
|
4
|
-
module Invoker
|
5
|
-
class Config
|
6
|
-
attr_accessor :processes
|
7
|
-
def initialize(filename)
|
8
|
-
@ini_content = File.read(filename)
|
9
|
-
@processes = process_ini(@ini_content)
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
def process_ini(ini_content)
|
14
|
-
document = IniParse.parse(ini_content)
|
15
|
-
document.map do |section|
|
16
|
-
check_directory(section["directory"])
|
17
|
-
OpenStruct.new(label: section.key, dir: section["directory"], cmd: section["command"])
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def check_directory(app_dir)
|
22
|
-
if app_dir && !app_dir.empty? && !File.directory?(app_dir)
|
23
|
-
raise Invoker::Errors::InvalidConfig.new("Invalid directory #{app_dir}")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|