veyron/tools/mgmt: migrate test.sh to v23tests.
MultiPart: 1/2

Change-Id: I83a4fd34246ecb7512ea257e61ea423f9e99b5fb
diff --git a/lib/testutil/v23tests/v23tests.go b/lib/testutil/v23tests/v23tests.go
index 9c15d25..e93850f 100644
--- a/lib/testutil/v23tests/v23tests.go
+++ b/lib/testutil/v23tests/v23tests.go
@@ -239,8 +239,10 @@
 	return syscall.Kill(pid, sig)
 }
 
-func location(depth int) string {
-	_, file, line, _ := runtime.Caller(depth + 1)
+// Caller returns a string of the form <filename>:<lineno> for the
+// caller specified by skip, where skip is as per runtime.Caller.
+func Caller(skip int) string {
+	_, file, line, _ := runtime.Caller(skip + 1)
 	return fmt.Sprintf("%s:%d", filepath.Base(file), line)
 }
 
@@ -250,7 +252,7 @@
 	buf := bytes.Buffer{}
 	_, err := buf.ReadFrom(i.Stdout())
 	if err != nil {
-		i.env.Fatalf("%s: ReadFrom() failed: %v", location(1), err)
+		i.env.Fatalf("%s: ReadFrom() failed: %v", Caller(1), err)
 	}
 	return buf.String()
 }
@@ -273,7 +275,7 @@
 // cause the current test to fail.
 func (i *Invocation) WaitOrDie(stdout, stderr io.Writer) {
 	if err := i.Wait(stdout, stderr); err != nil {
-		i.env.Fatalf("%s: FATAL: Wait() for pid %d failed: %v", location(1), i.handle.Pid(), err)
+		i.env.Fatalf("%s: FATAL: Wait() for pid %d failed: %v", Caller(1), i.handle.Pid(), err)
 	}
 }
 
@@ -298,10 +300,14 @@
 
 // Start starts the given binary with the given arguments.
 func (b *Binary) Start(args ...string) *Invocation {
-	loc := location(testutil.DepthToExternalCaller())
+	loc := Caller(testutil.DepthToExternalCaller())
 	vlog.Infof("%s: starting %s %s", loc, b.Path(), strings.Join(args, " "))
 	handle, err := b.env.shell.StartExternalCommand(b.envVars, append([]string{b.Path()}, args...)...)
 	if err != nil {
+		// TODO(cnicolaou): calling Fatalf etc from a goroutine often leads
+		// to deadlock. Need to make sure that we handle this here. Maybe
+		// it's best to just return an error? Or provide a StartWithError
+		// call for use from goroutines.
 		b.env.Fatalf("%s: StartExternalCommand(%v, %v) failed: %v", loc, b.Path(), strings.Join(args, ", "), err)
 	}
 	vlog.Infof("started PID %d\n", handle.Pid())
@@ -381,10 +387,12 @@
 				vlog.VI(2).Infof("%d: %s %v", i, inv.path, inv.args)
 				continue
 			}
-			m := fmt.Sprintf("%d: %s: shutdown status: %v", i, inv.path, inv.shutdownErr)
-			e.Log(m)
-			vlog.VI(1).Info(m)
-			vlog.VI(2).Infof("%d: %s %v", i, inv.path, inv.args)
+			if inv.shutdownErr != nil {
+				m := fmt.Sprintf("%d: %s: shutdown status: %v", i, inv.path, inv.shutdownErr)
+				e.Log(m)
+				vlog.VI(1).Info(m)
+				vlog.VI(2).Infof("%d: %s %v", i, inv.path, inv.args)
+			}
 		}
 	}
 
@@ -458,10 +466,11 @@
 	}
 }
 
-// DebugShell drops the user into a debug shell. If there is no
-// controlling TTY, DebugShell will emit a warning message and take no
-// futher action.
-func (e *T) DebugShell() {
+// DebugShell drops the user into a debug shell with any environment
+// variables specified in env... (in VAR=VAL format) available to it.
+// If there is no controlling TTY, DebugShell will emit a warning message
+// and take no futher action.
+func (e *T) DebugShell(env ...string) {
 	// Get the current working directory.
 	cwd, err := os.Getwd()
 	if err != nil {
@@ -487,6 +496,7 @@
 		Files: []*os.File{file, file, file},
 		Dir:   cwd,
 	}
+	// Set up agent for Child.
 	attr.Files = append(attr.Files, agentFile)
 	attr.Env = append(attr.Env, fmt.Sprintf("%s=%d", agent.FdVarName, len(attr.Files)-1))
 
@@ -495,6 +505,15 @@
 		attr.Env = append(attr.Env, v)
 	}
 
+	for i, td := range e.tempDirs {
+		attr.Env = append(attr.Env, fmt.Sprintf("V23_TMP_DIR%d=%s", i, td))
+	}
+
+	if len(e.cachedBinDir) > 0 {
+		attr.Env = append(attr.Env, "V23_BIN_DIR="+e.cachedBinDir)
+	}
+	attr.Env = append(attr.Env, env...)
+
 	// Start up a new shell.
 	writeStringOrDie(e, file, ">> Starting a new interactive shell\n")
 	writeStringOrDie(e, file, "Hit CTRL-D to resume the test\n")
@@ -505,9 +524,9 @@
 		}
 	}
 	if len(e.cachedBinDir) > 0 {
-		writeStringOrDie(e, file, fmt.Sprintf("Binaries are cached in %q", e.cachedBinDir))
+		writeStringOrDie(e, file, fmt.Sprintf("Binaries are cached in %q\n", e.cachedBinDir))
 	} else {
-		writeStringOrDie(e, file, "Caching of binaries was not enabled")
+		writeStringOrDie(e, file, "Caching of binaries was not enabled\n")
 	}
 
 	shellPath := "/bin/sh"
