veyron/runtimes/google/ipc: new Serve method to replace Register+Publish.
Change-Id: Icc69645e753a3e84c5d87a3e9d2d4abd36275fff
diff --git a/runtimes/google/ipc/benchmarks/server.go b/runtimes/google/ipc/benchmarks/server.go
index d585d72..bd5eb0c 100644
--- a/runtimes/google/ipc/benchmarks/server.go
+++ b/runtimes/google/ipc/benchmarks/server.go
@@ -42,13 +42,13 @@
if err != nil {
vlog.Fatalf("NewServer failed: %v", err)
}
- if err := server.Register("", ipc.SoloDispatcher(NewServerBenchmark(&impl{}), sflag.NewAuthorizerOrDie())); err != nil {
- vlog.Fatalf("Register failed: %v", err)
- }
ep, err := server.Listen(protocol, address)
if err != nil {
vlog.Fatalf("Listen failed: %v", err)
}
+ if err := server.Serve("", ipc.SoloDispatcher(NewServerBenchmark(&impl{}), sflag.NewAuthorizerOrDie())); err != nil {
+ vlog.Fatalf("Serve failed: %v", err)
+ }
return naming.JoinAddressName(ep.String(), ""), func() {
if err := server.Stop(); err != nil {
vlog.Fatalf("Stop() failed: %v", err)
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
index e856eb3..7009258 100644
--- a/runtimes/google/ipc/flow_test.go
+++ b/runtimes/google/ipc/flow_test.go
@@ -105,22 +105,16 @@
err error
}
tests := []testcase{
- {"closure", "A", nil, nil, nil},
- {"closure/foo", "B", nil, nil, errors.New("foo")},
-
- {"echo", "A", v{""}, v{`method:"A",suffix:"",arg:""`}, nil},
- {"echo", "B", v{"foo"}, v{`method:"B",suffix:"",arg:"foo"`}, nil},
- {"echo/abc", "C", v{""}, v{`method:"C",suffix:"abc",arg:""`}, nil},
- {"echo/abc", "D", v{"foo"}, v{`method:"D",suffix:"abc",arg:"foo"`}, nil},
+ {"echo", "A", v{""}, v{`method:"A",suffix:"echo",arg:""`}, nil},
+ {"echo", "B", v{"foo"}, v{`method:"B",suffix:"echo",arg:"foo"`}, nil},
+ {"echo/abc", "C", v{""}, v{`method:"C",suffix:"echo/abc",arg:""`}, nil},
+ {"echo/abc", "D", v{"foo"}, v{`method:"D",suffix:"echo/abc",arg:"foo"`}, nil},
}
name := func(t testcase) string {
return fmt.Sprintf("%s.%s%v", t.suffix, t.method, t.args)
}
- disptrie := newDisptrie()
- disptrie.Register("echo", testDisp{newEchoInvoker})
- disptrie.Register("closure", testDisp{newClosureInvoker})
- ipcServer := &server{disptrie: disptrie}
+ ipcServer := &server{disp: testDisp{newEchoInvoker}}
for _, test := range tests {
clientFlow, serverFlow := newTestFlows()
client := newFlowClient(clientFlow)
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 3c68e79..86b5a18 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -202,31 +202,33 @@
return nil, nil
}
-func (ns *namespace) SetRoots([]string) error {
+func (ns *namespace) SetRoots(...string) error {
panic("SetRoots not implemented")
return nil
}
-func startServer(t *testing.T, serverID security.PrivateID, sm stream.Manager, ns naming.Namespace, ts interface{}) ipc.Server {
+func (ns *namespace) Roots() []string {
+ panic("Roots not implemented")
+ return nil
+}
+
+func startServer(t *testing.T, serverID security.PrivateID, sm stream.Manager, ns naming.Namespace, ts interface{}) (naming.Endpoint, ipc.Server) {
vlog.VI(1).Info("InternalNewServer")
server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
if err != nil {
t.Errorf("InternalNewServer failed: %v", err)
}
- vlog.VI(1).Info("server.Register")
- disp := testServerDisp{ts}
- if err := server.Register("server", disp); err != nil {
- t.Errorf("server.Register failed: %v", err)
- }
vlog.VI(1).Info("server.Listen")
- if _, err := server.Listen("tcp", "localhost:0"); err != nil {
+ ep, err := server.Listen("tcp", "localhost:0")
+ if err != nil {
t.Errorf("server.Listen failed: %v", err)
}
- vlog.VI(1).Info("server.Publish")
- if err := server.Publish("mountpoint"); err != nil {
+ vlog.VI(1).Info("server.Serve")
+ disp := testServerDisp{ts}
+ if err := server.Serve("mountpoint/server", disp); err != nil {
t.Errorf("server.Publish failed: %v", err)
}
- return server
+ return ep, server
}
func verifyMount(t *testing.T, ns naming.Namespace, name string) {
@@ -243,28 +245,35 @@
func stopServer(t *testing.T, server ipc.Server, ns naming.Namespace) {
vlog.VI(1).Info("server.Stop")
- verifyMount(t, ns, "mountpoint/server")
+ n1 := "mountpoint/server"
+ n2 := "should_appear_in_mt/server"
+ verifyMount(t, ns, n1)
- // Check that we can still publish.
- server.Publish("should_appear_in_mt")
- verifyMount(t, ns, "should_appear_in_mt/server")
+ // publish a second name
+ if err := server.Serve(n2, nil); err != nil {
+ t.Errorf("server.Serve failed: %v", err)
+ }
+ verifyMount(t, ns, n2)
if err := server.Stop(); err != nil {
t.Errorf("server.Stop failed: %v", err)
}
- // Check that we can no longer publish after Stop.
- server.Publish("should_not_appear_in_mt")
- verifyMountMissing(t, ns, "should_not_appear_in_mt/server")
- verifyMountMissing(t, ns, "mountpoint/server")
- verifyMountMissing(t, ns, "should_appear_in_mt/server")
- verifyMountMissing(t, ns, "should_not_appear_in_mt/server")
+ verifyMountMissing(t, ns, n1)
+ verifyMountMissing(t, ns, n2)
+
+ // Check that we can no longer serve after Stop.
+ err := server.Serve("name doesn't matter", nil)
+ if err == nil || err.Error() != "ipc: server is stopped" {
+ t.Errorf("either no error, or a wrong error was returned: %v", err)
+ }
vlog.VI(1).Info("server.Stop DONE")
}
type bundle struct {
client ipc.Client
server ipc.Server
+ ep naming.Endpoint
ns naming.Namespace
sm stream.Manager
}
@@ -277,7 +286,7 @@
func createBundle(t *testing.T, clientID, serverID security.PrivateID, ts interface{}) (b bundle) {
b.sm = imanager.InternalNew(naming.FixedRoutingID(0x555555555))
b.ns = newNamespace()
- b.server = startServer(t, serverID, b.sm, b.ns, ts)
+ b.ep, b.server = startServer(t, serverID, b.sm, b.ns, ts)
var err error
b.client, err = InternalNewClient(b.sm, b.ns, veyron2.LocalID(clientID))
if err != nil {
@@ -313,6 +322,53 @@
return err == nil || strings.Index(err.Error(), pattern) >= 0
}
+func TestMultipleCallsToServe(t *testing.T) {
+ sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
+ ns := newNamespace()
+ server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
+ if err != nil {
+ t.Errorf("InternalNewServer failed: %v", err)
+ }
+ _, err = server.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Errorf("server.Listen failed: %v", err)
+ }
+
+ disp := &testServerDisp{&testServer{}}
+ if err := server.Serve("mountpoint/server", disp); err != nil {
+ t.Errorf("server.Publish failed: %v", err)
+ }
+
+ n1 := "mountpoint/server"
+ n2 := "should_appear_in_mt/server"
+ n3 := "should_appear_in_mt/server"
+ n4 := "should_not_appear_in_mt/server"
+
+ verifyMount(t, ns, n1)
+
+ if err := server.Serve(n2, disp); err != nil {
+ t.Errorf("server.Serve failed: %v", err)
+ }
+ if err := server.Serve(n3, nil); err != nil {
+ t.Errorf("server.Serve failed: %v", err)
+ }
+ verifyMount(t, ns, n2)
+ verifyMount(t, ns, n3)
+
+ if err := server.Serve(n4, &testServerDisp{&testServer{}}); err == nil {
+ t.Errorf("server.Serve should have failed")
+ }
+ verifyMountMissing(t, ns, n4)
+
+ if err := server.Stop(); err != nil {
+ t.Errorf("server.Stop failed: %v", err)
+ }
+
+ verifyMountMissing(t, ns, n1)
+ verifyMountMissing(t, ns, n2)
+ verifyMountMissing(t, ns, n3)
+}
+
func TestStartCall(t *testing.T) {
authorizeErr := "not authorized because"
nameErr := "does not match the provided pattern"
@@ -355,7 +411,7 @@
ns := newNamespace()
for _, test := range tests {
name := fmt.Sprintf("(clientID:%q serverID:%q)", test.clientID, test.serverID)
- server := startServer(t, test.serverID, mgr, ns, &testServer{})
+ _, server := startServer(t, test.serverID, mgr, ns, &testServer{})
client, err := InternalNewClient(mgr, ns, veyron2.LocalID(test.clientID))
if err != nil {
t.Errorf("%s: Client creation failed: %v", name, err)
@@ -732,15 +788,16 @@
publisher.AddServer("/@2@tcp@localhost:10000@@1000000@2000000@@")
publisher.AddServer("/@2@tcp@localhost:10001@@2000000@3000000@@")
- _, err := b.client.StartCall(&fakeContext{}, "incompatible/server/suffix", "Echo", []interface{}{"foo"})
+ _, err := b.client.StartCall(&fakeContext{}, "incompatible/suffix", "Echo", []interface{}{"foo"})
if !strings.Contains(err.Error(), version.NoCompatibleVersionErr.Error()) {
t.Errorf("Expected error %v, found: %v", version.NoCompatibleVersionErr, err)
}
// Now add a server with a compatible endpoint and try again.
- b.server.Publish("incompatible")
+ publisher.AddServer("/" + b.ep.String())
+ publisher.AddName("incompatible")
- call, err := b.client.StartCall(&fakeContext{}, "incompatible/server/suffix", "Echo", []interface{}{"foo"})
+ call, err := b.client.StartCall(&fakeContext{}, "incompatible/suffix", "Echo", []interface{}{"foo"})
if err != nil {
t.Fatal(err)
}
@@ -785,7 +842,7 @@
server.Stop()
continue
}
- if err := server.Publish("mountpoint"); err != nil {
+ if err := server.Serve("mountpoint", &testServerDisp{}); err != nil {
t.Errorf("server.Publish failed: %v", err)
server.Stop()
continue
@@ -835,7 +892,7 @@
if err != nil {
t.Fatalf("inaming.NewEndpoint(%q): %v", addr, err)
}
- serverName := naming.JoinAddressName(ep.String(), "server/suffix")
+ serverName := naming.JoinAddressName(ep.String(), "suffix")
makeCall := func() (string, error) {
call, err := b.client.StartCall(&fakeContext{}, serverName, "Echo", []interface{}{"bratman"})
if err != nil {
@@ -913,9 +970,6 @@
t.Fatal(err)
}
defer server.Stop()
- if err := server.Register("server", testServerDisp{&testServer{}}); err != nil {
- t.Fatal(err)
- }
name := "mountpoint/server/suffix"
makeCall := func() (string, error) {
@@ -937,7 +991,7 @@
if _, err := server.Listen(inaming.Network, "proxy"); err != nil {
t.Fatal(err)
}
- if err := server.Publish("mountpoint"); err != nil {
+ if err := server.Serve("mountpoint/server", testServerDisp{&testServer{}}); err != nil {
t.Fatal(err)
}
verifyMount(t, ns, name)
@@ -998,7 +1052,7 @@
vlog.Fatalf("InternalNewServer failed: %v", err)
}
disp := testServerDisp{new(testServer)}
- if err := server.Register("server", disp); err != nil {
+ if err := server.Serve("server", disp); err != nil {
vlog.Fatalf("server.Register failed: %v", err)
}
ep, err := server.Listen("tcp", argv[0])
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index fb4406d..f162fdf 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -36,10 +36,10 @@
sync.Mutex
ctx context.T // context used by the server to make internal RPCs.
streamMgr stream.Manager // stream manager to listen for new flows.
- disptrie *disptrie // dispatch trie for method dispatching.
publisher publisher.Publisher // publisher to publish mounttable mounts.
listenerOpts []stream.ListenerOpt // listener opts passed to Listen.
listeners map[stream.Listener]bool // listeners created by Listen.
+ disp ipc.Dispatcher // dispatcher to serve RPCs
active sync.WaitGroup // active goroutines we've spawned.
stopped bool // whether the server has been stopped.
stoppedChan chan struct{} // closed when the server has been stopped.
@@ -53,7 +53,6 @@
s := &server{
ctx: ctx,
streamMgr: streamMgr,
- disptrie: newDisptrie(),
publisher: publisher.New(ctx, ns, publishPeriod),
listeners: make(map[stream.Listener]bool),
stoppedChan: make(chan struct{}),
@@ -73,15 +72,6 @@
return s, nil
}
-func (s *server) Register(prefix string, disp ipc.Dispatcher) error {
- s.Lock()
- defer s.Unlock()
- if s.stopped {
- return errServerStopped
- }
- return s.disptrie.Register(prefix, disp)
-}
-
func (s *server) Published() ([]string, error) {
s.Lock()
defer s.Unlock()
@@ -246,8 +236,21 @@
}
}
-func (s *server) Publish(name string) error {
- s.publisher.AddName(name)
+func (s *server) Serve(name string, disp ipc.Dispatcher) error {
+ s.Lock()
+ defer s.Unlock()
+ if s.stopped {
+ return errServerStopped
+ }
+ if s.disp != nil && disp != nil && s.disp != disp {
+ return fmt.Errorf("attempt to change dispatcher")
+ }
+ if disp != nil {
+ s.disp = disp
+ }
+ if len(name) > 0 {
+ s.publisher.AddName(name)
+ }
return nil
}
@@ -297,39 +300,37 @@
// Wait for the publisher and active listener + flows to finish.
s.active.Wait()
-
- // Once all outstanding requests are done, we can clean up the rest of
- // the state.
- s.disptrie.Stop()
-
+ s.Lock()
+ s.disp = nil
+ s.Unlock()
return firstErr
}
// flowServer implements the RPC server-side protocol for a single RPC, over a
// flow that's already connected to the client.
type flowServer struct {
- disptrie *disptrie // dispatch trie
- server ipc.Server // ipc.Server that this flow server belongs to
- dec *vom.Decoder // to decode requests and args from the client
- enc *vom.Encoder // to encode responses and results to the client
- flow stream.Flow // underlying flow
+ disp ipc.Dispatcher
+ server ipc.Server // ipc.Server that this flow server belongs to
+ dec *vom.Decoder // to decode requests and args from the client
+ enc *vom.Encoder // to encode responses and results to the client
+ flow stream.Flow // underlying flow
// Fields filled in during the server invocation.
// authorizedRemoteID is the PublicID obtained after authorizing the remoteID
// of the underlying flow for the current request context.
- authorizedRemoteID security.PublicID
- blessing security.PublicID
- method, name, suffix string
- label security.Label
- discharges security.CaveatDischargeMap
- deadline time.Time
- endStreamArgs bool // are the stream args at EOF?
+ authorizedRemoteID security.PublicID
+ blessing security.PublicID
+ method, suffix string
+ label security.Label
+ discharges security.CaveatDischargeMap
+ deadline time.Time
+ endStreamArgs bool // are the stream args at EOF?
}
func newFlowServer(flow stream.Flow, server *server) *flowServer {
return &flowServer{
- server: server,
- disptrie: server.disptrie,
+ server: server,
+ disp: server.disp,
// TODO(toddw): Support different codecs
dec: vom.NewDecoder(flow),
enc: vom.NewEncoder(flow),
@@ -449,10 +450,10 @@
// should servers be able to assume that a blessing is something that does not
// have the authorizations that the server's own identity has?
}
+
// Lookup the invoker.
- invoker, auth, name, suffix, verr := fs.lookup(req.Suffix)
- fs.name = name
- fs.suffix = suffix
+ invoker, auth, suffix, verr := fs.lookup(req.Suffix)
+ fs.suffix = suffix // with leading /'s stripped
if verr != nil {
return nil, verr
}
@@ -479,7 +480,6 @@
LocalID: fs.flow.LocalID(),
RemoteID: fs.flow.RemoteID(),
Method: fs.method,
- Name: fs.name,
Suffix: fs.suffix,
Discharges: fs.discharges,
Label: fs.label})); err != nil {
@@ -499,20 +499,18 @@
// name. The name is stripped of any leading slashes, and the invoker is looked
// up in the server's dispatcher. The (stripped) name and dispatch suffix are
// also returned.
-func (fs *flowServer) lookup(name string) (ipc.Invoker, security.Authorizer, string, string, verror.E) {
+func (fs *flowServer) lookup(name string) (ipc.Invoker, security.Authorizer, string, verror.E) {
name = strings.TrimLeft(name, "/")
- disps, suffix := fs.disptrie.Lookup(name)
- for _, disp := range disps {
- invoker, auth, err := disp.Lookup(suffix)
+ if fs.disp != nil {
+ invoker, auth, err := fs.disp.Lookup(name)
switch {
case err != nil:
- return nil, nil, "", "", verror.Convert(err)
+ return nil, nil, "", verror.Convert(err)
case invoker != nil:
- return invoker, auth, name, suffix, nil
+ return invoker, auth, name, nil
}
- // The dispatcher doesn't handle this suffix, try the next one.
}
- return nil, nil, "", "", verror.NotFoundf(fmt.Sprintf("ipc: dispatcher not found for %q", name))
+ return nil, nil, "", verror.NotFoundf(fmt.Sprintf("ipc: dispatcher not found for %q", name))
}
func (fs *flowServer) authorize(auth security.Authorizer) error {
@@ -561,18 +559,23 @@
// Implementations of ipc.ServerContext methods.
-func (fs *flowServer) Server() ipc.Server { return fs.server }
-func (fs *flowServer) Method() string { return fs.method }
-func (fs *flowServer) Name() string { return fs.name }
-func (fs *flowServer) Suffix() string { return fs.suffix }
-func (fs *flowServer) Label() security.Label { return fs.label }
func (fs *flowServer) CaveatDischarges() security.CaveatDischargeMap { return fs.discharges }
-func (fs *flowServer) LocalID() security.PublicID { return fs.flow.LocalID() }
-func (fs *flowServer) RemoteID() security.PublicID { return fs.authorizedRemoteID }
-func (fs *flowServer) Deadline() time.Time { return fs.deadline }
-func (fs *flowServer) Blessing() security.PublicID { return fs.blessing }
-func (fs *flowServer) LocalEndpoint() naming.Endpoint { return fs.flow.LocalEndpoint() }
-func (fs *flowServer) RemoteEndpoint() naming.Endpoint { return fs.flow.RemoteEndpoint() }
+
+func (fs *flowServer) Server() ipc.Server { return fs.server }
+func (fs *flowServer) Method() string { return fs.method }
+
+// TODO(cnicolaou): remove Name from ipc.ServerContext and all of
+// its implementations
+func (fs *flowServer) Name() string { return fs.suffix }
+func (fs *flowServer) Suffix() string { return fs.suffix }
+func (fs *flowServer) Label() security.Label { return fs.label }
+
+func (fs *flowServer) LocalID() security.PublicID { return fs.flow.LocalID() }
+func (fs *flowServer) RemoteID() security.PublicID { return fs.authorizedRemoteID }
+func (fs *flowServer) Deadline() time.Time { return fs.deadline }
+func (fs *flowServer) Blessing() security.PublicID { return fs.blessing }
+func (fs *flowServer) LocalEndpoint() naming.Endpoint { return fs.flow.LocalEndpoint() }
+func (fs *flowServer) RemoteEndpoint() naming.Endpoint { return fs.flow.RemoteEndpoint() }
func (fs *flowServer) IsClosed() bool {
return fs.flow.IsClosed()
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index a628069..4db264c 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -1,7 +1,10 @@
package namespace_test
import (
+ "reflect"
+ "runtime"
"runtime/debug"
+ "sort"
"testing"
"time"
@@ -22,9 +25,19 @@
t.Fatal(string(debug.Stack()))
}
+func compare(t *testing.T, caller, name string, got, want []string) {
+ if len(got) != len(want) {
+ boom(t, "%s: %q returned wrong # servers: got %v, want %v, servers: got %v, want %v", caller, name, len(got), len(want), got, want)
+ }
+ sort.Strings(got)
+ sort.Strings(want)
+ if !reflect.DeepEqual(got, want) {
+ boom(t, "%s: %q: got %v, want %v", caller, name, got, want)
+ }
+}
+
func doGlob(t *testing.T, ctx context.T, ns naming.Namespace, pattern string) []string {
var replies []string
-
rc, err := ns.Glob(ctx, pattern)
if err != nil {
boom(t, "Glob(%s): %s", pattern, err)
@@ -35,27 +48,6 @@
return replies
}
-func checkMatch(t *testing.T, pattern string, expected []string, got []string) {
-L:
- for _, e := range expected {
- for _, g := range got {
- if g == e {
- continue L
- }
- }
- boom(t, "Glob %s expected %v got %v", pattern, expected, got)
- }
-L2:
- for _, g := range got {
- for _, e := range expected {
- if g == e {
- continue L2
- }
- }
- boom(t, "Glob %s expected %v got %v", pattern, expected, got)
- }
-}
-
type testServer struct{}
func (*testServer) KnockKnock(call ipc.ServerCall) string {
@@ -78,8 +70,423 @@
}
}
+func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
+ servers, err := ns.ResolveToMountTable(r.NewContext(), name)
+ if err != nil {
+ boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
+ }
+ compare(t, "ResolveToMoutTable", name, servers, want)
+}
+
+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)
+}
+
+func testUnresolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
+ servers, err := ns.Unresolve(r.NewContext(), name)
+ if err != nil {
+ boom(t, "Failed to Resolve %q: %s", name, err)
+ }
+ compare(t, "Unresolve", name, servers, want)
+}
+
+type serverEntry struct {
+ mountPoint string
+ server ipc.Server
+ endpoint naming.Endpoint
+ name string
+}
+
+func runServer(t *testing.T, sr veyron2.Runtime, disp ipc.Dispatcher, mountPoint string) *serverEntry {
+ return run(t, sr, disp, mountPoint, false)
+}
+
+func runMT(t *testing.T, sr veyron2.Runtime, mountPoint string) *serverEntry {
+ mt, err := service.NewMountTable("")
+ if err != nil {
+ boom(t, "NewMountTable returned error: %v", err)
+ }
+ return run(t, sr, mt, mountPoint, true)
+}
+
+func run(t *testing.T, sr veyron2.Runtime, disp ipc.Dispatcher, mountPoint string, mt bool) *serverEntry {
+ s, err := sr.NewServer(veyron2.ServesMountTableOpt(mt))
+ if err != nil {
+ boom(t, "r.NewServer: %s", err)
+ }
+ // Add a mount table server.
+ // Start serving on a loopback address.
+ ep, err := s.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ boom(t, "Failed to Listen: %s", err)
+ }
+ if err := s.Serve(mountPoint, disp); err != nil {
+ boom(t, "Failed to serve mount table at %s: %s", mountPoint, err)
+ }
+ name := naming.JoinAddressName(ep.String(), "")
+ if !mt {
+ name = name + "//"
+ }
+ return &serverEntry{mountPoint: mountPoint, server: s, endpoint: ep, name: name}
+}
+
+const (
+ mt1MP = "mt1"
+ mt2MP = "mt2"
+ mt3MP = "mt3"
+ mt4MP = "mt4"
+ mt5MP = "mt5"
+ j1MP = "joke1"
+ j2MP = "joke2"
+ j3MP = "joke3"
+
+ ttl = 100 * time.Second
+)
+
+// runMountTables creates a root mountable with some mount tables mounted
+// in it: mt{1,2,3,4,5}
+func runMountTables(t *testing.T, r veyron2.Runtime) (*serverEntry, map[string]*serverEntry) {
+ root := runMT(t, r, "")
+ r.Namespace().SetRoots(root.name)
+ t.Logf("mountTable %q -> %s", root.mountPoint, root.endpoint)
+
+ mps := make(map[string]*serverEntry)
+ for _, mp := range []string{mt1MP, mt2MP, mt3MP, mt4MP, mt5MP} {
+ m := runMT(t, r, mp)
+ t.Logf("mountTable %q -> %s", mp, m.endpoint)
+ mps[mp] = m
+ }
+ return root, mps
+}
+
+// createNamespace creates a hiearachy of mounttables and servers
+// as follows:
+// /mt1, /mt2, /mt3, /mt4, /mt5, /joke1, /joke2, /joke3.
+// That is, mt1 is a mount table mounted in the root mount table,
+// joke1 is a server mounted in the root mount table.
+func createNamespace(t *testing.T, r veyron2.Runtime) (*serverEntry, map[string]*serverEntry, map[string]*serverEntry, func()) {
+ root, mts := runMountTables(t, r)
+ jokes := make(map[string]*serverEntry)
+ // Let's run some non-mount table services.
+ for _, j := range []string{j1MP, j2MP, j3MP} {
+ disp := ipc.SoloDispatcher(&testServer{}, nil)
+ jokes[j] = runServer(t, r, disp, j)
+ }
+ return root, mts, jokes, func() {
+ for _, s := range jokes {
+ s.server.Stop()
+ }
+ for _, s := range mts {
+ s.server.Stop()
+ }
+ root.server.Stop()
+ }
+}
+
+// runNestedMountTables creates some nested mount tables in the hierarchy
+// created by createNamespace as follows:
+// /mt4/foo, /mt4/foo/bar and /mt4/baz where foo, bar and baz are mount tables.
+func runNestedMountTables(t *testing.T, r veyron2.Runtime, mts map[string]*serverEntry) {
+ ns, ctx := r.Namespace(), r.NewContext()
+ // Set up some nested mounts and verify resolution.
+ for _, m := range []string{"mt4/foo", "mt4/foo/bar"} {
+ mts[m] = runMT(t, r, m)
+ }
+
+ // Use a global name for a mount, rather than a relative one.
+ // We directly mount baz into the mt4/foo mount table.
+ globalMP := naming.JoinAddressName(mts["mt4/foo"].name, "baz")
+ mts["baz"] = runMT(t, r, "baz")
+ if err := ns.Mount(ctx, globalMP, mts["baz"].name, ttl); err != nil {
+ boom(t, "Failed to Mount %s: %s", globalMP, err)
+ }
+}
+
+// TestNamespaceCommon tests common use of the Namespace library
+// against a root mount table and some mount tables mounted on it.
+func TestNamespaceCommon(t *testing.T) {
+ // We need the default runtime for the server-side mounttable code
+ // which references rt.R() to create new endpoints
+ rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ root, mts, jokes, stopper := createNamespace(t, r)
+ defer stopper()
+ ns := r.Namespace()
+
+ // All of the initial mounts are served by the root mounttable
+ // and hence ResolveToMountTable should return the root mountable
+ // as the address portion of the terminal name for those mounttables.
+ testResolveToMountTable(t, r, ns, "", root.name)
+ for _, m := range []string{mt2MP, mt3MP, mt5MP} {
+ rootMT := naming.MakeTerminal(naming.Join(root.name, m))
+ // All of these mount tables are hosted by the root mount table
+ testResolveToMountTable(t, r, ns, m, rootMT)
+ testResolveToMountTable(t, r, ns, "//"+m, rootMT)
+
+ // The server registered for each mount point is a mount table
+ testResolve(t, r, ns, m, mts[m].name)
+
+ // ResolveToMountTable will walk through to the sub MountTables
+ mtbar := naming.Join(m, "bar")
+ subMT := naming.MakeTerminal(naming.Join(mts[m].name, "bar"))
+ testResolveToMountTable(t, r, ns, mtbar, subMT)
+
+ // ResolveToMountTable will not walk through if the name is terminal
+ testResolveToMountTable(t, r, ns, "//"+mtbar, naming.Join(rootMT, "bar"))
+ }
+
+ for _, j := range []string{j1MP, j2MP, j3MP} {
+ testResolve(t, r, ns, j, jokes[j].name)
+ }
+}
+
+// TestNamespaceDetails tests more detailed use of the Namespace library,
+// including the intricacies of // meaning and placement.
+func TestNamespaceDetails(t *testing.T) {
+ sr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ root, mts, _, stopper := createNamespace(t, sr)
+ defer stopper()
+
+ ns := r.Namespace()
+ ns.SetRoots(root.name)
+
+ // Mount using a relative name starting with //.
+ // This means don't walk out of the namespace's root mount table
+ // even if there is already something mounted at mt2. Thus, the example
+ // below will fail.
+ mt3Server := mts[mt3MP].name
+ mt2a := "//mt2/a"
+ if err := ns.Mount(r.NewContext(), mt2a, mt3Server, ttl); err != naming.ErrNoSuchName {
+ boom(t, "Successfully mounted %s - expected an err %v, not %v", mt2a, naming.ErrNoSuchName, err)
+ }
+
+ // Mount using the relative name not starting with //.
+ // This means walk through mt2 if it already exists and mount within
+ // the lower level mount table, if the name doesn't exist we'll create
+ // a new name for it.
+ mt2a = "mt2/a"
+ if err := ns.Mount(r.NewContext(), mt2a, mt3Server, ttl); err != nil {
+ boom(t, "Failed to Mount %s: %s", mt2a, err)
+ }
+
+ mt2mt := naming.MakeTerminal(naming.Join(mts[mt2MP].name, "a"))
+ // The mt2/a is served by the mt2 mount table
+ testResolveToMountTable(t, r, ns, mt2a, mt2mt)
+ // The server for mt2a is mt3server from the second mount above.
+ testResolve(t, r, ns, mt2a, mt3Server)
+
+ // Using a terminal or non-terminal name makes no difference if the
+ // mount is directed to the root name server (since that's the root
+ // for the namespace for this process) and the name exists within
+ // that mount table. In both cases, the server will be added to the
+ // set of mount table servers for that name.
+ for _, mp := range []struct{ name, server string }{
+ {"mt2", mts[mt4MP].name},
+ {"//mt2", mts[mt5MP].name},
+ } {
+ if err := ns.Mount(r.NewContext(), mp.name, mp.server, ttl); err != nil {
+ boom(t, "Failed to Mount %s: %s", mp.name, err)
+ }
+ }
+
+ // We now have 3 mount tables prepared to serve mt2/a
+ testResolveToMountTable(t, r, ns, "mt2/a",
+ mts[mt2MP].name+"//a",
+ mts[mt4MP].name+"//a",
+ mts[mt5MP].name+"//a")
+ testResolve(t, r, ns, "mt2", mts[mt2MP].name, mts[mt4MP].name, mts[mt5MP].name)
+}
+
+// TestNestedMounts tests some more deeply nested mounts
+func TestNestedMounts(t *testing.T) {
+ sr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ root, mts, _, stopper := createNamespace(t, sr)
+ runNestedMountTables(t, sr, mts)
+ defer stopper()
+
+ ns := r.Namespace()
+ ns.SetRoots(root.name)
+
+ // Set up some nested mounts and verify resolution.
+ for _, m := range []string{"mt4/foo", "mt4/foo/bar"} {
+ testResolve(t, r, ns, m, mts[m].name)
+ }
+
+ testResolveToMountTable(t, r, ns, "mt4/foo",
+ mts[mt4MP].name+"//foo")
+ testResolveToMountTable(t, r, ns, "mt4/foo/bar",
+ mts["mt4/foo"].name+"//bar")
+ testResolveToMountTable(t, r, ns, "mt4/foo/baz", mts["mt4/foo"].name+"//baz")
+}
+
+// TestServers tests invoking RPCs on simple servers
+func TestServers(t *testing.T) {
+ sr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ root, mts, jokes, stopper := createNamespace(t, sr)
+ defer stopper()
+ ns := r.Namespace()
+ ns.SetRoots(root.name)
+
+ // Let's run some non-mount table servic
+ for _, j := range []string{j1MP, j2MP, j3MP} {
+ testResolve(t, r, ns, j, jokes[j].name)
+ knockKnock(t, r, j)
+ globalName := naming.JoinAddressName(mts["mt4"].name, j)
+ disp := ipc.SoloDispatcher(&testServer{}, nil)
+ gj := "g_" + j
+ jokes[gj] = runServer(t, r, disp, globalName)
+ testResolve(t, r, ns, "mt4/"+j, jokes[gj].name)
+ knockKnock(t, r, "mt4/"+j)
+ testResolveToMountTable(t, r, ns, "mt4/"+j, naming.MakeTerminal(globalName))
+ testResolveToMountTable(t, r, ns, "mt4/"+j+"/garbage", naming.MakeTerminal(globalName+"/garbage"))
+ }
+}
+
+// TestGlob tests some glob patterns.
+func TestGlob(t *testing.T) {
+ sr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ root, mts, _, stopper := createNamespace(t, sr)
+ runNestedMountTables(t, sr, mts)
+ defer stopper()
+ ns := r.Namespace()
+ ns.SetRoots(root.name)
+
+ tln := []string{"baz", "mt1", "mt2", "mt3", "mt4", "mt5", "joke1", "joke2", "joke3"}
+ barbaz := []string{"mt4/foo/bar", "mt4/foo/baz"}
+ foo := append([]string{"mt4/foo"}, barbaz...)
+ // Try various globs.
+ globTests := []struct {
+ pattern string
+ expected []string
+ }{
+ {"*", tln},
+ // TODO(cnicolaou): the glob that doesn't match a name should fail.
+ //
+ {"x", []string{"x"}},
+ {"m*", []string{"mt1", "mt2", "mt3", "mt4", "mt5"}},
+ {"mt[2,3]", []string{"mt2", "mt3"}},
+ {"*z", []string{"baz"}},
+ {"...", append(append(tln, foo...), "")},
+ {"*/...", append(tln, foo...)},
+ {"*/foo/*", barbaz},
+ {"*/*/*z", []string{"mt4/foo/baz"}},
+ {"*/f??/*z", []string{"mt4/foo/baz"}},
+ }
+ for _, test := range globTests {
+ out := doGlob(t, r, ns, test.pattern)
+ compare(t, "Glob", test.pattern, test.expected, out)
+ }
+}
+
+func TestCycles(t *testing.T) {
+ t.Skip() // Remove when the bug is fixed.
+
+ sr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ defer r.Shutdown()
+
+ root, _, _, stopper := createNamespace(t, sr)
+ defer stopper()
+ ns := r.Namespace()
+ ns.SetRoots(root.name)
+
+ c1 := runMT(t, r, "c1")
+ c2 := runMT(t, r, "c2")
+ c3 := runMT(t, r, "c3")
+ defer c1.server.Stop()
+ defer c2.server.Stop()
+ defer c3.server.Stop()
+
+ m := "c1/c2"
+ if err := ns.Mount(r.NewContext(), m, c1.name, ttl); err != nil {
+ boom(t, "Failed to Mount %s: %s", "c1/c2", err)
+ }
+
+ m = "c1/c2/c3"
+ if err := ns.Mount(r.NewContext(), m, c3.name, ttl); err != nil {
+ boom(t, "Failed to Mount %s: %s", m, err)
+ }
+
+ m = "c1/c3/c4"
+ if err := ns.Mount(r.NewContext(), m, c1.name, ttl); err != nil {
+ boom(t, "Failed to Mount %s: %s", m, err)
+ }
+
+ testResolve(t, r, ns, "c1", c1.name)
+ testResolve(t, r, ns, "c1/c2", c1.name)
+ testResolve(t, r, ns, "c1/c3", c3.name)
+ testResolve(t, r, ns, "c1/c3/c4", c1.name)
+ testResolve(t, r, ns, "c1/c3/c4/c3/c4", c1.name)
+ cycle := "c3/c4"
+ for i := 0; i < 40; i++ {
+ cycle += "/c3/c4"
+ }
+ if _, err := ns.Resolve(r, "c1/"+cycle); err.Error() != "Resolution depth exceeded" {
+ boom(t, "Failed to detect cycle")
+ }
+
+ // Remove the timeout when the bug is fixed, right now, this just
+ // finishes the test immediately. Add a comparison for the expected
+ // output from glob also.
+ ch := make(chan struct{})
+ go func() {
+ doGlob(t, r, ns, "c1/...")
+ close(ch)
+ }()
+ select {
+ case <-ch:
+ case <-time.After(time.Millisecond * 100):
+ t.Errorf("glob timedout")
+ }
+}
+
+func TestUnresolve(t *testing.T) {
+ // TODO(cnicolaou): move unresolve tests into this test, right now,
+ // that's annoying because the stub compiler has some blocking bugs and the
+ // Unresolve functionality is partially implemented in the stubs.
+ t.Skip()
+ sr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ defer r.Shutdown()
+ root, mts, jokes, stopper := createNamespace(t, sr)
+ runNestedMountTables(t, sr, mts)
+ defer stopper()
+ ns := r.Namespace()
+ ns.SetRoots(root.name)
+
+ vlog.Infof("Glob: %v", doGlob(t, r, ns, "*"))
+ testResolve(t, r, ns, "joke1", jokes["joke1"].name)
+ testUnresolve(t, r, ns, "joke1", "")
+}
+
+// TestGoroutineLeaks tests for leaking goroutines - we have many:-(
+func TestGoroutineLeaks(t *testing.T) {
+ t.Skip()
+ sr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+ defer r.Shutdown()
+ _, _, _, stopper := createNamespace(t, sr)
+ defer func() {
+ vlog.Infof("%d goroutines:", runtime.NumGoroutine())
+ }()
+ defer stopper()
+ defer func() {
+ vlog.Infof("%d goroutines:", runtime.NumGoroutine())
+ }()
+ //panic("this will show up lots of goroutine+channel leaks!!!!")
+}
+
func TestBadRoots(t *testing.T) {
r, _ := rt.New()
+ defer r.Shutdown()
if _, err := namespace.New(r); err != nil {
t.Errorf("namespace.New should not have failed with no roots")
}
@@ -87,264 +494,3 @@
t.Errorf("namespace.New should have failed with an unrooted name")
}
}
-
-const (
- mt1Prefix = "mt1"
- mt2Prefix = "mt2"
- mt3Prefix = "mt3"
- mt4Prefix = "mt4"
- mt5Prefix = "mt5"
-)
-
-func testResolveToMountTable(t *testing.T, ctx context.T, mt naming.Namespace, name, want string) {
- servers, err := mt.ResolveToMountTable(ctx, name)
- if err != nil {
- boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
- }
- if len(servers) != 1 || servers[0] != want {
- boom(t, "ResolveToMountTable %q returned wrong servers: got %v, want %v", name, servers, want)
- }
-}
-
-func testResolve(t *testing.T, ctx context.T, mt naming.Namespace, name, want string) {
- servers, err := mt.Resolve(ctx, name)
- if err != nil {
- boom(t, "Failed to Resolve %q: %s", name, err)
- }
- if len(servers) != 1 || servers[0] != want {
- boom(t, "Resolve %q returned wrong servers: got %v, want %v", name, servers, want)
- }
-}
-
-func newMountTable(t *testing.T) ipc.Dispatcher {
- mt, err := service.NewMountTable("")
- if err != nil {
- boom(t, "NewMountTable returned error: %v", err)
- }
- return mt
-}
-
-func runServer(t *testing.T) (ipc.Server, naming.Endpoint) {
- // We are also running a server on this runtime using stubs so we must
- // use rt.Init(). If the server were in a separate address as per usual,
- // this wouldn't be needed and we could use rt.New.
- sr := rt.Init()
- vlog.Infof("TestNamespace")
- server, err := sr.NewServer()
- if err != nil {
- boom(t, "r.NewServer: %s", err)
- }
-
- // Add some mount table servers.
- if err := server.Register(mt1Prefix, newMountTable(t)); err != nil {
- boom(t, "Failed to register mount table: %s", err)
- }
-
- if err := server.Register(mt2Prefix, newMountTable(t)); err != nil {
- boom(t, "Failed to register mount table: %s", err)
- }
-
- if err := server.Register(mt3Prefix, newMountTable(t)); err != nil {
- boom(t, "Failed to register mount table: %s", err)
- }
-
- if err := server.Register(mt4Prefix, newMountTable(t)); err != nil {
- boom(t, "Failed to register mount table: %s", err)
- }
-
- if err := server.Register(mt5Prefix, newMountTable(t)); err != nil {
- boom(t, "Failed to register mount table: %s", err)
- }
-
- // Add a few simple services.
- if err := server.Register("joke1", ipc.SoloDispatcher(new(testServer), nil)); err != nil {
- boom(t, "Failed to register test service: %s", err)
- }
- if err := server.Register("joke2", ipc.SoloDispatcher(new(testServer), nil)); err != nil {
- boom(t, "Failed to register test service: %s", err)
- }
- if err := server.Register("joke3", ipc.SoloDispatcher(new(testServer), nil)); err != nil {
- boom(t, "Failed to register test service: %s", err)
- }
-
- // Start serving on a loopback address.
- ep, err := server.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- boom(t, "Failed to Listen: %s", err)
- }
- t.Logf("endpoint %s", ep)
- return server, ep
-}
-
-func TestNamespace(t *testing.T) {
- // Run a MountTable server, which is serving MountTables on:
- // /<estr>/{mt1,mt2,mt3,mt4,mt5}
- server, ep := runServer(t)
- defer server.Stop()
-
- estr := ep.String()
-
- // Run a client, creating a new runtime for it and intializing its
- // namespace root to point to the server created above on /<ep>/mt1.
- // This means that any relative names mounted using this local namespace
- // will appear below mt1.
- r, err := rt.New(veyron2.NamespaceRoots([]string{naming.JoinAddressName(estr, mt1Prefix)}))
- if err != nil {
- boom(t, "Failed to create client runtime: %s", err)
- }
- mt := r.Namespace()
-
- ctx := r.NewContext()
-
- // Create a DAG of mount table servers using relative addresses.
- ttl := time.Duration(100) * time.Second
- // Mount using a relative name starting with //. This means don't walk out of the
- // namespace's root mount table even if there is already something mounted at mt2.
- mt2Name := naming.JoinAddressName(estr, mt2Prefix)
- if err := mt.Mount(ctx, "//mt2", mt2Name, ttl); err != nil {
- boom(t, "Failed to Mount //mt2: %s", err)
- }
- // Mount using the relative name not starting with //. This means walk through mt3
- // if it already exists and mount at its root. However, since it doesn't exist, this is the
- // same as if we'd mounted at //mt3.
- //
- // NB: if we mount two replica mount table servers at the same place in the namespace,
- // we MUST use the // form or it will try to mount the second inside the first rather
- // than at the same place as the first.
- mt3Name := naming.JoinAddressName(estr, mt3Prefix)
- if err := mt.Mount(ctx, "mt3", mt3Name, ttl); err != nil {
- boom(t, "Failed to Mount mt3: %s", err)
- }
-
- mt1MT := naming.MakeTerminal(naming.JoinAddressName(estr, mt1Prefix))
- mt2MT := naming.MakeTerminal(naming.JoinAddressName(estr, naming.Join(mt1Prefix, mt2Prefix)))
- mt3MT := naming.MakeTerminal(naming.JoinAddressName(estr, naming.Join(mt1Prefix, mt3Prefix)))
-
- // After the mounts above we have MountTables at /<estr>/mt1{//mt2,//mt3},
- // with server addresses as per below.
- testResolveToMountTable(t, ctx, mt, "", mt1MT)
- testResolveToMountTable(t, ctx, mt, "mt2", mt2MT)
- testResolveToMountTable(t, ctx, mt, "mt3", mt3MT)
- testResolveToMountTable(t, ctx, mt, "//mt3", naming.JoinAddressName(estr, "//mt1//mt3"))
-
- // We can resolve to the MountTables using rooted, terminal names
- // as follows, both mt1 and mt1/{mt2,mt3} are served by the
- // top-level MountTable
- testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1"), mt1MT)
- testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1/mt2"), mt2MT)
- testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1/mt3"), mt3MT)
-
- // returns [mt2, mt3]
- vlog.Infof("GLOB: %s", doGlob(t, ctx, mt, "*"))
-
- // Perform two mounts that have to actually walk through other mount tables.
- if err := mt.Mount(ctx, "mt2/mt4", naming.JoinAddressName(estr, mt4Prefix), ttl); err != nil {
- boom(t, "Failed to Mount mt2/mt4: %s", err)
- }
- if err := mt.Mount(ctx, "mt3/mt4", naming.JoinAddressName(estr, mt4Prefix), ttl); err != nil {
- boom(t, "Failed to Mount mt3/mt4: %s", err)
- }
-
- // After the mounts above we now have /<estr>{/mt1/mt2/mt4,/mt1/mt3/mt4}.
- testResolveToMountTable(t, ctx, mt, "mt2/mt4", naming.JoinAddressName(estr, "//mt2/mt4"))
- testResolveToMountTable(t, ctx, mt, "mt3/mt4", naming.JoinAddressName(estr, "//mt3/mt4"))
-
- testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1/mt2/mt4"), naming.JoinAddressName(estr, "//mt1/mt2/mt4"))
-
- // Perform a mount that uses a global name as the mount point rather than
- // one relative to our namespace's root.
- global := naming.JoinAddressName(estr, "mt3/mt4/mt5")
- if err := mt.Mount(ctx, global, naming.JoinAddressName(estr, mt5Prefix), ttl); err != nil {
- boom(t, "Failed to Mount %s: %s", global, err)
- }
-
- // This mounts the service OA (ep/joke1) as joke1.
- if err := mt.Mount(ctx, "joke1", naming.JoinAddressName(estr, "//joke1"), ttl); err != nil {
- boom(t, "Failed to Mount joke1: %s", err)
- }
- // This mounts the raw server endpoint as joke2 -- like Publish would.
- if err := mt.Mount(ctx, "joke2", naming.JoinAddressName(estr, "")+"//", ttl); err != nil {
- boom(t, "Failed to Mount joke2: %s", err)
- }
- // This mounts the raw server endpoint as joke3 in mt3 -- like Publish would.
- if err := mt.Mount(ctx, "mt3/joke3", naming.JoinAddressName(estr, "")+"//", ttl); err != nil {
- boom(t, "Failed to Mount joke3: %s", err)
- }
-
- // After the mounts above we have:
- // /<estr>/mt3/mt4/mt5 - the global mount above
- // /<estr>/mt1/{joke1,joke2,mt3/joker3}
-
- // Now try resolving inside the namespace. This guarantees both that the mounts did
- // what we expected AND that we can actually resolve the results.
-
- // Get back an error since this will walk through mt5 to its root.
- _, err = mt.Resolve(ctx, "mt3/mt4/mt5")
- if err == nil {
- boom(t, "Should have failed to mt3/mt4/mt5")
- }
-
- // Resolving m3/mt4/mt5 to a MountTable using the local namepsace gives
- // us /<estr>//mt4/mt5.
- testResolveToMountTable(t, ctx, mt, "mt3/mt4/mt5", naming.JoinAddressName(estr, "//mt4/mt5"))
- testResolveToMountTable(t, ctx, mt, "mt3/mt4//mt5", naming.JoinAddressName(estr, "//mt4//mt5"))
-
- // But looking up mt4/mt5 in the local namespace will give us
- // /<estr>//mt1/mt4/mt5 since the local namespace has mt1 as its root!
- testResolveToMountTable(t, ctx, mt, "mt4/mt5", naming.JoinAddressName(estr, "//mt1/mt4/mt5"))
-
- // Looking mt3//mt4/mt5 will return the MountTable that serves //mt4/mt5.
- testResolveToMountTable(t, ctx, mt, "mt3//mt4/mt5", naming.JoinAddressName(estr, "//mt3//mt4/mt5"))
- // And the MountTable that serves //mt4/mt5 is /<epstr>//mt1/mt4/mt5
- testResolveToMountTable(t, ctx, mt, "//mt4/mt5", naming.JoinAddressName(estr, "//mt1//mt4/mt5"))
-
- vlog.Infof("\n-------------------------------------------------")
- jokeTests := []struct {
- name, resolved, resolvedToMT string
- }{
- {"joke1", naming.JoinAddressName(estr, "//joke1"), naming.JoinAddressName(estr, "//mt1/joke1")},
- {"joke2", naming.JoinAddressName(estr, "") + "//", naming.JoinAddressName(estr, "//mt1/joke2")},
- {"mt3/joke3", naming.JoinAddressName(estr, "") + "//", naming.JoinAddressName(estr, "//mt3/joke3")},
- }
- for _, test := range jokeTests {
-
- servers, err := mt.Resolve(ctx, test.name)
- if err != nil {
- boom(t, "Failed to Resolve %s: %s", test.name, err)
- }
- if len(servers) != 1 || servers[0] != test.resolved {
- boom(t, "Resolve %s returned wrong servers: %v, expected: %s", test.name, servers, test.resolved)
- }
-
- servers, err = mt.ResolveToMountTable(ctx, test.name)
- if err != nil {
- boom(t, "Failed to ResolveToMountTable %s: %s", test.name, err)
- }
- if len(servers) != 1 || servers[0] != test.resolvedToMT {
- boom(t, "ResolveToMountTable %s returned wrong servers: %v, expected: %s", test.name, servers, test.resolvedToMT)
- }
- }
-
- knockKnock(t, r, "joke1")
- knockKnock(t, r, "joke2/joke2")
- knockKnock(t, r, "mt3/joke3/joke3")
-
- // Try various globs.
- globTests := []struct {
- pattern string
- expected []string
- }{
- {"*", []string{"mt2", "mt3", "joke1", "joke2"}},
-
- {"*/...", []string{"mt2", "mt3", "mt2/mt4", "mt3/mt4", "mt2/mt4/mt5", "mt3/mt4/mt5", "joke1", "joke2", "mt3/joke3"}},
- {"*/m?4/*5", []string{"mt2/mt4/mt5", "mt3/mt4/mt5"}},
- {"*2*/*/*5", []string{"mt2/mt4/mt5"}},
- {"mt2/*/*5", []string{"mt2/mt4/mt5"}},
- {"mt2/mt4/*5", []string{"mt2/mt4/mt5"}},
- }
- for _, test := range globTests {
- out := doGlob(t, ctx, mt, test.pattern)
- checkMatch(t, test.pattern, test.expected, out)
- }
-
-}
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index 721ef85..78eeda3 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -5,6 +5,7 @@
"veyron2/context"
"veyron2/ipc"
+ "veyron2/vlog"
)
// mountIntoMountTable mounts a single server into a single mount table.
@@ -52,6 +53,7 @@
finalerr = err
}
}
+ vlog.VI(1).Infof("Mount(%s, %s) -> %v", name, server, finalerr)
return finalerr
}
diff --git a/runtimes/google/naming/namespace/namespace.go b/runtimes/google/naming/namespace/namespace.go
index bc64e1f..16b4fdd 100644
--- a/runtimes/google/naming/namespace/namespace.go
+++ b/runtimes/google/naming/namespace/namespace.go
@@ -41,7 +41,7 @@
}
// SetRoots implements naming.MountTable.SetRoots
-func (ns *namespace) SetRoots(roots []string) error {
+func (ns *namespace) SetRoots(roots ...string) error {
if !rooted(roots) {
return badRoots(roots)
}
@@ -52,6 +52,17 @@
return nil
}
+// Roots implements naming.MountTable.Roots
+func (ns *namespace) Roots() []string {
+ ns.RLock()
+ defer ns.RUnlock()
+ roots := make([]string, len(ns.roots))
+ for i, r := range ns.roots {
+ roots[i] = r
+ }
+ return roots
+}
+
// rootName 'roots' a name: if name is not a rooted name, it prepends the root
// mounttable's OA.
func (ns *namespace) rootName(name string) []string {
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index 92fc6f0..3043a31 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -2,6 +2,7 @@
import (
"errors"
+ "runtime"
"veyron2/context"
"veyron2/ipc"
@@ -72,60 +73,77 @@
return
}
-// Resolve implements veyron2/naming.MountTable.
+// Resolve implements veyron2/naming.Namespace.
func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
- vlog.VI(2).Infof("Resolve %s", name)
names := ns.rootName(name)
+ if vlog.V(2) {
+ _, file, line, _ := runtime.Caller(1)
+ vlog.Infof("Resolve(%s) called from %s:%d", name, file, line)
+ vlog.Infof("Resolve(%s) -> rootNames %s", name, names)
+ }
if len(names) == 0 {
return nil, naming.ErrNoMountTable
}
// Iterate walking through mount table servers.
for remaining := maxDepth; remaining > 0; remaining-- {
- vlog.VI(2).Infof("Resolve loop %s", names)
+ vlog.VI(2).Infof("Resolve(%s) loop %s", name, names)
if terminal(names) {
+ vlog.VI(1).Infof("Resolve(%s) -> %s", name, names)
return names, nil
}
var err error
curr := names
if names, err = resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
-
// If the name could not be found in the mount table, return an error.
if verror.Equal(naming.ErrNoSuchNameRoot, err) {
err = naming.ErrNoSuchName
}
if verror.Equal(naming.ErrNoSuchName, err) {
+ vlog.VI(1).Infof("Resolve(%s) -> (NoSuchName: %v)", name, curr)
return nil, err
}
// Any other failure (server not found, no ResolveStep
// method, etc.) are a sign that iterative resolution can
// stop.
- return makeTerminal(curr), nil
+ t := makeTerminal(curr)
+ vlog.VI(1).Infof("Resolve(%s) -> %s", name, t)
+ return t, nil
}
}
return nil, naming.ErrResolutionDepthExceeded
}
-// ResolveToMountTable implements veyron2/naming.MountTable.
+// ResolveToMountTable implements veyron2/naming.Namespace.
func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
names := ns.rootName(name)
- vlog.VI(2).Infof("ResolveToMountTable %s -> rootNames %s", name, names)
+ if vlog.V(2) {
+ _, file, line, _ := runtime.Caller(1)
+ vlog.Infof("ResolveToMountTable(%s) called from %s:%d", name, file, line)
+ vlog.Infof("ResolveToMountTable(%s) -> rootNames %s", name, names)
+ }
if len(names) == 0 {
return nil, naming.ErrNoMountTable
}
last := names
for remaining := maxDepth; remaining > 0; remaining-- {
- vlog.VI(2).Infof("ResolveToMountTable loop %s", names)
+ vlog.VI(2).Infof("ResolveToMountTable(%s) loop %s", name, names)
var err error
curr := names
if terminal(curr) {
- return makeTerminal(last), nil
+ t := makeTerminal(last)
+ vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
+ return t, nil
}
if names, err = resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
if verror.Equal(naming.ErrNoSuchNameRoot, err) {
- return makeTerminal(last), nil
+ t := makeTerminal(last)
+ vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchRoot: %v)", name, t, curr)
+ return t, nil
}
if verror.Equal(naming.ErrNoSuchName, err) {
- return makeTerminal(curr), nil
+ t := makeTerminal(curr)
+ vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchName: %v)", name, t, curr)
+ return t, nil
}
// Lots of reasons why another error can happen. We are trying
// to single out "this isn't a mount table".
@@ -133,11 +151,14 @@
// that means "we are up but don't implement what you are
// asking for".
if notAnMT(err) {
- return makeTerminal(last), nil
+ t := makeTerminal(last)
+ vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
+ return t, nil
}
// TODO(caprita): If the server is unreachable for
// example, we may still want to return its parent
// mounttable rather than an error.
+ vlog.VI(1).Infof("ResolveToMountTable(%s) -> %v", name, err)
return nil, err
}
@@ -184,7 +205,7 @@
// selecting the right branch (or should we return a representative of all
// branches?).
-// Unesolve implements veyron2/naming.MountTable.
+// Unesolve implements veyron2/naming.Namespace.
func (ns *namespace) Unresolve(ctx context.T, name string) ([]string, error) {
vlog.VI(2).Infof("Unresolve %s", name)
names, err := ns.Resolve(ctx, name)
diff --git a/runtimes/google/rt/mgmt.go b/runtimes/google/rt/mgmt.go
index ae53bb4..ccfebd7 100644
--- a/runtimes/google/rt/mgmt.go
+++ b/runtimes/google/rt/mgmt.go
@@ -52,16 +52,15 @@
if m.server, err = rt.NewServer(); err != nil {
return err
}
- const suffix = ""
- if err := m.server.Register(suffix, ipc.SoloDispatcher(appcycle.NewServerAppCycle(m), vflag.NewAuthorizerOrDie())); err != nil {
- return err
- }
// TODO(caprita): We should pick the address to listen on from config.
var ep naming.Endpoint
if ep, err = m.server.Listen("tcp", "127.0.0.1:0"); err != nil {
return err
}
- return m.callbackToParent(parentName, naming.JoinAddressName(ep.String(), suffix))
+ if err := m.server.Serve("", ipc.SoloDispatcher(appcycle.NewServerAppCycle(m), vflag.NewAuthorizerOrDie())); err != nil {
+ return err
+ }
+ return m.callbackToParent(parentName, naming.JoinAddressName(ep.String(), ""))
}
func (m *mgmtImpl) callbackToParent(parentName, myName string) error {
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index 0c38c3c..e3dfdf6 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -245,16 +245,16 @@
if err != nil {
t.Fatalf("Got error: %v", err)
}
- const suffix = ""
ch := make(chan string)
- if err := server.Register(suffix, ipc.SoloDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
- t.Fatalf("Got error: %v", err)
- }
+
var ep naming.Endpoint
if ep, err = server.Listen("tcp", "127.0.0.1:0"); err != nil {
t.Fatalf("Got error: %v", err)
}
- return server, naming.JoinAddressName(ep.String(), suffix), ch
+ if err := server.Serve("", ipc.SoloDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
+ t.Fatalf("Got error: %v", err)
+ }
+ return server, naming.JoinAddressName(ep.String(), ""), ch
}
diff --git a/runtimes/google/vsync/vsyncd/main.go b/runtimes/google/vsync/vsyncd/main.go
index a2ca5a9..506c49d 100644
--- a/runtimes/google/vsync/vsyncd/main.go
+++ b/runtimes/google/vsync/vsyncd/main.go
@@ -41,9 +41,7 @@
// Register the "sync" prefix with the sync dispatcher.
syncd := vsync.NewSyncd(*peerEndpoints, *peerDeviceIDs, *devid, *storePath, *vstoreEndpoint, *syncTick)
serverSync := vsync.NewServerSync(syncd)
- if err := s.Register("sync", ipc.SoloDispatcher(serverSync, nil)); err != nil {
- vlog.Fatalf("syncd:: error registering service: err %v", err)
- }
+ dispatcher := ipc.SoloDispatcher(serverSync, nil)
// Create an endpoint and begin listening.
if endpoint, err := s.Listen("tcp", *address); err == nil {
@@ -54,7 +52,7 @@
// Publish the vsync service. This will register it in the mount table and maintain the
// registration while the program runs.
- if err := s.Publish("sync"); err != nil {
+ if err := s.Serve("sync", dispatcher); err != nil {
vlog.Fatalf("syncd: error publishing service: err %v", err)
}