Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Fargher <jfargher@gitlab.com>2023-09-27 01:21:35 +0300
committerJames Fargher <jfargher@gitlab.com>2023-10-12 05:45:07 +0300
commitb2917103ebd854e4ce3e94440adc456d9b26740d (patch)
treef42644b1586c4b1746cb1874ee63d3ffe1996739
parent8bd3ca698d66b47af95f3c5ed5907ef7bf59c7a2 (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.go10
-rw-r--r--internal/backup/backup_test.go67
-rw-r--r--internal/backup/repository.go24
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
+}