Merge "config: Make sure all tests occur on random ports."
diff --git a/runtimes/google/ipc/discharges.go b/runtimes/google/ipc/discharges.go
index 84ed29f..40459fd 100644
--- a/runtimes/google/ipc/discharges.go
+++ b/runtimes/google/ipc/discharges.go
@@ -57,7 +57,7 @@
 	discharges := make([]security.Discharge, len(caveats))
 	d.cache.Discharges(caveats, discharges)
 
-	// Fetch discharges for caveats for which no discharges was found
+	// Fetch discharges for caveats for which no discharges were found
 	// in the cache.
 	d.fetchDischarges(d.ctx, caveats, impetus, discharges)
 	for _, d := range discharges {
@@ -91,15 +91,15 @@
 			wg.Add(1)
 			go func(i int, cav security.ThirdPartyCaveat) {
 				defer wg.Done()
-				vlog.VI(3).Infof("Fetching discharge for %T from %v (%+v)", cav, cav.Location(), cav.Requirements())
+				vlog.VI(3).Infof("Fetching discharge for %v", cav)
 				call, err := d.c.StartCall(ctx, cav.Location(), "Discharge", []interface{}{cav, filteredImpetus(cav.Requirements(), impetus)})
 				if err != nil {
-					vlog.VI(3).Infof("Discharge fetch for caveat %T from %v failed: %v", cav, cav.Location(), err)
+					vlog.VI(3).Infof("Discharge fetch for %v failed: %v", cav, err)
 					return
 				}
 				var dAny vdlutil.Any
 				if ierr := call.Finish(&dAny, &err); ierr != nil || err != nil {
-					vlog.VI(3).Infof("Discharge fetch for caveat %T from %v failed: (%v, %v)", cav, cav.Location(), err, ierr)
+					vlog.VI(3).Infof("Discharge fetch for %v failed: (%v, %v)", cav, err, ierr)
 					return
 				}
 				d, ok := dAny.(security.Discharge)
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index b9ab62a..045bd84 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -661,25 +661,35 @@
 	return newCaveat(tpc)
 }
 
-type dischargeImpetusTester struct {
-	LastDischargeImpetus security.DischargeImpetus
-}
-
-// Implements ipc.Dispatcher
-func (s *dischargeImpetusTester) Lookup(_ string) (interface{}, security.Authorizer, error) {
-	return ipc.ReflectInvoker(dischargeImpetusServer{s}), testServerAuthorizer{}, nil
-}
-
+// dischargeImpetusServer implements the discharge service. Always fails to
+// issue a discharge, but records the impetus.
 type dischargeImpetusServer struct {
-	s *dischargeImpetusTester
+	mu      sync.Mutex
+	impetus []security.DischargeImpetus // GUARDED_BY(mu)
 }
 
-// Implements the discharge service: Always fails to issue a discharge, but records the impetus
-func (s dischargeImpetusServer) Discharge(ctx ipc.ServerContext, cav vdlutil.Any, impetus security.DischargeImpetus) (vdlutil.Any, error) {
-	s.s.LastDischargeImpetus = impetus
+func (s *dischargeImpetusServer) Discharge(ctx ipc.ServerContext, cav vdlutil.Any, impetus security.DischargeImpetus) (vdlutil.Any, error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.impetus = append(s.impetus, impetus)
 	return nil, fmt.Errorf("discharges not issued")
 }
 
+// TestAndClearImpetus checks if all the recorded impetuses match want.
+// Returns an error if they do not.
+// Error or no error, it clears the set of recorded impetuses.
+func (s *dischargeImpetusServer) TestAndClearImpetus(want security.DischargeImpetus) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	defer func() { s.impetus = nil }()
+	for idx, imp := range s.impetus {
+		if !reflect.DeepEqual(imp, want) {
+			return fmt.Errorf("impetus %d of %d: Got [%v] want [%v]", idx, len(s.impetus), imp, want)
+		}
+	}
+	return nil
+}
+
 func names2patterns(names []string) []security.BlessingPattern {
 	ret := make([]security.BlessingPattern, len(names))
 	for idx, n := range names {
@@ -723,8 +733,8 @@
 		t.Fatal(err)
 	}
 
-	var tester dischargeImpetusTester
-	if err := server.ServeDispatcher("mountpoint", &tester); err != nil {
+	var tester dischargeImpetusServer
+	if err := server.Serve("mountpoint", &tester, testServerAuthorizer{}); err != nil {
 		t.Fatal(err)
 	}
 
@@ -762,8 +772,8 @@
 			t.Errorf("StartCall(%+v) failed: %v", test.Requirements, err)
 			continue
 		}
-		if got, want := tester.LastDischargeImpetus, test.Impetus; !reflect.DeepEqual(got, want) {
-			t.Errorf("Got [%v] want [%v] for test %+v", got, want, test.Requirements)
+		if err := tester.TestAndClearImpetus(test.Impetus); err != nil {
+			t.Errorf("Test %+v: %v", test.Requirements, err)
 		}
 	}
 }
diff --git a/services/mgmt/binary/impl/fs_utils.go b/services/mgmt/binary/impl/fs_utils.go
index 8cca7b6..431f434 100644
--- a/services/mgmt/binary/impl/fs_utils.go
+++ b/services/mgmt/binary/impl/fs_utils.go
@@ -6,6 +6,7 @@
 	"os"
 	"path/filepath"
 	"strconv"
+	"strings"
 
 	"veyron.io/veyron/veyron2/vlog"
 )
