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.
- 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
@@ -1,25 +0,0 @@
|
|
1
|
-
# Client/Server handshake
|
2
|
-
|
3
|
-
This takes place in `lib/zeus/server/client_handler.rb`, `lib/zeus/client.rb`, and `lib/zeus/server/acceptor.rb`.
|
4
|
-
|
5
|
-
The model is kind of convoluted, so here's an explanation of what's happening with all these sockets:
|
6
|
-
|
7
|
-
## Running a command
|
8
|
-
1. ClientHandler has a UNIXServer (`SVR`) listening.
|
9
|
-
2. ClientHandler has a socketpair with the acceptor referenced by the command (see `docs/acceptor_registration.md`) (`S_ACC`)
|
10
|
-
3. When clienthandler receives a connection (`S_CLI`) on `SVR`:
|
11
|
-
1. ClientHandler sends `S_CLI` over `S_ACC`, so the acceptor can communicate with the server's client.
|
12
|
-
2. ClientHandler sends a JSON-encoded array of `arguments` over `S_ACC`
|
13
|
-
3. Acceptor sends the newly-forked worker PID over `S_ACC` to clienthandler.
|
14
|
-
4. ClientHandler forwards the pid to the client over `S_CLI`.
|
15
|
-
|
16
|
-
|
17
|
-
## A sort of network diagram
|
18
|
-
client clienthandler acceptor
|
19
|
-
1 ----------> | {command: String, arguments: [String]}
|
20
|
-
2 ----------> | Terminal IO
|
21
|
-
3 -----------> | Terminal IO
|
22
|
-
4 -----------> | Arguments (json array)
|
23
|
-
5 <----------- | pid
|
24
|
-
6 <--------- | pid
|
25
|
-
|
data/ext/fsevents-wrapper/main.m
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
#import <Foundation/Foundation.h>
|
2
|
-
#include <CoreServices/CoreServices.h>
|
3
|
-
#include <sys/stat.h>
|
4
|
-
#include <fcntl.h>
|
5
|
-
|
6
|
-
|
7
|
-
static CFMutableArrayRef _watchedFiles;
|
8
|
-
static FSEventStreamRef _activeStream;
|
9
|
-
static NSMutableDictionary *_fileIsWatched;
|
10
|
-
|
11
|
-
static int flagsWorthReloadingFor = \
|
12
|
-
kFSEventStreamEventFlagItemRemoved | \
|
13
|
-
kFSEventStreamEventFlagItemRenamed | \
|
14
|
-
kFSEventStreamEventFlagItemModified;
|
15
|
-
|
16
|
-
void myCallbackFunction(
|
17
|
-
ConstFSEventStreamRef streamRef,
|
18
|
-
void *clientCallBackInfo,
|
19
|
-
size_t numEvents,
|
20
|
-
void *eventPaths,
|
21
|
-
const FSEventStreamEventFlags eventFlags[],
|
22
|
-
const FSEventStreamEventId eventIds[])
|
23
|
-
{
|
24
|
-
int i, flags;
|
25
|
-
char **paths = eventPaths;
|
26
|
-
|
27
|
-
for (i = 0; i < numEvents; i++) {
|
28
|
-
flags = eventFlags[i];
|
29
|
-
|
30
|
-
if (flags & (kFSEventStreamEventFlagItemIsFile | flagsWorthReloadingFor)) {
|
31
|
-
printf("%s\n", paths[i]);
|
32
|
-
fflush(stdout);
|
33
|
-
}
|
34
|
-
}
|
35
|
-
}
|
36
|
-
|
37
|
-
void configureStream()
|
38
|
-
{
|
39
|
-
if (CFArrayGetCount(_watchedFiles) == 0) return;
|
40
|
-
|
41
|
-
if (_activeStream) {
|
42
|
-
FSEventStreamStop(_activeStream);
|
43
|
-
FSEventStreamUnscheduleFromRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
44
|
-
//CFRelease(_activeStream);
|
45
|
-
}
|
46
|
-
|
47
|
-
_activeStream = FSEventStreamCreate(NULL,
|
48
|
-
&myCallbackFunction,
|
49
|
-
NULL,
|
50
|
-
_watchedFiles,
|
51
|
-
kFSEventStreamEventIdSinceNow,
|
52
|
-
1.0, // latency
|
53
|
-
kFSEventStreamCreateFlagFileEvents);
|
54
|
-
|
55
|
-
FSEventStreamScheduleWithRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
56
|
-
|
57
|
-
FSEventStreamStart(_activeStream);
|
58
|
-
|
59
|
-
}
|
60
|
-
|
61
|
-
int maybeAddFileToWatchList(char *line)
|
62
|
-
{
|
63
|
-
CFStringRef file = CFStringCreateWithCString(NULL, line, kCFStringEncodingASCII);
|
64
|
-
struct stat buf;
|
65
|
-
|
66
|
-
if ([_fileIsWatched valueForKey:(__bridge NSString *)file]) {
|
67
|
-
return 0;
|
68
|
-
} else if (stat(line, &buf) == 0) {
|
69
|
-
[_fileIsWatched setValue:@"yes" forKey:(__bridge NSString *)file];
|
70
|
-
CFArrayAppendValue(_watchedFiles, file);
|
71
|
-
return 1;
|
72
|
-
} else {
|
73
|
-
return 0;
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
void handleInputFiles()
|
78
|
-
{
|
79
|
-
int anyChanges = 0;
|
80
|
-
|
81
|
-
char line[2048];
|
82
|
-
|
83
|
-
while (fgets(line, sizeof(line), stdin) != NULL) {
|
84
|
-
line[strlen(line)-1] = 0;
|
85
|
-
anyChanges |= maybeAddFileToWatchList(line);
|
86
|
-
}
|
87
|
-
|
88
|
-
if (anyChanges) {
|
89
|
-
configureStream();
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
void configureTimerAndRun()
|
94
|
-
{
|
95
|
-
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL,
|
96
|
-
0,
|
97
|
-
0.5,
|
98
|
-
0,
|
99
|
-
0,
|
100
|
-
&handleInputFiles,
|
101
|
-
NULL);
|
102
|
-
|
103
|
-
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
|
104
|
-
CFRunLoopRun();
|
105
|
-
}
|
106
|
-
|
107
|
-
int main(int argc, const char * argv[])
|
108
|
-
{
|
109
|
-
int flags = fcntl(0, F_GETFL);
|
110
|
-
flags |= O_NONBLOCK;
|
111
|
-
fcntl(STDIN_FILENO, F_SETFL, flags);
|
112
|
-
|
113
|
-
_watchedFiles = CFArrayCreateMutable(NULL, 0, NULL);
|
114
|
-
_fileIsWatched = [[NSMutableDictionary alloc] initWithCapacity:500];
|
115
|
-
|
116
|
-
configureTimerAndRun();
|
117
|
-
return 0;
|
118
|
-
}
|
data/lib/thrud.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
class Thrud
|
2
|
-
|
3
|
-
class Task < Struct.new(:method_name, :desc, :long_desc, :method_options)
|
4
|
-
def arity(obj)
|
5
|
-
obj.method(method_name).arity
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.desc(name, a)
|
10
|
-
@desc = a
|
11
|
-
end
|
12
|
-
def self.long_desc(a)
|
13
|
-
@long_desc = a
|
14
|
-
end
|
15
|
-
def self.method_option(*a)
|
16
|
-
@method_options ||= []
|
17
|
-
@method_options << a
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.method_added(m)
|
21
|
-
desc, long_desc, method_options = @desc, @long_desc, (@method_options||[])
|
22
|
-
@desc, @long_desc, @method_options = nil, nil, nil
|
23
|
-
|
24
|
-
@tasks ||= {}
|
25
|
-
@tasks[m.to_s] = Task.new(m, desc, long_desc, method_options)
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.map(a)
|
29
|
-
a.each do |aliases, target|
|
30
|
-
aliases = [aliases] unless aliases.kind_of?(Array)
|
31
|
-
aliases.each do |name|
|
32
|
-
@tasks[name.to_s] = @tasks[target.to_s]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.task_for_name(name)
|
38
|
-
@tasks[name.to_s]
|
39
|
-
end
|
40
|
-
|
41
|
-
def task_for_name(name)
|
42
|
-
self.class.task_for_name(name)
|
43
|
-
end
|
44
|
-
|
45
|
-
def help(taskname = nil)
|
46
|
-
if taskname && task = task_for_name(taskname)
|
47
|
-
arity = task.arity(self)
|
48
|
-
Zeus.ui.info <<-BANNER
|
49
|
-
Usage:
|
50
|
-
zeus #{taskname} #{arity == -1 ? "[ARGS]" : ""}
|
51
|
-
|
52
|
-
Description:
|
53
|
-
#{task.long_desc || task.desc}
|
54
|
-
BANNER
|
55
|
-
else
|
56
|
-
# this is super non-generic. problem for future-burke.
|
57
|
-
project_tasks = self.class.instance_variable_get("@tasks").
|
58
|
-
reject{|k,v|['definition_file', 'initialize', 'version', 'init', 'start', 'help'].include?(v.method_name.to_s)}.values.uniq
|
59
|
-
|
60
|
-
tasks = project_tasks.map { |task|
|
61
|
-
" zeus %-14s # %s" % [task.method_name, task.desc]
|
62
|
-
}
|
63
|
-
|
64
|
-
Zeus.ui.info <<-BANNER
|
65
|
-
Global Commands:
|
66
|
-
zeus help # show this help menu
|
67
|
-
zeus help [COMMAND] # show help for a specific command
|
68
|
-
zeus init # #{task_for_name(:init).desc}
|
69
|
-
zeus start # #{task_for_name(:start).desc}
|
70
|
-
zeus version # #{task_for_name(:version).desc}
|
71
|
-
|
72
|
-
Project-local Commands:
|
73
|
-
#{tasks.join("\n")}
|
74
|
-
BANNER
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.start
|
79
|
-
taskname = ARGV.shift
|
80
|
-
arguments = ARGV
|
81
|
-
|
82
|
-
taskname ||= "help"
|
83
|
-
|
84
|
-
unless task = @tasks[taskname.to_s]
|
85
|
-
Zeus.ui.error "Could not find task \"#{taskname}\""
|
86
|
-
exit 1
|
87
|
-
end
|
88
|
-
|
89
|
-
instance = new
|
90
|
-
if instance.method(task.method_name).arity == 0 && arguments.any?
|
91
|
-
Zeus.ui.error "\"#{task.method_name}\" was called incorrectly. Call as \"zeus #{task.method_name}\"."
|
92
|
-
exit 1
|
93
|
-
end
|
94
|
-
instance.send(task.method_name, *arguments)
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
data/lib/zeus/cli.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
require 'thrud'
|
2
|
-
|
3
|
-
module Zeus
|
4
|
-
class CLI < Thrud
|
5
|
-
|
6
|
-
def initialize(*)
|
7
|
-
super
|
8
|
-
Zeus.ui = Zeus::UI.new
|
9
|
-
Zeus.ui.debug! #if options['verbose']
|
10
|
-
end
|
11
|
-
|
12
|
-
desc "init", "Generates a zeus config file in the current working directory"
|
13
|
-
long_desc <<-D
|
14
|
-
Init tries to determine what kind of project is in the current working directory,
|
15
|
-
and generates a relevant config file. Currently the only supported template is
|
16
|
-
rails.
|
17
|
-
D
|
18
|
-
# method_option "rails", type: :string, banner: "Use the rails template instead of auto-detecting based on project contents"
|
19
|
-
def init
|
20
|
-
require 'fileutils'
|
21
|
-
|
22
|
-
if File.exist?(".zeus.rb")
|
23
|
-
Zeus.ui.error ".zeus.rb already exists at #{Dir.pwd}/.zeus.rb"
|
24
|
-
exit 1
|
25
|
-
end
|
26
|
-
|
27
|
-
Zeus.ui.info "Writing new .zeus.rb to #{Dir.pwd}/.zeus.rb"
|
28
|
-
FileUtils.cp(File.expand_path("../templates/rails.rb", __FILE__), '.zeus.rb')
|
29
|
-
end
|
30
|
-
|
31
|
-
desc "start", "Start a zeus server for the project in the current working directory"
|
32
|
-
long_desc <<-D
|
33
|
-
starts a server that boots your application using the config file in
|
34
|
-
.zeus.rb. The server will take several seconds to start, after which you may
|
35
|
-
use the zeus runner commands (see `zeus help` for a list of available commands).
|
36
|
-
D
|
37
|
-
def start
|
38
|
-
begin
|
39
|
-
require self.class.definition_file
|
40
|
-
rescue LoadError
|
41
|
-
Zeus.ui.error("Your project is missing a config file (.zeus.rb), and it doesn't appear\n"\
|
42
|
-
"to be a rails project. You can run `zeus init` to generate a config file.")
|
43
|
-
exit 1
|
44
|
-
end
|
45
|
-
Zeus::Server.new.run
|
46
|
-
end
|
47
|
-
|
48
|
-
def help(*)
|
49
|
-
super
|
50
|
-
end
|
51
|
-
|
52
|
-
desc "version", "Print zeus's version information and exit"
|
53
|
-
def version
|
54
|
-
Zeus.ui.info "Zeus version #{Zeus::VERSION}"
|
55
|
-
end
|
56
|
-
map %w(-v --version) => :version
|
57
|
-
|
58
|
-
def self.definition_file
|
59
|
-
if !File.exists?('.zeus.rb') && File.exists?('script/rails')
|
60
|
-
File.expand_path("../templates/rails.rb", __FILE__)
|
61
|
-
else
|
62
|
-
'./.zeus.rb'
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
begin
|
67
|
-
require definition_file
|
68
|
-
rescue LoadError
|
69
|
-
end
|
70
|
-
|
71
|
-
Zeus::Server.acceptors.each do |acc|
|
72
|
-
desc acc.name, (acc.description || "#{acc.name} task defined in zeus definition file")
|
73
|
-
define_method(acc.name) { |*args|
|
74
|
-
Zeus::Client.run(acc.name, args)
|
75
|
-
}
|
76
|
-
map acc.aliases => acc.name
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
end
|
data/lib/zeus/client.rb
DELETED
@@ -1,114 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
begin
|
3
|
-
require "io/console"
|
4
|
-
rescue LoadError
|
5
|
-
Zeus.ui.error "io/console not found. Please `gem install io-console` or, preferably, " +
|
6
|
-
"install ruby 1.9.3 by following the instructions at: " +
|
7
|
-
"https://gist.github.com/1688857"
|
8
|
-
exit 1
|
9
|
-
end
|
10
|
-
require "json"
|
11
|
-
require "pty"
|
12
|
-
require "socket"
|
13
|
-
|
14
|
-
require 'zeus/client/winsize'
|
15
|
-
|
16
|
-
module Zeus
|
17
|
-
class Client
|
18
|
-
include Winsize
|
19
|
-
|
20
|
-
attr_accessor :pid
|
21
|
-
|
22
|
-
SIGNALS = {
|
23
|
-
"\x03" => "INT",
|
24
|
-
"\x1C" => "QUIT",
|
25
|
-
"\x1A" => "TSTP",
|
26
|
-
}
|
27
|
-
SIGNAL_REGEX = Regexp.union(SIGNALS.keys)
|
28
|
-
|
29
|
-
def self.run(command, args)
|
30
|
-
new.run(command, args)
|
31
|
-
end
|
32
|
-
|
33
|
-
def run(command, args)
|
34
|
-
maybe_raw do
|
35
|
-
PTY.open do |master, slave|
|
36
|
-
@exit_status, @es2 = IO.pipe
|
37
|
-
@master = master
|
38
|
-
set_winsize
|
39
|
-
make_winch_channel
|
40
|
-
@pid = connect_to_server(command, args, slave)
|
41
|
-
|
42
|
-
select_loop!
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def select_loop!
|
50
|
-
buffer = ""
|
51
|
-
while ready = select([winch, @master, $stdin, @exit_status])[0]
|
52
|
-
handle_winch if ready.include?(winch)
|
53
|
-
handle_stdin(buffer) if ready.include?($stdin)
|
54
|
-
handle_master(buffer) if ready.include?(@master)
|
55
|
-
handle_exit if ready.include?(@exit_status)
|
56
|
-
end
|
57
|
-
rescue EOFError
|
58
|
-
end
|
59
|
-
|
60
|
-
def handle_exit
|
61
|
-
exit @exit_status.readline.chomp.to_i
|
62
|
-
end
|
63
|
-
|
64
|
-
def connect_to_server(command, arguments, slave, socket_path = Zeus::SOCKET_NAME)
|
65
|
-
socket = UNIXSocket.new(socket_path)
|
66
|
-
socket << {command: command, arguments: arguments}.to_json << "\n"
|
67
|
-
socket.send_io(slave)
|
68
|
-
socket.send_io(@es2)
|
69
|
-
slave.close
|
70
|
-
|
71
|
-
pid = socket.readline.chomp.to_i
|
72
|
-
trap("CONT") { Process.kill("CONT", @pid) }
|
73
|
-
pid
|
74
|
-
rescue Errno::ENOENT, Errno::ECONNREFUSED, Errno::ECONNRESET
|
75
|
-
# we need a \r at the end because the terminal is in raw mode.
|
76
|
-
Zeus.ui.error "Zeus doesn't seem to be running, try 'zeus start`\r"
|
77
|
-
exit 1
|
78
|
-
end
|
79
|
-
|
80
|
-
def handle_stdin(buffer)
|
81
|
-
input = $stdin.readpartial(4096, buffer)
|
82
|
-
input.scan(SIGNAL_REGEX).each { |signal|
|
83
|
-
begin
|
84
|
-
send_signal(signal, pid)
|
85
|
-
rescue Errno::ESRCH
|
86
|
-
exit # the remote process died. Just quit.
|
87
|
-
end
|
88
|
-
}
|
89
|
-
@master << input
|
90
|
-
end
|
91
|
-
|
92
|
-
def send_signal(signal, pid)
|
93
|
-
if SIGNALS[signal] == "TSTP"
|
94
|
-
Process.kill("STOP", pid)
|
95
|
-
Process.kill("TSTP", Process.pid)
|
96
|
-
else
|
97
|
-
Process.kill(SIGNALS[signal], pid)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def handle_master(buffer)
|
102
|
-
$stdout << @master.readpartial(4096, buffer)
|
103
|
-
end
|
104
|
-
|
105
|
-
def maybe_raw(&b)
|
106
|
-
if $stdout.tty?
|
107
|
-
$stdout.raw(&b)
|
108
|
-
else
|
109
|
-
b.call
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
end
|
data/lib/zeus/client/winsize.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
module Zeus
|
2
|
-
class Client
|
3
|
-
module Winsize
|
4
|
-
|
5
|
-
attr_reader :winch
|
6
|
-
|
7
|
-
def set_winsize
|
8
|
-
$stdout.tty? and @master.winsize = $stdout.winsize
|
9
|
-
end
|
10
|
-
|
11
|
-
def make_winch_channel
|
12
|
-
@winch, winch_ = IO.pipe
|
13
|
-
trap("WINCH") { winch_ << "\0" }
|
14
|
-
end
|
15
|
-
|
16
|
-
def handle_winch
|
17
|
-
@winch.read(1)
|
18
|
-
set_winsize
|
19
|
-
begin
|
20
|
-
Process.kill("WINCH", pid) if pid
|
21
|
-
rescue Errno::ESRCH
|
22
|
-
exit # the remote process died. Just quit.
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|