1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
package git2go
import (
"bytes"
"context"
"encoding/gob"
"errors"
"fmt"
"io"
"strings"
"gitlab.com/gitlab-org/gitaly/v16/internal/command"
"gitlab.com/gitlab-org/gitaly/v16/internal/featureflag"
"gitlab.com/gitlab-org/gitaly/v16/internal/git"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/alternates"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/config"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
glog "gitlab.com/gitlab-org/gitaly/v16/internal/log"
"gitlab.com/gitlab-org/labkit/correlation"
)
var (
// ErrInvalidArgument is returned in case the merge arguments are invalid.
ErrInvalidArgument = errors.New("invalid parameters")
// BinaryName is the name of the gitaly-git2go binary.
BinaryName = "gitaly-git2go"
)
// Executor executes gitaly-git2go.
type Executor struct {
binaryPath string
signingKey string
gitCmdFactory git.CommandFactory
locator storage.Locator
logFormat, logLevel string
}
// NewExecutor returns a new gitaly-git2go executor using binaries as configured in the given
// configuration.
func NewExecutor(cfg config.Cfg, gitCmdFactory git.CommandFactory, locator storage.Locator) *Executor {
return &Executor{
binaryPath: cfg.BinaryPath(BinaryName),
signingKey: cfg.Git.SigningKey,
gitCmdFactory: gitCmdFactory,
locator: locator,
logFormat: cfg.Logging.Format,
logLevel: cfg.Logging.Level,
}
}
func (b *Executor) run(ctx context.Context, repo storage.Repository, stdin io.Reader, subcmd string, args ...string) (*bytes.Buffer, error) {
repoPath, err := b.locator.GetRepoPath(repo)
if err != nil {
return nil, fmt.Errorf("gitaly-git2go: %w", err)
}
var enabledFeatureFlags, disabledFeatureFlags []string
for flag, value := range featureflag.FromContext(ctx) {
switch value {
case true:
enabledFeatureFlags = append(enabledFeatureFlags, flag.MetadataKey())
case false:
disabledFeatureFlags = append(disabledFeatureFlags, flag.MetadataKey())
}
}
env := alternates.Env(repoPath, repo.GetGitObjectDirectory(), repo.GetGitAlternateObjectDirectories())
env = append(env, b.gitCmdFactory.GetExecutionEnvironment(ctx).EnvironmentVariables...)
// Pass the log output directly to gitaly-git2go. No need to reinterpret
// these logs as long as the destination is an append-only file. See
// https://pkg.go.dev/github.com/sirupsen/logrus#readme-thread-safety
log := glog.Default().Logger.Out
args = append([]string{
"-log-format", b.logFormat,
"-log-level", b.logLevel,
"-correlation-id", correlation.ExtractFromContext(ctx),
"-enabled-feature-flags", strings.Join(enabledFeatureFlags, ","),
"-disabled-feature-flags", strings.Join(disabledFeatureFlags, ","),
subcmd,
}, args...)
var stdout bytes.Buffer
cmd, err := command.New(ctx, append([]string{b.binaryPath}, args...),
command.WithStdin(stdin),
command.WithStdout(&stdout),
command.WithStderr(log),
command.WithEnvironment(env),
command.WithCommandName("gitaly-git2go", subcmd),
)
if err != nil {
return nil, err
}
if err := cmd.Wait(); err != nil {
return nil, err
}
return &stdout, nil
}
// runWithGob runs the specified gitaly-git2go cmd with the request gob-encoded
// as input and returns the commit ID as string or an error.
func (b *Executor) runWithGob(ctx context.Context, repo storage.Repository, cmd string, request interface{}) (git.ObjectID, error) {
input := &bytes.Buffer{}
if err := gob.NewEncoder(input).Encode(request); err != nil {
return "", fmt.Errorf("%s: %w", cmd, err)
}
output, err := b.run(ctx, repo, input, cmd)
if err != nil {
return "", fmt.Errorf("%s: %w", cmd, err)
}
var result Result
if err := gob.NewDecoder(output).Decode(&result); err != nil {
return "", fmt.Errorf("%s: %w", cmd, err)
}
if result.Err != nil {
return "", fmt.Errorf("%s: %w", cmd, result.Err)
}
commitID, err := git.ObjectHashSHA1.FromHex(result.CommitID)
if err != nil {
return "", fmt.Errorf("could not parse commit ID: %w", err)
}
return commitID, nil
}
|