blob: 0cab494355bd9df1b87890e74a6248fee49b7bc2 [file] [log] [blame]
package namespace
import (
"time"
inaming "v.io/core/veyron/runtimes/google/naming"
"v.io/core/veyron2"
"v.io/core/veyron2/context"
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/naming"
"v.io/core/veyron2/options"
"v.io/core/veyron2/vlog"
)
type status struct {
id string
err error
}
// 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 naming.MountFlag, id string) (s status) {
s.id = id
ctx, _ = context.WithTimeout(ctx, callTimeout)
call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags}, options.NoResolve{})
s.err = err
if err != nil {
return
}
if ierr := call.Finish(&s.err); ierr != nil {
s.err = ierr
}
return
}
// unmountFromMountTable removes a single mounted server from a single mount table.
func unmountFromMountTable(ctx *context.T, client ipc.Client, name, server string, id string) (s status) {
s.id = id
ctx, _ = context.WithTimeout(ctx, callTimeout)
call, err := client.StartCall(ctx, name, "Unmount", []interface{}{server}, options.NoResolve{})
s.err = err
if err != nil {
return
}
if ierr := call.Finish(&err); ierr != nil {
s.err = ierr
}
return
}
// nameToRID converts a name to a routing ID string. If a routing ID can't be obtained,
// it just returns the name.
func nameToRID(name string) string {
address, _ := naming.SplitAddressName(name)
if ep, err := inaming.NewEndpoint(address); err == nil {
return ep.RID.String()
}
return name
}
// collectStati collects n status messages from channel c and returns an error if, for
// any id, there is no successful reply.
func collectStati(c chan status, n int) error {
// Make a map indexed by the routing id (or address if routing id not found) of
// each mount table. A mount table may be reachable via multiple addresses but
// each address should have the same routing id. We should only return an error
// if any of the ids had no successful mounts.
statusByID := make(map[string]error)
// Get the status of each request.
for i := 0; i < n; i++ {
s := <-c
if _, ok := statusByID[s.id]; !ok || s.err == nil {
statusByID[s.id] = s.err
}
}
// Return any error.
for _, s := range statusByID {
if s != nil {
return s
}
}
return nil
}
// dispatch executes f in parallel for each mount table implementing mTName.
func (ns *namespace) dispatch(ctx *context.T, mTName string, f func(*context.T, string, string) status, opts ...naming.ResolveOpt) error {
// Resolve to all the mount tables implementing name.
me, err := ns.ResolveToMountTable(ctx, mTName, opts...)
if err != nil {
return err
}
mts := me.Names()
// Apply f to each of the returned mount tables.
c := make(chan status, len(mts))
for _, mt := range mts {
go func(mt string) {
c <- f(ctx, mt, nameToRID(mt))
}(mt)
}
finalerr := collectStati(c, len(mts))
// Forget any previous cached information about these names.
ns.resolutionCache.forget(mts)
return finalerr
}
func (ns *namespace) Mount(ctx *context.T, name, server string, ttl time.Duration, opts ...naming.MountOpt) error {
defer vlog.LogCall()()
var flags naming.MountFlag
for _, o := range opts {
// NB: used a switch since we'll be adding more options.
switch v := o.(type) {
case naming.ReplaceMountOpt:
if v {
flags |= naming.MountFlag(naming.Replace)
}
case naming.ServesMountTableOpt:
if v {
flags |= naming.MountFlag(naming.MT)
}
}
}
client := veyron2.GetClient(ctx)
// Mount the server in all the returned mount tables.
f := func(ctx *context.T, mt, id string) status {
return mountIntoMountTable(ctx, client, mt, server, ttl, flags, id)
}
err := ns.dispatch(ctx, name, f)
vlog.VI(1).Infof("Mount(%s, %s) -> %v", name, server, err)
return err
}
func (ns *namespace) Unmount(ctx *context.T, name, server string) error {
defer vlog.LogCall()()
// Unmount the server from all the mount tables.
client := veyron2.GetClient(ctx)
f := func(context *context.T, mt, id string) status {
return unmountFromMountTable(ctx, client, mt, server, id)
}
err := ns.dispatch(ctx, name, f)
vlog.VI(1).Infof("Unmount(%s, %s) -> %v", name, server, err)
return err
}