Browse files

WIP and docs

  • Loading branch information...
1 parent 47bf120 commit 432732f0b7a1255520879da14b95e895e5394f9d @burke committed Aug 24, 2012
Showing with 100 additions and 35 deletions.
  1. +1 −2 cmd/zeus/zeus.go
  2. +44 −0 docs/master_slave_handshake.md
  3. +9 −0 docs/terminology.md
  4. +46 −33 zeus/zeus.go
View
3 cmd/zeus/zeus.go
@@ -2,9 +2,8 @@ package main
import (
"github.com/burke/zeus/zeus"
- "fmt"
)
func main () {
- fmt.Println(zeus.Run())
+ zeus.Run()
}
View
44 docs/master_slave_handshake.md
@@ -0,0 +1,44 @@
+# Master/Slave Handshake
+
+#### 1. Socket
+
+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.
+
+The Slave should open a UNIX Domain Socket using the `ZEUS_MASTER_FD` File Descriptor (`globalMasterSock`).
+
+The Slave opens a new UNIX datagram Socketpair (`local`, `remote`)
+
+The Slave sends `remote` across `globalMasterSock`.
+
+#### 2. PID and Identifier
+
+The Slave determines whether it has been given an Identifier. If it is the first-booted slave, it was booted
+by the Master, and will not have one. When a Slave forks, it is passed an Identifier by the Master that it
+passes along to the newly-forked process.
+
+The Slave sends a JSON-encoded hash of "pid" and "identifier", with "pid" being the integer pid of the current process,
+and "identifier" being the identifier, if there is one, or the empty string if none.
+
+#### 3. Action
+
+The Master now sends, along `remote`, a string containing the code representing the action for this Slave.
+
+The Slave evaluates this code.
+
+#### 4. Action Result
+
+If there were no runtime errors in evaluating the action, the Slave writes "OK" to `local`.
+
+If there were runtime errors, the slave returns a string representing the errors in an arbitrary and
+hopefully helpful format. It should normally be identical to the console output format should the errors
+have been raised and printed to stderr.
+
+#### 5. Loaded Files
+
+Any time after the action has been executed, the Slave may (and should) send, over `local`, a list of files
+that have been newly-loaded in the course of evaluating the action.
+
+Languages are expected to implement this using clever tricks.
+
+Steps 1-4 happend sequentially and in-order, but Submitting files in Step 5 should not prevent the Slave from
+handling further commands from the master. The Slave should be considered 'connected' after Step 4.
View
9 docs/terminology.md
@@ -0,0 +1,9 @@
+# Terminology
+
+* a Client is a process initiated by the user requesting zeus to run a command.
+
+* the Master is the Go program which mediates all the interaction between the other processes
+
+* a Slave is a process managed by Zeus which is used to load dependencies for commands
+
+* a Command process is one forked from a Slave and connected to a Client
View
79 zeus/zeus.go
@@ -6,63 +6,76 @@ import (
"encoding/json"
"os/exec"
"fmt"
+ "errors"
)
type StageBootInfo struct {
Pid int
Identifier string
}
-func readMessageFromRuby(fd int) (string, error) {
- newFile := fdToFile(fd, "ruby-sock")
- newSocket, _ := makeUnixSocket(newFile)
- msg, _, e := readFromUnixSocket(newSocket)
- if e != nil {
- panic(e)
- }
+func runInitialCommand(sock *os.File) {
+ cmd := exec.Command("/Users/burke/.rbenv/shims/ruby", "/Users/burke/go/src/github.com/burke/zeus/a.rb")
+ cmd.Env = append(os.Environ(), fmt.Sprintf("ZEUS_MASTER_FD=%d", sock.Fd()))
+ cmd.ExtraFiles = []*os.File{sock}
- var bootInfo StageBootInfo
- err := json.Unmarshal([]byte(msg), &bootInfo)
+ go cmd.Run()
+}
+
+func Run () (error) {
+ masterSockLocal, masterSockRemote, err := socketpair(syscall.SOCK_DGRAM)
if err != nil {
- return "", err
+ return err
}
- newSocket.Write([]byte("File.open('omg.log','a'){|f|f.puts 'HAHA BUSINESS'}"))
-
- msg2, _, _ := readFromUnixSocket(newSocket)
- if msg2 == "OK" {
- newSocket.Write([]byte("default_bundle"))
+ masterUSockLocal, err := makeUnixSocket(masterSockLocal)
+ if err != nil {
+ return err
}
- return "ya", nil
-}
+ runInitialCommand(masterSockRemote)
-func Run () (string) {
- lf, rf, err := socketpair(syscall.SOCK_DGRAM)
+ // Having just started the process, we expect an IO, which we convert to a UNIX domain socket
+ _, fd, err := readFromUnixSocket(masterUSockLocal)
if err != nil {
- panic(err)
+ return err
}
-
- local, err := makeUnixSocket(lf)
+ if fd < 0 {
+ return errors.New("expected file descriptor, but got none")
+ }
+ clientFile := fdToFile(fd, "boot-sock")
+ clientSocket, err := makeUnixSocket(clientFile)
if err != nil {
- panic(err)
+ return err
}
- cmd := exec.Command("/Users/burke/.rbenv/shims/ruby", "/Users/burke/go/src/github.com/burke/zeus/a.rb")
- cmd.Env = append(os.Environ(), fmt.Sprintf("ZEUS_MASTER_FD=%d", rf.Fd()))
- cmd.ExtraFiles = []*os.File{rf}
+ // We now expect the client to use this fd they send us to send a JSON-encoded representation of their PID and identifier...
+ msg, _, err := readFromUnixSocket(clientSocket)
+ if err != nil {
+ return err
+ }
+ var bootInfo StageBootInfo
+ err = json.Unmarshal([]byte(msg), &bootInfo)
+ if err != nil {
+ return err
+ }
- go cmd.Run()
+ // Now that we have that, we look up and send the action for that identifier:
+ clientSocket.Write([]byte("File.open('omg.log','a'){|f|f.puts 'HAHA BUSINESS TIME'}"))
- msg, fd, err := readFromUnixSocket(local)
+ // It will respond with its status
+ msg, _, err = readFromUnixSocket(clientSocket)
if err != nil {
- panic(err)
+ return err
}
- if fd >= 0 {
- str, _ := readMessageFromRuby(fd)
- return str
+ if msg == "OK" {
+ clientSocket.Write([]byte("default_bundle"))
+ } else {
+ return errors.New(msg)
}
- return msg
+ // And now we could instruct it to fork:
+
+ return nil
}

0 comments on commit 432732f

Please sign in to comment.