diff options
author | James Fargher <jfargher@gitlab.com> | 2021-09-28 02:06:54 +0300 |
---|---|---|
committer | James Fargher <jfargher@gitlab.com> | 2021-10-04 02:35:36 +0300 |
commit | b578f037a03b37d6e8b7e5f9e21a6cc9ce1b6060 (patch) | |
tree | 2796cdfe4660e3b1b471c8ada8c046865a2c82aa | |
parent | 9589907901171fa95c456fc07387e0dd808231d4 (diff) |
backup: Restore from incremental backups
Changelog: added
-rw-r--r-- | internal/backup/backup_test.go | 39 | ||||
-rw-r--r-- | internal/backup/locator.go | 39 | ||||
-rw-r--r-- | internal/backup/locator_test.go | 54 |
3 files changed, 118 insertions, 14 deletions
diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index 88c060ae2..ab8304965 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -303,6 +303,45 @@ func testManagerRestore(t *testing.T, cfg config.Cfg, gitalyAddr string) { }, expectExists: true, }, + { + desc: "single incremental", + locators: []string{"pointer"}, + setup: func(t testing.TB) (*gitalypb.Repository, []string) { + const backupID = "abc123" + repo := createRepo(t, "incremental") + repoBackupPath := filepath.Join(path, repo.RelativePath) + backupPath := filepath.Join(repoBackupPath, backupID) + require.NoError(t, os.MkdirAll(backupPath, os.ModePerm)) + require.NoError(t, os.WriteFile(filepath.Join(repoBackupPath, "LATEST"), []byte(backupID), os.ModePerm)) + require.NoError(t, os.WriteFile(filepath.Join(backupPath, "LATEST"), []byte("001"), os.ModePerm)) + bundlePath := filepath.Join(backupPath, "001.bundle") + gittest.BundleTestRepo(t, cfg, "gitlab-test.git", bundlePath) + return repo, []string{bundlePath} + }, + expectExists: true, + }, + { + desc: "many incrementals", + locators: []string{"pointer"}, + setup: func(t testing.TB) (*gitalypb.Repository, []string) { + const backupID = "abc123" + repo := createRepo(t, "incremental") + repoBackupPath := filepath.Join(path, repo.RelativePath) + backupPath := filepath.Join(repoBackupPath, backupID) + require.NoError(t, os.MkdirAll(backupPath, os.ModePerm)) + require.NoError(t, os.WriteFile(filepath.Join(repoBackupPath, "LATEST"), []byte(backupID), os.ModePerm)) + require.NoError(t, os.WriteFile(filepath.Join(backupPath, "LATEST"), []byte("002"), os.ModePerm)) + + bundlePath1 := filepath.Join(backupPath, "001.bundle") + gittest.BundleTestRepo(t, cfg, "gitlab-test.git", bundlePath1, "master") + + bundlePath2 := filepath.Join(backupPath, "002.bundle") + gittest.BundleTestRepo(t, cfg, "gitlab-test.git", bundlePath2, "feature") + + return repo, []string{bundlePath1, bundlePath2} + }, + expectExists: true, + }, } { t.Run(tc.desc, func(t *testing.T) { require.GreaterOrEqual(t, len(tc.locators), 1, "each test case must specify a locator") diff --git a/internal/backup/locator.go b/internal/backup/locator.go index 5ef75f7d3..81e53dcb1 100644 --- a/internal/backup/locator.go +++ b/internal/backup/locator.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "path/filepath" + "strconv" "strings" "gitlab.com/gitlab-org/gitaly/v14/internal/helper/text" @@ -98,25 +99,39 @@ func (l PointerLocator) CommitFull(ctx context.Context, full *Step) error { // // If there is no `LATEST` file, the result of the `Fallback` is used. func (l PointerLocator) FindLatest(ctx context.Context, repo *gitalypb.Repository) (*Backup, error) { - backupPath := strings.TrimSuffix(repo.RelativePath, ".git") + repoPath := strings.TrimSuffix(repo.RelativePath, ".git") - backupID, err := l.findLatestID(ctx, backupPath) + backupID, err := l.findLatestID(ctx, repoPath) if err != nil { if l.Fallback != nil && errors.Is(err, ErrDoesntExist) { return l.Fallback.FindLatest(ctx, repo) } - return nil, fmt.Errorf("pointer locator: %w", err) + return nil, fmt.Errorf("pointer locator: backup: %w", err) } - return &Backup{ - Steps: []Step{ - { - BundlePath: filepath.Join(backupPath, backupID, "001.bundle"), - RefPath: filepath.Join(backupPath, backupID, "001.refs"), - CustomHooksPath: filepath.Join(backupPath, backupID, "001.custom_hooks.tar"), - }, - }, - }, nil + backupPath := filepath.Join(repoPath, backupID) + + latestIncrementID, err := l.findLatestID(ctx, backupPath) + if err != nil { + return nil, fmt.Errorf("pointer locator: latest incremental: %w", err) + } + + max, err := strconv.Atoi(latestIncrementID) + if err != nil { + return nil, fmt.Errorf("pointer locator: latest incremental: %w", err) + } + + var backup Backup + + for i := 1; i <= max; i++ { + backup.Steps = append(backup.Steps, Step{ + BundlePath: filepath.Join(backupPath, fmt.Sprintf("%03d.bundle", i)), + RefPath: filepath.Join(backupPath, fmt.Sprintf("%03d.refs", i)), + CustomHooksPath: filepath.Join(backupPath, fmt.Sprintf("%03d.custom_hooks.tar", i)), + }) + } + + return &backup, nil } func (l PointerLocator) findLatestID(ctx context.Context, backupPath string) (string, error) { diff --git a/internal/backup/locator_test.go b/internal/backup/locator_test.go index cc39bf66f..7bd3b48d7 100644 --- a/internal/backup/locator_test.go +++ b/internal/backup/locator_test.go @@ -100,8 +100,9 @@ func TestPointerLocator(t *testing.T) { _, err := l.FindLatest(ctx, repo) require.ErrorIs(t, err, ErrDoesntExist) - require.NoError(t, os.MkdirAll(filepath.Join(backupPath, repo.RelativePath), 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(backupPath, repo.RelativePath, backupID), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, "LATEST"), []byte(backupID), 0o644)) + require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, backupID, "LATEST"), []byte("003"), 0o644)) expected := &Backup{ Steps: []Step{ { @@ -109,6 +110,16 @@ func TestPointerLocator(t *testing.T) { RefPath: filepath.Join(repo.RelativePath, backupID, "001.refs"), CustomHooksPath: filepath.Join(repo.RelativePath, backupID, "001.custom_hooks.tar"), }, + { + BundlePath: filepath.Join(repo.RelativePath, backupID, "002.bundle"), + RefPath: filepath.Join(repo.RelativePath, backupID, "002.refs"), + CustomHooksPath: filepath.Join(repo.RelativePath, backupID, "002.custom_hooks.tar"), + }, + { + BundlePath: filepath.Join(repo.RelativePath, backupID, "003.bundle"), + RefPath: filepath.Join(repo.RelativePath, backupID, "003.refs"), + CustomHooksPath: filepath.Join(repo.RelativePath, backupID, "003.custom_hooks.tar"), + }, }, } @@ -142,8 +153,9 @@ func TestPointerLocator(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedFallback, fallbackFull) - require.NoError(t, os.MkdirAll(filepath.Join(backupPath, repo.RelativePath), 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(backupPath, repo.RelativePath, backupID), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, "LATEST"), []byte(backupID), 0o644)) + require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, backupID, "LATEST"), []byte("001"), 0o644)) expected := &Backup{ Steps: []Step{ { @@ -158,5 +170,43 @@ func TestPointerLocator(t *testing.T) { require.NoError(t, err) require.Equal(t, expected, full) }) + + t.Run("invalid backup LATEST", func(t *testing.T) { + backupPath := testhelper.TempDir(t) + var l Locator = PointerLocator{ + Sink: NewFilesystemSink(backupPath), + } + + ctx, cancel := testhelper.Context() + defer cancel() + + _, err := l.FindLatest(ctx, repo) + require.ErrorIs(t, err, ErrDoesntExist) + + require.NoError(t, os.MkdirAll(filepath.Join(backupPath, repo.RelativePath), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, "LATEST"), []byte("invalid"), 0o644)) + _, err = l.FindLatest(ctx, repo) + require.EqualError(t, err, "pointer locator: latest incremental: find latest ID: filesystem sink: get reader for \"TestPointerLocator/invalid/LATEST\": doesn't exist") + }) + + t.Run("invalid incremental LATEST", func(t *testing.T) { + backupPath := testhelper.TempDir(t) + var l Locator = PointerLocator{ + Sink: NewFilesystemSink(backupPath), + } + + ctx, cancel := testhelper.Context() + defer cancel() + + _, err := l.FindLatest(ctx, repo) + require.ErrorIs(t, err, ErrDoesntExist) + + require.NoError(t, os.MkdirAll(filepath.Join(backupPath, repo.RelativePath, backupID), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, "LATEST"), []byte(backupID), 0o644)) + require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, backupID, "LATEST"), []byte("invalid"), 0o644)) + + _, err = l.FindLatest(ctx, repo) + require.EqualError(t, err, "pointer locator: latest incremental: strconv.Atoi: parsing \"invalid\": invalid syntax") + }) }) } |