Skip to content
This repository
Browse code

WIP and docs

  • Loading branch information...
commit 432732f0b7a1255520879da14b95e895e5394f9d 1 parent 47bf120
Burke Libbey authored
3  cmd/zeus/zeus.go
@@ -2,9 +2,8 @@ package main
2 2
3 3 import (
4 4 "github.com/burke/zeus/zeus"
5   - "fmt"
6 5 )
7 6
8 7 func main () {
9   - fmt.Println(zeus.Run())
  8 + zeus.Run()
10 9 }
44 docs/master_slave_handshake.md
Source Rendered
... ... @@ -0,0 +1,44 @@
  1 +# Master/Slave Handshake
  2 +
  3 +#### 1. Socket
  4 +
  5 +The Slave is always started with an environment variable named `ZEUS_MASTER_FD`. The file descriptor at the given integer value is a socket to the Master process.
  6 +
  7 +The Slave should open a UNIX Domain Socket using the `ZEUS_MASTER_FD` File Descriptor (`globalMasterSock`).
  8 +
  9 +The Slave opens a new UNIX datagram Socketpair (`local`, `remote`)
  10 +
  11 +The Slave sends `remote` across `globalMasterSock`.
  12 +
  13 +#### 2. PID and Identifier
  14 +
  15 +The Slave determines whether it has been given an Identifier. If it is the first-booted slave, it was booted
  16 +by the Master, and will not have one. When a Slave forks, it is passed an Identifier by the Master that it
  17 +passes along to the newly-forked process.
  18 +
  19 +The Slave sends a JSON-encoded hash of "pid" and "identifier", with "pid" being the integer pid of the current process,
  20 +and "identifier" being the identifier, if there is one, or the empty string if none.
  21 +
  22 +#### 3. Action
  23 +
  24 +The Master now sends, along `remote`, a string containing the code representing the action for this Slave.
  25 +
  26 +The Slave evaluates this code.
  27 +
  28 +#### 4. Action Result
  29 +
  30 +If there were no runtime errors in evaluating the action, the Slave writes "OK" to `local`.
  31 +
  32 +If there were runtime errors, the slave returns a string representing the errors in an arbitrary and
  33 +hopefully helpful format. It should normally be identical to the console output format should the errors
  34 +have been raised and printed to stderr.
  35 +
  36 +#### 5. Loaded Files
  37 +
  38 +Any time after the action has been executed, the Slave may (and should) send, over `local`, a list of files
  39 +that have been newly-loaded in the course of evaluating the action.
  40 +
  41 +Languages are expected to implement this using clever tricks.
  42 +
  43 +Steps 1-4 happend sequentially and in-order, but Submitting files in Step 5 should not prevent the Slave from
  44 +handling further commands from the master. The Slave should be considered 'connected' after Step 4.
9 docs/terminology.md
Source Rendered
... ... @@ -0,0 +1,9 @@
  1 +# Terminology
  2 +
  3 +* a Client is a process initiated by the user requesting zeus to run a command.
  4 +
  5 +* the Master is the Go program which mediates all the interaction between the other processes
  6 +
  7 +* a Slave is a process managed by Zeus which is used to load dependencies for commands
  8 +
  9 +* a Command process is one forked from a Slave and connected to a Client
