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
+}