blob: d7272d3ce9177ca4446a01e8a65dc93fc212e365 [file] [log] [blame]
package mounttable
import (
"errors"
"veyron2"
"veyron2/ipc"
"veyron2/naming"
"veyron2/verror"
"veyron2/vlog"
)
const maxDepth = 32
func convertServersToStrings(servers []mountedServer, suffix string) (ret []string) {
for _, s := range servers {
ret = append(ret, naming.Join(s.Server, suffix))
}
return
}
func resolveAgainstMountTable(runtime veyron2.Runtime, names []string) ([]string, error) {
client := runtime.Client()
// 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)
call, err := client.StartCall(runtime.TODOContext(), name, "ResolveStep", nil, callTimeout)
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)
if ierr != nil {
// Internal/system error.
finalErr = ierr
vlog.VI(2).Infof("ResolveStep.Finish %s failed: %s", name, ierr)
continue
}
// If any replica says the name doesn't exist, return that fact.
if err != nil {
if verror.Equal(naming.ErrNoSuchName, err) || verror.Equal(naming.ErrNoSuchNameRoot, err) {
return nil, err
}
finalErr = err
vlog.VI(2).Infof("ResolveStep %s failed: %s", name, err)
continue
}
return convertServersToStrings(servers, suffix), nil
}
return nil, finalErr
}
func terminal(names []string) bool {
for _, name := range names {
if !naming.Terminal(name) {
return false
}
}
return true
}
func makeTerminal(names []string) (ret []string) {
for _, name := range names {
ret = append(ret, naming.MakeTerminal(name))
}
return
}
// Resolve implements veyron2/naming.MountTable.
func (ns *namespace) Resolve(name string) ([]string, error) {
vlog.VI(2).Infof("Resolve %s", name)
names := ns.rootName(name)
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)
if terminal(names) {
return names, nil
}
var err error
curr := names
if names, err = resolveAgainstMountTable(ns.rt, 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) {
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
}
}
return nil, naming.ErrResolutionDepthExceeded
}
// ResolveToMountTable implements veyron2/naming.MountTable.
func (ns *namespace) ResolveToMountTable(name string) ([]string, error) {
names := ns.rootName(name)
vlog.VI(2).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)
var err error
curr := names
if names, err = resolveAgainstMountTable(ns.rt, names); err != nil {
if verror.Equal(naming.ErrNoSuchNameRoot, err) {
return makeTerminal(last), nil
}
if verror.Equal(naming.ErrNoSuchName, err) {
return makeTerminal(curr), nil
}
// Lots of reasons why another error can happen. We are trying
// to single out "this isn't a mount table".
// TODO(p); make this less of a hack, make a specific verror code
// that means "we are up but don't implement what you are
// asking for".
if notAnMT(err) {
return makeTerminal(last), nil
}
// TODO(caprita): If the server is unreachable for
// example, we may still want to return its parent
// mounttable rather than an error.
return nil, err
}
if terminal(curr) {
return curr, nil
}
last = curr
}
return nil, naming.ErrResolutionDepthExceeded
}
func finishUnresolve(call ipc.Call) ([]string, error) {
var newNames []string
var unresolveErr error
if err := call.Finish(&newNames, &unresolveErr); err != nil {
return nil, err
}
return newNames, unresolveErr
}
func unresolveAgainstServer(runtime veyron2.Runtime, names []string) ([]string, error) {
client := runtime.Client()
finalErr := errors.New("no servers to unresolve")
for _, name := range names {
name = naming.MakeTerminal(name)
call, err := client.StartCall(runtime.TODOContext(), name, "UnresolveStep", nil, callTimeout)
if err != nil {
finalErr = err
vlog.VI(2).Infof("StartCall %q.UnresolveStep() failed: %s", name, err)
continue
}
newNames, err := finishUnresolve(call)
if err == nil {
return newNames, nil
}
finalErr = err
vlog.VI(2).Infof("Finish %s failed: %v", name, err)
}
return nil, finalErr
}
// TODO(caprita): Unresolve currently picks the first responsive server as it
// goes up the ancestry line. This means that, if a service has several
// ancestors (each with potentially more than one replica), the first responsive
// replica of the first responsive ancestor is preferred. In particular,
// other branches are ignored. We need to figure out a desired policy for
// selecting the right branch (or should we return a representative of all
// branches?).
// Unesolve implements veyron2/naming.MountTable.
func (ns *namespace) Unresolve(name string) ([]string, error) {
vlog.VI(2).Infof("Unresolve %s", name)
names, err := ns.Resolve(name)
if err != nil {
return nil, err
}
for remaining := maxDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("Unresolve loop %s", names)
curr := names
if names, err = unresolveAgainstServer(ns.rt, names); err != nil {
return nil, err
}
if len(names) == 0 {
return curr, nil
}
}
return nil, naming.ErrResolutionDepthExceeded
}