mirror of
https://github.com/rclone/rclone.git
synced 2024-12-23 15:38:57 +01:00
config: add rc operations for config
This commit is contained in:
parent
e600217666
commit
e6dd121f52
@ -1,8 +1,11 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ncw/rclone/cmd"
|
||||
"github.com/ncw/rclone/fs/config"
|
||||
"github.com/ncw/rclone/fs/rc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -93,7 +96,16 @@ you would do:
|
||||
`,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
cmd.CheckArgs(2, 256, command, args)
|
||||
return config.CreateRemote(args[0], args[1], args[2:])
|
||||
in, err := argsToMap(args[2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = config.CreateRemote(args[0], args[1], in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ShowRemote(args[0])
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@ -110,7 +122,16 @@ For example to update the env_auth field of a remote of name myremote you would
|
||||
`,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
cmd.CheckArgs(3, 256, command, args)
|
||||
return config.UpdateRemote(args[0], args[1:])
|
||||
in, err := argsToMap(args[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = config.UpdateRemote(args[0], in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ShowRemote(args[0])
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@ -136,6 +157,29 @@ For example to set password of a remote of name myremote you would do:
|
||||
`,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
cmd.CheckArgs(3, 256, command, args)
|
||||
return config.PasswordRemote(args[0], args[1:])
|
||||
in, err := argsToMap(args[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = config.PasswordRemote(args[0], in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ShowRemote(args[0])
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// This takes a list of arguments in key value key value form and
|
||||
// converts it into a map
|
||||
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{}
|
||||
// Set the config
|
||||
for i := 0; i < len(args); i += 2 {
|
||||
out[args[i]] = args[i+1]
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"github.com/ncw/rclone/fs/driveletter"
|
||||
"github.com/ncw/rclone/fs/fshttp"
|
||||
"github.com/ncw/rclone/fs/fspath"
|
||||
"github.com/ncw/rclone/fs/rc"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
@ -901,18 +902,24 @@ func ChooseOption(o *fs.Option, name string) string {
|
||||
return in
|
||||
}
|
||||
|
||||
// Suppress the confirm prompts and return a function to undo that
|
||||
func suppressConfirm() func() {
|
||||
old := fs.Config.AutoConfirm
|
||||
fs.Config.AutoConfirm = true
|
||||
return func() {
|
||||
fs.Config.AutoConfirm = old
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRemote adds the keyValues passed in to the remote of name.
|
||||
// keyValues should be key, value pairs.
|
||||
func UpdateRemote(name string, keyValues []string) error {
|
||||
if len(keyValues)%2 != 0 {
|
||||
return errors.New("found key without value")
|
||||
}
|
||||
func UpdateRemote(name string, keyValues rc.Params) error {
|
||||
defer suppressConfirm()()
|
||||
// Set the config
|
||||
for i := 0; i < len(keyValues); i += 2 {
|
||||
getConfigData().SetValue(name, keyValues[i], keyValues[i+1])
|
||||
for k, v := range keyValues {
|
||||
getConfigData().SetValue(name, k, fmt.Sprint(v))
|
||||
}
|
||||
RemoteConfig(name)
|
||||
ShowRemote(name)
|
||||
SaveConfig()
|
||||
return nil
|
||||
}
|
||||
@ -920,9 +927,7 @@ func UpdateRemote(name string, keyValues []string) error {
|
||||
// CreateRemote creates a new remote with name, provider and a list of
|
||||
// parameters which are key, value pairs. If update is set then it
|
||||
// adds the new keys rather than replacing all of them.
|
||||
func CreateRemote(name string, provider string, keyValues []string) error {
|
||||
// Suppress Confirm
|
||||
fs.Config.AutoConfirm = true
|
||||
func CreateRemote(name string, provider string, keyValues rc.Params) error {
|
||||
// Delete the old config if it exists
|
||||
getConfigData().DeleteSection(name)
|
||||
// Set the type
|
||||
@ -935,20 +940,12 @@ func CreateRemote(name string, provider string, keyValues []string) error {
|
||||
|
||||
// PasswordRemote adds the keyValues passed in to the remote of name.
|
||||
// keyValues should be key, value pairs.
|
||||
func PasswordRemote(name string, keyValues []string) error {
|
||||
if len(keyValues) != 2 {
|
||||
return errors.New("found key without value")
|
||||
func PasswordRemote(name string, keyValues rc.Params) error {
|
||||
defer suppressConfirm()()
|
||||
for k, v := range keyValues {
|
||||
keyValues[k] = obscure.MustObscure(fmt.Sprint(v))
|
||||
}
|
||||
// Suppress Confirm
|
||||
fs.Config.AutoConfirm = true
|
||||
passwd := obscure.MustObscure(keyValues[1])
|
||||
if passwd != "" {
|
||||
getConfigData().SetValue(name, keyValues[0], passwd)
|
||||
RemoteConfig(name)
|
||||
ShowRemote(name)
|
||||
SaveConfig()
|
||||
}
|
||||
return nil
|
||||
return UpdateRemote(name, keyValues)
|
||||
}
|
||||
|
||||
// JSONListProviders prints all the providers and options in JSON format
|
||||
@ -1297,16 +1294,28 @@ func FileSections() []string {
|
||||
return sections
|
||||
}
|
||||
|
||||
// Dump dumps all the config as a JSON file
|
||||
func Dump() error {
|
||||
dump := make(map[string]map[string]string)
|
||||
for _, name := range getConfigData().GetSectionList() {
|
||||
params := make(map[string]string)
|
||||
// DumpRcRemote dumps the config for a single remote
|
||||
func DumpRcRemote(name string) (dump rc.Params) {
|
||||
params := rc.Params{}
|
||||
for _, key := range getConfigData().GetKeyList(name) {
|
||||
params[key] = FileGet(name, key)
|
||||
}
|
||||
dump[name] = params
|
||||
return params
|
||||
}
|
||||
|
||||
// DumpRcBlob dumps all the config as an unstructured blob suitable
|
||||
// for the rc
|
||||
func DumpRcBlob() (dump rc.Params) {
|
||||
dump = rc.Params{}
|
||||
for _, name := range getConfigData().GetSectionList() {
|
||||
dump[name] = DumpRcRemote(name)
|
||||
}
|
||||
return dump
|
||||
}
|
||||
|
||||
// Dump dumps all the config as a JSON file
|
||||
func Dump() error {
|
||||
dump := DumpRcBlob()
|
||||
b, err := json.MarshalIndent(dump, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to marshal config dump")
|
||||
|
178
fs/config/rc.go
Normal file
178
fs/config/rc.go
Normal file
@ -0,0 +1,178 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/ncw/rclone/fs"
|
||||
"github.com/ncw/rclone/fs/rc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rc.Add(rc.Call{
|
||||
Path: "config/dump",
|
||||
Fn: rcDump,
|
||||
Title: "Dumps the config file.",
|
||||
AuthRequired: true,
|
||||
Help: `
|
||||
Returns a JSON object:
|
||||
- key: value
|
||||
|
||||
Where keys are remote names and values are the config parameters.
|
||||
|
||||
See the [config dump command](/commands/rclone_config_dump/) command for more information on the above.
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
// Return the config file dump
|
||||
func rcDump(in rc.Params) (out rc.Params, err error) {
|
||||
return DumpRcBlob(), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rc.Add(rc.Call{
|
||||
Path: "config/get",
|
||||
Fn: rcGet,
|
||||
Title: "Get a remote in the config file.",
|
||||
AuthRequired: true,
|
||||
Help: `
|
||||
Parameters:
|
||||
- name - name of remote to get
|
||||
|
||||
See the [config dump command](/commands/rclone_config_dump/) command for more information on the above.
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
// Return the config file get
|
||||
func rcGet(in rc.Params) (out rc.Params, err error) {
|
||||
name, err := in.GetString("name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return DumpRcRemote(name), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rc.Add(rc.Call{
|
||||
Path: "config/listremotes",
|
||||
Fn: rcListRemotes,
|
||||
Title: "Lists the remotes in the config file.",
|
||||
AuthRequired: true,
|
||||
Help: `
|
||||
Returns
|
||||
- remotes - array of remote names
|
||||
|
||||
See the [listremotes command](/commands/rclone_listremotes/) command for more information on the above.
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
// Return the a list of remotes in the config file
|
||||
func rcListRemotes(in rc.Params) (out rc.Params, err error) {
|
||||
var remotes = []string{}
|
||||
for _, remote := range getConfigData().GetSectionList() {
|
||||
remotes = append(remotes, remote)
|
||||
}
|
||||
out = rc.Params{
|
||||
"remotes": remotes,
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rc.Add(rc.Call{
|
||||
Path: "config/providers",
|
||||
Fn: rcProviders,
|
||||
Title: "Shows how providers are configured in the config file.",
|
||||
AuthRequired: true,
|
||||
Help: `
|
||||
Returns a JSON object:
|
||||
- providers - array of objects
|
||||
|
||||
See the [config providers command](/commands/rclone_config_providers/) command for more information on the above.
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
// Return the config file providers
|
||||
func rcProviders(in rc.Params) (out rc.Params, err error) {
|
||||
out = rc.Params{
|
||||
"providers": fs.Registry,
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, name := range []string{"create", "update", "password"} {
|
||||
name := name
|
||||
extraHelp := ""
|
||||
if name == "create" {
|
||||
extraHelp = "- type - type of the new remote\n"
|
||||
}
|
||||
rc.Add(rc.Call{
|
||||
Path: "config/" + name,
|
||||
AuthRequired: true,
|
||||
Fn: func(in rc.Params) (rc.Params, error) {
|
||||
return rcConfig(in, name)
|
||||
},
|
||||
Title: name + " the config for a remote.",
|
||||
Help: `This takes the following parameters
|
||||
|
||||
- name - name of remote
|
||||
- type - type of new remote
|
||||
` + extraHelp + `
|
||||
|
||||
See the [config ` + name + ` command](/commands/rclone_config_` + name + `/) command for more information on the above.`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Manipulate the config file
|
||||
func rcConfig(in rc.Params, what string) (out rc.Params, err error) {
|
||||
name, err := in.GetString("name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parameters := rc.Params{}
|
||||
err = in.GetStruct("parameters", ¶meters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch what {
|
||||
case "create":
|
||||
remoteType, err := in.GetString("type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, CreateRemote(name, remoteType, parameters)
|
||||
case "update":
|
||||
return nil, UpdateRemote(name, parameters)
|
||||
case "password":
|
||||
return nil, PasswordRemote(name, parameters)
|
||||
}
|
||||
panic("unknown rcConfig type")
|
||||
}
|
||||
|
||||
func init() {
|
||||
rc.Add(rc.Call{
|
||||
Path: "config/delete",
|
||||
Fn: rcDelete,
|
||||
Title: "Delete a remote in the config file.",
|
||||
AuthRequired: true,
|
||||
Help: `
|
||||
Parameters:
|
||||
- name - name of remote to delete
|
||||
|
||||
See the [config delete command](/commands/rclone_config_delete/) command for more information on the above.
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
// Return the config file delete
|
||||
func rcDelete(in rc.Params) (out rc.Params, err error) {
|
||||
name, err := in.GetString("name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
DeleteRemote(name)
|
||||
return nil, nil
|
||||
}
|
149
fs/config/rc_test.go
Normal file
149
fs/config/rc_test.go
Normal file
@ -0,0 +1,149 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "github.com/ncw/rclone/backend/local"
|
||||
"github.com/ncw/rclone/fs"
|
||||
"github.com/ncw/rclone/fs/config/obscure"
|
||||
"github.com/ncw/rclone/fs/rc"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const testName = "configTestNameForRc"
|
||||
|
||||
func TestRc(t *testing.T) {
|
||||
// Create the test remote
|
||||
call := rc.Calls.Get("config/create")
|
||||
assert.NotNil(t, call)
|
||||
in := rc.Params{
|
||||
"name": testName,
|
||||
"type": "local",
|
||||
"parameters": rc.Params{
|
||||
"test_key": "sausage",
|
||||
},
|
||||
}
|
||||
out, err := call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, out)
|
||||
assert.Equal(t, "local", FileGet(testName, "type"))
|
||||
assert.Equal(t, "sausage", FileGet(testName, "test_key"))
|
||||
|
||||
// The sub tests rely on the remote created above but they can
|
||||
// all be run independently
|
||||
|
||||
t.Run("Dump", func(t *testing.T) {
|
||||
call := rc.Calls.Get("config/dump")
|
||||
assert.NotNil(t, call)
|
||||
in := rc.Params{}
|
||||
out, err := call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, out)
|
||||
|
||||
require.NotNil(t, out[testName])
|
||||
config := out[testName].(rc.Params)
|
||||
|
||||
assert.Equal(t, "local", config["type"])
|
||||
assert.Equal(t, "sausage", config["test_key"])
|
||||
})
|
||||
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
call := rc.Calls.Get("config/get")
|
||||
assert.NotNil(t, call)
|
||||
in := rc.Params{
|
||||
"name": testName,
|
||||
}
|
||||
out, err := call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, out)
|
||||
|
||||
assert.Equal(t, "local", out["type"])
|
||||
assert.Equal(t, "sausage", out["test_key"])
|
||||
})
|
||||
|
||||
t.Run("ListRemotes", func(t *testing.T) {
|
||||
call := rc.Calls.Get("config/listremotes")
|
||||
assert.NotNil(t, call)
|
||||
in := rc.Params{}
|
||||
out, err := call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, out)
|
||||
|
||||
var remotes []string
|
||||
err = out.GetStruct("remotes", &remotes)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, remotes, testName)
|
||||
})
|
||||
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
call := rc.Calls.Get("config/update")
|
||||
assert.NotNil(t, call)
|
||||
in := rc.Params{
|
||||
"name": testName,
|
||||
"parameters": rc.Params{
|
||||
"test_key": "rutabaga",
|
||||
"test_key2": "cabbage",
|
||||
},
|
||||
}
|
||||
out, err := call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, out)
|
||||
|
||||
assert.Equal(t, "local", FileGet(testName, "type"))
|
||||
assert.Equal(t, "rutabaga", FileGet(testName, "test_key"))
|
||||
assert.Equal(t, "cabbage", FileGet(testName, "test_key2"))
|
||||
})
|
||||
|
||||
t.Run("Password", func(t *testing.T) {
|
||||
call := rc.Calls.Get("config/password")
|
||||
assert.NotNil(t, call)
|
||||
in := rc.Params{
|
||||
"name": testName,
|
||||
"parameters": rc.Params{
|
||||
"test_key": "rutabaga",
|
||||
"test_key2": "cabbage",
|
||||
},
|
||||
}
|
||||
out, err := call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, out)
|
||||
|
||||
assert.Equal(t, "local", FileGet(testName, "type"))
|
||||
assert.Equal(t, "rutabaga", obscure.MustReveal(FileGet(testName, "test_key")))
|
||||
assert.Equal(t, "cabbage", obscure.MustReveal(FileGet(testName, "test_key2")))
|
||||
})
|
||||
|
||||
// Delete the test remote
|
||||
call = rc.Calls.Get("config/delete")
|
||||
assert.NotNil(t, call)
|
||||
in = rc.Params{
|
||||
"name": testName,
|
||||
}
|
||||
out, err = call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, out)
|
||||
assert.Equal(t, "", FileGet(testName, "type"))
|
||||
assert.Equal(t, "", FileGet(testName, "test_key"))
|
||||
}
|
||||
|
||||
func TestRcProviders(t *testing.T) {
|
||||
call := rc.Calls.Get("config/providers")
|
||||
assert.NotNil(t, call)
|
||||
in := rc.Params{}
|
||||
out, err := call.Fn(in)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, out)
|
||||
var registry []*fs.RegInfo
|
||||
err = out.GetStruct("providers", ®istry)
|
||||
require.NoError(t, err)
|
||||
foundLocal := false
|
||||
for _, provider := range registry {
|
||||
if provider.Name == "local" {
|
||||
foundLocal = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, foundLocal, "didn't find local provider")
|
||||
}
|
Loading…
Reference in New Issue
Block a user