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.
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