veyron/runtimes/google/naming/namespace:
This is the second to last CL to get rid of //. Except for
all the tests that look for and add //'s they should no
longer be needed. ns.Mount, ns.Unmount, etc now add the
NotResolve flag to their StartCalls where needed. The
next CL will update the zillion+1 tests and eradicate
all code to handle //.
1) Add ns.ResolveX and ns.ResolveToMountTableX that
return a naming.MountEntry instead of a simple list of
names. Reimplement ns.Resolve and ns.ResolveToMountTable
as calls to these.
2) Make name resolution obey the ServesMountTable bit
in the MountEntry and the one in the Endpoint. This is
a better way to stop than putting a // somewhere in the
middle of the name.
3) Change the time in naming.MountEntry to be an
absolute expiration time. It used to be a duration
which was kind of meaningless except at the exact
moment it was returned.
Change-Id: Ifa50ac0be3adc0040f3ab525299a706e5e60cec4
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
index 9326030..b0104d5 100644
--- a/lib/modules/core/mounttable.go
+++ b/lib/modules/core/mounttable.go
@@ -89,7 +89,7 @@
output += fmt.Sprintf("R%d=%s[", entry, n.Name)
t := ""
for _, s := range n.Servers {
- t += fmt.Sprintf("%s:%s, ", s.Server, s.TTL)
+ t += fmt.Sprintf("%s:%s, ", s.Server, s.Expires)
}
t = strings.TrimSuffix(t, ", ")
output += fmt.Sprintf("%s]\n", t)
diff --git a/runtimes/google/lib/publisher/publisher.go b/runtimes/google/lib/publisher/publisher.go
index 83468e2..f7e474a 100644
--- a/runtimes/google/lib/publisher/publisher.go
+++ b/runtimes/google/lib/publisher/publisher.go
@@ -313,17 +313,17 @@
func (ps *pubState) published() []string {
var ret []string
for _, name := range ps.names {
- mtServers, err := ps.ns.ResolveToMountTable(ps.ctx, name)
+ e, err := ps.ns.ResolveToMountTableX(ps.ctx, name)
if err != nil {
vlog.Errorf("ipc pub: couldn't resolve %v to mount table: %v", name, err)
continue
}
- if len(mtServers) == 0 {
+ if len(e.Servers) == 0 {
vlog.Errorf("ipc pub: no mount table found for %v", name)
continue
}
- for _, s := range mtServers {
- ret = append(ret, naming.MakeResolvable(s))
+ for _, s := range e.Servers {
+ ret = append(ret, naming.JoinAddressName(s.Server, e.Name))
}
}
return ret
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index ad6a9b0..9c29b25 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -143,7 +143,7 @@
if err != nil {
boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
}
- compare(t, "ResolveToMoutTable", name, servers, want)
+ compare(t, "ResolveToMountTable", name, servers, want)
}
func testResolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
diff --git a/runtimes/google/naming/namespace/cache.go b/runtimes/google/naming/namespace/cache.go
index 4686928..01b704e 100644
--- a/runtimes/google/naming/namespace/cache.go
+++ b/runtimes/google/naming/namespace/cache.go
@@ -19,26 +19,25 @@
// cache is a generic interface to the resolution cache.
type cache interface {
- remember(prefix string, servers []mountedServer)
+ remember(prefix string, entry *naming.MountEntry)
forget(names []string)
- lookup(name string) ([]mountedServer, string)
+ lookup(name string) (naming.MountEntry, error)
}
// ttlCache is an instance of cache that obeys ttl from the mount points.
type ttlCache struct {
sync.Mutex
- epochStart time.Time
- entries map[string][]mountedServer
+ entries map[string]naming.MountEntry
}
// newTTLCache creates an empty ttlCache.
func newTTLCache() cache {
- return &ttlCache{epochStart: time.Now(), entries: make(map[string][]mountedServer)}
+ return &ttlCache{entries: make(map[string]naming.MountEntry)}
}
-func isStale(now uint32, servers []mountedServer) bool {
- for _, s := range servers {
- if s.TTL <= now {
+func isStale(now time.Time, e naming.MountEntry) bool {
+ for _, s := range e.Servers {
+ if s.Expires.Before(now) {
return true
}
}
@@ -54,11 +53,6 @@
return strings.TrimSuffix(name, "/")
}
-// esecs returns seconds since start of this cache's epoch.
-func (c *ttlCache) esecs() uint32 {
- return uint32(time.Since(c.epochStart).Seconds())
-}
-
// randomDrop randomly removes one cache entry. Assumes we've already locked the cache.
func (c *ttlCache) randomDrop() {
n := rand.Intn(len(c.entries))
@@ -74,7 +68,7 @@
// cleaner reduces the number of entries. Assumes we've already locked the cache.
func (c *ttlCache) cleaner() {
// First dump any stale entries.
- now := c.esecs()
+ now := time.Now()
for k, v := range c.entries {
if len(c.entries) < cacheHisteresisSize {
return
@@ -91,12 +85,19 @@
}
// remember the servers associated with name with suffix removed.
-func (c *ttlCache) remember(prefix string, servers []mountedServer) {
+func (c *ttlCache) remember(prefix string, entry *naming.MountEntry) {
+ // Remove suffix. We only care about the name that gets us
+ // to the mounttable from the last mounttable.
prefix = normalize(prefix)
- for i := range servers {
- // Remember when this cached entry times out relative to our epoch.
- servers[i].TTL += c.esecs()
+ prefix = naming.TrimSuffix(prefix, entry.Name)
+ // Copy the entry.
+ var ce naming.MountEntry
+ for _, s := range entry.Servers {
+ ce.Servers = append(ce.Servers, s)
}
+ ce.MT = entry.MT
+ // All keys must be terminal.
+ prefix = naming.MakeTerminal(prefix)
c.Lock()
// Enforce an upper limit on the cache size.
if len(c.entries) >= maxCacheEntries {
@@ -104,7 +105,7 @@
c.cleaner()
}
}
- c.entries[prefix] = servers
+ c.entries[prefix] = ce
c.Unlock()
}
@@ -125,25 +126,26 @@
}
// lookup searches the cache for a maximal prefix of name and returns the associated servers,
-// prefix, and suffix. If any of the associated servers is past its TTL, don't return anything
+// prefix, and suffix. If any of the associated servers is expired, don't return anything
// since that would reduce availability.
-func (c *ttlCache) lookup(name string) ([]mountedServer, string) {
+func (c *ttlCache) lookup(name string) (naming.MountEntry, error) {
name = normalize(name)
c.Lock()
defer c.Unlock()
- now := c.esecs()
+ now := time.Now()
for prefix, suffix := name, ""; len(prefix) > 0; prefix, suffix = backup(prefix, suffix) {
- servers, ok := c.entries[prefix]
+ e, ok := c.entries[prefix]
if !ok {
continue
}
- if isStale(now, servers) {
- return nil, ""
+ if isStale(now, e) {
+ return e, naming.ErrNoSuchName
}
- vlog.VI(2).Infof("namespace cache %s -> %v %s", name, servers, suffix)
- return servers, suffix
+ vlog.VI(2).Infof("namespace cache %s -> %v %s", name, e.Servers, e.Name)
+ e.Name = suffix
+ return e, nil
}
- return nil, ""
+ return naming.MountEntry{}, naming.ErrNoSuchName
}
// backup moves the last element of the prefix to the suffix. "//" is preserved. Thus
@@ -170,7 +172,7 @@
// nullCache is an instance of cache that does nothing.
type nullCache int
-func newNullCache() cache { return nullCache(1) }
-func (nullCache) remember(prefix string, servers []mountedServer) {}
-func (nullCache) forget(names []string) {}
-func (nullCache) lookup(name string) ([]mountedServer, string) { return nil, "" }
+func newNullCache() cache { return nullCache(1) }
+func (nullCache) remember(prefix string, entry *naming.MountEntry) {}
+func (nullCache) forget(names []string) {}
+func (nullCache) lookup(name string) (e naming.MountEntry, err error) { return e, naming.ErrNoSuchName }
diff --git a/runtimes/google/naming/namespace/cache_test.go b/runtimes/google/naming/namespace/cache_test.go
index e3908a5..d707509 100644
--- a/runtimes/google/naming/namespace/cache_test.go
+++ b/runtimes/google/naming/namespace/cache_test.go
@@ -3,17 +3,22 @@
import (
"fmt"
"testing"
+ "time"
"veyron.io/veyron/veyron2/naming"
)
-func compatible(server string, servers []mountedServer) bool {
+func compatible(server string, servers []naming.MountedServer) bool {
if len(servers) == 0 {
return server == ""
}
return servers[0].Server == server
}
+func future(secs uint32) time.Time {
+ return time.Now().Add(time.Duration(secs) * time.Second)
+}
+
// TestCache tests the cache directly rather than via the namespace methods.
func TestCache(t *testing.T) {
preload := []struct {
@@ -27,63 +32,69 @@
}
c := newTTLCache()
for _, p := range preload {
- c.remember(naming.TrimSuffix(p.name, p.suffix), []mountedServer{mountedServer{Server: p.server, TTL: 30}})
+ e := &naming.MountEntry{Name: p.suffix, Servers: []naming.MountedServer{naming.MountedServer{Server: p.server, Expires: future(30)}}}
+ c.remember(p.name, e)
}
tests := []struct {
- name string
- suffix string
- server string
+ name string
+ suffix string
+ server string
+ succeed bool
}{
- {"/h1//a/b/c/d", "c/d", "/h2"},
- {"/h2//c/d", "d", "/h3"},
- {"/h3//d", "", "/h4:1234"},
- {"/notintcache", "", ""},
- {"/h1//a/b/f//g", "f//g", "/h2"},
- {"/h3//d//e", "//e", "/h4:1234"},
+ {"/h1//a/b/c/d", "c/d", "/h2", true},
+ {"/h2//c/d", "d", "/h3", true},
+ {"/h3//d", "", "/h4:1234", true},
+ {"/notintcache", "", "", false},
+ {"/h1//a/b/f//g", "f//g", "/h2", true},
+ {"/h3//d//e", "//e", "/h4:1234", true},
}
for _, p := range tests {
- servers, suffix := c.lookup(p.name)
- if suffix != p.suffix || !compatible(p.server, servers) {
- t.Errorf("%s: unexpected depth: got %v, %s not %s, %s", p.name, servers, suffix, p.server, p.suffix)
+ e, err := c.lookup(p.name)
+ if (err == nil) != p.succeed {
+ t.Errorf("%s: lookup failed", p.name)
+ }
+ if e.Name != p.suffix || !compatible(p.server, e.Servers) {
+ t.Errorf("%s: got %v, %s not %s, %s", p.name, e.Name, e.Servers, p.suffix, p.server)
}
}
}
func TestCacheLimit(t *testing.T) {
c := newTTLCache().(*ttlCache)
- servers := []mountedServer{mountedServer{Server: "the rain in spain", TTL: 3000}}
+ e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: "the rain in spain", Expires: future(3000)}}}
for i := 0; i < maxCacheEntries; i++ {
- c.remember(fmt.Sprintf("%d", i), servers)
+ c.remember(fmt.Sprintf("%d", i), e)
if len(c.entries) > maxCacheEntries {
t.Errorf("unexpected cache size: got %d not %d", len(c.entries), maxCacheEntries)
}
}
// Adding one more element should reduce us to 3/4 full.
- c.remember(fmt.Sprintf("%d", maxCacheEntries), servers)
+ c.remember(fmt.Sprintf("%d", maxCacheEntries), e)
if len(c.entries) != cacheHisteresisSize {
t.Errorf("cache shrunk wrong amount: got %d not %d", len(c.entries), cacheHisteresisSize)
}
}
func TestCacheTTL(t *testing.T) {
+ before := time.Now()
c := newTTLCache().(*ttlCache)
// Fill cache.
- servers := []mountedServer{mountedServer{Server: "the rain in spain", TTL: 3000}}
+ e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: "the rain in spain", Expires: future(3000)}}}
for i := 0; i < maxCacheEntries; i++ {
- c.remember(fmt.Sprintf("%d", i), servers)
+ c.remember(fmt.Sprintf("%d", i), e)
}
// Time out half the entries.
i := len(c.entries) / 2
for k := range c.entries {
- c.entries[k][0].TTL = 0
+ c.entries[k].Servers[0].Expires = before
if i == 0 {
break
}
i--
}
// Add an entry and make sure we now have room.
- c.remember(fmt.Sprintf("%d", maxCacheEntries+2), servers)
+ c.remember(fmt.Sprintf("%d", maxCacheEntries+2), e)
if len(c.entries) > cacheHisteresisSize {
t.Errorf("entries did not timeout: got %d not %d", len(c.entries), cacheHisteresisSize)
}
@@ -101,7 +112,8 @@
ns, _ := New(nil)
c := ns.resolutionCache.(*ttlCache)
for _, p := range preload {
- c.remember(p.name, []mountedServer{mountedServer{Server: p.server, TTL: 3000}})
+ e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: "p.server", Expires: future(3000)}}}
+ c.remember(p.name, e)
}
toflush := "/h1/xyzzy"
if ns.FlushCacheEntry(toflush) {
@@ -112,17 +124,17 @@
t.Errorf("%s should have caused something to flush", toflush)
}
name := preload[2].name
- if _, ok := c.entries[name]; ok {
- t.Errorf("%s should have been flushed", name)
+ if _, ok := c.entries[name]; !ok {
+ t.Errorf("%s should not have been flushed", name)
}
if len(c.entries) != 2 {
t.Errorf("%s flushed too many entries", toflush)
}
+ toflush = preload[1].name
if !ns.FlushCacheEntry(toflush) {
t.Errorf("%s should have caused something to flush", toflush)
}
- name = preload[1].name
- if _, ok := c.entries[name]; ok {
+ if _, ok := c.entries[toflush]; ok {
t.Errorf("%s should have been flushed", name)
}
if len(c.entries) != 1 {
@@ -146,8 +158,9 @@
name := "/h1//a"
serverName := "/h2//"
c := ns.resolutionCache.(*ttlCache)
- c.remember(name, []mountedServer{mountedServer{Server: serverName, TTL: 3000}})
- if servers, _ := c.lookup(name); servers == nil || servers[0].Server != serverName {
+ e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: serverName, Expires: future(3000)}}}
+ c.remember(name, e)
+ if ne, err := c.lookup(name); err != nil || ne.Servers[0].Server != serverName {
t.Errorf("should have found the server in the cache")
}
@@ -157,8 +170,8 @@
t.Errorf("caching not disabled")
}
nc := ns.resolutionCache.(nullCache)
- nc.remember(name, []mountedServer{mountedServer{Server: serverName, TTL: 3000}})
- if servers, _ := nc.lookup(name); servers != nil {
+ nc.remember(name, e)
+ if _, err := nc.lookup(name); err == nil {
t.Errorf("should not have found the server in the cache")
}
@@ -168,8 +181,8 @@
t.Errorf("caching disabled")
}
c = ns.resolutionCache.(*ttlCache)
- c.remember(name, []mountedServer{mountedServer{Server: serverName, TTL: 3000}})
- if servers, _ := c.lookup(name); servers == nil || servers[0].Server != serverName {
+ c.remember(name, e)
+ if ne, err := c.lookup(name); err != nil || ne.Servers[0].Server != serverName {
t.Errorf("should have found the server in the cache")
}
}
diff --git a/runtimes/google/naming/namespace/glob.go b/runtimes/google/naming/namespace/glob.go
index 0992eaa..89fdfb1 100644
--- a/runtimes/google/naming/namespace/glob.go
+++ b/runtimes/google/naming/namespace/glob.go
@@ -4,12 +4,12 @@
"container/list"
"io"
"strings"
- "time"
"veyron.io/veyron/veyron/lib/glob"
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/vlog"
)
@@ -57,7 +57,7 @@
// At this point we're commited to a server since it answered tha call.
for {
- var e mountEntry
+ var e types.MountEntry
err := call.Recv(&e)
if err == io.EOF {
break
@@ -127,24 +127,6 @@
return reply, nil
}
-func convertStringsToServers(servers []string) (ret []naming.MountedServer) {
- for _, s := range servers {
- ret = append(ret, naming.MountedServer{Server: s})
- }
- return
-}
-
-// TODO(p): I may just give up and assume that these two will always be the same. For
-// now this lets me make the RPC interface and the model's MountTable structs be arbitrarily
-// different.
-func convertServers(servers []mountedServer) []naming.MountedServer {
- var reply []naming.MountedServer
- for _, s := range servers {
- reply = append(reply, naming.MountedServer{Server: s.Server, TTL: time.Duration(s.TTL) * time.Second})
- }
- return reply
-}
-
// depth returns the directory depth of a given name.
func depth(name string) int {
name = strings.Trim(name, "/")
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index 35d5c01..8339a8b 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -6,6 +6,7 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/vlog"
)
@@ -13,7 +14,7 @@
// mountIntoMountTable mounts a single server into a single mount table.
func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration, flags types.MountFlag) error {
ctx, _ = ctx.WithTimeout(callTimeout)
- call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags})
+ call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags}, options.NoResolve(true))
if err != nil {
return err
}
@@ -26,7 +27,7 @@
// unmountFromMountTable removes a single mounted server from a single mount table.
func unmountFromMountTable(ctx context.T, client ipc.Client, name, server string) error {
ctx, _ = ctx.WithTimeout(callTimeout)
- call, err := client.StartCall(ctx, name, "Unmount", []interface{}{server})
+ call, err := client.StartCall(ctx, name, "Unmount", []interface{}{server}, options.NoResolve(true))
if err != nil {
return err
}
diff --git a/runtimes/google/naming/namespace/namespace.go b/runtimes/google/naming/namespace/namespace.go
index 88855c0..5c2ceca 100644
--- a/runtimes/google/naming/namespace/namespace.go
+++ b/runtimes/google/naming/namespace/namespace.go
@@ -107,6 +107,32 @@
return []string{name}
}
+// rootMountEntry 'roots' a name creating a mount entry for the name.
+func (ns *namespace) rootMountEntry(name string) *naming.MountEntry {
+ e := new(naming.MountEntry)
+ expiration := time.Now().Add(time.Hour) // plenty of time for a call
+ address, suffix := naming.SplitAddressName(name)
+ if len(address) == 0 {
+ e.MT = true
+ e.Name = name
+ ns.RLock()
+ defer ns.RUnlock()
+ for _, r := range ns.roots {
+ e.Servers = append(e.Servers, naming.MountedServer{Server: r, Expires: expiration})
+ }
+ return e
+ }
+ // TODO(p): right now I assume any address handed to me to be resolved is a mount table.
+ // Eventually we should do something like the following:
+ // if ep, err := ns.rt.NewEndpoint(address); err == nil && ep.ServesMountTable() {
+ // e.MT = true
+ // }
+ e.MT = true
+ e.Name = suffix
+ e.Servers = append(e.Servers, naming.MountedServer{Server: address, Expires: expiration})
+ return e
+}
+
// notAnMT returns true if the error indicates this isn't a mounttable server.
func notAnMT(err error) bool {
switch verror.ErrorID(err) {
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index f246c06..b6a8315 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -7,40 +7,32 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/options"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
)
-func convertServersToStrings(servers []mountedServer, suffix string) (ret []string) {
- for _, s := range servers {
- ret = append(ret, naming.Join(s.Server, suffix))
- }
- return
-}
-
-func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, names []string) ([]string, error) {
+func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry) (*naming.MountEntry, error) {
// Try each server till one answers.
finalErr := errors.New("no servers to resolve query")
- for _, name := range names {
- // We want to resolve the name against the MountTable specified in its
- // address, without recursing through ourselves. To this we force
- // the entire name component to be terminal.
- name = naming.MakeTerminal(name)
+ for _, s := range e.Servers {
+ name := naming.JoinAddressName(s.Server, e.Name)
// First check the cache.
- if servers, suffix := ns.resolutionCache.lookup(name); len(servers) > 0 {
- return convertServersToStrings(servers, suffix), nil
+ if ne, err := ns.resolutionCache.lookup(name); err == nil {
+ vlog.VI(2).Infof("resolveAMT %s from cache -> %v", name, convertServersToStrings(ne.Servers, ne.Name))
+ return &ne, nil
}
// Not in cache, call the real server.
callCtx, _ := ctx.WithTimeout(callTimeout)
- call, err := client.StartCall(callCtx, name, "ResolveStep", nil)
+ call, err := client.StartCall(callCtx, name, "ResolveStepX", nil, options.NoResolve(true))
if err != nil {
finalErr = err
vlog.VI(2).Infof("ResolveStep.StartCall %s failed: %s", name, err)
continue
}
- servers := []mountedServer{}
- var suffix string
- ierr := call.Finish(&servers, &suffix, &err)
+ var entry types.MountEntry
+ ierr := call.Finish(&entry, &err)
if ierr != nil {
// Internal/system error.
finalErr = ierr
@@ -57,15 +49,17 @@
continue
}
// Add result to cache.
- ns.resolutionCache.remember(naming.TrimSuffix(name, suffix), servers)
- return convertServersToStrings(servers, suffix), nil
+ ne := convertMountEntry(&entry)
+ ns.resolutionCache.remember(name, ne)
+ vlog.VI(2).Infof("resolveAMT %s -> %v", name, *ne)
+ return ne, nil
}
return nil, finalErr
}
-func terminal(names []string) bool {
- for _, name := range names {
- if !naming.Terminal(name) {
+func terminal(e *naming.MountEntry) bool {
+ for _, s := range e.Servers {
+ if !naming.Terminal(naming.JoinAddressName(s.Server, e.Name)) {
return false
}
}
@@ -79,79 +73,86 @@
return
}
-// Resolve implements veyron2/naming.Namespace.
-func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
+// ResolveX implements veyron2/naming.Namespace.
+func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
defer vlog.LogCall()()
- names := ns.rootName(name)
+ e := ns.rootMountEntry(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)
+ vlog.Infof("ResolveX(%s) called from %s:%d", name, file, line)
+ vlog.Infof("ResolveX(%s) -> rootMountEntry %v", name, *e)
}
- if len(names) == 0 {
+ if len(e.Servers) == 0 {
return nil, naming.ErrNoMountTable
}
// Iterate walking through mount table servers.
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
- 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
+ vlog.VI(2).Infof("ResolveX(%s) loop %v", name, *e)
+ if !e.MT || terminal(e) {
+ vlog.VI(1).Infof("ResolveX(%s) -> %v", name, *e)
+ return e, nil
}
var err error
- curr := names
- if names, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
+ curr := e
+ if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr); 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)
+ vlog.VI(1).Infof("ResolveX(%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.
- t := makeTerminal(curr)
- vlog.VI(1).Infof("Resolve(%s) -> %s", name, t)
- return t, nil
+ vlog.VI(1).Infof("ResolveX(%s) -> %v", name, curr)
+ return curr, nil
}
}
return nil, naming.ErrResolutionDepthExceeded
}
-// ResolveToMountTable implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+// Resolve implements veyron2/naming.Namespace.
+func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
defer vlog.LogCall()()
- names := ns.rootName(name)
+ e, err := ns.ResolveX(ctx, name)
+ if err != nil {
+ return nil, err
+ }
+ return naming.ToStringSlice(e), nil
+}
+
+// ResolveToMountTableX implements veyron2/naming.Namespace.
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
+ defer vlog.LogCall()()
+ e := ns.rootMountEntry(name)
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)
+ vlog.Infof("ResolveToMountTableX(%s) called from %s:%d", name, file, line)
+ vlog.Infof("ResolveToMountTableX(%s) -> rootNames %v", name, e)
}
- if len(names) == 0 {
+ if len(e.Servers) == 0 {
return nil, naming.ErrNoMountTable
}
- last := names
+ last := e
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
- vlog.VI(2).Infof("ResolveToMountTable(%s) loop %s", name, names)
+ vlog.VI(2).Infof("ResolveToMountTable(%s) loop %v", name, e)
var err error
- curr := names
- if terminal(curr) {
- t := makeTerminal(last)
- vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
- return t, nil
+ curr := e
+ // If the next name to resolve doesn't point to a mount table, we're done.
+ if !e.MT || terminal(e) {
+ vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
+ return last, nil
}
- if names, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
+ if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e); err != nil {
if verror.Equal(naming.ErrNoSuchNameRoot, err) {
- t := makeTerminal(last)
- vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchRoot: %v)", name, t, curr)
- return t, nil
+ vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchRoot: %v)", name, last, curr)
+ return last, nil
}
if verror.Equal(naming.ErrNoSuchName, err) {
- t := makeTerminal(curr)
- vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchName: %v)", name, t, curr)
- return t, nil
+ vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchName: %v)", name, curr, curr)
+ return curr, nil
}
// Lots of reasons why another error can happen. We are trying
// to single out "this isn't a mount table".
@@ -159,22 +160,30 @@
// that means "we are up but don't implement what you are
// asking for".
if notAnMT(err) {
- t := makeTerminal(last)
- vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
- return t, nil
+ vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
+ return last, 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)
+ vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, err)
return nil, err
}
-
last = curr
}
return nil, naming.ErrResolutionDepthExceeded
}
+// ResolveToMountTable implements veyron2/naming.Namespace.
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+ defer vlog.LogCall()()
+ e, err := ns.ResolveToMountTableX(ctx, name)
+ if err != nil {
+ return nil, err
+ }
+ return naming.ToStringSlice(e), nil
+}
+
func finishUnresolve(call ipc.Call) ([]string, error) {
var newNames []string
var unresolveErr error
@@ -245,14 +254,14 @@
// all branches since we want to flush all entries at which we might end up whereas in a resolution,
// we stop with the first branch that works.
n := naming.MakeTerminal(n)
- if mts, suffix := ns.resolutionCache.lookup(n); mts != nil {
+ if e, err := ns.resolutionCache.lookup(n); err == nil {
// Recurse.
- for _, server := range mts {
- flushed = flushed || ns.FlushCacheEntry(naming.Join(server.Server, suffix))
+ for _, s := range e.Servers {
+ flushed = flushed || ns.FlushCacheEntry(naming.Join(s.Server, e.Name))
}
if !flushed {
// Forget the entry we just used.
- ns.resolutionCache.forget([]string{naming.TrimSuffix(n, suffix)})
+ ns.resolutionCache.forget([]string{naming.TrimSuffix(n, e.Name)})
flushed = true
}
}
diff --git a/runtimes/google/naming/namespace/stub.go b/runtimes/google/naming/namespace/stub.go
index 9661cae..5e1699c 100644
--- a/runtimes/google/naming/namespace/stub.go
+++ b/runtimes/google/naming/namespace/stub.go
@@ -1,21 +1,38 @@
package namespace
-// This file defines data types that are also defined in the idl for the
-// mounttable. We live with the duplication here to avoid having to depend
-// on stubs which in turn depend on the runtime etc.
+import (
+ "time"
-// mountedServer mirrors mounttable.MountedServer
-type mountedServer struct {
- // Server is the OA that's mounted.
- Server string
- // TTL is the remaining time (in seconds) before the mount entry expires.
- TTL uint32
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
+)
+
+func convertServersToStrings(servers []naming.MountedServer, suffix string) (ret []string) {
+ for _, s := range servers {
+ ret = append(ret, naming.Join(s.Server, suffix))
+ }
+ return
}
-// mountEntry mirrors mounttable.MountEntry
-type mountEntry struct {
- // Name is the mounted name.
- Name string
- // Servers (if present) specifies the mounted names (Link is empty).
- Servers []mountedServer
+func convertStringsToServers(servers []string) (ret []naming.MountedServer) {
+ for _, s := range servers {
+ ret = append(ret, naming.MountedServer{Server: s})
+ }
+ return
+}
+
+func convertServers(servers []types.MountedServer) []naming.MountedServer {
+ var reply []naming.MountedServer
+ for _, s := range servers {
+ if s.TTL == 0 {
+ s.TTL = 32000000 // > 1 year
+ }
+ expires := time.Now().Add(time.Duration(s.TTL) * time.Second)
+ reply = append(reply, naming.MountedServer{Server: s.Server, Expires: expires})
+ }
+ return reply
+}
+
+func convertMountEntry(e *types.MountEntry) *naming.MountEntry {
+ return &naming.MountEntry{Name: e.Name, MT: e.MT, Servers: convertServers(e.Servers)}
}
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index 58c914c..bff47f6 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -77,6 +77,34 @@
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) {
+ defer vlog.LogCall()()
+ e := new(naming.MountEntry)
+ if address, _ := naming.SplitAddressName(name); len(address) > 0 {
+ e.Servers = []naming.MountedServer{naming.MountedServer{Server: name, Expires: time.Now().Add(1000 * time.Hour)}}
+ return e, nil
+ }
+ ns.Lock()
+ defer ns.Unlock()
+ for prefix, servers := range ns.mounts {
+ if strings.HasPrefix(name, prefix) {
+ e.Name = strings.TrimLeft(strings.TrimPrefix(name, prefix), "/")
+ for _, s := range servers {
+ e.Servers = append(e.Servers, naming.MountedServer{Server: s, Expires: time.Now().Add(1000 * time.Hour)})
+ }
+ return e, nil
+ }
+ }
+ 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) {
+ 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) {
defer vlog.LogCall()()
// TODO(mattr): Implement this method for tests that might need it.
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index ce6e6f3..b052b1b 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -275,7 +275,8 @@
// Make sure the server name is reasonable.
epString, _ := naming.SplitAddressName(server)
- if _, err := rt.R().NewEndpoint(epString); err != nil {
+ ep, err := rt.R().NewEndpoint(epString)
+ if err != nil {
return fmt.Errorf("malformed address %q for mounted server %q", epString, server)
}
@@ -289,10 +290,11 @@
if hasReplaceFlag(flags) {
n.mount = nil
}
+ wantMT := hasMTFlag(flags) || ep.ServesMountTable()
if n.mount == nil {
- n.mount = &mount{servers: NewServerList(), mt: hasMTFlag(flags)}
+ n.mount = &mount{servers: NewServerList(), mt: wantMT}
} else {
- if hasMTFlag(flags) != n.mount.mt {
+ if wantMT != n.mount.mt {
return fmt.Errorf("MT doesn't match")
}
}
diff --git a/tools/debug/impl.go b/tools/debug/impl.go
index 4448abe..3b4ba10 100644
--- a/tools/debug/impl.go
+++ b/tools/debug/impl.go
@@ -91,7 +91,7 @@
}
fmt.Fprint(cmd.Stdout(), me.Name)
for _, s := range me.Servers {
- fmt.Fprintf(cmd.Stdout(), " %s (TTL %s)", s.Server, s.TTL)
+ fmt.Fprintf(cmd.Stdout(), " %s (Expires %s)", s.Server, s.Expires)
}
fmt.Fprintln(cmd.Stdout())
}
diff --git a/tools/mounttable/impl.go b/tools/mounttable/impl.go
index 1ad5896..1c88cc7 100644
--- a/tools/mounttable/impl.go
+++ b/tools/mounttable/impl.go
@@ -7,20 +7,27 @@
"veyron.io/veyron/veyron/lib/cmdline"
"veyron.io/veyron/veyron2/context"
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mounttable"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
)
func bindMT(ctx context.T, name string) (mounttable.MountTable, error) {
- mts, err := rt.R().Namespace().ResolveToMountTable(ctx, name)
+ e, err := rt.R().Namespace().ResolveToMountTableX(ctx, name)
if err != nil {
return nil, err
}
- if len(mts) == 0 {
+ if len(e.Servers) == 0 {
return nil, fmt.Errorf("Failed to find any mount tables at %q", name)
}
- fmt.Println(mts)
- return mounttable.BindMountTable(mts[0])
+ var servers []string
+ for _, s := range e.Servers {
+ servers = append(servers, naming.JoinAddressName(s.Server, e.Name))
+ }
+ fmt.Println(servers)
+ return mounttable.BindMountTable(servers[0])
}
var cmdGlob = &cmdline.Command{
@@ -86,23 +93,38 @@
}
func runMount(cmd *cmdline.Command, args []string) error {
- if expected, got := 3, len(args); expected != got {
- return cmd.UsageErrorf("mount: incorrect number of arguments, expected %d, got %d", expected, got)
+ got := len(args)
+ if got < 2 || got > 4 {
+ return cmd.UsageErrorf("mount: incorrect number of arguments, expected 2, 3, or 4, got %d", got)
+ }
+ var flags types.MountFlag
+ var seconds uint32
+ if got >= 3 {
+ ttl, err := time.ParseDuration(args[2])
+ if err != nil {
+ return fmt.Errorf("TTL parse error: %v", err)
+ }
+ seconds = uint32(ttl.Seconds())
+ }
+ if got >= 4 {
+ for _, c := range args[3] {
+ switch c {
+ case 'M':
+ flags |= types.MountFlag(types.MT)
+ case 'R':
+ flags |= types.MountFlag(types.Replace)
+ }
+ }
}
ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
defer cancel()
- c, err := bindMT(ctx, args[0])
- if err != nil {
- return fmt.Errorf("bind error: %v", err)
- }
- ttl, err := time.ParseDuration(args[2])
- if err != nil {
- return fmt.Errorf("TTL parse error: %v", err)
- }
- err = c.Mount(ctx, args[1], uint32(ttl.Seconds()), 0)
+ call, err := rt.R().Client().StartCall(ctx, args[0], "Mount", []interface{}{args[1], seconds, 0}, options.NoResolve(true))
if err != nil {
return err
}
+ if ierr := call.Finish(&err); ierr != nil {
+ return ierr
+ }
fmt.Fprintln(cmd.Stdout(), "Name mounted successfully.")
return nil
@@ -126,14 +148,13 @@
}
ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
defer cancel()
- c, err := bindMT(ctx, args[0])
- if err != nil {
- return fmt.Errorf("bind error: %v", err)
- }
- err = c.Unmount(ctx, args[1])
+ call, err := rt.R().Client().StartCall(ctx, args[0], "Unmount", []interface{}{args[1]}, options.NoResolve(true))
if err != nil {
return err
}
+ if ierr := call.Finish(&err); ierr != nil {
+ return ierr
+ }
fmt.Fprintln(cmd.Stdout(), "Name unmounted successfully.")
return nil
diff --git a/tools/namespace/impl.go b/tools/namespace/impl.go
index 0980229..5b00ce6 100644
--- a/tools/namespace/impl.go
+++ b/tools/namespace/impl.go
@@ -5,7 +5,7 @@
"time"
"veyron.io/veyron/veyron/lib/cmdline"
-
+ "veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/vlog"
)
@@ -38,7 +38,7 @@
for res := range c {
fmt.Fprint(cmd.Stdout(), res.Name)
for _, s := range res.Servers {
- fmt.Fprintf(cmd.Stdout(), " %s (TTL %s)", s.Server, s.TTL)
+ fmt.Fprintf(cmd.Stdout(), " %s (Expires %s)", s.Server, s.Expires)
}
fmt.Fprintln(cmd.Stdout())
}
@@ -156,13 +156,13 @@
ns := rt.R().Namespace()
ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
defer cancel()
- servers, err := ns.ResolveToMountTable(ctx, name)
+ e, err := ns.ResolveToMountTableX(ctx, name)
if err != nil {
- vlog.Infof("ns.ResolveToMountTable(%q) failed: %v", name, err)
+ vlog.Infof("ns.ResolveToMountTableX(%q) failed: %v", name, err)
return err
}
- for _, s := range servers {
- fmt.Fprintln(cmd.Stdout(), s)
+ for _, s := range e.Servers {
+ fmt.Fprintln(cmd.Stdout(), naming.JoinAddressName(s.Server, e.Name))
}
return nil
}
diff --git a/tools/naming/simulator/mt_complex.scr b/tools/naming/simulator/mt_complex.scr
index a5c85e2..027ddff 100644
--- a/tools/naming/simulator/mt_complex.scr
+++ b/tools/naming/simulator/mt_complex.scr
@@ -238,7 +238,7 @@
# Now, use mount directly to create a 'symlink'
set symlink_target=some/deep/name/that/is/a/mount
-mount tl/b/symlink /$mt_b_addr/$symlink_target 1h
+mount tl/b/symlink /$mt_b_addr/$symlink_target 1h M
wait $_
ls -l tl/b/symlink