mount: refactor mount remote control to use ParseOptions
This updates `mountRc` in `cmd/mountlib/rc.go` to parse options using the unified `rc.ParseOptions` helper. It also enforces parameter validation by calling `rc.CheckParamsUsed`. - Replace custom options parsing with rc.ParseOptions for vfsOpt and mountOpt. - Delete consumed params (mountPoint, mountType, fs) and call CheckParamsUsed before initiating FUSE mount to reject unknown parameters. - Clean up duplication tests (TestRcFlatOptions, TestRcFlatOptionsNull) in rc_test.go. - Update TestRc in rc_test.go to pass a clean params map to unmount.Fn.
This commit is contained in:
committed by
Nick Craig-Wood
parent
01495c8ded
commit
e8ff1b123b
@@ -1,105 +0,0 @@
|
||||
package mountlib
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/rclone/rclone/fs/config/configstruct"
|
||||
"github.com/rclone/rclone/fs/rc"
|
||||
"github.com/rclone/rclone/vfs/vfscommon"
|
||||
)
|
||||
|
||||
var (
|
||||
vfsOptionsOnce sync.Once
|
||||
vfsOptionsMap map[string]bool
|
||||
mountOptionsOnce sync.Once
|
||||
mountOptionsMap map[string]bool
|
||||
)
|
||||
|
||||
func initVfsOptions() {
|
||||
vfsOptionsOnce.Do(func() {
|
||||
vfsOptionsMap = make(map[string]bool, len(vfscommon.OptionsInfo))
|
||||
for _, opt := range vfscommon.OptionsInfo {
|
||||
vfsOptionsMap[opt.Name] = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func initMountOptions() {
|
||||
mountOptionsOnce.Do(func() {
|
||||
mountOptionsMap = make(map[string]bool, len(OptionsInfo))
|
||||
for _, opt := range OptionsInfo {
|
||||
mountOptionsMap[opt.Name] = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// isMap returns true if v's underlying type is a map
|
||||
func isMap(v any) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
t := reflect.TypeOf(v)
|
||||
for t.Kind() == reflect.Pointer {
|
||||
t = t.Elem()
|
||||
}
|
||||
return t.Kind() == reflect.Map
|
||||
}
|
||||
|
||||
// parseVfsOptions parses VFS options from in (both flat and nested) and updates vfsOpt
|
||||
func parseVfsOptions(in rc.Params, vfsOpt *vfscommon.Options) error {
|
||||
initVfsOptions()
|
||||
flatVfs := make(map[string]any)
|
||||
for k, v := range in {
|
||||
if vfsOptionsMap[k] {
|
||||
if isMap(v) {
|
||||
continue
|
||||
}
|
||||
flatVfs[k] = v
|
||||
}
|
||||
}
|
||||
if len(flatVfs) > 0 {
|
||||
err := configstruct.SetAny(flatVfs, vfsOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k := range flatVfs {
|
||||
delete(in, k)
|
||||
}
|
||||
}
|
||||
err := in.GetStructMissingOK("vfsOpt", vfsOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(in, "vfsOpt")
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseMountOptions parses Mount options from in (both flat and nested) and updates mountOpt
|
||||
func parseMountOptions(in rc.Params, mountOpt *Options) error {
|
||||
initMountOptions()
|
||||
flatMount := make(map[string]any)
|
||||
for k, v := range in {
|
||||
if mountOptionsMap[k] {
|
||||
if isMap(v) {
|
||||
continue
|
||||
}
|
||||
flatMount[k] = v
|
||||
}
|
||||
}
|
||||
if len(flatMount) > 0 {
|
||||
err := configstruct.SetAny(flatMount, mountOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k := range flatMount {
|
||||
delete(in, k)
|
||||
}
|
||||
}
|
||||
err := in.GetStructMissingOK("mountOpt", mountOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(in, "mountOpt")
|
||||
return nil
|
||||
}
|
||||
+11
-2
@@ -105,13 +105,13 @@ func mountRc(ctx context.Context, in rc.Params) (out rc.Params, err error) {
|
||||
}
|
||||
|
||||
vfsOpt := vfscommon.Opt
|
||||
err = parseVfsOptions(in, &vfsOpt)
|
||||
err = rc.ParseOptions(in, "vfsOpt", &vfsOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mountOpt := Opt
|
||||
err = parseMountOptions(in, &mountOpt)
|
||||
err = rc.ParseOptions(in, "mountOpt", &mountOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -139,6 +139,15 @@ func mountRc(ctx context.Context, in rc.Params) (out rc.Params, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clean up consumed keys and check for leftovers
|
||||
delete(in, "mountPoint")
|
||||
delete(in, "mountType")
|
||||
delete(in, "fs")
|
||||
err = rc.CheckParamsUsed(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mnt := NewMountPoint(mountFn, mountPoint, fdst, &mountOpt, &vfsOpt)
|
||||
_, err = mnt.Mount()
|
||||
if err != nil {
|
||||
|
||||
+3
-84
@@ -121,92 +121,11 @@ func TestRc(t *testing.T) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
t.Run("Unmount", func(t *testing.T) {
|
||||
_, err := unmount.Fn(ctx, in)
|
||||
_, err := unmount.Fn(ctx, rc.Params{
|
||||
"mountPoint": mountPoint,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(checkMountList()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRcFlatOptions(t *testing.T) {
|
||||
// Disable tests under macOS and the CI since they are locking up
|
||||
if runtime.GOOS == "darwin" {
|
||||
testy.SkipUnreliable(t)
|
||||
}
|
||||
ctx := context.Background()
|
||||
configfile.Install()
|
||||
mount := rc.Calls.Get("mount/mount")
|
||||
assert.NotNil(t, mount)
|
||||
unmount := rc.Calls.Get("mount/unmount")
|
||||
assert.NotNil(t, unmount)
|
||||
getMountTypes := rc.Calls.Get("mount/types")
|
||||
assert.NotNil(t, getMountTypes)
|
||||
|
||||
localDir := t.TempDir()
|
||||
err := os.WriteFile(filepath.Join(localDir, "file.txt"), []byte("hello"), 0666)
|
||||
require.NoError(t, err)
|
||||
|
||||
out, err := getMountTypes.Fn(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
var mountTypes []string
|
||||
err = out.GetStruct("mountTypes", &mountTypes)
|
||||
require.NoError(t, err)
|
||||
if len(mountTypes) == 0 {
|
||||
t.Skip("Can't mount")
|
||||
}
|
||||
|
||||
mountPointFlat := t.TempDir()
|
||||
if runtime.GOOS == "windows" {
|
||||
require.NoError(t, os.RemoveAll(mountPointFlat))
|
||||
}
|
||||
|
||||
in := rc.Params{
|
||||
"fs": localDir,
|
||||
"mountPoint": mountPointFlat,
|
||||
"file_perms": 0400, // flat VFS option
|
||||
"volname": "MyTestVolume", // flat Mount option
|
||||
}
|
||||
|
||||
// mount
|
||||
out, err = mount.Fn(ctx, in)
|
||||
if err != nil {
|
||||
t.Skipf("Mount failed - skipping test: %v", err)
|
||||
}
|
||||
|
||||
// check the returned mount point matches what we asked for
|
||||
returnedMountPoint, err := out.GetString("mountPoint")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, mountPointFlat, returnedMountPoint)
|
||||
|
||||
// check that the flat options were consumed and removed from parameter map
|
||||
_, ok := in["file_perms"]
|
||||
assert.False(t, ok, "file_perms flat option should have been deleted")
|
||||
_, ok = in["volname"]
|
||||
assert.False(t, ok, "volname flat option should have been deleted")
|
||||
|
||||
// unmount
|
||||
_, err = unmount.Fn(ctx, rc.Params{
|
||||
"mountPoint": mountPointFlat,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// FIXME wait a moment for the OS to release the mount point
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestRcFlatOptionsNull(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
configfile.Install()
|
||||
mount := rc.Calls.Get("mount/mount")
|
||||
assert.NotNil(t, mount)
|
||||
|
||||
in := rc.Params{
|
||||
"fs": "some_fs",
|
||||
"mountPoint": "some_mount_point",
|
||||
"vfs_cache_mode": nil, // flat VFS option set to null
|
||||
}
|
||||
|
||||
_, err := mount.Fn(ctx, in)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "interpreting <nil> as string failed")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user