backends: remove log.Fatal and replace with error returns #5234

This changes the Config interface so that it returns an error.
This commit is contained in:
Nick Craig-Wood 2021-04-06 21:27:34 +01:00
parent ef3c350686
commit b78c9a65fa
28 changed files with 179 additions and 166 deletions

View File

@ -16,7 +16,6 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"path"
"strings"
@ -70,11 +69,12 @@ func init() {
Prefix: "acd",
Description: "Amazon Drive",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
err := oauthutil.Config(ctx, "amazon cloud drive", name, m, acdConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: "checkpoint",

View File

@ -17,7 +17,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"path"
@ -84,7 +83,7 @@ func init() {
Name: "box",
Description: "Box",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
jsonFile, ok := m.Get("box_config_file")
boxSubType, boxSubTypeOk := m.Get("box_sub_type")
boxAccessToken, boxAccessTokenOk := m.Get("access_token")
@ -93,15 +92,16 @@ func init() {
if ok && boxSubTypeOk && jsonFile != "" && boxSubType != "" {
err = refreshJWTToken(ctx, jsonFile, boxSubType, name, m)
if err != nil {
log.Fatalf("Failed to configure token with jwt authentication: %v", err)
return errors.Wrap(err, "failed to configure token with jwt authentication")
}
// Else, if not using an access token, use oauth2
} else if boxAccessToken == "" || !boxAccessTokenOk {
err = oauthutil.Config(ctx, "box", name, m, oauthConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token with oauth authentication: %v", err)
return errors.Wrap(err, "failed to configure token with oauth authentication")
}
}
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: "root_folder_id",

View File

@ -836,7 +836,7 @@ func newRun() *run {
if uploadDir == "" {
r.tmpUploadDir, err = ioutil.TempDir("", "rclonecache-tmp")
if err != nil {
log.Fatalf("Failed to create temp dir: %v", err)
panic(fmt.Sprintf("Failed to create temp dir: %v", err))
}
} else {
r.tmpUploadDir = uploadDir

View File

@ -14,7 +14,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"mime"
"net/http"
"path"
@ -183,13 +182,12 @@ func init() {
Description: "Google Drive",
NewFs: NewFs,
CommandHelp: commandHelp,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
// Parse config into Options struct
opt := new(Options)
err := configstruct.Set(m, opt)
if err != nil {
fs.Errorf(nil, "Couldn't parse config into struct: %v", err)
return
return errors.Wrap(err, "couldn't parse config into struct")
}
// Fill in the scopes
@ -202,13 +200,14 @@ func init() {
if opt.ServiceAccountFile == "" && opt.ServiceAccountCredentials == "" {
err = oauthutil.Config(ctx, "drive", name, m, driveConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
}
err = configTeamDrive(ctx, opt, m, name)
if err != nil {
log.Fatalf("Failed to configure Shared Drive: %v", err)
return errors.Wrap(err, "failed to configure Shared Drive")
}
return nil
},
Options: append(driveOAuthOptions(), []fs.Option{{
Name: "scope",
@ -522,7 +521,7 @@ If this flag is set then rclone will ignore shortcut files completely.
} {
for mimeType, extension := range m {
if err := mime.AddExtensionType(extension, mimeType); err != nil {
log.Fatalf("Failed to register MIME type %q: %v", mimeType, err)
fs.Errorf("Failed to register MIME type %q: %v", mimeType, err)
}
}
}

View File

@ -25,7 +25,6 @@ import (
"context"
"fmt"
"io"
"log"
"path"
"regexp"
"strings"
@ -144,7 +143,7 @@ func init() {
Name: "dropbox",
Description: "Dropbox",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
opt := oauthutil.Options{
NoOffline: true,
OAuth2Opts: []oauth2.AuthCodeOption{
@ -153,8 +152,9 @@ func init() {
}
err := oauthutil.Config(ctx, "dropbox", name, m, getOauthConfig(m), &opt)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: "chunk_size",

View File

@ -35,9 +35,7 @@ func init() {
fs.Register(&fs.RegInfo{
Name: "fichier",
Description: "1Fichier",
Config: func(ctx context.Context, name string, config configmap.Mapper) {
},
NewFs: NewFs,
NewFs: NewFs,
Options: []fs.Option{{
Help: "Your API Key, get it from https://1fichier.com/console/params.pl",
Name: "api_key",

View File

@ -19,7 +19,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"path"
"strings"
@ -76,17 +75,18 @@ func init() {
Prefix: "gcs",
Description: "Google Cloud Storage (this is not Google Drive)",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
saFile, _ := m.Get("service_account_file")
saCreds, _ := m.Get("service_account_credentials")
anonymous, _ := m.Get("anonymous")
if saFile != "" || saCreds != "" || anonymous == "true" {
return
return nil
}
err := oauthutil.Config(ctx, "google cloud storage", name, m, storageConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: "project_number",

View File

@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io"
golog "log"
"net/http"
"net/url"
"path"
@ -78,13 +77,12 @@ func init() {
Prefix: "gphotos",
Description: "Google Photos",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
// Parse config into Options struct
opt := new(Options)
err := configstruct.Set(m, opt)
if err != nil {
fs.Errorf(nil, "Couldn't parse config into struct: %v", err)
return
return errors.Wrap(err, "couldn't parse config into struct")
}
// Fill in the scopes
@ -97,7 +95,7 @@ func init() {
// Do the oauth
err = oauthutil.Config(ctx, "google photos", name, m, oauthConfig, nil)
if err != nil {
golog.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
// Warn the user
@ -108,6 +106,7 @@ func init() {
`)
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: "read_only",

View File

@ -11,7 +11,6 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
@ -56,11 +55,12 @@ func init() {
Name: "hubic",
Description: "Hubic",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
err := oauthutil.Config(ctx, "hubic", name, m, oauthConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: append(oauthutil.SharedOptions, swift.SharedOptions...),
})

View File

@ -10,7 +10,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net/http"
"net/url"
@ -87,12 +86,12 @@ func init() {
Name: "jottacloud",
Description: "Jottacloud",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
refresh := false
if version, ok := m.Get("configVersion"); ok {
ver, err := strconv.Atoi(version)
if err != nil {
log.Fatalf("Failed to parse config version - corrupted config")
return errors.Wrap(err, "failed to parse config version - corrupted config")
}
refresh = (ver != configVersion) && (ver != v1configVersion)
}
@ -104,7 +103,7 @@ func init() {
if ok && tokenString != "" {
fmt.Printf("Already have a token - refresh?\n")
if !config.Confirm(false) {
return
return nil
}
}
}
@ -116,11 +115,13 @@ func init() {
switch config.ChooseNumber("Your choice", 1, 3) {
case 1:
v2config(ctx, name, m)
return v2config(ctx, name, m)
case 2:
v1config(ctx, name, m)
return v1config(ctx, name, m)
case 3:
teliaCloudConfig(ctx, name, m)
return teliaCloudConfig(ctx, name, m)
default:
return errors.New("unknown config choice")
}
},
Options: []fs.Option{{
@ -242,7 +243,7 @@ func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, err
return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
}
func teliaCloudConfig(ctx context.Context, name string, m configmap.Mapper) {
func teliaCloudConfig(ctx context.Context, name string, m configmap.Mapper) error {
teliaCloudOauthConfig := &oauth2.Config{
Endpoint: oauth2.Endpoint{
AuthURL: teliaCloudAuthURL,
@ -255,15 +256,14 @@ func teliaCloudConfig(ctx context.Context, name string, m configmap.Mapper) {
err := oauthutil.Config(ctx, "jottacloud", name, m, teliaCloudOauthConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return
return errors.Wrap(err, "failed to configure token")
}
fmt.Printf("\nDo you want to use a non standard device/mountpoint e.g. for accessing files uploaded using the official Jottacloud client?\n\n")
if config.Confirm(false) {
oAuthClient, _, err := oauthutil.NewClient(ctx, name, m, teliaCloudOauthConfig)
if err != nil {
log.Fatalf("Failed to load oAuthClient: %s", err)
return errors.Wrap(err, "failed to load oAuthClient")
}
srv := rest.NewClient(oAuthClient).SetRoot(rootURL)
@ -271,7 +271,7 @@ func teliaCloudConfig(ctx context.Context, name string, m configmap.Mapper) {
device, mountpoint, err := setupMountpoint(ctx, srv, apiSrv)
if err != nil {
log.Fatalf("Failed to setup mountpoint: %s", err)
return errors.Wrap(err, "failed to setup mountpoint")
}
m.Set(configDevice, device)
m.Set(configMountpoint, mountpoint)
@ -280,17 +280,18 @@ func teliaCloudConfig(ctx context.Context, name string, m configmap.Mapper) {
m.Set("configVersion", strconv.Itoa(configVersion))
m.Set(configClientID, teliaCloudClientID)
m.Set(configTokenURL, teliaCloudTokenURL)
return nil
}
// v1config configure a jottacloud backend using legacy authentication
func v1config(ctx context.Context, name string, m configmap.Mapper) {
func v1config(ctx context.Context, name string, m configmap.Mapper) error {
srv := rest.NewClient(fshttp.NewClient(ctx))
fmt.Printf("\nDo you want to create a machine specific API key?\n\nRclone has it's own Jottacloud API KEY which works fine as long as one only uses rclone on a single machine. When you want to use rclone with this account on more than one machine it's recommended to create a machine specific API key. These keys can NOT be shared between machines.\n\n")
if config.Confirm(false) {
deviceRegistration, err := registerDevice(ctx, srv)
if err != nil {
log.Fatalf("Failed to register device: %v", err)
return errors.Wrap(err, "failed to register device")
}
m.Set(configClientID, deviceRegistration.ClientID)
@ -318,18 +319,18 @@ func v1config(ctx context.Context, name string, m configmap.Mapper) {
token, err := doAuthV1(ctx, srv, username, password)
if err != nil {
log.Fatalf("Failed to get oauth token: %s", err)
return errors.Wrap(err, "failed to get oauth token")
}
err = oauthutil.PutToken(name, m, &token, true)
if err != nil {
log.Fatalf("Error while saving token: %s", err)
return errors.Wrap(err, "error while saving token")
}
fmt.Printf("\nDo you want to use a non standard device/mountpoint e.g. for accessing files uploaded using the official Jottacloud client?\n\n")
if config.Confirm(false) {
oAuthClient, _, err := oauthutil.NewClient(ctx, name, m, oauthConfig)
if err != nil {
log.Fatalf("Failed to load oAuthClient: %s", err)
return errors.Wrap(err, "failed to load oAuthClient")
}
srv = rest.NewClient(oAuthClient).SetRoot(rootURL)
@ -337,13 +338,14 @@ func v1config(ctx context.Context, name string, m configmap.Mapper) {
device, mountpoint, err := setupMountpoint(ctx, srv, apiSrv)
if err != nil {
log.Fatalf("Failed to setup mountpoint: %s", err)
return errors.Wrap(err, "failed to setup mountpoint")
}
m.Set(configDevice, device)
m.Set(configMountpoint, mountpoint)
}
m.Set("configVersion", strconv.Itoa(v1configVersion))
return nil
}
// registerDevice register a new device for use with the jottacloud API
@ -418,7 +420,7 @@ func doAuthV1(ctx context.Context, srv *rest.Client, username, password string)
}
// v2config configure a jottacloud backend using the modern JottaCli token based authentication
func v2config(ctx context.Context, name string, m configmap.Mapper) {
func v2config(ctx context.Context, name string, m configmap.Mapper) error {
srv := rest.NewClient(fshttp.NewClient(ctx))
fmt.Printf("Generate a personal login token here: https://www.jottacloud.com/web/secure\n")
@ -430,31 +432,32 @@ func v2config(ctx context.Context, name string, m configmap.Mapper) {
token, err := doAuthV2(ctx, srv, loginToken, m)
if err != nil {
log.Fatalf("Failed to get oauth token: %s", err)
return errors.Wrap(err, "failed to get oauth token")
}
err = oauthutil.PutToken(name, m, &token, true)
if err != nil {
log.Fatalf("Error while saving token: %s", err)
return errors.Wrap(err, "error while saving token")
}
fmt.Printf("\nDo you want to use a non standard device/mountpoint e.g. for accessing files uploaded using the official Jottacloud client?\n\n")
if config.Confirm(false) {
oAuthClient, _, err := oauthutil.NewClient(ctx, name, m, oauthConfig)
if err != nil {
log.Fatalf("Failed to load oAuthClient: %s", err)
return errors.Wrap(err, "failed to load oAuthClient")
}
srv = rest.NewClient(oAuthClient).SetRoot(rootURL)
apiSrv := rest.NewClient(oAuthClient).SetRoot(apiURL)
device, mountpoint, err := setupMountpoint(ctx, srv, apiSrv)
if err != nil {
log.Fatalf("Failed to setup mountpoint: %s", err)
return errors.Wrap(err, "failed to setup mountpoint")
}
m.Set(configDevice, device)
m.Set(configMountpoint, mountpoint)
}
m.Set("configVersion", strconv.Itoa(configVersion))
return nil
}
// doAuthV2 runs the actual token request for V2 authentication

View File

@ -6,8 +6,8 @@ import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
"time"
"github.com/pkg/errors"
@ -48,7 +48,7 @@ func (w *BinWriter) Reader() io.Reader {
// WritePu16 writes a short as unsigned varint
func (w *BinWriter) WritePu16(val int) {
if val < 0 || val > 65535 {
log.Fatalf("Invalid UInt16 %v", val)
panic(fmt.Sprintf("Invalid UInt16 %v", val))
}
w.WritePu64(int64(val))
}
@ -56,7 +56,7 @@ func (w *BinWriter) WritePu16(val int) {
// WritePu32 writes a signed long as unsigned varint
func (w *BinWriter) WritePu32(val int64) {
if val < 0 || val > 4294967295 {
log.Fatalf("Invalid UInt32 %v", val)
panic(fmt.Sprintf("Invalid UInt32 %v", val))
}
w.WritePu64(val)
}
@ -64,7 +64,7 @@ func (w *BinWriter) WritePu32(val int64) {
// WritePu64 writes an unsigned (actually, signed) long as unsigned varint
func (w *BinWriter) WritePu64(val int64) {
if val < 0 {
log.Fatalf("Invalid UInt64 %v", val)
panic(fmt.Sprintf("Invalid UInt64 %v", val))
}
w.b.Write(w.a[:binary.PutUvarint(w.a, uint64(val))])
}
@ -123,7 +123,7 @@ func (r *BinReader) check(err error) bool {
r.err = err
}
if err != io.EOF {
log.Fatalf("Error parsing response: %v", err)
panic(fmt.Sprintf("Error parsing response: %v", err))
}
return false
}

View File

@ -9,7 +9,6 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"path"
@ -99,7 +98,7 @@ func init() {
Name: "onedrive",
Description: "Microsoft OneDrive",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
region, _ := m.Get("region")
graphURL := graphAPIEndpoint[region] + "/v1.0"
oauthConfig.Endpoint = oauth2.Endpoint{
@ -109,13 +108,12 @@ func init() {
ci := fs.GetConfig(ctx)
err := oauthutil.Config(ctx, "onedrive", name, m, oauthConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return
return errors.Wrap(err, "failed to configure token")
}
// Stop if we are running non-interactive config
if ci.AutoConfirm {
return
return nil
}
type driveResource struct {
@ -138,7 +136,7 @@ func init() {
oAuthClient, _, err := oauthutil.NewClient(ctx, name, m, oauthConfig)
if err != nil {
log.Fatalf("Failed to configure OneDrive: %v", err)
return errors.Wrap(err, "failed to configure OneDrive")
}
srv := rest.NewClient(oAuthClient)
@ -203,18 +201,17 @@ func init() {
sites := siteResponse{}
_, err := srv.CallJSON(ctx, &opts, nil, &sites)
if err != nil {
log.Fatalf("Failed to query available sites: %v", err)
return errors.Wrap(err, "failed to query available sites")
}
if len(sites.Sites) == 0 {
log.Fatalf("Search for '%s' returned no results", searchTerm)
} else {
fmt.Printf("Found %d sites, please select the one you want to use:\n", len(sites.Sites))
for index, site := range sites.Sites {
fmt.Printf("%d: %s (%s) id=%s\n", index, site.SiteName, site.SiteURL, site.SiteID)
}
siteID = sites.Sites[config.ChooseNumber("Chose drive to use:", 0, len(sites.Sites)-1)].SiteID
return errors.Errorf("search for %q returned no results", searchTerm)
}
fmt.Printf("Found %d sites, please select the one you want to use:\n", len(sites.Sites))
for index, site := range sites.Sites {
fmt.Printf("%d: %s (%s) id=%s\n", index, site.SiteName, site.SiteURL, site.SiteID)
}
siteID = sites.Sites[config.ChooseNumber("Chose drive to use:", 0, len(sites.Sites)-1)].SiteID
}
// if we use server-relative URL for finding the drive
@ -227,7 +224,7 @@ func init() {
site := siteResource{}
_, err := srv.CallJSON(ctx, &opts, nil, &site)
if err != nil {
log.Fatalf("Failed to query available site by relative path: %v", err)
return errors.Wrap(err, "failed to query available site by relative path")
}
siteID = site.SiteID
}
@ -247,7 +244,7 @@ func init() {
drives := drivesResponse{}
_, err := srv.CallJSON(ctx, &opts, nil, &drives)
if err != nil {
log.Fatalf("Failed to query available drives: %v", err)
return errors.Wrap(err, "failed to query available drives")
}
// Also call /me/drive as sometimes /me/drives doesn't return it #4068
@ -256,7 +253,7 @@ func init() {
meDrive := driveResource{}
_, err := srv.CallJSON(ctx, &opts, nil, &meDrive)
if err != nil {
log.Fatalf("Failed to query available drives: %v", err)
return errors.Wrap(err, "failed to query available drives")
}
found := false
for _, drive := range drives.Drives {
@ -273,14 +270,13 @@ func init() {
}
if len(drives.Drives) == 0 {
log.Fatalf("No drives found")
} else {
fmt.Printf("Found %d drives, please select the one you want to use:\n", len(drives.Drives))
for index, drive := range drives.Drives {
fmt.Printf("%d: %s (%s) id=%s\n", index, drive.DriveName, drive.DriveType, drive.DriveID)
}
finalDriveID = drives.Drives[config.ChooseNumber("Chose drive to use:", 0, len(drives.Drives)-1)].DriveID
return errors.New("no drives found")
}
fmt.Printf("Found %d drives, please select the one you want to use:\n", len(drives.Drives))
for index, drive := range drives.Drives {
fmt.Printf("%d: %s (%s) id=%s\n", index, drive.DriveName, drive.DriveType, drive.DriveID)
}
finalDriveID = drives.Drives[config.ChooseNumber("Chose drive to use:", 0, len(drives.Drives)-1)].DriveID
}
// Test the driveID and get drive type
@ -291,17 +287,18 @@ func init() {
var rootItem api.Item
_, err = srv.CallJSON(ctx, &opts, nil, &rootItem)
if err != nil {
log.Fatalf("Failed to query root for drive %s: %v", finalDriveID, err)
return errors.Wrapf(err, "failed to query root for drive %s", finalDriveID)
}
fmt.Printf("Found drive '%s' of type '%s', URL: %s\nIs that okay?\n", rootItem.Name, rootItem.ParentReference.DriveType, rootItem.WebURL)
// This does not work, YET :)
if !config.ConfirmWithConfig(ctx, m, "config_drive_ok", true) {
log.Fatalf("Cancelled by user")
return errors.New("cancelled by user")
}
m.Set(configDriveID, finalDriveID)
m.Set(configDriveType, rootItem.ParentReference.DriveType)
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: "region",

View File

@ -12,7 +12,6 @@ import (
"context"
"fmt"
"io"
"log"
"net/http"
"net/url"
"path"
@ -72,7 +71,7 @@ func init() {
Name: "pcloud",
Description: "Pcloud",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
optc := new(Options)
err := configstruct.Set(m, optc)
if err != nil {
@ -100,8 +99,9 @@ func init() {
}
err = oauthutil.Config(ctx, "pcloud", name, m, oauthConfig, &opt)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: config.ConfigEncoding,

View File

@ -20,7 +20,6 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
@ -78,11 +77,12 @@ func init() {
Name: "premiumizeme",
Description: "premiumize.me",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
err := oauthutil.Config(ctx, "premiumizeme", name, m, oauthConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: []fs.Option{{
Name: "api_key",

View File

@ -2,10 +2,10 @@ package putio
import (
"context"
"log"
"regexp"
"time"
"github.com/pkg/errors"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/config/configmap"
@ -60,14 +60,15 @@ func init() {
Name: "putio",
Description: "Put.io",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
opt := oauthutil.Options{
NoOffline: true,
}
err := oauthutil.Config(ctx, "putio", name, m, putioConfig, &opt)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: []fs.Option{{
Name: config.ConfigEncoding,

View File

@ -296,36 +296,32 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
}
// Config callback for 2FA
func Config(ctx context.Context, name string, m configmap.Mapper) {
func Config(ctx context.Context, name string, m configmap.Mapper) error {
ci := fs.GetConfig(ctx)
serverURL, ok := m.Get(configURL)
if !ok || serverURL == "" {
// If there's no server URL, it means we're trying an operation at the backend level, like a "rclone authorize seafile"
fmt.Print("\nOperation not supported on this remote.\nIf you need a 2FA code on your account, use the command:\n\nrclone config reconnect <remote name>:\n\n")
return
return errors.New("operation not supported on this remote. If you need a 2FA code on your account, use the command: nrclone config reconnect <remote name>: ")
}
// Stop if we are running non-interactive config
if ci.AutoConfirm {
return
return nil
}
u, err := url.Parse(serverURL)
if err != nil {
fs.Errorf(nil, "Invalid server URL %s", serverURL)
return
return errors.Errorf("invalid server URL %s", serverURL)
}
is2faEnabled, _ := m.Get(config2FA)
if is2faEnabled != "true" {
fmt.Println("Two-factor authentication is not enabled on this account.")
return
return errors.New("two-factor authentication is not enabled on this account")
}
username, _ := m.Get(configUser)
if username == "" {
fs.Errorf(nil, "A username is required")
return
return errors.New("a username is required")
}
password, _ := m.Get(configPassword)
@ -376,6 +372,7 @@ func Config(ctx context.Context, name string, m configmap.Mapper) {
break
}
}
return nil
}
// sets the AuthorizationToken up

View File

@ -77,7 +77,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"path"
@ -136,7 +135,7 @@ func init() {
Name: "sharefile",
Description: "Citrix Sharefile",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
oauthConfig := newOauthConfig("")
checkAuth := func(oauthConfig *oauth2.Config, auth *oauthutil.AuthResult) error {
if auth == nil || auth.Form == nil {
@ -157,8 +156,9 @@ func init() {
}
err := oauthutil.Config(ctx, "sharefile", name, m, oauthConfig, &opt)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: []fs.Option{{
Name: "upload_cutoff",

View File

@ -16,7 +16,6 @@ import (
"context"
"fmt"
"io"
"log"
"net/http"
"net/url"
"path"
@ -76,17 +75,17 @@ func init() {
Name: "sugarsync",
Description: "Sugarsync",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
opt := new(Options)
err := configstruct.Set(m, opt)
if err != nil {
log.Fatalf("Failed to read options: %v", err)
return errors.Wrap(err, "failed to read options")
}
if opt.RefreshToken != "" {
fmt.Printf("Already have a token - refresh?\n")
if !config.ConfirmWithConfig(ctx, m, "config_refresh_token", true) {
return
return nil
}
}
fmt.Printf("Username (email address)> ")
@ -114,10 +113,11 @@ func init() {
// return shouldRetry(ctx, resp, err)
//})
if err != nil {
log.Fatalf("Failed to get token: %v", err)
return errors.Wrap(err, "failed to get token")
}
opt.RefreshToken = resp.Header.Get("Location")
m.Set("refresh_token", opt.RefreshToken)
return nil
},
Options: []fs.Option{{
Name: "app_id",

View File

@ -7,7 +7,6 @@ import (
"context"
"fmt"
"io"
"log"
"path"
"strings"
"time"
@ -42,7 +41,7 @@ func init() {
Name: "tardigrade",
Description: "Tardigrade Decentralized Cloud Storage",
NewFs: NewFs,
Config: func(ctx context.Context, name string, configMapper configmap.Mapper) {
Config: func(ctx context.Context, name string, configMapper configmap.Mapper) error {
provider, _ := configMapper.Get(fs.ConfigProvider)
config.FileDeleteKey(name, fs.ConfigProvider)
@ -54,7 +53,7 @@ func init() {
// satelliteString contains always default and passphrase can be empty
if apiKey == "" {
return
return nil
}
satellite, found := satMap[satelliteString]
@ -64,12 +63,12 @@ func init() {
access, err := uplink.RequestAccessWithPassphrase(context.TODO(), satellite, apiKey, passphrase)
if err != nil {
log.Fatalf("Couldn't create access grant: %v", err)
return errors.Wrap(err, "couldn't create access grant")
}
serializedAccess, err := access.Serialize()
if err != nil {
log.Fatalf("Couldn't serialize access grant: %v", err)
return errors.Wrap(err, "couldn't serialize access grant")
}
configMapper.Set("satellite_address", satellite)
configMapper.Set("access_grant", serializedAccess)
@ -78,8 +77,9 @@ func init() {
config.FileDeleteKey(name, "api_key")
config.FileDeleteKey(name, "passphrase")
} else {
log.Fatalf("Invalid provider type: %s", provider)
return errors.Errorf("invalid provider type: %s", provider)
}
return nil
},
Options: []fs.Option{
{

View File

@ -41,9 +41,7 @@ func init() {
fs.Register(&fs.RegInfo{
Name: "uptobox",
Description: "Uptobox",
Config: func(ctx context.Context, name string, config configmap.Mapper) {
},
NewFs: NewFs,
NewFs: NewFs,
Options: []fs.Option{{
Help: "Your access Token, get it from https://uptobox.com/my_account",
Name: "access_token",

View File

@ -60,12 +60,12 @@ func init() {
Name: "yandex",
Description: "Yandex Disk",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
err := oauthutil.Config(ctx, "yandex", name, m, oauthConfig, nil)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return
return errors.Wrap(err, "failed to configure token")
}
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: config.ConfigEncoding,
@ -251,22 +251,22 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
token, err := oauthutil.GetToken(name, m)
if err != nil {
log.Fatalf("Couldn't read OAuth token (this should never happen).")
return nil, errors.Wrap(err, "couldn't read OAuth token")
}
if token.RefreshToken == "" {
log.Fatalf("Unable to get RefreshToken. If you are upgrading from older versions of rclone, please run `rclone config` and re-configure this backend.")
return nil, errors.New("unable to get RefreshToken. If you are upgrading from older versions of rclone, please run `rclone config` and re-configure this backend")
}
if token.TokenType != "OAuth" {
token.TokenType = "OAuth"
err = oauthutil.PutToken(name, m, token, false)
if err != nil {
log.Fatalf("Couldn't save OAuth token (this should never happen).")
return nil, errors.Wrap(err, "couldn't save OAuth token")
}
log.Printf("Automatically upgraded OAuth config.")
}
oAuthClient, _, err := oauthutil.NewClient(ctx, name, m, oauthConfig)
if err != nil {
log.Fatalf("Failed to configure Yandex: %v", err)
return nil, errors.Wrap(err, "failed to configure Yandex")
}
ci := fs.GetConfig(ctx)

View File

@ -7,7 +7,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"path"
@ -73,37 +72,41 @@ func init() {
Name: "zoho",
Description: "Zoho",
NewFs: NewFs,
Config: func(ctx context.Context, name string, m configmap.Mapper) {
Config: func(ctx context.Context, name string, m configmap.Mapper) error {
// Need to setup region before configuring oauth
setupRegion(m)
err := setupRegion(m)
if err != nil {
return err
}
opt := oauthutil.Options{
// No refresh token unless ApprovalForce is set
OAuth2Opts: []oauth2.AuthCodeOption{oauth2.ApprovalForce},
}
if err := oauthutil.Config(ctx, "zoho", name, m, oauthConfig, &opt); err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
// We need to rewrite the token type to "Zoho-oauthtoken" because Zoho wants
// it's own custom type
token, err := oauthutil.GetToken(name, m)
if err != nil {
log.Fatalf("Failed to read token: %v", err)
return errors.Wrap(err, "failed to read token")
}
if token.TokenType != "Zoho-oauthtoken" {
token.TokenType = "Zoho-oauthtoken"
err = oauthutil.PutToken(name, m, token, false)
if err != nil {
log.Fatalf("Failed to configure token: %v", err)
return errors.Wrap(err, "failed to configure token")
}
}
if fs.GetConfig(ctx).AutoConfirm {
return
return nil
}
if err = setupRoot(ctx, name, m); err != nil {
log.Fatalf("Failed to configure root directory: %v", err)
return errors.Wrap(err, "failed to configure root directory")
}
return nil
},
Options: append(oauthutil.SharedOptions, []fs.Option{{
Name: "region",
@ -164,15 +167,16 @@ type Object struct {
// ------------------------------------------------------------
func setupRegion(m configmap.Mapper) {
func setupRegion(m configmap.Mapper) error {
region, ok := m.Get("region")
if !ok || region == "" {
log.Fatalf("No region set\n")
return errors.New("no region set")
}
rootURL = fmt.Sprintf("https://workdrive.zoho.%s/api/v1", region)
accountsURL = fmt.Sprintf("https://accounts.zoho.%s", region)
oauthConfig.Endpoint.AuthURL = fmt.Sprintf("https://accounts.zoho.%s/oauth/v2/auth", region)
oauthConfig.Endpoint.TokenURL = fmt.Sprintf("https://accounts.zoho.%s/oauth/v2/token", region)
return nil
}
// ------------------------------------------------------------
@ -208,7 +212,7 @@ func listWorkspaces(ctx context.Context, teamID string, srv *rest.Client) ([]api
func setupRoot(ctx context.Context, name string, m configmap.Mapper) error {
oAuthClient, _, err := oauthutil.NewClient(ctx, name, m, oauthConfig)
if err != nil {
log.Fatalf("Failed to load oAuthClient: %s", err)
return errors.Wrap(err, "failed to load oAuthClient")
}
authSrv := rest.NewClient(oAuthClient).SetRoot(accountsURL)
opts := rest.Opts{
@ -377,7 +381,10 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
if err := configstruct.Set(m, opt); err != nil {
return nil, err
}
setupRegion(m)
err := setupRegion(m)
if err != nil {
return nil, err
}
root = parsePath(root)
oAuthClient, _, err := oauthutil.NewClient(ctx, name, m, oauthConfig)

View File

@ -42,9 +42,9 @@ var configCommand = &cobra.Command{
remotes and manage existing ones. You may also set or remove a
password to protect your configuration.
`,
Run: func(command *cobra.Command, args []string) {
RunE: func(command *cobra.Command, args []string) error {
cmd.CheckArgs(0, 0, command, args)
config.EditConfig(context.Background())
return config.EditConfig(context.Background())
},
}
@ -272,8 +272,7 @@ This normally means going through the interactive oauth flow again.
if fsInfo.Config == nil {
return errors.Errorf("%s: doesn't support Reconnect", configName)
}
fsInfo.Config(ctx, configName, config)
return nil
return fsInfo.Config(ctx, configName, config)
},
}

View File

@ -60,7 +60,10 @@ func Authorize(ctx context.Context, args []string, noAutoBrowser bool) error {
m.AddSetter(outM)
m.AddGetter(outM, configmap.PriorityNormal)
ri.Config(ctx, name, m)
err = ri.Config(ctx, name, m)
if err != nil {
return err
}
// Print the code for the user to paste
out := outM["token"]

View File

@ -455,7 +455,10 @@ func UpdateRemote(ctx context.Context, name string, keyValues rc.Params, doObscu
}
LoadedData().SetValue(name, k, vStr)
}
RemoteConfig(ctx, name)
err = RemoteConfig(ctx, name)
if err != nil {
return err
}
SaveConfig()
cache.ClearConfig(name) // remove any remotes based on this config from the cache
return nil

View File

@ -270,13 +270,14 @@ func OkRemote(name string) bool {
}
// RemoteConfig runs the config helper for the remote if needed
func RemoteConfig(ctx context.Context, name string) {
func RemoteConfig(ctx context.Context, name string) error {
fmt.Printf("Remote config\n")
f := mustFindByName(name)
if f.Config != nil {
m := fs.ConfigMap(f, name, nil)
f.Config(ctx, name, m)
return f.Config(ctx, name, m)
}
return nil
}
// matchProvider returns true if provider matches the providerConfig string.
@ -456,7 +457,7 @@ func editOptions(ri *fs.RegInfo, name string, isNew bool) {
}
// NewRemote make a new remote from its name
func NewRemote(ctx context.Context, name string) {
func NewRemote(ctx context.Context, name string) error {
var (
newType string
ri *fs.RegInfo
@ -476,16 +477,19 @@ func NewRemote(ctx context.Context, name string) {
LoadedData().SetValue(name, "type", newType)
editOptions(ri, name, true)
RemoteConfig(ctx, name)
err = RemoteConfig(ctx, name)
if err != nil {
return err
}
if OkRemote(name) {
SaveConfig()
return
return nil
}
EditRemote(ctx, ri, name)
return EditRemote(ctx, ri, name)
}
// EditRemote gets the user to edit a remote
func EditRemote(ctx context.Context, ri *fs.RegInfo, name string) {
func EditRemote(ctx context.Context, ri *fs.RegInfo, name string) error {
ShowRemote(name)
fmt.Printf("Edit remote\n")
for {
@ -495,7 +499,7 @@ func EditRemote(ctx context.Context, ri *fs.RegInfo, name string) {
}
}
SaveConfig()
RemoteConfig(ctx, name)
return RemoteConfig(ctx, name)
}
// DeleteRemote gets the user to delete a remote
@ -560,7 +564,7 @@ func ShowConfig() {
}
// EditConfig edits the config file interactively
func EditConfig(ctx context.Context) {
func EditConfig(ctx context.Context) (err error) {
for {
haveRemotes := len(LoadedData().GetSectionList()) != 0
what := []string{"eEdit existing remote", "nNew remote", "dDelete remote", "rRename remote", "cCopy remote", "sSet configuration password", "qQuit config"}
@ -577,9 +581,15 @@ func EditConfig(ctx context.Context) {
case 'e':
name := ChooseRemote()
fs := mustFindByName(name)
EditRemote(ctx, fs, name)
err = EditRemote(ctx, fs, name)
if err != nil {
return err
}
case 'n':
NewRemote(ctx, NewRemoteName())
err = NewRemote(ctx, NewRemoteName())
if err != nil {
return err
}
case 'd':
name := ChooseRemote()
DeleteRemote(name)
@ -590,8 +600,7 @@ func EditConfig(ctx context.Context) {
case 's':
SetPassword()
case 'q':
return
return nil
}
}
}

View File

@ -103,7 +103,7 @@ func TestCRUD(t *testing.T) {
"secret", // repeat
"y", // looks good, save
})
config.NewRemote(ctx, "test")
require.NoError(t, config.NewRemote(ctx, "test"))
assert.Equal(t, []string{"test"}, config.Data().GetSectionList())
assert.Equal(t, "config_test_remote", config.FileGet("test", "type"))
@ -146,7 +146,7 @@ func TestChooseOption(t *testing.T) {
assert.Equal(t, 1024, bits)
return "not very random password", nil
}
config.NewRemote(ctx, "test")
require.NoError(t, config.NewRemote(ctx, "test"))
assert.Equal(t, "false", config.FileGet("test", "bool"))
assert.Equal(t, "not very random password", obscure.MustReveal(config.FileGet("test", "pass")))
@ -158,7 +158,7 @@ func TestChooseOption(t *testing.T) {
"n", // not required
"y", // looks good, save
})
config.NewRemote(ctx, "test")
require.NoError(t, config.NewRemote(ctx, "test"))
assert.Equal(t, "true", config.FileGet("test", "bool"))
assert.Equal(t, "", config.FileGet("test", "pass"))
@ -175,7 +175,7 @@ func TestNewRemoteName(t *testing.T) {
"n", // not required
"y", // looks good, save
})
config.NewRemote(ctx, "test")
require.NoError(t, config.NewRemote(ctx, "test"))
config.ReadLine = makeReadLine([]string{
"test", // already exists

View File

@ -89,7 +89,7 @@ type RegInfo struct {
// the parent of that object and ErrorIsFile.
NewFs func(ctx context.Context, name string, root string, config configmap.Mapper) (Fs, error) `json:"-"`
// Function to call to help with config
Config func(ctx context.Context, name string, config configmap.Mapper) `json:"-"`
Config func(ctx context.Context, name string, config configmap.Mapper) error `json:"-"`
// Options for the Fs configuration
Options Options
// The command help, if any