2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-02 11:46:36 +00:00

Merge pull request #1075 from restic/migrate-s3-continue

s3: Improve migration to new layout
This commit is contained in:
Alexander Neumann 2017-07-03 19:47:22 +02:00
commit ed2999a163
4 changed files with 60 additions and 6 deletions

View File

@ -4,6 +4,12 @@ released version of restic from the perspective of the user.
Important Changes in 0.X.Y Important Changes in 0.X.Y
========================== ==========================
* The `migrate` command for chaning the `s3legacy` layout to the `default`
layout for s3 backends has been improved: It can now be restarted with
`restic migrate --force s3_layout` and automatically retries operations on
error.
https://github.com/restic/restic/issues/1073
https://github.com/restic/restic/pull/1075
Important Changes in 0.7.0 Important Changes in 0.7.0
========================== ==========================

View File

@ -21,12 +21,15 @@ name is explicitely given, a list of migrations that can be applied is printed.
// MigrateOptions bundles all options for the 'check' command. // MigrateOptions bundles all options for the 'check' command.
type MigrateOptions struct { type MigrateOptions struct {
Force bool
} }
var migrateOptions MigrateOptions var migrateOptions MigrateOptions
func init() { func init() {
cmdRoot.AddCommand(cmdMigrate) cmdRoot.AddCommand(cmdMigrate)
f := cmdMigrate.Flags()
f.BoolVarP(&migrateOptions.Force, "force", "f", false, `apply a migration a second time`)
} }
func checkMigrations(opts MigrateOptions, gopts GlobalOptions, repo restic.Repository) error { func checkMigrations(opts MigrateOptions, gopts GlobalOptions, repo restic.Repository) error {
@ -59,10 +62,14 @@ func applyMigrations(opts MigrateOptions, gopts GlobalOptions, repo restic.Repos
} }
if !ok { if !ok {
Warnf("migration %v cannot be applied: check failed\n", m.Name()) if !opts.Force {
Warnf("migration %v cannot be applied: check failed\nIf you want to apply this migration anyway, re-run with option --force\n", m.Name())
continue continue
} }
Warnf("check for migration %v failed, continuing anyway\n", m.Name())
}
Printf("applying migration %v...\n", m.Name()) Printf("applying migration %v...\n", m.Name())
if err = m.Apply(ctx, repo); err != nil { if err = m.Apply(ctx, repo); err != nil {
Warnf("migration %v failed: %v\n", m.Name(), err) Warnf("migration %v failed: %v\n", m.Name(), err)

View File

@ -453,10 +453,20 @@ func (be *Backend) Rename(h restic.Handle, l backend.Layout) error {
oldname := be.Filename(h) oldname := be.Filename(h)
newname := l.Filename(h) newname := l.Filename(h)
if oldname == newname {
debug.Log(" %v is already renamed", newname)
return nil
}
debug.Log(" %v -> %v", oldname, newname) debug.Log(" %v -> %v", oldname, newname)
coreClient := minio.Core{Client: be.client} coreClient := minio.Core{Client: be.client}
err := coreClient.CopyObject(be.cfg.Bucket, newname, path.Join(be.cfg.Bucket, oldname), minio.CopyConditions{}) err := coreClient.CopyObject(be.cfg.Bucket, newname, path.Join(be.cfg.Bucket, oldname), minio.CopyConditions{})
if err != nil && be.IsNotExist(err) {
debug.Log("copy failed: %v, seems to already have been renamed", err)
return nil
}
if err != nil { if err != nil {
debug.Log("copy failed: %v", err) debug.Log("copy failed: %v", err)
return err return err

View File

@ -2,6 +2,8 @@ package migrations
import ( import (
"context" "context"
"fmt"
"os"
"path" "path"
"restic" "restic"
"restic/backend" "restic/backend"
@ -34,13 +36,35 @@ func (m *S3Layout) Check(ctx context.Context, repo restic.Repository) (bool, err
return true, nil return true, nil
} }
func retry(max int, fail func(err error), f func() error) error {
var err error
for i := 0; i < max; i++ {
err = f()
if err == nil {
return err
}
if fail != nil {
fail(err)
}
}
return err
}
// maxErrors for retrying renames on s3.
const maxErrors = 20
func (m *S3Layout) moveFiles(ctx context.Context, be *s3.Backend, l backend.Layout, t restic.FileType) error { func (m *S3Layout) moveFiles(ctx context.Context, be *s3.Backend, l backend.Layout, t restic.FileType) error {
printErr := func(err error) {
fmt.Fprintf(os.Stderr, "renaming file returned error: %v\n", err)
}
for name := range be.List(ctx, t) { for name := range be.List(ctx, t) {
h := restic.Handle{Type: t, Name: name} h := restic.Handle{Type: t, Name: name}
debug.Log("move %v", h) debug.Log("move %v", h)
if err := be.Rename(h, l); err != nil {
return err retry(maxErrors, printErr, func() error {
} return be.Rename(h, l)
})
} }
return nil return nil
@ -54,15 +78,22 @@ func (m *S3Layout) Apply(ctx context.Context, repo restic.Repository) error {
return errors.New("backend is not s3") return errors.New("backend is not s3")
} }
oldLayout := &backend.S3LegacyLayout{
Path: be.Path(),
Join: path.Join,
}
newLayout := &backend.DefaultLayout{ newLayout := &backend.DefaultLayout{
Path: be.Path(), Path: be.Path(),
Join: path.Join, Join: path.Join,
} }
be.Layout = oldLayout
for _, t := range []restic.FileType{ for _, t := range []restic.FileType{
restic.KeyFile,
restic.SnapshotFile, restic.SnapshotFile,
restic.DataFile, restic.DataFile,
restic.KeyFile,
restic.LockFile, restic.LockFile,
} { } {
err := m.moveFiles(ctx, be, newLayout, t) err := m.moveFiles(ctx, be, newLayout, t)