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:
authorSami Hiltunen <shiltunen@gitlab.com>2024-01-12 19:09:37 +0300
committerSami Hiltunen <shiltunen@gitlab.com>2024-01-13 14:42:39 +0300
commit9be461c8617c48d3a86069250aba5dca0bc6971f (patch)
tree50d643d7ed92d8d4e359c7360cf596fce3b29548
parent511a64dd9dfbfb9745c4475fe1143b697e5a938b (diff)
Add QuarantineOnly() for getting the repository with its quarantine only
Repository quarantining works by configuring a temporary object directory via the `GIT_OBJECTS_DIRECTORY` environment variable, and configuring the actual object directory as an alternate via `GIT_ALTERNATE_OBJECT_DIRECTORIES`. This ensures that new objects are written into the quarantine but the old objects are still readable through the alternate. There are some use cases where we don't want to have the alternate configured. For example, if we want to list only the new objects in the quarantine, we can invoke Git without the alternate configured. This way Git only sees the new objects. We're soon going to walk the new objects a transaction introduced to pack them in TransactionManager. To do so, this commit implements a helper method to get a quarantined localrepo.Repo instance without the alternates configured.
-rw-r--r--internal/git/localrepo/repo.go32
-rw-r--r--internal/git/localrepo/repo_test.go53
2 files changed, 85 insertions, 0 deletions
diff --git a/internal/git/localrepo/repo.go b/internal/git/localrepo/repo.go
index 8bbf40237..7e8f976bc 100644
--- a/internal/git/localrepo/repo.go
+++ b/internal/git/localrepo/repo.go
@@ -2,6 +2,7 @@ package localrepo
import (
"context"
+ "errors"
"fmt"
"io"
"os"
@@ -22,6 +23,7 @@ import (
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg"
"gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "google.golang.org/protobuf/proto"
)
// Repo represents a local Git repository.
@@ -75,6 +77,36 @@ func (repo *Repo) Quarantine(quarantineDirectory string) (*Repo, error) {
), nil
}
+// QuarantineOnly returns the repository with only the quarantine directory configured as an object
+// directory by dropping the alternate object directories. Returns an error if the repository doesn't
+// have a quarantine directory configured.
+//
+// Only the alternates configured in the *gitalypb.Repository object are dropped, not the alternates
+// that could be in `objects/info/alternates`. Dropping the configured alternates does however also
+// implicitly remove the `objects/info/alternates` in the alternate object directory since the file
+// would exist there. The quarantine directory itself would not typically contain an
+// `objects/info/alternates` file.
+func (repo *Repo) QuarantineOnly() (*Repo, error) {
+ pbRepo, ok := repo.Repository.(*gitalypb.Repository)
+ if !ok {
+ return nil, fmt.Errorf("unexpected repository type %t", repo.Repository)
+ }
+
+ cloneRepo := proto.Clone(pbRepo).(*gitalypb.Repository)
+ cloneRepo.GitAlternateObjectDirectories = nil
+ if cloneRepo.GitObjectDirectory == "" {
+ return nil, errors.New("repository wasn't quarantined")
+ }
+
+ return New(
+ repo.logger,
+ repo.locator,
+ repo.gitCmdFactory,
+ repo.catfileCache,
+ cloneRepo,
+ ), nil
+}
+
// NewTestRepo constructs a Repo. It is intended as a helper function for tests which assembles
// dependencies ad-hoc from the given config.
func NewTestRepo(tb testing.TB, cfg config.Cfg, repo storage.Repository, factoryOpts ...git.ExecCommandFactoryOption) *Repo {
diff --git a/internal/git/localrepo/repo_test.go b/internal/git/localrepo/repo_test.go
index 41862391d..f5208ef4f 100644
--- a/internal/git/localrepo/repo_test.go
+++ b/internal/git/localrepo/repo_test.go
@@ -3,6 +3,7 @@ package localrepo
import (
"bytes"
"context"
+ "errors"
"fmt"
"path/filepath"
"testing"
@@ -171,6 +172,58 @@ func TestRepo_Quarantine_nonExistentRepository(t *testing.T) {
}
}
+func TestRepo_QuarantineOnly(t *testing.T) {
+ t.Parallel()
+
+ cfg := testcfg.Build(t)
+ catfileCache := catfile.NewCache(cfg)
+ defer catfileCache.Stop()
+
+ ctx := testhelper.Context(t)
+ repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
+ SkipCreationViaService: true,
+ })
+
+ unquarantinedRepo := New(
+ testhelper.NewLogger(t),
+ config.NewLocator(cfg),
+ gittest.NewCommandFactory(t, cfg),
+ catfileCache,
+ repoProto,
+ )
+
+ t.Run("fails with unquarantined repository", func(t *testing.T) {
+ t.Parallel()
+
+ _, err := unquarantinedRepo.QuarantineOnly()
+ require.Equal(t, err, errors.New("repository wasn't quarantined"))
+ })
+
+ t.Run("returns the repository with only the quarantine directory", func(t *testing.T) {
+ t.Parallel()
+
+ quarantinedRepo, err := unquarantinedRepo.Quarantine(filepath.Join(repoPath, "quarantine-directory"))
+ require.NoError(t, err)
+
+ expectedRepo := &gitalypb.Repository{
+ StorageName: repoProto.StorageName,
+ RelativePath: repoProto.RelativePath,
+ GlRepository: repoProto.GlRepository,
+ GlProjectPath: repoProto.GlProjectPath,
+ GitObjectDirectory: "quarantine-directory",
+ GitAlternateObjectDirectories: []string{"objects"},
+ }
+
+ testhelper.ProtoEqual(t, expectedRepo, quarantinedRepo.Repository)
+
+ onlyQuarantineRepo, err := quarantinedRepo.QuarantineOnly()
+ require.NoError(t, err)
+
+ expectedRepo.GitAlternateObjectDirectories = nil
+ testhelper.ProtoEqual(t, expectedRepo, onlyQuarantineRepo.Repository)
+ })
+}
+
func TestRepo_StorageTempDir(t *testing.T) {
ctx := testhelper.Context(t)
cfg := testcfg.Build(t)