v23tests: port tools/debug to v23tests + v23tests improvements/fixes

Improvements to v23tests:
- invocations should be shut down in LIFO order - they were not!
- adopt the convention of naming v23 integration tests as:
	<pkg>_v23_test.go
- export NewChildCredentials from the shell and use it in
  v23tests to give DebugShell's access to the security agent.
- remove an annoying .VI(1) in vif.go
- make sure that all V23Tests in a single package share binaries, it's
  up to v23 to arrange for sharing binaries across multiple packages.
- transition a bunch of testdata tests to <pkg>_v23_tests.

MultiPart: 1/2

Change-Id: Iee6db154a6908c0f4c817f7cb9d56f8982c188a5
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index cfdfc73..df14af9 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -136,7 +136,12 @@
 	return sh, nil
 }
 
-func (sh *Shell) getChildCredentials() (c *os.File, err error) {
+// NewChildCredentials creates a new set of credentials, stored in the
+// security agent, that have a blessing from this shell's principal.
+// All processes started by this shell will have access to these credentials
+// and this method can be used to create other processes that can communicate
+// with these.
+func (sh *Shell) NewChildCredentials() (c *os.File, err error) {
 	if sh.ctx == nil {
 		return nil, nil
 	}
@@ -273,7 +278,7 @@
 	if err != nil {
 		return nil, err
 	}
-	p, err := sh.getChildCredentials()
+	p, err := sh.NewChildCredentials()
 	if err != nil {
 		return nil, err
 	}
diff --git a/lib/testutil/v23tests/v23tests.go b/lib/testutil/v23tests/v23tests.go
index 00fe66f..b1d392b 100644
--- a/lib/testutil/v23tests/v23tests.go
+++ b/lib/testutil/v23tests/v23tests.go
@@ -93,16 +93,12 @@
 	"v.io/core/veyron/lib/modules"
 	"v.io/core/veyron/lib/testutil"
 	tsecurity "v.io/core/veyron/lib/testutil/security"
+	"v.io/core/veyron/security/agent"
 )
 
 // TODO(cnicolaou): need to enable running under the agent as per the old shell tests,
 // via the shell_test::enable_agent "$@" mechanism.
 //
-//
-// TODO(sjr,cnicolaou): caching of binaries is per test environment -
-// it should be in a file system somewhere and should handle all tests run
-// from a single invocation of v23.
-//
 // TODO(sjr): make this thread safe.
 //
 // TODO(sjr): document all of the methods.
@@ -499,10 +495,11 @@
 	}
 
 	vlog.VI(1).Infof("V23Test.Cleanup")
-	// Shut down all processes before attempting to delete any
+	// Shut down all processes in LIFO order before attempting to delete any
 	// files/directories to avoid potential 'file system busy' problems
 	// on non-unix systems.
