Merge "veyron/runtimes/google/ipc: Fix race in choosing an error to return when the flow closes."
diff --git a/lib/expect/expect.go b/lib/expect/expect.go
index c36bec4..8f0bb72 100644
--- a/lib/expect/expect.go
+++ b/lib/expect/expect.go
@@ -100,16 +100,20 @@
s.verbose = v
}
-func (s *Session) log(format string, args ...interface{}) {
+func (s *Session) log(err error, format string, args ...interface{}) {
if !s.verbose {
return
}
_, path, line, _ := runtime.Caller(2)
+ errstr := ""
+ if err != nil {
+ errstr = err.Error() + ": "
+ }
loc := fmt.Sprintf("%s:%d", filepath.Base(path), line)
o := strings.TrimRight(fmt.Sprintf(format, args...), "\n\t ")
- vlog.VI(2).Infof("%s: %s", loc, o)
+ vlog.VI(2).Infof("%s: %s%s", loc, errstr, o)
if s.t == nil {
- fmt.Fprint(os.Stderr, loc, o)
+ fmt.Fprintf(os.Stderr, "%s: %s%s\n", loc, errstr, o)
return
}
s.t.Log(loc, o)
@@ -181,7 +185,7 @@
return
}
line, err := s.read(readLine)
- s.log("Expect: %v: %s", err, line)
+ s.log(err, "Expect: %s", line)
if err != nil {
s.error(err)
return
@@ -217,7 +221,7 @@
return [][]string{}
}
l, m, err := s.expectRE(pattern, n)
- s.log("ExpectRE: %v: %s", err, l)
+ s.log(err, "ExpectVar: %s", l)
if err != nil {
s.error(err)
return [][]string{}
@@ -235,7 +239,7 @@
return ""
}
l, m, err := s.expectRE(name+"=(.*)", 1)
- s.log("ExpectVar: %v: %s", err, l)
+ s.log(err, "ExpectVar: %s", l)
if err != nil {
s.error(err)
return ""
@@ -255,7 +259,7 @@
return ""
}
l, err := s.read(readLine)
- s.log("Readline: %v: %s", err, l)
+ s.log(err, "Readline: %s", l)
if err != nil {
s.error(err)
}
diff --git a/lib/modules/core/core.go b/lib/modules/core/core.go
index 98a6656..ccb14dd 100644
--- a/lib/modules/core/core.go
+++ b/lib/modules/core/core.go
@@ -87,7 +87,7 @@
prints the current time`)
shell.AddFunction(NamespaceCacheCommand, namespaceCache, `on|off
turns the namespace cache on or off`)
- shell.AddFunction(MountCommand, mountServer, `<mountpoint> <server> <ttl>
+ shell.AddFunction(MountCommand, mountServer, `<mountpoint> <server> <ttl> [M][R]
invokes namespace.Mount(<mountpoint>, <server>, <ttl>)`)
shell.AddSubprocess(EchoClientCommand, `<name> <message>...
invokes name.Echo(message)`)
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index 6d1731d..a9094bb 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -21,7 +21,7 @@
func TestCommands(t *testing.T) {
shell := core.NewShell()
- defer shell.Cleanup(os.Stderr)
+ defer shell.Cleanup(nil, os.Stderr)
for _, c := range []string{core.RootMTCommand, core.MTCommand} {
if len(shell.Help(c)) == 0 {
t.Fatalf("missing command %q", c)
@@ -37,9 +37,9 @@
sh := core.NewShell()
return sh, func() {
if testing.Verbose() {
- sh.Cleanup(os.Stderr)
+ sh.Cleanup(os.Stderr, os.Stderr)
} else {
- sh.Cleanup(nil)
+ sh.Cleanup(nil, nil)
}
}
}
@@ -56,7 +56,6 @@
s.ExpectVar("MT_ADDR")
s.ExpectVar("PID")
root.CloseStdin()
- s.Expect("PASS")
}
func startMountTables(t *testing.T, sh *modules.Shell, mountPoints ...string) (map[string]string, error) {
@@ -235,7 +234,7 @@
if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
t.Errorf("got %v, want %v", got, want)
}
- if err = resolver.Shutdown(nil); err != nil {
+ if err = resolver.Shutdown(nil, os.Stderr); err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -252,7 +251,7 @@
if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
t.Fatalf("got %v, want %v", got, want)
}
- if err := resolver.Shutdown(nil); err != nil {
+ if err := resolver.Shutdown(nil, os.Stderr); err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -261,7 +260,7 @@
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- if err := nsroots.Shutdown(nil); err != nil {
+ if err := nsroots.Shutdown(nil, os.Stderr); err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -276,16 +275,11 @@
if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
t.Fatalf("got %v, want %v", got, want)
}
- if err := resolver.Shutdown(nil); err != nil {
+ if err := resolver.Shutdown(nil, os.Stderr); err != nil {
t.Fatalf("unexpected error: %s", err)
}
}
func TestHelperProcess(t *testing.T) {
- if !modules.IsTestHelperProcess() {
- return
- }
- if err := modules.Dispatch(); err != nil {
- t.Fatalf("failed: %v", err)
- }
+ modules.DispatchInTest()
}
diff --git a/lib/modules/core/echo.go b/lib/modules/core/echo.go
index f3c3fce..459e7fe 100644
--- a/lib/modules/core/echo.go
+++ b/lib/modules/core/echo.go
@@ -37,10 +37,10 @@
}
func echoServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- if len(args) != 2 {
+ if len(args) != 3 {
return fmt.Errorf("wrong # args")
}
- id, mp := args[0], args[1]
+ id, mp := args[1], args[2]
disp := &treeDispatcher{id: id}
server, err := rt.R().NewServer()
if err != nil {
@@ -62,11 +62,11 @@
}
func echoClient(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- if len(args) < 2 {
+ if len(args) < 3 {
return fmt.Errorf("wrong # args")
}
- name := args[0]
- args = args[1:]
+ name := args[1]
+ args = args[2:]
client := rt.R().Client()
for _, a := range args {
ctxt := rt.R().NewContext()
diff --git a/lib/modules/core/misc.go b/lib/modules/core/misc.go
index 033fd09..e70aa02 100644
--- a/lib/modules/core/misc.go
+++ b/lib/modules/core/misc.go
@@ -13,9 +13,9 @@
func sleep(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
d := time.Second
- if len(args) > 0 {
+ if len(args) > 1 {
var err error
- if d, err = time.ParseDuration(args[0]); err != nil {
+ if d, err = time.ParseDuration(args[1]); err != nil {
return err
}
}
@@ -42,28 +42,39 @@
}
func mountServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- if len(args) != 3 {
+ if len(args) < 4 {
return fmt.Errorf("wrong # args")
}
- mp, server, ttlstr := args[0], args[1], args[2]
+ var opts []naming.MountOpt
+ for _, arg := range args[4:] {
+ for _, c := range arg {
+ switch c {
+ case 'R':
+ opts = append(opts, naming.ReplaceMountOpt(true))
+ case 'M':
+ opts = append(opts, naming.ServesMountTableOpt(true))
+ }
+ }
+ }
+ mp, server, ttlstr := args[1], args[2], args[3]
ttl, err := time.ParseDuration(ttlstr)
if err != nil {
return fmt.Errorf("failed to parse time from %q", ttlstr)
}
ns := rt.R().Namespace()
- if err := ns.Mount(rt.R().NewContext(), mp, server, ttl); err != nil {
+ if err := ns.Mount(rt.R().NewContext(), mp, server, ttl, opts...); err != nil {
return err
}
- fmt.Fprintf(stdout, "Mount(%s, %s, %s)\n", mp, server, ttl)
+ fmt.Fprintf(stdout, "Mount(%s, %s, %s, %v)\n", mp, server, ttl, opts)
return nil
}
func namespaceCache(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- if len(args) != 1 {
+ if len(args) != 2 {
return fmt.Errorf("wrong # args")
}
disable := true
- switch args[0] {
+ switch args[1] {
case "on":
disable = false
case "off":
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
index d21d2c0..b013862 100644
--- a/lib/modules/core/mounttable.go
+++ b/lib/modules/core/mounttable.go
@@ -23,14 +23,14 @@
}
func mountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- if len(args) != 1 {
+ if len(args) != 2 {
return fmt.Errorf("expected exactly one argument: <mount point>")
}
return runMT(false, stdin, stdout, stderr, env, args...)
}
func rootMountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- if len(args) != 0 {
+ if len(args) != 1 {
return fmt.Errorf("expected no arguments")
}
return runMT(true, stdin, stdout, stderr, env, args...)
@@ -44,7 +44,7 @@
}
mp := ""
if !root {
- mp = args[0]
+ mp = args[1]
}
mt, err := mounttable.NewMountTable("")
if err != nil {
@@ -67,6 +67,7 @@
func ls(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
details := false
+ args = args[1:] // skip over comamnd name
if len(args) > 0 && args[0] == "-l" {
details = true
args = args[1:]
@@ -106,10 +107,10 @@
type resolver func(ctx context.T, name string) (names []string, err error)
func resolve(fn resolver, stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- if len(args) != 1 {
+ if len(args) != 2 {
return fmt.Errorf("wrong # args")
}
- name := args[0]
+ name := args[1]
servers, err := fn(rt.R().NewContext(), name)
if err != nil {
fmt.Fprintf(stdout, "RN=0\n")
@@ -132,5 +133,8 @@
func setNamespaceRoots(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
ns := rt.R().Namespace()
- return ns.SetRoots(args...)
+ if len(args) < 2 {
+ return fmt.Errorf("wrong # args")
+ }
+ return ns.SetRoots(args[1:]...)
}
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index b2a12da..5be34de 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -1,11 +1,9 @@
package modules
import (
- "bufio"
"flag"
"fmt"
"io"
- "io/ioutil"
"os"
"os/exec"
"strings"
@@ -137,7 +135,8 @@
newargs := append(testFlags(), args...)
cmd := exec.Command(os.Args[0], newargs...)
cmd.Env = append(sh.mergeOSEnvSlice(), eh.entryPoint)
- stderr, err := ioutil.TempFile("", "__modules__"+strings.TrimLeft(eh.entryPoint, "-\n\t "))
+ fname := strings.TrimPrefix(eh.entryPoint, ShellEntryPoint+"=")
+ stderr, err := newLogfile(strings.TrimLeft(fname, "-\n\t "))
if err != nil {
return nil, err
}
@@ -164,28 +163,37 @@
return eh, err
}
-func (eh *execHandle) Shutdown(output io.Writer) error {
+func (eh *execHandle) Shutdown(stdout, stderr io.Writer) error {
eh.mu.Lock()
defer eh.mu.Unlock()
eh.stdin.Close()
+ logFile := eh.stderr.Name()
defer eh.sh.forget(eh)
- if eh.stderr != nil {
- defer func() {
- eh.stderr.Close()
- os.Remove(eh.stderr.Name())
- }()
- if output == nil {
- return eh.cmd.Wait()
- }
- if _, err := eh.stderr.Seek(0, 0); err != nil {
- return eh.cmd.Wait()
- }
- scanner := bufio.NewScanner(eh.stderr)
- for scanner.Scan() {
- fmt.Fprintf(output, "%s\n", scanner.Text())
- }
+
+ defer func() {
+ os.Remove(logFile)
+ }()
+
+ if stdout == nil && stderr == nil {
+ return eh.cmd.Wait()
}
- return eh.cmd.Wait()
+ // Read from stdin before waiting for the child process to ensure
+ // that we get to read all of its output.
+ readTo(eh.stdout, stdout)
+
+ procErr := eh.cmd.Wait()
+
+ // Stderr is buffered to a file, so we can safely read it after we
+ // wait for the process.
+ eh.stderr.Close()
+ stderrFile, err := os.Open(logFile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to open %q: %s", logFile, err)
+ return procErr
+ }
+ readTo(stderrFile, stderr)
+ stderrFile.Close()
+ return procErr
}
const ShellEntryPoint = "VEYRON_SHELL_HELPER_PROCESS_ENTRY_POINT"
@@ -196,7 +204,25 @@
child.mains[name] = main
}
+// DispatchInTest will execute the requested subproccess command from within
+// a unit test run as a subprocess.
+func DispatchInTest() {
+ if !IsTestHelperProcess() {
+ return
+ }
+ if err := child.dispatch(); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed: %s\n", err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
+
+// Dispatch will execute the request subprocess command from a within a
+// a subprocess that is not a unit test.
func Dispatch() error {
+ if IsTestHelperProcess() {
+ return fmt.Errorf("use DispatchInTest in unittests")
+ }
return child.dispatch()
}
diff --git a/lib/modules/func.go b/lib/modules/func.go
index f44127b..6273c64 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -1,7 +1,6 @@
package modules
import (
- "bufio"
"fmt"
"io"
"os"
@@ -12,12 +11,13 @@
r, w *os.File
}
type functionHandle struct {
- mu sync.Mutex
- main Main
- stdin, stderr, stdout pipe
- err error
- sh *Shell
- wg sync.WaitGroup
+ mu sync.Mutex
+ main Main
+ stdin, stdout pipe
+ stderr *os.File
+ err error
+ sh *Shell
+ wg sync.WaitGroup
}
func newFunctionHandle(main Main) command {
@@ -33,7 +33,7 @@
func (fh *functionHandle) Stderr() io.Reader {
fh.mu.Lock()
defer fh.mu.Unlock()
- return fh.stderr.r
+ return os.NewFile(fh.stderr.Fd(), "stderr")
}
func (fh *functionHandle) Stdin() io.Writer {
@@ -52,19 +52,24 @@
fh.mu.Lock()
defer fh.mu.Unlock()
fh.sh = sh
- for _, p := range []*pipe{&fh.stdin, &fh.stdout, &fh.stderr} {
+ for _, p := range []*pipe{&fh.stdin, &fh.stdout} {
var err error
if p.r, p.w, err = os.Pipe(); err != nil {
return nil, err
}
}
+ stderr, err := newLogfile(args[0])
+ if err != nil {
+ return nil, err
+ }
+ fh.stderr = stderr
fh.wg.Add(1)
go func() {
fh.mu.Lock()
stdin := fh.stdin.r
stdout := fh.stdout.w
- stderr := fh.stderr.w
+ stderr := fh.stderr
main := fh.main
fh.mu.Unlock()
@@ -76,7 +81,6 @@
fh.mu.Lock()
fh.stdin.r.Close()
fh.stdout.w.Close()
- fh.stderr.w.Close()
fh.err = err
fh.mu.Unlock()
fh.wg.Done()
@@ -84,26 +88,34 @@
return fh, nil
}
-func (fh *functionHandle) Shutdown(output io.Writer) error {
+func (fh *functionHandle) Shutdown(stdout_w, stderr_w io.Writer) error {
fh.mu.Lock()
fh.stdin.w.Close()
- stderr := fh.stderr.r
+ stdout := fh.stdout.r
+ stderr := fh.stderr
fh.mu.Unlock()
- if output != nil {
- scanner := bufio.NewScanner(stderr)
- for scanner.Scan() {
- l := scanner.Text()
- fmt.Fprintf(output, "%s\n", l)
- }
+ // Read stdout until EOF to ensure that we read all of it.
+ readTo(stdout, stdout_w)
+ fh.wg.Wait()
+
+ fh.mu.Lock()
+ funcErr := fh.err
+ fh.mu.Unlock()
+
+ // Safe to close stderr now.
+ stderr.Close()
+ stderr, err := os.Open(stderr.Name())
+ if err == nil {
+ readTo(stderr, stderr_w)
+ stderr.Close()
+ } else {
+ fmt.Fprintf(os.Stderr, "failed to open %q: %s", stderr.Name(), err)
}
- fh.wg.Wait()
fh.mu.Lock()
fh.stdout.r.Close()
- fh.stderr.r.Close()
- err := fh.err
fh.sh.forget(fh)
fh.mu.Unlock()
- return err
+ return funcErr
}
diff --git a/lib/modules/modules_internal_test.go b/lib/modules/modules_internal_test.go
index 000819b..c5e686e 100644
--- a/lib/modules/modules_internal_test.go
+++ b/lib/modules/modules_internal_test.go
@@ -43,7 +43,7 @@
assertNumHandles(t, sh, 2)
for i, h := range []Handle{hs, hf} {
- if got := h.Shutdown(nil); got != nil {
+ if got := h.Shutdown(nil, nil); got != nil {
t.Errorf("%d: got %q, want %q", i, got, nil)
}
}
@@ -53,6 +53,6 @@
hf, _ = sh.Start("echof", "c")
assertNumHandles(t, sh, 2)
- sh.Cleanup(nil)
+ sh.Cleanup(nil, nil)
assertNumHandles(t, sh, 0)
}
diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go
index cd7db53..7320eb6 100644
--- a/lib/modules/modules_test.go
+++ b/lib/modules/modules_test.go
@@ -2,9 +2,9 @@
import (
"bufio"
+ "bytes"
"fmt"
"io"
- "os"
"testing"
"time"
@@ -13,9 +13,31 @@
func init() {
modules.RegisterChild("envtest", PrintEnv)
+ modules.RegisterChild("echo", Echo)
modules.RegisterChild("errortest", ErrorMain)
}
+func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ for _, a := range args {
+ fmt.Fprintf(stdout, "stdout: %s\n", a)
+ fmt.Fprintf(stderr, "stderr: %s\n", a)
+ }
+ return nil
+}
+
+func Print(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ for _, a := range args {
+ if v := env[a]; len(v) > 0 {
+ fmt.Fprintf(stdout, a+"="+v+"\n")
+ } else {
+ fmt.Fprintf(stderr, "missing %s\n", a)
+ }
+ }
+ modules.WaitForEOF(stdin)
+ fmt.Fprintf(stdout, "done\n")
+ return nil
+}
+
func PrintEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
for _, a := range args {
if v := env[a]; len(v) > 0 {
@@ -53,7 +75,14 @@
t.Fatalf("unexpected error: %s", err)
}
defer func() {
- sh.Cleanup(os.Stderr)
+ var stdout, stderr bytes.Buffer
+ sh.Cleanup(&stdout, &stderr)
+ if len(stdout.String()) != 0 {
+ t.Errorf("unexpected stdout: %q", stdout.String())
+ }
+ if len(stderr.String()) != 0 {
+ t.Errorf("unexpected stderr: %q", stderr.String())
+ }
}()
scanner := bufio.NewScanner(h.Stdout())
if !waitForInput(scanner) {
@@ -71,13 +100,14 @@
if got, want := scanner.Text(), "done"; got != want {
t.Errorf("got %q, want %q", got, want)
}
- if err := h.Shutdown(nil); err != nil {
+ if err := h.Shutdown(nil, nil); err != nil {
t.Fatalf("unexpected error: %s", err)
}
}
func TestChild(t *testing.T) {
sh := modules.NewShell()
+ defer sh.Cleanup(nil, nil)
key, val := "simpleVar", "foo & bar"
sh.SetVar(key, val)
sh.AddSubprocess("envtest", "envtest: <variables to print>...")
@@ -86,6 +116,7 @@
func TestFunction(t *testing.T) {
sh := modules.NewShell()
+ defer sh.Cleanup(nil, nil)
key, val := "simpleVar", "foo & bar & baz"
sh.SetVar(key, val)
sh.AddFunction("envtest", PrintEnv, "envtest: <variables to print>...")
@@ -94,35 +125,67 @@
func TestErrorChild(t *testing.T) {
sh := modules.NewShell()
+ defer sh.Cleanup(nil, nil)
sh.AddSubprocess("errortest", "")
h, err := sh.Start("errortest")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- if got, want := h.Shutdown(nil), "exit status 1"; got == nil || got.Error() != want {
+ if got, want := h.Shutdown(nil, nil), "exit status 1"; got == nil || got.Error() != want {
t.Errorf("got %q, want %q", got, want)
}
}
+func testShutdown(t *testing.T, sh *modules.Shell) {
+ result := ""
+ args := []string{"a", "b c", "ddd"}
+ if _, err := sh.Start("echo", args...); err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ var stdoutBuf bytes.Buffer
+ var stderrBuf bytes.Buffer
+ sh.Cleanup(&stdoutBuf, &stderrBuf)
+ stdoutOutput, stderrOutput := "stdout: echo\n", "stderr: echo\n"
+ for _, a := range args {
+ stdoutOutput += fmt.Sprintf("stdout: %s\n", a)
+ stderrOutput += fmt.Sprintf("stderr: %s\n", a)
+ }
+ if got, want := stdoutBuf.String(), stdoutOutput+result; got != want {
+ t.Errorf("got %q want %q", got, want)
+ }
+ if got, want := stderrBuf.String(), stderrOutput; got != want {
+ t.Errorf("got %q want %q", got, want)
+ }
+
+}
+
+func TestShutdownSubprocess(t *testing.T) {
+ sh := modules.NewShell()
+ sh.AddSubprocess("echo", "[args]*")
+ testShutdown(t, sh)
+}
+
+func TestShutdownFunction(t *testing.T) {
+ sh := modules.NewShell()
+ sh.AddFunction("echo", Echo, "[args]*")
+ testShutdown(t, sh)
+}
+
func TestErrorFunc(t *testing.T) {
sh := modules.NewShell()
+ defer sh.Cleanup(nil, nil)
sh.AddFunction("errortest", ErrorMain, "")
h, err := sh.Start("errortest")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- if got, want := h.Shutdown(nil), "an error"; got != nil && got.Error() != want {
+ if got, want := h.Shutdown(nil, nil), "an error"; got != nil && got.Error() != want {
t.Errorf("got %q, want %q", got, want)
}
}
func TestHelperProcess(t *testing.T) {
- if !modules.IsTestHelperProcess() {
- return
- }
- if err := modules.Dispatch(); err != nil {
- t.Fatalf("failed: %v", err)
- }
+ modules.DispatchInTest()
}
// TODO(cnicolaou): more complete tests for environment variables,
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 561e3b2..26c3869 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -175,7 +175,7 @@
sh.mu.Unlock()
return nil, fmt.Errorf("command %q is not available", command)
}
- expanded := sh.expand(args...)
+ expanded := append([]string{command}, sh.expand(args...)...)
sh.mu.Unlock()
h, err := cmd.factory().start(sh, expanded...)
if err != nil {
@@ -238,10 +238,9 @@
}
// Cleanup calls Shutdown on all of the Handles currently being tracked
-// by the Shell. Any buffered output from the command's stderr stream
-// will be written to the supplied io.Writer. If the io.Writer is nil
-// then any such output is lost.
-func (sh *Shell) Cleanup(output io.Writer) {
+// by the Shell and writes to stdout and stderr as per the Shutdown
+// method in the Handle interface.
+func (sh *Shell) Cleanup(stdout, stderr io.Writer) {
sh.mu.Lock()
handles := make(map[Handle]struct{})
for k, v := range sh.handles {
@@ -250,7 +249,7 @@
sh.handles = make(map[Handle]struct{})
sh.mu.Unlock()
for k, _ := range handles {
- k.Shutdown(output)
+ k.Shutdown(stdout, stderr)
}
if len(sh.idfile) > 0 {
os.Remove(sh.idfile)
@@ -277,10 +276,11 @@
CloseStdin()
// Shutdown closes the Stdin for the command and then reads output
- // from the command's stdout until it encounters EOF and writes that
- // output to the supplied io.Writer. It returns any error returned by
- // the command.
- Shutdown(io.Writer) error
+ // from the command's stdout until it encounters EOF, waits for
+ // the command to complete and then reads all of its stderr output.
+ // The stdout and stderr contents are written to the corresponding
+ // io.Writers if they are non-nil, otherwise the content is discarded.
+ Shutdown(stdout, stderr io.Writer) error
}
// command is used to abstract the implementations of inprocess and subprocess
diff --git a/lib/modules/util.go b/lib/modules/util.go
new file mode 100644
index 0000000..21fc4c2
--- /dev/null
+++ b/lib/modules/util.go
@@ -0,0 +1,27 @@
+package modules
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+)
+
+func newLogfile(prefix string) (*os.File, error) {
+ f, err := ioutil.TempFile("", "__modules__"+prefix)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+func readTo(r io.Reader, w io.Writer) {
+ if w == nil {
+ return
+ }
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ fmt.Fprintf(w, "%s\n", scanner.Text())
+ }
+}
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index f4e177b..6f8539f 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -159,28 +159,29 @@
return false
}
+func getRetryTimeoutOpt(opts []ipc.CallOpt) (time.Duration, bool) {
+ for _, o := range opts {
+ if r, ok := o.(veyron2.RetryTimeoutOpt); ok {
+ return time.Duration(r), true
+ }
+ }
+ return 0, false
+}
+
func (c *client) StartCall(ctx context.T, name, method string, args []interface{}, opts ...ipc.CallOpt) (ipc.Call, error) {
defer vlog.LogCall()()
- var retry = true
+ // Context specified deadline.
deadline, hasDeadline := ctx.Deadline()
if !hasDeadline {
- // If no deadline is set, use the default
+ // Default deadline.
deadline = time.Now().Add(defaultCallTimeout)
}
- for _, o := range opts {
- r, ok := o.(veyron2.RetryTimeoutOpt)
- if !ok {
- continue
- }
- if r == 0 {
- retry = false
- } else {
- deadline = time.Now().Add(time.Duration(r))
- }
- break
+ if r, ok := getRetryTimeoutOpt(opts); ok {
+ // Caller specified deadline.
+ deadline = time.Now().Add(time.Duration(r))
}
var lastErr verror.E
- for retries := 0; deadline.After(time.Now()); retries++ {
+ for retries := 0; ; retries++ {
if retries != 0 {
if !backoff(retries, deadline) {
break
@@ -191,23 +192,37 @@
return call, nil
}
lastErr = err
- if !retry || !retriable(err) {
+ if deadline.After(time.Now()) || !retriable(err) {
break
}
}
return nil, lastErr
}
+func getNoResolveOpt(opts []ipc.CallOpt) bool {
+ for _, o := range opts {
+ if r, ok := o.(veyron2.NoResolveOpt); ok {
+ return bool(r)
+ }
+ }
+ return false
+}
+
// startCall ensures StartCall always returns verror.E.
func (c *client) startCall(ctx context.T, name, method string, args []interface{}, opts ...ipc.CallOpt) (ipc.Call, verror.E) {
if ctx == nil {
return nil, verror.BadArgf("ipc: %s.%s called with nil context", name, method)
}
ctx, _ = vtrace.WithNewSpan(ctx, fmt.Sprintf("Client Call: %s.%s", name, method))
-
- servers, err := c.ns.Resolve(ctx, name)
- if err != nil {
- return nil, verror.NoExistf("ipc: Resolve(%q) failed: %v", name, err)
+ // Resolve name unless told not to.
+ var servers []string
+ if getNoResolveOpt(opts) {
+ servers = []string{name}
+ } else {
+ var err error
+ if servers, err = c.ns.Resolve(ctx, name); err != nil {
+ return nil, verror.NoExistf("ipc: Resolve(%q) failed: %v", name, err)
+ }
}
// Try all servers, and if none of them are authorized for the call then return the error of the last server
// that was tried.
diff --git a/runtimes/google/ipc/debug_test.go b/runtimes/google/ipc/debug_test.go
index 6bcfc69..887bdec 100644
--- a/runtimes/google/ipc/debug_test.go
+++ b/runtimes/google/ipc/debug_test.go
@@ -5,6 +5,7 @@
"sort"
"testing"
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/services/mounttable/types"
@@ -37,7 +38,7 @@
ctx := testContext()
// Call the Foo method on ""
{
- addr := naming.JoinAddressName(ep.String(), "//")
+ addr := naming.JoinAddressName(ep.String(), "")
call, err := client.StartCall(ctx, addr, "Foo", nil)
if err != nil {
t.Fatalf("client.StartCall failed: %v", err)
@@ -52,8 +53,8 @@
}
// Call Glob on __debug
{
- addr := naming.JoinAddressName(ep.String(), "//__debug")
- call, err := client.StartCall(ctx, addr, "Glob", []interface{}{"*"})
+ addr := naming.JoinAddressName(ep.String(), "__debug")
+ call, err := client.StartCall(ctx, addr, "Glob", []interface{}{"*"}, veyron2.NoResolveOpt(true))
if err != nil {
t.Fatalf("client.StartCall failed: %v", err)
}
@@ -82,8 +83,8 @@
{
foo := stats.NewString("testing/foo")
foo.Set("The quick brown fox jumps over the lazy dog")
- addr := naming.JoinAddressName(ep.String(), "//__debug/stats/testing/foo")
- call, err := client.StartCall(ctx, addr, "Value", nil)
+ addr := naming.JoinAddressName(ep.String(), "__debug/stats/testing/foo")
+ call, err := client.StartCall(ctx, addr, "Value", nil, veyron2.NoResolveOpt(true))
if err != nil {
t.Fatalf("client.StartCall failed: %v", err)
}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 1174d02..5c4b391 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -941,8 +941,8 @@
defer publisher.WaitForStop()
defer publisher.Stop()
publisher.AddName("incompatible")
- publisher.AddServer("/@2@tcp@localhost:10000@@1000000@2000000@@")
- publisher.AddServer("/@2@tcp@localhost:10001@@2000000@3000000@@")
+ publisher.AddServer("/@2@tcp@localhost:10000@@1000000@2000000@@", false)
+ publisher.AddServer("/@2@tcp@localhost:10001@@2000000@3000000@@", false)
_, err := b.client.StartCall(testContext(), "incompatible/suffix", "Echo", []interface{}{"foo"})
if !strings.Contains(err.Error(), version.NoCompatibleVersionErr.Error()) {
@@ -950,7 +950,7 @@
}
// Now add a server with a compatible endpoint and try again.
- publisher.AddServer("/" + b.ep.String())
+ publisher.AddServer("/"+b.ep.String(), false)
publisher.AddName("incompatible")
call, err := b.client.StartCall(testContext(), "incompatible/suffix", "Echo", []interface{}{"foo"})
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 3188aeb..7665b83 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -204,7 +204,7 @@
}(ln, ep)
}
s.Unlock()
- s.publisher.AddServer(s.publishEP(ep))
+ s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
return ep, nil
}
@@ -340,9 +340,9 @@
s.proxyListenLoop(ln, ep, proxy)
s.active.Done()
}(pln, pep, listenSpec.Proxy)
- s.publisher.AddServer(s.publishEP(pep))
+ s.publisher.AddServer(s.publishEP(pep), s.servesMountTable)
} else {
- s.publisher.AddServer(s.publishEP(ep))
+ s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
}
s.Unlock()
return ep, nil
@@ -393,7 +393,7 @@
}
}
// (3) reconnected, publish new address
- s.publisher.AddServer(s.publishEP(ep))
+ s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
s.Lock()
s.listeners[ln] = nil
s.Unlock()
@@ -459,7 +459,7 @@
switch setting.Name() {
case ipc.NewAddrsSetting:
vlog.Infof("Added some addresses: %q", v)
- s.applyChange(dhcpl, v, publisher.AddServer)
+ s.applyChange(dhcpl, v, func(name string) { publisher.AddServer(name, s.servesMountTable) })
case ipc.RmAddrsSetting:
vlog.Infof("Removed some addresses: %q", v)
s.applyChange(dhcpl, v, publisher.RemoveServer)
diff --git a/runtimes/google/lib/publisher/publisher.go b/runtimes/google/lib/publisher/publisher.go
index 25db23f..83468e2 100644
--- a/runtimes/google/lib/publisher/publisher.go
+++ b/runtimes/google/lib/publisher/publisher.go
@@ -16,7 +16,7 @@
// Publisher manages the publishing of servers in mounttable.
type Publisher interface {
// AddServer adds a new server to be mounted.
- AddServer(server string)
+ AddServer(server string, ServesMountTable bool)
// RemoveServer removes a server from the list of mounts.
RemoveServer(server string)
// AddName adds a new name for all servers to be mounted as.
@@ -49,6 +49,7 @@
type addServerCmd struct {
server string // server to add
+ mt bool // true if server serves a mount table
done chan struct{} // closed when the cmd is done
}
@@ -87,9 +88,9 @@
}
}
-func (p *publisher) AddServer(server string) {
+func (p *publisher) AddServer(server string, mt bool) {
done := make(chan struct{})
- if p.sendCmd(addServerCmd{server, done}) {
+ if p.sendCmd(addServerCmd{server, mt, done}) {
<-done
}
}
@@ -161,7 +162,7 @@
vlog.VI(2).Info("ipc pub: exit runLoop")
return
case addServerCmd:
- state.addServer(tcmd.server)
+ state.addServer(tcmd.server, tcmd.mt)
close(tcmd.done)
case removeServerCmd:
state.removeServer(tcmd.server)
@@ -192,6 +193,7 @@
deadline time.Time // deadline for the next sync call
names []string // names that have been added
servers map[string]bool // servers that have been added
+ servesMT map[string]bool // true if server is a mount table server
mounts map[mountKey]*mountStatus // map each (name,server) to its status
}
@@ -214,6 +216,7 @@
period: period,
deadline: time.Now().Add(period),
servers: make(map[string]bool),
+ servesMT: make(map[string]bool),
mounts: make(map[mountKey]*mountStatus),
}
}
@@ -234,19 +237,20 @@
for server, _ := range ps.servers {
status := new(mountStatus)
ps.mounts[mountKey{name, server}] = status
- ps.mount(name, server, status)
+ ps.mount(name, server, status, ps.servesMT[server])
}
}
-func (ps *pubState) addServer(server string) {
+func (ps *pubState) addServer(server string, servesMT bool) {
// Each non-dup server that is added causes new mounts to be created for all
// existing names.
if !ps.servers[server] {
ps.servers[server] = true
+ ps.servers[server] = servesMT
for _, name := range ps.names {
status := new(mountStatus)
ps.mounts[mountKey{name, server}] = status
- ps.mount(name, server, status)
+ ps.mount(name, server, status, servesMT)
}
}
}
@@ -263,13 +267,13 @@
}
}
-func (ps *pubState) mount(name, server string, status *mountStatus) {
+func (ps *pubState) mount(name, server string, status *mountStatus, servesMT bool) {
// Always mount with ttl = period + slack, regardless of whether this is
// triggered by a newly added server or name, or by sync. The next call
// to sync will occur within the next period, and refresh all mounts.
ttl := ps.period + mountTTLSlack
status.lastMount = time.Now()
- status.lastMountErr = ps.ns.Mount(ps.ctx, name, server, ttl)
+ status.lastMountErr = ps.ns.Mount(ps.ctx, name, server, ttl, naming.ServesMountTableOpt(servesMT))
if status.lastMountErr != nil {
vlog.Errorf("ipc pub: couldn't mount(%v, %v, %v): %v", name, server, ttl, status.lastMountErr)
} else {
@@ -284,7 +288,7 @@
// Desired state is "unmounted", failed at previous attempt. Retry.
ps.unmount(key.name, key.server, status)
} else {
- ps.mount(key.name, key.server, status)
+ ps.mount(key.name, key.server, status, ps.servesMT[key.server])
}
}
}
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index 49cc811..35d5c01 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -5,13 +5,15 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/vlog"
)
// mountIntoMountTable mounts a single server into a single mount table.
-func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration) error {
+func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration, flags types.MountFlag) error {
ctx, _ = ctx.WithTimeout(callTimeout)
- call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds())})
+ call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags})
if err != nil {
return err
}
@@ -34,8 +36,24 @@
return err
}
-func (ns *namespace) Mount(ctx context.T, name, server string, ttl time.Duration) error {
+func (ns *namespace) Mount(ctx context.T, name, server string, ttl time.Duration, opts ...naming.MountOpt) error {
defer vlog.LogCall()()
+
+ var flags types.MountFlag
+ for _, o := range opts {
+ // NB: used a switch since we'll be adding more options.
+ switch v := o.(type) {
+ case naming.ReplaceMountOpt:
+ if v {
+ flags |= types.MountFlag(types.Replace)
+ }
+ case naming.ServesMountTableOpt:
+ if v {
+ flags |= types.MountFlag(types.MT)
+ }
+ }
+ }
+
// Resolve to all the mount tables implementing name.
mtServers, err := ns.ResolveToMountTable(ctx, name)
if err != nil {
@@ -45,7 +63,7 @@
c := make(chan error, len(mtServers))
for _, mt := range mtServers {
go func() {
- c <- mountIntoMountTable(ctx, ns.rt.Client(), mt, server, ttl)
+ c <- mountIntoMountTable(ctx, ns.rt.Client(), mt, server, ttl, flags)
}()
}
// Return error if any mounts failed, since otherwise we'll get
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index 68b0bfe..58c914c 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -25,7 +25,7 @@
mounts map[string][]string
}
-func (ns *namespace) Mount(ctx context.T, name, server string, _ time.Duration) error {
+func (ns *namespace) Mount(ctx context.T, name, server string, _ time.Duration, _ ...naming.MountOpt) error {
defer vlog.LogCall()()
ns.Lock()
defer ns.Unlock()
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index 85936ec..2a1376d 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -47,6 +47,7 @@
// point can be sent to any of these servers.
type mount struct {
servers *serverList
+ mt bool
}
// node is a single point in the tree representing the mount table.
@@ -225,8 +226,16 @@
return n.mount.servers.copyToSlice(), slashSlashJoin(elems), nil
}
+func hasMTFlag(flags types.MountFlag) bool {
+ return (flags & types.MT) == types.MT
+}
+
+func hasReplaceFlag(flags types.MountFlag) bool {
+ return (flags & types.Replace) == types.Replace
+}
+
// Mount a server onto the name in the receiver.
-func (ms *mountContext) Mount(context ipc.ServerContext, server string, ttlsecs uint32) error {
+func (ms *mountContext) Mount(context ipc.ServerContext, server string, ttlsecs uint32, flags types.MountFlag) error {
mt := ms.mt
if ttlsecs == 0 {
ttlsecs = 10 * 365 * 24 * 60 * 60 // a really long time
@@ -246,13 +255,17 @@
if n == nil {
return naming.ErrNoSuchName
}
+ if hasReplaceFlag(flags) {
+ n.mount = nil
+ }
if n.mount == nil {
- n.mount = &mount{
- servers: NewServerList(),
+ n.mount = &mount{servers: NewServerList(), mt: hasMTFlag(flags)}
+ } else {
+ if hasMTFlag(flags) != n.mount.mt {
+ return fmt.Errorf("MT doesn't match")
}
}
- m := n.mount
- m.servers.add(server, time.Duration(ttlsecs)*time.Second)
+ n.mount.servers.add(server, time.Duration(ttlsecs)*time.Second)
return nil
}
diff --git a/services/mounttable/lib/mounttable_test.go b/services/mounttable/lib/mounttable_test.go
index d830415..c773312 100644
--- a/services/mounttable/lib/mounttable_test.go
+++ b/services/mounttable/lib/mounttable_test.go
@@ -53,7 +53,7 @@
return c
}
-func (stupidNS) Mount(context.T, string, string, time.Duration) error {
+func (stupidNS) Mount(context.T, string, string, time.Duration, ...naming.MountOpt) error {
return errors.New("unimplemented")
}
@@ -124,7 +124,7 @@
if err != nil {
boom(t, "Failed to BindMountTable: %s", err)
}
- if err := mtpt.Mount(rt.R().NewContext(), service, uint32(ttlSecs), veyron2.RetryTimeoutOpt(0)); err != nil {
+ if err := mtpt.Mount(rt.R().NewContext(), service, uint32(ttlSecs), 0, veyron2.RetryTimeoutOpt(0)); err != nil {
if shouldSucceed {
boom(t, "Failed to Mount %s onto %s: %s", service, name, err)
}
diff --git a/services/mounttable/lib/neighborhood.go b/services/mounttable/lib/neighborhood.go
index 6b6df4d..0381352 100644
--- a/services/mounttable/lib/neighborhood.go
+++ b/services/mounttable/lib/neighborhood.go
@@ -221,7 +221,7 @@
}
// Mount not implemented.
-func (*neighborhoodService) Mount(_ ipc.ServerContext, server string, ttlsecs uint32) error {
+func (*neighborhoodService) Mount(_ ipc.ServerContext, server string, ttlsecs uint32, opts types.MountFlag) error {
return errors.New("this server does not implement Mount")
}
@@ -247,7 +247,7 @@
if ok, _, _ := g.MatchInitialSegment(k); !ok {
continue
}
- if err := sender.Send(types.MountEntry{Name: k, Servers: n}); err != nil {
+ if err := sender.Send(types.MountEntry{Name: k, Servers: n, MT: true}); err != nil {
return err
}
}
@@ -257,7 +257,7 @@
if neighbor == nil {
return naming.ErrNoSuchName
}
- return sender.Send(types.MountEntry{Name: "", Servers: neighbor})
+ return sender.Send(types.MountEntry{Name: "", Servers: neighbor, MT: true})
default:
return naming.ErrNoSuchName
}
diff --git a/services/proxy/proxyd/main.go b/services/proxy/proxyd/main.go
index ce9859b..903db8b 100644
--- a/services/proxy/proxyd/main.go
+++ b/services/proxy/proxyd/main.go
@@ -46,7 +46,7 @@
publisher := publisher.New(r.NewContext(), r.Namespace(), time.Minute)
defer publisher.WaitForStop()
defer publisher.Stop()
- publisher.AddServer(naming.JoinAddressName(proxy.Endpoint().String(), "//"))
+ publisher.AddServer(naming.JoinAddressName(proxy.Endpoint().String(), "//"), false)
publisher.AddName(*name)
}
diff --git a/tools/mounttable/impl/impl.go b/tools/mounttable/impl/impl.go
index 0bec3fe..fb39d1a 100644
--- a/tools/mounttable/impl/impl.go
+++ b/tools/mounttable/impl/impl.go
@@ -99,7 +99,7 @@
if err != nil {
return fmt.Errorf("TTL parse error: %v", err)
}
- err = c.Mount(ctx, args[1], uint32(ttl.Seconds()))
+ err = c.Mount(ctx, args[1], uint32(ttl.Seconds()), 0)
if err != nil {
return err
}
diff --git a/tools/mounttable/impl/impl_test.go b/tools/mounttable/impl/impl_test.go
index 77a5a12..b4f96c7 100644
--- a/tools/mounttable/impl/impl_test.go
+++ b/tools/mounttable/impl/impl_test.go
@@ -25,12 +25,12 @@
func (s *server) Glob(_ ipc.ServerContext, pattern string, stream mounttable.GlobbableServiceGlobStream) error {
vlog.VI(2).Infof("Glob() was called. suffix=%v pattern=%q", s.suffix, pattern)
sender := stream.SendStream()
- sender.Send(types.MountEntry{"name1", []types.MountedServer{{"server1", 123}}})
- sender.Send(types.MountEntry{"name2", []types.MountedServer{{"server2", 456}, {"server3", 789}}})
+ sender.Send(types.MountEntry{"name1", []types.MountedServer{{"server1", 123}}, false})
+ sender.Send(types.MountEntry{"name2", []types.MountedServer{{"server2", 456}, {"server3", 789}}, false})
return nil
}
-func (s *server) Mount(_ ipc.ServerContext, server string, ttl uint32) error {
+func (s *server) Mount(_ ipc.ServerContext, server string, ttl uint32, flags types.MountFlag) error {
vlog.VI(2).Infof("Mount() was called. suffix=%v server=%q ttl=%d", s.suffix, server, ttl)
return nil
}
diff --git a/tools/naming/simulator/commands.go b/tools/naming/simulator/commands.go
index 3ec00dc..b11a3da 100644
--- a/tools/naming/simulator/commands.go
+++ b/tools/naming/simulator/commands.go
@@ -105,7 +105,7 @@
func readStderr(state *cmdState) (string, error) {
var b bytes.Buffer
- if err := state.Handle.Shutdown(&b); err != nil && err != io.EOF {
+ if err := state.Handle.Shutdown(nil, &b); err != nil && err != io.EOF {
return b.String(), err
}
return b.String(), nil
@@ -182,7 +182,7 @@
func quit(sh *modules.Shell, _ *cmdState, args ...string) (string, error) {
r := ""
for k, h := range handles {
- if err := h.Handle.Shutdown(os.Stdout); err != nil {
+ if err := h.Handle.Shutdown(os.Stdout, os.Stdout); err != nil {
r += fmt.Sprintf("%s: %v\n", k, err)
} else {
r += fmt.Sprintf("%s: ok\n", k)
diff --git a/tools/naming/simulator/driver.go b/tools/naming/simulator/driver.go
index b9c91ba..60028b5 100644
--- a/tools/naming/simulator/driver.go
+++ b/tools/naming/simulator/driver.go
@@ -100,7 +100,6 @@
if os.Getenv(modules.ShellEntryPoint) != "" {
// Subprocess, run the requested command.
if err := modules.Dispatch(); err != nil {
- fmt.Fprintf(os.Stdout, "failed: %v\n", err)
fmt.Fprintf(os.Stderr, "failed: %v\n", err)
return
}
@@ -108,7 +107,7 @@
}
shell := modules.NewShell()
- defer shell.Cleanup(os.Stderr)
+ defer shell.Cleanup(os.Stderr, os.Stderr)
if os.Getenv("VEYRON_IDENTITY") == "" {
shell.CreateAndUseNewID()
}
diff --git a/tools/naming/simulator/t.scr b/tools/naming/simulator/t.scr
deleted file mode 100644
index adf0571..0000000
--- a/tools/naming/simulator/t.scr
+++ /dev/null
@@ -1,5 +0,0 @@
-mount a b
-set w=$_
-read $w
-read $w
-wait $w
diff --git a/tools/naming/simulator/test.sh b/tools/naming/simulator/test.sh
new file mode 100755
index 0000000..040625c
--- /dev/null
+++ b/tools/naming/simulator/test.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Test the simulator command-line tool.
+#
+
+source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
+
+main() {
+ # Build binaries.
+ cd "${TMPDIR}"
+ local -r PKG=veyron.io/veyron/veyron/tools/naming/simulator
+ veyron go build "${PKG}" || shell_test::fail "line ${LINENO}: failed to build simulator"
+
+ local -r DIR=$(shell::go_package_dir "${PKG}")
+ local file
+ for file in ${DIR}/*.scr; do
+ echo $file
+ ./simulator < $file > /dev/null 2>&1 || shell_test::fail "failed for $file"
+ done
+ shell_test::pass
+}
+
+main "$@"