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:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2022-06-28 16:00:32 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2022-06-30 18:31:32 +0300
commitfcd94ab1c42e746c5a83ec8bcae2e3ed93d018e8 (patch)
tree46567872a6a78803449dac5a3e6970cfbbf31ae8
parent99599232a0a8f24f438d780ac3f7142718de06ef (diff)
repository: Verify behaviour when fetching into pooled repospks-fetch-remote-test-with-alternates
We don't have any tests right now that verify that fetching into a pooled repository is doing the right thing. Most imporantly, we don't assert that we ignore alternate references present in the object pool. This missing test coverage had been biting us because we were indeed including alternate references in the reference negotiation phase before this was fixed via 2383696d1 (git: Always disable use of alternate refs for git-fetch(1), 2022-06-21). Add a test to verify that we indeed don't use alternate refs in pooled repositories to avoid any future regressions.
-rw-r--r--internal/gitaly/service/repository/fetch_remote_test.go122
1 files changed, 122 insertions, 0 deletions
diff --git a/internal/gitaly/service/repository/fetch_remote_test.go b/internal/gitaly/service/repository/fetch_remote_test.go
index 5e2202d73..c5e164ff1 100644
--- a/internal/gitaly/service/repository/fetch_remote_test.go
+++ b/internal/gitaly/service/repository/fetch_remote_test.go
@@ -1,8 +1,10 @@
package repository
import (
+ "bytes"
"context"
"fmt"
+ "io"
"net/http"
"net/http/httptest"
"os"
@@ -752,3 +754,123 @@ func TestFetchRemote_httpWithTimeout(t *testing.T) {
require.Contains(t, err.Error(), "fetch remote: signal: terminated")
}
+
+func TestFetchRemote_pooledRepository(t *testing.T) {
+ t.Parallel()
+
+ ctx := testhelper.Context(t)
+
+ // By default git-fetch(1) will always run with `core.alternateRefsCommand=exit 0 #`, which
+ // effectively disables use of alternate refs. We can't just unset this value, so instead we
+ // just write a script that knows to execute git-for-each-ref(1) as expected by this config
+ // option.
+ //
+ // Note that we're using a separate command factory here just to ease the setup because we
+ // need to recreate the other command factory with the Git configuration specified by the
+ // test.
+ alternateRefsCommandFactory := gittest.NewCommandFactory(t, testcfg.Build(t))
+ exec := testhelper.WriteExecutable(t,
+ filepath.Join(testhelper.TempDir(t), "alternate-refs"),
+ []byte(fmt.Sprintf(`#!/bin/sh
+ exec %q -C "$1" for-each-ref --format='%%(objectname)'
+ `, alternateRefsCommandFactory.GetExecutionEnvironment(ctx).BinaryPath)),
+ )
+
+ for _, tc := range []struct {
+ desc string
+ cfg config.Cfg
+ shouldAnnouncePooledRefs bool
+ }{
+ {
+ desc: "with default configuration",
+ },
+ {
+ desc: "with alternates",
+ cfg: config.Cfg{
+ Git: config.Git{
+ Config: []config.GitConfig{
+ {
+ Key: "core.alternateRefsCommand",
+ Value: exec,
+ },
+ },
+ },
+ },
+ shouldAnnouncePooledRefs: true,
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ cfg := testcfg.Build(t, testcfg.WithBase(tc.cfg))
+ gitCmdFactory := gittest.NewCommandFactory(t, cfg)
+
+ client, swocketPath := runRepositoryService(t, cfg, nil, testserver.WithGitCommandFactory(gitCmdFactory))
+ cfg.SocketPath = swocketPath
+
+ // Create a repository that emulates an object pool. This object contains a
+ // single reference with an object that is neither in the pool member nor in
+ // the remote. If alternate refs are used, then Git will announce it to the
+ // remote as "have".
+ _, poolRepoPath := gittest.CreateRepository(ctx, t, cfg)
+ poolCommitID := gittest.WriteCommit(t, cfg, poolRepoPath,
+ gittest.WithParents(),
+ gittest.WithBranch("pooled"),
+ gittest.WithTreeEntries(gittest.TreeEntry{Path: "pool", Mode: "100644", Content: "pool contents"}),
+ )
+
+ // Create the pooled repository and link it to its pool. This is the
+ // repository we're fetching into.
+ pooledRepoProto, pooledRepoPath := gittest.CreateRepository(ctx, t, cfg)
+ require.NoError(t, os.WriteFile(filepath.Join(pooledRepoPath, "objects", "info", "alternates"), []byte(filepath.Join(poolRepoPath, "objects")), 0o644))
+
+ // And then finally create a third repository that emulates the remote side
+ // we're fetching from. We need to create at least one reference so that Git
+ // would actually try to fetch objects.
+ _, remoteRepoPath := gittest.CreateRepository(ctx, t, cfg)
+ gittest.WriteCommit(t, cfg, remoteRepoPath,
+ gittest.WithParents(),
+ gittest.WithBranch("remote"),
+ gittest.WithTreeEntries(gittest.TreeEntry{Path: "remote", Mode: "100644", Content: "remote contents"}),
+ )
+
+ // Set up an HTTP server and intercept the request. This is done so that we
+ // can observe the reference negotiation and check whether alternate refs
+ // are announced or not.
+ var requestBuffer bytes.Buffer
+ port, stop := gittest.HTTPServer(ctx, t, gitCmdFactory, remoteRepoPath, func(responseWriter http.ResponseWriter, request *http.Request, handler http.Handler) {
+ closer := request.Body
+ defer testhelper.MustClose(t, closer)
+
+ request.Body = io.NopCloser(io.TeeReader(request.Body, &requestBuffer))
+
+ handler.ServeHTTP(responseWriter, request)
+ })
+ defer func() {
+ require.NoError(t, stop())
+ }()
+
+ // Perform the fetch.
+ _, err := client.FetchRemote(ctx, &gitalypb.FetchRemoteRequest{
+ Repository: pooledRepoProto,
+ RemoteParams: &gitalypb.Remote{
+ Url: fmt.Sprintf("http://127.0.0.1:%d/%s", port, filepath.Base(remoteRepoPath)),
+ },
+ })
+ require.NoError(t, err)
+
+ // This should result in the "remote" branch having been fetched into the
+ // pooled repository.
+ require.Equal(t,
+ gittest.ResolveRevision(t, cfg, pooledRepoPath, "refs/heads/remote"),
+ gittest.ResolveRevision(t, cfg, remoteRepoPath, "refs/heads/remote"),
+ )
+
+ // Verify whether alternate refs have been announced as part of the
+ // reference negotiation phase.
+ if tc.shouldAnnouncePooledRefs {
+ require.Contains(t, requestBuffer.String(), fmt.Sprintf("have %s", poolCommitID))
+ } else {
+ require.NotContains(t, requestBuffer.String(), fmt.Sprintf("have %s", poolCommitID))
+ }
+ })
+ }
+}