func (c *MountCommand) mountFolder(r req.MountFolder) error { warning, err := c.Klient.RemoteMountFolder(r) if err != nil { switch { case klientctlerrors.IsExistingMountErr(err): util.MustConfirm("This folder is already mounted. Remount? [Y|n]") // unmount using mount path // // TODO: Fix abstraction leak. if err := unmount(c.Klient.GetClient(), r.Name, r.LocalPath, c.Log); err != nil { c.printfln(defaultHealthChecker.CheckAllFailureOrMessagef(FailedToUnmount)) return fmt.Errorf("Error unmounting (remounting). err:%s", err) } warning, err = c.Klient.RemoteMountFolder(r) if err != nil { c.printfln(defaultHealthChecker.CheckAllFailureOrMessagef(FailedToMount)) return fmt.Errorf("Error mounting (remounting). err:%s", err) } case klientctlerrors.IsDialFailedErr(err): c.printfln(defaultHealthChecker.CheckAllFailureOrMessagef(FailedDialingRemote)) return fmt.Errorf("Error dialing remote klient. err:%s", err) case klientctlerrors.IsMachineNotValidYetErr(err): c.printfln(defaultHealthChecker.CheckAllFailureOrMessagef(MachineNotValidYet)) return fmt.Errorf("Machine is not valid yet. err:%s", err) case klientctlerrors.IsRemotePathNotExistErr(err): c.printfln(RemotePathDoesNotExist) return fmt.Errorf("Remote path does not exist. err:%s", err) case klientctlerrors.IsMachineActionLockedErr(err): c.printfln(MachineMountActionIsLocked, r.Name) return fmt.Errorf("Machine is locked. err:%s", err) default: // catch any remaining errors c.printfln(defaultHealthChecker.CheckAllFailureOrMessagef(FailedToMount)) return fmt.Errorf("Error mounting directory. err:%s", err) } } // TODO: Remove this check? The above switch has a default case, this is useless, // right? // // catch errors other than klientctlerrors.IsExistingMountErr if err != nil { c.printfln(defaultHealthChecker.CheckAllFailureOrMessagef(FailedToMount)) return fmt.Errorf("Error mounting directory. err:%s", err) } if warning != "" { c.printfln("Warning: %s\n", warning) } return nil }
// callRemoteCache performs a Klient.RemoteCache request. func (c *Command) callRemoteCache(r req.Cache, progressCb func(par *dnode.Partial)) error { if err := c.Klient.RemoteCache(r, progressCb); err != nil { // Because we have a progress bar in the UX currently, we need to add a // newline if there's an error. c.Stdout.Printlnf("") switch { case klientctlerrors.IsRemotePathNotExistErr(err): c.Stdout.Printlnf(errormessages.RemotePathDoesNotExist) return fmt.Errorf("Remote path does not exist. err:%s", err) case klientctlerrors.IsProcessError(err): c.Stdout.Printlnf(errormessages.RemoteProcessFailed, err) return err default: c.Stdout.Printlnf( c.HealthChecker.CheckAllFailureOrMessagef(errormessages.FailedPrefetchFolder), ) return fmt.Errorf("remote.cacheFolder returned an error. err:%s", err) } } return nil }
// smartOptions attempts to assign options based on the remote and local environment. // Eg, if it's a small directory, pure fuse is used. Large directory, and // onewaysync is used. Massive directory (too big for local system), and fuse is used. func (c *MountCommand) smartOptions() (err error) { if c.hasFlaggedOpts() { c.Log.Debug("Mount has flagged options, not using SmartOptions.") return nil } c.Log.Debug("Mount has no flagged options, using SmartOptions.") if c.Options.Name == "" || c.Options.LocalPath == "" { c.printfln("Mount name and local path are required options.\n") c.Help() return errors.New("not enough arguments: missing Name or LocalPath") } // Repeat the message after every smart option return, if no errors. // Using a defer to avoid code reuse. defer func() { if err == nil { c.printfln("To manually specify mount options, see: kd mount --help\n") } }() remoteSize, err := c.Klient.RemoteGetPathSize(req.GetPathSizeOptions{ Debug: c.Options.Debug, Machine: c.Options.Name, RemotePath: c.Options.RemotePath, }) if err != nil { c.Log.Error("Unable to get remote path size. err:%s", err) if klientctlerrors.IsRemotePathNotExistErr(err) { c.printfln(errormessages.RemotePathDoesNotExist) return fmt.Errorf("remote path does not exist: %s", err) } return err } // Using parent since the given directory will not yet exist. localSize, err := getLocalDiskSize(filepath.Dir(c.Options.LocalPath)) if err != nil { c.Log.Error("Unable to get local disk size. err:%s", err) return err } c.Log.Debug( "Deciding on smart options. RemotePathSize:%d, LocalDiskSize:%d", remoteSize, localSize, ) // if the folder is larger than our specified largeFolderBytes, store isLargeFolder // for easy usage. isLargeFolder := remoteSize > largeFolderBytes // If the mounting the remote folder will take up more than 75% of // the local disk, use fuse not rsync, no matter what size the remote folder is. if remoteSize > uint64(float64(localSize)*0.75) { c.printfln("Remote folder will take more than 75%% of local disk space.") if isLargeFolder { // no options needed, fuse is default. c.printfln("Because of this, mounting via fuse.") } else { // if the remote folder is large, use fuse caching option. c.printfln("Because of this, mounting via fuse and prefetchall.") c.Options.PrefetchAll = true } // fuse is the default, so we don't need any values. return nil } if isLargeFolder { c.printfln("Remote folder is over 1Gb, mounting via oneway sync.") c.Options.OneWaySync = true return nil } c.printfln("Mounting via fuse.") return nil }