config: allow config create and friends to take key=value parameters #3455

This commit is contained in:
Nick Craig-Wood 2021-05-05 12:22:39 +01:00
parent 095cf9e4be
commit 99caf79ffe
2 changed files with 100 additions and 28 deletions

View File

@ -108,6 +108,11 @@ var configProvidersCommand = &cobra.Command{
var updateRemoteOpt config.UpdateRemoteOpt var updateRemoteOpt config.UpdateRemoteOpt
var configPasswordHelp = strings.ReplaceAll(` var configPasswordHelp = strings.ReplaceAll(`
Note that if the config process would normally ask a question the
default is taken (unless |--non-interactive| is used). Each time
that happens rclone will print or DEBUG a message saying how to
affect the value taken.
If any of the parameters passed is a password field, then rclone will If any of the parameters passed is a password field, then rclone will
automatically obscure them if they aren't already obscured before automatically obscure them if they aren't already obscured before
putting them in the config file. putting them in the config file.
@ -170,24 +175,21 @@ with |State| as empty string.
var configCreateCommand = &cobra.Command{ var configCreateCommand = &cobra.Command{
Use: "create `name` `type` [`key` `value`]*", Use: "create `name` `type` [`key` `value`]*",
Short: `Create a new remote with name, type and options.`, Short: `Create a new remote with name, type and options.`,
Long: ` Long: strings.ReplaceAll(`
Create a new remote of ` + "`name`" + ` with ` + "`type`" + ` and options. The options Create a new remote of |name| with |type| and options. The options
should be passed in pairs of ` + "`key` `value`" + `. should be passed in pairs of |key| |value| or as |key=value|.
For example to make a swift remote of name myremote using auto config For example to make a swift remote of name myremote using auto config
you would do: you would do:
rclone config create myremote swift env_auth true rclone config create myremote swift env_auth true
rclone config create myremote swift env_auth=true
Note that if the config process would normally ask a question the
default is taken. Each time that happens rclone will print a message
saying how to affect the value taken.
` + configPasswordHelp + `
So for example if you wanted to configure a Google Drive remote but So for example if you wanted to configure a Google Drive remote but
using remote authorization you would do this: using remote authorization you would do this:
rclone config create mydrive drive config_is_local false rclone config create mydrive drive config_is_local=false
`, `, "|", "`") + configPasswordHelp,
RunE: func(command *cobra.Command, args []string) error { RunE: func(command *cobra.Command, args []string) error {
cmd.CheckArgs(2, 256, command, args) cmd.CheckArgs(2, 256, command, args)
in, err := argsToMap(args[2:]) in, err := argsToMap(args[2:])
@ -233,22 +235,23 @@ func init() {
var configUpdateCommand = &cobra.Command{ var configUpdateCommand = &cobra.Command{
Use: "update `name` [`key` `value`]+", Use: "update `name` [`key` `value`]+",
Short: `Update options in an existing remote.`, Short: `Update options in an existing remote.`,
Long: ` Long: strings.ReplaceAll(`
Update an existing remote's options. The options should be passed in Update an existing remote's options. The options should be passed in
in pairs of ` + "`key` `value`" + `. pairs of |key| |value| or as |key=value|.
For example to update the env_auth field of a remote of name myremote For example to update the env_auth field of a remote of name myremote
you would do: you would do:
rclone config update myremote swift env_auth true rclone config update myremote env_auth true
` + configPasswordHelp + ` rclone config update myremote env_auth=true
If the remote uses OAuth the token will be updated, if you don't If the remote uses OAuth the token will be updated, if you don't
require this add an extra parameter thus: require this add an extra parameter thus:
rclone config update myremote swift env_auth true config_refresh_token false rclone config update myremote swift env_auth=true config_refresh_token=false
`, `, "|", "`") + configPasswordHelp,
RunE: func(command *cobra.Command, args []string) error { RunE: func(command *cobra.Command, args []string) error {
cmd.CheckArgs(3, 256, command, args) cmd.CheckArgs(1, 256, command, args)
in, err := argsToMap(args[1:]) in, err := argsToMap(args[1:])
if err != nil { if err != nil {
return err return err
@ -271,19 +274,21 @@ var configDeleteCommand = &cobra.Command{
var configPasswordCommand = &cobra.Command{ var configPasswordCommand = &cobra.Command{
Use: "password `name` [`key` `value`]+", Use: "password `name` [`key` `value`]+",
Short: `Update password in an existing remote.`, Short: `Update password in an existing remote.`,
Long: ` Long: strings.ReplaceAll(`
Update an existing remote's password. The password Update an existing remote's password. The password
should be passed in pairs of ` + "`key` `value`" + `. should be passed in pairs of |key| |password| or as |key=password|.
The |password| should be passed in in clear (unobscured).
For example to set password of a remote of name myremote you would do: For example to set password of a remote of name myremote you would do:
rclone config password myremote fieldname mypassword rclone config password myremote fieldname mypassword
rclone config password myremote fieldname=mypassword
This command is obsolete now that "config update" and "config create" This command is obsolete now that "config update" and "config create"
both support obscuring passwords directly. both support obscuring passwords directly.
`, `, "|", "`"),
RunE: func(command *cobra.Command, args []string) error { RunE: func(command *cobra.Command, args []string) error {
cmd.CheckArgs(3, 256, command, args) cmd.CheckArgs(1, 256, command, args)
in, err := argsToMap(args[1:]) in, err := argsToMap(args[1:])
if err != nil { if err != nil {
return err return err
@ -297,16 +302,24 @@ both support obscuring passwords directly.
}, },
} }
// This takes a list of arguments in key value key value form and // This takes a list of arguments in key value key value form, or
// converts it into a map // key=value key=value form and converts it into a map
func argsToMap(args []string) (out rc.Params, err error) { func argsToMap(args []string) (out rc.Params, err error) {
if len(args)%2 != 0 {
return nil, errors.New("found key without value")
}
out = rc.Params{} out = rc.Params{}
// Set the config for i := 0; i < len(args); i++ {
for i := 0; i < len(args); i += 2 { key := args[i]
out[args[i]] = args[i+1] equals := strings.IndexRune(key, '=')
var value string
if equals >= 0 {
key, value = key[:equals], key[equals+1:]
} else {
i++
if i >= len(args) {
return nil, errors.New("found key without value")
}
value = args[i]
}
out[key] = value
} }
return out, nil return out, nil
} }

59
cmd/config/config_test.go Normal file
View File

@ -0,0 +1,59 @@
package config
import (
"fmt"
"testing"
"github.com/rclone/rclone/fs/rc"
"github.com/stretchr/testify/assert"
)
func TestArgsToMap(t *testing.T) {
for _, test := range []struct {
args []string
want rc.Params
wantErr bool
}{
{
args: []string{},
want: rc.Params{},
},
{
args: []string{"hello", "42"},
want: rc.Params{"hello": "42"},
},
{
args: []string{"hello", "42", "bye", "43"},
want: rc.Params{"hello": "42", "bye": "43"},
},
{
args: []string{"hello=42", "bye", "43"},
want: rc.Params{"hello": "42", "bye": "43"},
},
{
args: []string{"hello", "42", "bye=43"},
want: rc.Params{"hello": "42", "bye": "43"},
},
{
args: []string{"hello=42", "bye=43"},
want: rc.Params{"hello": "42", "bye": "43"},
},
{
args: []string{"hello", "42", "bye", "43", "unused"},
wantErr: true,
},
{
args: []string{"hello=42", "bye=43", "unused"},
wantErr: true,
},
} {
what := fmt.Sprintf("args = %#v", test.args)
got, err := argsToMap(test.args)
if test.wantErr {
assert.Error(t, err, what)
} else {
assert.NoError(t, err, what)
assert.Equal(t, test.want, got, what)
}
}
}