Welcome to mirror list, hosted at ThFree Co, Russian Federation.

executor.go « git2go « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f5cca8252e6d844e54986510499dfacb8e0069d4 (plain)
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
134
135
136
137
138
139
140
141
142
package git2go

import (
	"bytes"
	"context"
	"encoding/gob"
	"errors"
	"fmt"
	"io"
	"strings"

	"github.com/sirupsen/logrus"
	"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"
	"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
	logger              logrus.FieldLogger
	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, logger logrus.FieldLogger) *Executor {
	return &Executor{
		binaryPath:    cfg.BinaryPath(BinaryName),
		signingKey:    cfg.Git.SigningKey,
		gitCmdFactory: gitCmdFactory,
		locator:       locator,
		logger:        logger,
		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...)

	// Construct a log writer that we pass to gitaly-git2go. Note that this is not exactly pretty: the output
	// generated by the logger is not going to be parsed, but will be written to the log without any change as log
	// message.
	//
	// Previously, we would have written these messages to `logrus.Logger.Out` directly. But we have refactored our
	// code to get rid of the global logger instance, and thus we cannot access that field anymore. Given that the
	// git2go executor is going away next release anyway, let's just live with this ugly workaround here. We still
	// have visibility into what the process is doing, but with a bit less comfort. That seems like an acceptable
	// tradeoff in order to get rid of the global logger altogether.
	logWriter := b.logger.WithField("component", "git2go."+subcmd).Writer()
	defer logWriter.Close()

	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(logWriter),
		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
}