namespace: Add a trailing Glob pattern ***. This is like ... but
tags the pattern as Restricted(). Up to the caller to decide what
restricted means. The namespace Glob interprets it as don't
descend into mount table servers.
This will require a new nametabled on envyor for the *** to work
correctly.
Required bug fixes in various places for tagging servers as
mount tables.
Change-Id: Iaef6a3ac783b9d353ffce7a18152bb57466221ab
diff --git a/lib/glob/glob.go b/lib/glob/glob.go
index 353179d..94453f9 100644
--- a/lib/glob/glob.go
+++ b/lib/glob/glob.go
@@ -25,8 +25,9 @@
// Glob represents a slash separated path glob expression.
type Glob struct {
- elems []string
- recursive bool
+ elems []string
+ recursive bool
+ restricted bool
}
// Parse returns a new Glob.
@@ -39,9 +40,15 @@
if pattern != "" {
g.elems = strings.Split(pattern, "/")
}
- if last := len(g.elems) - 1; last >= 0 && g.elems[last] == "..." {
- g.elems = g.elems[:last]
- g.recursive = true
+ if last := len(g.elems) - 1; last >= 0 {
+ if g.elems[last] == "..." {
+ g.elems = g.elems[:last]
+ g.recursive = true
+ } else if g.elems[last] == "***" {
+ g.elems = g.elems[:last]
+ g.recursive = true
+ g.restricted = true
+ }
}
// The only error we can get from the filepath library is badpattern.
@@ -67,12 +74,18 @@
return !g.recursive && len(g.elems) == 0
}
+// Restricted returns true if recursion is restricted (up to the caller to
+// know what that means).
+func (g *Glob) Restricted() bool {
+ return g.restricted
+}
+
// Split returns the suffix of g starting at the path element corresponding to start.
func (g *Glob) Split(start int) *Glob {
if start >= len(g.elems) {
- return &Glob{elems: nil, recursive: g.recursive}
+ return &Glob{elems: nil, recursive: g.recursive, restricted: g.restricted}
}
- return &Glob{elems: g.elems[start:], recursive: g.recursive}
+ return &Glob{elems: g.elems[start:], recursive: g.recursive, restricted: g.restricted}
}
// MatchInitialSegment tries to match segment against the initial element of g.
@@ -169,7 +182,11 @@
func (g *Glob) String() string {
e := g.elems
if g.recursive {
- e = append(e, "...")
+ if g.restricted {
+ e = append(e, "***")
+ } else {
+ e = append(e, "...")
+ }
}
return filepath.Join(e...)
}
diff --git a/runtimes/google/lib/publisher/publisher.go b/runtimes/google/lib/publisher/publisher.go
index f7e474a..63fad95 100644
--- a/runtimes/google/lib/publisher/publisher.go
+++ b/runtimes/google/lib/publisher/publisher.go
@@ -190,11 +190,11 @@
ctx context.T
ns naming.Namespace
period time.Duration
- deadline time.Time // deadline for the next sync call
- names []string // names that have been added
- servers map[string]bool // servers that have been added
- servesMT map[string]bool // true if server is a mount table server
- mounts map[mountKey]*mountStatus // map each (name,server) to its status
+ deadline time.Time // deadline for the next sync call
+ names []string // names that have been added
+ servers map[string]bool // servers that have been added, true
+ // if server is a mount table server
+ mounts map[mountKey]*mountStatus // map each (name,server) to its status
}
type mountKey struct {
@@ -216,7 +216,6 @@
period: period,
deadline: time.Now().Add(period),
servers: make(map[string]bool),
- servesMT: make(map[string]bool),
mounts: make(map[mountKey]*mountStatus),
}
}
@@ -234,18 +233,17 @@
}
}
ps.names = append(ps.names, name)
- for server, _ := range ps.servers {
+ for server, servesMT := range ps.servers {
status := new(mountStatus)
ps.mounts[mountKey{name, server}] = status
- ps.mount(name, server, status, ps.servesMT[server])
+ ps.mount(name, server, status, servesMT)
}
}
func (ps *pubState) addServer(server string, servesMT bool) {
// Each non-dup server that is added causes new mounts to be created for all
// existing names.
- if !ps.servers[server] {
- ps.servers[server] = true
+ if _, exists := ps.servers[server]; !exists {
ps.servers[server] = servesMT
for _, name := range ps.names {
status := new(mountStatus)
@@ -288,7 +286,7 @@
// Desired state is "unmounted", failed at previous attempt. Retry.
ps.unmount(key.name, key.server, status)
} else {
- ps.mount(key.name, key.server, status, ps.servesMT[key.server])
+ ps.mount(key.name, key.server, status, ps.servers[key.server])
}
}
}
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index 941694c..b803577 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -358,7 +358,7 @@
{"mt2", mts[mt4MP].name},
{"//mt2", mts[mt5MP].name},
} {
- if err := ns.Mount(r.NewContext(), mp.name, mp.server, ttl); err != nil {
+ if err := ns.Mount(r.NewContext(), mp.name, mp.server, ttl, naming.ServesMountTableOpt(true)); err != nil {
boom(t, "Failed to Mount %s: %s", mp.name, err)
}
}
@@ -507,14 +507,21 @@
tests := []struct {
pattern string
expectedCalls int
+ expected []string
}{
- {"mt4/foo/bar/glob", 0},
- {"mt4/foo/bar/glob/...", 1},
- {"mt4/foo/bar/*", 0},
+ {"mt4/foo/bar/glob", 0, []string{"mt4/foo/bar/glob"}},
+ {"mt4/foo/bar/glob/...", 1, []string{"mt4/foo/bar/glob"}},
+ {"mt4/foo/bar/glob/*", 1, nil},
+ {"mt4/foo/bar/***", 0, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
+ {"mt4/foo/bar/...", 1, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
+ {"mt4/foo/bar/*", 0, []string{"mt4/foo/bar/glob"}},
+ {"mt4/***/bar/***", 0, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
+ {"mt4/*/bar/***", 0, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
}
+ // Test allowing the tests to descend into leaves.
for _, test := range tests {
out := doGlob(t, r, ns, test.pattern, 0)
- compare(t, "Glob", test.pattern, []string{"mt4/foo/bar/glob"}, out)
+ compare(t, "Glob", test.pattern, out, test.expected)
if calls := globServer.GetAndResetCount(); calls != test.expectedCalls {
boom(t, "Wrong number of Glob calls to terminal server got: %d want: %d.", calls, test.expectedCalls)
}
@@ -539,17 +546,17 @@
defer c3.server.Stop()
m := "c1/c2"
- if err := ns.Mount(r.NewContext(), m, c1.name, ttl); err != nil {
+ if err := ns.Mount(r.NewContext(), m, c1.name, ttl, naming.ServesMountTableOpt(true)); 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 {
+ if err := ns.Mount(r.NewContext(), m, c3.name, ttl, naming.ServesMountTableOpt(true)); 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 {
+ if err := ns.Mount(r.NewContext(), m, c1.name, ttl, naming.ServesMountTableOpt(true)); err != nil {
boom(t, "Failed to Mount %s: %s", m, err)
}
diff --git a/runtimes/google/naming/namespace/cache.go b/runtimes/google/naming/namespace/cache.go
index 13ee576..be7b469 100644
--- a/runtimes/google/naming/namespace/cache.go
+++ b/runtimes/google/naming/namespace/cache.go
@@ -96,7 +96,7 @@
for _, s := range entry.Servers {
ce.Servers = append(ce.Servers, s)
}
- ce.MT = entry.MT
+ ce.SetServesMountTable(entry.ServesMountTable())
// All keys must be terminal.
prefix = naming.MakeTerminal(prefix)
c.Lock()
diff --git a/runtimes/google/naming/namespace/cache_test.go b/runtimes/google/naming/namespace/cache_test.go
index d707509..0f4cffd 100644
--- a/runtimes/google/naming/namespace/cache_test.go
+++ b/runtimes/google/naming/namespace/cache_test.go
@@ -124,7 +124,7 @@
t.Errorf("%s should have caused something to flush", toflush)
}
name := preload[2].name
- if _, ok := c.entries[name]; !ok {
+ if _, err := c.lookup(name); err != nil {
t.Errorf("%s should not have been flushed", name)
}
if len(c.entries) != 2 {
diff --git a/runtimes/google/naming/namespace/glob.go b/runtimes/google/naming/namespace/glob.go
index ff11b80..4083bb3 100644
--- a/runtimes/google/naming/namespace/glob.go
+++ b/runtimes/google/naming/namespace/glob.go
@@ -9,6 +9,7 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/services/mounttable/types"
verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
@@ -40,17 +41,26 @@
// query on since we know the server will not supply a new address for the
// current name.
if pattern.Finished() {
+ if !server.ServesMountTable() {
+ return nil
+ }
+ // TODO(p): soon to be unnecessary.
_, n := naming.SplitAddressName(s.Server)
if strings.HasPrefix(n, "//") {
return nil
}
}
+ // If this is restricted recursive and not a mount table, don't
+ // descend into it.
+ if pattern.Restricted() && !server.ServesMountTable() && pattern.Len() == 0 {
+ return nil
+ }
+
// Don't further resolve s.Server.
- s := naming.MakeTerminal(s.Server)
callCtx, _ := ctx.WithTimeout(callTimeout)
client := ns.rt.Client()
- call, err := client.StartCall(callCtx, s, "Glob", []interface{}{pstr})
+ call, err := client.StartCall(callCtx, s.Server, "Glob", []interface{}{pstr}, options.NoResolve(true))
if err != nil {
lastErr = err
continue // try another instance
@@ -79,6 +89,7 @@
},
depth: qe.depth,
}
+ x.me.SetServesMountTable(e.MT)
// x.depth is the number of severs we've walked through since we've gone
// recursive (i.e. with pattern length of 0).
if pattern.Len() == 0 {
@@ -102,29 +113,25 @@
// Glob implements naming.MountTable.Glob.
func (ns *namespace) Glob(ctx context.T, pattern string) (chan naming.MountEntry, error) {
defer vlog.LogCall()()
- root, globPattern := naming.SplitAddressName(pattern)
- g, err := glob.Parse(globPattern)
+ e, patternWasRooted := ns.rootMountEntry(pattern)
+ if len(e.Servers) == 0 {
+ return nil, verror.Make(naming.ErrNoMountTable, ctx)
+ }
+
+ // If pattern was already rooted, make sure we tack that root
+ // onto all returned names. Otherwise, just return the relative
+ // name.
+ var prefix string
+ if patternWasRooted {
+ prefix = e.Servers[0].Server
+ }
+ g, err := glob.Parse(e.Name)
if err != nil {
return nil, err
}
-
- // Add constant components of pattern to the servers' addresses and
- // to the prefix.
- //var prefixElements []string
- //prefixElements, g = g.SplitFixedPrefix()
- //prefix := strings.Join(prefixElements, "/")
- prefix := ""
- if len(root) != 0 {
- prefix = naming.JoinAddressName(root, prefix)
- }
-
- // Start a thread to get the results and return the reply channel to the caller.
- servers := ns.rootName(prefix)
- if len(servers) == 0 {
- return nil, verror.Make(naming.ErrNoMountTable, ctx)
- }
+ e.Name = ""
reply := make(chan naming.MountEntry, 100)
- go ns.globLoop(ctx, servers, prefix, g, reply)
+ go ns.globLoop(ctx, e, prefix, g, reply)
return reply, nil
}
@@ -137,7 +144,7 @@
return strings.Count(name, "/") - strings.Count(name, "//") + 1
}
-func (ns *namespace) globLoop(ctx context.T, servers []string, prefix string, pattern *glob.Glob, reply chan naming.MountEntry) {
+func (ns *namespace) globLoop(ctx context.T, e *naming.MountEntry, prefix string, pattern *glob.Glob, reply chan naming.MountEntry) {
defer close(reply)
// As we encounter new mount tables while traversing the Glob, we add them to the list 'l'. The loop below
@@ -145,7 +152,7 @@
// server. globAtServer will send on 'reply' any terminal entries that match the glob and add any new mount
// tables to be traversed to the list 'l'.
l := list.New()
- l.PushBack(&queuedEntry{me: &naming.MountEntry{Name: "", Servers: convertStringsToServers(servers)}})
+ l.PushBack(&queuedEntry{me: e})
atRoot := true
// Perform a breadth first search of the name graph.
@@ -168,8 +175,7 @@
reply <- x
}
- // 2. The current name fullfills the pattern and further servers did not respond
- // with "". That is, we want to prefer foo/ over foo.
+ // 2. The current name fullfills the pattern.
if suffix.Len() == 0 && !atRoot {
x := *e.me
x.Name = naming.Join(prefix, x.Name)
diff --git a/runtimes/google/naming/namespace/namespace.go b/runtimes/google/naming/namespace/namespace.go
index 2353607..1b966d7 100644
--- a/runtimes/google/naming/namespace/namespace.go
+++ b/runtimes/google/naming/namespace/namespace.go
@@ -109,29 +109,26 @@
}
// rootMountEntry 'roots' a name creating a mount entry for the name.
-func (ns *namespace) rootMountEntry(name string) *naming.MountEntry {
+func (ns *namespace) rootMountEntry(name string) (*naming.MountEntry, bool) {
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.SetServesMountTable(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
+ return e, false
}
// 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.SetServesMountTable(true)
e.Name = suffix
- e.Servers = append(e.Servers, naming.MountedServer{Server: address, Expires: expiration})
- return e
+ e.Servers = append(e.Servers, naming.MountedServer{Server: naming.JoinAddressName(address, ""), Expires: expiration})
+ return e, true
}
// notAnMT returns true if the error indicates this isn't a mounttable server.
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index 7333bf9..5943b6a 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -66,17 +66,10 @@
return true
}
-func makeTerminal(names []string) (ret []string) {
- for _, name := range names {
- ret = append(ret, naming.MakeTerminal(name))
- }
- return
-}
-
// ResolveX implements veyron2/naming.Namespace.
func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
defer vlog.LogCall()()
- e := ns.rootMountEntry(name)
+ e, _ := ns.rootMountEntry(name)
if vlog.V(2) {
_, file, line, _ := runtime.Caller(1)
vlog.Infof("ResolveX(%s) called from %s:%d", name, file, line)
@@ -88,7 +81,7 @@
// Iterate walking through mount table servers.
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("ResolveX(%s) loop %v", name, *e)
- if !e.MT || terminal(e) {
+ if !e.ServesMountTable() || terminal(e) {
vlog.VI(1).Infof("ResolveX(%s) -> %v", name, *e)
return e, nil
}
@@ -126,7 +119,7 @@
// ResolveToMountTableX implements veyron2/naming.Namespace.
func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
defer vlog.LogCall()()
- e := ns.rootMountEntry(name)
+ e, _ := ns.rootMountEntry(name)
if vlog.V(2) {
_, file, line, _ := runtime.Caller(1)
vlog.Infof("ResolveToMountTableX(%s) called from %s:%d", name, file, line)
@@ -141,7 +134,7 @@
var err error
curr := e
// If the next name to resolve doesn't point to a mount table, we're done.
- if !e.MT || terminal(e) {
+ if !e.ServesMountTable() || terminal(e) {
vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
return last, nil
}
@@ -196,9 +189,8 @@
func unresolveAgainstServer(ctx context.T, client ipc.Client, names []string) ([]string, error) {
finalErr := errors.New("no servers to unresolve")
for _, name := range names {
- name = naming.MakeTerminal(name)
callCtx, _ := ctx.WithTimeout(callTimeout)
- call, err := client.StartCall(callCtx, name, "UnresolveStep", nil)
+ call, err := client.StartCall(callCtx, name, "UnresolveStep", nil, options.NoResolve(true))
if err != nil {
finalErr = err
vlog.VI(2).Infof("StartCall %q.UnresolveStep() failed: %s", name, err)
diff --git a/runtimes/google/naming/namespace/stub.go b/runtimes/google/naming/namespace/stub.go
index 5e1699c..2f62a39 100644
--- a/runtimes/google/naming/namespace/stub.go
+++ b/runtimes/google/naming/namespace/stub.go
@@ -34,5 +34,7 @@
}
func convertMountEntry(e *types.MountEntry) *naming.MountEntry {
- return &naming.MountEntry{Name: e.Name, MT: e.MT, Servers: convertServers(e.Servers)}
+ v := &naming.MountEntry{Name: e.Name, Servers: convertServers(e.Servers)}
+ v.SetServesMountTable(e.MT)
+ return v
}
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index c6e659f..f13dd72 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -275,7 +275,7 @@
// Make sure the server name is reasonable.
epString, _ := naming.SplitAddressName(server)
- ep, err := rt.R().NewEndpoint(epString)
+ _, err := rt.R().NewEndpoint(epString)
if err != nil {
return fmt.Errorf("malformed address %q for mounted server %q", epString, server)
}
@@ -290,7 +290,9 @@
if hasReplaceFlag(flags) {
n.mount = nil
}
- wantMT := hasMTFlag(flags) || ep.ServesMountTable()
+ // TODO(p): When the endpoint actually has the ServesMountTable bit,
+ // or this with ep.ServesMountTable().
+ wantMT := hasMTFlag(flags)
if n.mount == nil {
n.mount = &mount{servers: NewServerList(), mt: wantMT}
} else {
@@ -384,7 +386,11 @@
n.removeUseless()
return
}
- sender.Send(types.MountEntry{Name: name, Servers: m.servers.copyToSlice()})
+ sender.Send(
+ types.MountEntry{
+ Name: name, Servers: m.servers.copyToSlice(),
+ MT: n.mount.mt,
+ })
return
}