diff options
Diffstat (limited to 'internal/backup/backup_test.go')
-rw-r--r-- | internal/backup/backup_test.go | 179 |
1 files changed, 130 insertions, 49 deletions
diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index d5027d0be..f7cddf10c 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -1,6 +1,7 @@ package backup_test import ( + "bytes" "context" "fmt" "os" @@ -21,6 +22,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/transaction" "gitlab.com/gitlab-org/gitaly/v16/internal/grpc/client" "gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm" + "gitlab.com/gitlab-org/gitaly/v16/internal/log" "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg" "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver" @@ -567,22 +569,22 @@ func TestManager_Restore_latest(t *testing.T) { for _, managerTC := range []struct { desc string - setup func(t testing.TB, sink backup.Sink, locator backup.Locator) *backup.Manager + setup func(t testing.TB, sink backup.Sink, locator backup.Locator, logger log.LogrusLogger) *backup.Manager }{ { desc: "RPC manager", - setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator) *backup.Manager { + setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator, logger log.LogrusLogger) *backup.Manager { pool := client.NewPool() tb.Cleanup(func() { testhelper.MustClose(tb, pool) }) - return backup.NewManager(sink, testhelper.SharedLogger(t), locator, pool) + return backup.NewManager(sink, logger, locator, pool) }, }, { desc: "Local manager", - setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator) *backup.Manager { + setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator, logger log.LogrusLogger) *backup.Manager { if testhelper.IsPraefectEnabled() { tb.Skip("local backup manager expects to operate on the local filesystem so cannot operate through praefect") } @@ -593,7 +595,7 @@ func TestManager_Restore_latest(t *testing.T) { tb.Cleanup(catfileCache.Stop) txManager := transaction.NewTrackingManager() - return backup.NewManagerLocal(sink, testhelper.SharedLogger(t), locator, storageLocator, gitCmdFactory, catfileCache, txManager, repoCounter) + return backup.NewManagerLocal(sink, logger, locator, storageLocator, gitCmdFactory, catfileCache, txManager, repoCounter) }, }, } { @@ -610,45 +612,73 @@ func TestManager_Restore_latest(t *testing.T) { repoClient := gitalypb.NewRepositoryServiceClient(cc) - _, repoPath := gittest.CreateRepository(t, ctx, cfg) - commitID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main")) - gittest.WriteTag(t, cfg, repoPath, "v1.0.0", commitID.Revision()) - repoChecksum := gittest.ChecksumRepo(t, cfg, repoPath) - repoBundle := gittest.BundleRepo(t, cfg, repoPath, "-") - repoRefs := gittest.Exec(t, cfg, "-C", repoPath, "show-ref", "--head") - backupRoot := testhelper.TempDir(t) for _, tc := range []struct { - desc string - locators []string - setup func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) - alwaysCreate bool - expectExists bool - expectedPaths []string - expectedErrAs error + desc string + locators []string + setup func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) + alwaysCreate bool + expectExists bool + expectedPaths []string + expectedErrAs error + shouldUseResetRefsOptimisation bool }{ { - desc: "existing repo, without hooks", + desc: "non-optimised", locators: []string{"legacy", "pointer"}, setup: func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) { - repo, _ := gittest.CreateRepository(t, ctx, cfg) + _, repoPath, _ := createAndSeedRepository(t, ctx, cfg) + repoChecksum, repoBundle, repoRefs := createBackupArtifacts(t, cfg, repoPath) + // Restoring into an empty repo, so ref reset won't work. + repo, _ := gittest.CreateRepository(t, ctx, cfg) relativePath := stripRelativePath(tb, repo) testhelper.WriteFiles(tb, backupRoot, map[string]any{ relativePath + ".bundle": repoBundle, relativePath + ".refs": repoRefs, }) + customHooksPath := filepath.Join(backupRoot, relativePath, "custom_hooks.tar") + require.NoError(tb, os.MkdirAll(filepath.Join(backupRoot, relativePath), perm.PublicDir)) + testhelper.CopyFile(tb, mustCreateCustomHooksArchive(t, ctx), customHooksPath) + return repo, repoChecksum }, + expectedPaths: []string{ + "custom_hooks/pre-commit.sample", + "custom_hooks/prepare-commit-msg.sample", + "custom_hooks/pre-push.sample", + }, expectExists: true, }, { + desc: "existing repo, without hooks", + locators: []string{"legacy", "pointer"}, + setup: func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) { + repo, repoPath, head := createAndSeedRepository(t, ctx, cfg) + repoChecksum, repoBundle, repoRefs := createBackupArtifacts(t, cfg, repoPath) + gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(head), gittest.WithBranch("main")) + + relativePath := stripRelativePath(tb, repo) + testhelper.WriteFiles(tb, backupRoot, map[string]any{ + relativePath + ".bundle": repoBundle, + relativePath + ".refs": repoRefs, + }) + + return repo, repoChecksum + }, + expectExists: true, + shouldUseResetRefsOptimisation: true, + }, + + { desc: "existing repo, with hooks", locators: []string{"legacy", "pointer"}, setup: func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) { - repo, _ := gittest.CreateRepository(t, ctx, cfg) + repo, repoPath, head := createAndSeedRepository(t, ctx, cfg) + repoChecksum, repoBundle, repoRefs := createBackupArtifacts(t, cfg, repoPath) + gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(head), gittest.WithBranch("main")) relativePath := stripRelativePath(tb, repo) customHooksPath := filepath.Join(backupRoot, relativePath, "custom_hooks.tar") @@ -667,7 +697,8 @@ func TestManager_Restore_latest(t *testing.T) { "custom_hooks/prepare-commit-msg.sample", "custom_hooks/pre-push.sample", }, - expectExists: true, + expectExists: true, + shouldUseResetRefsOptimisation: true, }, { desc: "missing backup", @@ -723,27 +754,33 @@ func TestManager_Restore_latest(t *testing.T) { desc: "nonexistent repo", locators: []string{"legacy", "pointer"}, setup: func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) { - repo := &gitalypb.Repository{ + _, repoPath, _ := createAndSeedRepository(t, ctx, cfg) + repoChecksum, repoBundle, repoRefs := createBackupArtifacts(t, cfg, repoPath) + + nonexistentRepo := &gitalypb.Repository{ StorageName: "default", RelativePath: gittest.NewRepositoryName(tb), } - relativePath := stripRelativePath(tb, repo) + relativePath := stripRelativePath(tb, nonexistentRepo) testhelper.WriteFiles(tb, backupRoot, map[string]any{ relativePath + ".bundle": repoBundle, relativePath + ".refs": repoRefs, }) - return repo, repoChecksum + return nonexistentRepo, repoChecksum }, - expectExists: true, + expectExists: true, + shouldUseResetRefsOptimisation: true, }, { desc: "single incremental", locators: []string{"pointer"}, setup: func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) { const backupID = "abc123" - repo, _ := gittest.CreateRepository(t, ctx, cfg) + repo, repoPath, head := createAndSeedRepository(t, ctx, cfg) + repoChecksum, repoBundle, repoRefs := createBackupArtifacts(t, cfg, repoPath) + gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(head), gittest.WithBranch("main")) relativePath := stripRelativePath(tb, repo) testhelper.WriteFiles(tb, backupRoot, map[string]any{ @@ -755,14 +792,15 @@ func TestManager_Restore_latest(t *testing.T) { return repo, repoChecksum }, - expectExists: true, + expectExists: true, + shouldUseResetRefsOptimisation: true, }, { desc: "single incremental, empty backup", locators: []string{"pointer"}, setup: func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) { const backupID = "abc123" - repo, _ := gittest.CreateRepository(t, ctx, cfg) + repo, _, _ := createAndSeedRepository(t, ctx, cfg) relativePath := stripRelativePath(tb, repo) testhelper.WriteFiles(tb, backupRoot, map[string]any{ @@ -834,7 +872,8 @@ func TestManager_Restore_latest(t *testing.T) { return repo, checksum }, - expectExists: true, + expectExists: true, + shouldUseResetRefsOptimisation: true, }, } { t.Run(tc.desc, func(t *testing.T) { @@ -850,7 +889,12 @@ func TestManager_Restore_latest(t *testing.T) { locator, err := backup.ResolveLocator(locatorName, sink) require.NoError(t, err) - fsBackup := managerTC.setup(t, sink, locator) + loggerOutput := &bytes.Buffer{} + logger := testhelper.NewLogger(t, + testhelper.WithLoggerName(fmt.Sprintf("%s-%s", locatorName, tc.desc)), + testhelper.WithOutput(loggerOutput)) + + fsBackup := managerTC.setup(t, sink, locator, logger) err = fsBackup.Restore(ctx, &backup.RestoreRequest{ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, Repository: repo, @@ -888,6 +932,12 @@ func TestManager_Restore_latest(t *testing.T) { require.FileExists(t, filepath.Join(repoPath, p)) } } + + if tc.shouldUseResetRefsOptimisation { + require.NotContains(t, loggerOutput.String(), "unable to reset refs. Proceeding with a normal restore") + } else { + require.Contains(t, loggerOutput.String(), "unable to reset refs. Proceeding with a normal restore") + } }) } }) @@ -908,22 +958,22 @@ func TestManager_Restore_specific(t *testing.T) { for _, managerTC := range []struct { desc string - setup func(t testing.TB, sink backup.Sink, locator backup.Locator) *backup.Manager + setup func(t testing.TB, sink backup.Sink, locator backup.Locator, logger log.LogrusLogger) *backup.Manager }{ { desc: "RPC manager", - setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator) *backup.Manager { + setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator, logger log.LogrusLogger) *backup.Manager { pool := client.NewPool() tb.Cleanup(func() { testhelper.MustClose(tb, pool) }) - return backup.NewManager(sink, testhelper.SharedLogger(t), locator, pool) + return backup.NewManager(sink, logger, locator, pool) }, }, { desc: "Local manager", - setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator) *backup.Manager { + setup: func(tb testing.TB, sink backup.Sink, locator backup.Locator, logger log.LogrusLogger) *backup.Manager { if testhelper.IsPraefectEnabled() { tb.Skip("local backup manager expects to operate on the local filesystem so cannot operate through praefect") } @@ -934,7 +984,7 @@ func TestManager_Restore_specific(t *testing.T) { tb.Cleanup(catfileCache.Stop) txManager := transaction.NewTrackingManager() - return backup.NewManagerLocal(sink, testhelper.SharedLogger(t), locator, storageLocator, gitCmdFactory, catfileCache, txManager, repoCounter) + return backup.NewManagerLocal(sink, logger, locator, storageLocator, gitCmdFactory, catfileCache, txManager, repoCounter) }, }, } { @@ -961,13 +1011,14 @@ func TestManager_Restore_specific(t *testing.T) { backupRoot := testhelper.TempDir(t) for _, tc := range []struct { - desc string - setup func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) - alwaysCreate bool - expectExists bool - expectedPaths []string - expectedErrAs error - expectedHeadReference git.ReferenceName + desc string + setup func(tb testing.TB) (*gitalypb.Repository, *git.Checksum) + alwaysCreate bool + expectExists bool + expectedPaths []string + expectedErrAs error + expectedHeadReference git.ReferenceName + shouldUseResetRefsOptimisation bool }{ { desc: "missing backup", @@ -996,7 +1047,8 @@ func TestManager_Restore_specific(t *testing.T) { return repo, repoChecksum }, - expectExists: true, + expectExists: true, + shouldUseResetRefsOptimisation: true, }, { desc: "many incrementals", @@ -1056,7 +1108,8 @@ func TestManager_Restore_specific(t *testing.T) { return repo, checksum }, - expectExists: true, + expectExists: true, + shouldUseResetRefsOptimisation: true, }, { desc: "manifest, empty backup", @@ -1108,8 +1161,9 @@ custom_hooks_path = 'custom_hooks.tar' return repo, checksum }, - expectExists: true, - expectedHeadReference: "refs/heads/banana", + expectExists: true, + expectedHeadReference: "refs/heads/banana", + shouldUseResetRefsOptimisation: true, }, } { t.Run(tc.desc, func(t *testing.T) { @@ -1121,7 +1175,12 @@ custom_hooks_path = 'custom_hooks.tar' locator, err := backup.ResolveLocator("pointer", sink) require.NoError(t, err) - fsBackup := managerTC.setup(t, sink, locator) + loggerOutput := &bytes.Buffer{} + logger := testhelper.NewLogger(t, + testhelper.WithLoggerName(tc.desc), + testhelper.WithOutput(loggerOutput)) + + fsBackup := managerTC.setup(t, sink, locator, logger) err = fsBackup.Restore(ctx, &backup.RestoreRequest{ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, Repository: repo, @@ -1167,6 +1226,12 @@ custom_hooks_path = 'custom_hooks.tar' ref := gittest.GetSymbolicRef(t, cfg, repoPath, "HEAD") require.Equal(t, tc.expectedHeadReference, git.ReferenceName(ref.Target)) } + + if tc.shouldUseResetRefsOptimisation { + require.NotContains(t, loggerOutput.String(), "unable to reset refs. Proceeding with a normal restore") + } else { + require.Contains(t, loggerOutput.String(), "unable to reset refs. Proceeding with a normal restore") + } }) } }) @@ -1275,3 +1340,19 @@ func mustCreateCustomHooksArchive(t *testing.T, ctx context.Context) string { return archivePath } + +func createAndSeedRepository(t *testing.T, ctx context.Context, cfg config.Cfg) (repo *gitalypb.Repository, repoPath string, head git.ObjectID) { + repo, repoPath = gittest.CreateRepository(t, ctx, cfg) + commitID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main")) + gittest.WriteTag(t, cfg, repoPath, "v1.0.0", commitID.Revision()) + + return repo, repoPath, commitID +} + +func createBackupArtifacts(t *testing.T, cfg config.Cfg, repoPath string) (repoChecksum *git.Checksum, repoBundle []byte, repoRefs []byte) { + repoChecksum = gittest.ChecksumRepo(t, cfg, repoPath) + repoBundle = gittest.BundleRepo(t, cfg, repoPath, "-") + repoRefs = gittest.Exec(t, cfg, "-C", repoPath, "show-ref", "--head") + + return repoChecksum, repoBundle, repoRefs +} |