veyron/services/mgmt/node: Add namespace skeleton
This change adds the Globbable to the node manager interfaces and an
incomplete implementation of Glob. The complete implementation will
follow in other changes.
Change-Id: I7265f096c6c897a1170afa73a5e742e92a71e2e8
diff --git a/services/mgmt/debug/dispatcher.go b/services/mgmt/debug/dispatcher.go
index 4497217..7287f0b 100644
--- a/services/mgmt/debug/dispatcher.go
+++ b/services/mgmt/debug/dispatcher.go
@@ -4,16 +4,13 @@
"strings"
"time"
- "veyron.io/veyron/veyron/lib/glob"
+ "veyron.io/veyron/veyron/services/mgmt/lib/toplevelglob"
logreaderimpl "veyron.io/veyron/veyron/services/mgmt/logreader/impl"
pprofimpl "veyron.io/veyron/veyron/services/mgmt/pprof/impl"
statsimpl "veyron.io/veyron/veyron/services/mgmt/stats/impl"
"veyron.io/veyron/veyron2/ipc"
- "veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/services/mounttable/types"
- "veyron.io/veyron/veyron2/verror"
)
// dispatcher holds the state of the debug dispatcher.
@@ -32,7 +29,7 @@
}
if len(suffix) == 0 {
leaves := []string{"logs", "pprof", "stats"}
- return ipc.ReflectInvoker(&topLevelGlobInvoker{d, leaves}), d.auth, nil
+ return toplevelglob.New(d, leaves), d.auth, nil
}
parts := strings.SplitN(suffix, "/", 2)
if len(parts) == 2 {
@@ -53,71 +50,3 @@
}
return nil, d.auth, nil
}
-
-type topLevelGlobInvoker struct {
- d *dispatcher
- leaves []string
-}
-
-func (i *topLevelGlobInvoker) Glob(call ipc.ServerCall, pattern string) error {
- g, err := glob.Parse(pattern)
- if err != nil {
- return err
- }
- for _, leaf := range i.leaves {
- if ok, _, left := g.MatchInitialSegment(leaf); ok {
- if err := i.leafGlob(call, leaf, left.String()); err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-func (i *topLevelGlobInvoker) leafGlob(call ipc.ServerCall, leaf string, pattern string) error {
- invoker, _, err := i.d.Lookup(leaf, "Glob")
- if err != nil {
- return err
- }
- argptrs := []interface{}{&pattern}
- leafCall := &localServerCall{call, leaf}
- results, err := invoker.Invoke("Glob", leafCall, argptrs)
- if err != nil {
- return err
- }
- if len(results) != 1 {
- return verror.BadArgf("unexpected number of results. Got %d, want 1", len(results))
- }
- res := results[0]
- if res == nil {
- return nil
- }
- err, ok := res.(error)
- if !ok {
- return verror.BadArgf("unexpected result type. Got %T, want error", res)
- }
- return err
-}
-
-// An ipc.ServerCall implementation used to Invoke methods on the invokers
-// directly. Everything is the same as the original ServerCall, except the
-// Stream implementation.
-type localServerCall struct {
- ipc.ServerCall
- name string
-}
-
-// Re-Implement ipc.Stream
-func (c *localServerCall) Recv(v interface{}) error {
- panic("Recv not implemented")
- return nil
-}
-
-func (c *localServerCall) Send(v interface{}) error {
- me, ok := v.(types.MountEntry)
- if !ok {
- return verror.BadArgf("unexpected stream type. Got %T, want MountEntry", v)
- }
- me.Name = naming.Join(c.name, me.Name)
- return c.ServerCall.Send(me)
-}
diff --git a/services/mgmt/lib/toplevelglob/invoker.go b/services/mgmt/lib/toplevelglob/invoker.go
new file mode 100644
index 0000000..439d129
--- /dev/null
+++ b/services/mgmt/lib/toplevelglob/invoker.go
@@ -0,0 +1,87 @@
+// Package toplevelglob implements a Glob invoker that recurses into other
+// invokers.
+package toplevelglob
+
+import (
+ "veyron.io/veyron/veyron/lib/glob"
+
+ "veyron.io/veyron/veyron2/ipc"
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
+ "veyron.io/veyron/veyron2/verror"
+)
+
+type invoker struct {
+ d ipc.Dispatcher
+ leaves []string
+}
+
+// New returns a new top-level Glob invoker. The invoker implements a Glob
+// method that will match the given leaves, and recurse into leaf invokers
+// on the given dispatcher.
+func New(d ipc.Dispatcher, leaves []string) ipc.Invoker {
+ return ipc.ReflectInvoker(&invoker{d, leaves})
+}
+
+func (i *invoker) Glob(call ipc.ServerCall, pattern string) error {
+ g, err := glob.Parse(pattern)
+ if err != nil {
+ return err
+ }
+ for _, leaf := range i.leaves {
+ if ok, _, left := g.MatchInitialSegment(leaf); ok {
+ if err := i.leafGlob(call, leaf, left.String()); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (i *invoker) leafGlob(call ipc.ServerCall, leaf string, pattern string) error {
+ invoker, _, err := i.d.Lookup(leaf, "Glob")
+ if err != nil {
+ return err
+ }
+ argptrs := []interface{}{&pattern}
+ leafCall := &localServerCall{call, leaf}
+ results, err := invoker.Invoke("Glob", leafCall, argptrs)
+ if err != nil {
+ return err
+ }
+ if len(results) != 1 {
+ return verror.BadArgf("unexpected number of results. Got %d, want 1", len(results))
+ }
+ res := results[0]
+ if res == nil {
+ return nil
+ }
+ err, ok := res.(error)
+ if !ok {
+ return verror.BadArgf("unexpected result type. Got %T, want error", res)
+ }
+ return err
+}
+
+// An ipc.ServerCall implementation used to Invoke methods on the invokers
+// directly. Everything is the same as the original ServerCall, except the
+// Stream implementation.
+type localServerCall struct {
+ ipc.ServerCall
+ name string
+}
+
+// Re-Implement ipc.Stream
+func (c *localServerCall) Recv(v interface{}) error {
+ panic("Recv not implemented")
+ return nil
+}
+
+func (c *localServerCall) Send(v interface{}) error {
+ me, ok := v.(types.MountEntry)
+ if !ok {
+ return verror.BadArgf("unexpected stream type. Got %T, want MountEntry", v)
+ }
+ me.Name = naming.Join(c.name, me.Name)
+ return c.ServerCall.Send(me)
+}
diff --git a/services/mgmt/node/impl/app_invoker.go b/services/mgmt/node/impl/app_invoker.go
index b7e3a9a..5922123 100644
--- a/services/mgmt/node/impl/app_invoker.go
+++ b/services/mgmt/node/impl/app_invoker.go
@@ -103,9 +103,12 @@
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/appcycle"
"veyron.io/veyron/veyron2/services/mgmt/application"
+ "veyron.io/veyron/veyron2/services/mounttable"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/vlog"
vexec "veyron.io/veyron/veyron/lib/exec"
+ "veyron.io/veyron/veyron/lib/glob"
iconfig "veyron.io/veyron/veyron/services/mgmt/node/config"
)
@@ -725,3 +728,15 @@
}
return updateLink(prevVersionDir, currLink)
}
+
+func (i *appInvoker) Glob(ctx ipc.ServerContext, pattern string, stream mounttable.GlobbableServiceGlobStream) error {
+ // TODO(rthellend): Finish implementing Glob
+ g, err := glob.Parse(pattern)
+ if err != nil {
+ return err
+ }
+ if g.Len() == 0 {
+ return stream.SendStream().Send(types.MountEntry{Name: ""})
+ }
+ return nil
+}
diff --git a/services/mgmt/node/impl/dispatcher.go b/services/mgmt/node/impl/dispatcher.go
index fa6b7eb..c71e71a 100644
--- a/services/mgmt/node/impl/dispatcher.go
+++ b/services/mgmt/node/impl/dispatcher.go
@@ -14,6 +14,7 @@
vsecurity "veyron.io/veyron/veyron/security"
vflag "veyron.io/veyron/veyron/security/flag"
"veyron.io/veyron/veyron/security/serialization"
+ "veyron.io/veyron/veyron/services/mgmt/lib/toplevelglob"
inode "veyron.io/veyron/veyron/services/mgmt/node"
"veyron.io/veyron/veyron/services/mgmt/node/config"
@@ -216,6 +217,9 @@
}
}
if len(components) == 0 {
+ if method == "Glob" {
+ return toplevelglob.New(d, []string{nodeSuffix, appsSuffix}), d.auth, nil
+ }
return nil, nil, errInvalidSuffix
}
// The implementation of the node manager is split up into several
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 879a34a..c0f74d7 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -10,6 +10,8 @@
"os"
goexec "os/exec"
"path/filepath"
+ "reflect"
+ "sort"
"strconv"
"strings"
"syscall"
@@ -32,6 +34,7 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/node"
+ "veyron.io/veyron/veyron2/services/mounttable"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
)
@@ -887,3 +890,49 @@
t.Fatalf("Install should have failed with claimer identity")
}
}
+
+func TestNodeManagerGlob(t *testing.T) {
+ // Set up mount table.
+ defer setupLocalNamespace(t)()
+ root, cleanup := setupRootDir(t)
+ defer cleanup()
+
+ // Set up the node manager. Since we won't do node manager updates,
+ // don't worry about its application envelope and current link.
+ nm := blackbox.HelperCommand(t, "nodeManager", "nm", root, "unused app repo name", "unused curr link")
+ defer setupChildCommand(nm)()
+ if err := nm.Cmd.Start(); err != nil {
+ t.Fatalf("Start() failed: %v", err)
+ }
+ defer nm.Cleanup()
+ readPID(t, nm)
+
+ c, err := mounttable.BindGlobbable("nm")
+ if err != nil {
+ t.Fatalf("BindGlobbable failed: %v", err)
+ }
+
+ stream, err := c.Glob(rt.R().NewContext(), "...")
+ if err != nil {
+ t.Errorf("Glob failed: %v", err)
+ }
+ results := []string{}
+ iterator := stream.RecvStream()
+ for iterator.Advance() {
+ results = append(results, iterator.Value().Name)
+ }
+ sort.Strings(results)
+ expected := []string{
+ "apps",
+ "nm",
+ }
+ if !reflect.DeepEqual(results, expected) {
+ t.Errorf("unexpected result. Got %v, want %v", results, expected)
+ }
+ if err := iterator.Err(); err != nil {
+ t.Errorf("unexpected stream error: %v", err)
+ }
+ if err := stream.Finish(); err != nil {
+ t.Errorf("Finish failed: %v", err)
+ }
+}
diff --git a/services/mgmt/node/impl/node_invoker.go b/services/mgmt/node/impl/node_invoker.go
index 9db7b75..2f3ea88 100644
--- a/services/mgmt/node/impl/node_invoker.go
+++ b/services/mgmt/node/impl/node_invoker.go
@@ -43,9 +43,12 @@
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/node"
+ "veyron.io/veyron/veyron2/services/mounttable"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/vlog"
vexec "veyron.io/veyron/veyron/lib/exec"
+ "veyron.io/veyron/veyron/lib/glob"
iconfig "veyron.io/veyron/veyron/services/mgmt/node/config"
"veyron.io/veyron/veyron/services/mgmt/profile"
)
@@ -375,3 +378,15 @@
func (i *nodeInvoker) GetACL(_ ipc.ServerContext) (acl security.ACL, etag string, err error) {
return i.disp.getACL()
}
+
+func (i *nodeInvoker) Glob(ctx ipc.ServerContext, pattern string, stream mounttable.GlobbableServiceGlobStream) error {
+ // TODO(rthellend): Finish implementing Glob
+ g, err := glob.Parse(pattern)
+ if err != nil {
+ return err
+ }
+ if g.Len() == 0 {
+ return stream.SendStream().Send(types.MountEntry{Name: ""})
+ }
+ return nil
+}