@@ -58,7 +59,13 @@
 		vlog.Errorf("ReadDir(%v) failed: %v", path, err)
 		return []string{}, errOperationFailed
 	}
-	result := make([]string, len(infos))
+	nDirs := 0
+	for _, info := range infos {
+		if info.IsDir() {
+			nDirs++
+		}
+	}
+	result := make([]string, nDirs)
 	for _, info := range infos {
 		if info.IsDir() {
 			partName := info.Name()
@@ -72,9 +79,65 @@
 			}
 			result[idx] = filepath.Join(path, partName)
 		} else {
+			if info.Name() == "name" {
+				continue
+			}
 			// The only entries should correspond to the part dirs.
 			return []string{}, errOperationFailed
 		}
 	}
 	return result, nil
 }
+
+// createObjectNameTree returns a tree of all the valid object names in the
+// repository.
+func (i *binaryService) createObjectNameTree() *treeNode {
+	pattern := i.state.root
+	for d := 0; d < i.state.depth; d++ {
+		pattern = filepath.Join(pattern, "*")
+	}
+	pattern = filepath.Join(pattern, "*", "name")
+	matches, err := filepath.Glob(pattern)
+	if err != nil {
+		return nil
+	}
+	tree := newTreeNode()
+	for _, m := range matches {
+		name, err := ioutil.ReadFile(m)
+		if err != nil {
+			continue
+		}
+		elems := strings.Split(string(name), string(filepath.Separator))
+		tree.find(elems, true)
+	}
+	return tree
+}
+
+type treeNode struct {
+	children map[string]*treeNode
+}
+
+func newTreeNode() *treeNode {
+	return &treeNode{children: make(map[string]*treeNode)}
+}
+
+func (n *treeNode) find(names []string, create bool) *treeNode {
+	for {
+		if len(names) == 0 {
+			return n
+		}
+		if next, ok := n.children[names[0]]; ok {
+			n = next
+			names = names[1:]
+			continue
+		}
+		if create {
+			nn := newTreeNode()
+			n.children[names[0]] = nn
+			n = nn
+			names = names[1:]
+			continue
+		}
+		return nil
+	}
+}
diff --git a/services/mgmt/binary/impl/http_test.go b/services/mgmt/binary/impl/http_test.go
index 5b31d0f..123540e 100644
--- a/services/mgmt/binary/impl/http_test.go
+++ b/services/mgmt/binary/impl/http_test.go
@@ -18,7 +18,7 @@
 	// TODO(caprita): This is based on TestMultiPart (impl_test.go).  Share
 	// the code where possible.
 	for length := 2; length < 5; length++ {
-		binary, url, cleanup := startServer(t, 2)
+		binary, _, url, cleanup := startServer(t, 2)
 		defer cleanup()
 		// Create <length> chunks of up to 4MB of random bytes.
 		data := make([][]byte, length)
diff --git a/services/mgmt/binary/impl/impl_test.go b/services/mgmt/binary/impl/impl_test.go
index a835440..75bb0e0 100644
--- a/services/mgmt/binary/impl/impl_test.go
+++ b/services/mgmt/binary/impl/impl_test.go
@@ -10,6 +10,7 @@
 	"net/http"
 	"os"
 	"path/filepath"
+	"reflect"
 	"testing"
 
 	"veyron.io/veyron/veyron2/naming"
@@ -95,7 +96,7 @@
 }
 
 // startServer starts the binary repository server.
