diff options
author | James Fargher <jfargher@gitlab.com> | 2023-09-27 01:21:35 +0300 |
---|---|---|
committer | James Fargher <jfargher@gitlab.com> | 2023-10-12 05:45:07 +0300 |
commit | b2917103ebd854e4ce3e94440adc456d9b26740d (patch) | |
tree | f42644b1586c4b1746cb1874ee63d3ffe1996739 | |
parent | 8bd3ca698d66b47af95f3c5ed5907ef7bf59c7a2 (diff) |
backup: Detect object format when creating a backup
Eventually the manifest paths will be blank if that file was not
written. For example, when backing up an empty repository the BundlePath
would be empty. This will ensure that new files cannot be introduced to
the backup after the fact.
Changelog: changed
-rw-r--r-- | internal/backup/backup.go | 10 | ||||
-rw-r--r-- | internal/backup/backup_test.go | 67 | ||||
-rw-r--r-- | internal/backup/repository.go | 24 |
3 files changed, 90 insertions, 11 deletions
diff --git a/internal/backup/backup.go b/internal/backup/backup.go index d730c2c93..af3e845f0 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -104,6 +104,8 @@ type Repository interface { FetchBundle(ctx context.Context, reader io.Reader) error // SetCustomHooks updates the custom hooks for the repository. SetCustomHooks(ctx context.Context, reader io.Reader) error + // ObjectHash detects the object hash used by the repository. + ObjectHash(ctx context.Context) (git.ObjectHash, error) } // ResolveLocator returns a locator implementation based on a locator identifier. @@ -224,13 +226,19 @@ func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error { backup = mgr.locator.BeginFull(ctx, req.VanityRepository, req.BackupID) } - refs, err := repo.ListRefs(ctx) + hash, err := repo.ObjectHash(ctx) switch { case status.Code(err) == codes.NotFound: return fmt.Errorf("manager: repository not found: %w", ErrSkipped) case err != nil: return fmt.Errorf("manager: %w", err) } + backup.ObjectFormat = hash.Format + + refs, err := repo.ListRefs(ctx) + if err != nil { + return fmt.Errorf("manager: %w", err) + } step := &backup.Steps[len(backup.Steps)-1] diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index 97e35c455..ab4acdd08 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -111,13 +111,14 @@ func TestManager_Create(t *testing.T) { } { type setupData struct { - repo *gitalypb.Repository - repoPath string + repo *gitalypb.Repository + repoPath string + expectedBackup *backup.Backup } for _, tc := range []struct { desc string - setup func(tb testing.TB) setupData + setup func(tb testing.TB, vanityRepo storage.Repository) setupData createsRefList bool createsBundle bool createsCustomHooks bool @@ -125,13 +126,25 @@ func TestManager_Create(t *testing.T) { }{ { desc: "no hooks", - setup: func(tb testing.TB) setupData { + setup: func(tb testing.TB, vanityRepo storage.Repository) setupData { repo, repoPath := gittest.CreateRepository(tb, ctx, cfg) gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch(git.DefaultBranch)) return setupData{ repo: repo, repoPath: repoPath, + expectedBackup: &backup.Backup{ + ID: backupID, + Repository: vanityRepo, + ObjectFormat: gittest.DefaultObjectHash.Format, + Steps: []backup.Step{ + { + BundlePath: joinBackupPath(t, "", vanityRepo, backupID, "001.bundle"), + RefPath: joinBackupPath(t, "", vanityRepo, backupID, "001.refs"), + CustomHooksPath: joinBackupPath(t, "", vanityRepo, backupID, "001.custom_hooks.tar"), + }, + }, + }, } }, createsRefList: true, @@ -140,7 +153,7 @@ func TestManager_Create(t *testing.T) { }, { desc: "hooks", - setup: func(tb testing.TB) setupData { + setup: func(tb testing.TB, vanityRepo storage.Repository) setupData { repo, repoPath := gittest.CreateRepository(tb, ctx, cfg) gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch(git.DefaultBranch)) require.NoError(tb, os.Mkdir(filepath.Join(repoPath, "custom_hooks"), perm.PublicDir)) @@ -149,6 +162,18 @@ func TestManager_Create(t *testing.T) { return setupData{ repo: repo, repoPath: repoPath, + expectedBackup: &backup.Backup{ + ID: backupID, + Repository: vanityRepo, + ObjectFormat: gittest.DefaultObjectHash.Format, + Steps: []backup.Step{ + { + BundlePath: joinBackupPath(t, "", vanityRepo, backupID, "001.bundle"), + RefPath: joinBackupPath(t, "", vanityRepo, backupID, "001.refs"), + CustomHooksPath: joinBackupPath(t, "", vanityRepo, backupID, "001.custom_hooks.tar"), + }, + }, + }, } }, createsRefList: true, @@ -157,12 +182,24 @@ func TestManager_Create(t *testing.T) { }, { desc: "empty repo", - setup: func(tb testing.TB) setupData { + setup: func(tb testing.TB, vanityRepo storage.Repository) setupData { emptyRepo, repoPath := gittest.CreateRepository(tb, ctx, cfg) return setupData{ repo: emptyRepo, repoPath: repoPath, + expectedBackup: &backup.Backup{ + ID: backupID, + Repository: vanityRepo, + ObjectFormat: gittest.DefaultObjectHash.Format, + Steps: []backup.Step{ + { + BundlePath: joinBackupPath(t, "", vanityRepo, backupID, "001.bundle"), + RefPath: joinBackupPath(t, "", vanityRepo, backupID, "001.refs"), + CustomHooksPath: joinBackupPath(t, "", vanityRepo, backupID, "001.custom_hooks.tar"), + }, + }, + }, } }, createsRefList: true, @@ -171,7 +208,7 @@ func TestManager_Create(t *testing.T) { }, { desc: "nonexistent repo", - setup: func(tb testing.TB) setupData { + setup: func(tb testing.TB, vanityRepo storage.Repository) setupData { emptyRepo, repoPath := gittest.CreateRepository(tb, ctx, cfg) nonexistentRepo := proto.Clone(emptyRepo).(*gitalypb.Repository) nonexistentRepo.RelativePath = gittest.NewRepositoryName(t) @@ -188,13 +225,14 @@ func TestManager_Create(t *testing.T) { }, } { t.Run(tc.desc, func(t *testing.T) { - data := tc.setup(t) backupRoot := testhelper.TempDir(t) vanityRepo := &gitalypb.Repository{ RelativePath: "some/path.git", StorageName: "some_storage", } + data := tc.setup(t, vanityRepo) + 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") @@ -258,6 +296,15 @@ func TestManager_Create(t *testing.T) { } else { require.NoFileExists(t, customHooksPath) } + + if data.expectedBackup == nil { + _, err := locator.Find(ctx, vanityRepo, backupID) + require.ErrorIs(t, err, backup.ErrDoesntExist) + } else { + backup, err := locator.Find(ctx, vanityRepo, backupID) + require.NoError(t, err) + require.Equal(t, data.expectedBackup, backup) + } }) } } @@ -1033,14 +1080,14 @@ func TestResolveLocator(t *testing.T) { } } -func joinBackupPath(tb testing.TB, backupRoot string, repo *gitalypb.Repository, elements ...string) string { +func joinBackupPath(tb testing.TB, backupRoot string, repo storage.Repository, elements ...string) string { return filepath.Join(append([]string{ backupRoot, stripRelativePath(tb, repo), }, elements...)...) } -func stripRelativePath(tb testing.TB, repo *gitalypb.Repository) string { +func stripRelativePath(tb testing.TB, repo storage.Repository) string { return strings.TrimSuffix(repo.GetRelativePath(), ".git") } diff --git a/internal/backup/repository.go b/internal/backup/repository.go index 2ef51046a..a92ba5f41 100644 --- a/internal/backup/repository.go +++ b/internal/backup/repository.go @@ -278,6 +278,20 @@ func (rr *remoteRepository) SetCustomHooks(ctx context.Context, reader io.Reader return nil } +// ObjectHash detects the object hash used by the repository. +func (rr *remoteRepository) ObjectHash(ctx context.Context) (git.ObjectHash, error) { + repoClient := rr.newRepoClient() + + response, err := repoClient.ObjectFormat(ctx, &gitalypb.ObjectFormatRequest{ + Repository: rr.repo, + }) + if err != nil { + return git.ObjectHash{}, fmt.Errorf("remote repository: object hash: %w", err) + } + + return git.ObjectHashByProto(response.Format) +} + func (rr *remoteRepository) newRepoClient() gitalypb.RepositoryServiceClient { return gitalypb.NewRepositoryServiceClient(rr.conn) } @@ -416,3 +430,13 @@ func (r *localRepository) SetCustomHooks(ctx context.Context, reader io.Reader) } return nil } + +// ObjectHash detects the object hash used by the repository. +func (r *localRepository) ObjectHash(ctx context.Context) (git.ObjectHash, error) { + hash, err := r.repo.ObjectHash(ctx) + if err != nil { + return git.ObjectHash{}, fmt.Errorf("local repository: object hash: %w", err) + } + + return hash, nil +} |