| 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 |
| } |