@@ -546,7 +565,7 @@
 // binary.
 func (e *T) BuildGoPkg(binary_path string) *Binary {
 	then := time.Now()
-	loc := location(1)
+	loc := Caller(1)
 	cached, built_path, cleanup, err := buildPkg(e.cachedBinDir, binary_path)
 	if err != nil {
 		e.Fatalf("%s: buildPkg(%s) failed: %v", loc, binary_path, err)
@@ -574,10 +593,10 @@
 	return binary
 }
 
-// TempFile creates a temporary file. Temporary files will be deleted
+// NewTempFile creates a temporary file. Temporary files will be deleted
 // by Cleanup.
-func (e *T) TempFile() *os.File {
-	loc := location(1)
+func (e *T) NewTempFile() *os.File {
+	loc := Caller(1)
 	f, err := ioutil.TempFile("", "")
 	if err != nil {
 		e.Fatalf("%s: TempFile() failed: %v", loc, err)
@@ -587,10 +606,10 @@
 	return f
 }
 
-// TempDir creates a temporary directory. Temporary directories and
+// NewTempDir creates a temporary directory. Temporary directories and
 // their contents will be deleted by Cleanup.
-func (e *T) TempDir() string {
-	loc := location(1)
+func (e *T) NewTempDir() string {
+	loc := Caller(1)
 	f, err := ioutil.TempDir("", "")
 	if err != nil {
 		e.Fatalf("%s: TempDir() failed: %v", loc, err)
@@ -708,7 +727,7 @@
 	return b, inv
 }
 
-// UseShardBinDir ensures that a shared directory is used for binaries
+// UseSharedBinDir 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
diff --git a/lib/testutil/v23tests/v23tests_test.go b/lib/testutil/v23tests/v23tests_test.go
index 9546c05..1ab0f52 100644
--- a/lib/testutil/v23tests/v23tests_test.go
+++ b/lib/testutil/v23tests/v23tests_test.go
@@ -153,7 +153,7 @@
 	// Read something from a file.
 	{
 		want := "Hello from a file!"
-		f := env.TempFile()
+		f := env.NewTempFile()
 		f.WriteString(want)
 		f.Seek(0, 0)
 		if got := cat.WithStdin(f).Start().Output(); want != got {
@@ -165,7 +165,7 @@
 	{
 		want := testutil.RandomBytes(1 << 20)
 		expectedSum := sha1.Sum(want)
-		f := env.TempFile()
+		f := env.NewTempFile()
 		f.Write(want)
 		f.Seek(0, 0)
 		got := cat.WithStdin(f).Start().Output()
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index f7c252b..85a6f35 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -611,8 +611,10 @@
 			if err := fs.serve(); err != nil {
 				// TODO(caprita): Logging errors here is too spammy. For example, "not
 				// authorized" errors shouldn't be logged as server errors.
+				// TODO(cnicolaou): revisit this when verror2 transition is
+				// done.
 				if err != io.EOF {
-					vlog.Errorf("Flow serve on %v failed: %v", ep, err)
+					vlog.VI(2).Infof("Flow serve on %v failed: %v", ep, err)
 				}
 			}
 		}(flow)
diff --git a/security/agent/agent_v23_test.go b/security/agent/agent_v23_test.go
index f074006..0125236 100644
--- a/security/agent/agent_v23_test.go
+++ b/security/agent/agent_v23_test.go
@@ -18,7 +18,7 @@
 	// Test passphrase handling.
 
 	bin := i.BuildGoPkg("v.io/core/veyron/security/agent/agentd")
-	credentials := "VEYRON_CREDENTIALS=" + i.TempDir()
+	credentials := "VEYRON_CREDENTIALS=" + i.NewTempDir()
 
 	// Create the passphrase
 	agent := bin.WithEnv(credentials).Start("echo", "Hello")
@@ -54,7 +54,7 @@
 	agentBin := i.BuildGoPkg("v.io/core/veyron/security/agent/agentd")
 	principalBin := i.BuildGoPkg("v.io/core/veyron/security/agent/test_principal")
 
-	credentials := "VEYRON_CREDENTIALS=" + i.TempDir()
+	credentials := "VEYRON_CREDENTIALS=" + i.NewTempDir()
 	agent := agentBin.WithEnv(credentials).Start(principalBin.Path())
 	agent.WaitOrDie(nil, os.Stderr)
 }
@@ -79,7 +79,7 @@
 
 	agentBin := i.BuildGoPkg("v.io/core/veyron/security/agent/agentd")
 	testChildBin := i.BuildGoPkg("v.io/core/veyron/security/agent/test_child")
-	credentials := "VEYRON_CREDENTIALS=" + i.TempDir()
+	credentials := "VEYRON_CREDENTIALS=" + i.NewTempDir()
 
 	// Test running a single app.
 	pingpongBin := buildAndRunPingpongServer(i, rootMTArg)
@@ -129,11 +129,11 @@
 	vrun := i.BuildGoPkg("v.io/core/veyron/tools/vrun")
 
 	pingpongBin := buildAndRunPingpongServer(i, rootMTArg)
-	credentials := "VEYRON_CREDENTIALS=" + i.TempDir()
+	credentials := "VEYRON_CREDENTIALS=" + i.NewTempDir()
 
 	// This script increments a counter in $COUNTER_FILE and exits with exit code 0
 	// while the counter is < 5, and 1 otherwise.
-	counterFile := i.TempFile()
+	counterFile := i.NewTempFile()
 
 	script := fmt.Sprintf("%q %q|| exit 101\n", pingpongBin.Path(), rootMTArg)
 	script += fmt.Sprintf("%q %q %q|| exit 102\n", vrun.Path(), pingpongBin.Path(), rootMTArg)
@@ -147,7 +147,7 @@
 		i.Logf(exit)
 		counterFile.Seek(0, 0)
 		fmt.Fprintln(counterFile, "0")
-		agent := agentBin.WithEnv(credentials).Start("--additional_principals", i.TempDir(), exit, "bash", "-c", script)
+		agent := agentBin.WithEnv(credentials).Start("--additional_principals", i.NewTempDir(), exit, "bash", "-c", script)
 		if err := agent.Wait(nil, nil); err == nil {
 			if len(status) > 0 {
 				i.Fatalf("%s: expected an error %q that didn't occur", loc, status)
diff --git a/services/mgmt/application/applicationd/applicationd_v23_test.go b/services/mgmt/application/applicationd/applicationd_v23_test.go
index a3c0c16..7fa3406 100644
--- a/services/mgmt/application/applicationd/applicationd_v23_test.go
+++ b/services/mgmt/application/applicationd/applicationd_v23_test.go
@@ -53,7 +53,7 @@
 
 	// Start the application repository.
 	appRepoName := "test-app-repo"
-	appRepoStore := i.TempDir()
+	appRepoStore := i.NewTempDir()
 	args := []string{
 		"-name=" + appRepoName,
 		"-store=" + appRepoStore,
@@ -90,7 +90,7 @@
 
 	// Create an application envelope.
 	appRepoSuffix := "test-application/v1"
-	appEnvelopeFile := i.TempFile()
+	appEnvelopeFile := i.NewTempFile()
 	wantEnvelope := `{
   "Title": "title",
   "Args": null,
diff --git a/services/mgmt/binary/binaryd/binaryd_v23_test.go b/services/mgmt/binary/binaryd/binaryd_v23_test.go
index 9646991..2ecdbf4 100644
--- a/services/mgmt/binary/binaryd/binaryd_v23_test.go
+++ b/services/mgmt/binary/binaryd/binaryd_v23_test.go
@@ -123,7 +123,7 @@
 	binaryRepoBin.Start(args...)
 
 	// Upload a random binary file.
-	binFile := i.TempFile()
+	binFile := i.NewTempFile()
 	if _, err := binFile.Write(testutil.RandomBytes(16 * 1000 * 1000)); err != nil {
 		i.Fatalf("Write() failed: %v", err)
 	}
diff --git a/services/mgmt/build/buildd/buildd_v23_test.go b/services/mgmt/build/buildd/buildd_v23_test.go
index 99b28d0..54de166 100644
--- a/services/mgmt/build/buildd/buildd_v23_test.go
+++ b/services/mgmt/build/buildd/buildd_v23_test.go
@@ -47,7 +47,7 @@
 	buildServerBin.Start(args...)
 
 	// Create and build a test source file.
-	testGoPath := i.TempDir()
+	testGoPath := i.NewTempDir()
 	testBinDir := filepath.Join(testGoPath, "bin")
 	if err := os.MkdirAll(testBinDir, os.FileMode(0700)); err != nil {
 		i.Fatalf("MkdirAll(%v) failed: %v", testBinDir, err)
diff --git a/services/mgmt/profile/profiled/profiled_v23_test.go b/services/mgmt/profile/profiled/profiled_v23_test.go
index 4180ebe..f0d62c4 100644
--- a/services/mgmt/profile/profiled/profiled_v23_test.go
+++ b/services/mgmt/profile/profiled/profiled_v23_test.go
@@ -45,7 +45,7 @@
 
 	// Start the profile repository.
 	profileRepoName := "test-profile-repo"
-	profileRepoStore := i.TempDir()
+	profileRepoStore := i.NewTempDir()
 	args := []string{
 		"-name=" + profileRepoName, "-store=" + profileRepoStore,
 		"-veyron.tcp.address=127.0.0.1:0",
diff --git a/tools/debug/debug_v23_test.go b/tools/debug/debug_v23_test.go
index 4cb0ccd..dee8b6a 100644
--- a/tools/debug/debug_v23_test.go
+++ b/tools/debug/debug_v23_test.go
@@ -34,7 +34,7 @@
 	v23tests.RunRootMT(i, "--veyron.tcp.address=127.0.0.1:0")
 
 	// Create a temp file before we list the logs.
-	fileName := filepath.Base(i.TempFile().Name())
+	fileName := filepath.Base(i.NewTempFile().Name())
 	binary := i.BuildGoPkg("v.io/core/veyron/tools/debug")
 	output := binary.Start("glob", "__debug/logs/*").Output()
 
@@ -61,7 +61,7 @@
 }
 
 func createTestLogFile(i *v23tests.T, content string) *os.File {
-	file := i.TempFile()
+	file := i.NewTempFile()
 	_, err := file.Write([]byte(content))
 	if err != nil {
 		i.Fatalf("Write failed: %v", err)
diff --git a/tools/mgmt/dummy.go b/tools/mgmt/dummy.go
new file mode 100644
index 0000000..2d4fedb
--- /dev/null
+++ b/tools/mgmt/dummy.go
@@ -0,0 +1 @@
+package mgmt
diff --git a/tools/mgmt/mgmt_v23_test.go b/tools/mgmt/mgmt_v23_test.go
new file mode 100644
index 0000000..1fa0fef
--- /dev/null
+++ b/tools/mgmt/mgmt_v23_test.go
@@ -0,0 +1,462 @@
+// Test the device manager and related services and tools.
+//
+// By default, this script tests the device manager in a fashion amenable
+// to automatic testing: the --single_user is passed to the device
+// manager so that all device manager components run as the same user and
+// no user input (such as an agent pass phrase) is needed.
+//
+// When this script is invoked with the --with_suid <user> flag, it
+// installs the device manager in its more secure multi-account
+// configuration where the device manager runs under the account of the
+// invoker and test apps will be executed as <user>. This mode will
+// require root permisisons to install and may require configuring an
+// agent passphrase.
+//
+// For exanple:
+//
+//   v23 go test -v . --v23.tests --with_suid vanaguest
+//
+// to test a device manager with multi-account support enabled for app
+// account vanaguest.
+//
+package mgmt_test
+
+//go:generate v23 test generate .
+
+import (
+	"bytes"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"math/rand"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"v.io/core/veyron2/vlog"
+
+	"v.io/core/veyron/lib/testutil/v23tests"
+	_ "v.io/core/veyron/profiles"
+)
+
+var (
+	suidUserFlag string
+	hostname     string
+	errTimeout   = errors.New("timeout")
+)
+
+func init() {
+	flag.StringVar(&suidUserFlag, "with_suid", "", "run the device manager as the specified user")
+	name, err := os.Hostname()
+	if err != nil {
+		panic(fmt.Sprintf("Hostname() failed: %v", err))
+	}
+	hostname = name
+}
+
+// waitFor waits for the specified timeout for the supplied function parameter
+// function to return a result or an error. If error is nil, the function
+// will be called again to see if a result can be obtained until the timeout
+// expires.
+func waitFor(fn func() (string, error), timeout time.Duration) (string, error) {
+	errCh := make(chan error)
+	resultCh := make(chan string)
+	cancelCh := make(chan struct{})
+
+	eval := func() {
+		for {
+			result, err := fn()
+			switch {
+			case err != nil:
+				errCh <- err
+				close(errCh)
+				close(resultCh)
+				return
+			case len(result) > 0:
+				resultCh <- result
+				close(resultCh)
+				close(errCh)
+				return
+			}
+			time.Sleep(100 * time.Millisecond)
+			select {
+			case <-cancelCh:
+				return
+			default:
+			}
+		}
+	}
+
+	go eval()
+
+	select {
+	case <-time.After(timeout):
+		close(cancelCh)
+		return "", errTimeout
+	case result := <-resultCh:
+		return strings.TrimRight(result, "\n"), nil
+	case err := <-errCh:
+		return "", err
+	}
+}
+
+func waitForOrDie(i *v23tests.T, fn func() (string, error), timeout time.Duration) string {
+	result, err := waitFor(fn, timeout)
+	if err != nil {
+		i.Fatal(fmt.Sprintf("%s: %s", v23tests.Caller(1), err))
+	}
+	return result
+}
+
+func V23TestNodeManager(i *v23tests.T) {
+	defer fmt.Fprintf(os.Stderr, "--------------- SHUTDOWN ---------------\n")
+	userFlag := "--single_user"
+	withSuid := false
+	if len(suidUserFlag) > 0 {
+		userFlag = "--with_suid=" + suidUserFlag
+		withSuid = true
+	}
+	i.Logf("user flag: %q", userFlag)
+
+	v23tests.RunRootMT(i, "--veyron.tcp.address=127.0.0.1:0")
+	workDir := i.NewTempDir()
+
+	mkSubdir := func(sub string) string {
+		n := filepath.Join(workDir, sub)
+		if err := os.Mkdir(n, 0755); err != nil {
+			i.Fatalf("failed to create %q: %v", n, err)
+		}
+		return n
+	}
+
+	binStagingDir := mkSubdir("bin")
+	agentServerBin := i.BuildGoPkg("v.io/core/veyron/security/agent/agentd")
+	suidHelperBin := i.BuildGoPkg("v.io/core/veyron/services/mgmt/suidhelper")
+	initHelperBin := i.BuildGoPkg("v.io/core/veyron/services/mgmt/inithelper")
+
+	// Device manager and principal use their own set of credentials.
+	// The credentials directory will be populated with Start an application
+	// server under the blessing "alice/myworkstation/applicationd" so that
+	// the device ("alice/myworkstation") can talk to it. ALl of the binaries
+	// that communicate with each other must share this credentials directory.
+	credentials := "VEYRON_CREDENTIALS=" + i.NewTempDir()
+	namespaceBin := i.BuildGoPkg("v.io/core/veyron/tools/namespace").WithEnv(credentials)
+	debugBin := i.BuildGoPkg("v.io/core/veyron/tools/debug").WithEnv(credentials)
+	deviceBin := i.BuildGoPkg("v.io/core/veyron/tools/mgmt/device").WithEnv(credentials)
+	devicedBin := i.BuildGoPkg("v.io/core/veyron/services/mgmt/device/deviced").WithEnv(credentials)
+	deviceScript := i.BinaryFromPath("device/devicex").WithEnv(credentials)
+	principalBin := i.BuildGoPkg("v.io/core/veyron/tools/principal").WithEnv(credentials)
+	binarydBin := i.BuildGoPkg("v.io/core/veyron/services/mgmt/binary/binaryd").WithEnv(credentials)
+	binaryBin := i.BuildGoPkg("v.io/core/veyron/tools/binary").WithEnv(credentials)
+	applicationdBin := i.BuildGoPkg("v.io/core/veyron/services/mgmt/application/applicationd").WithEnv(credentials)
+	applicationBin := i.BuildGoPkg("v.io/core/veyron/tools/application").WithEnv(credentials)
+
+	appDName := "applicationd"
+	devicedAppName := filepath.Join(appDName, "deviced", "test")
+
+	i.BinaryFromPath("/bin/cp").Start(agentServerBin.Path(), suidHelperBin.Path(), initHelperBin.Path(), devicedBin.Path(), binStagingDir).WaitOrDie(os.Stdout, os.Stderr)
+
+	dmInstallDir := filepath.Join(workDir, "dm")
+	i.SetVar("VANADIUM_DEVICE_DIR", dmInstallDir)
+
+	neighborhoodName := fmt.Sprintf("%s-%d-%d", hostname, os.Getpid(), rand.Int())
+
+	deviceScript.Start(
+		"install",
+		binStagingDir,
+		userFlag,
+		"--origin="+devicedAppName,
+		"--",
+		"--veyron.tcp.address=127.0.0.1:0",
+		"--neighborhood_name="+neighborhoodName).
+		WaitOrDie(os.Stdout, os.Stderr)
+
+	deviceScript.Start("start").WaitOrDie(os.Stdout, os.Stderr)
+
+	mtName := "devices/" + hostname
+
+	resolve := func(name string) (string, error) {
+		inv := namespaceBin.Start("resolve", name)
+		// Avoid leaving a ton of invocations lying around to obscure
+		// logging output. Ignore the error code from Wait since it'll
+		// deadlock if we call Fatalf etc from a goroutine.
+		defer inv.Wait(nil, os.Stderr)
+		return strings.TrimRight(inv.Output(), "\n"), nil
+	}
+	resolveMtName := func() (string, error) {
+		return resolve(mtName)
+	}
+
+	mtEP := waitForOrDie(i, resolveMtName, time.Minute)
+
+	run := func(bin *v23tests.Binary, args ...string) string {
+		var stdout, stderr bytes.Buffer
+		inv := bin.Start(args...)
+		if err := inv.Wait(&stdout, &stderr); err != nil {
+			loc := v23tests.Caller(1)
+			i.Logf("%s: %v failed: %s", loc, filepath.Base(bin.Path()), err)
+			i.Fatalf("%s: %v", loc, stderr.String())
+		}
+		return strings.TrimRight(stdout.String(), "\n")
+	}
+
+	// Verify that device manager's mounttable is published under the expected
+	// name (hostname).
+	if got := run(namespaceBin, "glob", mtName); len(got) == 0 {
+		i.Fatalf("glob failed for %q", mtName)
+	}
+
+	// Create a self-signed blessing with name "alice" and set it as default
+	// and shareable with all peers on the principal that the device manager
+	// and principal are sharing (via the .WithEnv method) above. This
+	// blessing will be used by all commands run by the device manager that
+	// specify the same credentials.
+	// TODO - update these commands
+	// that except those
+	// run with a different which gets a principal forked from the
+	// process principal.
+	blessingFilename := filepath.Join(workDir, "alice.bless")
+	blessing := run(principalBin, "blessself", "alice")
+	if err := ioutil.WriteFile(blessingFilename, []byte(blessing), 0755); err != nil {
+		i.Fatal(err)
+	}
+	run(principalBin, "store", "setdefault", blessingFilename)
+	run(principalBin, "store", "set", blessingFilename, "...")
+	defer os.Remove(blessingFilename)
+
+	// Claim the device as "alice/myworkstation".
+	deviceBin.Start("claim", mtName+"/devmgr/device", "myworkstation")
+
+	resolveChanged := func(name, prev string) (string, error) {
+		ep, err := resolve(name)
+		if err == nil && ep != prev {
+			return ep, nil
+		}
+		return "", nil
+	}
+
+	waitForChange := func(name, prevEP string) string {
+		newEP := waitForOrDie(i, func() (string, error) { return resolveChanged(name, prevEP) }, time.Minute)
+		loc := v23tests.Caller(1)
+		if newEP == prevEP {
+			i.Fatalf("%s: failed to obtain new endpoint for %s: same as before %v", loc, name, newEP)
+		}
+		vlog.Infof("%s: new endpoint: %s -> %s", loc, name, newEP)
+		return newEP
+	}
+
+	// Wait for the device manager to update its mount table entry.
+	mtEP = waitForChange(mtName, mtEP)
+
+	if withSuid {
+		/*
+		   		   "${DEVICE_BIN}" associate add "${MT_NAME}/devmgr/device" "${SUID_USER}"  "alice"
+		        shell_test::assert_eq   "$("${DEVICE_BIN}" associate list "${MT_NAME}/devmgr/device")" \
+		          "alice ${SUID_USER}" "${LINENO}"
+		*/
+	}
+
+	// Verify the device's default blessing is as expected.
+	inv := debugBin.Start("stats", "read", mtName+"/devmgr/__debug/stats/security/principal/*/blessingstore")
+	inv.ExpectRE(".*Default blessings: alice/myworkstation$", -1)
+
+	// Get the device's profile, which should be set to non-empty string
+	inv = deviceBin.Start("describe", mtName+"/devmgr/device")
+
+	parts := inv.ExpectRE(`{Profiles:map\[(.*):{}\]}`, 1)
+	expectOneMatch := func(parts [][]string) string {
+		if len(parts) != 1 || len(parts[0]) != 2 {
+			loc := v23tests.Caller(1)
+			i.Fatalf("%s: failed to match profile: %#v", loc, parts)
+		}
+		return parts[0][1]
+	}
+	deviceProfile := expectOneMatch(parts)
+	if len(deviceProfile) == 0 {
+		i.Fatalf("failed to get profile")
+	}
+
+	binarydName := "binaryd"
+	// Start an application server under the blessing
+	// "alice/myworkstation/applicationd" so that
+	// the device ("alice/myworkstation") can talk to it.
+	binarydBin.Start(
+		"--name="+binarydName,
+		"--root_dir="+filepath.Join(workDir, "binstore"),
+		"--veyron.tcp.address=127.0.0.1:0",
+		"--http=127.0.0.1:0")
+
+	sampleAppBinName := binarydName + "/testapp"
+	run(binaryBin, "upload", sampleAppBinName, binarydBin.Path())
+
+	// Verify that the binary we uploaded is shown by glob, we need to run
+	// with the same blessed credentials as binaryd in order to be able to
+	// glob its names pace.
+	if got := run(namespaceBin.WithEnv(credentials), "glob", sampleAppBinName); len(got) == 0 {
+		i.Fatalf("glob failed for %q", sampleAppBinName)
+	}
+
+	appstoreDir := mkSubdir("apptstore")
+
+	applicationdBin.Start(
+		"--name="+appDName,
+		"--store="+appstoreDir,
+		"--veyron.tcp.address=127.0.0.1:0",
+	)
+
+	sampleAppName := appDName + "/testapp/v0"
+	appPubName := "testbinaryd"
+	appEnvelopeFilename := filepath.Join(workDir, "app.envelope")
+	appEnvelope := fmt.Sprintf("{\"Title\":\"BINARYD\", \"Args\":[\"--name=%s\", \"--root_dir=./binstore\", \"--veyron.tcp.address=127.0.0.1:0\", \"--http=127.0.0.1:0\"], \"Binary\":{\"File\":%q}, \"Env\":[]}", appPubName, sampleAppBinName)
+	ioutil.WriteFile(appEnvelopeFilename, []byte(appEnvelope), 0666)
+	defer os.Remove(appEnvelopeFilename)
+
+	output := run(applicationBin, "put", sampleAppName, deviceProfile, appEnvelopeFilename)
+	if got, want := output, "Application envelope added successfully."; got != want {
+		i.Fatalf("got %q, want %q", got, want)
+	}
+
+	// Verify that the envelope we uploaded shows up with glob.
+	inv = applicationBin.Start("match", sampleAppName, deviceProfile)
+	parts = inv.ExpectSetEventuallyRE(`"Title": "(.*)",`, `"File": "(.*)",`)
+	if got, want := len(parts), 2; got != want {
+		i.Fatalf("got %d, want %d", got, want)
+	}
+	for line, want := range []string{"BINARYD", sampleAppBinName} {
+		if got := parts[line][1]; got != want {
+			i.Fatalf("got %q, want %q", got, want)
+		}
+	}
+
+	// Install the app on the device.
+	inv = deviceBin.Start("install", mtName+"/devmgr/apps", sampleAppName)
+	parts = inv.ExpectRE(`Successfully installed: "(.*)"`, 1)
+	installationName := expectOneMatch(parts)
+
+	// Verify that the installation shows up when globbing the device manager.
+	output = run(namespaceBin, "glob", mtName+"/devmgr/apps/BINARYD/*")
+	if got, want := output, installationName; got != want {
+		i.Fatalf("got %q, want %q", got, want)
+	}
+
+	// Start an instance of the app, granting it blessing extension myapp.
+	inv = deviceBin.Start("start", installationName, "myapp")
+	parts = inv.ExpectRE(`Successfully started: "(.*)"`, 1)
+	instanceName := expectOneMatch(parts)
+
+	resolveInstanceName := func() (string, error) {
+		return resolve(mtName + "/" + appPubName)
+	}
+	waitForOrDie(i, resolveInstanceName, time.Minute)
+
+	// Verify that the instance shows up when globbing the device manager.
+	output = run(namespaceBin, "glob", mtName+"/devmgr/apps/BINARYD/*/*")
+	if got, want := output, instanceName; got != want {
+		i.Fatalf("got %q, want %q", got, want)
+	}
+
+	// TODO(rjkroege): Verify that the app is actually running as ${SUID_USER}
+
+	// Verify the app's default blessing.
+	inv = debugBin.Start("stats", "read", instanceName+"/stats/security/principal/*/blessingstore")
+	// Why is this alice/myworkstation/myapp/BINARYD and not
+	// alice/myapp/BINARYD as seen by the test.sh?
+	inv.ExpectRE(".*Default blessings: alice/myworkstation/myapp/BINARYD$", -1)
+
+	// Stop the instance
+	run(deviceBin, "stop", instanceName)
+
+	// Verify that logs, but not stats, show up when globbing the
+	// stopped instance.
+	output = run(namespaceBin, "glob", instanceName+"/stats/...")
+	if len(output) > 0 {
+		i.Fatalf("no output expected for glob %s/stats/..., got %q", output, instanceName)
+	}
+	output = run(namespaceBin, "glob", instanceName+"/logs/...")
+	if len(output) == 0 {
+		i.Fatalf("output expected for glob %s/logs/..., but got none", instanceName)
+	}
+
+	// Upload a deviced binary
+	devicedAppBinName := binarydName + "/deviced"
+	run(binaryBin, "upload", devicedAppBinName, devicedBin.Path())
+
+	// Upload a device manager envelope, make sure that we set
+	// VEYRON_CREDENTIALS in the enevelope, otherwise the updated device
+	// manager will use new credentials.
+	devicedEnvelopeFilename := filepath.Join(workDir, "deviced.envelope")
+	devicedEnvelope := fmt.Sprintf("{\"Title\":\"device manager\", \"Binary\":{\"File\":%q}, \"Env\":[%q]}", devicedAppBinName, credentials)
+	ioutil.WriteFile(devicedEnvelopeFilename, []byte(devicedEnvelope), 0666)
+	defer os.Remove(devicedEnvelopeFilename)
+	run(applicationBin, "put", devicedAppName, deviceProfile, devicedEnvelopeFilename)
+
+	// Update the device manager.
+	run(deviceBin, "update", mtName+"/devmgr/device")
+	mtEP = waitForChange(mtName, mtEP)
+
+	// Verify that device manager's mounttable is still published under the
+	// expected name (hostname).
+	if run(namespaceBin, "glob", mtName) == "" {
+		i.Fatalf("failed to glob %s", mtName)
+	}
+
+	// Revert the device manager
+	run(deviceBin, "revert", mtName+"/devmgr/device")
+	mtEP = waitForChange(mtName, mtEP)
+
+	// Verify that device manager's mounttable is still published under the
+	// expected name (hostname).
+	if run(namespaceBin, "glob", mtName) == "" {
+		i.Fatalf("failed to glob %s", mtName)
+	}
+
+	// Verify that the local mounttable exists, and that the device manager,
+	// the global namespace, and the neighborhood are mounted on it.
+	n := mtEP + "/devmgr"
+	if run(namespaceBin, "resolve", n) == "" {
+		i.Fatalf("failed to resolve %s", n)
+	}
+	n = mtEP + "/nh"
+	if run(namespaceBin, "resolve", n) == "" {
+		i.Fatalf("failed to resolve %s", n)
+	}
+	namespaceRoot, _ := i.GetVar("NAMESPACE_ROOT")
+	n = mtEP + "/global"
+	if got, want := run(namespaceBin, "resolve", n), namespaceRoot; got != want {
+		i.Fatalf("got %q, want %q", got, want)
+	}
+
+	// Suspend the device manager.
+	run(deviceBin, "suspend", mtName+"/devmgr/device")
+	mtEP = waitForChange(mtName, mtEP)
+
+	// Stop the device manager.
+	run(deviceScript, "stop")
+
+	// Wait for the mounttable entry to go away.
+	noEntry := func() (string, error) {
+		ep, err := resolve(mtName)
+		if err == nil && len(ep) == 0 {
+			return "done", nil
+		}
+		return "", nil
+	}
+	waitForOrDie(i, noEntry, time.Minute)
+
+	fi, err := ioutil.ReadDir(dmInstallDir)
+	if err != nil {
+		i.Fatalf("failed to readdir for %q: %v", dmInstallDir, err)
+	}
+
+	run(deviceScript, "uninstall")
+
+	fi, err = ioutil.ReadDir(dmInstallDir)
+	if err == nil || len(fi) > 0 {
+		i.Fatalf("managed to read %d entries from %q", len(fi), dmInstallDir)
+	}
+	if err != nil && !strings.Contains(err.Error(), "no such file or directory") {
+		i.Fatalf("wrong error: %v", err)
+	}
+}
diff --git a/tools/mgmt/v23_test.go b/tools/mgmt/v23_test.go
new file mode 100644
index 0000000..b72930b
--- /dev/null
+++ b/tools/mgmt/v23_test.go
@@ -0,0 +1,21 @@
+// This file was auto-generated via go generate.
+// DO NOT UPDATE MANUALLY
+package mgmt_test
+
+import "testing"
+import "os"
+
+import "v.io/core/veyron/lib/testutil"
+import "v.io/core/veyron/lib/testutil/v23tests"
+
+func TestMain(m *testing.M) {
+	testutil.Init()
+	cleanup := v23tests.UseSharedBinDir()
+	r := m.Run()
+	cleanup()
+	os.Exit(r)
+}
+
+func TestV23NodeManager(t *testing.T) {
+	v23tests.RunTest(t, V23TestNodeManager)
+}
diff --git a/tools/principal/principal_v23_test.go b/tools/principal/principal_v23_test.go
index 0ea874e..d4b03ab 100644
--- a/tools/principal/principal_v23_test.go
+++ b/tools/principal/principal_v23_test.go
@@ -41,7 +41,7 @@
 
 func V23TestBlessSelf(t *v23tests.T) {
 	var (
-		outputDir         = t.TempDir()
+		outputDir         = t.NewTempDir()
 		aliceDir          = filepath.Join(outputDir, "alice")
 		aliceBlessingFile = filepath.Join(outputDir, "aliceself")
 	)
@@ -65,7 +65,7 @@
 
 func V23TestStore(t *v23tests.T) {
 	var (
-		outputDir   = t.TempDir()
+		outputDir   = t.NewTempDir()
 		bin         = t.BuildGoPkg("v.io/core/veyron/tools/principal")
 		aliceDir    = filepath.Join(outputDir, "alice")
 		aliceFriend = filepath.Join(outputDir, "alice.bless")
@@ -103,7 +103,7 @@
 
 func V23TestDump(t *v23tests.T) {
 	var (
-		outputDir = t.TempDir()
+		outputDir = t.NewTempDir()
 		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
 		aliceDir  = filepath.Join(outputDir, "alice")
 	)
@@ -128,7 +128,7 @@
 
 func V23TestRecvBlessings(t *v23tests.T) {
 	var (
-		outputDir = t.TempDir()
+		outputDir = t.NewTempDir()
 		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
 		aliceDir  = filepath.Join(outputDir, "alice")
 		carolDir  = filepath.Join(outputDir, "carol")
@@ -214,7 +214,7 @@
 
 func V23TestFork(t *v23tests.T) {
 	var (
-		outputDir             = t.TempDir()
+		outputDir             = t.NewTempDir()
 		bin                   = t.BuildGoPkg("v.io/core/veyron/tools/principal")
 		aliceDir              = filepath.Join(outputDir, "alice")
 		alicePhoneDir         = filepath.Join(outputDir, "alice-phone")
@@ -267,7 +267,7 @@
 
 func V23TestCreate(t *v23tests.T) {
 	var (
-		outputDir = t.TempDir()
+		outputDir = t.NewTempDir()
 		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
 		aliceDir  = filepath.Join(outputDir, "alice")
 	)
@@ -286,7 +286,7 @@
 
 func V23TestCaveats(t *v23tests.T) {
 	var (
-		outputDir         = t.TempDir()
+		outputDir         = t.NewTempDir()
 		aliceDir          = filepath.Join(outputDir, "alice")
 		aliceBlessingFile = filepath.Join(outputDir, "aliceself")
 	)
@@ -318,13 +318,13 @@
 
 func V23TestForkWithoutVDLPATH(t *v23tests.T) {
 	var (
-		parent = t.TempDir()
+		parent = t.NewTempDir()
 		bin    = t.BuildGoPkg("v.io/core/veyron/tools/principal").WithEnv("VANADIUM_ROOT=''", "VDLPATH=''")
 	)
 	if err := bin.Start("create", parent, "parent").Wait(os.Stdout, os.Stderr); err != nil {
 		t.Fatalf("create %q failed: %v", parent, err)
 	}
-	if err := bin.Start("--veyron.credentials="+parent, "fork", t.TempDir(), "child").Wait(os.Stdout, os.Stderr); err != nil {
+	if err := bin.Start("--veyron.credentials="+parent, "fork", t.NewTempDir(), "child").Wait(os.Stdout, os.Stderr); err != nil {
 		t.Errorf("fork failed: %v", err)
 	}
 }
diff --git a/tools/vrun/internal/vrun_v23_test.go b/tools/vrun/internal/vrun_v23_test.go
index 083bc20..fbb13cf 100644
--- a/tools/vrun/internal/vrun_v23_test.go
+++ b/tools/vrun/internal/vrun_v23_test.go
@@ -18,7 +18,7 @@
 
 	v23tests.RunRootMT(t, "--veyron.tcp.address=127.0.0.1:0")
 
-	creds := t.TempDir()
+	creds := t.NewTempDir()
 	agentdBin.WithEnv("VEYRON_CREDENTIALS="+creds).Start("--no_passphrase",
 		"--additional_principals="+creds,
 		helperBin.Path(),