veyron2/naming: Add SetACL,GetACL to Namespace ifc.
- type Namespace interface is now in naming/ns.
- Added SetACL/GetACL
- parallelStartCall broken out as common twixt
Glob and GetACL.
MultiPart: 1/3
Change-Id: I26e35052f5e2a7946c9484b569ea39586f2d940f
diff --git a/runtimes/google/ipc/cancel_test.go b/runtimes/google/ipc/cancel_test.go
index 4883bb3..e53500e 100644
--- a/runtimes/google/ipc/cancel_test.go
+++ b/runtimes/google/ipc/cancel_test.go
@@ -10,6 +10,7 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/security"
"v.io/v23/vlog"
)
@@ -22,7 +23,7 @@
type canceld struct {
sm stream.Manager
- ns naming.Namespace
+ ns ns.Namespace
name string
child string
started chan struct{}
@@ -53,7 +54,7 @@
return nil
}
-func makeCanceld(ns naming.Namespace, name, child string) (*canceld, error) {
+func makeCanceld(ns ns.Namespace, name, child string) (*canceld, error) {
sm := manager.InternalNew(naming.FixedRoutingID(0x111111111))
ctx := testContext()
s, err := testInternalNewServer(ctx, sm, ns)
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 9eee69b..958521e 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -16,6 +16,7 @@
"v.io/v23/i18n"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/options"
"v.io/v23/security"
"v.io/v23/vdl"
@@ -96,7 +97,7 @@
type client struct {
streamMgr stream.Manager
- ns naming.Namespace
+ ns ns.Namespace
vcOpts []stream.VCOpt // vc opts passed to dial
preferredProtocols []string
@@ -133,7 +134,7 @@
func (PreferredProtocols) IPCClientOpt() {}
-func InternalNewClient(streamMgr stream.Manager, ns naming.Namespace, opts ...ipc.ClientOpt) (ipc.Client, error) {
+func InternalNewClient(streamMgr stream.Manager, ns ns.Namespace, opts ...ipc.ClientOpt) (ipc.Client, error) {
c := &client{
streamMgr: streamMgr,
ns: ns,
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 13d3b1e..9bab076 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -19,6 +19,7 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/options"
"v.io/v23/security"
"v.io/v23/services/security/access"
@@ -89,7 +90,7 @@
return ctx
}
-func testInternalNewServer(ctx *context.T, streamMgr stream.Manager, ns naming.Namespace, opts ...ipc.ServerOpt) (ipc.Server, error) {
+func testInternalNewServer(ctx *context.T, streamMgr stream.Manager, ns ns.Namespace, opts ...ipc.ServerOpt) (ipc.Server, error) {
client, err := InternalNewClient(streamMgr, ns)
if err != nil {
return nil, err
@@ -207,7 +208,7 @@
return security.MarshalDischarge(d), nil
}
-func startServer(t *testing.T, principal security.Principal, sm stream.Manager, ns naming.Namespace, name string, disp ipc.Dispatcher, opts ...ipc.ServerOpt) (naming.Endpoint, ipc.Server) {
+func startServer(t *testing.T, principal security.Principal, sm stream.Manager, ns ns.Namespace, name string, disp ipc.Dispatcher, opts ...ipc.ServerOpt) (naming.Endpoint, ipc.Server) {
return startServerWS(t, principal, sm, ns, name, disp, noWebsocket, opts...)
}
@@ -220,7 +221,7 @@
return r
}
-func startServerWS(t *testing.T, principal security.Principal, sm stream.Manager, ns naming.Namespace, name string, disp ipc.Dispatcher, shouldUseWebsocket websocketMode, opts ...ipc.ServerOpt) (naming.Endpoint, ipc.Server) {
+func startServerWS(t *testing.T, principal security.Principal, sm stream.Manager, ns ns.Namespace, name string, disp ipc.Dispatcher, shouldUseWebsocket websocketMode, opts ...ipc.ServerOpt) (naming.Endpoint, ipc.Server) {
vlog.VI(1).Info("InternalNewServer")
opts = append(opts, vc.LocalPrincipal{principal})
ctx := testContext()
@@ -258,7 +259,7 @@
return fmt.Sprintf("%s:%d", filepath.Base(file), line)
}
-func verifyMount(t *testing.T, ns naming.Namespace, name string) []string {
+func verifyMount(t *testing.T, ns ns.Namespace, name string) []string {
me, err := ns.Resolve(testContext(), name)
if err != nil {
t.Errorf("%s: %s not found in mounttable", loc(1), name)
@@ -267,14 +268,14 @@
return me.Names()
}
-func verifyMountMissing(t *testing.T, ns naming.Namespace, name string) {
+func verifyMountMissing(t *testing.T, ns ns.Namespace, name string) {
if me, err := ns.Resolve(testContext(), name); err == nil {
names := me.Names()
t.Errorf("%s: %s not supposed to be found in mounttable; got %d servers instead: %v (%+v)", loc(1), name, len(names), names, me)
}
}
-func stopServer(t *testing.T, server ipc.Server, ns naming.Namespace, name string) {
+func stopServer(t *testing.T, server ipc.Server, ns ns.Namespace, name string) {
vlog.VI(1).Info("server.Stop")
new_name := "should_appear_in_mt/server"
verifyMount(t, ns, name)
@@ -304,7 +305,7 @@
// the use of websockets. It does so by resolving the original name
// and choosing the 'ws' endpoint from the set of endpoints returned.
// It must return a name since it'll be passed to StartCall.
-func fakeWSName(ns naming.Namespace, name string) (string, error) {
+func fakeWSName(ns ns.Namespace, name string) (string, error) {
// Find the ws endpoint and use that.
me, err := ns.Resolve(testContext(), name)
if err != nil {
@@ -323,7 +324,7 @@
client ipc.Client
server ipc.Server
ep naming.Endpoint
- ns naming.Namespace
+ ns ns.Namespace
sm stream.Manager
name string
}
diff --git a/runtimes/google/ipc/proxy_test.go b/runtimes/google/ipc/proxy_test.go
index 4ac7f3c..8ca2335 100644
--- a/runtimes/google/ipc/proxy_test.go
+++ b/runtimes/google/ipc/proxy_test.go
@@ -12,6 +12,7 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/options"
"v.io/v23/security"
"v.io/v23/verror"
@@ -65,7 +66,7 @@
}
type proxyHandle struct {
- ns naming.Namespace
+ ns ns.Namespace
sh *modules.Shell
proxy modules.Handle
name string
@@ -310,7 +311,7 @@
}
}
-func verifyMount(t *testing.T, ctx *context.T, ns naming.Namespace, name string) []string {
+func verifyMount(t *testing.T, ctx *context.T, ns ns.Namespace, name string) []string {
me, err := ns.Resolve(ctx, name)
if err != nil {
t.Errorf("%s not found in mounttable", name)
@@ -319,7 +320,7 @@
return me.Names()
}
-func verifyMountMissing(t *testing.T, ctx *context.T, ns naming.Namespace, name string) {
+func verifyMountMissing(t *testing.T, ctx *context.T, ns ns.Namespace, name string) {
if me, err := ns.Resolve(ctx, name); err == nil {
names := me.Names()
t.Errorf("%s not supposed to be found in mounttable; got %d servers instead: %v", name, len(names), names)
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 2d3a414..09b70b3 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -15,6 +15,7 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/options"
"v.io/v23/security"
"v.io/v23/services/security/access"
@@ -94,7 +95,7 @@
// network interfaces through os syscall.
// TODO(jhahn): Add monitoring the network interface changes.
ipNets []*net.IPNet
- ns naming.Namespace
+ ns ns.Namespace
servesMountTable bool
// TODO(cnicolaou): add roaming stats to ipcStats
@@ -164,7 +165,7 @@
func (ReservedNameDispatcher) IPCServerOpt() {}
-func InternalNewServer(ctx *context.T, streamMgr stream.Manager, ns naming.Namespace, client ipc.Client, opts ...ipc.ServerOpt) (ipc.Server, error) {
+func InternalNewServer(ctx *context.T, streamMgr stream.Manager, ns ns.Namespace, client ipc.Client, opts ...ipc.ServerOpt) (ipc.Server, error) {
ctx, cancel := context.WithRootCancel(ctx)
ctx, _ = vtrace.SetNewSpan(ctx, "NewServer")
statsPrefix := naming.Join("ipc", "server", "routing-id", streamMgr.RoutingID().String())
diff --git a/runtimes/google/lib/publisher/publisher.go b/runtimes/google/lib/publisher/publisher.go
index f9599b6..0ba8d2b 100644
--- a/runtimes/google/lib/publisher/publisher.go
+++ b/runtimes/google/lib/publisher/publisher.go
@@ -12,6 +12,7 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/vlog"
)
@@ -79,7 +80,7 @@
type stopCmd struct{} // sent to the runloop when we want it to exit.
// New returns a new publisher that updates mounts on ns every period.
-func New(ctx *context.T, ns naming.Namespace, period time.Duration) Publisher {
+func New(ctx *context.T, ns ns.Namespace, period time.Duration) Publisher {
p := &publisher{
cmdchan: make(chan interface{}),
donechan: make(chan struct{}),
@@ -160,7 +161,7 @@
<-p.donechan
}
-func runLoop(ctx *context.T, cmdchan chan interface{}, donechan chan struct{}, ns naming.Namespace, period time.Duration) {
+func runLoop(ctx *context.T, cmdchan chan interface{}, donechan chan struct{}, ns ns.Namespace, period time.Duration) {
vlog.VI(2).Info("ipc pub: start runLoop")
state := newPubState(ctx, ns, period)
@@ -207,7 +208,7 @@
// it's only used in the sequential publisher runLoop.
type pubState struct {
ctx *context.T
- ns naming.Namespace
+ ns ns.Namespace
period time.Duration
deadline time.Time // deadline for the next sync call
names map[string]bool // names that have been added
@@ -216,7 +217,7 @@
mounts map[mountKey]*ipc.MountStatus
}
-func newPubState(ctx *context.T, ns naming.Namespace, period time.Duration) *pubState {
+func newPubState(ctx *context.T, ns ns.Namespace, period time.Duration) *pubState {
return &pubState{
ctx: ctx,
ns: ns,
diff --git a/runtimes/google/lib/publisher/publisher_test.go b/runtimes/google/lib/publisher/publisher_test.go
index e0c7fbf..ac40b9e 100644
--- a/runtimes/google/lib/publisher/publisher_test.go
+++ b/runtimes/google/lib/publisher/publisher_test.go
@@ -8,7 +8,7 @@
"time"
"v.io/v23/context"
- "v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/vtrace"
"v.io/core/veyron/lib/flags"
@@ -30,7 +30,7 @@
return ctx
}
-func resolve(t *testing.T, ns naming.Namespace, name string) []string {
+func resolve(t *testing.T, ns ns.Namespace, name string) []string {
me, err := ns.Resolve(testContext(), name)
if err != nil {
t.Fatalf("failed to resolve %q", name)
diff --git a/runtimes/google/naming/namespace/acl.go b/runtimes/google/naming/namespace/acl.go
new file mode 100644
index 0000000..448ba0d
--- /dev/null
+++ b/runtimes/google/naming/namespace/acl.go
@@ -0,0 +1,58 @@
+package namespace
+
+import (
+ "v.io/v23"
+ "v.io/v23/context"
+ "v.io/v23/ipc"
+ "v.io/v23/options"
+ "v.io/v23/services/security/access"
+ "v.io/v23/vlog"
+)
+
+// setACLInMountTable sets the ACL in a single server.
+func setACLInMountTable(ctx *context.T, client ipc.Client, name string, acl access.TaggedACLMap, etag, id string) (s status) {
+ s.id = id
+ ctx, _ = context.WithTimeout(ctx, callTimeout)
+ call, err := client.StartCall(ctx, name, "SetACL", []interface{}{acl, etag}, options.NoResolve{})
+ s.err = err
+ if err != nil {
+ return
+ }
+ s.err = call.Finish()
+ return
+}
+
+func (ns *namespace) SetACL(ctx *context.T, name string, acl access.TaggedACLMap, etag string) error {
+ defer vlog.LogCall()()
+ client := v23.GetClient(ctx)
+
+ // Apply to all mount tables implementing the name.
+ f := func(ctx *context.T, mt, id string) status {
+ return setACLInMountTable(ctx, client, mt, acl, etag, id)
+ }
+ err := ns.dispatch(ctx, name, f)
+ vlog.VI(1).Infof("SetACL(%s, %v, %s) -> %v", name, acl, etag, err)
+ return err
+}
+
+// GetACL gets an ACL from a mount table.
+func (ns *namespace) GetACL(ctx *context.T, name string) (acl access.TaggedACLMap, etag string, err error) {
+ defer vlog.LogCall()()
+ client := v23.GetClient(ctx)
+
+ // Resolve to all the mount tables implementing name.
+ me, rerr := ns.ResolveToMountTable(ctx, name)
+ if rerr != nil {
+ err = rerr
+ return
+ }
+ mts := me.Names()
+
+ call, serr := ns.parallelStartCall(ctx, client, mts, "GetACL", []interface{}{})
+ if serr != nil {
+ err = serr
+ return
+ }
+ err = call.Finish(&acl, &etag)
+ return
+}
diff --git a/runtimes/google/naming/namespace/acl_test.go b/runtimes/google/naming/namespace/acl_test.go
new file mode 100644
index 0000000..4502155
--- /dev/null
+++ b/runtimes/google/naming/namespace/acl_test.go
@@ -0,0 +1,138 @@
+package namespace_test
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "v.io/v23"
+ "v.io/v23/context"
+ "v.io/v23/naming"
+ "v.io/v23/security"
+ "v.io/v23/services/security/access"
+
+ "v.io/core/veyron/lib/testutil"
+ tsecurity "v.io/core/veyron/lib/testutil/security"
+ _ "v.io/core/veyron/profiles"
+ service "v.io/core/veyron/services/mounttable/lib"
+)
+
+func init() {
+ testutil.Init()
+}
+
+func initTest() (rootCtx *context.T, aliceCtx *context.T, bobCtx *context.T, shutdown v23.Shutdown) {
+ ctx, shutdown := testutil.InitForTest()
+ var err error
+ if rootCtx, err = v23.SetPrincipal(ctx, tsecurity.NewPrincipal("root")); err != nil {
+ panic("failed to set root principal")
+ }
+ if aliceCtx, err = v23.SetPrincipal(ctx, tsecurity.NewPrincipal("alice")); err != nil {
+ panic("failed to set alice principal")
+ }
+ if bobCtx, err = v23.SetPrincipal(ctx, tsecurity.NewPrincipal("bob")); err != nil {
+ panic("failed to set bob principal")
+ }
+ for _, r := range []*context.T{rootCtx, aliceCtx, bobCtx} {
+ // A hack to set the namespace roots to a value that won't work.
+ v23.GetNamespace(r).SetRoots()
+ // And have all principals recognize each others blessings.
+ p1 := v23.GetPrincipal(r)
+ for _, other := range []*context.T{rootCtx, aliceCtx, bobCtx} {
+ // tsecurity.NewPrincipal has already setup each
+ // principal to use the same blessing for both server
+ // and client activities.
+ if err := p1.AddToRoots(v23.GetPrincipal(other).BlessingStore().Default()); err != nil {
+ panic(err)
+ }
+ }
+ }
+ return rootCtx, aliceCtx, bobCtx, shutdown
+}
+
+// Create a new mounttable service.
+func newMT(t *testing.T, ctx *context.T) (func(), string) {
+ estr, stopFunc, err := service.StartServers(ctx, v23.GetListenSpec(ctx), "", "", "")
+ if err != nil {
+ t.Fatalf("r.NewServer: %s", err)
+ }
+ return stopFunc, estr
+}
+
+func TestACLs(t *testing.T) {
+ // Create three different personalities.
+ // TODO(p): Use the multiple personalities to test ACL functionality.
+ rootCtx, _, _, shutdown := initTest()
+ defer shutdown()
+
+ // Create root mounttable.
+ stop, rmtAddr := newMT(t, rootCtx)
+ fmt.Printf("rmt at %s\n", rmtAddr)
+ defer stop()
+ ns := v23.GetNamespace(rootCtx)
+ ns.SetRoots("/" + rmtAddr)
+
+ // Create two parallel mount tables.
+ stop1, mt1Addr := newMT(t, rootCtx)
+ fmt.Printf("mt1 at %s\n", mt1Addr)
+ defer stop1()
+ stop2, mt2Addr := newMT(t, rootCtx)
+ fmt.Printf("mt2 at %s\n", mt2Addr)
+ defer stop2()
+
+ // Mount them into the root.
+ if err := ns.Mount(rootCtx, "a/b/c", mt1Addr, 0, naming.ServesMountTableOpt(true)); err != nil {
+ t.Fatalf("Failed to Mount %s onto a/b/c: %s", "/"+mt1Addr, err)
+ }
+ if err := ns.Mount(rootCtx, "a/b/c", mt2Addr, 0, naming.ServesMountTableOpt(true)); err != nil {
+ t.Fatalf("Failed to Mount %s onto a/b/c: %s", "/"+mt2Addr, err)
+ }
+
+ // Set/Get the mount point's ACL.
+ acl, etag, err := ns.GetACL(rootCtx, "a/b/c")
+ if err != nil {
+ t.Fatalf("GetACL a/b/c: %s", err)
+ }
+ acl = access.TaggedACLMap{"Read": access.ACL{In: []security.BlessingPattern{security.AllPrincipals}}}
+ if err := ns.SetACL(rootCtx, "a/b/c", acl, etag); err != nil {
+ t.Fatalf("SetACL a/b/c: %s", err)
+ }
+ nacl, _, err := ns.GetACL(rootCtx, "a/b/c")
+ if err != nil {
+ t.Fatalf("GetACL a/b/c: %s", err)
+ }
+ if !reflect.DeepEqual(acl, nacl) {
+ t.Fatalf("want %v, got %v", acl, nacl)
+ }
+
+ // Now Set/Get the parallel mount point's ACL.
+ etag = "" // Parallel setacl with any other value is dangerous
+ acl = access.TaggedACLMap{"Read": access.ACL{In: []security.BlessingPattern{security.AllPrincipals}},
+ "Admin": access.ACL{In: []security.BlessingPattern{security.AllPrincipals}}}
+ if err := ns.SetACL(rootCtx, "a/b/c/d/e", acl, etag); err != nil {
+ t.Fatalf("SetACL a/b/c/d/e: %s", err)
+ }
+ nacl, _, err = ns.GetACL(rootCtx, "a/b/c/d/e")
+ if err != nil {
+ t.Fatalf("GetACL a/b/c/d/e: %s", err)
+ }
+ if !reflect.DeepEqual(acl, nacl) {
+ t.Fatalf("want %v, got %v", acl, nacl)
+ }
+
+ // Get from each server individually to make sure both are set.
+ nacl, _, err = ns.GetACL(rootCtx, naming.Join(mt1Addr, "d/e"))
+ if err != nil {
+ t.Fatalf("GetACL a/b/c/d/e: %s", err)
+ }
+ if !reflect.DeepEqual(acl, nacl) {
+ t.Fatalf("want %v, got %v", acl, nacl)
+ }
+ nacl, _, err = ns.GetACL(rootCtx, naming.Join(mt2Addr, "d/e"))
+ if err != nil {
+ t.Fatalf("GetACL a/b/c/d/e: %s", err)
+ }
+ if !reflect.DeepEqual(acl, nacl) {
+ t.Fatalf("want %v, got %v", acl, nacl)
+ }
+}
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index 0a0d41a..8f135e6 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -12,6 +12,7 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/options"
"v.io/v23/security"
"v.io/v23/verror"
@@ -77,7 +78,7 @@
}
}
-func doGlob(t *testing.T, ctx *context.T, ns naming.Namespace, pattern string, limit int) []string {
+func doGlob(t *testing.T, ctx *context.T, ns ns.Namespace, pattern string, limit int) []string {
var replies []string
rc, err := ns.Glob(ctx, pattern)
if err != nil {
@@ -152,19 +153,19 @@
compare(t, fname, name, me.Names(), want)
}
-func testResolveToMountTable(t *testing.T, ctx *context.T, ns naming.Namespace, name string, want ...string) {
+func testResolveToMountTable(t *testing.T, ctx *context.T, ns ns.Namespace, name string, want ...string) {
doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, ctx, name, want)
}
-func testResolveToMountTableWithPattern(t *testing.T, ctx *context.T, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
+func testResolveToMountTableWithPattern(t *testing.T, ctx *context.T, ns ns.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, ctx, name, want, pattern)
}
-func testResolve(t *testing.T, ctx *context.T, ns naming.Namespace, name string, want ...string) {
+func testResolve(t *testing.T, ctx *context.T, ns ns.Namespace, name string, want ...string) {
doResolveTest(t, "Resolve", ns.Resolve, ctx, name, want)
}
-func testResolveWithPattern(t *testing.T, ctx *context.T, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
+func testResolveWithPattern(t *testing.T, ctx *context.T, ns ns.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
doResolveTest(t, "Resolve", ns.Resolve, ctx, name, want, pattern)
}
diff --git a/runtimes/google/naming/namespace/glob.go b/runtimes/google/naming/namespace/glob.go
index 174d701..54b8bd7 100644
--- a/runtimes/google/naming/namespace/glob.go
+++ b/runtimes/google/naming/namespace/glob.go
@@ -10,7 +10,6 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
- "v.io/v23/options"
"v.io/v23/verror"
"v.io/v23/vlog"
)
@@ -39,51 +38,20 @@
pstr := t.pattern.String()
vlog.VI(2).Infof("globAtServer(%v, %v)", *t.me, pstr)
- // We collect errors trying to connect to servers so that we have something to
- // return if we go through them all and noone answers.
- var lastErr error
+ servers := []string{}
+ for _, s := range t.me.Servers {
+ servers = append(servers, naming.JoinAddressName(s.Server, ""))
+ }
- type tryResult struct {
- index int
- call ipc.Call
- err error
+ // If there are no servers to call, this isn't a mount point. No sense
+ // trying to call servers that aren't there.
+ if len(servers) == 0 {
+ t.error = nil
+ return
}
- var cancels = make([]func(), len(t.me.Servers))
- ch := make(chan tryResult, len(t.me.Servers))
-
- for i, s := range t.me.Servers {
- callCtx, cancel := context.WithTimeout(ctx, callTimeout)
- cancels[i] = cancel
-
- vlog.VI(2).Infof("globAtServer: Trying %d %q", i, s.Server)
- go func(callCtx *context.T, i int, s naming.MountedServer) {
- call, err := client.StartCall(callCtx, naming.JoinAddressName(s.Server, ""), ipc.GlobMethod, []interface{}{pstr}, options.NoResolve{})
- ch <- tryResult{i, call, err}
- }(callCtx, i, s)
- }
- var call ipc.Call
- // Wait for the first successful StartCall.
- for range t.me.Servers {
- result := <-ch
- if result.err != nil {
- lastErr = result.err
- continue
- }
- vlog.VI(2).Infof("globAtServer: Got successful call from %d %q", result.index, t.me.Servers[result.index].Server)
- cancels[result.index] = nil
- call = result.call
- break
- }
- // Cancel all the other StartCalls
- for i, cancel := range cancels {
- if cancel != nil {
- vlog.VI(2).Infof("globAtServer: Canceling call to %d %q", i, t.me.Servers[i].Server)
- cancel()
- }
- }
- if call == nil {
- // No one answered.
- t.error = lastErr
+ call, err := ns.parallelStartCall(ctx, client, servers, ipc.GlobMethod, []interface{}{pstr})
+ if err != nil {
+ t.error = err
return
}
@@ -178,7 +146,6 @@
// We want to output this entry if there was a real error other than
// "not a mount table".
- // TODO(p): return errors on a different reply channel?
//
// An error reply is also a terminated task.
// If no tasks are running, return.
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index dfe7e8b..844baed 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -4,8 +4,6 @@
"fmt"
"time"
- inaming "v.io/core/veyron/runtimes/google/naming"
-
"v.io/v23"
"v.io/v23/context"
"v.io/v23/ipc"
@@ -15,11 +13,6 @@
"v.io/v23/vlog"
)
-type status struct {
- id string
- err error
-}
-
// mountIntoMountTable mounts a single server into a single mount table.
func mountIntoMountTable(ctx *context.T, client ipc.Client, name, server string, patterns []security.BlessingPattern, ttl time.Duration, flags naming.MountFlag, id string) (s status) {
s.id = id
@@ -46,61 +39,6 @@
return
}
-// nameToRID converts a name to a routing ID string. If a routing ID can't be obtained,
-// it just returns the name.
-func nameToRID(name string) string {
- address, _ := naming.SplitAddressName(name)
- if ep, err := inaming.NewEndpoint(address); err == nil {
- return ep.RID.String()
- }
- return name
-}
-
-// collectStati collects n status messages from channel c and returns an error if, for
-// any id, there is no successful reply.
-func collectStati(c chan status, n int) error {
- // Make a map indexed by the routing id (or address if routing id not found) of
- // each mount table. A mount table may be reachable via multiple addresses but
- // each address should have the same routing id. We should only return an error
- // if any of the ids had no successful mounts.
- statusByID := make(map[string]error)
- // Get the status of each request.
- for i := 0; i < n; i++ {
- s := <-c
- if _, ok := statusByID[s.id]; !ok || s.err == nil {
- statusByID[s.id] = s.err
- }
- }
- // Return any error.
- for _, s := range statusByID {
- if s != nil {
- return s
- }
- }
- return nil
-}
-
-// dispatch executes f in parallel for each mount table implementing mTName.
-func (ns *namespace) dispatch(ctx *context.T, mTName string, f func(*context.T, string, string) status, opts ...naming.ResolveOpt) error {
- // Resolve to all the mount tables implementing name.
- me, err := ns.ResolveToMountTable(ctx, mTName, opts...)
- if err != nil {
- return err
- }
- mts := me.Names()
- // Apply f to each of the returned mount tables.
- c := make(chan status, len(mts))
- for _, mt := range mts {
- go func(mt string) {
- c <- f(ctx, mt, nameToRID(mt))
- }(mt)
- }
- finalerr := collectStati(c, len(mts))
- // Forget any previous cached information about these names.
- ns.resolutionCache.forget(mts)
- return finalerr
-}
-
func (ns *namespace) Mount(ctx *context.T, name, server string, ttl time.Duration, opts ...naming.MountOpt) error {
defer vlog.LogCall()()
@@ -150,7 +88,7 @@
defer vlog.LogCall()()
// Unmount the server from all the mount tables.
client := v23.GetClient(ctx)
- f := func(context *context.T, mt, id string) status {
+ f := func(ctx *context.T, mt, id string) status {
return unmountFromMountTable(ctx, client, mt, server, id)
}
err := ns.dispatch(ctx, name, f)
diff --git a/runtimes/google/naming/namespace/parallelstartcall.go b/runtimes/google/naming/namespace/parallelstartcall.go
new file mode 100644
index 0000000..6c1f30f
--- /dev/null
+++ b/runtimes/google/naming/namespace/parallelstartcall.go
@@ -0,0 +1,118 @@
+package namespace
+
+import (
+ inaming "v.io/core/veyron/runtimes/google/naming"
+ "v.io/v23/context"
+ "v.io/v23/ipc"
+ "v.io/v23/naming"
+ "v.io/v23/options"
+ "v.io/v23/verror"
+)
+
+type startStatus struct {
+ index int
+ err error
+ call ipc.Call
+}
+
+func tryStartCall(ctx *context.T, client ipc.Client, target, method string, args []interface{}, c chan startStatus, index int) {
+ call, err := client.StartCall(ctx, target, method, args, options.NoResolve{})
+ c <- startStatus{index: index, err: err, call: call}
+}
+
+// parallelStartCall returns the first succeeding StartCall.
+func (ns *namespace) parallelStartCall(ctx *context.T, client ipc.Client, servers []string, method string, args []interface{}) (ipc.Call, error) {
+ if len(servers) == 0 {
+ return nil, verror.New(verror.ErrNoExist, ctx, "no servers to resolve query")
+ }
+
+ // StartCall to each of the servers.
+ c := make(chan startStatus, len(servers))
+ cancelFuncs := make([]context.CancelFunc, len(servers))
+ for index, server := range servers {
+ callCtx, cancel := context.WithTimeout(ctx, callTimeout)
+ cancelFuncs[index] = cancel
+ go tryStartCall(callCtx, client, server, method, args, c, index)
+ }
+
+ // First positive response wins. Cancel the rest. The cancellation
+ // will prevent any RPCs from starting or progressing. We do not close
+ // the channel since some go routines may still be in flight and want to
+ // write status to it. The channel will be garbage collected when all
+ // references to it disappear.
+ var final startStatus
+ for range servers {
+ final = <-c
+ if final.err == nil {
+ cancelFuncs[final.index] = nil
+ break
+ }
+ }
+ // Cancel the rest.
+ for _, cancel := range cancelFuncs {
+ if cancel != nil {
+ cancel()
+ }
+ }
+ return final.call, final.err
+}
+
+type status struct {
+ id string
+ err error
+}
+
+// nameToRID converts a name to a routing ID string. If a routing ID can't be obtained,
+// it just returns the name.
+func nameToRID(name string) string {
+ address, _ := naming.SplitAddressName(name)
+ if ep, err := inaming.NewEndpoint(address); err == nil {
+ return ep.RID.String()
+ }
+ return name
+}
+
+// collectStati collects n status messages from channel c and returns an error if, for
+// any id, there is no successful reply.
+func collectStati(c chan status, n int) error {
+ // Make a map indexed by the routing id (or address if routing id not found) of
+ // each mount table. A mount table may be reachable via multiple addresses but
+ // each address should have the same routing id. We should only return an error
+ // if any of the ids had no successful mounts.
+ statusByID := make(map[string]error)
+ // Get the status of each request.
+ for i := 0; i < n; i++ {
+ s := <-c
+ if _, ok := statusByID[s.id]; !ok || s.err == nil {
+ statusByID[s.id] = s.err
+ }
+ }
+ // Return any error.
+ for _, s := range statusByID {
+ if s != nil {
+ return s
+ }
+ }
+ return nil
+}
+
+// dispatch executes f in parallel for each mount table implementing mTName.
+func (ns *namespace) dispatch(ctx *context.T, mTName string, f func(*context.T, string, string) status, opts ...naming.ResolveOpt) error {
+ // Resolve to all the mount tables implementing name.
+ me, err := ns.ResolveToMountTable(ctx, mTName, opts...)
+ if err != nil {
+ return err
+ }
+ mts := me.Names()
+ // Apply f to each of the returned mount tables.
+ c := make(chan status, len(mts))
+ for _, mt := range mts {
+ go func(mt string) {
+ c <- f(ctx, mt, nameToRID(mt))
+ }(mt)
+ }
+ finalerr := collectStati(c, len(mts))
+ // Forget any previous cached information about these names.
+ ns.resolutionCache.forget(mts)
+ return finalerr
+}
diff --git a/runtimes/google/rt/runtime.go b/runtimes/google/rt/runtime.go
index 665b68b..d28cf61 100644
--- a/runtimes/google/rt/runtime.go
+++ b/runtimes/google/rt/runtime.go
@@ -14,6 +14,7 @@
"v.io/v23/i18n"
"v.io/v23/ipc"
"v.io/v23/naming"
+ ns "v.io/v23/naming/ns"
"v.io/v23/options"
"v.io/v23/security"
"v.io/v23/verror"
@@ -216,7 +217,7 @@
return nil, fmt.Errorf("failed to create ipc/stream/Manager: %v", err)
}
- ns, _ := ctx.Value(namespaceKey).(naming.Namespace)
+ ns, _ := ctx.Value(namespaceKey).(ns.Namespace)
principal, _ := ctx.Value(principalKey).(security.Principal)
client, _ := ctx.Value(clientKey).(ipc.Client)
@@ -329,7 +330,7 @@
otherOpts := append([]ipc.ClientOpt{}, opts...)
sm, _ := ctx.Value(streamManagerKey).(stream.Manager)
- ns, _ := ctx.Value(namespaceKey).(naming.Namespace)
+ ns, _ := ctx.Value(namespaceKey).(ns.Namespace)
p, _ := ctx.Value(principalKey).(security.Principal)
otherOpts = append(otherOpts, vc.LocalPrincipal{p}, &imanager.DialTimeout{5 * time.Minute})
@@ -353,7 +354,7 @@
return cl
}
-func (r *Runtime) setNewNamespace(ctx *context.T, roots ...string) (*context.T, naming.Namespace, error) {
+func (r *Runtime) setNewNamespace(ctx *context.T, roots ...string) (*context.T, ns.Namespace, error) {
ns, err := namespace.New(roots...)
if oldNS := r.GetNamespace(ctx); oldNS != nil {
@@ -366,7 +367,7 @@
return ctx, ns, err
}
-func (r *Runtime) SetNewNamespace(ctx *context.T, roots ...string) (*context.T, naming.Namespace, error) {
+func (r *Runtime) SetNewNamespace(ctx *context.T, roots ...string) (*context.T, ns.Namespace, error) {
newctx, ns, err := r.setNewNamespace(ctx, roots...)
if err != nil {
return ctx, nil, err
@@ -381,8 +382,8 @@
return newctx, ns, err
}
-func (*Runtime) GetNamespace(ctx *context.T) naming.Namespace {
- ns, _ := ctx.Value(namespaceKey).(naming.Namespace)
+func (*Runtime) GetNamespace(ctx *context.T) ns.Namespace {
+ ns, _ := ctx.Value(namespaceKey).(ns.Namespace)
return ns
}
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index d991fdb..57d86ce 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -8,6 +8,8 @@
"v.io/v23/context"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
+ "v.io/v23/services/security/access"
"v.io/v23/verror"
"v.io/v23/vlog"
@@ -17,7 +19,7 @@
// NewSimpleNamespace returns a simple implementation of a Namespace
// server for use in tests. In particular, it ignores TTLs and not
// allow fully overlapping mount names.
-func NewSimpleNamespace() naming.Namespace {
+func NewSimpleNamespace() ns.Namespace {
ns, err := vnamespace.New()
if err != nil {
panic(err)
@@ -25,11 +27,11 @@
return &namespace{mounts: make(map[string]*naming.MountEntry), ns: ns}
}
-// namespace is a simple partial implementation of naming.Namespace.
+// namespace is a simple partial implementation of ns.Namespace.
type namespace struct {
sync.Mutex
mounts map[string]*naming.MountEntry
- ns naming.Namespace
+ ns ns.Namespace
}
func (ns *namespace) Mount(ctx *context.T, name, server string, _ time.Duration, opts ...naming.MountOpt) error {
@@ -159,3 +161,15 @@
panic("Calling Roots on a mock namespace. This is not supported.")
return nil
}
+
+func (ns *namespace) GetACL(ctx *context.T, name string) (acl access.TaggedACLMap, etag string, err error) {
+ defer vlog.LogCall()()
+ panic("Calling GetACL on a mock namespace. This is not supported.")
+ return nil, "", nil
+}
+
+func (ns *namespace) SetACL(ctx *context.T, name string, acl access.TaggedACLMap, etag string) error {
+ defer vlog.LogCall()()
+ panic("Calling SetACL on a mock namespace. This is not supported.")
+ return nil
+}
diff --git a/runtimes/google/vtrace/vtrace_test.go b/runtimes/google/vtrace/vtrace_test.go
index c06f654..1e174e1 100644
--- a/runtimes/google/vtrace/vtrace_test.go
+++ b/runtimes/google/vtrace/vtrace_test.go
@@ -9,6 +9,7 @@
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/naming"
+ "v.io/v23/naming/ns"
"v.io/v23/security"
"v.io/v23/vlog"
"v.io/v23/vtrace"
@@ -47,7 +48,7 @@
type testServer struct {
sm stream.Manager
- ns naming.Namespace
+ ns ns.Namespace
name string
child string
stop func() error
@@ -83,7 +84,7 @@
return nil
}
-func makeTestServer(ctx *context.T, ns naming.Namespace, name, child string, forceCollect bool) (*testServer, error) {
+func makeTestServer(ctx *context.T, ns ns.Namespace, name, child string, forceCollect bool) (*testServer, error) {
sm := manager.InternalNew(naming.FixedRoutingID(0x111111111))
client, err := iipc.InternalNewClient(sm, ns)
if err != nil {