Moves files

This commit is contained in:
Alexander Neumann
2017-07-23 14:19:13 +02:00
parent d1bd160b0a
commit 83d1a46526
284 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
// Package migrations contains migrations that can be applied to a repository and/or backend.
package migrations

View File

@@ -0,0 +1,21 @@
package migrations
import (
"context"
"restic"
)
// Migration implements a data migration.
type Migration interface {
// Check returns true if the migration can be applied to a repo.
Check(context.Context, restic.Repository) (bool, error)
// Apply runs the migration.
Apply(context.Context, restic.Repository) error
// Name returns a short name.
Name() string
// Descr returns a description what the migration does.
Desc() string
}

View File

@@ -0,0 +1,8 @@
package migrations
// All contains all migrations.
var All []Migration
func register(m Migration) {
All = append(All, m)
}

View File

@@ -0,0 +1,118 @@
package migrations
import (
"context"
"fmt"
"os"
"path"
"restic"
"restic/backend"
"restic/backend/s3"
"restic/debug"
"restic/errors"
)
func init() {
register(&S3Layout{})
}
// S3Layout migrates a repository on an S3 backend from the "s3legacy" to the
// "default" layout.
type S3Layout struct{}
// Check tests whether the migration can be applied.
func (m *S3Layout) Check(ctx context.Context, repo restic.Repository) (bool, error) {
be, ok := repo.Backend().(*s3.Backend)
if !ok {
debug.Log("backend is not s3")
return false, nil
}
if be.Layout.Name() != "s3legacy" {
debug.Log("layout is not s3legacy")
return false, 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 {
printErr := func(err error) {
fmt.Fprintf(os.Stderr, "renaming file returned error: %v\n", err)
}
for name := range be.List(ctx, t) {
h := restic.Handle{Type: t, Name: name}
debug.Log("move %v", h)
retry(maxErrors, printErr, func() error {
return be.Rename(h, l)
})
}
return nil
}
// Apply runs the migration.
func (m *S3Layout) Apply(ctx context.Context, repo restic.Repository) error {
be, ok := repo.Backend().(*s3.Backend)
if !ok {
debug.Log("backend is not s3")
return errors.New("backend is not s3")
}
oldLayout := &backend.S3LegacyLayout{
Path: be.Path(),
Join: path.Join,
}
newLayout := &backend.DefaultLayout{
Path: be.Path(),
Join: path.Join,
}
be.Layout = oldLayout
for _, t := range []restic.FileType{
restic.SnapshotFile,
restic.DataFile,
restic.KeyFile,
restic.LockFile,
} {
err := m.moveFiles(ctx, be, newLayout, t)
if err != nil {
return err
}
}
be.Layout = newLayout
return nil
}
// Name returns the name for this migration.
func (m *S3Layout) Name() string {
return "s3_layout"
}
// Desc returns a short description what the migration does.
func (m *S3Layout) Desc() string {
return "move files from 's3legacy' to the 'default' repository layout"
}