1
mirror of https://github.com/rclone/rclone synced 2024-11-27 05:23:40 +01:00

rc: implement backend/command for running backend commands remotely

This commit is contained in:
Nick Craig-Wood 2020-04-28 13:02:19 +01:00
parent e2916f3a55
commit d80fdad6da
2 changed files with 126 additions and 0 deletions

View File

@ -373,3 +373,88 @@ func rcFsInfo(ctx context.Context, in rc.Params) (out rc.Params, err error) {
} }
return out, nil return out, nil
} }
func init() {
rc.Add(rc.Call{
Path: "backend/command",
AuthRequired: true,
Fn: rcBackend,
Title: "Runs a backend command.",
Help: `This takes the following parameters
- command - a string with the command name
- fs - a remote name string eg "drive:"
- arg - a list of arguments for the backend command
- opt - a map of string to string of options
Returns
- result - result from the backend command
For example
rclone rc backend/command command=noop fs=. -o echo=yes -o blue -a path1 -a path2
Returns
` + "```" + `
{
"result": {
"arg": [
"path1",
"path2"
],
"name": "noop",
"opt": {
"blue": "",
"echo": "yes"
}
}
}
` + "```" + `
Note that this is the direct equivalent of using this "backend"
command:
rclone backend noop . -o echo=yes -o blue path1 path2
Note that arguments must be preceeded by the "-a" flag
See the [backend](/commands/rclone_backend/) command for more information.
`,
})
}
// Make a public link
func rcBackend(ctx context.Context, in rc.Params) (out rc.Params, err error) {
f, err := rc.GetFs(in)
if err != nil {
return nil, err
}
doCommand := f.Features().Command
if doCommand == nil {
return nil, errors.Errorf("%v: doesn't support backend commands", f)
}
command, err := in.GetString("command")
if err != nil {
return nil, err
}
var opt = map[string]string{}
err = in.GetStructMissingOK("opt", &opt)
if err != nil {
return nil, err
}
var arg = []string{}
err = in.GetStructMissingOK("arg", &arg)
if err != nil {
return nil, err
}
result, err := doCommand(context.Background(), command, arg, opt)
if err != nil {
return nil, errors.Wrapf(err, "command %q failed", command)
}
out = make(rc.Params)
out["result"] = result
return out, nil
}

View File

@ -434,3 +434,44 @@ func TestRcFsInfo(t *testing.T) {
assert.Equal(t, features, got["Features"]) assert.Equal(t, features, got["Features"])
} }
// operations/command: Runs a backend command
func TestRcCommand(t *testing.T) {
r, call := rcNewRun(t, "backend/command")
defer r.Finalise()
in := rc.Params{
"fs": r.FremoteName,
"command": "noop",
"opt": map[string]string{
"echo": "true",
"blue": "",
},
"arg": []string{
"path1",
"path2",
},
}
got, err := call.Fn(context.Background(), in)
if err != nil {
assert.False(t, r.Fremote.Features().IsLocal, "mustn't fail on local remote")
assert.Contains(t, err.Error(), "command not found")
return
}
want := rc.Params{"result": map[string]interface{}{
"arg": []string{
"path1",
"path2",
},
"name": "noop",
"opt": map[string]string{
"blue": "",
"echo": "true",
},
}}
assert.Equal(t, want, got)
errTxt := "explosion in the sausage factory"
in["opt"].(map[string]string)["error"] = errTxt
_, err = call.Fn(context.Background(), in)
assert.Error(t, err)
assert.Contains(t, err.Error(), errTxt)
}