From 0c6f816a494f8c571b0683f8482e3066bd78dd1f Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 20 Aug 2015 21:07:00 +0100 Subject: [PATCH] Implement --retries flag - fixes #109 --- fs/accounting.go | 7 ++++ rclone.go | 101 ++++++++++++++++++++++------------------------- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/fs/accounting.go b/fs/accounting.go index 5f2dfb8a4..a3ce0c9b0 100644 --- a/fs/accounting.go +++ b/fs/accounting.go @@ -134,6 +134,13 @@ func (s *StatsInfo) ResetCounters() { s.transfers = 0 } +// ResetErrors sets the errors count to 0 +func (s *StatsInfo) ResetErrors() { + s.lock.RLock() + defer s.lock.RUnlock() + s.errors = 0 +} + // Errored returns whether there have been any errors func (s *StatsInfo) Errored() bool { s.lock.RLock() diff --git a/rclone.go b/rclone.go index 35756e5da..e51c1b29e 100644 --- a/rclone.go +++ b/rclone.go @@ -31,16 +31,18 @@ var ( statsInterval = pflag.DurationP("stats", "", time.Minute*1, "Interval to print stats (0 to disable)") version = pflag.BoolP("version", "V", false, "Print the version number") logFile = pflag.StringP("log-file", "", "", "Log everything to this file") + retries = pflag.IntP("retries", "", 3, "Retry operations this many times if they fail") ) type Command struct { Name string Help string ArgsHelp string - Run func(fdst, fsrc fs.Fs) + Run func(fdst, fsrc fs.Fs) error MinArgs int MaxArgs int NoStats bool + Retry bool } // checkArgs checks there are enough arguments and prints a message if not @@ -64,14 +66,12 @@ var Commands = []Command{ Copy the source to the destination. Doesn't transfer unchanged files, testing by size and modification time or MD5SUM. Doesn't delete files from the destination.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.Sync(fdst, fsrc, false) - if err != nil { - log.Fatalf("Failed to copy: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.Sync(fdst, fsrc, false) }, MinArgs: 2, MaxArgs: 2, + Retry: true, }, { Name: "sync", @@ -82,25 +82,20 @@ var Commands = []Command{ modification time or MD5SUM. Destination is updated to match source, including deleting files if necessary. Since this can cause data loss, test first with the --dry-run flag.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.Sync(fdst, fsrc, true) - if err != nil { - log.Fatalf("Failed to sync: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.Sync(fdst, fsrc, true) }, MinArgs: 2, MaxArgs: 2, + Retry: true, }, { Name: "ls", ArgsHelp: "[remote:path]", Help: ` List all the objects in the the path with size and path.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.List(fdst, os.Stdout) - if err != nil { - log.Fatalf("Failed to list: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.List(fdst, os.Stdout) }, MinArgs: 1, MaxArgs: 1, @@ -110,11 +105,8 @@ var Commands = []Command{ ArgsHelp: "[remote:path]", Help: ` List all directories/containers/buckets in the the path.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.ListDir(fdst, os.Stdout) - if err != nil { - log.Fatalf("Failed to listdir: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.ListDir(fdst, os.Stdout) }, MinArgs: 1, MaxArgs: 1, @@ -125,11 +117,8 @@ var Commands = []Command{ Help: ` List all the objects in the the path with modification time, size and path.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.ListLong(fdst, os.Stdout) - if err != nil { - log.Fatalf("Failed to list long: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.ListLong(fdst, os.Stdout) }, MinArgs: 1, MaxArgs: 1, @@ -140,11 +129,8 @@ var Commands = []Command{ Help: ` Produces an md5sum file for all the objects in the path. This is in the same format as the standard md5sum tool produces.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.Md5sum(fdst, os.Stdout) - if err != nil { - log.Fatalf("Failed to list: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.Md5sum(fdst, os.Stdout) }, MinArgs: 1, MaxArgs: 1, @@ -154,14 +140,12 @@ var Commands = []Command{ ArgsHelp: "remote:path", Help: ` Make the path if it doesn't already exist`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.Mkdir(fdst) - if err != nil { - log.Fatalf("Failed to mkdir: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.Mkdir(fdst) }, MinArgs: 1, MaxArgs: 1, + Retry: true, }, { Name: "rmdir", @@ -169,28 +153,24 @@ var Commands = []Command{ Help: ` Remove the path. Note that you can't remove a path with objects in it, use purge for that.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.Rmdir(fdst) - if err != nil { - log.Fatalf("Failed to rmdir: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.Rmdir(fdst) }, MinArgs: 1, MaxArgs: 1, + Retry: true, }, { Name: "purge", ArgsHelp: "remote:path", Help: ` Remove the path and all of its contents.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.Purge(fdst) - if err != nil { - log.Fatalf("Failed to purge: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.Purge(fdst) }, MinArgs: 1, MaxArgs: 1, + Retry: true, }, { Name: "check", @@ -199,11 +179,8 @@ var Commands = []Command{ Checks the files in the source and destination match. It compares sizes and MD5SUMs and prints a report of files which don't match. It doesn't alter the source or destination.`, - Run: func(fdst, fsrc fs.Fs) { - err := fs.Check(fdst, fsrc) - if err != nil { - log.Fatalf("Failed to check: %v", err) - } + Run: func(fdst, fsrc fs.Fs) error { + return fs.Check(fdst, fsrc) }, MinArgs: 2, MaxArgs: 2, @@ -212,8 +189,9 @@ var Commands = []Command{ Name: "config", Help: ` Enter an interactive configuration session.`, - Run: func(fdst, fsrc fs.Fs) { + Run: func(fdst, fsrc fs.Fs) error { fs.EditConfig() + return nil }, NoStats: true, }, @@ -376,7 +354,24 @@ func main() { // Run the actual command if command.Run != nil { - command.Run(fdst, fsrc) + var err error + for try := 1; try <= *retries; try++ { + err = command.Run(fdst, fsrc) + if !command.Retry || (err == nil && !fs.Stats.Errored()) { + break + } + if err != nil { + fs.Log(nil, "Attempt %d/%d failed with %d errors and: %v", try, *retries, fs.Stats.GetErrors(), err) + } else { + fs.Log(nil, "Attempt %d/%d failed with %d errors", try, *retries, fs.Stats.GetErrors()) + } + if try < *retries { + fs.Stats.ResetErrors() + } + } + if err != nil { + log.Fatalf("Failed to %s: %v", command.Name, err) + } if !command.NoStats && (!fs.Config.Quiet || fs.Stats.Errored() || *statsInterval > 0) { fmt.Fprintln(os.Stderr, fs.Stats) }