zeus 0.4.6 → 0.10.0.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/Gemfile +0 -4
- data/Rakefile +52 -8
- data/bin/zeus +14 -6
- data/build/fsevents-wrapper +0 -0
- data/build/zeus-darwin-amd64 +0 -0
- data/build/zeus-linux-386 +0 -0
- data/build/zeus-linux-amd64 +0 -0
- data/examples/zeus.json +22 -0
- data/ext/fsevents-wrapper/fsevents-wrapper +0 -0
- data/ext/inotify-wrapper/extconf.rb +24 -0
- data/ext/inotify-wrapper/inotify-wrapper.cpp +86 -0
- data/lib/zeus.rb +123 -35
- data/lib/zeus/{server/load_tracking.rb → load_tracking.rb} +1 -3
- data/lib/zeus/rails.rb +141 -0
- data/man/build/zeus +49 -0
- data/man/build/zeus-init +13 -0
- data/man/build/zeus-init.txt +17 -0
- data/man/build/zeus-start +16 -0
- data/man/build/zeus-start.txt +18 -0
- data/man/build/zeus.txt +50 -0
- data/zeus.gemspec +17 -6
- metadata +27 -58
- data/.gitignore +0 -17
- data/.travis.yml +0 -5
- data/.zeus.rb +0 -11
- data/README.md +0 -73
- data/docs/acceptor_registration.md +0 -14
- data/docs/client_server_handshake.md +0 -25
- data/ext/fsevents-wrapper/main.m +0 -118
- data/lib/thrud.rb +0 -97
- data/lib/zeus/cli.rb +0 -80
- data/lib/zeus/client.rb +0 -114
- data/lib/zeus/client/winsize.rb +0 -28
- data/lib/zeus/error_printer.rb +0 -16
- data/lib/zeus/plan.rb +0 -18
- data/lib/zeus/plan/acceptor.rb +0 -38
- data/lib/zeus/plan/node.rb +0 -66
- data/lib/zeus/plan/stage.rb +0 -50
- data/lib/zeus/server.rb +0 -103
- data/lib/zeus/server/acceptor.rb +0 -79
- data/lib/zeus/server/acceptor_registration_monitor.rb +0 -75
- data/lib/zeus/server/client_handler.rb +0 -106
- data/lib/zeus/server/command_runner.rb +0 -70
- data/lib/zeus/server/file_monitor.rb +0 -8
- data/lib/zeus/server/file_monitor/fsevent.rb +0 -102
- data/lib/zeus/server/process_tree_monitor.rb +0 -89
- data/lib/zeus/server/stage.rb +0 -88
- data/lib/zeus/server/stage/error_state.rb +0 -42
- data/lib/zeus/server/stage/feature_notifier.rb +0 -38
- data/lib/zeus/templates/rails.rb +0 -133
- data/lib/zeus/ui.rb +0 -57
- data/lib/zeus/version.rb +0 -3
- data/spec/cli_spec.rb +0 -95
- data/spec/error_printer_spec.rb +0 -27
- data/spec/integration_spec.rb +0 -106
- data/spec/server/file_monitor/fsevent_spec.rb +0 -88
- data/spec/server/load_tracking_spec.rb +0 -67
- data/spec/server/process_tree_monitor_spec.rb +0 -50
- data/spec/spec_helper.rb +0 -38
- data/spec/ui_spec.rb +0 -54
data/lib/zeus/error_printer.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module Zeus
|
2
|
-
class ErrorPrinter
|
3
|
-
attr_reader :error
|
4
|
-
def initialize(error)
|
5
|
-
@error = error
|
6
|
-
end
|
7
|
-
|
8
|
-
def write_to(io)
|
9
|
-
io.puts "#{error.backtrace[0]}: #{error.message} (#{error.class})"
|
10
|
-
error.backtrace[1..-1].each do |line|
|
11
|
-
io.puts "\tfrom #{line}"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
data/lib/zeus/plan.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
require 'zeus/plan/node'
|
4
|
-
require 'zeus/plan/stage'
|
5
|
-
require 'zeus/plan/acceptor'
|
6
|
-
|
7
|
-
module Zeus
|
8
|
-
module Plan
|
9
|
-
class Evaluator
|
10
|
-
def stage(name, &b)
|
11
|
-
stage = Plan::Stage.new(name)
|
12
|
-
stage.root = true
|
13
|
-
stage.instance_eval(&b)
|
14
|
-
stage
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
data/lib/zeus/plan/acceptor.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
module Zeus
|
2
|
-
module Plan
|
3
|
-
|
4
|
-
class Acceptor < Node
|
5
|
-
|
6
|
-
attr_reader :name, :aliases, :description, :action
|
7
|
-
def initialize(name, aliases, description, &b)
|
8
|
-
super(name)
|
9
|
-
@description = description
|
10
|
-
@aliases = aliases
|
11
|
-
@action = b
|
12
|
-
end
|
13
|
-
|
14
|
-
# ^ configuration
|
15
|
-
# V later use
|
16
|
-
|
17
|
-
def commands
|
18
|
-
[name, *aliases].map(&:to_s)
|
19
|
-
end
|
20
|
-
|
21
|
-
def acceptors
|
22
|
-
self
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_process_object(server)
|
26
|
-
Zeus::Server::Acceptor.new(server).tap do |stage|
|
27
|
-
stage.name = @name
|
28
|
-
stage.aliases = @aliases
|
29
|
-
stage.action = @action
|
30
|
-
stage.description = @description
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
data/lib/zeus/plan/node.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
module Zeus
|
2
|
-
module Plan
|
3
|
-
|
4
|
-
class Node
|
5
|
-
attr_reader :name, :stages, :features
|
6
|
-
attr_accessor :pid, :root
|
7
|
-
|
8
|
-
def initialize(name)
|
9
|
-
@name = name
|
10
|
-
@stages = []
|
11
|
-
@features = Set.new # hash might be faster than ruby's inane impl of set.
|
12
|
-
end
|
13
|
-
|
14
|
-
def stage_has_feature(name, file)
|
15
|
-
node_for_name(name).features << file
|
16
|
-
end
|
17
|
-
|
18
|
-
def stage_has_pid(name, pid)
|
19
|
-
node_for_name(name).pid = pid
|
20
|
-
end
|
21
|
-
|
22
|
-
def kill_nodes_with_feature(file)
|
23
|
-
if features.include?(file)
|
24
|
-
if root
|
25
|
-
Zeus.ui.error "One of zeus's dependencies changed. Not killing zeus. You may have to restart the server."
|
26
|
-
else
|
27
|
-
kill!
|
28
|
-
end
|
29
|
-
else
|
30
|
-
stages.each do |child|
|
31
|
-
child.kill_nodes_with_feature(file)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# We send STOP before actually killing the processes here.
|
37
|
-
# This is to prevent parents from respawning before all the children
|
38
|
-
# are killed. This prevents a race condition.
|
39
|
-
def kill!
|
40
|
-
Process.kill("STOP", pid) if pid
|
41
|
-
# Protected methods don't work with each(&:m) notation.
|
42
|
-
stages.each { |stage| stage.kill! }
|
43
|
-
old_pid = pid
|
44
|
-
self.pid = nil
|
45
|
-
Process.kill("KILL", old_pid) if old_pid
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def node_for_name(name)
|
51
|
-
@nodes_by_name ||= __nodes_by_name
|
52
|
-
@nodes_by_name[name]
|
53
|
-
end
|
54
|
-
|
55
|
-
def __nodes_by_name
|
56
|
-
nodes = {name => self}
|
57
|
-
stages.each do |child|
|
58
|
-
nodes.merge!(child.__nodes_by_name)
|
59
|
-
end
|
60
|
-
nodes
|
61
|
-
end ; protected :__nodes_by_name
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|
data/lib/zeus/plan/stage.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
module Zeus
|
2
|
-
module Plan
|
3
|
-
|
4
|
-
class Stage < Node
|
5
|
-
|
6
|
-
attr_reader :actions
|
7
|
-
def initialize(name)
|
8
|
-
super(name)
|
9
|
-
@actions = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def action(&b)
|
13
|
-
@actions << b
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
def desc(desc)
|
18
|
-
@desc = desc
|
19
|
-
end
|
20
|
-
|
21
|
-
def stage(name, &b)
|
22
|
-
@stages << Plan::Stage.new(name).tap { |s| s.instance_eval(&b) }
|
23
|
-
self
|
24
|
-
end
|
25
|
-
|
26
|
-
def command(name, *aliases, &b)
|
27
|
-
@stages << Plan::Acceptor.new(name, aliases, @desc, &b)
|
28
|
-
@desc = nil
|
29
|
-
self
|
30
|
-
end
|
31
|
-
|
32
|
-
# ^ configuration
|
33
|
-
# V later use
|
34
|
-
|
35
|
-
def acceptors
|
36
|
-
stages.map(&:acceptors).flatten
|
37
|
-
end
|
38
|
-
|
39
|
-
def to_process_object(server)
|
40
|
-
Zeus::Server::Stage.new(server).tap do |stage|
|
41
|
-
stage.name = @name
|
42
|
-
stage.stages = @stages.map { |stage| stage.to_process_object(server) }
|
43
|
-
stage.actions = @actions
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
data/lib/zeus/server.rb
DELETED
@@ -1,103 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'socket'
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
require 'zeus/server/stage'
|
6
|
-
require 'zeus/server/acceptor'
|
7
|
-
require 'zeus/server/file_monitor'
|
8
|
-
require 'zeus/server/load_tracking'
|
9
|
-
require 'zeus/server/client_handler'
|
10
|
-
require 'zeus/server/command_runner'
|
11
|
-
require 'zeus/server/process_tree_monitor'
|
12
|
-
require 'zeus/server/acceptor_registration_monitor'
|
13
|
-
|
14
|
-
module Zeus
|
15
|
-
class Server
|
16
|
-
extend Forwardable
|
17
|
-
|
18
|
-
def self.define!(&b)
|
19
|
-
@@definition = Zeus::Plan::Evaluator.new.instance_eval(&b)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.acceptors
|
23
|
-
defined?(@@definition) ? @@definition.acceptors : []
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize
|
27
|
-
@file_monitor = FileMonitor::FSEvent.new(&method(:dependency_did_change))
|
28
|
-
@acceptor_registration_monitor = AcceptorRegistrationMonitor.new
|
29
|
-
@process_tree_monitor = ProcessTreeMonitor.new(@file_monitor, @@definition)
|
30
|
-
@client_handler = ClientHandler.new(acceptor_commands, self)
|
31
|
-
|
32
|
-
@plan = @@definition.to_process_object(self)
|
33
|
-
end
|
34
|
-
|
35
|
-
def dependency_did_change(file)
|
36
|
-
@process_tree_monitor.kill_nodes_with_feature(file)
|
37
|
-
end
|
38
|
-
|
39
|
-
def monitors
|
40
|
-
[@file_monitor, @process_tree_monitor, @acceptor_registration_monitor, @client_handler]
|
41
|
-
end
|
42
|
-
|
43
|
-
def run
|
44
|
-
$0 = "zeus master"
|
45
|
-
trap("TERM") { exit 0 }
|
46
|
-
trap("INT") { puts "\n\x1b[31mExiting\x1b[0m" ; exit }
|
47
|
-
LoadTracking.server = self
|
48
|
-
|
49
|
-
@plan.run(true) # boot the actual app
|
50
|
-
master = Process.pid
|
51
|
-
at_exit { cleanup_all_children if Process.pid == master }
|
52
|
-
monitors.each(&:close_child_socket)
|
53
|
-
|
54
|
-
runloop!
|
55
|
-
ensure
|
56
|
-
File.unlink(Zeus::SOCKET_NAME)
|
57
|
-
end
|
58
|
-
|
59
|
-
def cleanup_all_children
|
60
|
-
@process_tree_monitor.kill_all_nodes
|
61
|
-
@file_monitor.kill_wrapper
|
62
|
-
end
|
63
|
-
|
64
|
-
def add_extra_feature(full_expanded_path)
|
65
|
-
$extra_loaded_features ||= []
|
66
|
-
$extra_loaded_features << full_expanded_path
|
67
|
-
end
|
68
|
-
|
69
|
-
def extra_features
|
70
|
-
$extra_loaded_features || []
|
71
|
-
end
|
72
|
-
|
73
|
-
# Child process API
|
74
|
-
def __CHILD__close_parent_sockets
|
75
|
-
monitors.each(&:close_parent_socket)
|
76
|
-
end
|
77
|
-
|
78
|
-
def_delegators :@acceptor_registration_monitor,
|
79
|
-
:__CHILD__register_acceptor,
|
80
|
-
:__CHILD__find_acceptor_for_command
|
81
|
-
|
82
|
-
def_delegators :@process_tree_monitor,
|
83
|
-
:__CHILD__stage_starting_with_pid,
|
84
|
-
:__CHILD__stage_has_feature
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def acceptor_commands
|
89
|
-
self.class.acceptors.map(&:commands).flatten
|
90
|
-
end
|
91
|
-
|
92
|
-
def runloop!
|
93
|
-
loop do
|
94
|
-
ready, = IO.select(monitors.map(&:datasource), [], [], 1)
|
95
|
-
next unless ready
|
96
|
-
monitors.each do |m|
|
97
|
-
m.on_datasource_event if ready.include?(m.datasource)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
end
|
103
|
-
end
|
data/lib/zeus/server/acceptor.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'socket'
|
3
|
-
|
4
|
-
module Zeus
|
5
|
-
class Server
|
6
|
-
class Acceptor
|
7
|
-
attr_accessor :aliases, :description, :action, :name
|
8
|
-
def initialize(server)
|
9
|
-
@server = server
|
10
|
-
end
|
11
|
-
|
12
|
-
def descendent_acceptors
|
13
|
-
self
|
14
|
-
end
|
15
|
-
|
16
|
-
def run
|
17
|
-
register_with_client_handler(Process.pid)
|
18
|
-
Zeus.ui.info("starting #{process_type} `#{@name}`")
|
19
|
-
Zeus.thread_with_backtrace_on_error { runloop! }
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def command_runner
|
25
|
-
CommandRunner.new(name, action, @s_acceptor)
|
26
|
-
end
|
27
|
-
|
28
|
-
def register_with_client_handler(pid)
|
29
|
-
@s_client_handler, @s_acceptor = UNIXSocket.pair
|
30
|
-
@s_acceptor.puts registration_data(pid)
|
31
|
-
at_exit { @s_client_handler.close ; @s_acceptor.close }
|
32
|
-
@server.__CHILD__register_acceptor(@s_client_handler)
|
33
|
-
end
|
34
|
-
|
35
|
-
def registration_data(pid)
|
36
|
-
{type: 'registration', pid: pid, commands: [name, *aliases], description: description}.to_json
|
37
|
-
end
|
38
|
-
|
39
|
-
def accept_connection
|
40
|
-
terminal = @s_acceptor.recv_io # blocking
|
41
|
-
exit_status_socket = @s_acceptor.recv_io
|
42
|
-
arguments = JSON.parse(@s_acceptor.readline.chomp)
|
43
|
-
|
44
|
-
[terminal, exit_status_socket, arguments]
|
45
|
-
end
|
46
|
-
|
47
|
-
def process_type
|
48
|
-
"acceptor"
|
49
|
-
end
|
50
|
-
|
51
|
-
def runloop!
|
52
|
-
loop do
|
53
|
-
terminal, exit_status_socket, arguments = accept_connection # blocking
|
54
|
-
command_runner.run(terminal, exit_status_socket, arguments)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
module ErrorState
|
59
|
-
NOT_A_PID = 0
|
60
|
-
attr_accessor :error
|
61
|
-
|
62
|
-
def process_type
|
63
|
-
"error-state acceptor"
|
64
|
-
end
|
65
|
-
|
66
|
-
def runloop!
|
67
|
-
loop do
|
68
|
-
terminal = @s_acceptor.recv_io
|
69
|
-
_ = @s_acceptor.readline
|
70
|
-
@s_acceptor << NOT_A_PID << "\n"
|
71
|
-
ErrorPrinter.new(@error).write_to(terminal)
|
72
|
-
terminal.close
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module Zeus
|
2
|
-
class Server
|
3
|
-
class AcceptorRegistrationMonitor
|
4
|
-
|
5
|
-
def datasource ; @sock ; end
|
6
|
-
def on_datasource_event ; handle_message ; end
|
7
|
-
# @__CHILD__sock is not closed here, as it's used by the master to respond
|
8
|
-
# on behalf of unbooted acceptors
|
9
|
-
def close_child_socket ; end
|
10
|
-
def close_parent_socket ; @sock.close ; end
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@sock, @__CHILD__sock = UNIXSocket.pair
|
14
|
-
@acceptors = []
|
15
|
-
@pings = {}
|
16
|
-
end
|
17
|
-
|
18
|
-
AcceptorStub = Struct.new(:pid, :socket, :commands, :description)
|
19
|
-
|
20
|
-
def handle_message
|
21
|
-
io = @sock.recv_io
|
22
|
-
|
23
|
-
data = JSON.parse(io.readline.chomp)
|
24
|
-
type = data['type']
|
25
|
-
|
26
|
-
case type
|
27
|
-
when 'wait' ; handle_wait(io, data)
|
28
|
-
when 'registration' ; handle_registration(io, data)
|
29
|
-
else raise "invalid message"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def handle_wait(io, data)
|
34
|
-
command = data['command'].to_s
|
35
|
-
@pings[command] ||= []
|
36
|
-
@pings[command] << io
|
37
|
-
end
|
38
|
-
|
39
|
-
def handle_registration(io, data)
|
40
|
-
pid = data['pid'].to_i
|
41
|
-
commands = data['commands']
|
42
|
-
description = data['description']
|
43
|
-
|
44
|
-
@acceptors.reject!{|ac|ac.commands == commands}
|
45
|
-
@acceptors << AcceptorStub.new(pid, io, commands, description)
|
46
|
-
notify_pings_for_commands(commands)
|
47
|
-
end
|
48
|
-
|
49
|
-
def notify_pings_for_commands(commands)
|
50
|
-
(commands || []).each do |command|
|
51
|
-
(@pings[command.to_s] || []).each do |ping|
|
52
|
-
ping.puts "ready\n"
|
53
|
-
ping.close
|
54
|
-
end
|
55
|
-
@pings[command.to_s] = nil
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
module ChildProcessApi
|
60
|
-
|
61
|
-
def __CHILD__find_acceptor_for_command(command)
|
62
|
-
@acceptors.detect { |acceptor|
|
63
|
-
acceptor.commands.include?(command)
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
def __CHILD__register_acceptor(io)
|
68
|
-
@__CHILD__sock.send_io(io)
|
69
|
-
end
|
70
|
-
|
71
|
-
end ; include ChildProcessApi
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|