func (me *Master) createMirror(addr string, jobs int) (*mirrorConnection, os.Error) { conn, err := DialTypedConnection(addr, RPC_CHANNEL, me.secret) if err != nil { return nil, err } defer conn.Close() rpcId := ConnectionId() rpcConn, err := DialTypedConnection(addr, rpcId, me.secret) if err != nil { return nil, err } revId := ConnectionId() revConn, err := DialTypedConnection(addr, revId, me.secret) if err != nil { rpcConn.Close() return nil, err } req := CreateMirrorRequest{ RpcId: rpcId, RevRpcId: revId, WritableRoot: me.writableRoot, MaxJobCount: jobs, } rep := CreateMirrorResponse{} cl := rpc.NewClient(conn) err = cl.Call("WorkerDaemon.CreateMirror", &req, &rep) if err != nil { revConn.Close() rpcConn.Close() return nil, err } go me.fileServerRpc.ServeConn(revConn) mc := &mirrorConnection{ rpcClient: rpc.NewClient(rpcConn), connection: rpcConn, maxJobs: rep.GrantedJobCount, availableJobs: rep.GrantedJobCount, } mc.queueFiles(me.fileServer.copyCache()) return mc, nil }
func TestEndToEndShutdown(t *testing.T) { if os.Geteuid() == 0 { log.Println("This test should not run as root") return } tc := NewTestCase(t) defer tc.Clean() // In the test, shutdown doesn't really exit the worker, since // we can't stop the already running accept(); retry would // cause the test to hang. tc.master.retryCount = 0 req := WorkRequest{ Binary: tc.FindBin("touch"), Argv: []string{"touch", "file.txt"}, Env: testEnv(), Dir: tc.tmp + "/wd", } rep := tc.Run(req) hostname, err := os.Hostname() if err != nil { t.Fatalf("hostname error %v", err) } conn, err := DialTypedConnection( fmt.Sprintf("%s:%d", hostname, tc.workerPort), RPC_CHANNEL, tc.secret) if conn == nil { t.Fatal("DialTypedConnection to shutdown worker: ", err) } stopReq := 1 stopRep := 1 err = rpc.NewClient(conn).Call("WorkerDaemon.Shutdown", &stopReq, &stopRep) if err != nil { t.Errorf("Shutdown insuccessful: %v", err) } rpcConn := OpenSocketConnection(tc.socket, RPC_CHANNEL) err = rpc.NewClient(rpcConn).Call("LocalMaster.Run", &req, &rep) if err == nil { t.Error("LocalMaster.Run should fail after shutdown") } // TODO - check that DialTypedConnection to worker stops working? }
func (me *testCase) Run(req WorkRequest) (rep WorkReply) { rpcConn := OpenSocketConnection(me.socket, RPC_CHANNEL) client := rpc.NewClient(rpcConn) err := client.Call("LocalMaster.Run", &req, &rep) if err != nil { me.tester.Fatal("LocalMaster.Run: ", err) } return rep }
func Refresh() { socket := termite.FindSocket() conn := termite.OpenSocketConnection(socket, termite.RPC_CHANNEL) client := rpc.NewClient(conn) req := 1 rep := 1 err := client.Call("LocalMaster.RefreshAttributeCache", &req, &rep) if err != nil { log.Fatal("LocalMaster.RefreshAttributeCache: ", err) } conn.Close() }
func (me *Master) setupWorkers(addresses []string) { var out []*rpc.Client for _, addr := range addresses { conn, err := SetupClient(addr, me.secret) if err != nil { log.Println("Failed setting up connection with: ", addr, err) continue } out = append(out, rpc.NewClient(conn)) } if len(out) == 0 { log.Fatal("No workers available.") } me.workServers = out }
func NewMirror(daemon *WorkerDaemon, rpcConn, revConn net.Conn) *Mirror { log.Println("Mirror for", rpcConn, revConn) mirror := &Mirror{ fileServerConn: revConn, rpcConn: rpcConn, fileServer: rpc.NewClient(revConn), daemon: daemon, workingFileSystems: make(map[*WorkerFuseFs]string), } mirror.rpcFs = NewRpcFs(mirror.fileServer, daemon.contentCache) mirror.rpcFs.localRoots = []string{"/lib", "/usr"} mirror.cond = sync.NewCond(&mirror.fuseFileSystemsMutex) go mirror.serveRpc() return mirror }
func test_rpc() { fmt.Println("Testing rpc with TimeService...") cli, srv := net.Pipe() go rpc.ServeConn(srv) client := rpc.NewClient(cli) defer client.Close() // Synchronous calls args := &Args{"GMT"} reply := new(Reply) for i := 0; i < 10; i++ { err := client.Call("TimeService.GetTime", args, reply) if err != nil { fmt.Errorf("TimeService.GetTime: expected no error but got string %q", err.String()) } fmt.Printf("time:%s\n rpc.counter:%d\n", reply.Time, reply.Counter) } }
func (me *Coordinator) workerHandler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/html") q := req.URL.Query() vs, ok := q["host"] if !ok || len(vs) == 0 { fmt.Fprintf(w, "<html><body>404 query param 'host' missing</body></html>") return } addr := string(vs[0]) fmt.Fprintf(w, "<html><head><title>Termite worker status</title></head>") fmt.Fprintf(w, "<body><h1>Status %s</h1>", addr) defer fmt.Fprintf(w, "</body></html>") if !me.haveWorker(addr) { fmt.Fprintf(w, "<p><tt>worker %q unknown<tt>", addr) return } conn, err := DialTypedConnection(addr, RPC_CHANNEL, me.secret) if err != nil { fmt.Fprintf(w, "<p><tt>error dialing: %v<tt>", err) return } statusReq := WorkerStatusRequest{} status := WorkerStatusResponse{} client := rpc.NewClient(conn) err = client.Call("WorkerDaemon.Status", &statusReq, &status) if err != nil { fmt.Fprintf(w, "<p><tt>RPC error: %v<tt>\n", err) return } fmt.Fprintf(w, "<p>Worker %s<p>Version %s<p>Jobs %d\n", addr, status.Version, status.MaxJobCount) for _, mirrorStatus := range status.MirrorStatus { me.mirrorStatusHtml(w, mirrorStatus) } fmt.Fprintf(w, "<p><a href=\"/workerkill?host=%s\">Kill worker %s</a>\n", addr, addr) conn.Close() }
func main() { cachedir := flag.String("cachedir", "/tmp/termite-cache", "content cache") server := flag.String("server", "localhost:1234", "file server") secretFile := flag.String("secret", "/tmp/secret.txt", "file containing password.") flag.Parse() if flag.NArg() < 1 { fmt.Fprintf(os.Stderr, "usage: %s MOUNTPOINT\n", os.Args[0]) os.Exit(2) } secret, err := ioutil.ReadFile(*secretFile) if err != nil { log.Fatal("ReadFile", err) } rpcConn, err := termite.SetupClient(*server, secret) if err != nil { log.Fatal("dialing:", err) } var fs fuse.FileSystem cache := termite.NewDiskFileCache(*cachedir) fs = termite.NewRpcFs(rpc.NewClient(rpcConn), cache) conn := fuse.NewFileSystemConnector(fs, nil) state := fuse.NewMountState(conn) opts := fuse.MountOptions{} if os.Geteuid() == 0 { opts.AllowOther = true } state.Mount(flag.Arg(0), &opts) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } state.Debug = true state.Loop(false) }
func (me *Coordinator) killHandler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/html") q := req.URL.Query() vs, ok := q["host"] if !ok || len(vs) == 0 { fmt.Fprintf(w, "<html><body>404 query param 'host' missing</body></html>") return } addr := string(vs[0]) fmt.Fprintf(w, "<html><head><title>Termite worker status</title></head>") fmt.Fprintf(w, "<body><h1>Status %s</h1>", addr) defer fmt.Fprintf(w, "</body></html>") if !me.haveWorker(addr) { fmt.Fprintf(w, "<p><tt>worker %q unknown<tt>", addr) return } conn, err := DialTypedConnection(addr, RPC_CHANNEL, me.secret) if err != nil { fmt.Fprintf(w, "<p><tt>error dialing: %v<tt>", err) return } killReq := 1 rep := 1 err = rpc.NewClient(conn).Call("WorkerDaemon.Shutdown", &killReq, &rep) if err != nil { fmt.Fprintf(w, "<p><tt>RPC error: %v<tt>", err) return } fmt.Fprintf(w, "<p>Shutdown of %s in progress", addr) conn.Close() }
func main() { command := flag.String("c", "", "command to run.") refresh := flag.Bool("refresh", false, "refresh master file cache.") debug := flag.Bool("dbg", false, "set on debugging in request.") flag.Parse() if *refresh { Refresh() } if *command == "" { return } os.Args[0] = _SHELL TryRunDirect(*command) socket := termite.FindSocket() if socket == "" { log.Fatal("Could not find .termite-socket") } topDir, _ := filepath.Split(socket) localWaitMsg, localRule := TryRunLocally(*command, topDir) if localWaitMsg != nil && !localRule.SkipRefresh { Refresh() } wd, err := os.Getwd() if err != nil { log.Fatal("Getwd", err) } conn := termite.OpenSocketConnection(socket, termite.RPC_CHANNEL) // TODO - could skip the shell if we can deduce it is a // no-frills command invocation. req := termite.WorkRequest{ Binary: _SHELL, Argv: []string{"/bin/sh", "-c", *command}, Env: cleanEnv(os.Environ()), Dir: wd, Debug: os.Getenv("TERMITE_DEBUG") != "" || *debug, RanLocally: localWaitMsg != nil, } client := rpc.NewClient(conn) rep := termite.WorkReply{} err = client.Call("LocalMaster.Run", &req, &rep) if err != nil { log.Fatal("LocalMaster.Run: ", err) } os.Stdout.Write([]byte(rep.Stdout)) os.Stderr.Write([]byte(rep.Stderr)) // TODO -something with signals. if localWaitMsg == nil { localWaitMsg = &rep.Exit } conn.Close() os.Exit(localWaitMsg.ExitStatus()) }
func TestRpcFS(t *testing.T) { tmp, _ := ioutil.TempDir("", "") defer os.RemoveAll(tmp) mnt := tmp + "/mnt" orig := tmp + "/orig" srvCache := tmp + "/server-cache" clientCache := tmp + "/client-cache" os.Mkdir(mnt, 0700) os.Mkdir(orig, 0700) os.Mkdir(orig+"/subdir", 0700) content := "hello" err := ioutil.WriteFile(orig+"/file.txt", []byte(content), 0644) if err != nil { t.Fatal(err) } cache := NewContentCache(srvCache) server := NewFsServer(orig, cache, []string{}) l, r, err := fuse.Socketpair("unix") if err != nil { t.Fatal(err) } defer l.Close() defer r.Close() rpcServer := rpc.NewServer() rpcServer.Register(server) go rpcServer.ServeConn(l) rpcClient := rpc.NewClient(r) fs := NewRpcFs(rpcClient, NewContentCache(clientCache)) state, _, err := fuse.MountPathFileSystem(mnt, fs, nil) state.Debug = true if err != nil { t.Fatal("Mount", err) } defer func() { log.Println("unmounting") err := state.Unmount() if err == nil { os.RemoveAll(tmp) } }() go state.Loop(false) fi, err := os.Lstat(mnt + "/subdir") if fi == nil || !fi.IsDirectory() { t.Fatal("subdir stat", fi, err) } c, err := ioutil.ReadFile(mnt + "/file.txt") if err != nil || string(c) != "hello" { t.Error("Readfile", c) } entries, err := ioutil.ReadDir(mnt) if err != nil || len(entries) != 2 { t.Error("Readdir", err, entries) } // This test implementation detail - should be separate? storedHash := server.hashCache["/file.txt"] if storedHash == "" || string(storedHash) != string(md5str(content)) { t.Errorf("cache error %x (%v)", storedHash, storedHash) } newData := []FileAttr{ FileAttr{ Path: "/file.txt", Hash: md5str("somethingelse"), }, } server.updateFiles(newData) storedHash = server.hashCache["/file.txt"] if storedHash == "" || storedHash != newData[0].Hash { t.Errorf("cache error %x (%v)", storedHash, storedHash) } }