veyron/services/mgmt/binary: Add object names to namespace
This changes makes the name of the objects in the repository
discoverable via the namespace.
Change-Id: I0354e6f7935ff5da1bd93dd0dfe9313d2977cc29
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
+}