veyron/runtimes/google/ipc: Add internal Glob
This change adds a built-in Glob method on the root object of every
server. It exposes internal objects, e.g. __debug, such that they are
discoverable in the namespace, but only if the "__" part is specified
explicitly.
Change-Id: Id514901feea4c0da93173b0a1972cd2d708b5c40
diff --git a/runtimes/google/ipc/debug_test.go b/runtimes/google/ipc/debug_test.go
index 887bdec..cddcb94 100644
--- a/runtimes/google/ipc/debug_test.go
+++ b/runtimes/google/ipc/debug_test.go
@@ -51,34 +51,6 @@
t.Errorf("unexpected value: Got %v, want %v", value, want)
}
}
- // Call Glob on __debug
- {
- addr := naming.JoinAddressName(ep.String(), "__debug")
- call, err := client.StartCall(ctx, addr, "Glob", []interface{}{"*"}, veyron2.NoResolveOpt(true))
- if err != nil {
- t.Fatalf("client.StartCall failed: %v", err)
- }
- results := []string{}
- for {
- var me types.MountEntry
- if err := call.Recv(&me); err != nil {
- break
- }
- results = append(results, me.Name)
- }
- if ferr := call.Finish(&err); ferr != nil {
- t.Fatalf("call.Finish failed: %v", ferr)
- }
- sort.Strings(results)
- want := []string{
- "logs",
- "pprof",
- "stats",
- }
- if !reflect.DeepEqual(want, results) {
- t.Errorf("unexpected results. Got %v, want %v", results, want)
- }
- }
// Call Value on __debug/stats/testing/foo
{
foo := stats.NewString("testing/foo")
@@ -99,6 +71,39 @@
t.Errorf("unexpected result: Got %v, want %v", value, want)
}
}
+
+ // Call Glob
+ testcases := []struct {
+ name, pattern string
+ expected []string
+ }{
+ {"", "*", []string{}},
+ {"", "__*", []string{"__debug"}},
+ {"", "__*/*", []string{"__debug/logs", "__debug/pprof", "__debug/stats"}},
+ {"__debug", "*", []string{"logs", "pprof", "stats"}},
+ }
+ for _, tc := range testcases {
+ addr := naming.JoinAddressName(ep.String(), "//"+tc.name)
+ call, err := client.StartCall(ctx, addr, "Glob", []interface{}{tc.pattern})
+ if err != nil {
+ t.Fatalf("client.StartCall failed: %v", err)
+ }
+ results := []string{}
+ for {
+ var me types.MountEntry
+ if err := call.Recv(&me); err != nil {
+ break
+ }
+ results = append(results, me.Name)
+ }
+ if ferr := call.Finish(&err); ferr != nil {
+ t.Fatalf("call.Finish failed: %v", ferr)
+ }
+ sort.Strings(results)
+ if !reflect.DeepEqual(tc.expected, results) {
+ t.Errorf("unexpected results. Got %v, want %v", results, tc.expected)
+ }
+ }
}
type testObject struct {
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index a8ff3b3..d9a0276 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -9,6 +9,7 @@
"sync"
"time"
+ "veyron.io/veyron/veyron/lib/glob"
"veyron.io/veyron/veyron/lib/netstate"
"veyron.io/veyron/veyron/runtimes/google/lib/publisher"
inaming "veyron.io/veyron/veyron/runtimes/google/naming"
@@ -26,6 +27,7 @@
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
+ mttypes "veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron2/vom"
@@ -805,6 +807,9 @@
// and dispatch suffix are also returned.
func (fs *flowServer) lookup(name, method string) (ipc.Invoker, security.Authorizer, string, verror.E) {
name = strings.TrimLeft(name, "/")
+ if method == "Glob" && len(name) == 0 {
+ return ipc.ReflectInvoker(&globInvoker{fs}), &acceptAllAuthorizer{}, name, nil
+ }
disp := fs.disp
if name == ipc.DebugKeyword || strings.HasPrefix(name, ipc.DebugKeyword+"/") {
name = strings.TrimPrefix(name, ipc.DebugKeyword)
@@ -820,7 +825,98 @@
return invoker, auth, name, nil
}
}
- return nil, nil, "", verror.NoExistf(fmt.Sprintf("ipc: dispatcher not found for %q", name))
+ return nil, nil, "", verror.NoExistf("ipc: invoker not found for %q", name)
+}
+
+type acceptAllAuthorizer struct{}
+
+func (acceptAllAuthorizer) Authorize(security.Context) error {
+ return nil
+}
+
+type globInvoker struct {
+ fs *flowServer
+}
+
+// Glob matches the pattern against internal object names if the double-
+// underscore prefix is explicitly part of the pattern. Otherwise, it invokes
+// the service's Glob method.
+func (i *globInvoker) Glob(call ipc.ServerCall, pattern string) error {
+ g, err := glob.Parse(pattern)
+ if err != nil {
+ return err
+ }
+ if strings.HasPrefix(pattern, "__") {
+ var err error
+ // Match against internal object names.
+ internalLeaves := []string{ipc.DebugKeyword}
+ for _, leaf := range internalLeaves {
+ if ok, _, left := g.MatchInitialSegment(leaf); ok {
+ if ierr := i.invokeGlob(call, i.fs.debugDisp, leaf, left.String()); ierr != nil {
+ err = ierr
+ }
+ }
+ }
+ return err
+ }
+ // Invoke the service's method.
+ return i.invokeGlob(call, i.fs.disp, "", pattern)
+}
+
+func (i *globInvoker) invokeGlob(call ipc.ServerCall, d ipc.Dispatcher, prefix, pattern string) error {
+ if d == nil {
+ return nil
+ }
+ invoker, auth, err := d.Lookup("", "Glob")
+ if err != nil {
+ return err
+ }
+ if invoker == nil {
+ return verror.NoExistf("ipc: invoker not found for Glob")
+ }
+
+ argptrs, label, err := invoker.Prepare("Glob", 1)
+ i.fs.label = label
+ if err != nil {
+ return verror.Makef(verror.ErrorID(err), "%s", err)
+ }
+ if err := i.fs.authorize(auth); err != nil {
+ return errNotAuthorized(fmt.Errorf("%q not authorized for method %q: %v", i.fs.RemoteID(), i.fs.Method(), err))
+ }
+ leafCall := &localServerCall{call, prefix}
+ argptrs[0] = &pattern
+ 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 that prepends a prefix to all the names in the streamed
+// MountEntry objects.
+type localServerCall struct {
+ ipc.ServerCall
+ prefix string
+}
+
+func (c *localServerCall) Send(v interface{}) error {
+ me, ok := v.(mttypes.MountEntry)
+ if !ok {
+ return verror.BadArgf("unexpected stream type. Got %T, want MountEntry", v)
+ }
+ me.Name = naming.Join(c.prefix, me.Name)
+ return c.ServerCall.Send(me)
}
func (fs *flowServer) authorize(auth security.Authorizer) error {