Merge "veyron/{services/mgmt,tools}: end-to-end tests for binaryd, buildd, and profiled"
diff --git a/services/mgmt/application/applicationd/main.go b/services/mgmt/application/applicationd/main.go
index a152b6e..425eb92 100644
--- a/services/mgmt/application/applicationd/main.go
+++ b/services/mgmt/application/applicationd/main.go
@@ -17,8 +17,8 @@
 	protocol = flag.String("protocol", "tcp", "protocol to listen on")
 	address  = flag.String("address", ":0", "address to listen on")
 
-	name      = flag.String("name", "", "name to mount the application manager as")
-	storeName = flag.String("store", "", "object name of the application manager store")
+	name      = flag.String("name", "", "name to mount the application repository as")
+	storeName = flag.String("store", "", "object name of the application repository store")
 )
 
 func main() {
@@ -45,7 +45,7 @@
 	if err := server.Serve(*name, dispatcher); err != nil {
 		vlog.Fatalf("Serve(%v) failed: %v", *name, err)
 	}
-	vlog.VI(0).Infof("Application repository published at %v/%v", endpoint, *name)
+	vlog.Infof("Application repository running at endpoint=%q", endpoint)
 
 	// Wait until shutdown.
 	<-signals.ShutdownOnSignals()
diff --git a/services/mgmt/application/applicationd/test.sh b/services/mgmt/application/applicationd/test.sh
index cfa1004..af10d2a 100755
--- a/services/mgmt/application/applicationd/test.sh
+++ b/services/mgmt/application/applicationd/test.sh
@@ -1,8 +1,8 @@
 #!/bin/bash
 
-# Test the applicationd binary.
+# Test the application repository daemon.
 #
-# This test starts an application repository server and uses the
+# This test starts an application repository daemon and uses the
 # application repository client to verify that <application>.Put(),
 # <application>.Match(), and <application>.Remove() work as expected.
 
@@ -11,59 +11,47 @@
 build() {
   local -r GO="${REPO_ROOT}/scripts/build/go"
   "${GO}" build veyron/services/mgmt/application/applicationd || shell_test::fail "line ${LINENO}: failed to build 'applicationd'"
-  "${GO}" build veyron/services/mounttable/mounttabled || shell_test::fail "line ${LINENO}: failed to build 'mounttabled'"
   "${GO}" build veyron/services/store/stored || shell_test::fail "line ${LINENO}: failed to build 'stored'"
   "${GO}" build veyron/tools/application || shell_test::fail "line ${LINENO}: failed to build 'application'"
-  "${GO}" build veyron/tools/findunusedport || shell_test::fail "line ${LINENO}: failed to build 'findunusedport'"
-  "${GO}" build veyron/tools/identity || shell_test::fail "line ${LINENO}: failed to build 'identity'"
 }
 
 main() {
   cd "${TMPDIR}"
   build
 
-  # Generate a self-signed identity.
-  local -r ID_FILE=$(shell::tmp_file)
-  ./identity generate > "${ID_FILE}"
-
-  # Start the mounttable daemon.
-  local -r MT_PORT=$(./findunusedport)
-  local -r MT_LOG=$(shell::tmp_file)
-  ./mounttabled --address="127.0.0.1:${MT_PORT}" &> "${MT_LOG}" &
-  shell_test::wait_for "${MT_LOG}" "Mount table service"
-
-  export VEYRON_IDENTITY="${ID_FILE}"
-  export NAMESPACE_ROOT="/127.0.0.1:${MT_PORT}"
+  shell_test::setup_server_test
 
   # Start the store daemon.
   local -r DB_DIR=$(shell::tmp_dir)
   local -r STORE="application-test-store"
-  local -r STORE_LOG=$(shell::tmp_file)
-  ./stored --name="${STORE}" --address=127.0.0.1:0 --db="${DB_DIR}" &> "${STORE_LOG}" &
-  shell_test::wait_for "${STORE_LOG}" "Viewer running"
+  shell_test::start_server ./stored --name="${STORE}" --address=127.0.0.1:0 --db="${DB_DIR}"
 
   # Start the application repository daemon.
   local -r REPO="applicationd-test-repo"
-  local -r REPO_LOG=$(shell::tmp_file)
-  ./applicationd --name="${REPO}" --address=127.0.0.1:0 --store="${STORE}" &> "${REPO_LOG}" &
-  shell_test::wait_for "${REPO_LOG}" "Application repository published"
+  shell_test::start_server ./applicationd --name="${REPO}" --address=127.0.0.1:0 --store="${STORE}" -logtostderr
 
   # Create an application envelope.
   local -r APPLICATION="${REPO}/test-application/v1"
   local -r PROFILE="test-profile"
-  local -r ENVELOPE=$(shell::tmp_file)
-  cat > "${ENVELOPE}" <<EOF
+  local -r ENVELOPE_WANT=$(shell::tmp_file)
+  cat > "${ENVELOPE_WANT}" <<EOF
 {"Title":"title", "Args":[], "Binary":"foo", "Env":[]}
 EOF
-  ./application put "${APPLICATION}" "${PROFILE}" "${ENVELOPE}" || shell_test::fail "line ${LINENO}: 'put' failed"
+  ./application put "${APPLICATION}" "${PROFILE}" "${ENVELOPE_WANT}" || shell_test::fail "line ${LINENO}: 'put' failed"
 
-  # Match an application envelope.
-  local -r ENVELOPE2=$(shell::tmp_file)
-  ./application match "${APPLICATION}" "${PROFILE}" | tee "${ENVELOPE2}" || shell_test::fail "line ${LINENO}: 'match' failed"
+  # Match the application envelope.
+  local -r ENVELOPE_GOT=$(shell::tmp_file)
+  ./application match "${APPLICATION}" "${PROFILE}" | tee "${ENVELOPE_GOT}" || shell_test::fail "line ${LINENO}: 'match' failed"
+  if [[ $(cmp "${ENVELOPE_WANT}" "${ENVELOPE_GOT}" &> /dev/null) ]]; then
+    shell_test::fail "mismatching application envelopes"
+  fi
 
-  # Remove an application envelope.
+  # Remove the application envelope.
   ./application remove "${APPLICATION}" "${PROFILE}" || shell_test::fail "line ${LINENO}: 'remove' failed"
 
+  # Check the application envelope no longer exists.
+  ./application match "${APPLICATION}" "${PROFILE}" && "line ${LINENO}: 'match' did not fail when it should"
+
   shell_test::pass
 }
 
diff --git a/services/mgmt/binary/binaryd/main.go b/services/mgmt/binary/binaryd/main.go
index da36a77..cfa6001 100644
--- a/services/mgmt/binary/binaryd/main.go
+++ b/services/mgmt/binary/binaryd/main.go
@@ -87,7 +87,7 @@
 		vlog.Errorf("Serve(%v) failed: %v", *name, err)
 		return
 	}
