zeus 0.4.5 → 0.4.6
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/README.md +2 -1
- data/Rakefile +9 -3
- data/bin/zeus +4 -0
- data/docs/acceptor_registration.md +14 -0
- data/docs/client_server_handshake.md +25 -0
- data/lib/zeus.rb +19 -7
- data/lib/zeus/client.rb +36 -36
- data/lib/zeus/client/winsize.rb +28 -0
- data/lib/zeus/error_printer.rb +16 -0
- data/lib/zeus/plan.rb +18 -0
- data/lib/zeus/plan/acceptor.rb +38 -0
- data/lib/zeus/plan/node.rb +66 -0
- data/lib/zeus/plan/stage.rb +50 -0
- data/lib/zeus/server.rb +17 -17
- data/lib/zeus/server/acceptor.rb +7 -24
- data/lib/zeus/server/acceptor_registration_monitor.rb +2 -10
- data/lib/zeus/server/client_handler.rb +14 -37
- data/lib/zeus/server/command_runner.rb +36 -16
- data/lib/zeus/server/file_monitor.rb +2 -1
- data/lib/zeus/server/file_monitor/fsevent.rb +6 -1
- data/lib/zeus/server/stage.rb +49 -40
- data/lib/zeus/server/stage/error_state.rb +42 -0
- data/lib/zeus/server/stage/feature_notifier.rb +38 -0
- data/lib/zeus/version.rb +1 -1
- data/spec/error_printer_spec.rb +27 -0
- data/spec/integration_spec.rb +31 -12
- metadata +14 -4
- data/lib/zeus/dsl.rb +0 -154
- data/lib/zeus/server/forked_process.rb +0 -98
@@ -0,0 +1,42 @@
|
|
1
|
+
module Zeus
|
2
|
+
class Server
|
3
|
+
class Stage
|
4
|
+
|
5
|
+
module ErrorState
|
6
|
+
def handle_load_error(e)
|
7
|
+
errored_file = full_path_of_file_from_error(e)
|
8
|
+
|
9
|
+
# register all the decendent acceptors as stubs with errors
|
10
|
+
register_acceptors_as_errors(e)
|
11
|
+
|
12
|
+
feature_notifier.notify_feature(errored_file)
|
13
|
+
feature_notifier.notify_new_features
|
14
|
+
|
15
|
+
# we do not need to do anything. We wait, until a dependency changes.
|
16
|
+
# At that point, we get killed and restarted.
|
17
|
+
sleep
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def full_path_of_file_from_error(e)
|
23
|
+
errored_file = e.backtrace[0].scan(/(.+?):\d+:in/)[0][0]
|
24
|
+
|
25
|
+
# handle relative paths
|
26
|
+
unless errored_file =~ /^\//
|
27
|
+
errored_file = File.expand_path(errored_file, Dir.pwd)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def register_acceptors_as_errors(e)
|
32
|
+
descendent_acceptors.each do |acc|
|
33
|
+
acc = acc.extend(Acceptor::ErrorState)
|
34
|
+
acc.error = e
|
35
|
+
acc.run
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Zeus
|
2
|
+
class Server
|
3
|
+
class Stage
|
4
|
+
class FeatureNotifier
|
5
|
+
|
6
|
+
def initialize(server, stage_name)
|
7
|
+
@server = server
|
8
|
+
@stage_name = stage_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def notify_new_features
|
12
|
+
new_features = newly_loaded_features()
|
13
|
+
$previously_loaded_features ||= []
|
14
|
+
$previously_loaded_features |= new_features
|
15
|
+
Thread.new {
|
16
|
+
new_features.each { |f| notify_feature(f) }
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def notify_feature(feature)
|
21
|
+
@server.__CHILD__stage_has_feature(@stage_name, feature)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def newly_loaded_features
|
27
|
+
old_features = defined?($previously_loaded_features) ? $previously_loaded_features : []
|
28
|
+
($LOADED_FEATURES + @server.extra_features) - old_features
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
data/lib/zeus/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module Zeus
|
6
|
+
describe ErrorPrinter do
|
7
|
+
|
8
|
+
let(:error) {
|
9
|
+
begin
|
10
|
+
raise
|
11
|
+
rescue => e
|
12
|
+
e
|
13
|
+
end
|
14
|
+
}
|
15
|
+
|
16
|
+
it 'prints an error just like ruby does by default' do
|
17
|
+
io = StringIO.new
|
18
|
+
ErrorPrinter.new(error).write_to(io)
|
19
|
+
io.rewind
|
20
|
+
lines = io.readlines
|
21
|
+
lines[0].should =~ /^[^\s]*error_printer_spec.rb:\d+:in `.+': \(RuntimeError\)$/
|
22
|
+
lines[1].should =~ /^\tfrom .*\.rb:\d+:in `.*'$/
|
23
|
+
lines.size.should > 5
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/spec/integration_spec.rb
CHANGED
@@ -7,22 +7,29 @@ describe "Integration" do
|
|
7
7
|
|
8
8
|
context "in a non-rails project with a .zeus.rb" do
|
9
9
|
it "starts the zeus server and responds to commands" do
|
10
|
-
|
11
|
-
Zeus::Server.define! do
|
12
|
-
stage :foo do
|
13
|
-
command :bar do
|
14
|
-
puts "YES"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
RUBY
|
19
|
-
|
10
|
+
bar_setup("puts 'YES'")
|
20
11
|
start, run = start_and_run("bar")
|
21
12
|
start.should include "spawner `foo`"
|
22
13
|
start.should include "acceptor `bar`"
|
23
14
|
run.should == ["YES\r\n"]
|
24
15
|
end
|
25
16
|
|
17
|
+
it "receives ARGV after command" do
|
18
|
+
bar_setup("puts ARGV.join(', ')")
|
19
|
+
start, run = start_and_run("bar 1 '2 3 4' --123")
|
20
|
+
run.should == ["1, 2 3 4, --123\r\n"]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "cam exist with 0" do
|
24
|
+
bar_setup("exit 0")
|
25
|
+
start_and_run("bar")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can exist with non-0" do
|
29
|
+
bar_setup("exit 1")
|
30
|
+
start_and_run("bar", :fail => true)
|
31
|
+
end
|
32
|
+
|
26
33
|
it "can run via command alias" do
|
27
34
|
write ".zeus.rb", <<-RUBY
|
28
35
|
Zeus::Server.define! do
|
@@ -43,6 +50,18 @@ describe "Integration" do
|
|
43
50
|
|
44
51
|
private
|
45
52
|
|
53
|
+
def bar_setup(inner)
|
54
|
+
write ".zeus.rb", <<-RUBY
|
55
|
+
Zeus::Server.define! do
|
56
|
+
stage :foo do
|
57
|
+
command :bar do
|
58
|
+
#{inner}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
RUBY
|
63
|
+
end
|
64
|
+
|
46
65
|
def zeus(command, options={})
|
47
66
|
command = zeus_command(command)
|
48
67
|
result = `#{command}`
|
@@ -62,11 +81,11 @@ describe "Integration" do
|
|
62
81
|
end
|
63
82
|
end
|
64
83
|
|
65
|
-
def start_and_run(commands)
|
84
|
+
def start_and_run(commands, options={})
|
66
85
|
start_output = ""
|
67
86
|
t1 = Thread.new { record_start(start_output) }
|
68
87
|
sleep 0.1
|
69
|
-
run_output = [*commands].map{ |cmd| zeus(cmd) }
|
88
|
+
run_output = [*commands].map{ |cmd| zeus(cmd, options) }
|
70
89
|
sleep 0.2
|
71
90
|
t1.kill
|
72
91
|
[start_output, run_output]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Boot any rails app in under a second
|
15
15
|
email:
|
@@ -27,13 +27,20 @@ files:
|
|
27
27
|
- README.md
|
28
28
|
- Rakefile
|
29
29
|
- bin/zeus
|
30
|
+
- docs/acceptor_registration.md
|
31
|
+
- docs/client_server_handshake.md
|
30
32
|
- ext/fsevents-wrapper/fsevents-wrapper
|
31
33
|
- ext/fsevents-wrapper/main.m
|
32
34
|
- lib/thrud.rb
|
33
35
|
- lib/zeus.rb
|
34
36
|
- lib/zeus/cli.rb
|
35
37
|
- lib/zeus/client.rb
|
36
|
-
- lib/zeus/
|
38
|
+
- lib/zeus/client/winsize.rb
|
39
|
+
- lib/zeus/error_printer.rb
|
40
|
+
- lib/zeus/plan.rb
|
41
|
+
- lib/zeus/plan/acceptor.rb
|
42
|
+
- lib/zeus/plan/node.rb
|
43
|
+
- lib/zeus/plan/stage.rb
|
37
44
|
- lib/zeus/server.rb
|
38
45
|
- lib/zeus/server/acceptor.rb
|
39
46
|
- lib/zeus/server/acceptor_registration_monitor.rb
|
@@ -41,14 +48,16 @@ files:
|
|
41
48
|
- lib/zeus/server/command_runner.rb
|
42
49
|
- lib/zeus/server/file_monitor.rb
|
43
50
|
- lib/zeus/server/file_monitor/fsevent.rb
|
44
|
-
- lib/zeus/server/forked_process.rb
|
45
51
|
- lib/zeus/server/load_tracking.rb
|
46
52
|
- lib/zeus/server/process_tree_monitor.rb
|
47
53
|
- lib/zeus/server/stage.rb
|
54
|
+
- lib/zeus/server/stage/error_state.rb
|
55
|
+
- lib/zeus/server/stage/feature_notifier.rb
|
48
56
|
- lib/zeus/templates/rails.rb
|
49
57
|
- lib/zeus/ui.rb
|
50
58
|
- lib/zeus/version.rb
|
51
59
|
- spec/cli_spec.rb
|
60
|
+
- spec/error_printer_spec.rb
|
52
61
|
- spec/integration_spec.rb
|
53
62
|
- spec/server/file_monitor/fsevent_spec.rb
|
54
63
|
- spec/server/load_tracking_spec.rb
|
@@ -84,6 +93,7 @@ summary: Zeus is an intelligent preloader for ruby applications. It allows norma
|
|
84
93
|
development tasks to be run in a fraction of a second.
|
85
94
|
test_files:
|
86
95
|
- spec/cli_spec.rb
|
96
|
+
- spec/error_printer_spec.rb
|
87
97
|
- spec/integration_spec.rb
|
88
98
|
- spec/server/file_monitor/fsevent_spec.rb
|
89
99
|
- spec/server/load_tracking_spec.rb
|
data/lib/zeus/dsl.rb
DELETED
@@ -1,154 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
module Zeus
|
4
|
-
module DSL
|
5
|
-
|
6
|
-
class Evaluator
|
7
|
-
def stage(name, &b)
|
8
|
-
stage = DSL::Stage.new(name)
|
9
|
-
stage.root = true
|
10
|
-
stage.instance_eval(&b)
|
11
|
-
stage
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class Node
|
16
|
-
attr_reader :name, :stages, :features
|
17
|
-
attr_accessor :pid, :root
|
18
|
-
|
19
|
-
def initialize(name)
|
20
|
-
@name = name
|
21
|
-
@stages = []
|
22
|
-
@features = Set.new # hash might be faster than ruby's inane impl of set.
|
23
|
-
end
|
24
|
-
|
25
|
-
def stage_has_feature(name, file)
|
26
|
-
node_for_name(name).features << file
|
27
|
-
end
|
28
|
-
|
29
|
-
def stage_has_pid(name, pid)
|
30
|
-
node_for_name(name).pid = pid
|
31
|
-
end
|
32
|
-
|
33
|
-
def kill_nodes_with_feature(file)
|
34
|
-
if features.include?(file)
|
35
|
-
if root
|
36
|
-
Zeus.ui.error "One of zeus's dependencies changed. Not killing zeus. You may have to restart the server."
|
37
|
-
else
|
38
|
-
kill!
|
39
|
-
end
|
40
|
-
else
|
41
|
-
stages.each do |child|
|
42
|
-
child.kill_nodes_with_feature(file)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# We send STOP before actually killing the processes here.
|
48
|
-
# This is to prevent parents from respawning before all the children
|
49
|
-
# are killed. This prevents a race condition.
|
50
|
-
def kill!
|
51
|
-
Process.kill("STOP", pid) if pid
|
52
|
-
# Protected methods don't work with each(&:m) notation.
|
53
|
-
stages.each { |stage| stage.kill! }
|
54
|
-
old_pid = pid
|
55
|
-
self.pid = nil
|
56
|
-
Process.kill("KILL", old_pid) if old_pid
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def node_for_name(name)
|
62
|
-
@nodes_by_name ||= __nodes_by_name
|
63
|
-
@nodes_by_name[name]
|
64
|
-
end
|
65
|
-
|
66
|
-
def __nodes_by_name
|
67
|
-
nodes = {name => self}
|
68
|
-
stages.each do |child|
|
69
|
-
nodes.merge!(child.__nodes_by_name)
|
70
|
-
end
|
71
|
-
nodes
|
72
|
-
end ; protected :__nodes_by_name
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
class Acceptor < Node
|
77
|
-
|
78
|
-
attr_reader :name, :aliases, :description, :action
|
79
|
-
def initialize(name, aliases, description, &b)
|
80
|
-
super(name)
|
81
|
-
@description = description
|
82
|
-
@aliases = aliases
|
83
|
-
@action = b
|
84
|
-
end
|
85
|
-
|
86
|
-
# ^ configuration
|
87
|
-
# V later use
|
88
|
-
|
89
|
-
def commands
|
90
|
-
[name, *aliases].map(&:to_s)
|
91
|
-
end
|
92
|
-
|
93
|
-
def acceptors
|
94
|
-
self
|
95
|
-
end
|
96
|
-
|
97
|
-
def to_process_object(server)
|
98
|
-
Zeus::Server::Acceptor.new(server).tap do |stage|
|
99
|
-
stage.name = @name
|
100
|
-
stage.aliases = @aliases
|
101
|
-
stage.action = @action
|
102
|
-
stage.description = @description
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
class Stage < Node
|
109
|
-
|
110
|
-
attr_reader :actions
|
111
|
-
def initialize(name)
|
112
|
-
super(name)
|
113
|
-
@actions = []
|
114
|
-
end
|
115
|
-
|
116
|
-
def action(&b)
|
117
|
-
@actions << b
|
118
|
-
self
|
119
|
-
end
|
120
|
-
|
121
|
-
def desc(desc)
|
122
|
-
@desc = desc
|
123
|
-
end
|
124
|
-
|
125
|
-
def stage(name, &b)
|
126
|
-
@stages << DSL::Stage.new(name).tap { |s| s.instance_eval(&b) }
|
127
|
-
self
|
128
|
-
end
|
129
|
-
|
130
|
-
def command(name, *aliases, &b)
|
131
|
-
@stages << DSL::Acceptor.new(name, aliases, @desc, &b)
|
132
|
-
@desc = nil
|
133
|
-
self
|
134
|
-
end
|
135
|
-
|
136
|
-
# ^ configuration
|
137
|
-
# V later use
|
138
|
-
|
139
|
-
def acceptors
|
140
|
-
stages.map(&:acceptors).flatten
|
141
|
-
end
|
142
|
-
|
143
|
-
def to_process_object(server)
|
144
|
-
Zeus::Server::Stage.new(server).tap do |stage|
|
145
|
-
stage.name = @name
|
146
|
-
stage.stages = @stages.map { |stage| stage.to_process_object(server) }
|
147
|
-
stage.actions = @actions
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
end
|
152
|
-
|
153
|
-
end
|
154
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
module Zeus
|
2
|
-
class Server
|
3
|
-
# base class for Stage and Acceptor
|
4
|
-
class ForkedProcess
|
5
|
-
|
6
|
-
attr_accessor :name
|
7
|
-
attr_reader :pid
|
8
|
-
def initialize(server)
|
9
|
-
@server = server
|
10
|
-
end
|
11
|
-
|
12
|
-
def notify_feature(feature)
|
13
|
-
@server.__CHILD__stage_has_feature(@name, feature)
|
14
|
-
end
|
15
|
-
|
16
|
-
def descendent_acceptors
|
17
|
-
raise NotImplementedError
|
18
|
-
end
|
19
|
-
|
20
|
-
def process_type
|
21
|
-
raise "NotImplementedError"
|
22
|
-
end
|
23
|
-
|
24
|
-
def notify_started
|
25
|
-
@server.__CHILD__stage_starting_with_pid(@name, Process.pid)
|
26
|
-
Zeus.ui.info("starting #{process_type} `#{@name}`")
|
27
|
-
end
|
28
|
-
|
29
|
-
def notify_terminated
|
30
|
-
# @server.__CHILD__stage_terminating(@name)
|
31
|
-
Zeus.ui.info("killing #{process_type} `#{@name}`")
|
32
|
-
end
|
33
|
-
|
34
|
-
def setup_forked_process(close_parent_sockets)
|
35
|
-
@server.__CHILD__close_parent_sockets if close_parent_sockets
|
36
|
-
|
37
|
-
notify_started
|
38
|
-
|
39
|
-
$0 = "zeus #{process_type}: #{@name}"
|
40
|
-
|
41
|
-
trap("INT") { exit }
|
42
|
-
trap("TERM") {
|
43
|
-
notify_terminated
|
44
|
-
exit
|
45
|
-
}
|
46
|
-
|
47
|
-
defined?(ActiveRecord::Base) and ActiveRecord::Base.clear_all_connections!
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
def newly_loaded_features
|
52
|
-
old_features = defined?($previously_loaded_features) ? $previously_loaded_features : []
|
53
|
-
($LOADED_FEATURES + @server.extra_features) - old_features
|
54
|
-
end
|
55
|
-
|
56
|
-
def runloop!
|
57
|
-
raise NotImplementedError
|
58
|
-
end
|
59
|
-
|
60
|
-
def before_setup
|
61
|
-
end
|
62
|
-
|
63
|
-
def after_setup
|
64
|
-
end
|
65
|
-
|
66
|
-
def notify_new_features
|
67
|
-
new_features = newly_loaded_features()
|
68
|
-
$previously_loaded_features ||= []
|
69
|
-
$previously_loaded_features |= new_features
|
70
|
-
Thread.new {
|
71
|
-
new_features.each { |f| notify_feature(f) }
|
72
|
-
}
|
73
|
-
end
|
74
|
-
|
75
|
-
def after_notify
|
76
|
-
end
|
77
|
-
|
78
|
-
# TODO: This just got really ugly and needs a refactor more than ever.
|
79
|
-
def run(close_parent_sockets = false)
|
80
|
-
@pid = fork {
|
81
|
-
before_setup
|
82
|
-
setup_forked_process(close_parent_sockets)
|
83
|
-
|
84
|
-
Zeus.run_after_fork!
|
85
|
-
|
86
|
-
after_setup
|
87
|
-
notify_new_features
|
88
|
-
|
89
|
-
after_notify
|
90
|
-
runloop!
|
91
|
-
}
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|