github.com/mckael/restic@v0.8.3/internal/migrations/s3_layout.go (about) 1 package migrations 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path" 8 9 "github.com/restic/restic/internal/backend" 10 "github.com/restic/restic/internal/backend/s3" 11 "github.com/restic/restic/internal/debug" 12 "github.com/restic/restic/internal/errors" 13 "github.com/restic/restic/internal/restic" 14 ) 15 16 func init() { 17 register(&S3Layout{}) 18 } 19 20 // S3Layout migrates a repository on an S3 backend from the "s3legacy" to the 21 // "default" layout. 22 type S3Layout struct{} 23 24 // Check tests whether the migration can be applied. 25 func (m *S3Layout) Check(ctx context.Context, repo restic.Repository) (bool, error) { 26 be, ok := repo.Backend().(*s3.Backend) 27 if !ok { 28 debug.Log("backend is not s3") 29 return false, nil 30 } 31 32 if be.Layout.Name() != "s3legacy" { 33 debug.Log("layout is not s3legacy") 34 return false, nil 35 } 36 37 return true, nil 38 } 39 40 func retry(max int, fail func(err error), f func() error) error { 41 var err error 42 for i := 0; i < max; i++ { 43 err = f() 44 if err == nil { 45 return err 46 } 47 if fail != nil { 48 fail(err) 49 } 50 } 51 return err 52 } 53 54 // maxErrors for retrying renames on s3. 55 const maxErrors = 20 56 57 func (m *S3Layout) moveFiles(ctx context.Context, be *s3.Backend, l backend.Layout, t restic.FileType) error { 58 printErr := func(err error) { 59 fmt.Fprintf(os.Stderr, "renaming file returned error: %v\n", err) 60 } 61 62 return be.List(ctx, t, func(fi restic.FileInfo) error { 63 h := restic.Handle{Type: t, Name: fi.Name} 64 debug.Log("move %v", h) 65 66 return retry(maxErrors, printErr, func() error { 67 return be.Rename(h, l) 68 }) 69 }) 70 71 return nil 72 } 73 74 // Apply runs the migration. 75 func (m *S3Layout) Apply(ctx context.Context, repo restic.Repository) error { 76 be, ok := repo.Backend().(*s3.Backend) 77 if !ok { 78 debug.Log("backend is not s3") 79 return errors.New("backend is not s3") 80 } 81 82 oldLayout := &backend.S3LegacyLayout{ 83 Path: be.Path(), 84 Join: path.Join, 85 } 86 87 newLayout := &backend.DefaultLayout{ 88 Path: be.Path(), 89 Join: path.Join, 90 } 91 92 be.Layout = oldLayout 93 94 for _, t := range []restic.FileType{ 95 restic.SnapshotFile, 96 restic.DataFile, 97 restic.KeyFile, 98 restic.LockFile, 99 } { 100 err := m.moveFiles(ctx, be, newLayout, t) 101 if err != nil { 102 return err 103 } 104 } 105 106 be.Layout = newLayout 107 108 return nil 109 } 110 111 // Name returns the name for this migration. 112 func (m *S3Layout) Name() string { 113 return "s3_layout" 114 } 115 116 // Desc returns a short description what the migration does. 117 func (m *S3Layout) Desc() string { 118 return "move files from 's3legacy' to the 'default' repository layout" 119 }