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