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