forked from TrueCloudLab/rclone
config: add sub commands for full config file management
Previously config sub commands were manually parsed rather than using cobra. Make config command have the following sub commands: * create Create a new remote with name, type and options. * delete Delete an existing remote <name>. * dump Dump the config file as JSON. * edit Enter an interactive configuration session. * file Show path of configuration file in use. * providers List in JSON format all the providers and options. * show Print (decrypted) config file, or the config for a single remote. * update Update options in an existing remote. The following changes were made to existing commands * listproviders was renamed to providers * listoptions was removed in favour of providing the output in providers * jsonconfig was renamed to create * an optional parameter was added to the show command
This commit is contained in:
parent
0575623dff
commit
edfab09eb9
3 changed files with 163 additions and 94 deletions
|
@ -1,65 +1,123 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ncw/rclone/cmd"
|
"github.com/ncw/rclone/cmd"
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.Root.AddCommand(commandDefintion)
|
cmd.Root.AddCommand(configCommand)
|
||||||
|
configCommand.AddCommand(configEditCommand)
|
||||||
|
configCommand.AddCommand(configFileCommand)
|
||||||
|
configCommand.AddCommand(configShowCommand)
|
||||||
|
configCommand.AddCommand(configDumpCommand)
|
||||||
|
configCommand.AddCommand(configProvidersCommand)
|
||||||
|
configCommand.AddCommand(configCreateCommand)
|
||||||
|
configCommand.AddCommand(configUpdateCommand)
|
||||||
|
configCommand.AddCommand(configDeleteCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandDefintion = &cobra.Command{
|
var configCommand = &cobra.Command{
|
||||||
Use: "config [function]",
|
Use: "config",
|
||||||
Short: `Enter an interactive configuration session.`,
|
Short: `Enter an interactive configuration session.`,
|
||||||
Long: "`rclone config`" + `
|
Long: `Enter an interactive configuration session where you can setup new
|
||||||
enters an interactive configuration sessions where you can setup
|
remotes and manage existing ones. You may also set or remove a
|
||||||
new remotes and manage existing ones. You may also set or remove a password to
|
password to protect your configuration.
|
||||||
protect your configuration.
|
|
||||||
|
|
||||||
Additional functions:
|
|
||||||
|
|
||||||
* ` + "`rclone config edit`" + ` – same as above
|
|
||||||
* ` + "`rclone config file`" + ` – show path of configuration file in use
|
|
||||||
* ` + "`rclone config show`" + ` – print (decrypted) config file
|
|
||||||
* ` + "`rclone config listproviders`" + ` – List, in json format, the protocols supported by sync
|
|
||||||
* ` + "`rclone config listoptions type`" + ` – Lists all the options needed to connect to a protocol
|
|
||||||
* ` + "`rclone config jsonconfig`name type jsonoptions" + ` – Created a new remote type X with parameters Y
|
|
||||||
`,
|
`,
|
||||||
Run: func(command *cobra.Command, args []string) {
|
Run: func(command *cobra.Command, args []string) {
|
||||||
cmd.CheckArgs(0, 4, command, args)
|
cmd.CheckArgs(0, 0, command, args)
|
||||||
if len(args) == 0 {
|
|
||||||
fs.EditConfig()
|
fs.EditConfig()
|
||||||
} else if (len(args) == 1) {
|
},
|
||||||
switch args[0] {
|
}
|
||||||
case "edit":
|
|
||||||
fs.EditConfig()
|
var configEditCommand = &cobra.Command{
|
||||||
case "show":
|
Use: "edit",
|
||||||
fs.ShowConfig()
|
Short: configCommand.Short,
|
||||||
case "file":
|
Long: configCommand.Long,
|
||||||
fs.ShowConfigLocation()
|
Run: configCommand.Run,
|
||||||
case "listproviders":
|
}
|
||||||
fs.ListProviders()
|
|
||||||
default:
|
var configFileCommand = &cobra.Command{
|
||||||
fmt.Fprintf(os.Stderr, "Unknown subcommand %q, %s only supports edit, show and file.\n", args[0], command.Name())
|
Use: "file",
|
||||||
}
|
Short: `Show path of configuration file in use.`,
|
||||||
} else if (len(args) == 2) {
|
Run: func(command *cobra.Command, args []string) {
|
||||||
if ((args[0] == "listoptions") && (args[1] != "")) {
|
cmd.CheckArgs(0, 0, command, args)
|
||||||
fs.ListOptions(args[1])
|
fs.ShowConfigLocation()
|
||||||
} else {
|
},
|
||||||
fmt.Fprintf(os.Stderr, "Unknown subcommand %q %q, %s only supports optionsprovider <type>.\n", args[0], args[1], command.Name())
|
}
|
||||||
}
|
|
||||||
} else if (len(args) == 4) {
|
var configShowCommand = &cobra.Command{
|
||||||
if ((args[0] == "jsonconfig") && (args[1] != "") && (args[2] != "") && (args[3]!= "")) {
|
Use: "show [<remote>]",
|
||||||
fs.JsonConfig(args[1], args[2], args[3])
|
Short: `Print (decrypted) config file, or the config for a single remote.`,
|
||||||
} else {
|
Run: func(command *cobra.Command, args []string) {
|
||||||
fmt.Fprintf(os.Stderr, "Unknown subcommand %q %q %q %q, %s only supports jsonconfig <name> <type> <json:options>.\n", args[0], args[1], args[2], args[3], command.Name())
|
cmd.CheckArgs(0, 1, command, args)
|
||||||
}
|
if len(args) == 0 {
|
||||||
}
|
fs.ShowConfig()
|
||||||
return
|
} else {
|
||||||
|
fs.ShowRemote(args[0])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var configDumpCommand = &cobra.Command{
|
||||||
|
Use: "dump",
|
||||||
|
Short: `Dump the config file as JSON.`,
|
||||||
|
RunE: func(command *cobra.Command, args []string) error {
|
||||||
|
cmd.CheckArgs(0, 0, command, args)
|
||||||
|
return fs.ConfigDump()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var configProvidersCommand = &cobra.Command{
|
||||||
|
Use: "providers",
|
||||||
|
Short: `List in JSON format all the providers and options.`,
|
||||||
|
RunE: func(command *cobra.Command, args []string) error {
|
||||||
|
cmd.CheckArgs(0, 0, command, args)
|
||||||
|
return fs.JSONListProviders()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var configCreateCommand = &cobra.Command{
|
||||||
|
Use: "create <name> <type> [<key> <value>]*",
|
||||||
|
Short: `Create a new remote with name, type and options.`,
|
||||||
|
Long: `
|
||||||
|
Create a new remote of <name> with <type> and options. The options
|
||||||
|
should be passed in in pairs of <key> <value>.
|
||||||
|
|
||||||
|
For example to make a swift remote of name myremote using auto config
|
||||||
|
you would do:
|
||||||
|
|
||||||
|
rclone config create myremote swift env_auth true
|
||||||
|
`,
|
||||||
|
RunE: func(command *cobra.Command, args []string) error {
|
||||||
|
cmd.CheckArgs(2, 256, command, args)
|
||||||
|
return fs.CreateRemote(args[0], args[1], args[2:])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var configUpdateCommand = &cobra.Command{
|
||||||
|
Use: "update <name> [<key> <value>]+",
|
||||||
|
Short: `Update options in an existing remote.`,
|
||||||
|
Long: `
|
||||||
|
Update an existing remote's options. The options should be passed in
|
||||||
|
in pairs of <key> <value>.
|
||||||
|
|
||||||
|
For example to update the env_auth field of a remote of name myremote you would do:
|
||||||
|
|
||||||
|
rclone config update myremote swift env_auth true
|
||||||
|
`,
|
||||||
|
RunE: func(command *cobra.Command, args []string) error {
|
||||||
|
cmd.CheckArgs(3, 256, command, args)
|
||||||
|
return fs.UpdateRemote(args[0], args[1:])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var configDeleteCommand = &cobra.Command{
|
||||||
|
Use: "delete <name>",
|
||||||
|
Short: `Delete an existing remote <name>.`,
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
cmd.CheckArgs(1, 1, command, args)
|
||||||
|
fs.DeleteRemote(args[0])
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
90
fs/config.go
90
fs/config.go
|
@ -992,56 +992,47 @@ func ChooseOption(o *Option) string {
|
||||||
return ReadLine()
|
return ReadLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
// JsonConfig Created a new remote type X with parameters Y
|
// UpdateRemote adds the keyValues passed in to the remote of name.
|
||||||
func JsonConfig(name string, provider string, jsonstr string) {
|
// keyValues should be key, value pairs.
|
||||||
bytes := []byte(jsonstr)
|
func UpdateRemote(name string, keyValues []string) error {
|
||||||
|
if len(keyValues)%2 != 0 {
|
||||||
// Unmarshal string into structs.
|
return errors.New("found key without value")
|
||||||
var options []Option
|
}
|
||||||
json.Unmarshal(bytes, &options)
|
// Set the config
|
||||||
|
for i := 0; i < len(keyValues); i += 2 {
|
||||||
configData.SetValue(name, "type", provider)
|
configData.SetValue(name, keyValues[i], keyValues[i+1])
|
||||||
// Loop over structs and display them.
|
|
||||||
for op := range options {
|
|
||||||
configData.SetValue(name, options[op].Name, options[op].Value)
|
|
||||||
}
|
}
|
||||||
configData.SetValue(name, ConfigAutomatic, "yes")
|
|
||||||
RemoteConfig(name)
|
RemoteConfig(name)
|
||||||
ShowRemote(name)
|
ShowRemote(name)
|
||||||
SaveConfig()
|
SaveConfig()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListOptions Lists all the options needed to connect to a protocol
|
// CreateRemote creates a new remote with name, provider and a list of
|
||||||
func ListOptions(provider string) {
|
// parameters which are key, value pairs. If update is set then it
|
||||||
fs := MustFind(provider)
|
// adds the new keys rather than replacing all of them.
|
||||||
b, err := json.Marshal(fs.Options)
|
func CreateRemote(name string, provider string, keyValues []string) error {
|
||||||
if err != nil {
|
// Delete the old config if it exists
|
||||||
fmt.Println("error:", err)
|
configData.DeleteSection(name)
|
||||||
}
|
// Set the type
|
||||||
os.Stdout.Write(b)
|
configData.SetValue(name, "type", provider)
|
||||||
|
// Show this is automatically configured
|
||||||
|
configData.SetValue(name, ConfigAutomatic, "yes")
|
||||||
|
// Set the remaining values
|
||||||
|
return UpdateRemote(name, keyValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListProviders print all providersList, in json format, the protocols supported by sync
|
// JSONListProviders prints all the providers and options in JSON format
|
||||||
func ListProviders() {
|
func JSONListProviders() error {
|
||||||
o := &Option{
|
b, err := json.MarshalIndent(fsRegistry, "", " ")
|
||||||
Name: "Storage",
|
|
||||||
Help: "Type of storage to configure.",
|
|
||||||
}
|
|
||||||
for _, item := range fsRegistry {
|
|
||||||
example := OptionExample{
|
|
||||||
Value: item.Name,
|
|
||||||
Help: item.Description,
|
|
||||||
}
|
|
||||||
o.Examples = append(o.Examples, example)
|
|
||||||
}
|
|
||||||
if len(o.Examples) > 0 {
|
|
||||||
o.Examples.Sort()
|
|
||||||
b, err := json.Marshal(o.Examples)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("error:", err)
|
return errors.Wrap(err, "failed to marshal examples")
|
||||||
}
|
}
|
||||||
os.Stdout.Write(b)
|
_, err = os.Stdout.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write providers list")
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fsOption returns an Option describing the possible remotes
|
// fsOption returns an Option describing the possible remotes
|
||||||
|
@ -1367,3 +1358,24 @@ func ConfigFileSections() []string {
|
||||||
}
|
}
|
||||||
return sections
|
return sections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigDump dumps all the config as a JSON file
|
||||||
|
func ConfigDump() error {
|
||||||
|
dump := make(map[string]map[string]string)
|
||||||
|
for _, name := range configData.GetSectionList() {
|
||||||
|
params := make(map[string]string)
|
||||||
|
for _, key := range configData.GetKeyList(name) {
|
||||||
|
params[key] = ConfigFileGet(name, key)
|
||||||
|
}
|
||||||
|
dump[name] = params
|
||||||
|
}
|
||||||
|
b, err := json.MarshalIndent(dump, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to marshal config dump")
|
||||||
|
}
|
||||||
|
_, err = os.Stdout.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write config dump")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
7
fs/fs.go
7
fs/fs.go
|
@ -62,9 +62,9 @@ type RegInfo struct {
|
||||||
// Create a new file system. If root refers to an existing
|
// Create a new file system. If root refers to an existing
|
||||||
// object, then it should return a Fs which which points to
|
// object, then it should return a Fs which which points to
|
||||||
// the parent of that object and ErrorIsFile.
|
// the parent of that object and ErrorIsFile.
|
||||||
NewFs func(name string, root string) (Fs, error)
|
NewFs func(name string, root string) (Fs, error) `json:"-"`
|
||||||
// Function to call to help with config
|
// Function to call to help with config
|
||||||
Config func(string)
|
Config func(string) `json:"-"`
|
||||||
// Options for the Fs configuration
|
// Options for the Fs configuration
|
||||||
Options []Option
|
Options []Option
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,7 @@ type Option struct {
|
||||||
Help string
|
Help string
|
||||||
Optional bool
|
Optional bool
|
||||||
IsPassword bool
|
IsPassword bool
|
||||||
Examples OptionExamples
|
Examples OptionExamples `json:",omitempty"`
|
||||||
Value string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OptionExamples is a slice of examples
|
// OptionExamples is a slice of examples
|
||||||
|
|
Loading…
Reference in a new issue