-	vlog.Infof("Binary repository published at %v/%v", endpoint, *name)
+	vlog.Infof("Binary repository running at endpoint=%q", endpoint)
 	// Wait until shutdown.
 	<-signals.ShutdownOnSignals()
 }
diff --git a/services/mgmt/binary/binaryd/test.sh b/services/mgmt/binary/binaryd/test.sh
new file mode 100755
index 0000000..ec54850
--- /dev/null
+++ b/services/mgmt/binary/binaryd/test.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Test the binary repository daemon.
+#
+# This test starts a binary repository daemon and uses the binary
+# repository client to verify that <binary>.Upload(),
+# <binary>.Download(), and <binary>.Delete() work as expected.
+
+source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
+
+build() {
+  local -r GO="${REPO_ROOT}/scripts/build/go"
+  "${GO}" build veyron/services/mgmt/binary/binaryd || shell_test::fail "line ${LINENO}: failed to build 'binaryd'"
+  "${GO}" build veyron/tools/binary || shell_test::fail "line ${LINENO}: failed to build 'binary'"
+}
+
+main() {
+  cd "${TMPDIR}"
+  build
+
+  shell_test::setup_server_test
+
+  # Start the binary repository daemon.
+  local -r REPO="binaryd-test-repo"
+  shell_test::start_server ./binaryd --name="${REPO}" --address=127.0.0.1:0
+
+  # Create a binary file.
+  local -r BINARY="${REPO}/test-binary"
+  local -r BINARY_FILE=$(shell::tmp_file)
+  dd if=/dev/urandom of="${BINARY_FILE}" bs=1024*1024 count=16
+  ./binary upload "${BINARY}" "${BINARY_FILE}" || shell_test::fail "line ${LINENO}: 'upload' failed"
+
+  # Download the binary file.
+  local -r BINARY_FILE2=$(shell::tmp_file)
+  ./binary download "${BINARY}" "${BINARY_FILE2}" || shell_test::fail "line ${LINENO}: 'download' failed"
+  if [[ $(cmp "${BINARY_FILE}" "${BINARY_FILE2}" &> /dev/null) ]]; then
+    shell_test::fail "mismatching binary files"
+  fi
+
+  # Remove the binary file.
+  ./binary delete "${BINARY}" || shell_test::fail "line ${LINENO}: 'delete' failed"
+
+  # Check the binary no longer exists.
+  ./binary download "${BINARY}" "${BINARY_FILE2}" && "line ${LINENO}: 'download' did not fail when it should"
+
+  shell_test::pass
+}
+
+main "$@"
diff --git a/services/mgmt/build/buildd/main.go b/services/mgmt/build/buildd/main.go
index dec12a2..7f7aa55 100644
--- a/services/mgmt/build/buildd/main.go
+++ b/services/mgmt/build/buildd/main.go
@@ -42,7 +42,7 @@
 		vlog.Errorf("Serve(%v) failed: %v", *name, err)
 		return
 	}
