diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-02-24 12:39:08 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-02-24 12:39:08 +0300 |
commit | c35fba1c073deed91d1a1f9f11dd668856841d80 (patch) | |
tree | eb65fbc383d6e3c1ac5241ca599c0445968dc174 | |
parent | 1d9db407eaf8c890aedd457151de836f532d3eab (diff) | |
parent | 5534d65713f23389ab606eabccde3b0db37de8cf (diff) |
Merge branch 'smh-test-rewrite-relative-path' into 'master'
Prepare tests for relative path rewriting
See merge request gitlab-org/gitaly!4360
34 files changed, 279 insertions, 335 deletions
diff --git a/cmd/gitaly-backup/restore_test.go b/cmd/gitaly-backup/restore_test.go index 2f8e535b9..9414c28ba 100644 --- a/cmd/gitaly-backup/restore_test.go +++ b/cmd/gitaly-backup/restore_test.go @@ -6,12 +6,10 @@ import ( "flag" "fmt" "io" - "os" "path/filepath" "testing" "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/gitaly/v14/client" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/setup" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" @@ -27,33 +25,14 @@ func TestRestoreSubcommand(t *testing.T) { cfg := testcfg.Build(t) testcfg.BuildGitalyHooks(t, cfg) - gitalyAddr := testserver.RunGitalyServer(t, cfg, nil, setup.RegisterAll) + cfg.SocketPath = testserver.RunGitalyServer(t, cfg, nil, setup.RegisterAll) - path := testhelper.TempDir(t) - - conn, err := client.Dial(gitalyAddr, nil) - require.NoError(t, err) - defer testhelper.MustClose(t, conn) - - existingRepo := &gitalypb.Repository{ + existingRepo, existRepoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ + Seed: gittest.SeedGitLabTest, RelativePath: "existing_repo", - StorageName: cfg.Storages[0].Name, - } - - // We need to create the repository entry in the database such that we can call - // `RemoveRepository()` and have Praefect forward the request to Gitaly as expected. - repoClient := gitalypb.NewRepositoryServiceClient(conn) - _, err = repoClient.CreateRepository(ctx, &gitalypb.CreateRepositoryRequest{Repository: existingRepo}) - require.NoError(t, err) - existingRepo.RelativePath = testhelper.GetReplicaPath(ctx, t, conn, existingRepo) - - // This is a bit awkward: we have to delete the created repository on-disk again and - // recreate it with the seed repository. - existRepoPath := filepath.Join(cfg.Storages[0].Path, existingRepo.RelativePath) - require.NoError(t, os.RemoveAll(existRepoPath)) - gittest.CloneRepo(t, cfg, cfg.Storages[0], gittest.CloneRepoOpts{ - RelativePath: existingRepo.RelativePath, }) + + path := testhelper.TempDir(t) existingRepoBundlePath := filepath.Join(path, existingRepo.RelativePath+".bundle") gittest.Exec(t, cfg, "-C", existRepoPath, "bundle", "create", existingRepoBundlePath, "--all") @@ -70,7 +49,7 @@ func TestRestoreSubcommand(t *testing.T) { encoder := json.NewEncoder(&stdin) for _, repo := range repos { require.NoError(t, encoder.Encode(map[string]string{ - "address": gitalyAddr, + "address": cfg.SocketPath, "token": cfg.Auth.Token, "storage_name": repo.StorageName, "relative_path": repo.RelativePath, @@ -95,7 +74,7 @@ func TestRestoreSubcommand(t *testing.T) { "restore: pipeline: 1 failures encountered:\n - invalid: manager: remove repository: could not dial source: invalid connection string: \"invalid\"\n") for _, repo := range repos { - repoPath := filepath.Join(cfg.Storages[0].Path, repo.RelativePath) + repoPath := filepath.Join(cfg.Storages[0].Path, gittest.GetReplicaPath(ctx, t, cfg, repo)) bundlePath := filepath.Join(path, repo.RelativePath+".bundle") output := gittest.Exec(t, cfg, "-C", repoPath, "bundle", "verify", bundlePath) diff --git a/cmd/praefect/subcmd_remove_repository_test.go b/cmd/praefect/subcmd_remove_repository_test.go index 49bc60fd7..040dd40e5 100644 --- a/cmd/praefect/subcmd_remove_repository_test.go +++ b/cmd/praefect/subcmd_remove_repository_test.go @@ -220,6 +220,7 @@ func TestRemoveRepository_Exec(t *testing.T) { logger := testhelper.NewDiscardingLogger(t) loggerHook := test.NewLocal(logger) + cmd := &removeRepository{ logger: logrus.NewEntry(logger), virtualStorage: praefectStorage, diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index 1486aa211..330778397 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -1,7 +1,6 @@ package backup import ( - "context" "fmt" "os" "path/filepath" @@ -11,14 +10,10 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/client" "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" - "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config" - gitalylog "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config/log" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/setup" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/storage" - praefectConfig "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/config" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testcfg" - "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testdb" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testserver" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" "google.golang.org/protobuf/proto" @@ -37,42 +32,42 @@ func TestManager_Create(t *testing.T) { for _, tc := range []struct { desc string - setup func(t testing.TB) *gitalypb.Repository + setup func(t testing.TB) (*gitalypb.Repository, string) createsBundle bool createsCustomHooks bool err error }{ { desc: "no hooks", - setup: func(t testing.TB) *gitalypb.Repository { - noHooksRepo, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ + setup: func(t testing.TB) (*gitalypb.Repository, string) { + noHooksRepo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ RelativePath: "no-hooks", Seed: gittest.SeedGitLabTest, }) - return noHooksRepo + return noHooksRepo, repoPath }, createsBundle: true, createsCustomHooks: false, }, { desc: "hooks", - setup: func(t testing.TB) *gitalypb.Repository { + setup: func(t testing.TB) (*gitalypb.Repository, string) { hooksRepo, hooksRepoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ RelativePath: "hooks", Seed: gittest.SeedGitLabTest, }) require.NoError(t, os.Mkdir(filepath.Join(hooksRepoPath, "custom_hooks"), os.ModePerm)) require.NoError(t, os.WriteFile(filepath.Join(hooksRepoPath, "custom_hooks/pre-commit.sample"), []byte("Some hooks"), os.ModePerm)) - return hooksRepo + return hooksRepo, hooksRepoPath }, createsBundle: true, createsCustomHooks: true, }, { desc: "empty repo", - setup: func(t testing.TB) *gitalypb.Repository { - emptyRepo, _ := gittest.CreateRepository(ctx, t, cfg) - return emptyRepo + setup: func(t testing.TB) (*gitalypb.Repository, string) { + emptyRepo, repoPath := gittest.CreateRepository(ctx, t, cfg) + return emptyRepo, repoPath }, createsBundle: false, createsCustomHooks: false, @@ -80,11 +75,11 @@ func TestManager_Create(t *testing.T) { }, { desc: "nonexistent repo", - setup: func(t testing.TB) *gitalypb.Repository { - emptyRepo, _ := gittest.CreateRepository(ctx, t, cfg) + setup: func(t testing.TB) (*gitalypb.Repository, string) { + emptyRepo, repoPath := gittest.CreateRepository(ctx, t, cfg) nonexistentRepo := proto.Clone(emptyRepo).(*gitalypb.Repository) nonexistentRepo.RelativePath = "nonexistent" - return nonexistentRepo + return nonexistentRepo, repoPath }, createsBundle: false, createsCustomHooks: false, @@ -92,8 +87,7 @@ func TestManager_Create(t *testing.T) { }, } { t.Run(tc.desc, func(t *testing.T) { - repo := tc.setup(t) - repoPath := filepath.Join(cfg.Storages[0].Path, repo.RelativePath) + repo, repoPath := tc.setup(t) path := testhelper.TempDir(t) refsPath := filepath.Join(path, repo.RelativePath, backupID, "001.refs") bundlePath := filepath.Join(path, repo.RelativePath, backupID, "001.bundle") @@ -160,24 +154,24 @@ func TestManager_Create_incremental(t *testing.T) { for _, tc := range []struct { desc string - setup func(t testing.TB, backupRoot string) *gitalypb.Repository + setup func(t testing.TB, backupRoot string) (*gitalypb.Repository, string) expectedIncrement string expectedErr error }{ { desc: "no previous backup", - setup: func(t testing.TB, backupRoot string) *gitalypb.Repository { - repo, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ + setup: func(t testing.TB, backupRoot string) (*gitalypb.Repository, string) { + repo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ RelativePath: "repo", Seed: gittest.SeedGitLabTest, }) - return repo + return repo, repoPath }, expectedIncrement: "001", }, { desc: "previous backup, no updates", - setup: func(t testing.TB, backupRoot string) *gitalypb.Repository { + setup: func(t testing.TB, backupRoot string) (*gitalypb.Repository, string) { repo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ RelativePath: "repo", Seed: gittest.SeedGitLabTest, @@ -197,13 +191,13 @@ func TestManager_Create_incremental(t *testing.T) { require.NoError(t, os.WriteFile(filepath.Join(backupRepoPath, "LATEST"), []byte(backupID), os.ModePerm)) require.NoError(t, os.WriteFile(filepath.Join(backupPath, "LATEST"), []byte("001"), os.ModePerm)) - return repo + return repo, repoPath }, expectedErr: fmt.Errorf("manager: write bundle: %w", fmt.Errorf("*backup.FilesystemSink write: %w: no changes to bundle", ErrSkipped)), }, { desc: "previous backup, updates", - setup: func(t testing.TB, backupRoot string) *gitalypb.Repository { + setup: func(t testing.TB, backupRoot string) (*gitalypb.Repository, string) { repo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ RelativePath: "repo", Seed: gittest.SeedGitLabTest, @@ -225,16 +219,15 @@ func TestManager_Create_incremental(t *testing.T) { gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("master")) - return repo + return repo, repoPath }, expectedIncrement: "002", }, } { t.Run(tc.desc, func(t *testing.T) { path := testhelper.TempDir(t) - repo := tc.setup(t, path) + repo, repoPath := tc.setup(t, path) - repoPath := filepath.Join(cfg.Storages[0].Path, repo.RelativePath) refsPath := filepath.Join(path, repo.RelativePath, backupID, tc.expectedIncrement+".refs") bundlePath := filepath.Join(path, repo.RelativePath, backupID, tc.expectedIncrement+".bundle") @@ -274,49 +267,10 @@ func TestManager_Restore(t *testing.T) { cfg := testcfg.Build(t) testcfg.BuildGitalyHooks(t, cfg) - gitalyAddr := testserver.RunGitalyServer(t, cfg, nil, setup.RegisterAll) - ctx := testhelper.Context(t) - - testManagerRestore(t, ctx, cfg, gitalyAddr) -} - -func TestManager_Restore_praefect(t *testing.T) { - t.Parallel() - - gitalyCfg := testcfg.Build(t, testcfg.WithStorages("gitaly-1")) - - testcfg.BuildGitalyHooks(t, gitalyCfg) - - gitalyAddr := testserver.RunGitalyServer(t, gitalyCfg, nil, setup.RegisterAll, testserver.WithDisablePraefect()) - - conf := praefectConfig.Config{ - SocketPath: testhelper.GetTemporaryGitalySocketFileName(t), - VirtualStorages: []*praefectConfig.VirtualStorage{ - { - Name: "default", - Nodes: []*praefectConfig.Node{ - {Storage: gitalyCfg.Storages[0].Name, Address: gitalyAddr}, - }, - }, - }, - DB: testdb.GetConfig(t, testdb.New(t).Name), - Failover: praefectConfig.Failover{ - Enabled: true, - ElectionStrategy: praefectConfig.ElectionStrategyPerRepository, - }, - Replication: praefectConfig.DefaultReplicationConfig(), - Logging: gitalylog.Config{ - Format: "json", - Level: "panic", - }, - } + cfg.SocketPath = testserver.RunGitalyServer(t, cfg, nil, setup.RegisterAll) ctx := testhelper.Context(t) - testManagerRestore(t, ctx, gitalyCfg, testserver.StartPraefect(t, conf).Address()) -} - -func testManagerRestore(t *testing.T, ctx context.Context, cfg config.Cfg, gitalyAddr string) { - cc, err := client.Dial(gitalyAddr, nil) + cc, err := client.Dial(cfg.SocketPath, nil) require.NoError(t, err) defer testhelper.MustClose(t, cc) @@ -336,7 +290,7 @@ func testManagerRestore(t *testing.T, ctx context.Context, cfg config.Cfg, gital // The repository might be created through Praefect and the tests reach into the repository directly // on the filesystem. To ensure the test accesses correct directory, we need to rewrite the relative // path if the repository creation went through Praefect. - repo.RelativePath = testhelper.GetReplicaPath(ctx, t, cc, repo) + repo.RelativePath = gittest.GetReplicaPath(ctx, t, cfg, repo) return repo } @@ -515,7 +469,7 @@ func testManagerRestore(t *testing.T, ctx context.Context, cfg config.Cfg, gital fsBackup := NewManager(sink, locator, pool, "unused-backup-id") err = fsBackup.Restore(ctx, &RestoreRequest{ - Server: storage.ServerInfo{Address: gitalyAddr, Token: cfg.Auth.Token}, + Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, Repository: repo, AlwaysCreate: tc.alwaysCreate, }) @@ -544,7 +498,7 @@ func testManagerRestore(t *testing.T, ctx context.Context, cfg config.Cfg, gital // Restore has to use the rewritten path as the relative path due to the test creating // the repository through Praefect. In order to get to the correct disk paths, we need // to get the replica path of the rewritten repository. - repoPath := filepath.Join(cfg.Storages[0].Path, testhelper.GetReplicaPath(ctx, t, cc, repo)) + repoPath := filepath.Join(cfg.Storages[0].Path, gittest.GetReplicaPath(ctx, t, cfg, repo)) for _, p := range tc.expectedPaths { require.FileExists(t, filepath.Join(repoPath, p)) } diff --git a/internal/git/gittest/commit_test.go b/internal/git/gittest/commit_test.go index a05a70adb..515455252 100644 --- a/internal/git/gittest/commit_test.go +++ b/internal/git/gittest/commit_test.go @@ -1,4 +1,4 @@ -package gittest +package gittest_test import ( "testing" @@ -6,13 +6,16 @@ import ( "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/catfile" + "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/git/localrepo" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" + "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testcfg" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" ) func TestWriteCommit(t *testing.T) { - cfg, repoProto, repoPath := setup(t) + cfg, repoProto, repoPath := testcfg.BuildWithRepo(t) + repo := localrepo.NewTestRepo(t, cfg, repoProto) ctx := testhelper.Context(t) @@ -23,8 +26,8 @@ func TestWriteCommit(t *testing.T) { require.NoError(t, err) defaultCommitter := &gitalypb.CommitAuthor{ - Name: []byte(committerName), - Email: []byte(committerEmail), + Name: []byte("Scrooge McDuck"), + Email: []byte("scrooge@mcduck.com"), } defaultParentID := "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863" @@ -40,9 +43,9 @@ func TestWriteCommit(t *testing.T) { for _, tc := range []struct { desc string - opts []WriteCommitOption + opts []gittest.WriteCommitOption expectedCommit *gitalypb.GitCommit - expectedTreeEntries []TreeEntry + expectedTreeEntries []gittest.TreeEntry expectedRevUpdate git.Revision }{ { @@ -60,8 +63,8 @@ func TestWriteCommit(t *testing.T) { }, { desc: "with commit message", - opts: []WriteCommitOption{ - WithMessage("my custom message\n\nfoobar\n"), + opts: []gittest.WriteCommitOption{ + gittest.WithMessage("my custom message\n\nfoobar\n"), }, expectedCommit: &gitalypb.GitCommit{ Author: defaultCommitter, @@ -76,8 +79,8 @@ func TestWriteCommit(t *testing.T) { }, { desc: "with no parents", - opts: []WriteCommitOption{ - WithParents(), + opts: []gittest.WriteCommitOption{ + gittest.WithParents(), }, expectedCommit: &gitalypb.GitCommit{ Author: defaultCommitter, @@ -90,8 +93,8 @@ func TestWriteCommit(t *testing.T) { }, { desc: "with multiple parents", - opts: []WriteCommitOption{ - WithParents(revisions["refs/heads/master"], revisions["refs/heads/master~"]), + opts: []gittest.WriteCommitOption{ + gittest.WithParents(revisions["refs/heads/master"], revisions["refs/heads/master~"]), }, expectedCommit: &gitalypb.GitCommit{ Author: defaultCommitter, @@ -107,8 +110,8 @@ func TestWriteCommit(t *testing.T) { }, { desc: "with branch", - opts: []WriteCommitOption{ - WithBranch("foo"), + opts: []gittest.WriteCommitOption{ + gittest.WithBranch("foo"), }, expectedCommit: &gitalypb.GitCommit{ Author: defaultCommitter, @@ -124,8 +127,8 @@ func TestWriteCommit(t *testing.T) { }, { desc: "with tree entry", - opts: []WriteCommitOption{ - WithTreeEntries(TreeEntry{ + opts: []gittest.WriteCommitOption{ + gittest.WithTreeEntries(gittest.TreeEntry{ Content: "foobar", Mode: "100644", Path: "file", @@ -141,7 +144,7 @@ func TestWriteCommit(t *testing.T) { defaultParentID, }, }, - expectedTreeEntries: []TreeEntry{ + expectedTreeEntries: []gittest.TreeEntry{ { Content: "foobar", Mode: "100644", @@ -151,15 +154,15 @@ func TestWriteCommit(t *testing.T) { }, } { t.Run(tc.desc, func(t *testing.T) { - oid := WriteCommit(t, cfg, repoPath, tc.opts...) + oid := gittest.WriteCommit(t, cfg, repoPath, tc.opts...) commit, err := catfile.GetCommit(ctx, objectReader, oid.Revision()) require.NoError(t, err) - CommitEqual(t, tc.expectedCommit, commit) + gittest.CommitEqual(t, tc.expectedCommit, commit) if tc.expectedTreeEntries != nil { - RequireTree(t, cfg, repoPath, oid.String(), tc.expectedTreeEntries) + gittest.RequireTree(t, cfg, repoPath, oid.String(), tc.expectedTreeEntries) } if tc.expectedRevUpdate != "" { diff --git a/internal/git/gittest/repo.go b/internal/git/gittest/repo.go index 10167fb92..0f83a2535 100644 --- a/internal/git/gittest/repo.go +++ b/internal/git/gittest/repo.go @@ -14,6 +14,7 @@ import ( gitalyauth "gitlab.com/gitlab-org/gitaly/v14/auth" "gitlab.com/gitlab-org/gitaly/v14/client" "gitlab.com/gitlab-org/gitaly/v14/internal/git" + "gitlab.com/gitlab-org/gitaly/v14/internal/git/repository" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/v14/internal/helper/text" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" @@ -92,6 +93,17 @@ type CreateRepositoryConfig struct { Seed string } +func dialService(ctx context.Context, t testing.TB, cfg config.Cfg) *grpc.ClientConn { + dialOptions := []grpc.DialOption{} + if cfg.Auth.Token != "" { + dialOptions = append(dialOptions, grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2(cfg.Auth.Token))) + } + + conn, err := client.DialContext(ctx, cfg.SocketPath, dialOptions) + require.NoError(t, err) + return conn +} + // CreateRepository creates a new repository and returns it and its absolute path. func CreateRepository(ctx context.Context, t testing.TB, cfg config.Cfg, configs ...CreateRepositoryConfig) (*gitalypb.Repository, string) { t.Helper() @@ -105,14 +117,7 @@ func CreateRepository(ctx context.Context, t testing.TB, cfg config.Cfg, configs conn := opts.ClientConn if conn == nil { - dialOptions := []grpc.DialOption{} - if cfg.Auth.Token != "" { - dialOptions = append(dialOptions, grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2(cfg.Auth.Token))) - } - - var err error - conn, err = client.DialContext(ctx, cfg.SocketPath, dialOptions) - require.NoError(t, err) + conn = dialService(ctx, t, cfg) t.Cleanup(func() { conn.Close() }) } @@ -166,7 +171,47 @@ func CreateRepository(ctx context.Context, t testing.TB, cfg config.Cfg, configs // if the tests modify the returned repository. clonedRepo := proto.Clone(repository).(*gitalypb.Repository) - return clonedRepo, filepath.Join(storage.Path, testhelper.GetReplicaPath(ctx, t, conn, repository)) + return clonedRepo, filepath.Join(storage.Path, getReplicaPath(ctx, t, conn, repository)) +} + +// GetReplicaPath retrieves the repository's replica path if the test has been +// run with Praefect in front of it. This is necessary if the test creates a repository +// through Praefect and peeks into the filesystem afterwards. Conn should be pointing to +// Praefect. +func GetReplicaPath(ctx context.Context, t testing.TB, cfg config.Cfg, repo repository.GitRepo) string { + conn := dialService(ctx, t, cfg) + defer conn.Close() + + return getReplicaPath(ctx, t, conn, repo) +} + +func getReplicaPath(ctx context.Context, t testing.TB, conn *grpc.ClientConn, repo repository.GitRepo) string { + metadata, err := gitalypb.NewPraefectInfoServiceClient(conn).GetRepositoryMetadata( + ctx, &gitalypb.GetRepositoryMetadataRequest{ + Query: &gitalypb.GetRepositoryMetadataRequest_Path_{ + Path: &gitalypb.GetRepositoryMetadataRequest_Path{ + VirtualStorage: repo.GetStorageName(), + RelativePath: repo.GetRelativePath(), + }, + }, + }) + if status, ok := status.FromError(err); ok && status.Code() == codes.Unimplemented && status.Message() == "unknown service gitaly.PraefectInfoService" { + // The repository is stored at relative path if the test is running without Praefect in front. + return repo.GetRelativePath() + } + require.NoError(t, err) + + return metadata.ReplicaPath +} + +// RewrittenRepository returns the repository as it would be received by a Gitaly after being rewritten by Praefect. +// This should be used when the repository is being accessed through the filesystem to ensure the access path is +// correct. If the test is not running with Praefect in front, it returns the an unaltered copy of repository. +func RewrittenRepository(ctx context.Context, t testing.TB, cfg config.Cfg, repository *gitalypb.Repository) *gitalypb.Repository { + // Don't modify the original repository. + rewritten := proto.Clone(repository).(*gitalypb.Repository) + rewritten.RelativePath = GetReplicaPath(ctx, t, cfg, repository) + return rewritten } // InitRepoOpts contains options for InitRepo. diff --git a/internal/git/localrepo/repo.go b/internal/git/localrepo/repo.go index 6fcb32836..806b61553 100644 --- a/internal/git/localrepo/repo.go +++ b/internal/git/localrepo/repo.go @@ -9,9 +9,13 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/command" "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/catfile" + "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/git/repository" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/storage" + "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" + "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testcfg" + "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" ) // Repo represents a local Git repository. @@ -37,6 +41,15 @@ func New(locator storage.Locator, gitCmdFactory git.CommandFactory, catfileCache func NewTestRepo(t testing.TB, cfg config.Cfg, repo repository.GitRepo, factoryOpts ...git.ExecCommandFactoryOption) *Repo { t.Helper() + if cfg.SocketPath != testcfg.UnconfiguredSocketPath { + repo = gittest.RewrittenRepository(testhelper.Context(t), t, cfg, &gitalypb.Repository{ + StorageName: repo.GetStorageName(), + RelativePath: repo.GetRelativePath(), + GitObjectDirectory: repo.GetGitObjectDirectory(), + GitAlternateObjectDirectories: repo.GetGitAlternateObjectDirectories(), + }) + } + gitCmdFactory, cleanup, err := git.NewExecCommandFactory(cfg, factoryOpts...) t.Cleanup(cleanup) require.NoError(t, err) diff --git a/internal/gitaly/service/blob/blobs_test.go b/internal/gitaly/service/blob/blobs_test.go index f33484944..119254518 100644 --- a/internal/gitaly/service/blob/blobs_test.go +++ b/internal/gitaly/service/blob/blobs_test.go @@ -283,10 +283,19 @@ func TestListAllBlobs(t *testing.T) { ctx := testhelper.Context(t) cfg, repo, _, client := setup(ctx, t) - quarantine, err := quarantine.New(ctx, repo, config.NewLocator(cfg)) + quarantine, err := quarantine.New(ctx, gittest.RewrittenRepository(ctx, t, cfg, repo), config.NewLocator(cfg)) require.NoError(t, err) - quarantineRepoWithoutAlternates := proto.Clone(quarantine.QuarantinedRepo()).(*gitalypb.Repository) + // quarantine.New in Gitaly would receive an already rewritten repository. Gitaly would then calculate + // the quarantine directories based on the rewritten relative path. That quarantine would then be looped + // through Rails, which would then send a request with the quarantine object directories set based on the + // rewritten relative path but with the original relative path of the repository. Since we're using the production + // helpers here, we need to manually substitute the rewritten relative path with the original one when sending + // it back through the API. + quarantinedRepo := quarantine.QuarantinedRepo() + quarantinedRepo.RelativePath = repo.RelativePath + + quarantineRepoWithoutAlternates := proto.Clone(quarantinedRepo).(*gitalypb.Repository) quarantineRepoWithoutAlternates.GitAlternateObjectDirectories = []string{} emptyRepo, _ := gittest.CreateRepository(ctx, t, cfg) @@ -377,7 +386,7 @@ func TestListAllBlobs(t *testing.T) { { desc: "quarantine repo with alternates", request: &gitalypb.ListAllBlobsRequest{ - Repository: quarantine.QuarantinedRepo(), + Repository: quarantinedRepo, }, verify: func(t *testing.T, blobs []*gitalypb.ListAllBlobsResponse_Blob) { require.Greater(t, len(blobs), 300) diff --git a/internal/gitaly/service/commit/find_commit_test.go b/internal/gitaly/service/commit/find_commit_test.go index 8a57d56e0..54b2ad459 100644 --- a/internal/gitaly/service/commit/find_commit_test.go +++ b/internal/gitaly/service/commit/find_commit_test.go @@ -337,7 +337,7 @@ func TestFindCommitWithCache(t *testing.T) { // get a list of revisions gitCmdFactory := gittest.NewCommandFactory(t, cfg) - logCmd, err := gitCmdFactory.New(ctx, repo, + logCmd, err := gitCmdFactory.New(ctx, gittest.RewrittenRepository(ctx, t, cfg, repo), git.SubCmd{Name: "log", Flags: []git.Option{git.Flag{Name: "--format=format:%H"}}}) require.NoError(t, err) diff --git a/internal/gitaly/service/objectpool/alternates_test.go b/internal/gitaly/service/objectpool/alternates_test.go index 85ce42bbd..851dafb8e 100644 --- a/internal/gitaly/service/objectpool/alternates_test.go +++ b/internal/gitaly/service/objectpool/alternates_test.go @@ -82,7 +82,9 @@ func TestDisconnectGitAlternatesUnexpectedAlternates(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - repoProto, _ := gittest.CloneRepo(t, cfg, cfg.Storages[0]) + repoProto, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ + Seed: gittest.SeedGitLabTest, + }) repo := localrepo.NewTestRepo(t, cfg, repoProto) altPath, err := repo.InfoAlternatesPath() diff --git a/internal/gitaly/service/objectpool/create_test.go b/internal/gitaly/service/objectpool/create_test.go index 63ae86ebf..f7dc13371 100644 --- a/internal/gitaly/service/objectpool/create_test.go +++ b/internal/gitaly/service/objectpool/create_test.go @@ -30,9 +30,8 @@ func TestCreate(t *testing.T) { _, err := client.CreateObjectPool(ctx, poolReq) require.NoError(t, err) - defer func() { - require.NoError(t, pool.Remove(ctx)) - }() + + pool = rewrittenObjectPool(ctx, t, cfg, pool) // Checks if the underlying repository is valid require.True(t, pool.IsValid()) diff --git a/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go b/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go index a2583bd47..12e1376c3 100644 --- a/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go +++ b/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go @@ -49,6 +49,8 @@ func TestFetchIntoObjectPool_Success(t *testing.T) { _, err = client.FetchIntoObjectPool(ctx, req) require.NoError(t, err) + pool = rewrittenObjectPool(ctx, t, cfg, pool) + require.True(t, pool.IsValid(), "ensure underlying repository is valid") // No problems diff --git a/internal/gitaly/service/objectpool/link_test.go b/internal/gitaly/service/objectpool/link_test.go index a1a7fb154..49d9176a0 100644 --- a/internal/gitaly/service/objectpool/link_test.go +++ b/internal/gitaly/service/objectpool/link_test.go @@ -156,6 +156,8 @@ func TestLinkNoPool(t *testing.T) { _, err = client.LinkRepositoryToObjectPool(ctx, request) require.NoError(t, err) + pool = rewrittenObjectPool(ctx, t, cfg, pool) + poolRepoPath, err := locator.GetRepoPath(pool) require.NoError(t, err) diff --git a/internal/gitaly/service/objectpool/testhelper_test.go b/internal/gitaly/service/objectpool/testhelper_test.go index b0e35f6e3..36b349132 100644 --- a/internal/gitaly/service/objectpool/testhelper_test.go +++ b/internal/gitaly/service/objectpool/testhelper_test.go @@ -79,14 +79,10 @@ func runObjectPoolServer(t *testing.T, cfg config.Cfg, locator storage.Locator, }, append(opts, testserver.WithLocator(locator), testserver.WithLogger(logger))...) } -// initObjectPool creates a new empty object pool in the given storage. -func initObjectPool(t testing.TB, cfg config.Cfg, storage config.Storage) *objectpool.ObjectPool { - t.Helper() - - relativePath := gittest.NewObjectPoolName(t) - gittest.InitRepoDir(t, storage.Path, relativePath) +func newObjectPool(t testing.TB, cfg config.Cfg, storage, relativePath string) *objectpool.ObjectPool { catfileCache := catfile.NewCache(cfg) t.Cleanup(catfileCache.Stop) + txManager := transaction.NewManager(cfg, backchannel.NewRegistry()) pool, err := objectpool.NewObjectPool( @@ -95,13 +91,34 @@ func initObjectPool(t testing.TB, cfg config.Cfg, storage config.Storage) *objec catfileCache, txManager, housekeeping.NewManager(txManager), - storage.Name, + storage, relativePath, ) require.NoError(t, err) + return pool +} + +// initObjectPool creates a new empty object pool in the given storage. +func initObjectPool(t testing.TB, cfg config.Cfg, storage config.Storage) *objectpool.ObjectPool { + t.Helper() + + relativePath := gittest.NewObjectPoolName(t) + gittest.InitRepoDir(t, storage.Path, relativePath) + catfileCache := catfile.NewCache(cfg) + t.Cleanup(catfileCache.Stop) + + pool := newObjectPool(t, cfg, storage.Name, relativePath) + poolPath := filepath.Join(storage.Path, relativePath) t.Cleanup(func() { require.NoError(t, os.RemoveAll(poolPath)) }) return pool } + +// rewrittenObjectPool returns a pool that is rewritten as if it was passed through Praefect. This should be used +// to access the pool on the disk if the tests are running with Praefect in front of them. +func rewrittenObjectPool(ctx context.Context, t testing.TB, cfg config.Cfg, pool *objectpool.ObjectPool) *objectpool.ObjectPool { + replicaPath := gittest.GetReplicaPath(ctx, t, cfg, pool) + return newObjectPool(t, cfg, pool.GetStorageName(), replicaPath) +} diff --git a/internal/gitaly/service/operations/apply_patch_test.go b/internal/gitaly/service/operations/apply_patch_test.go index 5bf66a9fe..c7e5ffb15 100644 --- a/internal/gitaly/service/operations/apply_patch_test.go +++ b/internal/gitaly/service/operations/apply_patch_test.go @@ -278,6 +278,7 @@ To restore the original branch and stop patching, run "git am --abort". repoPb, repoPath := gittest.CreateRepository(ctx, t, cfg) repo := localrepo.NewTestRepo(t, cfg, repoPb) + rewrittenRepo := gittest.RewrittenRepository(ctx, t, cfg, repoPb) executor := git2go.NewExecutor(cfg, gittest.NewCommandFactory(t, cfg), config.NewLocator(cfg)) @@ -290,7 +291,7 @@ To restore the original branch and stop patching, run "git am --abort". var baseCommit git.ObjectID for _, action := range tc.baseCommit { var err error - baseCommit, err = executor.Commit(ctx, repoPb, git2go.CommitParams{ + baseCommit, err = executor.Commit(ctx, rewrittenRepo, git2go.CommitParams{ Repository: repoPath, Author: author, Committer: committer, @@ -306,7 +307,7 @@ To restore the original branch and stop patching, run "git am --abort". } if tc.extraBranches != nil { - emptyCommit, err := executor.Commit(ctx, repoPb, git2go.CommitParams{ + emptyCommit, err := executor.Commit(ctx, rewrittenRepo, git2go.CommitParams{ Repository: repoPath, Author: author, Committer: committer, @@ -326,7 +327,7 @@ To restore the original branch and stop patching, run "git am --abort". commit := baseCommit for _, action := range commitActions { var err error - commit, err = executor.Commit(ctx, repoPb, git2go.CommitParams{ + commit, err = executor.Commit(ctx, rewrittenRepo, git2go.CommitParams{ Repository: repoPath, Author: author, Committer: committer, diff --git a/internal/gitaly/service/remote/update_remote_mirror_test.go b/internal/gitaly/service/remote/update_remote_mirror_test.go index 52cb9aafb..5d624c531 100644 --- a/internal/gitaly/service/remote/update_remote_mirror_test.go +++ b/internal/gitaly/service/remote/update_remote_mirror_test.go @@ -493,6 +493,8 @@ func TestUpdateRemoteMirror(t *testing.T) { }, } { t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) cfg := testcfg.Build(t) @@ -555,13 +557,14 @@ func TestUpdateRemoteMirror(t *testing.T) { var commitOID git.ObjectID for _, commit := range commits { var err error - commitOID, err = executor.Commit(ctx, c.repoProto, git2go.CommitParams{ - Repository: c.repoPath, - Author: commitSignature, - Committer: commitSignature, - Message: commit, - Parent: commitOID.String(), - }) + commitOID, err = executor.Commit(ctx, gittest.RewrittenRepository(ctx, t, cfg, c.repoProto), + git2go.CommitParams{ + Repository: c.repoPath, + Author: commitSignature, + Committer: commitSignature, + Message: commit, + Parent: commitOID.String(), + }) require.NoError(t, err) } diff --git a/internal/gitaly/service/repository/apply_gitattributes_test.go b/internal/gitaly/service/repository/apply_gitattributes_test.go index 741a1d8d6..802e3db7d 100644 --- a/internal/gitaly/service/repository/apply_gitattributes_test.go +++ b/internal/gitaly/service/repository/apply_gitattributes_test.go @@ -29,9 +29,9 @@ func TestApplyGitattributesSuccess(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) - cfg, repo, _, client := setupRepositoryService(ctx, t) + _, repo, repoPath, client := setupRepositoryService(ctx, t) - infoPath := filepath.Join(cfg.Storages[0].Path, repo.GetRelativePath(), "info") + infoPath := filepath.Join(repoPath, "info") attributesPath := filepath.Join(infoPath, "attributes") tests := []struct { diff --git a/internal/gitaly/service/repository/create_fork_test.go b/internal/gitaly/service/repository/create_fork_test.go index 0b7f36d68..800a9c7ac 100644 --- a/internal/gitaly/service/repository/create_fork_test.go +++ b/internal/gitaly/service/repository/create_fork_test.go @@ -107,7 +107,7 @@ func TestCreateFork_successful(t *testing.T) { if !tt.secure { // Only the insecure test cases run through Praefect, so we only rewrite the path // in that case. - replicaPath = getReplicaPath(ctx, t, client, forkedRepo) + replicaPath = gittest.GetReplicaPath(ctx, t, cfg, forkedRepo) } forkedRepoPath := filepath.Join(cfg.Storages[0].Path, replicaPath) @@ -163,7 +163,7 @@ func TestCreateFork_refs(t *testing.T) { storagePath, err := config.NewLocator(cfg).GetStorageByName(targetRepo.GetStorageName()) require.NoError(t, err) - targetRepoPath := filepath.Join(storagePath, getReplicaPath(ctx, t, client, targetRepo)) + targetRepoPath := filepath.Join(storagePath, gittest.GetReplicaPath(ctx, t, cfg, targetRepo)) require.Equal(t, []string{ @@ -186,7 +186,6 @@ func TestCreateFork_targetExists(t *testing.T) { for _, tc := range []struct { desc string seed func(t *testing.T, targetPath string) - expectedErr error expectedErrWithAtomicCreation error }{ { @@ -206,7 +205,6 @@ func TestCreateFork_targetExists(t *testing.T) { 0o644, )) }, - expectedErr: helper.ErrInvalidArgumentf("CreateFork: destination directory is not empty"), expectedErrWithAtomicCreation: helper.ErrAlreadyExistsf("creating fork: repository exists already"), }, { @@ -215,7 +213,6 @@ func TestCreateFork_targetExists(t *testing.T) { require.NoError(t, os.MkdirAll(filepath.Dir(targetPath), 0o770)) require.NoError(t, os.WriteFile(targetPath, nil, 0o644)) }, - expectedErr: helper.ErrInvalidArgumentf("CreateFork: destination path exists"), expectedErrWithAtomicCreation: helper.ErrAlreadyExistsf("creating fork: repository exists already"), }, } { @@ -227,7 +224,9 @@ func TestCreateFork_targetExists(t *testing.T) { forkedRepo := &gitalypb.Repository{ // As this test can run with Praefect in front of it, we'll use the next replica path Praefect will // assign in order to ensure this repository creation conflicts even with Praefect in front of it. - RelativePath: praefectutil.DeriveReplicaPath(1), + // As the source repository created in the setup is the first one, this would get the repository + // ID 2. + RelativePath: praefectutil.DeriveReplicaPath(2), StorageName: repo.StorageName, } diff --git a/internal/gitaly/service/repository/create_repository_from_bundle_test.go b/internal/gitaly/service/repository/create_repository_from_bundle_test.go index 90772615b..8f544432e 100644 --- a/internal/gitaly/service/repository/create_repository_from_bundle_test.go +++ b/internal/gitaly/service/repository/create_repository_from_bundle_test.go @@ -74,9 +74,8 @@ func TestCreateRepositoryFromBundle_successful(t *testing.T) { _, err = stream.CloseAndRecv() require.NoError(t, err) - importedRepoProto.RelativePath = getReplicaPath(ctx, t, client, importedRepoProto) importedRepo := localrepo.NewTestRepo(t, cfg, importedRepoProto) - importedRepoPath, err := locator.GetPath(importedRepoProto) + importedRepoPath, err := locator.GetPath(gittest.RewrittenRepository(ctx, t, cfg, importedRepoProto)) require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(importedRepoPath)) }() diff --git a/internal/gitaly/service/repository/create_repository_from_snapshot_test.go b/internal/gitaly/service/repository/create_repository_from_snapshot_test.go index 9c9a8049c..bda1cf756 100644 --- a/internal/gitaly/service/repository/create_repository_from_snapshot_test.go +++ b/internal/gitaly/service/repository/create_repository_from_snapshot_test.go @@ -106,14 +106,14 @@ func TestCreateRepositoryFromSnapshot_success(t *testing.T) { HttpHost: host, } - serverSocketPath := runRepositoryServerWithConfig(t, cfg, nil) - client := newRepositoryClient(t, cfg, serverSocketPath) + cfg.SocketPath = runRepositoryServerWithConfig(t, cfg, nil) + client := newRepositoryClient(t, cfg, cfg.SocketPath) rsp, err := client.CreateRepositoryFromSnapshot(ctx, req) require.NoError(t, err) testhelper.ProtoEqual(t, rsp, &gitalypb.CreateRepositoryFromSnapshotResponse{}) - repoAbsolutePath := filepath.Join(cfg.Storages[0].Path, getReplicaPath(ctx, t, client, repo)) + repoAbsolutePath := filepath.Join(cfg.Storages[0].Path, gittest.GetReplicaPath(ctx, t, cfg, repo)) require.DirExists(t, repoAbsolutePath) for _, entry := range entries { if strings.HasSuffix(entry, "/") { diff --git a/internal/gitaly/service/repository/create_repository_from_url_test.go b/internal/gitaly/service/repository/create_repository_from_url_test.go index 77fbd486c..d727949eb 100644 --- a/internal/gitaly/service/repository/create_repository_from_url_test.go +++ b/internal/gitaly/service/repository/create_repository_from_url_test.go @@ -48,7 +48,7 @@ func TestCreateRepotitoryFromURL_successful(t *testing.T) { _, err := client.CreateRepositoryFromURL(ctx, req) require.NoError(t, err) - importedRepoPath := filepath.Join(cfg.Storages[0].Path, getReplicaPath(ctx, t, client, importedRepo)) + importedRepoPath := filepath.Join(cfg.Storages[0].Path, gittest.GetReplicaPath(ctx, t, cfg, importedRepo)) gittest.Exec(t, cfg, "-C", importedRepoPath, "fsck") diff --git a/internal/gitaly/service/repository/create_repository_test.go b/internal/gitaly/service/repository/create_repository_test.go index 338fa8b91..da35e3098 100644 --- a/internal/gitaly/service/repository/create_repository_test.go +++ b/internal/gitaly/service/repository/create_repository_test.go @@ -52,7 +52,7 @@ func TestCreateRepository_successful(t *testing.T) { _, err := client.CreateRepository(ctx, req) require.NoError(t, err) - repoDir := filepath.Join(cfg.Storages[0].Path, getReplicaPath(ctx, t, client, repo)) + repoDir := filepath.Join(cfg.Storages[0].Path, gittest.GetReplicaPath(ctx, t, cfg, repo)) require.NoError(t, unix.Access(repoDir, unix.R_OK)) require.NoError(t, unix.Access(repoDir, unix.W_OK)) @@ -143,7 +143,7 @@ func TestCreateRepository_transactional(t *testing.T) { _, err = client.CreateRepository(ctx, &gitalypb.CreateRepositoryRequest{Repository: repo}) require.NoError(t, err) - require.DirExists(t, filepath.Join(cfg.Storages[0].Path, getReplicaPath(ctx, t, client, repo))) + require.DirExists(t, filepath.Join(cfg.Storages[0].Path, gittest.GetReplicaPath(ctx, t, cfg, repo))) require.Equal(t, 2, len(txManager.Votes()), "expected transactional vote") }) diff --git a/internal/gitaly/service/repository/fetch_remote_test.go b/internal/gitaly/service/repository/fetch_remote_test.go index 76e845217..998ba2be6 100644 --- a/internal/gitaly/service/repository/fetch_remote_test.go +++ b/internal/gitaly/service/repository/fetch_remote_test.go @@ -191,7 +191,7 @@ func TestFetchRemote_transaction(t *testing.T) { client := newRepositoryClient(t, sourceCfg, addr) ctx := testhelper.Context(t) - _, sourceRepoPath := gittest.CreateRepository(ctx, t, sourceCfg, gittest.CreateRepositoryConfig{ + repo, _ := gittest.CreateRepository(ctx, t, sourceCfg, gittest.CreateRepositoryConfig{ RelativePath: t.Name(), Seed: gittest.SeedGitLabTest, }) @@ -213,7 +213,7 @@ func TestFetchRemote_transaction(t *testing.T) { _, err = client.FetchRemote(ctx, &gitalypb.FetchRemoteRequest{ Repository: targetRepoProto, RemoteParams: &gitalypb.Remote{ - Url: fmt.Sprintf("http://127.0.0.1:%d/%s", port, filepath.Base(sourceRepoPath)), + Url: fmt.Sprintf("http://127.0.0.1:%d/%s", port, repo.GetRelativePath()), }, }) require.NoError(t, err) diff --git a/internal/gitaly/service/repository/gc_test.go b/internal/gitaly/service/repository/gc_test.go index d3c42e2c5..2849a12fb 100644 --- a/internal/gitaly/service/repository/gc_test.go +++ b/internal/gitaly/service/repository/gc_test.go @@ -47,7 +47,7 @@ func TestGarbageCollectSuccess(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) - cfg, repo, _, client := setupRepositoryService(ctx, t) + _, repo, repoPath, client := setupRepositoryService(ctx, t) tests := []struct { req *gitalypb.GarbageCollectRequest @@ -63,7 +63,7 @@ func TestGarbageCollectSuccess(t *testing.T) { }, } - packPath := filepath.Join(cfg.Storages[0].Path, repo.GetRelativePath(), "objects", "pack") + packPath := filepath.Join(repoPath, "objects", "pack") for _, test := range tests { t.Run(test.desc, func(t *testing.T) { diff --git a/internal/gitaly/service/repository/rename_test.go b/internal/gitaly/service/repository/rename_test.go index 16ab49e43..9983eb894 100644 --- a/internal/gitaly/service/repository/rename_test.go +++ b/internal/gitaly/service/repository/rename_test.go @@ -56,7 +56,7 @@ func TestRenameRepository_DestinationExists(t *testing.T) { _, err = client.CreateRepository(ctx, &gitalypb.CreateRepositoryRequest{Repository: renamedRepo}) require.NoError(t, err) - destinationRepoPath := filepath.Join(cfg.Storages[0].Path, getReplicaPath(ctx, t, client, existingDestinationRepo)) + destinationRepoPath := filepath.Join(cfg.Storages[0].Path, gittest.GetReplicaPath(ctx, t, cfg, existingDestinationRepo)) commitID := gittest.WriteCommit(t, cfg, destinationRepoPath, gittest.WithParents()) _, err = client.RenameRepository(ctx, &gitalypb.RenameRepositoryRequest{ diff --git a/internal/gitaly/service/repository/size_test.go b/internal/gitaly/service/repository/size_test.go index ecb7754f6..5aa51da73 100644 --- a/internal/gitaly/service/repository/size_test.go +++ b/internal/gitaly/service/repository/size_test.go @@ -88,11 +88,20 @@ func TestGetObjectDirectorySize_quarantine(t *testing.T) { Seed: gittest.SeedGitLabTest, }) - quarantine, err := quarantine.New(ctx, repo, locator) + quarantine, err := quarantine.New(ctx, gittest.RewrittenRepository(ctx, t, cfg, repo), locator) require.NoError(t, err) + // quarantine.New in Gitaly would receive an already rewritten repository. Gitaly would then calculate + // the quarantine directories based on the rewritten relative path. That quarantine would then be looped + // through Rails, which would then send a request with the quarantine object directories set based on the + // rewritten relative path but with the original relative path of the repository. Since we're using the production + // helpers here, we need to manually substitute the rewritten relative path with the original one when sending + // it back through the API. + quarantinedRepo := quarantine.QuarantinedRepo() + quarantinedRepo.RelativePath = repo.RelativePath + response, err := client.GetObjectDirectorySize(ctx, &gitalypb.GetObjectDirectorySizeRequest{ - Repository: quarantine.QuarantinedRepo(), + Repository: quarantinedRepo, }) require.NoError(t, err) require.NotNil(t, response) @@ -106,13 +115,13 @@ func TestGetObjectDirectorySize_quarantine(t *testing.T) { repo1, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, }) - quarantine1, err := quarantine.New(ctx, repo1, locator) + quarantine1, err := quarantine.New(ctx, gittest.RewrittenRepository(ctx, t, cfg, repo1), locator) require.NoError(t, err) repo2, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, }) - quarantine2, err := quarantine.New(ctx, repo2, locator) + quarantine2, err := quarantine.New(ctx, gittest.RewrittenRepository(ctx, t, cfg, repo2), locator) require.NoError(t, err) // We swap out the the object directories of both quarantines. So while both are @@ -120,6 +129,13 @@ func TestGetObjectDirectorySize_quarantine(t *testing.T) { // swapped-in quarantine directory does not belong to our repository. repo := proto.Clone(quarantine1.QuarantinedRepo()).(*gitalypb.Repository) repo.GitObjectDirectory = quarantine2.QuarantinedRepo().GetGitObjectDirectory() + // quarantine.New in Gitaly would receive an already rewritten repository. Gitaly would then calculate + // the quarantine directories based on the rewritten relative path. That quarantine would then be looped + // through Rails, which would then send a request with the quarantine object directories set based on the + // rewritten relative path but with the original relative path of the repository. Since we're using the production + // helpers here, we need to manually substitute the rewritten relative path with the original one when sending + // it back through the API. + repo.RelativePath = repo1.RelativePath response, err := client.GetObjectDirectorySize(ctx, &gitalypb.GetObjectDirectorySizeRequest{ Repository: repo, diff --git a/internal/gitaly/service/repository/snapshot_test.go b/internal/gitaly/service/repository/snapshot_test.go index dd3deffff..4813c86ae 100644 --- a/internal/gitaly/service/repository/snapshot_test.go +++ b/internal/gitaly/service/repository/snapshot_test.go @@ -148,7 +148,7 @@ func TestGetSnapshotWithDedupe(t *testing.T) { gittest.RequireObjectExists(t, cfg, repoPath, secondCommitID) repoCopy, _ := copyRepoUsingSnapshot(t, ctx, cfg, client, repoProto) - repoCopy.RelativePath = getReplicaPath(ctx, t, client, repoCopy) + repoCopy.RelativePath = gittest.GetReplicaPath(ctx, t, cfg, repoCopy) repoCopyPath, err := locator.GetRepoPath(repoCopy) require.NoError(t, err) @@ -227,7 +227,7 @@ func TestGetSnapshot_alternateObjectDirectory(t *testing.T) { }() repoCopy, _ := copyRepoUsingSnapshot(t, ctx, cfg, client, repoProto) - repoCopy.RelativePath = getReplicaPath(ctx, t, client, repoCopy) + repoCopy.RelativePath = gittest.GetReplicaPath(ctx, t, cfg, repoCopy) repoCopyPath, err := locator.GetRepoPath(repoCopy) require.NoError(t, err) diff --git a/internal/gitaly/service/repository/testhelper_test.go b/internal/gitaly/service/repository/testhelper_test.go index df74b6fb6..69c0b0448 100644 --- a/internal/gitaly/service/repository/testhelper_test.go +++ b/internal/gitaly/service/repository/testhelper_test.go @@ -64,17 +64,6 @@ func TestWithRubySidecar(t *testing.T) { } } -// clientWithConn is used to pass through the connection to getReplicaPath. The tests themselves just use the -// RepositoryServiceClient interface. -type clientWithConn struct { - gitalypb.RepositoryServiceClient - *grpc.ClientConn -} - -func getReplicaPath(ctx context.Context, t testing.TB, client gitalypb.RepositoryServiceClient, repo *gitalypb.Repository) string { - return testhelper.GetReplicaPath(ctx, t, client.(clientWithConn).ClientConn, repo) -} - func newRepositoryClient(t testing.TB, cfg config.Cfg, serverSocketPath string) gitalypb.RepositoryServiceClient { var connOpts []grpc.DialOption if cfg.Auth.Token != "" { @@ -84,10 +73,7 @@ func newRepositoryClient(t testing.TB, cfg config.Cfg, serverSocketPath string) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, conn.Close()) }) - return clientWithConn{ - RepositoryServiceClient: gitalypb.NewRepositoryServiceClient(conn), - ClientConn: conn, - } + return gitalypb.NewRepositoryServiceClient(conn) } func newMuxedRepositoryClient(t *testing.T, ctx context.Context, cfg config.Cfg, serverSocketPath string, handshaker internalclient.Handshaker) gitalypb.RepositoryServiceClient { diff --git a/internal/gitaly/service/smarthttp/inforefs_test.go b/internal/gitaly/service/smarthttp/inforefs_test.go index 4c98efbea..3ab23b3b2 100644 --- a/internal/gitaly/service/smarthttp/inforefs_test.go +++ b/internal/gitaly/service/smarthttp/inforefs_test.go @@ -379,7 +379,8 @@ func TestCacheInfoRefsUploadPack(t *testing.T) { } assertNormalResponse(gitalyServer.Address()) - require.FileExists(t, pathToCachedResponse(t, ctx, cache, rpcRequest)) + rewrittenRequest := &gitalypb.InfoRefsRequest{Repository: gittest.RewrittenRepository(ctx, t, cfg, repo)} + require.FileExists(t, pathToCachedResponse(t, ctx, cache, rewrittenRequest)) replacedContents := []string{ "first line", @@ -389,7 +390,7 @@ func TestCacheInfoRefsUploadPack(t *testing.T) { } // replace cached response file to prove the info-ref uses the cache - replaceCachedResponse(t, ctx, cache, rpcRequest, strings.Join(replacedContents, "\n")) + replaceCachedResponse(t, ctx, cache, rewrittenRequest, strings.Join(replacedContents, "\n")) response, err := makeInfoRefsUploadPackRequest(ctx, t, gitalyServer.Address(), cfg.Auth.Token, rpcRequest) require.NoError(t, err) assertGitRefAdvertisement(t, "InfoRefsUploadPack", string(response), @@ -397,7 +398,7 @@ func TestCacheInfoRefsUploadPack(t *testing.T) { ) invalidateCacheForRepo := func() { - ender, err := cache.StartLease(rpcRequest.Repository) + ender, err := cache.StartLease(rewrittenRequest.Repository) require.NoError(t, err) require.NoError(t, ender.EndLease(setInfoRefsUploadPackMethod(ctx))) } diff --git a/internal/gitaly/service/smarthttp/receive_pack_test.go b/internal/gitaly/service/smarthttp/receive_pack_test.go index a422e6d6e..122defe7e 100644 --- a/internal/gitaly/service/smarthttp/receive_pack_test.go +++ b/internal/gitaly/service/smarthttp/receive_pack_test.go @@ -93,7 +93,7 @@ func TestSuccessfulReceivePackRequest(t *testing.T) { // Compare the repository up front so that we can use require.Equal for // the remaining values. - testhelper.ProtoEqual(t, repo, payload.Repo) + testhelper.ProtoEqual(t, gittest.RewrittenRepository(ctx, t, cfg, repo), payload.Repo) payload.Repo = nil // If running tests with Praefect, then the transaction would be set, but we have no way of @@ -625,7 +625,7 @@ func TestPostReceivePackToHooks(t *testing.T) { cfg.SocketPath = runSmartHTTPServer(t, cfg) ctx := testhelper.Context(t) - repo, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ + repo, testRepoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, }) @@ -644,7 +644,6 @@ func TestPostReceivePackToHooks(t *testing.T) { cfg.Gitlab.SecretFile = gitlab.WriteShellSecretFile(t, cfg.GitlabShell.Dir, secretToken) push := newTestPush(t, cfg, nil) - testRepoPath := filepath.Join(cfg.Storages[0].Path, repo.RelativePath) oldHead := text.ChompBytes(gittest.Exec(t, cfg, "-C", testRepoPath, "rev-parse", "HEAD")) changes := fmt.Sprintf("%s %s refs/heads/master\n", oldHead, push.newHead) diff --git a/internal/gitaly/service/ssh/receive_pack_test.go b/internal/gitaly/service/ssh/receive_pack_test.go index 8ea6a4b68..0057bd144 100644 --- a/internal/gitaly/service/ssh/receive_pack_test.go +++ b/internal/gitaly/service/ssh/receive_pack_test.go @@ -103,7 +103,8 @@ func TestReceivePackPushSuccess(t *testing.T) { cfg.SocketPath = runSSHServer(t, cfg, testserver.WithGitCommandFactory(gitCmdFactory)) - repo, repoPath := gittest.CreateRepository(testhelper.Context(t), t, cfg, gittest.CreateRepositoryConfig{ + ctx := testhelper.Context(t) + repo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, RelativePath: "gitlab-test-ssh-receive-pack.git", }) @@ -141,7 +142,7 @@ func TestReceivePackPushSuccess(t *testing.T) { // the remaining values. testhelper.ProtoEqual(t, &gitalypb.Repository{ StorageName: cfg.Storages[0].Name, - RelativePath: "gitlab-test-ssh-receive-pack.git", + RelativePath: gittest.GetReplicaPath(ctx, t, cfg, repo), GlProjectPath: glProjectPath, GlRepository: glRepository, }, payload.Repo) @@ -271,18 +272,13 @@ func TestObjectPoolRefAdvertisementHidingSSH(t *testing.T) { require.NoError(t, err) require.NoError(t, pool.Create(ctx, repo)) - defer func() { - require.NoError(t, pool.Remove(ctx)) - }() require.NoError(t, pool.Link(ctx, repo)) commitID := gittest.WriteCommit(t, cfg, pool.FullPath(), gittest.WithBranch(t.Name())) // First request - require.NoError(t, stream.Send(&gitalypb.SSHReceivePackRequest{ - Repository: &gitalypb.Repository{StorageName: cfg.Storages[0].Name, RelativePath: repo.GetRelativePath()}, GlId: "user-123", - })) + require.NoError(t, stream.Send(&gitalypb.SSHReceivePackRequest{Repository: repoProto, GlId: "user-123"})) require.NoError(t, stream.Send(&gitalypb.SSHReceivePackRequest{Stdin: []byte("0000")})) require.NoError(t, stream.CloseSend()) diff --git a/internal/gitaly/service/ssh/upload_pack_test.go b/internal/gitaly/service/ssh/upload_pack_test.go index 9cda95921..e560cdf0d 100644 --- a/internal/gitaly/service/ssh/upload_pack_test.go +++ b/internal/gitaly/service/ssh/upload_pack_test.go @@ -622,14 +622,14 @@ func TestUploadPackCloneGitFailure(t *testing.T) { cfg.SocketPath = runSSHServer(t, cfg) - repo, _ := gittest.CreateRepository(testhelper.Context(t), t, cfg, gittest.CreateRepositoryConfig{ + repo, repoPath := gittest.CreateRepository(testhelper.Context(t), t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, }) client, conn := newSSHClient(t, cfg.SocketPath) defer conn.Close() - configPath := filepath.Join(cfg.Storages[0].Path, repo.RelativePath, "config") + configPath := filepath.Join(repoPath, "config") gitconfig, err := os.Create(configPath) require.NoError(t, err) diff --git a/internal/praefect/coordinator_test.go b/internal/praefect/coordinator_test.go index 4749b3acb..3fc515da0 100644 --- a/internal/praefect/coordinator_test.go +++ b/internal/praefect/coordinator_test.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" "sync" - "sync/atomic" "testing" "time" @@ -806,41 +805,33 @@ func TestStreamDirector_repo_creation(t *testing.T) { db := testdb.New(t) - for _, tc := range []struct { + for i, tc := range []struct { desc string - electionStrategy config.ElectionStrategy replicationFactor int primaryStored bool assignmentsStored bool }{ { - desc: "virtual storage scoped primaries", - electionStrategy: config.ElectionStrategySQL, - replicationFactor: 3, // assignments are not set when not using repository specific primaries - primaryStored: false, - assignmentsStored: false, - }, - { - desc: "repository specific primaries without variable replication factor", - electionStrategy: config.ElectionStrategyPerRepository, + desc: "without variable replication factor", primaryStored: true, assignmentsStored: false, }, { - desc: "repository specific primaries with variable replication factor", - electionStrategy: config.ElectionStrategyPerRepository, + desc: "with variable replication factor", replicationFactor: 3, primaryStored: true, assignmentsStored: true, }, } { t.Run(tc.desc, func(t *testing.T) { - db.TruncateAll(t) + tx := db.Begin(t) + defer tx.Rollback(t) + primaryNode := &config.Node{Storage: "praefect-internal-1"} healthySecondaryNode := &config.Node{Storage: "praefect-internal-2"} unhealthySecondaryNode := &config.Node{Storage: "praefect-internal-3"} conf := config.Config{ - Failover: config.Failover{ElectionStrategy: tc.electionStrategy}, + Failover: config.Failover{ElectionStrategy: config.ElectionStrategyPerRepository}, VirtualStorages: []*config.VirtualStorage{ { Name: "praefect", @@ -856,90 +847,38 @@ func TestStreamDirector_repo_creation(t *testing.T) { RelativePath: "/path/to/hashed/storage", } - var createRepositoryCalled int64 - rs := datastore.MockRepositoryStore{ - CreateRepositoryFunc: func(ctx context.Context, repoID int64, virtualStorage, relativePath, replicaPath, primary string, updatedSecondaries, outdatedSecondaries []string, storePrimary, storeAssignments bool) error { - atomic.AddInt64(&createRepositoryCalled, 1) - assert.Equal(t, int64(0), repoID) - assert.Equal(t, targetRepo.StorageName, virtualStorage) - assert.Equal(t, targetRepo.RelativePath, relativePath) - assert.Equal(t, targetRepo.RelativePath, replicaPath) - assert.Equal(t, rewrittenStorage, primary) - assert.Equal(t, []string{healthySecondaryNode.Storage}, updatedSecondaries) - assert.Equal(t, []string{unhealthySecondaryNode.Storage}, outdatedSecondaries) - assert.Equal(t, tc.primaryStored, storePrimary) - assert.Equal(t, tc.assignmentsStored, storeAssignments) - return nil + conns := Connections{ + "praefect": { + primaryNode.Storage: &grpc.ClientConn{}, + healthySecondaryNode.Storage: &grpc.ClientConn{}, + unhealthySecondaryNode.Storage: &grpc.ClientConn{}, }, } - var router Router - var primaryConnPointer string - var secondaryConnPointers []string - switch tc.electionStrategy { - case config.ElectionStrategySQL: - gitalySocket0 := testhelper.GetTemporaryGitalySocketFileName(t) - gitalySocket1 := testhelper.GetTemporaryGitalySocketFileName(t) - gitalySocket2 := testhelper.GetTemporaryGitalySocketFileName(t) - testhelper.NewServerWithHealth(t, gitalySocket0) - testhelper.NewServerWithHealth(t, gitalySocket1) - healthSrv2 := testhelper.NewServerWithHealth(t, gitalySocket2) - healthSrv2.SetServingStatus("", grpc_health_v1.HealthCheckResponse_NOT_SERVING) - - primaryNode.Address = "unix://" + gitalySocket0 - healthySecondaryNode.Address = "unix://" + gitalySocket1 - unhealthySecondaryNode.Address = "unix://" + gitalySocket2 - - nodeMgr, err := nodes.NewManager(testhelper.NewDiscardingLogEntry(t), conf, nil, nil, promtest.NewMockHistogramVec(), protoregistry.GitalyProtoPreregistered, nil, nil, nil) - require.NoError(t, err) - nodeMgr.Start(0, time.Hour) - defer nodeMgr.Stop() - - router = NewNodeManagerRouter(nodeMgr, rs) - for _, node := range nodeMgr.Nodes()["praefect"] { - if node.GetStorage() == primaryNode.Storage { - primaryConnPointer = fmt.Sprintf("%p", node.GetConnection()) - continue - } - - if node.GetStorage() == healthySecondaryNode.Storage { - secondaryConnPointers = []string{fmt.Sprintf("%p", node.GetConnection())} - } - } - case config.ElectionStrategyPerRepository: - conns := Connections{ - "praefect": { - primaryNode.Storage: &grpc.ClientConn{}, - healthySecondaryNode.Storage: &grpc.ClientConn{}, - unhealthySecondaryNode.Storage: &grpc.ClientConn{}, + primaryConnPointer := fmt.Sprintf("%p", conns["praefect"][primaryNode.Storage]) + secondaryConnPointers := []string{fmt.Sprintf("%p", conns["praefect"][healthySecondaryNode.Storage])} + rs := datastore.NewPostgresRepositoryStore(tx, conf.StorageNames()) + router := NewPerRepositoryRouter( + conns, + nil, + StaticHealthChecker{"praefect": {primaryNode.Storage, healthySecondaryNode.Storage}}, + mockRandom{ + intnFunc: func(n int) int { + require.Equal(t, n, 2, "number of primary candidates should match the number of healthy nodes") + return 0 }, - } - primaryConnPointer = fmt.Sprintf("%p", conns["praefect"][primaryNode.Storage]) - secondaryConnPointers = []string{fmt.Sprintf("%p", conns["praefect"][healthySecondaryNode.Storage])} - router = NewPerRepositoryRouter( - conns, - nil, - StaticHealthChecker{"praefect": {primaryNode.Storage, healthySecondaryNode.Storage}}, - mockRandom{ - intnFunc: func(n int) int { - require.Equal(t, n, 2, "number of primary candidates should match the number of healthy nodes") - return 0 - }, - shuffleFunc: func(n int, swap func(int, int)) { - require.Equal(t, n, 2, "number of secondary candidates should match the number of node minus the primary") - }, + shuffleFunc: func(n int, swap func(int, int)) { + require.Equal(t, n, 2, "number of secondary candidates should match the number of node minus the primary") }, - nil, - nil, - rs, - conf.DefaultReplicationFactors(), - ) - default: - t.Fatalf("unexpected election strategy: %q", tc.electionStrategy) - } + }, + nil, + nil, + rs, + conf.DefaultReplicationFactors(), + ) txMgr := transactions.NewManager(conf) - queueInterceptor := datastore.NewReplicationEventQueueInterceptor(datastore.NewPostgresReplicationEventQueue(db)) + queueInterceptor := datastore.NewReplicationEventQueueInterceptor(datastore.NewPostgresReplicationEventQueue(tx)) coordinator := NewCoordinator( queueInterceptor, @@ -1007,6 +946,7 @@ func TestStreamDirector_repo_creation(t *testing.T) { CreatedAt: actual[0].CreatedAt, UpdatedAt: actual[0].UpdatedAt, Job: datastore.ReplicationJob{ + RepositoryID: int64(i + 1), Change: datastore.UpdateRepo, VirtualStorage: conf.VirtualStorages[0].Name, RelativePath: targetRepo.RelativePath, @@ -1018,7 +958,6 @@ func TestStreamDirector_repo_creation(t *testing.T) { } require.Equal(t, expectedEvents, actualEvents, "ensure replication job created by stream director is correct") - require.EqualValues(t, 1, atomic.LoadInt64(&createRepositoryCalled), "ensure CreateRepository is called on datastore") }) } } diff --git a/internal/testhelper/testcfg/gitaly_builder.go b/internal/testhelper/testcfg/gitaly_builder.go index fb4d1b466..3f670ebcb 100644 --- a/internal/testhelper/testcfg/gitaly_builder.go +++ b/internal/testhelper/testcfg/gitaly_builder.go @@ -13,6 +13,12 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" ) +// UnconfiguredSocketPath is used to bypass config validation errors +// when building the configuration. The socket path is now known yet +// at the time of building the configuration and is substituted later +// when the service is actually spun up. +const UnconfiguredSocketPath = "it is a stub to bypass Validate method" + // Option is a configuration option for the builder. type Option func(*GitalyCfgBuilder) @@ -74,7 +80,7 @@ func (gc *GitalyCfgBuilder) Build(t testing.TB) config.Cfg { cfg := gc.cfg if cfg.SocketPath == "" { - cfg.SocketPath = "it is a stub to bypass Validate method" + cfg.SocketPath = UnconfiguredSocketPath } root := testhelper.TempDir(t) diff --git a/internal/testhelper/testhelper.go b/internal/testhelper/testhelper.go index d279b1b4e..a9d51ef98 100644 --- a/internal/testhelper/testhelper.go +++ b/internal/testhelper/testhelper.go @@ -25,10 +25,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" - "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) const ( @@ -40,29 +36,6 @@ const ( DefaultStorageName = "default" ) -// GetReplicaPath retrieves the repository's replica path if the test has been -// run with Praefect in front of it. This is necessary if the test creates a repository -// through Praefect and peeks into the filesystem afterwards. Conn should be pointing to -// Praefect. -func GetReplicaPath(ctx context.Context, t testing.TB, conn *grpc.ClientConn, repo *gitalypb.Repository) string { - metadata, err := gitalypb.NewPraefectInfoServiceClient(conn).GetRepositoryMetadata( - ctx, &gitalypb.GetRepositoryMetadataRequest{ - Query: &gitalypb.GetRepositoryMetadataRequest_Path_{ - Path: &gitalypb.GetRepositoryMetadataRequest_Path{ - VirtualStorage: repo.GetStorageName(), - RelativePath: repo.GetRelativePath(), - }, - }, - }) - if status, ok := status.FromError(err); ok && status.Code() == codes.Unimplemented && status.Message() == "unknown service gitaly.PraefectInfoService" { - // The repository is stored at relative path if the test is running without Praefect in front. - return repo.RelativePath - } - require.NoError(t, err) - - return metadata.ReplicaPath -} - // IsPraefectEnabled returns whether this testing run is done with Praefect in front of the Gitaly. func IsPraefectEnabled() bool { _, enabled := os.LookupEnv("GITALY_TEST_WITH_PRAEFECT") |