diff options
author | Justin Tobler <jtobler@gitlab.com> | 2023-09-26 01:18:36 +0300 |
---|---|---|
committer | Justin Tobler <jtobler@gitlab.com> | 2023-09-26 01:18:36 +0300 |
commit | 7a80007379240d0c628407b00062cf6f69c6334d (patch) | |
tree | 456f588066984e7cecb2bba42d02697bc397d14a | |
parent | 017b89db0457773ddb47105fdba1e634521dd975 (diff) | |
parent | b584d322dd8a379b92ef7eb33bc22a20700e73a3 (diff) |
Merge branch 'backup_manifests' into 'master'
Generate backup manifest files
See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/6391
Merged-by: Justin Tobler <jtobler@gitlab.com>
Approved-by: James Liu <jliu@gitlab.com>
Approved-by: Justin Tobler <jtobler@gitlab.com>
Reviewed-by: James Fargher <jfargher@gitlab.com>
Reviewed-by: James Liu <jliu@gitlab.com>
Reviewed-by: Justin Tobler <jtobler@gitlab.com>
Co-authored-by: James Fargher <jfargher@gitlab.com>
-rw-r--r-- | internal/backup/backup.go | 62 | ||||
-rw-r--r-- | internal/backup/backup_test.go | 7 | ||||
-rw-r--r-- | internal/backup/locator.go | 150 | ||||
-rw-r--r-- | internal/backup/locator_test.go | 147 | ||||
-rw-r--r-- | internal/backup/server_side_test.go | 5 | ||||
-rw-r--r-- | internal/gitaly/service/repository/restore_repository_test.go | 10 |
6 files changed, 299 insertions, 82 deletions
diff --git a/internal/backup/backup.go b/internal/backup/backup.go index a11aa4953..a2b38fc58 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -40,41 +40,48 @@ type Sink interface { // Backup represents all the information needed to restore a backup for a repository type Backup struct { + // ID is the identifier that uniquely identifies the backup for this repository. + ID string `toml:"-"` + // Repository is the repository being backed up. + Repository storage.Repository `toml:"-"` // Steps are the ordered list of steps required to restore this backup - Steps []Step + Steps []Step `toml:"steps"` // ObjectFormat is the name of the object hash used by the repository. - ObjectFormat string + ObjectFormat string `toml:"object_format"` } // Step represents an incremental step that makes up a complete backup for a repository type Step struct { // BundlePath is the path of the bundle - BundlePath string + BundlePath string `toml:"bundle_path,omitempty"` // RefPath is the path of the ref file - RefPath string + RefPath string `toml:"ref_path,omitempty"` // PreviousRefPath is the path of the previous ref file - PreviousRefPath string + PreviousRefPath string `toml:"previous_ref_path,omitempty"` // CustomHooksPath is the path of the custom hooks archive - CustomHooksPath string + CustomHooksPath string `toml:"custom_hooks_path,omitempty"` } // Locator finds sink backup paths for repositories type Locator interface { - // BeginFull returns a tentative first step needed to create a new full backup. - BeginFull(ctx context.Context, repo *gitalypb.Repository, backupID string) *Step + // BeginFull returns the tentative backup paths needed to create a full backup. + BeginFull(ctx context.Context, repo storage.Repository, backupID string) *Backup - // BeginIncremental returns a tentative step needed to create a new incremental backup. - BeginIncremental(ctx context.Context, repo *gitalypb.Repository, backupID string) (*Step, error) + // BeginIncremental returns the backup with the last element of Steps being + // the tentative step needed to create an incremental backup. + BeginIncremental(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) - // Commit persists the step so that it can be looked up by FindLatest - Commit(ctx context.Context, step *Step) error + // Commit persists the backup so that it can be looked up by FindLatest. It + // is expected that the last element of Steps will be the newly created + // backup. + Commit(ctx context.Context, backup *Backup) error // FindLatest returns the latest backup that was written by Commit - FindLatest(ctx context.Context, repo *gitalypb.Repository) (*Backup, error) + FindLatest(ctx context.Context, repo storage.Repository) (*Backup, error) // Find returns the repository backup at the given backupID. If the backup does // not exist then the error ErrDoesntExist is returned. - Find(ctx context.Context, repo *gitalypb.Repository, backupID string) (*Backup, error) + Find(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) } // Repository abstracts git access required to make a repository backup @@ -100,18 +107,25 @@ type Repository interface { // ResolveLocator returns a locator implementation based on a locator identifier. func ResolveLocator(layout string, sink Sink) (Locator, error) { - legacy := LegacyLocator{} + var locator Locator = LegacyLocator{} + switch layout { case "legacy": - return legacy, nil case "pointer": - return PointerLocator{ + locator = PointerLocator{ Sink: sink, - Fallback: legacy, - }, nil + Fallback: locator, + } default: return nil, fmt.Errorf("unknown layout: %q", layout) } + + locator = ManifestLocator{ + Sink: sink, + Fallback: locator, + } + + return locator, nil } // Manager manages process of the creating/restoring backups. @@ -197,15 +211,15 @@ func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error { return fmt.Errorf("manager: %w", err) } - var step *Step + var backup *Backup if req.Incremental { var err error - step, err = mgr.locator.BeginIncremental(ctx, req.VanityRepository, req.BackupID) + backup, err = mgr.locator.BeginIncremental(ctx, req.VanityRepository, req.BackupID) if err != nil { return fmt.Errorf("manager: %w", err) } } else { - step = mgr.locator.BeginFull(ctx, req.VanityRepository, req.BackupID) + backup = mgr.locator.BeginFull(ctx, req.VanityRepository, req.BackupID) } refs, err := repo.ListRefs(ctx) @@ -216,6 +230,8 @@ func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error { return fmt.Errorf("manager: %w", err) } + step := &backup.Steps[len(backup.Steps)-1] + if err := mgr.writeRefs(ctx, step.RefPath, refs); err != nil { return fmt.Errorf("manager: %w", err) } @@ -226,7 +242,7 @@ func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error { return fmt.Errorf("manager: %w", err) } - if err := mgr.locator.Commit(ctx, step); err != nil { + if err := mgr.locator.Commit(ctx, backup); err != nil { return fmt.Errorf("manager: %w", err) } diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index ac7b155c0..9b84cec55 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -173,6 +173,7 @@ func TestManager_Create(t *testing.T) { StorageName: "some_storage", } + manifestPath := filepath.Join(backupRoot, "manifests", vanityRepo.StorageName, vanityRepo.RelativePath, backupID+".toml") refsPath := joinBackupPath(t, backupRoot, vanityRepo, backupID, "001.refs") bundlePath := joinBackupPath(t, backupRoot, vanityRepo, backupID, "001.bundle") customHooksPath := joinBackupPath(t, backupRoot, vanityRepo, backupID, "001.custom_hooks.tar") @@ -224,6 +225,12 @@ func TestManager_Create(t *testing.T) { require.NoFileExists(t, refsPath) } + if tc.createsBundle || tc.createsRefList { + require.FileExists(t, manifestPath) + } else { + require.NoFileExists(t, manifestPath) + } + if tc.createsCustomHooks { require.FileExists(t, customHooksPath) } else { diff --git a/internal/backup/locator.go b/internal/backup/locator.go index f2897f2e0..da789011d 100644 --- a/internal/backup/locator.go +++ b/internal/backup/locator.go @@ -5,13 +5,15 @@ import ( "errors" "fmt" "io" + "path" "path/filepath" "strconv" "strings" + "github.com/pelletier/go-toml/v2" "gitlab.com/gitlab-org/gitaly/v16/internal/git" + "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage" "gitlab.com/gitlab-org/gitaly/v16/internal/helper/text" - "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" ) // LegacyLocator locates backup paths for historic backups. This is the @@ -28,42 +30,43 @@ import ( type LegacyLocator struct{} // BeginFull returns the static paths for a legacy repository backup -func (l LegacyLocator) BeginFull(ctx context.Context, repo *gitalypb.Repository, backupID string) *Step { +func (l LegacyLocator) BeginFull(ctx context.Context, repo storage.Repository, backupID string) *Backup { return l.newFull(repo) } // BeginIncremental is not supported for legacy backups -func (l LegacyLocator) BeginIncremental(ctx context.Context, repo *gitalypb.Repository, backupID string) (*Step, error) { +func (l LegacyLocator) BeginIncremental(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) { return nil, errors.New("legacy layout: begin incremental: not supported") } // Commit is unused as the locations are static -func (l LegacyLocator) Commit(ctx context.Context, full *Step) error { +func (l LegacyLocator) Commit(ctx context.Context, full *Backup) error { return nil } // FindLatest returns the static paths for a legacy repository backup -func (l LegacyLocator) FindLatest(ctx context.Context, repo *gitalypb.Repository) (*Backup, error) { - return &Backup{ - Steps: []Step{ - *l.newFull(repo), - }, - ObjectFormat: git.ObjectHashSHA1.Format, - }, nil +func (l LegacyLocator) FindLatest(ctx context.Context, repo storage.Repository) (*Backup, error) { + return l.newFull(repo), nil } // Find is not supported for legacy backups. -func (l LegacyLocator) Find(ctx context.Context, repo *gitalypb.Repository, backupID string) (*Backup, error) { +func (l LegacyLocator) Find(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) { return nil, errors.New("legacy layout: find: not supported") } -func (l LegacyLocator) newFull(repo *gitalypb.Repository) *Step { - backupPath := strings.TrimSuffix(repo.RelativePath, ".git") +func (l LegacyLocator) newFull(repo storage.Repository) *Backup { + backupPath := strings.TrimSuffix(repo.GetRelativePath(), ".git") - return &Step{ - BundlePath: backupPath + ".bundle", - RefPath: backupPath + ".refs", - CustomHooksPath: filepath.Join(backupPath, "custom_hooks.tar"), + return &Backup{ + Repository: repo, + ObjectFormat: git.ObjectHashSHA1.Format, + Steps: []Step{ + { + BundlePath: backupPath + ".bundle", + RefPath: backupPath + ".refs", + CustomHooksPath: filepath.Join(backupPath, "custom_hooks.tar"), + }, + }, } } @@ -84,13 +87,20 @@ type PointerLocator struct { } // BeginFull returns a tentative first step needed to create a new full backup. -func (l PointerLocator) BeginFull(ctx context.Context, repo *gitalypb.Repository, backupID string) *Step { - repoPath := strings.TrimSuffix(repo.RelativePath, ".git") +func (l PointerLocator) BeginFull(ctx context.Context, repo storage.Repository, backupID string) *Backup { + repoPath := strings.TrimSuffix(repo.GetRelativePath(), ".git") - return &Step{ - BundlePath: filepath.Join(repoPath, backupID, "001.bundle"), - RefPath: filepath.Join(repoPath, backupID, "001.refs"), - CustomHooksPath: filepath.Join(repoPath, backupID, "001.custom_hooks.tar"), + return &Backup{ + ID: backupID, + Repository: repo, + ObjectFormat: git.ObjectHashSHA1.Format, + Steps: []Step{ + { + BundlePath: filepath.Join(repoPath, backupID, "001.bundle"), + RefPath: filepath.Join(repoPath, backupID, "001.refs"), + CustomHooksPath: filepath.Join(repoPath, backupID, "001.custom_hooks.tar"), + }, + }, } } @@ -98,8 +108,8 @@ func (l PointerLocator) BeginFull(ctx context.Context, repo *gitalypb.Repository // backup. The incremental backup is always based off of the latest full // backup. If there is no latest backup, a new full backup step is returned // using fallbackBackupID -func (l PointerLocator) BeginIncremental(ctx context.Context, repo *gitalypb.Repository, fallbackBackupID string) (*Step, error) { - repoPath := strings.TrimSuffix(repo.RelativePath, ".git") +func (l PointerLocator) BeginIncremental(ctx context.Context, repo storage.Repository, fallbackBackupID string) (*Backup, error) { + repoPath := strings.TrimSuffix(repo.GetRelativePath(), ".git") backupID, err := l.findLatestID(ctx, repoPath) if err != nil { if errors.Is(err, ErrDoesntExist) { @@ -125,16 +135,23 @@ func (l PointerLocator) BeginIncremental(ctx context.Context, repo *gitalypb.Rep } id++ - return &Step{ + backup.ID = fallbackBackupID + backup.Steps = append(backup.Steps, Step{ BundlePath: filepath.Join(backupPath, fmt.Sprintf("%03d.bundle", id)), RefPath: filepath.Join(backupPath, fmt.Sprintf("%03d.refs", id)), PreviousRefPath: previous.RefPath, CustomHooksPath: filepath.Join(backupPath, fmt.Sprintf("%03d.custom_hooks.tar", id)), - }, nil + }) + + return backup, nil } // Commit persists the step so that it can be looked up by FindLatest -func (l PointerLocator) Commit(ctx context.Context, step *Step) error { +func (l PointerLocator) Commit(ctx context.Context, backup *Backup) error { + if len(backup.Steps) < 1 { + return fmt.Errorf("pointer locator: commit: no steps") + } + step := backup.Steps[len(backup.Steps)-1] backupPath := filepath.Dir(step.BundlePath) bundleName := filepath.Base(step.BundlePath) repoPath := filepath.Dir(backupPath) @@ -153,8 +170,8 @@ func (l PointerLocator) Commit(ctx context.Context, step *Step) error { // FindLatest returns the paths committed by the latest call to CommitFull. // // 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) { - repoPath := strings.TrimSuffix(repo.RelativePath, ".git") +func (l PointerLocator) FindLatest(ctx context.Context, repo storage.Repository) (*Backup, error) { + repoPath := strings.TrimSuffix(repo.GetRelativePath(), ".git") backupID, err := l.findLatestID(ctx, repoPath) if err != nil { @@ -173,7 +190,7 @@ func (l PointerLocator) FindLatest(ctx context.Context, repo *gitalypb.Repositor // Find returns the repository backup at the given backupID. If the backup does // not exist then the error ErrDoesntExist is returned. -func (l PointerLocator) Find(ctx context.Context, repo *gitalypb.Repository, backupID string) (*Backup, error) { +func (l PointerLocator) Find(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) { backup, err := l.find(ctx, repo, backupID) if err != nil { return nil, fmt.Errorf("pointer locator: %w", err) @@ -181,8 +198,8 @@ func (l PointerLocator) Find(ctx context.Context, repo *gitalypb.Repository, bac return backup, nil } -func (l PointerLocator) find(ctx context.Context, repo *gitalypb.Repository, backupID string) (*Backup, error) { - repoPath := strings.TrimSuffix(repo.RelativePath, ".git") +func (l PointerLocator) find(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) { + repoPath := strings.TrimSuffix(repo.GetRelativePath(), ".git") backupPath := filepath.Join(repoPath, backupID) latestIncrementID, err := l.findLatestID(ctx, backupPath) @@ -196,6 +213,8 @@ func (l PointerLocator) find(ctx context.Context, repo *gitalypb.Repository, bac } backup := Backup{ + ID: backupID, + Repository: repo, ObjectFormat: git.ObjectHashSHA1.Format, } @@ -247,3 +266,66 @@ func (l PointerLocator) writeLatest(ctx context.Context, path, target string) (r return nil } + +// ManifestLocator locates backup paths based on manifest files that are +// written to a predetermined path: +// +// manifests/<repo_storage_name>/<repo_relative_path>/<backup_id>.toml +// +// It relies on Fallback to determine paths of new backups. +type ManifestLocator struct { + Sink Sink + Fallback Locator +} + +// BeginFull passes through to Fallback +func (l ManifestLocator) BeginFull(ctx context.Context, repo storage.Repository, backupID string) *Backup { + return l.Fallback.BeginFull(ctx, repo, backupID) +} + +// BeginIncremental passes through to Fallback +func (l ManifestLocator) BeginIncremental(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) { + return l.Fallback.BeginIncremental(ctx, repo, backupID) +} + +// Commit passes through to Fallback, then writes a manifest file for the backup. +func (l ManifestLocator) Commit(ctx context.Context, backup *Backup) (returnErr error) { + if err := l.Fallback.Commit(ctx, backup); err != nil { + return err + } + + f, err := l.Sink.GetWriter(ctx, manifestPath(backup)) + if err != nil { + return fmt.Errorf("manifest: commit: %w", err) + } + defer func() { + if err := f.Close(); err != nil && returnErr == nil { + returnErr = fmt.Errorf("manifest: commit: %w", err) + } + }() + + if err := toml.NewEncoder(f).Encode(backup); err != nil { + return fmt.Errorf("manifest: commit: %w", err) + } + + return nil +} + +// FindLatest passes through to Fallback +func (l ManifestLocator) FindLatest(ctx context.Context, repo storage.Repository) (*Backup, error) { + return l.Fallback.FindLatest(ctx, repo) +} + +// Find passes through to Fallback +func (l ManifestLocator) Find(ctx context.Context, repo storage.Repository, backupID string) (*Backup, error) { + return l.Fallback.Find(ctx, repo, backupID) +} + +func manifestPath(backup *Backup) string { + storageName := backup.Repository.GetStorageName() + // Other locators strip the .git suffix off of relative paths. This suffix + // is determined by gitlab-rails not gitaly. So here we leave the relative + // path as-is so that new backups can be more independent. + relativePath := backup.Repository.GetRelativePath() + return path.Join("manifests", storageName, relativePath, backup.ID+".toml") +} diff --git a/internal/backup/locator_test.go b/internal/backup/locator_test.go index c9f48fdf1..7aed83608 100644 --- a/internal/backup/locator_test.go +++ b/internal/backup/locator_test.go @@ -33,10 +33,17 @@ func TestLegacyLocator(t *testing.T) { t.Run("Begin/Commit Full", func(t *testing.T) { t.Parallel() - expected := &Step{ - BundlePath: repo.RelativePath + ".bundle", - RefPath: repo.RelativePath + ".refs", - CustomHooksPath: filepath.Join(repo.RelativePath, "custom_hooks.tar"), + expected := &Backup{ + ID: "", // legacy storage can only store a single backup. + Repository: repo, + ObjectFormat: git.ObjectHashSHA1.Format, + Steps: []Step{ + { + BundlePath: repo.RelativePath + ".bundle", + RefPath: repo.RelativePath + ".refs", + CustomHooksPath: filepath.Join(repo.RelativePath, "custom_hooks.tar"), + }, + }, } full := l.BeginFull(ctx, repo, "abc123") @@ -49,6 +56,8 @@ func TestLegacyLocator(t *testing.T) { t.Parallel() expected := &Backup{ + ID: "", // legacy storage can only store a single backup. + Repository: repo, ObjectFormat: git.ObjectHashSHA1.Format, Steps: []Step{ { @@ -89,10 +98,17 @@ func TestPointerLocator(t *testing.T) { } const expectedIncrement = "001" - expected := &Step{ - BundlePath: filepath.Join(repo.RelativePath, backupID, expectedIncrement+".bundle"), - RefPath: filepath.Join(repo.RelativePath, backupID, expectedIncrement+".refs"), - CustomHooksPath: filepath.Join(repo.RelativePath, backupID, expectedIncrement+".custom_hooks.tar"), + expected := &Backup{ + ID: backupID, + Repository: repo, + ObjectFormat: git.ObjectHashSHA1.Format, + Steps: []Step{ + { + BundlePath: filepath.Join(repo.RelativePath, backupID, expectedIncrement+".bundle"), + RefPath: filepath.Join(repo.RelativePath, backupID, expectedIncrement+".refs"), + CustomHooksPath: filepath.Join(repo.RelativePath, backupID, expectedIncrement+".custom_hooks.tar"), + }, + }, } full := l.BeginFull(ctx, repo, backupID) @@ -146,19 +162,24 @@ func TestPointerLocator(t *testing.T) { tc.setup(t, ctx, backupPath) } - var expected *Step + var expected *Backup for i := 1; i <= 3; i++ { - incrementID := i + tc.expectedOffset - var previousRefPath string - if incrementID > 1 { - previousRefPath = filepath.Join(repo.RelativePath, tc.expectedBackupID, fmt.Sprintf("%03d.refs", incrementID-1)) + var previousRefPath, expectedIncrement string + expected = &Backup{ + ID: fallbackBackupID, + Repository: repo, + ObjectFormat: git.ObjectHashSHA1.Format, } - expectedIncrement := fmt.Sprintf("%03d", incrementID) - expected = &Step{ - BundlePath: filepath.Join(repo.RelativePath, tc.expectedBackupID, expectedIncrement+".bundle"), - RefPath: filepath.Join(repo.RelativePath, tc.expectedBackupID, expectedIncrement+".refs"), - PreviousRefPath: previousRefPath, - CustomHooksPath: filepath.Join(repo.RelativePath, tc.expectedBackupID, expectedIncrement+".custom_hooks.tar"), + for incrementID := 1; incrementID <= i+tc.expectedOffset; incrementID++ { + expectedIncrement = fmt.Sprintf("%03d", incrementID) + step := Step{ + BundlePath: filepath.Join(repo.RelativePath, tc.expectedBackupID, expectedIncrement+".bundle"), + RefPath: filepath.Join(repo.RelativePath, tc.expectedBackupID, expectedIncrement+".refs"), + PreviousRefPath: previousRefPath, + CustomHooksPath: filepath.Join(repo.RelativePath, tc.expectedBackupID, expectedIncrement+".custom_hooks.tar"), + } + expected.Steps = append(expected.Steps, step) + previousRefPath = step.RefPath } step, err := l.BeginIncremental(ctx, repo, fallbackBackupID) @@ -195,6 +216,8 @@ func TestPointerLocator(t *testing.T) { require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, "LATEST"), []byte(backupID), perm.SharedFile)) require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, backupID, "LATEST"), []byte("003"), perm.SharedFile)) expected := &Backup{ + ID: backupID, + Repository: repo, ObjectFormat: git.ObjectHashSHA1.Format, Steps: []Step{ { @@ -232,6 +255,8 @@ func TestPointerLocator(t *testing.T) { } expectedFallback := &Backup{ + ID: "", + Repository: repo, ObjectFormat: git.ObjectHashSHA1.Format, Steps: []Step{ { @@ -250,6 +275,8 @@ func TestPointerLocator(t *testing.T) { require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, "LATEST"), []byte(backupID), perm.SharedFile)) require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, backupID, "LATEST"), []byte("001"), perm.SharedFile)) expected := &Backup{ + ID: backupID, + Repository: repo, ObjectFormat: git.ObjectHashSHA1.Format, Steps: []Step{ { @@ -328,6 +355,8 @@ func TestPointerLocator(t *testing.T) { require.NoError(t, os.MkdirAll(filepath.Join(backupPath, repo.RelativePath, backupID), perm.SharedDir)) require.NoError(t, os.WriteFile(filepath.Join(backupPath, repo.RelativePath, backupID, "LATEST"), []byte("003"), perm.SharedFile)) expected := &Backup{ + ID: backupID, + Repository: repo, ObjectFormat: git.ObjectHashSHA1.Format, Steps: []Step{ { @@ -374,3 +403,83 @@ func TestPointerLocator(t *testing.T) { }) }) } + +func TestManifestLocator(t *testing.T) { + t.Parallel() + + const backupID = "abc123" + + ctx := testhelper.Context(t) + cfg := testcfg.Build(t) + + repo, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{ + SkipCreationViaService: true, + RelativePath: t.Name(), + }) + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch(git.DefaultBranch)) + + t.Run("BeginFull/Commit", func(t *testing.T) { + t.Parallel() + + backupPath := testhelper.TempDir(t) + sink := NewFilesystemSink(backupPath) + var l Locator = PointerLocator{ + Sink: sink, + } + l = ManifestLocator{ + Sink: sink, + Fallback: l, + } + + full := l.BeginFull(ctx, repo, backupID) + require.NoError(t, l.Commit(ctx, full)) + + manifest := testhelper.MustReadFile(t, filepath.Join(backupPath, "manifests", repo.StorageName, repo.RelativePath, backupID+".toml")) + require.Equal(t, fmt.Sprintf(`object_format = 'sha1' + +[[steps]] +bundle_path = '%[1]s/%[2]s/001.bundle' +ref_path = '%[1]s/%[2]s/001.refs' +custom_hooks_path = '%[1]s/%[2]s/001.custom_hooks.tar' +`, repo.RelativePath, backupID), string(manifest)) + }) + + t.Run("BeginIncremental/Commit", func(t *testing.T) { + t.Parallel() + + backupPath := testhelper.TempDir(t) + + testhelper.WriteFiles(t, backupPath, map[string]any{ + filepath.Join(repo.RelativePath, "LATEST"): "abc123", + filepath.Join(repo.RelativePath, "abc123", "LATEST"): "001", + }) + + sink := NewFilesystemSink(backupPath) + var l Locator = PointerLocator{ + Sink: sink, + } + l = ManifestLocator{ + Sink: sink, + Fallback: l, + } + + incremental, err := l.BeginIncremental(ctx, repo, backupID) + require.NoError(t, err) + require.NoError(t, l.Commit(ctx, incremental)) + + manifest := testhelper.MustReadFile(t, filepath.Join(backupPath, "manifests", repo.StorageName, repo.RelativePath, backupID+".toml")) + require.Equal(t, fmt.Sprintf(`object_format = 'sha1' + +[[steps]] +bundle_path = '%[1]s/%[2]s/001.bundle' +ref_path = '%[1]s/%[2]s/001.refs' +custom_hooks_path = '%[1]s/%[2]s/001.custom_hooks.tar' + +[[steps]] +bundle_path = '%[1]s/%[2]s/002.bundle' +ref_path = '%[1]s/%[2]s/002.refs' +previous_ref_path = '%[1]s/%[2]s/001.refs' +custom_hooks_path = '%[1]s/%[2]s/002.custom_hooks.tar' +`, repo.RelativePath, backupID), string(manifest)) + }) +} diff --git a/internal/backup/server_side_test.go b/internal/backup/server_side_test.go index 13d375d69..b60918439 100644 --- a/internal/backup/server_side_test.go +++ b/internal/backup/server_side_test.go @@ -157,7 +157,8 @@ func TestServerSideAdapter_Restore(t *testing.T) { checksum := gittest.ChecksumRepo(t, cfg, templateRepoPath) repo, repoPath := gittest.CreateRepository(t, ctx, cfg) - step := backupLocator.BeginFull(ctx, repo, "abc123") + backup := backupLocator.BeginFull(ctx, repo, "abc123") + step := backup.Steps[len(backup.Steps)-1] w, err := backupSink.GetWriter(ctx, step.BundlePath) require.NoError(t, err) @@ -173,7 +174,7 @@ func TestServerSideAdapter_Restore(t *testing.T) { require.NoError(t, err) require.NoError(t, w.Close()) - require.NoError(t, backupLocator.Commit(ctx, step)) + require.NoError(t, backupLocator.Commit(ctx, backup)) return setupData{ repo: repo, diff --git a/internal/gitaly/service/repository/restore_repository_test.go b/internal/gitaly/service/repository/restore_repository_test.go index 5d5fbfbb0..a8b50c92d 100644 --- a/internal/gitaly/service/repository/restore_repository_test.go +++ b/internal/gitaly/service/repository/restore_repository_test.go @@ -49,7 +49,8 @@ func TestRestoreRepository(t *testing.T) { checksum := gittest.ChecksumRepo(t, cfg, templateRepoPath) repo, repoPath := gittest.CreateRepository(t, ctx, cfg) - step := backupLocator.BeginFull(ctx, repo, "abc123") + backup := backupLocator.BeginFull(ctx, repo, "abc123") + step := backup.Steps[len(backup.Steps)-1] w, err := backupSink.GetWriter(ctx, step.BundlePath) require.NoError(t, err) @@ -65,7 +66,7 @@ func TestRestoreRepository(t *testing.T) { require.NoError(t, err) require.NoError(t, w.Close()) - require.NoError(t, backupLocator.Commit(ctx, step)) + require.NoError(t, backupLocator.Commit(ctx, backup)) return setupData{ cfg: cfg, @@ -91,7 +92,8 @@ func TestRestoreRepository(t *testing.T) { checksum := gittest.ChecksumRepo(t, cfg, templateRepoPath) repo, repoPath := gittest.CreateRepository(t, ctx, cfg) - step := backupLocator.BeginFull(ctx, repo, "abc123") + backup := backupLocator.BeginFull(ctx, repo, "abc123") + step := backup.Steps[len(backup.Steps)-1] w, err := backupSink.GetWriter(ctx, step.BundlePath) require.NoError(t, err) @@ -107,7 +109,7 @@ func TestRestoreRepository(t *testing.T) { require.NoError(t, err) require.NoError(t, w.Close()) - require.NoError(t, backupLocator.Commit(ctx, step)) + require.NoError(t, backupLocator.Commit(ctx, backup)) return setupData{ cfg: cfg, |