-func startServer(t *testing.T, depth int) (repository.BinaryClientMethods, string, func()) {
+func startServer(t *testing.T, depth int) (repository.BinaryClientMethods, string, string, func()) {
 	// Setup the root of the binary repository.
 	root, err := ioutil.TempDir("", veyronPrefix)
 	if err != nil {
@@ -134,7 +135,7 @@
 	}
 	name := naming.JoinAddressName(endpoint.String(), "test")
 	binary := repository.BinaryClient(name)
-	return binary, fmt.Sprintf("http://%s/test", listener.Addr()), func() {
+	return binary, endpoint.String(), fmt.Sprintf("http://%s/test", listener.Addr()), func() {
 		// Shutdown the binary repository server.
 		if err := server.Stop(); err != nil {
 			t.Fatalf("Stop() failed: %v", err)
@@ -155,7 +156,7 @@
 // hierarchy that stores binary objects in the local file system.
 func TestHierarchy(t *testing.T) {
 	for i := 0; i < md5.Size; i++ {
-		binary, _, cleanup := startServer(t, i)
+		binary, ep, _, cleanup := startServer(t, i)
 		defer cleanup()
 		// Create up to 4MB of random bytes.
 		size := testutil.Rand.Intn(1000 * bufferLength)
@@ -187,6 +188,13 @@
 		if bytes.Compare(output, data) != 0 {
 			t.Fatalf("Unexpected output: expected %v, got %v", data, output)
 		}
+		results, err := testutil.GlobName(naming.JoinAddressName(ep, ""), "...")
+		if err != nil {
+			t.Fatalf("GlobName failed: %v", err)
+		}
+		if expected := []string{"", "test"}; !reflect.DeepEqual(results, expected) {
+			t.Errorf("Unexpected results: expected %q, got %q", expected, results)
+		}
 		if err := binary.Delete(rt.R().NewContext()); err != nil {
 			t.Fatalf("Delete() failed: %v", err)
 		}
@@ -198,7 +206,7 @@
 // consists of.
 func TestMultiPart(t *testing.T) {
 	for length := 2; length < 5; length++ {
-		binary, _, cleanup := startServer(t, 2)
+		binary, _, _, cleanup := startServer(t, 2)
 		defer cleanup()
 		// Create <length> chunks of up to 4MB of random bytes.
 		data := make([][]byte, length)
@@ -248,7 +256,7 @@
 // of.
 func TestResumption(t *testing.T) {
 	for length := 2; length < 5; length++ {
-		binary, _, cleanup := startServer(t, 2)
+		binary, _, _, cleanup := startServer(t, 2)
 		defer cleanup()
 		// Create <length> chunks of up to 4MB of random bytes.
 		data := make([][]byte, length)
@@ -290,7 +298,7 @@
 
 // TestErrors checks that the binary interface correctly reports errors.
 func TestErrors(t *testing.T) {
-	binary, _, cleanup := startServer(t, 2)
+	binary, _, _, cleanup := startServer(t, 2)
 	defer cleanup()
 	const length = 2
 	data := make([][]byte, length)
@@ -351,3 +359,32 @@
 		t.Fatalf("Unexpected error: %v, expected error id %v", err, want)
 	}
 }
+
+func TestGlob(t *testing.T) {
+	_, ep, _, cleanup := startServer(t, 2)
+	defer cleanup()
+	// Create up to 4MB of random bytes.
+	size := testutil.Rand.Intn(1000 * bufferLength)
+	data := testutil.RandomBytes(size)
+
+	objects := []string{"foo", "bar", "hello world", "a/b/c"}
+	for _, obj := range objects {
+		name := naming.JoinAddressName(ep, obj)
+		binary := repository.BinaryClient(name)
+
+		if err := binary.Create(rt.R().NewContext(), 1); err != nil {
+			t.Fatalf("Create() failed: %v", err)
+		}
+		if streamErr, err := invokeUpload(t, binary, data, 0); streamErr != nil || err != nil {
+			t.FailNow()
+		}
+	}
+	results, err := testutil.GlobName(naming.JoinAddressName(ep, ""), "...")
+	if err != nil {
+		t.Fatalf("GlobName failed: %v", err)
+	}
+	expected := []string{"", "a", "a/b", "a/b/c", "bar", "foo", "hello world"}
+	if !reflect.DeepEqual(results, expected) {
+		t.Errorf("Unexpected results: expected %q, got %q", expected, results)
+	}
+}
diff --git a/services/mgmt/binary/impl/service.go b/services/mgmt/binary/impl/service.go
index 039e13f..8a01122 100644
--- a/services/mgmt/binary/impl/service.go
+++ b/services/mgmt/binary/impl/service.go
@@ -5,8 +5,9 @@
 // local filesystem: /<root>/<dir_1>/.../<dir_n>/<hash>. The root and
 // the directory depth are parameters of the implementation. The
 // contents of the directory include the checksum and data for each of
-// the individual parts of the binary:
+// the individual parts of the binary, and the name of the object:
 //
+// name
 // <part_1>/checksum
 // <part_1>/data
 // ...
@@ -25,6 +26,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 	"syscall"
 
 	"veyron.io/veyron/veyron2/ipc"
@@ -89,6 +91,11 @@
 		vlog.Errorf("TempDir(%v, %v) failed: %v", parent, prefix, err)
 		return errOperationFailed
 	}
+	nameFile := filepath.Join(tmpDir, "name")
+	if err := ioutil.WriteFile(nameFile, []byte(i.suffix), os.FileMode(0600)); err != nil {
+		vlog.Errorf("WriteFile(%q) failed: %v", nameFile)
+		return errOperationFailed
+	}
 	for j := 0; j < int(nparts); j++ {
 		partPath, partPerm := generatePartPath(tmpDir, j), os.FileMode(0700)
 		if err := os.MkdirAll(partPath, partPerm); err != nil {
@@ -295,3 +302,21 @@
 	}
 	return nil
 }
+
+func (i *binaryService) VGlobChildren() ([]string, error) {
+	elems := strings.Split(i.suffix, "/")
+	if len(elems) == 1 && elems[0] == "" {
+		elems = nil
+	}
+	n := i.createObjectNameTree().find(elems, false)
+	if n == nil {
+		return nil, errOperationFailed
+	}
+	results := make([]string, len(n.children))
+	index := 0
+	for k, _ := range n.children {
+		results[index] = k
+		index++
+	}
+	return results, nil
+}