-	vlog.Infof("Build server endpoint=%q name=%q", endpoint, *name)
+	vlog.Infof("Build server running at endpoint=%q", endpoint)
 
 	// Wait until shutdown.
 	<-signals.ShutdownOnSignals()
diff --git a/services/mgmt/build/buildd/test.sh b/services/mgmt/build/buildd/test.sh
new file mode 100755
index 0000000..ec0a441
--- /dev/null
+++ b/services/mgmt/build/buildd/test.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# Test the build server daemon.
+#
+# This test starts a build server daemon and uses the build client to
+# verify that <build>.Build() works as expected.
+
+source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
+
+build() {
+  local -r GO="${REPO_ROOT}/scripts/build/go"
+  "${GO}" build veyron/services/mgmt/build/buildd || shell_test::fail "line ${LINENO}: failed to build 'buildd'"
+  "${GO}" build veyron/tools/build || shell_test::fail "line ${LINENO}: failed to build 'build'"
+}
+
+main() {
+  cd "${TMPDIR}"
+  build
+
+  shell_test::setup_server_test
+
+  # Start the binary repository daemon.
+  local -r SERVER="buildd-test-server"
+  shell_test::start_server ./buildd --name="${SERVER}" --gobin="${VEYRON_ROOT}/environment/go/bin/go" --address=127.0.0.1:0
+
+  # Create and build a test source file.
+  local -r ROOT=$(shell::tmp_dir)
+  local -r BIN_DIR="${ROOT}/bin"
+  mkdir -p "${BIN_DIR}"
+  local -r SRC_DIR="${ROOT}/src/test"
+  mkdir -p "${SRC_DIR}"
+  local -r SRC_FILE="${SRC_DIR}/test.go"
+  cat > "${SRC_FILE}" <<EOF
+package main
+
+import "fmt"
+
+func main() {
+  fmt.Printf("Hello World!\n")
+}
+EOF
+  GOPATH="${ROOT}" TMPDIR="${BIN_DIR}" ./build build "${SERVER}" "test" || shell_test::fail "line ${LINENO}: 'build' failed"
+  if [[ ! -e "${BIN_DIR}/test" ]]; then
+    shell_test::fail "test binary not found"
+  fi
+  local -r GOT=$("${BIN_DIR}/test")
+  local -r WANT="Hello World!"
+  if [[ "${GOT}" != "${WANT}" ]]; then
+    shell_test::fail "unexpected result: want '${WANT}', got '${GOT}'"
+  fi
+
+  shell_test::pass
+}
+
+main "$@"
diff --git a/services/mgmt/profile/impl/invoker.go b/services/mgmt/profile/impl/invoker.go
index f077cf1..720b418 100644
--- a/services/mgmt/profile/impl/invoker.go
+++ b/services/mgmt/profile/impl/invoker.go
@@ -4,6 +4,7 @@
 	"errors"
 
 	"veyron/services/mgmt/profile"
