zeus 0.4.6 → 0.10.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/Gemfile +0 -4
  2. data/Rakefile +52 -8
  3. data/bin/zeus +14 -6
  4. data/build/fsevents-wrapper +0 -0
  5. data/build/zeus-darwin-amd64 +0 -0
  6. data/build/zeus-linux-386 +0 -0
  7. data/build/zeus-linux-amd64 +0 -0
  8. data/examples/zeus.json +22 -0
  9. data/ext/fsevents-wrapper/fsevents-wrapper +0 -0
  10. data/ext/inotify-wrapper/extconf.rb +24 -0
  11. data/ext/inotify-wrapper/inotify-wrapper.cpp +86 -0
  12. data/lib/zeus.rb +123 -35
  13. data/lib/zeus/{server/load_tracking.rb → load_tracking.rb} +1 -3
  14. data/lib/zeus/rails.rb +141 -0
  15. data/man/build/zeus +49 -0
  16. data/man/build/zeus-init +13 -0
  17. data/man/build/zeus-init.txt +17 -0
  18. data/man/build/zeus-start +16 -0
  19. data/man/build/zeus-start.txt +18 -0
  20. data/man/build/zeus.txt +50 -0
  21. data/zeus.gemspec +17 -6
  22. metadata +27 -58
  23. data/.gitignore +0 -17
  24. data/.travis.yml +0 -5
  25. data/.zeus.rb +0 -11
  26. data/README.md +0 -73
  27. data/docs/acceptor_registration.md +0 -14
  28. data/docs/client_server_handshake.md +0 -25
  29. data/ext/fsevents-wrapper/main.m +0 -118
  30. data/lib/thrud.rb +0 -97
  31. data/lib/zeus/cli.rb +0 -80
  32. data/lib/zeus/client.rb +0 -114
  33. data/lib/zeus/client/winsize.rb +0 -28
  34. data/lib/zeus/error_printer.rb +0 -16
  35. data/lib/zeus/plan.rb +0 -18
  36. data/lib/zeus/plan/acceptor.rb +0 -38
  37. data/lib/zeus/plan/node.rb +0 -66
  38. data/lib/zeus/plan/stage.rb +0 -50
  39. data/lib/zeus/server.rb +0 -103
  40. data/lib/zeus/server/acceptor.rb +0 -79
  41. data/lib/zeus/server/acceptor_registration_monitor.rb +0 -75
  42. data/lib/zeus/server/client_handler.rb +0 -106
  43. data/lib/zeus/server/command_runner.rb +0 -70
  44. data/lib/zeus/server/file_monitor.rb +0 -8
  45. data/lib/zeus/server/file_monitor/fsevent.rb +0 -102
  46. data/lib/zeus/server/process_tree_monitor.rb +0 -89
  47. data/lib/zeus/server/stage.rb +0 -88
  48. data/lib/zeus/server/stage/error_state.rb +0 -42
  49. data/lib/zeus/server/stage/feature_notifier.rb +0 -38
  50. data/lib/zeus/templates/rails.rb +0 -133
  51. data/lib/zeus/ui.rb +0 -57
  52. data/lib/zeus/version.rb +0 -3
  53. data/spec/cli_spec.rb +0 -95
  54. data/spec/error_printer_spec.rb +0 -27
  55. data/spec/integration_spec.rb +0 -106
  56. data/spec/server/file_monitor/fsevent_spec.rb +0 -88
  57. data/spec/server/load_tracking_spec.rb +0 -67
  58. data/spec/server/process_tree_monitor_spec.rb +0 -50
  59. data/spec/spec_helper.rb +0 -38
  60. data/spec/ui_spec.rb +0 -54
@@ -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
@@ -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
@@ -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
-
@@ -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
@@ -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
@@ -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
@@ -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