Permalink
Browse files

Make all config file variables be settable in the environment

These are set in the form RCLONE_CONFIG_remote_option where remote is
the uppercased remote name and option is the uppercased config file
option name.  Note that RCLONE_CONFIG_remote_TYPE must be set if
defining a new remote.

Fixes #616
  • Loading branch information...
1 parent 0d75d25 commit f73ee5eade8516f0e956381ef5b9971460187d96 @ncw committed Dec 20, 2016
View
@@ -229,15 +229,15 @@ func NewFs(name, root string) (fs.Fs, error) {
if err != nil {
return nil, err
}
- account := fs.ConfigFile.MustValue(name, "account")
+ account := fs.ConfigFileGet(name, "account")
if account == "" {
return nil, errors.New("account not found")
}
- key := fs.ConfigFile.MustValue(name, "key")
+ key := fs.ConfigFileGet(name, "key")
if key == "" {
return nil, errors.New("key not found")
}
- endpoint := fs.ConfigFile.MustValue(name, "endpoint", defaultEndpoint)
+ endpoint := fs.ConfigFileGet(name, "endpoint", defaultEndpoint)
f := &Fs{
name: name,
bucket: bucket,
@@ -29,7 +29,7 @@ When uses with the -l flag it lists the types too.
`,
Run: func(command *cobra.Command, args []string) {
cmd.CheckArgs(0, 0, command, args)
- remotes := fs.ConfigFile.GetSectionList()
+ remotes := fs.ConfigFileSections()
sort.Strings(remotes)
maxlen := 1
for _, remote := range remotes {
@@ -39,7 +39,7 @@ When uses with the -l flag it lists the types too.
}
for _, remote := range remotes {
if listLong {
- remoteType := fs.ConfigFile.MustValue(remote, "type", "UNKNOWN")
+ remoteType := fs.ConfigFileGet(remote, "type", "UNKNOWN")
fmt.Printf("%-*s %s\n", maxlen+1, remote+":", remoteType)
} else {
fmt.Printf("%s:\n", remote)
View
@@ -48,19 +48,19 @@ func init() {
// NewFs contstructs an Fs from the path, container:path
func NewFs(name, rpath string) (fs.Fs, error) {
- mode, err := NewNameEncryptionMode(fs.ConfigFile.MustValue(name, "filename_encryption", "standard"))
+ mode, err := NewNameEncryptionMode(fs.ConfigFileGet(name, "filename_encryption", "standard"))
if err != nil {
return nil, err
}
- password := fs.ConfigFile.MustValue(name, "password", "")
+ password := fs.ConfigFileGet(name, "password", "")
if password == "" {
return nil, errors.New("password not set in config file")
}
password, err = fs.Reveal(password)
if err != nil {
return nil, errors.Wrap(err, "failed to decrypt password")
}
- salt := fs.ConfigFile.MustValue(name, "password2", "")
+ salt := fs.ConfigFileGet(name, "password2", "")
if salt != "" {
salt, err = fs.Reveal(salt)
if err != nil {
@@ -71,7 +71,7 @@ func NewFs(name, rpath string) (fs.Fs, error) {
if err != nil {
return nil, errors.Wrap(err, "failed to make cipher")
}
- remote := fs.ConfigFile.MustValue(name, "remote")
+ remote := fs.ConfigFileGet(name, "remote")
if strings.HasPrefix(remote, name+":") {
return nil, errors.New("can't point crypt remote at itself - check the value of the remote setting")
}
View
@@ -63,7 +63,7 @@ func init() {
// Configuration helper - called after the user has put in the defaults
func configHelper(name string) {
// See if already have a token
- token := fs.ConfigFile.MustValue(name, "token")
+ token := fs.ConfigFileGet(name, "token")
if token != "" {
fmt.Printf("Already have a dropbox token - refresh?\n")
if !fs.Confirm() {
@@ -86,9 +86,9 @@ func configHelper(name string) {
token = db.AccessToken()
// Stuff it in the config file if it has changed
- old := fs.ConfigFile.MustValue(name, "token")
+ old := fs.ConfigFileGet(name, "token")
if token != old {
- fs.ConfigFile.SetValue(name, "token", token)
+ fs.ConfigFileSet(name, "token", token)
fs.SaveConfig()
}
}
@@ -133,11 +133,11 @@ func (f *Fs) String() string {
func newDropbox(name string) (*dropbox.Dropbox, error) {
db := dropbox.NewDropbox()
- appKey := fs.ConfigFile.MustValue(name, "app_key")
+ appKey := fs.ConfigFileGet(name, "app_key")
if appKey == "" {
appKey = rcloneAppKey
}
- appSecret := fs.ConfigFile.MustValue(name, "app_secret")
+ appSecret := fs.ConfigFileGet(name, "app_secret")
if appSecret == "" {
appSecret = fs.MustReveal(rcloneEncryptedAppSecret)
}
@@ -162,7 +162,7 @@ func NewFs(name, root string) (fs.Fs, error) {
f.setRoot(root)
// Read the token from the config file
- token := fs.ConfigFile.MustValue(name, "token")
+ token := fs.ConfigFileGet(name, "token")
// Set our custom context which enables our custom transport for timeouts etc
db.SetContext(oauthutil.Context())
View
@@ -17,6 +17,7 @@ import (
"os"
"os/user"
"path"
+ "regexp"
"sort"
"strconv"
"strings"
@@ -47,8 +48,8 @@ const (
// Global
var (
- // ConfigFile is the config file data structure
- ConfigFile *goconfig.ConfigFile
+ // configData is the config file data structure
+ configData *goconfig.ConfigFile
// HomeDir is the home directory of the user
HomeDir = configHome()
// ConfigPath points to the config file
@@ -284,10 +285,10 @@ func LoadConfig() {
// Load configuration file.
var err error
- ConfigFile, err = loadConfigFile()
+ configData, err = loadConfigFile()
if err == errorConfigFileNotFound {
Log(nil, "Config file %q not found - using defaults", ConfigPath)
- ConfigFile, _ = goconfig.LoadFromReader(&bytes.Buffer{})
+ configData, _ = goconfig.LoadFromReader(&bytes.Buffer{})
} else if err != nil {
log.Fatalf("Failed to load config file %q: %v", ConfigPath, err)
}
@@ -483,7 +484,7 @@ func changeConfigPassword() {
// if configKey has been set, the file will be encrypted.
func SaveConfig() {
if len(configKey) == 0 {
- err := goconfig.SaveConfigFile(ConfigFile, ConfigPath)
+ err := goconfig.SaveConfigFile(configData, ConfigPath)
if err != nil {
log.Fatalf("Failed to save config file: %v", err)
}
@@ -494,7 +495,7 @@ func SaveConfig() {
return
}
var buf bytes.Buffer
- err := goconfig.SaveConfigData(ConfigFile, &buf)
+ err := goconfig.SaveConfigData(configData, &buf)
if err != nil {
log.Fatalf("Failed to save config file: %v", err)
}
@@ -545,7 +546,7 @@ func SaveConfig() {
// disk first and overwrites the given value only.
func ConfigSetValueAndSave(name, key, value string) (err error) {
// Set the value in config in case we fail to reload it
- ConfigFile.SetValue(name, key, value)
+ configData.SetValue(name, key, value)
// Reload the config file
reloadedConfigFile, err := loadConfigFile()
if err == errorConfigFileNotFound {
@@ -560,7 +561,7 @@ func ConfigSetValueAndSave(name, key, value string) (err error) {
return err
}
// Update the config file with the reloaded version
- ConfigFile = reloadedConfigFile
+ configData = reloadedConfigFile
// Set the value in the reloaded version
reloadedConfigFile.SetValue(name, key, value)
// Save it again
@@ -570,21 +571,21 @@ func ConfigSetValueAndSave(name, key, value string) (err error) {
// ShowRemotes shows an overview of the config file
func ShowRemotes() {
- remotes := ConfigFile.GetSectionList()
+ remotes := configData.GetSectionList()
if len(remotes) == 0 {
return
}
sort.Strings(remotes)
fmt.Printf("%-20s %s\n", "Name", "Type")
fmt.Printf("%-20s %s\n", "====", "====")
for _, remote := range remotes {
- fmt.Printf("%-20s %s\n", remote, ConfigFile.MustValue(remote, "type"))
+ fmt.Printf("%-20s %s\n", remote, ConfigFileGet(remote, "type"))
}
}
// ChooseRemote chooses a remote name
func ChooseRemote() string {
- remotes := ConfigFile.GetSectionList()
+ remotes := configData.GetSectionList()
sort.Strings(remotes)
return Choose("remote", remotes, nil, false)
}
@@ -703,15 +704,15 @@ func ShowRemote(name string) {
fmt.Printf("--------------------\n")
fmt.Printf("[%s]\n", name)
fs := MustFindByName(name)
- for _, key := range ConfigFile.GetKeyList(name) {
+ for _, key := range configData.GetKeyList(name) {
isPassword := false
for _, option := range fs.Options {
if option.Name == key && option.IsPassword {
isPassword = true
break
}
}
- value := ConfigFile.MustValue(name, key)
+ value := ConfigFileGet(name, key)
if isPassword && value != "" {
fmt.Printf("%s = *** ENCRYPTED ***\n", key)
} else {
@@ -730,7 +731,7 @@ func OkRemote(name string) bool {
case 'e':
return false
case 'd':
- ConfigFile.DeleteSection(name)
+ configData.DeleteSection(name)
return true
default:
ErrorLog(nil, "Bad choice %c", i)
@@ -741,7 +742,7 @@ func OkRemote(name string) bool {
// MustFindByName finds the RegInfo for the remote name passed in or
// exits with a fatal error.
func MustFindByName(name string) *RegInfo {
- fsType := ConfigFile.MustValue(name, "type")
+ fsType := ConfigFileGet(name, "type")
if fsType == "" {
log.Fatalf("Couldn't find type of fs for %q", name)
}
@@ -829,10 +830,10 @@ func fsOption() *Option {
// NewRemote make a new remote from its name
func NewRemote(name string) {
newType := ChooseOption(fsOption())
- ConfigFile.SetValue(name, "type", newType)
+ configData.SetValue(name, "type", newType)
fs := MustFind(newType)
for _, option := range fs.Options {
- ConfigFile.SetValue(name, option.Name, ChooseOption(&option))
+ configData.SetValue(name, option.Name, ChooseOption(&option))
}
RemoteConfig(name)
if OkRemote(name) {
@@ -849,12 +850,12 @@ func EditRemote(fs *RegInfo, name string) {
for {
for _, option := range fs.Options {
key := option.Name
- value := ConfigFile.MustValue(name, key)
+ value := ConfigFileGet(name, key)
fmt.Printf("Value %q = %q\n", key, value)
fmt.Printf("Edit? (y/n)>\n")
if Confirm() {
newValue := ChooseOption(&option)
- ConfigFile.SetValue(name, key, newValue)
+ configData.SetValue(name, key, newValue)
}
}
RemoteConfig(name)
@@ -867,14 +868,14 @@ func EditRemote(fs *RegInfo, name string) {
// DeleteRemote gets the user to delete a remote
func DeleteRemote(name string) {
- ConfigFile.DeleteSection(name)
+ configData.DeleteSection(name)
SaveConfig()
}
// EditConfig edits the config file interactively
func EditConfig() {
for {
- haveRemotes := len(ConfigFile.GetSectionList()) != 0
+ haveRemotes := len(configData.GetSectionList()) != 0
what := []string{"eEdit existing remote", "nNew remote", "dDelete remote", "sSet configuration password", "qQuit config"}
if haveRemotes {
fmt.Printf("Current remotes:\n\n")
@@ -981,10 +982,87 @@ func Authorize(args []string) {
defer DeleteRemote(name)
// Indicate that we want fully automatic configuration.
- ConfigFile.SetValue(name, ConfigAutomatic, "yes")
+ configData.SetValue(name, ConfigAutomatic, "yes")
if len(args) == 3 {
- ConfigFile.SetValue(name, ConfigClientID, args[1])
- ConfigFile.SetValue(name, ConfigClientSecret, args[2])
+ configData.SetValue(name, ConfigClientID, args[1])
+ configData.SetValue(name, ConfigClientSecret, args[2])
}
fs.Config(name)
}
+
+// configToEnv converts an config section and name, eg ("myremote",
+// "ignore-size") into an environment name
+// "RCLONE_CONFIG_MYREMOTE_IGNORE_SIZE"
+func configToEnv(section, name string) string {
+ return "RCLONE_CONFIG_" + strings.ToUpper(strings.Replace(section+"_"+name, "-", "_", -1))
+}
+
+// ConfigFileGet gets the config key under section returning the
+// default or empty string if not set.
+//
+// It looks up defaults in the environment if they are present
+func ConfigFileGet(section, key string, defaultVal ...string) string {
+ envKey := configToEnv(section, key)
+ newValue, found := os.LookupEnv(envKey)
+ if found {
+ defaultVal = []string{newValue}
+ }
+ return configData.MustValue(section, key, defaultVal...)
+}
+
+// ConfigFileGetBool gets the config key under section returning the
+// default or false if not set.
+//
+// It looks up defaults in the environment if they are present
+func ConfigFileGetBool(section, key string, defaultVal ...bool) bool {
+ envKey := configToEnv(section, key)
+ newValue, found := os.LookupEnv(envKey)
+ if found {
+ newBool, err := strconv.ParseBool(newValue)
+ if err != nil {
+ ErrorLog(nil, "Couldn't parse %q into bool - ignoring: %v", envKey, err)
+ } else {
+ defaultVal = []bool{newBool}
+ }
+ }
+ return configData.MustBool(section, key, defaultVal...)
+}
+
+// ConfigFileGetInt gets the config key under section returning the
+// default or 0 if not set.
+//
+// It looks up defaults in the environment if they are present
+func ConfigFileGetInt(section, key string, defaultVal ...int) int {
+ envKey := configToEnv(section, key)
+ newValue, found := os.LookupEnv(envKey)
+ if found {
+ newInt, err := strconv.Atoi(newValue)
+ if err != nil {
+ ErrorLog(nil, "Couldn't parse %q into int - ignoring: %v", envKey, err)
+ } else {
+ defaultVal = []int{newInt}
+ }
+ }
+ return configData.MustInt(section, key, defaultVal...)
+}
+
+// ConfigFileSet sets the key in section to value. It doesn't save
+// the config file.
+func ConfigFileSet(section, key, value string) {
+ configData.SetValue(section, key, value)
+}
+
+var matchEnv = regexp.MustCompile(`^RCLONE_CONFIG_(.*?)_TYPE=.*$`)
+
+// ConfigFileSections returns the sections in the config file
+// including any defined by environment variables.
+func ConfigFileSections() []string {
+ sections := configData.GetSectionList()
+ for _, item := range os.Environ() {
+ matches := matchEnv.FindStringSubmatch(item)
+ if len(matches) == 2 {
+ sections = append(sections, strings.ToLower(matches[1]))
+ }
+ }
+ return sections
+}
View
@@ -430,9 +430,8 @@ func ParseRemote(path string) (fsInfo *RegInfo, configName, fsPath string, err e
fsName, configName, fsPath = "local", "local", path
if parts != nil && !isDriveLetter(parts[1]) {
configName, fsPath = parts[1], parts[2]
- var err error
- fsName, err = ConfigFile.GetValue(configName, "type")
- if err != nil {
+ fsName = ConfigFileGet(configName, "type")
+ if fsName == "" {
return nil, "", "", ErrorNotFoundInConfigFile
}
}
@@ -71,7 +71,7 @@ func TestInit(t *testing.T) {
fs.LoadConfig()
// Set extra config if supplied
for _, item := range ExtraConfig {
- fs.ConfigFile.SetValue(item.Name, item.Key, item.Value)
+ fs.ConfigFileSet(item.Name, item.Key, item.Value)
}
fs.Config.Verbose = *verbose
fs.Config.Quiet = !*verbose
Oops, something went wrong.

0 comments on commit f73ee5e

Please sign in to comment.