79 zeus/zeus.go
@@ -6,6 +6,7 @@ import (
6 6 "encoding/json"
7 7 "os/exec"
8 8 "fmt"
  9 + "errors"
9 10 )
10 11
11 12 type StageBootInfo struct {
@@ -13,56 +14,68 @@ type StageBootInfo struct {
13 14 Identifier string
14 15 }
15 16
16   -func readMessageFromRuby(fd int) (string, error) {
17   - newFile := fdToFile(fd, "ruby-sock")
18   - newSocket, _ := makeUnixSocket(newFile)
19   - msg, _, e := readFromUnixSocket(newSocket)
20   - if e != nil {
21   - panic(e)
22   - }
  17 +func runInitialCommand(sock *os.File) {
  18 + cmd := exec.Command("/Users/burke/.rbenv/shims/ruby", "/Users/burke/go/src/github.com/burke/zeus/a.rb")
  19 + cmd.Env = append(os.Environ(), fmt.Sprintf("ZEUS_MASTER_FD=%d", sock.Fd()))
  20 + cmd.ExtraFiles = []*os.File{sock}
23 21
24   - var bootInfo StageBootInfo
25   - err := json.Unmarshal([]byte(msg), &bootInfo)
  22 + go cmd.Run()
  23 +}
  24 +
  25 +func Run () (error) {
  26 + masterSockLocal, masterSockRemote, err := socketpair(syscall.SOCK_DGRAM)
26 27 if err != nil {
27   - return "", err
  28 + return err
28 29 }
29 30
30   - newSocket.Write([]byte("File.open('omg.log','a'){|f|f.puts 'HAHA BUSINESS'}"))
31   -
32   - msg2, _, _ := readFromUnixSocket(newSocket)
33   - if msg2 == "OK" {
34   - newSocket.Write([]byte("default_bundle"))
  31 + masterUSockLocal, err := makeUnixSocket(masterSockLocal)
  32 + if err != nil {
  33 + return err
35 34 }
36 35
37   - return "ya", nil
38   -}
  36 + runInitialCommand(masterSockRemote)
39 37
40   -func Run () (string) {
41   - lf, rf, err := socketpair(syscall.SOCK_DGRAM)
  38 + // Having just started the process, we expect an IO, which we convert to a UNIX domain socket
  39 + _, fd, err := readFromUnixSocket(masterUSockLocal)
42 40 if err != nil {
43   - panic(err)
  41 + return err
44 42 }
45   -
46   - local, err := makeUnixSocket(lf)
  43 + if fd < 0 {
  44 + return errors.New("expected file descriptor, but got none")
  45 + }
  46 + clientFile := fdToFile(fd, "boot-sock")
  47 + clientSocket, err := makeUnixSocket(clientFile)
47 48 if err != nil {
48   - panic(err)
  49 + return err
49 50 }
50 51
51   - cmd := exec.Command("/Users/burke/.rbenv/shims/ruby", "/Users/burke/go/src/github.com/burke/zeus/a.rb")
52   - cmd.Env = append(os.Environ(), fmt.Sprintf("ZEUS_MASTER_FD=%d", rf.Fd()))
53   - cmd.ExtraFiles = []*os.File{rf}
  52 + // We now expect the client to use this fd they send us to send a JSON-encoded representation of their PID and identifier...
  53 + msg, _, err := readFromUnixSocket(clientSocket)
  54 + if err != nil {
  55 + return err
  56 + }
  57 + var bootInfo StageBootInfo
  58 + err = json.Unmarshal([]byte(msg), &bootInfo)
  59 + if err != nil {
  60 + return err
  61 + }
54 62
55   - go cmd.Run()
  63 + // Now that we have that, we look up and send the action for that identifier:
  64 + clientSocket.Write([]byte("File.open('omg.log','a'){|f|f.puts 'HAHA BUSINESS TIME'}"))
56 65
57   - msg, fd, err := readFromUnixSocket(local)
  66 + // It will respond with its status
  67 + msg, _, err = readFromUnixSocket(clientSocket)
58 68 if err != nil {
59   - panic(err)
  69 + return err
60 70 }
61   - if fd >= 0 {
62   - str, _ := readMessageFromRuby(fd)
63   - return str
  71 + if msg == "OK" {
  72 + clientSocket.Write([]byte("default_bundle"))
  73 + } else {
  74 + return errors.New(msg)
64 75 }
65 76
66   - return msg
  77 + // And now we could instruct it to fork:
  78 +
  79 + return nil
67 80 }
68 81

0 comments on commit 432732f

Please sign in to comment.
Something went wrong with that request. Please try again.