mirror of
https://github.com/octoleo/restic.git
synced 2024-12-22 19:08:55 +00:00
Add tests for upgrade migration
This commit is contained in:
parent
a5f1d318ac
commit
8c244214bf
@ -15,6 +15,26 @@ func init() {
|
|||||||
register(&UpgradeRepoV2{})
|
register(&UpgradeRepoV2{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpgradeRepoV2Error struct {
|
||||||
|
UploadNewConfigError error
|
||||||
|
ReuploadOldConfigError error
|
||||||
|
|
||||||
|
BackupFilePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *UpgradeRepoV2Error) Error() string {
|
||||||
|
if err.ReuploadOldConfigError != nil {
|
||||||
|
return fmt.Sprintf("error uploading config (%v), re-uploading old config filed failed as well (%v), but there is a backup of the config file in %v", err.UploadNewConfigError, err.ReuploadOldConfigError, err.BackupFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("error uploading config (%v), re-uploaded old config was successful, there is a backup of the config file in %v", err.UploadNewConfigError, err.BackupFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *UpgradeRepoV2Error) Unwrap() error {
|
||||||
|
// consider the original upload error as the primary cause
|
||||||
|
return err.UploadNewConfigError
|
||||||
|
}
|
||||||
|
|
||||||
type UpgradeRepoV2 struct{}
|
type UpgradeRepoV2 struct{}
|
||||||
|
|
||||||
func (*UpgradeRepoV2) Name() string {
|
func (*UpgradeRepoV2) Name() string {
|
||||||
@ -69,7 +89,8 @@ func (m *UpgradeRepoV2) Apply(ctx context.Context, repo restic.Repository) error
|
|||||||
return fmt.Errorf("load config file failed: %w", err)
|
return fmt.Errorf("load config file failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(filepath.Join(tempdir, "config.old"), rawConfigFile, 0600)
|
backupFileName := filepath.Join(tempdir, "config")
|
||||||
|
err = ioutil.WriteFile(backupFileName, rawConfigFile, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("write config file backup to %v failed: %w", tempdir, err)
|
return fmt.Errorf("write config file backup to %v failed: %w", tempdir, err)
|
||||||
}
|
}
|
||||||
@ -77,14 +98,21 @@ func (m *UpgradeRepoV2) Apply(ctx context.Context, repo restic.Repository) error
|
|||||||
// run the upgrade
|
// run the upgrade
|
||||||
err = m.upgrade(ctx, repo)
|
err = m.upgrade(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// try contingency methods, reupload the original file
|
|
||||||
_ = repo.Backend().Remove(ctx, h)
|
// build an error we can return to the caller
|
||||||
uploadError := repo.Backend().Save(ctx, h, restic.NewByteReader(rawConfigFile, nil))
|
repoError := &UpgradeRepoV2Error{
|
||||||
if uploadError != nil {
|
UploadNewConfigError: err,
|
||||||
return fmt.Errorf("error uploading config (%w), re-uploading old config filed failed as well (%v) but there is a backup in %v", err, uploadError, tempdir)
|
BackupFilePath: backupFileName,
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("error uploading config (%w), re-uploadid old config, there is a backup in %v", err, tempdir)
|
// try contingency methods, reupload the original file
|
||||||
|
_ = repo.Backend().Remove(ctx, h)
|
||||||
|
err = repo.Backend().Save(ctx, h, restic.NewByteReader(rawConfigFile, nil))
|
||||||
|
if err != nil {
|
||||||
|
repoError.ReuploadOldConfigError = err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repoError
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = os.Remove(backupFileName)
|
_ = os.Remove(backupFileName)
|
||||||
|
112
internal/migrations/upgrade_repo_v2_test.go
Normal file
112
internal/migrations/upgrade_repo_v2_test.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/restic/restic/internal/errors"
|
||||||
|
"github.com/restic/restic/internal/repository"
|
||||||
|
"github.com/restic/restic/internal/restic"
|
||||||
|
"github.com/restic/restic/internal/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpgradeRepoV2(t *testing.T) {
|
||||||
|
repo, cleanup := repository.TestRepositoryWithVersion(t, 1)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if repo.Config().Version != 1 {
|
||||||
|
t.Fatal("test repo has wrong version")
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &UpgradeRepoV2{}
|
||||||
|
|
||||||
|
ok, err := m.Check(context.Background(), repo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("migration check returned false")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Apply(context.Background(), repo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type failBackend struct {
|
||||||
|
restic.Backend
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
ConfigFileSavesUntilError uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (be *failBackend) Save(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
||||||
|
if h.Type != restic.ConfigFile {
|
||||||
|
return be.Backend.Save(ctx, h, rd)
|
||||||
|
}
|
||||||
|
|
||||||
|
be.mu.Lock()
|
||||||
|
if be.ConfigFileSavesUntilError == 0 {
|
||||||
|
be.mu.Unlock()
|
||||||
|
return errors.New("failure induced for testing")
|
||||||
|
}
|
||||||
|
|
||||||
|
be.ConfigFileSavesUntilError--
|
||||||
|
be.mu.Unlock()
|
||||||
|
|
||||||
|
return be.Backend.Save(ctx, h, rd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpgradeRepoV2Failure(t *testing.T) {
|
||||||
|
be, cleanup := repository.TestBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
// wrap backend so that it fails upgrading the config after the initial write
|
||||||
|
be = &failBackend{
|
||||||
|
ConfigFileSavesUntilError: 1,
|
||||||
|
Backend: be,
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, cleanup := repository.TestRepositoryWithBackend(t, be, 1)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if repo.Config().Version != 1 {
|
||||||
|
t.Fatal("test repo has wrong version")
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &UpgradeRepoV2{}
|
||||||
|
|
||||||
|
ok, err := m.Check(context.Background(), repo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("migration check returned false")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Apply(context.Background(), repo)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error returned from Apply(), got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
upgradeErr := err.(*UpgradeRepoV2Error)
|
||||||
|
if upgradeErr.UploadNewConfigError == nil {
|
||||||
|
t.Fatal("expected upload error, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if upgradeErr.ReuploadOldConfigError == nil {
|
||||||
|
t.Fatal("expected reupload error, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if upgradeErr.BackupFilePath == "" {
|
||||||
|
t.Fatal("no backup file path found")
|
||||||
|
}
|
||||||
|
test.OK(t, os.Remove(upgradeErr.BackupFilePath))
|
||||||
|
test.OK(t, os.Remove(filepath.Dir(upgradeErr.BackupFilePath)))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user