+	_ "veyron/services/store/typeregistryhack"
 
 	"veyron2/ipc"
 	"veyron2/naming"
@@ -43,9 +44,13 @@
 	for i := 0; i < len(pathComponents); i++ {
 		name := pathComponents[:i].String()
 		object := tx.Bind(name)
-		if _, err := object.Get(context); err != nil {
-			if _, err := object.Put(context, &dir{}); err != nil {
-				return errOperationFailed
+		if exists, err := object.Exists(context); err != nil {
+			return errOperationFailed
+		} else if !exists {
+			if _, err := object.Get(context); err != nil {
+				if _, err := object.Put(context, &dir{}); err != nil {
+					return errOperationFailed
+				}
 			}
 		}
 	}
diff --git a/services/mgmt/profile/profiled/main.go b/services/mgmt/profile/profiled/main.go
index 73f3465..93321d2 100644
--- a/services/mgmt/profile/profiled/main.go
+++ b/services/mgmt/profile/profiled/main.go
@@ -17,8 +17,8 @@
 	protocol = flag.String("protocol", "tcp", "protocol to listen on")
 	address  = flag.String("address", ":0", "address to listen on")
 
-	name      = flag.String("name", "", "name to mount the profile manager as")
-	storeName = flag.String("store", "", "object name of the profile manager store")
+	name      = flag.String("name", "", "name to mount the profile repository as")
+	storeName = flag.String("store", "", "object name of the profile repository store")
 )
 
 func main() {
@@ -45,7 +45,7 @@
 	if err := server.Serve(*name, dispatcher); err != nil {
 		vlog.Fatalf("Serve(%v) failed: %v", *name, err)
 	}
-	vlog.VI(0).Infof("Profile manager published at %v/%v", endpoint, *name)
+	vlog.Infof("Profile repository running at endpoint=%q", endpoint)
 
 	// Wait until shutdown.
 	<-signals.ShutdownOnSignals()
diff --git a/services/mgmt/profile/profiled/test.sh b/services/mgmt/profile/profiled/test.sh
new file mode 100755
index 0000000..72bccce
--- /dev/null
+++ b/services/mgmt/profile/profiled/test.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+# Test the profile repository daemon.
+#
+# This test starts an profile repository daemon and uses the profile
+# repository client to verify that <profile>.Put(), <profile>.Label(),
+# <profile>.Description(), <profile>.Speficiation(), and
+# <profile>.Remove() work as expected.
+
+source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
+
+build() {
+  local -r GO="${REPO_ROOT}/scripts/build/go"
+  "${GO}" build veyron/services/mgmt/profile/profiled || shell_test::fail "line ${LINENO}: failed to build 'profiled'"
+  "${GO}" build veyron/services/store/stored || shell_test::fail "line ${LINENO}: failed to build 'stored'"
+  "${GO}" build veyron/tools/profile || shell_test::fail "line ${LINENO}: failed to build 'profile'"
+}
+
+main() {
+  cd "${TMPDIR}"
+  build
+
+  shell_test::setup_server_test
+
+  # Start the store daemon.
+  local -r DB_DIR=$(shell::tmp_dir)
+  local -r STORE="profile-test-store"
+  shell_test::start_server ./stored --name="${STORE}" --address=127.0.0.1:0 --db="${DB_DIR}"
+
+  # Start the profile repository daemon.
+  local -r REPO="profiled-test-repo"
+  shell_test::start_server ./profiled --name="${REPO}" --address=127.0.0.1:0 --store="${STORE}"
+
+  # Create a profile.
+  local -r PROFILE="${REPO}/test-profile"
+  ./profile put "${PROFILE}" || shell_test::fail "line ${LINENO}: 'put' failed"
+
+  # Retrieve the profile label.
+  local OUTPUT=$(shell::tmp_file)
+  ./profile label "${PROFILE}" | tee "${OUTPUT}" || shell_test::fail "line ${LINENO}: 'label' failed"
+  local GOT=$(cat "${OUTPUT}")
+  local WANT="example"
+  if [[ "${GOT}" != "${WANT}" ]]; then
+    shell_test::fail "unexpected result: want '${WANT}', got '${GOT}'"
+  fi
+
+  # Retrieve the profile description.
+  local OUTPUT=$(shell::tmp_file)
+  ./profile description "${PROFILE}" | tee "${OUTPUT}" || shell_test::fail "line ${LINENO}: 'description' failed"
+  local GOT=$(cat "${OUTPUT}")
+  local WANT="Example profile to test the profile manager implementation."
+  if [[ "${GOT}" != "${WANT}" ]]; then
+    shell_test::fail "unexpected result: want '${WANT}', got '${GOT}'"
+  fi
+
+  # Retrieve the profile specification.
+  local OUTPUT=$(shell::tmp_file)
+  ./profile spec "${PROFILE}" | tee "${OUTPUT}" || shell_test::fail "line ${LINENO}: 'spec' failed"
+  local GOT=$(cat "${OUTPUT}")
+  local WANT='profile.Specification{Arch:"amd64", Description:"Example profile to test the profile manager implementation.", Format:"ELF", Libraries:map[profile.Library]struct {}{profile.Library{Name:"foo", MajorVersion:"1", MinorVersion:"0"}:struct {}{}}, Label:"example", OS:"linux"}'
+  if [[ "${GOT}" != "${WANT}" ]]; then
+    shell_test::fail "unexpected result: want '${WANT}', got '${GOT}'"
+  fi
+
+  # Remove the profile.
+  ./profile remove "${PROFILE}" || shell_test::fail "line ${LINENO}: 'remove' failed"
+
+  # Check the profile no longer exists.
+  ./profile label "${PROFILE}" && "line ${LINENO}: 'label' did not fail when it should"
+  ./profile description "${PROFILE}" && "line ${LINENO}: 'description' did not fail when it should"
+  ./profile spec "${PROFILE}" && "line ${LINENO}: 'spec' did not fail when it should"
+
+  shell_test::pass
+}
+
+main "$@"
diff --git a/tools/application/impl/impl.go b/tools/application/impl/impl.go
index 6182b16..4a804f6 100644
--- a/tools/application/impl/impl.go
+++ b/tools/application/impl/impl.go
@@ -22,11 +22,11 @@
 func getEnvelopeJSON(ctx context.T, app repository.Application, profiles string) ([]byte, error) {
 	env, err := app.Match(ctx, strings.Split(profiles, ","))
 	if err != nil {
-		env = application.Envelope{}
+		return nil, err
 	}
 	j, err := json.MarshalIndent(env, "", "  ")
 	if err != nil {
-		return nil, fmt.Errorf("json: %v", err)
+		return nil, fmt.Errorf("MarshalIndent(%v) failed: %v", env, err)
 	}
 	return j, nil
 }
@@ -34,7 +34,7 @@
 func putEnvelopeJSON(ctx context.T, app repository.Application, profiles string, j []byte) error {
 	var env application.Envelope
 	if err := json.Unmarshal(j, &env); err != nil {
-		return fmt.Errorf("json: %v", err)
+		return fmt.Errorf("Unmarshal(%v) failed: %v", string(j), err)
 	}
 	if err := app.Put(ctx, strings.Split(profiles, ","), env); err != nil {
 		return err
@@ -66,13 +66,14 @@
 	if expected, got := 2, len(args); expected != got {
 		return cmd.Errorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	app, err := repository.BindApplication(args[0])
+	name, profiles := args[0], args[1]
+	app, err := repository.BindApplication(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	j, err := getEnvelopeJSON(ctx, app, args[1])
+	j, err := getEnvelopeJSON(ctx, app, profiles)
 	if err != nil {
 		return err
 	}
@@ -96,17 +97,18 @@
 	if expected, got := 3, len(args); expected != got {
 		return cmd.Errorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	app, err := repository.BindApplication(args[0])
+	name, profiles, envelope := args[0], args[1], args[2]
+	app, err := repository.BindApplication(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
 	}
-	j, err := ioutil.ReadFile(args[2])
+	j, err := ioutil.ReadFile(envelope)
 	if err != nil {
-		return fmt.Errorf("read file %s: %v", args[2], err)
+		return fmt.Errorf("ReadFile(%v): %v", envelope, err)
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	if err = putEnvelopeJSON(ctx, app, args[1], j); err != nil {
+	if err = putEnvelopeJSON(ctx, app, profiles, j); err != nil {
 		return err
 	}
 	fmt.Fprintln(cmd.Stdout(), "Application envelope added successfully.")
@@ -128,13 +130,14 @@
 	if expected, got := 2, len(args); expected != got {
 		return cmd.Errorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	app, err := repository.BindApplication(args[0])
+	name, profile := args[0], args[1]
+	app, err := repository.BindApplication(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	if err = app.Remove(ctx, args[1]); err != nil {
+	if err = app.Remove(ctx, profile); err != nil {
 		return err
 	}
 	fmt.Fprintln(cmd.Stdout(), "Application envelope removed successfully.")
@@ -156,13 +159,14 @@
 	if expected, got := 2, len(args); expected != got {
 		return cmd.Errorf("edit: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	app, err := repository.BindApplication(args[0])
+	name, profile := args[0], args[1]
+	app, err := repository.BindApplication(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
 	}
 	f, err := ioutil.TempFile("", "application-edit-")
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("TempFile() failed: %v", err)
 	}
 	fileName := f.Name()
 	f.Close()
@@ -170,7 +174,7 @@
 
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	envData, err := getEnvelopeJSON(ctx, app, args[1])
+	envData, err := getEnvelopeJSON(ctx, app, profile)
 	if err != nil {
 		return err
 	}
@@ -201,7 +205,7 @@
 			fmt.Fprintln(cmd.Stdout(), "Nothing changed")
 			return nil
 		}
-		if err = putEnvelopeJSON(ctx, app, args[1], newData); err != nil {
+		if err = putEnvelopeJSON(ctx, app, profile, newData); err != nil {
 			fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
 			if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
 				continue
diff --git a/tools/build/impl/impl.go b/tools/build/impl/impl.go
index a584861..f575475 100644
--- a/tools/build/impl/impl.go
+++ b/tools/build/impl/impl.go
@@ -177,8 +177,8 @@
 			errchan <- fmt.Errorf("Advance() failed: %v", err)
 			return
 		}
-		if _, err := stream.Finish(); err != nil {
-			errchan <- fmt.Errorf("Finish() failed: %v", err)
+		if out, err := stream.Finish(); err != nil {
+			errchan <- fmt.Errorf("Finish() failed: (%v, %v)", out, err)
 			return
 		}
 		errchan <- nil
diff --git a/tools/profile/impl/impl.go b/tools/profile/impl/impl.go
index 8ca871e..41d19eb 100644
--- a/tools/profile/impl/impl.go
+++ b/tools/profile/impl/impl.go
@@ -25,9 +25,10 @@
 	if expected, got := 1, len(args); expected != got {
 		return cmd.Errorf("label: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	p, err := repository.BindProfile(args[0])
+	name := args[0]
+	p, err := repository.BindProfile(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindProfile(%v) failed: %v", name, err)
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
@@ -52,9 +53,10 @@
 	if expected, got := 1, len(args); expected != got {
 		return cmd.Errorf("description: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	p, err := repository.BindProfile(args[0])
+	name := args[0]
+	p, err := repository.BindProfile(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindProfile(%v) failed: %v", name, err)
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
@@ -66,8 +68,8 @@
 	return nil
 }
 
-var cmdSpec = &cmdline.Command{
-	Run:      runSpec,
+var cmdSpecification = &cmdline.Command{
+	Run:      runSpecification,
 	Name:     "spec",
 	Short:    "Shows the specification of the profile.",
 	Long:     "Shows the specification of the profile.",
@@ -75,13 +77,14 @@
 	ArgsLong: "<profile> is the full name of the profile.",
 }
 
-func runSpec(cmd *cmdline.Command, args []string) error {
+func runSpecification(cmd *cmdline.Command, args []string) error {
 	if expected, got := 1, len(args); expected != got {
 		return cmd.Errorf("spec: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	p, err := repository.BindProfile(args[0])
+	name := args[0]
+	p, err := repository.BindProfile(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindProfile(%v) failed: %v", name, err)
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
@@ -106,9 +109,10 @@
 	if expected, got := 1, len(args); expected != got {
 		return cmd.Errorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	p, err := repository.BindProfile(args[0])
+	name := args[0]
+	p, err := repository.BindProfile(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindProfile(%v) failed: %v", name, err)
 	}
 
 	// TODO(rthellend): Read an actual specification from a file.
@@ -125,7 +129,7 @@
 	if err := p.Put(ctx, spec); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Specification updated successfully.")
+	fmt.Fprintln(cmd.Stdout(), "Profile added successfully.")
 	return nil
 }
 
@@ -142,9 +146,10 @@
 	if expected, got := 1, len(args); expected != got {
 		return cmd.Errorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	p, err := repository.BindProfile(args[0])
+	name := args[0]
+	p, err := repository.BindProfile(name)
 	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
+		return fmt.Errorf("BindProfile(%v) failed: %v", name, err)
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
@@ -160,6 +165,6 @@
 		Name:     "profile",
 		Short:    "Command-line tool for interacting with the veyron profile repository",
 		Long:     "Command-line tool for interacting with the veyron profile repository",
-		Children: []*cmdline.Command{cmdLabel, cmdDescription, cmdSpec, cmdPut, cmdRemove},
+		Children: []*cmdline.Command{cmdLabel, cmdDescription, cmdSpecification, cmdPut, cmdRemove},
 	}
 }
diff --git a/tools/profile/impl/impl_test.go b/tools/profile/impl/impl_test.go
index 731e0a9..caf9248 100644
--- a/tools/profile/impl/impl_test.go
+++ b/tools/profile/impl/impl_test.go
@@ -153,7 +153,7 @@
 	if err := cmd.Execute([]string{"put", exists}); err != nil {
 		t.Fatalf("%v", err)
 	}
-	if expected, got := "Specification updated successfully.", strings.TrimSpace(stdout.String()); got != expected {
+	if expected, got := "Profile added successfully.", strings.TrimSpace(stdout.String()); got != expected {
 		t.Errorf("Got %q, expected %q", got, expected)
 	}
 	stdout.Reset()