runtimes/google/naming/namespace: Support blessing pattern for the root mount table.
Change-Id: I6c291975c3299a59db40475f999b822b374dc5fb
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
index 7d5d7f8..306498f 100644
--- a/lib/modules/core/mounttable.go
+++ b/lib/modules/core/mounttable.go
@@ -108,7 +108,7 @@
return nil
}
-type resolver func(ctx context.T, name string) (names []string, err error)
+type resolver func(ctx context.T, name string, opts ...naming.ResolveOpt) (names []string, err error)
func resolve(fn resolver, stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
if err := checkArgs(args[1:], 1, "<name>"); err != nil {
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 1b72b7a..34a3492 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -303,13 +303,13 @@
// tryCall makes a single attempt at a call, against possibly multiple servers.
func (c *client) tryCall(ctx context.T, name, method string, args []interface{}, opts []ipc.CallOpt) (ipc.Call, verror.E) {
ctx, _ = vtrace.WithNewSpan(ctx, fmt.Sprintf("<client>\"%s\".%s", name, method))
- _, serverPattern, name := splitObjectName(name)
+ mtPattern, serverPattern, name := splitObjectName(name)
// Resolve name unless told not to.
var servers []string
if getNoResolveOpt(opts) {
servers = []string{name}
} else {
- if resolved, err := c.ns.Resolve(ctx, name); err != nil {
+ if resolved, err := c.ns.Resolve(ctx, name, naming.RootBlessingPatternOpt(mtPattern)); err != nil {
return nil, verror.NoExistf("ipc: Resolve(%q) failed: %v", name, err)
} else {
// An empty set of protocols means all protocols...
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index 4192a68..163bcc9 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -8,6 +8,7 @@
"time"
"veyron.io/veyron/veyron2"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
@@ -20,6 +21,7 @@
"veyron.io/veyron/veyron/lib/glob"
"veyron.io/veyron/veyron/lib/testutil"
_ "veyron.io/veyron/veyron/profiles"
+ "veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
"veyron.io/veyron/veyron/runtimes/google/naming/namespace"
service "veyron.io/veyron/veyron/services/mounttable/lib"
)
@@ -138,20 +140,28 @@
}
}
-func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
- servers, err := ns.ResolveToMountTable(r.NewContext(), name)
+func doResolveTest(t *testing.T, fname string, f func(context.T, string, ...naming.ResolveOpt) ([]string, error), ctx context.T, name string, want []string, opts ...naming.ResolveOpt) {
+ servers, err := f(ctx, name, opts...)
if err != nil {
- boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
+ boom(t, "Failed to %s %s: %s", fname, name, err)
}
- compare(t, "ResolveToMountTable", name, servers, want)
+ compare(t, fname, name, servers, want)
+}
+
+func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
+ doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, r.NewContext(), name, want)
+}
+
+func testResolveToMountTableWithPattern(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
+ doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, r.NewContext(), name, want, pattern)
}
func testResolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
- servers, err := ns.Resolve(r.NewContext(), name)
- if err != nil {
- boom(t, "Failed to Resolve %q: %s", name, err)
- }
- compare(t, "Resolve", name, servers, want)
+ doResolveTest(t, "Resolve", ns.Resolve, r.NewContext(), name, want)
+}
+
+func testResolveWithPattern(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
+ doResolveTest(t, "Resolve", ns.Resolve, r.NewContext(), name, want, pattern)
}
func testUnresolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
@@ -610,3 +620,56 @@
t.Errorf("namespace.New should have failed with an unrooted name")
}
}
+
+func bless(blesser, delegate security.Principal, extension string) {
+ b, err := blesser.Bless(delegate.PublicKey(), blesser.BlessingStore().Default(), extension, security.UnconstrainedUse())
+ if err != nil {
+ panic(err)
+ }
+ delegate.BlessingStore().SetDefault(b)
+}
+
+func TestRootBlessing(t *testing.T) {
+ // We need the default runtime for the server-side mounttable code
+ // which references rt.R() to create new endpoints
+ cr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+
+ proot := sectest.NewPrincipal("root")
+ bless(proot, r.Principal(), "server")
+ bless(proot, cr.Principal(), "client")
+
+ cr.Principal().AddToRoots(proot.BlessingStore().Default())
+ r.Principal().AddToRoots(proot.BlessingStore().Default())
+
+ root, mts, _, stopper := createNamespace(t, r)
+ defer stopper()
+ ns := r.Namespace()
+
+ name := naming.Join(root.name, mt2MP)
+ // First check with a non-matching blessing pattern.
+ _, err := ns.Resolve(r.NewContext(), name, naming.RootBlessingPatternOpt("root/foobar"))
+ if !verror.Is(err, verror.NoAccess.ID) {
+ t.Errorf("Resolve expected NoAccess error, got %v", err)
+ }
+ _, err = ns.ResolveToMountTable(r.NewContext(), name, naming.RootBlessingPatternOpt("root/foobar"))
+ if !verror.Is(err, verror.NoAccess.ID) {
+ t.Errorf("ResolveToMountTable expected NoAccess error, got %v", err)
+ }
+
+ // Now check a matching pattern.
+ testResolveWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/server"), mts[mt2MP].name)
+ testResolveToMountTableWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/server"), name)
+
+ // After successful lookup it should be cached, so the pattern doesn't matter.
+ testResolveWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/foobar"), mts[mt2MP].name)
+
+ // Test calling a method.
+ jokeName := naming.Join(root.name, mt4MP, j1MP)
+ runServer(t, r, &dispatcher{}, naming.Join(mts["mt4"].name, j1MP))
+ _, err = r.Client().StartCall(r.NewContext(), "[root/foobar]"+jokeName, "KnockKnock", nil)
+ if err == nil {
+ t.Errorf("StartCall expected NoAccess error, got %v", err)
+ }
+ knockKnock(t, r, "[root/server]"+jokeName)
+}
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index eb69a3c..8ac7773 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -2,6 +2,7 @@
import (
"errors"
+ "fmt"
"runtime"
"veyron.io/veyron/veyron2/context"
@@ -12,11 +13,17 @@
"veyron.io/veyron/veyron2/vlog"
)
-func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry) (*naming.MountEntry, error) {
+func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry, pattern string) (*naming.MountEntry, error) {
// Try each server till one answers.
finalErr := errors.New("no servers to resolve query")
for _, s := range e.Servers {
+ var pattern_and_name string
name := naming.JoinAddressName(s.Server, e.Name)
+ if pattern != "" {
+ pattern_and_name = naming.JoinAddressName(s.Server, fmt.Sprintf("[%s]%s", pattern, e.Name))
+ } else {
+ pattern_and_name = name
+ }
// First check the cache.
if ne, err := ns.resolutionCache.lookup(name); err == nil {
vlog.VI(2).Infof("resolveAMT %s from cache -> %v", name, convertServersToStrings(ne.Servers, ne.Name))
@@ -24,7 +31,7 @@
}
// Not in cache, call the real server.
callCtx, _ := ctx.WithTimeout(callTimeout)
- call, err := client.StartCall(callCtx, name, "ResolveStepX", nil, options.NoResolve(true))
+ call, err := client.StartCall(callCtx, pattern_and_name, "ResolveStepX", nil, options.NoResolve(true))
if err != nil {
finalErr = err
vlog.VI(2).Infof("ResolveStep.StartCall %s failed: %s", name, err)
@@ -61,7 +68,7 @@
}
// ResolveX implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
e, _ := ns.rootMountEntry(name)
if vlog.V(2) {
@@ -72,6 +79,7 @@
if len(e.Servers) == 0 {
return nil, verror.Make(naming.ErrNoSuchName, ctx, name)
}
+ pattern := getRootPattern(opts)
// Iterate walking through mount table servers.
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("ResolveX(%s) loop %v", name, *e)
@@ -81,7 +89,7 @@
}
var err error
curr := e
- if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr); err != nil {
+ if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr, pattern); err != nil {
// If the name could not be found in the mount table, return an error.
if verror.Is(err, naming.ErrNoSuchNameRoot.ID) {
err = verror.Make(naming.ErrNoSuchName, ctx, name)
@@ -90,20 +98,26 @@
vlog.VI(1).Infof("ResolveX(%s) -> (NoSuchName: %v)", name, curr)
return nil, err
}
+ if verror.Is(err, verror.NoAccess.ID) {
+ vlog.VI(1).Infof("ResolveX(%s) -> (NoAccess: %v)", name, curr)
+ return nil, err
+
+ }
// Any other failure (server not found, no ResolveStep
// method, etc.) are a sign that iterative resolution can
// stop.
vlog.VI(1).Infof("ResolveX(%s) -> %v", name, curr)
return curr, nil
}
+ pattern = ""
}
return nil, verror.Make(naming.ErrResolutionDepthExceeded, ctx)
}
// Resolve implements veyron2/naming.Namespace.
-func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) Resolve(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
- e, err := ns.ResolveX(ctx, name)
+ e, err := ns.ResolveX(ctx, name, opts...)
if err != nil {
return nil, err
}
@@ -111,7 +125,7 @@
}
// ResolveToMountTableX implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
e, _ := ns.rootMountEntry(name)
if vlog.V(2) {
@@ -122,6 +136,7 @@
if len(e.Servers) == 0 {
return nil, verror.Make(naming.ErrNoMountTable, ctx)
}
+ pattern := getRootPattern(opts)
last := e
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("ResolveToMountTableX(%s) loop %v", name, e)
@@ -132,7 +147,7 @@
vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
return last, nil
}
- if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e); err != nil {
+ if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e, pattern); err != nil {
if verror.Is(err, naming.ErrNoSuchNameRoot.ID) {
vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchRoot: %v)", name, last, curr)
return last, nil
@@ -157,14 +172,15 @@
return nil, err
}
last = curr
+ pattern = ""
}
return nil, verror.Make(naming.ErrResolutionDepthExceeded, ctx)
}
// ResolveToMountTable implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
- e, err := ns.ResolveToMountTableX(ctx, name)
+ e, err := ns.ResolveToMountTableX(ctx, name, opts...)
if err != nil {
return nil, err
}
@@ -254,3 +270,12 @@
}
return flushed
}
+
+func getRootPattern(opts []naming.ResolveOpt) string {
+ for _, opt := range opts {
+ if pattern, ok := opt.(naming.RootBlessingPatternOpt); ok {
+ return string(pattern)
+ }
+ }
+ return ""
+}
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index bff47f6..f6b0ba0 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -57,7 +57,7 @@
return nil
}
-func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) Resolve(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
if address, _ := naming.SplitAddressName(name); len(address) > 0 {
return []string{name}, nil
@@ -77,7 +77,7 @@
return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
}
-func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
e := new(naming.MountEntry)
if address, _ := naming.SplitAddressName(name); len(address) > 0 {
@@ -98,14 +98,14 @@
return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
}
-func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
// TODO(mattr): Implement this method for tests that might need it.
panic("ResolveToMountTable not implemented")
return nil, nil
}
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
// TODO(mattr): Implement this method for tests that might need it.
panic("ResolveToMountTable not implemented")
diff --git a/tools/mgmt/nodex/impl_test.go b/tools/mgmt/nodex/impl_test.go
index 031447d..3eb47c7 100644
--- a/tools/mgmt/nodex/impl_test.go
+++ b/tools/mgmt/nodex/impl_test.go
@@ -107,7 +107,7 @@
r := ir.(GetACLResponse)
return r.acl, r.etag, r.err
}
-
+
type dispatcher struct {
tape *Tape
t *testing.T