-	for _, inv := range e.invocations {
+	for i := len(e.invocations); i > 0; i-- {
+		inv := e.invocations[i-1]
 		if inv.hasShutdown {
 			vlog.VI(1).Infof("V23Test.Cleanup: %q has been shutdown", inv.Path())
 			continue
@@ -578,22 +575,24 @@
 		vlog.Errorf("WARNING: Open(%v) failed, was asked to create a debug shell but cannot: %v", dev, err)
 		return
 	}
+
+	agentFile, err := e.shell.NewChildCredentials()
+	if err != nil {
+		vlog.Errorf("WARNING: failed to obtain credentials for the debug shell: %v", err)
+	}
+
 	file := os.NewFile(uintptr(fd), dev)
 	attr := os.ProcAttr{
 		Files: []*os.File{file, file, file},
 		Dir:   cwd,
 	}
+	attr.Files = append(attr.Files, agentFile)
+	attr.Env = append(attr.Env, fmt.Sprintf("%s=%d", agent.FdVarName, len(attr.Files)-1))
 
 	// Set up environment for Child.
 	if ns, ok := e.GetVar("NAMESPACE_ROOT"); ok {
 		attr.Env = append(attr.Env, "NAMESPACE_ROOT="+ns)
 	}
-	// TODO(sjr): talk to Ankur about how to do this properly/safely
-	// using either the agent, or a file descriptor inherited by the shell
-	// and its children. This is preferable since it avoids compiling and
-	// running the agent which can be an overhead in tests.
-	dir, _ := tsecurity.ForkCredentials(e.principal, "debugShell")
-	attr.Env = append(attr.Env, "VEYRON_CREDENTIALS="+dir)
 
 	// Start up a new shell.
 	writeStringOrDie(e, file, ">> Starting a new interactive shell\n")
@@ -638,25 +637,24 @@
 }
 
 func (e *testEnvironment) BuildGoPkg(binary_path string) TestBinary {
-	vlog.Infof("building %s...", binary_path)
-	if cached_binary := e.builtBinaries[binary_path]; cached_binary != nil {
-		vlog.Infof("using cached binary for %s at %s.", binary_path, cached_binary.Path())
-		return cached_binary
-	}
-	built_path, cleanup, err := buildPkg(e.cachedBinDir, binary_path)
+	then := time.Now()
+	cached, built_path, cleanup, err := buildPkg(e.cachedBinDir, binary_path)
 	if err != nil {
-		e.Fatalf("buildPkg() failed: %v", err)
+		e.Fatalf("buildPkg(%s) failed: %v", binary_path, err)
 		return nil
 	}
 	output_path := path.Join(built_path, path.Base(binary_path))
 
-	if fi, err := os.Stat(output_path); err != nil {
-		e.Fatalf("failed to stat %q", output_path)
+	if _, err := os.Stat(output_path); err != nil {
+		e.Fatalf("buildPkg(%s) failed to stat %q", binary_path, output_path)
+	}
+	taken := time.Now().Sub(then)
+	if cached {
+		vlog.Infof("using %s, from %s in %s.", binary_path, output_path, taken)
 	} else {
-		vlog.VI(1).Info("using %q: built at %s", output_path, fi.ModTime())
+		vlog.Infof("built %s, written to %s in %s.", binary_path, output_path, taken)
 	}
 
-	vlog.Infof("done building %s, written to %s.", binary_path, output_path)
 	binary := &testBinary{
 		env:         e,
 		envVars:     nil,
@@ -720,12 +718,12 @@
 	shell.SetStartTimeout(1 * time.Minute)
 	shell.SetWaitTimeout(5 * time.Minute)
 
+	// The V23_BIN_DIR environment variable can be
+	// used to identify a directory that multiple integration
+	// tests can use to share binaries. Whoever sets this
+	// environment variable is responsible for cleaning up the
+	// directory it points to.
 	cachedBinDir := os.Getenv("V23_BIN_DIR")
-	if len(cachedBinDir) == 0 {
-		vlog.Infof("WARNING: not using a cached binary")
-	} else {
-		vlog.Infof("Caching binaries in %q", cachedBinDir)
-	}
 	return &testEnvironment{
 		Test:          t,
 		principal:     principal,
@@ -743,12 +741,7 @@
 // build artifacts. Note that the clients of this function should not modify
 // the contents of this directory directly and instead defer to the cleanup
 // function.
-func buildPkg(binDir, pkg string) (string, func(), error) {
-	// The V23_BIN_DIR environment variable can be
-	// used to identify a directory that multiple integration
-	// tests can use to share binaries. Whoever sets this
-	// environment variable is responsible for cleaning up the
-	// directory it points to.
+func buildPkg(binDir, pkg string) (bool, string, func(), error) {
 	cleanupFn := func() {}
 	if len(binDir) == 0 {
 		// If the aforementioned environment variable is not
@@ -756,21 +749,23 @@
 		// directory, which the cleanup function removes.
 		tmpDir, err := ioutil.TempDir("", "")
 		if err != nil {
-			return "", nil, fmt.Errorf("TempDir() failed: %v", err)
+			return false, "", nil, fmt.Errorf("TempDir() failed: %v", err)
 		}
 		binDir, cleanupFn = tmpDir, func() { os.RemoveAll(tmpDir) }
 	}
 	binFile := filepath.Join(binDir, path.Base(pkg))
+	cached := true
 	if _, err := os.Stat(binFile); err != nil {
 		if !os.IsNotExist(err) {
-			return "", nil, err
+			return false, "", nil, err
 		}
 		cmd := exec.Command("v23", "go", "build", "-o", filepath.Join(binDir, path.Base(pkg)), pkg)
 		if err := cmd.Run(); err != nil {
-			return "", nil, err
+			return false, "", nil, err
 		}
+		cached = false
 	}
-	return binDir, cleanupFn, nil
+	return cached, binDir, cleanupFn, nil
 }
 
 // RunTest runs a single Vanadium 'v23 style' integration test.
@@ -794,8 +789,29 @@
 	s := expect.NewSession(t, i.Stdout(), time.Minute)
 	name := s.ExpectVar("NAME")
 	i.Environment().SetVar("NAMESPACE_ROOT", name)
+	vlog.Infof("Running root mount table: %q", name)
 	return b, i
 }
 
+// UseShardBinDir ensures that a shared directory is used for binaries
+// across multiple instances of the test environment. This is achieved
+// by setting the V23_BIN_DIR environment variable if it is not already
+// set in the test processes environment (as will typically be the case when
+// these tests are run from the v23 tool). It is intended to be called
+// from TestMain.
+func UseSharedBinDir() func() {
+	if v23BinDir := os.Getenv("V23_BIN_DIR"); len(v23BinDir) == 0 {
+		v23BinDir, err := ioutil.TempDir("", "bin-")
+		if err == nil {
+			vlog.Infof("Setting V23_BIN_DIR to %q", v23BinDir)
+			os.Setenv("V23_BIN_DIR", v23BinDir)
+			return func() { os.RemoveAll(v23BinDir) }
+		}
+	} else {
+		vlog.Infof("Using V23_BIN_DIR %q", v23BinDir)
+	}
+	return func() {}
+}
+
 // TODO(sjr): provided convenience wrapper for dealing with credentials if
 // necessary.