mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 17:11:49 +00:00
gitserver: Sunset Exec endpoint (#62774)
After we finally migrated all calls to Exec by proper API offerings, we're finally not using this API anymore. The previous PR deprecated this endpoint, and this one will fully remove it. We can merge it early June when after the next release was cut. As a result of this, gitserver no longer exposes a blanket exec endpoint that could pose several risks from running dangerous commands to just running commands that could cause resource exhaustion. Closes https://github.com/sourcegraph/sourcegraph/issues/62099 Test plan: This endpoint is unused, CI did not find any issues from removing it.
This commit is contained in:
parent
614375e538
commit
9183f528c7
@ -93,7 +93,7 @@ func (g *gitCLIBackend) NewCommand(ctx context.Context, optFns ...CommandOptionF
|
||||
|
||||
if !IsAllowedGitCmd(logger, opts.arguments) {
|
||||
blockedCommandExecutedCounter.Inc()
|
||||
return nil, ErrBadGitCommand
|
||||
return nil, errBadGitCommand
|
||||
}
|
||||
|
||||
if len(opts.arguments) == 0 {
|
||||
@ -177,16 +177,16 @@ func (g *gitCLIBackend) NewCommand(ctx context.Context, optFns ...CommandOptionF
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
// ErrBadGitCommand is returned from the git CLI backend if the arguments provided
|
||||
// errBadGitCommand is returned from the git CLI backend if the arguments provided
|
||||
// are not allowed.
|
||||
var ErrBadGitCommand = errors.New("bad git command, not allowed")
|
||||
var errBadGitCommand = errors.New("bad git command, not allowed")
|
||||
|
||||
func commandFailedError(ctx context.Context, err error, cmd wrexec.Cmder, stderr []byte) error {
|
||||
func newCommandFailedError(ctx context.Context, err error, cmd wrexec.Cmder, stderr []byte) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
return &CommandFailedError{
|
||||
return &commandFailedError{
|
||||
Inner: err,
|
||||
args: cmd.Unwrap().Args,
|
||||
Stderr: stderr,
|
||||
@ -194,18 +194,18 @@ func commandFailedError(ctx context.Context, err error, cmd wrexec.Cmder, stderr
|
||||
}
|
||||
}
|
||||
|
||||
type CommandFailedError struct {
|
||||
type commandFailedError struct {
|
||||
Stderr []byte
|
||||
ExitStatus int
|
||||
Inner error
|
||||
args []string
|
||||
}
|
||||
|
||||
func (e *CommandFailedError) Unwrap() error {
|
||||
func (e *commandFailedError) Unwrap() error {
|
||||
return e.Inner
|
||||
}
|
||||
|
||||
func (e *CommandFailedError) Error() string {
|
||||
func (e *commandFailedError) Error() string {
|
||||
return fmt.Sprintf("git command %v failed with status code %d (output: %q)", e.args, e.ExitStatus, e.Stderr)
|
||||
}
|
||||
|
||||
@ -260,7 +260,7 @@ func (rc *cmdReader) waitCmd() error {
|
||||
if checkMaybeCorruptRepo(rc.logger, rc.gitDir, rc.repoName, rc.stderr.String()) {
|
||||
rc.err = common.ErrRepoCorrupted{Reason: rc.stderr.String()}
|
||||
} else {
|
||||
rc.err = commandFailedError(rc.ctx, rc.err, rc.cmd, rc.stderr.Bytes())
|
||||
rc.err = newCommandFailedError(rc.ctx, rc.err, rc.cmd, rc.stderr.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -131,7 +131,7 @@ func (it *commitLogIterator) Next() (*git.GitCommitWithFiles, error) {
|
||||
// If exit code is 128 and `fatal: bad object` is part of stderr, most likely we
|
||||
// are referencing a commit that does not exist.
|
||||
// We want to return a gitdomain.RevisionNotFoundError in that case.
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 128 {
|
||||
if (bytes.Contains(e.Stderr, []byte("fatal: your current branch")) && bytes.Contains(e.Stderr, []byte("does not have any commits yet"))) || bytes.Contains(e.Stderr, []byte("fatal: bad revision 'HEAD'")) {
|
||||
return nil, io.EOF
|
||||
@ -174,7 +174,7 @@ func (it *commitLogIterator) Next() (*git.GitCommitWithFiles, error) {
|
||||
func (it *commitLogIterator) Close() error {
|
||||
err := it.Closer.Close()
|
||||
if err != nil {
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 128 {
|
||||
if (bytes.Contains(e.Stderr, []byte("fatal: your current branch")) && bytes.Contains(e.Stderr, []byte("does not have any commits yet"))) || bytes.Contains(e.Stderr, []byte("fatal: bad revision 'HEAD'")) {
|
||||
return nil
|
||||
|
||||
@ -24,7 +24,7 @@ func (g *gitCLIBackend) Get(ctx context.Context, key string) (string, error) {
|
||||
out, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
// Exit code 1 means the key is not set.
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 1 {
|
||||
return "", nil
|
||||
}
|
||||
@ -63,7 +63,7 @@ func (g *gitCLIBackend) Unset(ctx context.Context, key string) error {
|
||||
_, err = io.Copy(io.Discard, r)
|
||||
if err != nil {
|
||||
// Exit code 5 means the key is not set.
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 5 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ func (g *gitCLIBackend) ContributorCounts(ctx context.Context, opt git.Contribut
|
||||
// If exit code is 128 and `fatal: bad object` is part of stderr, most likely we
|
||||
// are referencing a range that does not exist.
|
||||
// We want to return a gitdomain.RevisionNotFoundError in that case.
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 128 && (bytes.Contains(e.Stderr, []byte("fatal: bad object")) ||
|
||||
bytes.Contains(e.Stderr, []byte("fatal: bad revision"))) {
|
||||
return nil, &gitdomain.RevisionNotFoundError{Repo: g.repoName, Spec: string(opt.Range)}
|
||||
|
||||
@ -1,18 +1,12 @@
|
||||
package gitcli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sourcegraph/log"
|
||||
)
|
||||
|
||||
func (g *gitCLIBackend) Exec(ctx context.Context, args ...string) (io.ReadCloser, error) {
|
||||
return g.NewCommand(ctx, WithArguments(args...))
|
||||
}
|
||||
|
||||
var (
|
||||
// gitCmdAllowlist are commands and arguments that are allowed to execute and are
|
||||
// checked by IsAllowedGitCmd
|
||||
|
||||
@ -18,7 +18,7 @@ func (g *gitCLIBackend) SymbolicRefHead(ctx context.Context, short bool) (refNam
|
||||
// TODO: implement refs_shorten_unambiguous_ref from git: https://sourcegraph.com/github.com/git/git/-/blob/refs.c?L1376,
|
||||
// so QuickSymbolicRefHead can also be used when short=true.
|
||||
if !short {
|
||||
refName, err = QuickSymbolicRefHead(g.dir)
|
||||
refName, err = quickSymbolicRefHead(g.dir)
|
||||
if err == nil {
|
||||
return refName, err
|
||||
}
|
||||
@ -46,7 +46,7 @@ func (g *gitCLIBackend) SymbolicRefHead(ctx context.Context, short bool) (refNam
|
||||
}
|
||||
|
||||
func (g *gitCLIBackend) RevParseHead(ctx context.Context) (sha api.CommitID, err error) {
|
||||
shaStr, err := QuickRevParseHead(g.dir)
|
||||
shaStr, err := quickRevParseHead(g.dir)
|
||||
if err == nil {
|
||||
return api.CommitID(shaStr), nil
|
||||
}
|
||||
@ -78,9 +78,9 @@ func (g *gitCLIBackend) RevParseHead(ctx context.Context) (sha api.CommitID, err
|
||||
|
||||
const headFileRefPrefix = "ref: "
|
||||
|
||||
// QuickSymbolicRefHead best-effort mimics the execution of `git symbolic-ref HEAD`, but doesn't exec a child process.
|
||||
// quickSymbolicRefHead best-effort mimics the execution of `git symbolic-ref HEAD`, but doesn't exec a child process.
|
||||
// It just reads the .git/HEAD file from the bare git repository directory.
|
||||
func QuickSymbolicRefHead(dir common.GitDir) (string, error) {
|
||||
func quickSymbolicRefHead(dir common.GitDir) (string, error) {
|
||||
// See if HEAD contains a commit hash and fail if so.
|
||||
head, err := os.ReadFile(dir.Path("HEAD"))
|
||||
if err != nil {
|
||||
@ -99,9 +99,9 @@ func QuickSymbolicRefHead(dir common.GitDir) (string, error) {
|
||||
return string(headRef), nil
|
||||
}
|
||||
|
||||
// QuickRevParseHead best-effort mimics the execution of `git rev-parse HEAD`, but doesn't exec a child process.
|
||||
// quickRevParseHead best-effort mimics the execution of `git rev-parse HEAD`, but doesn't exec a child process.
|
||||
// It just reads the relevant files from the bare git repository directory.
|
||||
func QuickRevParseHead(dir common.GitDir) (string, error) {
|
||||
func quickRevParseHead(dir common.GitDir) (string, error) {
|
||||
// See if HEAD contains a commit hash and return it if so.
|
||||
head, err := os.ReadFile(dir.Path("HEAD"))
|
||||
if err != nil {
|
||||
|
||||
@ -129,14 +129,14 @@ func BenchmarkQuickRevParseHeadQuickSymbolicRefHead_packed_refs(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
|
||||
for range b.N {
|
||||
rev, err := QuickRevParseHead(gitDir)
|
||||
rev, err := quickRevParseHead(gitDir)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if rev != masterRev {
|
||||
b.Fatal("unexpected rev: ", rev)
|
||||
}
|
||||
ref, err := QuickSymbolicRefHead(gitDir)
|
||||
ref, err := quickSymbolicRefHead(gitDir)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@ -182,14 +182,14 @@ func BenchmarkQuickRevParseHeadQuickSymbolicRefHead_unpacked_refs(b *testing.B)
|
||||
b.ResetTimer()
|
||||
|
||||
for range b.N {
|
||||
rev, err := QuickRevParseHead(gitDir)
|
||||
rev, err := quickRevParseHead(gitDir)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if rev != masterRev {
|
||||
b.Fatal("unexpected rev: ", rev)
|
||||
}
|
||||
ref, err := QuickSymbolicRefHead(gitDir)
|
||||
ref, err := quickSymbolicRefHead(gitDir)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ func (g *gitCLIBackend) MergeBase(ctx context.Context, baseRevspec, headRevspec
|
||||
stdout, err := io.ReadAll(out)
|
||||
if err != nil {
|
||||
// Exit code 1 and empty output most likely means that no common merge-base was found.
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) {
|
||||
if e.ExitStatus == 1 {
|
||||
if len(e.Stderr) == 0 {
|
||||
|
||||
@ -46,7 +46,7 @@ func (g *gitCLIBackend) getObjectType(ctx context.Context, objectID string) (git
|
||||
|
||||
stdout, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 128 && (bytes.Contains(e.Stderr, []byte("Not a valid object name")) ||
|
||||
bytes.Contains(e.Stderr, []byte("git cat-file: could not get object info"))) {
|
||||
return "", &gitdomain.RevisionNotFoundError{Repo: g.repoName, Spec: objectID}
|
||||
|
||||
@ -50,7 +50,7 @@ func (g *gitCLIBackend) GetCommit(ctx context.Context, commit api.CommitID, incl
|
||||
// If exit code is 128 and `fatal: bad object` is part of stderr, most likely we
|
||||
// are referencing a commit that does not exist.
|
||||
// We want to return a gitdomain.RevisionNotFoundError in that case.
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 128 && bytes.Contains(e.Stderr, []byte("fatal: bad object")) {
|
||||
return nil, &gitdomain.RevisionNotFoundError{Repo: g.repoName, Spec: string(commit)}
|
||||
}
|
||||
@ -175,7 +175,7 @@ func (g *gitCLIBackend) getBlobOID(ctx context.Context, commit api.CommitID, pat
|
||||
// If exit code is 128 and `not a tree object` is part of stderr, most likely we
|
||||
// are referencing a commit that does not exist.
|
||||
// We want to return a gitdomain.RevisionNotFoundError in that case.
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 128 {
|
||||
if bytes.Contains(e.Stderr, []byte("not a tree object")) || bytes.Contains(e.Stderr, []byte("Not a valid object name")) {
|
||||
return "", &gitdomain.RevisionNotFoundError{Repo: g.repoName, Spec: string(commit)}
|
||||
@ -225,7 +225,7 @@ func (g *gitCLIBackend) BehindAhead(ctx context.Context, left, right string) (*g
|
||||
|
||||
out, err := io.ReadAll(rc)
|
||||
if err != nil {
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) {
|
||||
switch {
|
||||
case e.ExitStatus == 128 && bytes.Contains(e.Stderr, []byte("fatal: ambiguous argument")):
|
||||
@ -262,7 +262,7 @@ func (g *gitCLIBackend) FirstEverCommit(ctx context.Context) (api.CommitID, erro
|
||||
|
||||
out, err := io.ReadAll(rc)
|
||||
if err != nil {
|
||||
var cmdFailedErr *CommandFailedError
|
||||
var cmdFailedErr *commandFailedError
|
||||
if errors.As(err, &cmdFailedErr) {
|
||||
if cmdFailedErr.ExitStatus == 129 && bytes.Contains(cmdFailedErr.Stderr, []byte(revListUsageString)) {
|
||||
// If the error is due to an empty repository, return a sentinel error.
|
||||
@ -490,7 +490,7 @@ func (it *readDirIterator) Next() (fs.FileInfo, error) {
|
||||
}
|
||||
|
||||
if err := it.sc.Err(); err != nil {
|
||||
var cfe *CommandFailedError
|
||||
var cfe *commandFailedError
|
||||
if errors.As(err, &cfe) {
|
||||
if bytes.Contains(cfe.Stderr, []byte("exists on disk, but not in")) {
|
||||
return nil, &os.PathError{Op: "ls-tree", Path: filepath.ToSlash(it.path), Err: os.ErrNotExist}
|
||||
@ -515,7 +515,7 @@ func (it *readDirIterator) Next() (fs.FileInfo, error) {
|
||||
|
||||
func (it *readDirIterator) Close() error {
|
||||
if err := it.r.Close(); err != nil {
|
||||
var cfe *CommandFailedError
|
||||
var cfe *commandFailedError
|
||||
if errors.As(err, &cfe) {
|
||||
if bytes.Contains(cfe.Stderr, []byte("exists on disk, but not in")) {
|
||||
return &os.PathError{Op: "ls-tree", Path: filepath.ToSlash(it.path), Err: os.ErrNotExist}
|
||||
|
||||
@ -40,7 +40,7 @@ func (g *gitCLIBackend) revParse(ctx context.Context, spec string) (api.CommitID
|
||||
|
||||
stdout, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
var e *CommandFailedError
|
||||
var e *commandFailedError
|
||||
if errors.As(err, &e) && e.ExitStatus == 128 && (bytes.Contains(e.Stderr, []byte("bad revision")) ||
|
||||
bytes.Contains(e.Stderr, []byte("unknown revision")) ||
|
||||
bytes.Contains(e.Stderr, []byte("expected commit type"))) {
|
||||
|
||||
@ -117,11 +117,6 @@ type GitBackend interface {
|
||||
// Empty branches return an iterator that emits zero commits, not an error.
|
||||
CommitLog(ctx context.Context, opt CommitLogOpts) (CommitLogIterator, error)
|
||||
|
||||
// Exec is a temporary helper to run arbitrary git commands from the exec endpoint.
|
||||
// No new usages of it should be introduced and once the migration is done we will
|
||||
// remove this method.
|
||||
Exec(ctx context.Context, args ...string) (io.ReadCloser, error)
|
||||
|
||||
// FirstEverCommit returns the first commit ever made to the repository.
|
||||
//
|
||||
// If the repository is empty, a RevisionNotFoundError is returned (as the
|
||||
|
||||
@ -570,9 +570,6 @@ type MockGitBackend struct {
|
||||
// ContributorCountsFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method ContributorCounts.
|
||||
ContributorCountsFunc *GitBackendContributorCountsFunc
|
||||
// ExecFunc is an instance of a mock function object controlling the
|
||||
// behavior of the method Exec.
|
||||
ExecFunc *GitBackendExecFunc
|
||||
// FirstEverCommitFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method FirstEverCommit.
|
||||
FirstEverCommitFunc *GitBackendFirstEverCommitFunc
|
||||
@ -659,11 +656,6 @@ func NewMockGitBackend() *MockGitBackend {
|
||||
return
|
||||
},
|
||||
},
|
||||
ExecFunc: &GitBackendExecFunc{
|
||||
defaultHook: func(context.Context, ...string) (r0 io.ReadCloser, r1 error) {
|
||||
return
|
||||
},
|
||||
},
|
||||
FirstEverCommitFunc: &GitBackendFirstEverCommitFunc{
|
||||
defaultHook: func(context.Context) (r0 api.CommitID, r1 error) {
|
||||
return
|
||||
@ -781,11 +773,6 @@ func NewStrictMockGitBackend() *MockGitBackend {
|
||||
panic("unexpected invocation of MockGitBackend.ContributorCounts")
|
||||
},
|
||||
},
|
||||
ExecFunc: &GitBackendExecFunc{
|
||||
defaultHook: func(context.Context, ...string) (io.ReadCloser, error) {
|
||||
panic("unexpected invocation of MockGitBackend.Exec")
|
||||
},
|
||||
},
|
||||
FirstEverCommitFunc: &GitBackendFirstEverCommitFunc{
|
||||
defaultHook: func(context.Context) (api.CommitID, error) {
|
||||
panic("unexpected invocation of MockGitBackend.FirstEverCommit")
|
||||
@ -889,9 +876,6 @@ func NewMockGitBackendFrom(i GitBackend) *MockGitBackend {
|
||||
ContributorCountsFunc: &GitBackendContributorCountsFunc{
|
||||
defaultHook: i.ContributorCounts,
|
||||
},
|
||||
ExecFunc: &GitBackendExecFunc{
|
||||
defaultHook: i.Exec,
|
||||
},
|
||||
FirstEverCommitFunc: &GitBackendFirstEverCommitFunc{
|
||||
defaultHook: i.FirstEverCommit,
|
||||
},
|
||||
@ -1704,120 +1688,6 @@ func (c GitBackendContributorCountsFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// GitBackendExecFunc describes the behavior when the Exec method of the
|
||||
// parent MockGitBackend instance is invoked.
|
||||
type GitBackendExecFunc struct {
|
||||
defaultHook func(context.Context, ...string) (io.ReadCloser, error)
|
||||
hooks []func(context.Context, ...string) (io.ReadCloser, error)
|
||||
history []GitBackendExecFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// Exec delegates to the next hook function in the queue and stores the
|
||||
// parameter and result values of this invocation.
|
||||
func (m *MockGitBackend) Exec(v0 context.Context, v1 ...string) (io.ReadCloser, error) {
|
||||
r0, r1 := m.ExecFunc.nextHook()(v0, v1...)
|
||||
m.ExecFunc.appendCall(GitBackendExecFuncCall{v0, v1, r0, r1})
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the Exec method of the
|
||||
// parent MockGitBackend instance is invoked and the hook queue is empty.
|
||||
func (f *GitBackendExecFunc) SetDefaultHook(hook func(context.Context, ...string) (io.ReadCloser, error)) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// Exec method of the parent MockGitBackend instance invokes the hook at the
|
||||
// front of the queue and discards it. After the queue is empty, the default
|
||||
// hook function is invoked for any future action.
|
||||
func (f *GitBackendExecFunc) PushHook(hook func(context.Context, ...string) (io.ReadCloser, error)) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultHook with a function that returns the
|
||||
// given values.
|
||||
func (f *GitBackendExecFunc) SetDefaultReturn(r0 io.ReadCloser, r1 error) {
|
||||
f.SetDefaultHook(func(context.Context, ...string) (io.ReadCloser, error) {
|
||||
return r0, r1
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushHook with a function that returns the given values.
|
||||
func (f *GitBackendExecFunc) PushReturn(r0 io.ReadCloser, r1 error) {
|
||||
f.PushHook(func(context.Context, ...string) (io.ReadCloser, error) {
|
||||
return r0, r1
|
||||
})
|
||||
}
|
||||
|
||||
func (f *GitBackendExecFunc) nextHook() func(context.Context, ...string) (io.ReadCloser, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *GitBackendExecFunc) appendCall(r0 GitBackendExecFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of GitBackendExecFuncCall objects describing
|
||||
// the invocations of this function.
|
||||
func (f *GitBackendExecFunc) History() []GitBackendExecFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]GitBackendExecFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// GitBackendExecFuncCall is an object that describes an invocation of
|
||||
// method Exec on an instance of MockGitBackend.
|
||||
type GitBackendExecFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is a slice containing the values of the variadic arguments
|
||||
// passed to this method invocation.
|
||||
Arg1 []string
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 io.ReadCloser
|
||||
// Result1 is the value of the 2nd result returned from this method
|
||||
// invocation.
|
||||
Result1 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation. The variadic slice argument is flattened in this array such
|
||||
// that one positional argument and three variadic arguments would result in
|
||||
// a slice of four, not two.
|
||||
func (c GitBackendExecFuncCall) Args() []interface{} {
|
||||
trailing := []interface{}{}
|
||||
for _, val := range c.Arg1 {
|
||||
trailing = append(trailing, val)
|
||||
}
|
||||
|
||||
return append([]interface{}{c.Arg0}, trailing...)
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c GitBackendExecFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// GitBackendFirstEverCommitFunc describes the behavior when the
|
||||
// FirstEverCommit method of the parent MockGitBackend instance is invoked.
|
||||
type GitBackendFirstEverCommitFunc struct {
|
||||
|
||||
@ -210,30 +210,6 @@ func (b *observableBackend) ReadFile(ctx context.Context, commit api.CommitID, p
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *observableBackend) Exec(ctx context.Context, args ...string) (_ io.ReadCloser, err error) {
|
||||
ctx, errCollector, endObservation := b.operations.exec.WithErrors(ctx, &err, observation.Args{})
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
endObservation.OnCancel(ctx, 1, observation.Args{})
|
||||
|
||||
concurrentOps.WithLabelValues("Exec").Inc()
|
||||
|
||||
r, err := b.backend.Exec(ctx, args...)
|
||||
if err != nil {
|
||||
concurrentOps.WithLabelValues("Exec").Dec()
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &observableReadCloser{
|
||||
inner: r,
|
||||
endObservation: func(err error) {
|
||||
concurrentOps.WithLabelValues("Exec").Dec()
|
||||
errCollector.Collect(&err)
|
||||
cancel()
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *observableBackend) ArchiveReader(ctx context.Context, format ArchiveFormat, treeish string, paths []string) (_ io.ReadCloser, err error) {
|
||||
ctx, errCollector, endObservation := b.operations.archiveReader.WithErrors(ctx, &err, observation.Args{})
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
@ -545,7 +521,6 @@ type operations struct {
|
||||
symbolicRefHead *observation.Operation
|
||||
revParseHead *observation.Operation
|
||||
readFile *observation.Operation
|
||||
exec *observation.Operation
|
||||
getCommit *observation.Operation
|
||||
archiveReader *observation.Operation
|
||||
resolveRevision *observation.Operation
|
||||
@ -598,7 +573,6 @@ func newOperations(observationCtx *observation.Context) *operations {
|
||||
symbolicRefHead: op("symbolic-ref-head"),
|
||||
revParseHead: op("rev-parse-head"),
|
||||
readFile: op("read-file"),
|
||||
exec: op("exec"),
|
||||
getCommit: op("get-commit"),
|
||||
archiveReader: op("archive-reader"),
|
||||
resolveRevision: op("resolve-revision"),
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@ -16,7 +15,6 @@ import (
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/accesslog"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/git"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/git/gitcli"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/gitserverfs"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/perforce"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
@ -135,110 +133,6 @@ func (gs *grpcServer) DiskInfo(_ context.Context, _ *proto.DiskInfoRequest) (*pr
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (gs *grpcServer) Exec(req *proto.ExecRequest, ss proto.GitserverService_ExecServer) error {
|
||||
ctx := ss.Context()
|
||||
|
||||
// Log which actor is accessing the repo.
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
args := byteSlicesToStrings(req.GetArgs())
|
||||
logAttrs := []log.Field{}
|
||||
if len(args) > 0 {
|
||||
logAttrs = append(logAttrs,
|
||||
log.String("cmd", args[0]),
|
||||
log.Strings("args", args[1:]),
|
||||
)
|
||||
}
|
||||
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
accesslog.Record(ctx, req.GetRepo(), logAttrs...)
|
||||
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
if req.GetRepo() == "" {
|
||||
return status.New(codes.InvalidArgument, "repo must be specified").Err()
|
||||
}
|
||||
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
repoName := api.RepoName(req.GetRepo())
|
||||
repoDir := gs.fs.RepoDir(repoName)
|
||||
backend := gs.gitBackendSource(repoDir, repoName)
|
||||
|
||||
if err := gs.checkRepoExists(ctx, repoName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := streamio.NewWriter(func(p []byte) error {
|
||||
return ss.Send(&proto.ExecResponse{
|
||||
Data: p,
|
||||
})
|
||||
})
|
||||
|
||||
// Special-case `git rev-parse HEAD` requests. These are invoked by search queries for every repo in scope.
|
||||
// For searches over large repo sets (> 1k), this leads to too many child process execs, which can lead
|
||||
// to a persistent failure mode where every exec takes > 10s, which is disastrous for gitserver performance.
|
||||
if len(args) == 2 && args[0] == "rev-parse" && args[1] == "HEAD" {
|
||||
if resolved, err := gitcli.QuickRevParseHead(repoDir); err == nil && gitdomain.IsAbsoluteRevision(resolved) {
|
||||
_, _ = w.Write([]byte(resolved))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Special-case `git symbolic-ref HEAD` requests. These are invoked by resolvers determining the default branch of a repo.
|
||||
// For searches over large repo sets (> 1k), this leads to too many child process execs, which can lead
|
||||
// to a persistent failure mode where every exec takes > 10s, which is disastrous for gitserver performance.
|
||||
if len(args) == 2 && args[0] == "symbolic-ref" && args[1] == "HEAD" {
|
||||
if resolved, err := gitcli.QuickSymbolicRefHead(repoDir); err == nil {
|
||||
_, _ = w.Write([]byte(resolved))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
stdout, err := backend.Exec(ctx, args...)
|
||||
if err != nil {
|
||||
if errors.Is(err, gitcli.ErrBadGitCommand) {
|
||||
return status.New(codes.InvalidArgument, "invalid command").Err()
|
||||
}
|
||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||
return status.FromContextError(ctxErr).Err()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
defer stdout.Close()
|
||||
|
||||
_, err = io.Copy(w, stdout)
|
||||
if err != nil {
|
||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||
return status.FromContextError(ctxErr).Err()
|
||||
}
|
||||
|
||||
commandFailedErr := &gitcli.CommandFailedError{}
|
||||
if errors.As(err, &commandFailedErr) {
|
||||
gRPCStatus := codes.Unknown
|
||||
if strings.Contains(commandFailedErr.Error(), "signal: killed") {
|
||||
gRPCStatus = codes.Aborted
|
||||
}
|
||||
|
||||
var errString string
|
||||
if commandFailedErr.Unwrap() != nil {
|
||||
errString = commandFailedErr.Unwrap().Error()
|
||||
}
|
||||
s, err := status.New(gRPCStatus, errString).WithDetails(&proto.ExecStatusPayload{
|
||||
StatusCode: int32(commandFailedErr.ExitStatus),
|
||||
Stderr: string(commandFailedErr.Stderr),
|
||||
})
|
||||
if err != nil {
|
||||
gs.logger.Error("failed to marshal status", log.Error(err))
|
||||
return err
|
||||
}
|
||||
return s.Err()
|
||||
}
|
||||
gs.svc.LogIfCorrupt(ctx, repoName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gs *grpcServer) Archive(req *proto.ArchiveRequest, ss proto.GitserverService_ArchiveServer) error {
|
||||
ctx := ss.Context()
|
||||
|
||||
|
||||
@ -150,38 +150,6 @@ func (l *loggingGRPCServer) DiskInfo(ctx context.Context, request *proto.DiskInf
|
||||
return l.base.DiskInfo(ctx, request)
|
||||
}
|
||||
|
||||
func (l *loggingGRPCServer) Exec(request *proto.ExecRequest, server proto.GitserverService_ExecServer) (err error) {
|
||||
start := time.Now()
|
||||
|
||||
defer func() {
|
||||
elapsed := time.Since(start)
|
||||
|
||||
doLog(
|
||||
l.logger,
|
||||
proto.GitserverService_Exec_FullMethodName,
|
||||
status.Code(err),
|
||||
trace.Context(server.Context()).TraceID,
|
||||
elapsed,
|
||||
|
||||
execRequestToLogFields(request)...)
|
||||
}()
|
||||
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
return l.base.Exec(request, server)
|
||||
}
|
||||
|
||||
func execRequestToLogFields(req *proto.ExecRequest) []log.Field {
|
||||
return []log.Field{
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
log.String("repo", req.GetRepo()),
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
log.String("ensureRevision", string(req.GetEnsureRevision())),
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
log.Strings("args", byteSlicesToStrings(req.GetArgs())),
|
||||
// 🚨SECURITY: We don't log the stdin field because it could 1) contain sensitive data 2) be very large.
|
||||
}
|
||||
}
|
||||
|
||||
func (l *loggingGRPCServer) GetObject(ctx context.Context, request *proto.GetObjectRequest) (response *proto.GetObjectResponse, err error) {
|
||||
start := time.Now()
|
||||
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/internal/actor"
|
||||
@ -18,8 +16,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/time/rate"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/sourcegraph/log/logtest"
|
||||
|
||||
@ -29,13 +25,10 @@ import (
|
||||
"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/gitserverfs"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/vcssyncer"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
"github.com/sourcegraph/sourcegraph/internal/conf"
|
||||
"github.com/sourcegraph/sourcegraph/internal/database"
|
||||
"github.com/sourcegraph/sourcegraph/internal/database/dbmocks"
|
||||
"github.com/sourcegraph/sourcegraph/internal/database/dbtest"
|
||||
"github.com/sourcegraph/sourcegraph/internal/gitserver"
|
||||
"github.com/sourcegraph/sourcegraph/internal/gitserver/connection"
|
||||
v1 "github.com/sourcegraph/sourcegraph/internal/gitserver/v1"
|
||||
"github.com/sourcegraph/sourcegraph/internal/limiter"
|
||||
"github.com/sourcegraph/sourcegraph/internal/observation"
|
||||
"github.com/sourcegraph/sourcegraph/internal/ratelimit"
|
||||
@ -45,204 +38,6 @@ import (
|
||||
"github.com/sourcegraph/sourcegraph/lib/errors"
|
||||
)
|
||||
|
||||
type Test struct {
|
||||
Name string
|
||||
Request *v1.ExecRequest
|
||||
ExpectedCode codes.Code
|
||||
ExpectedBody string
|
||||
ExpectedError string
|
||||
ExpectedDetails []any
|
||||
}
|
||||
|
||||
func TestExecRequest(t *testing.T) {
|
||||
conf.Mock(&conf.Unified{})
|
||||
t.Cleanup(func() { conf.Mock(nil) })
|
||||
|
||||
tests := []Test{
|
||||
{
|
||||
Name: "Command",
|
||||
Request: &v1.ExecRequest{
|
||||
Repo: "github.com/gorilla/mux",
|
||||
Args: [][]byte{[]byte("diff-tree")},
|
||||
},
|
||||
ExpectedCode: codes.Unknown,
|
||||
ExpectedBody: "teststdout",
|
||||
ExpectedError: "teststderr",
|
||||
ExpectedDetails: []any{&v1.ExecStatusPayload{
|
||||
StatusCode: 42,
|
||||
Stderr: "teststderr",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Name: "Error",
|
||||
Request: &v1.ExecRequest{
|
||||
Repo: "github.com/gorilla/mux",
|
||||
Args: [][]byte{[]byte("merge-base")},
|
||||
},
|
||||
ExpectedCode: codes.Unknown,
|
||||
ExpectedError: "testerror",
|
||||
ExpectedDetails: []any{&v1.ExecStatusPayload{
|
||||
StatusCode: 1,
|
||||
Stderr: "teststderr",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Name: "EmptyInput",
|
||||
Request: &v1.ExecRequest{
|
||||
Repo: "github.com/gorilla/mux",
|
||||
},
|
||||
ExpectedCode: codes.InvalidArgument,
|
||||
ExpectedError: "invalid command",
|
||||
},
|
||||
{
|
||||
Name: "BadCommand",
|
||||
Request: &v1.ExecRequest{
|
||||
Repo: "github.com/gorilla/mux",
|
||||
Args: [][]byte{[]byte("invalid-command")},
|
||||
},
|
||||
ExpectedCode: codes.InvalidArgument,
|
||||
ExpectedError: "invalid command",
|
||||
},
|
||||
}
|
||||
|
||||
getRemoteURLFunc := func(ctx context.Context, name api.RepoName) (string, error) {
|
||||
return "https://" + string(name) + ".git", nil
|
||||
}
|
||||
|
||||
db := dbmocks.NewMockDB()
|
||||
gr := dbmocks.NewMockGitserverRepoStore()
|
||||
db.GitserverReposFunc.SetDefaultReturn(gr)
|
||||
fs := gitserverfs.NewMockFSFrom(gitserverfs.New(&observation.TestContext, t.TempDir()))
|
||||
require.NoError(t, fs.Initialize())
|
||||
s := NewServer(&ServerOpts{
|
||||
Logger: logtest.Scoped(t),
|
||||
FS: fs,
|
||||
GitBackendSource: func(dir common.GitDir, repoName api.RepoName) git.GitBackend {
|
||||
backend := git.NewMockGitBackend()
|
||||
backend.ExecFunc.SetDefaultHook(func(ctx context.Context, args ...string) (io.ReadCloser, error) {
|
||||
if !gitcli.IsAllowedGitCmd(logtest.Scoped(t), args) {
|
||||
return nil, gitcli.ErrBadGitCommand
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "diff-tree":
|
||||
var stdout bytes.Buffer
|
||||
stdout.Write([]byte("teststdout"))
|
||||
return &errorReader{
|
||||
ReadCloser: io.NopCloser(&stdout),
|
||||
err: &gitcli.CommandFailedError{
|
||||
Stderr: []byte("teststderr"),
|
||||
ExitStatus: 42,
|
||||
Inner: errors.New("teststderr"),
|
||||
},
|
||||
}, nil
|
||||
case "merge-base":
|
||||
return &errorReader{
|
||||
ReadCloser: io.NopCloser(&bytes.Buffer{}),
|
||||
err: &gitcli.CommandFailedError{
|
||||
Stderr: []byte("teststderr"),
|
||||
ExitStatus: 1,
|
||||
Inner: errors.New("testerror"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return io.NopCloser(&bytes.Buffer{}), nil
|
||||
})
|
||||
return backend
|
||||
},
|
||||
GetRemoteURLFunc: getRemoteURLFunc,
|
||||
GetVCSSyncer: func(ctx context.Context, name api.RepoName) (vcssyncer.VCSSyncer, error) {
|
||||
|
||||
getRemoteURLSource := func(ctx context.Context, name api.RepoName) (vcssyncer.RemoteURLSource, error) {
|
||||
return vcssyncer.RemoteURLSourceFunc(func(ctx context.Context) (*vcs.URL, error) {
|
||||
raw := "https://" + string(name) + ".git"
|
||||
u, err := vcs.ParseURL(raw)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse URL %q", raw)
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}), nil
|
||||
}
|
||||
|
||||
return vcssyncer.NewGitRepoSyncer(logtest.Scoped(t), wrexec.NewNoOpRecordingCommandFactory(), getRemoteURLSource), nil
|
||||
},
|
||||
DB: db,
|
||||
RecordingCommandFactory: wrexec.NewNoOpRecordingCommandFactory(),
|
||||
Locker: NewRepositoryLocker(),
|
||||
RPSLimiter: ratelimit.NewInstrumentedLimiter("GitserverTest", rate.NewLimiter(rate.Inf, 10)),
|
||||
})
|
||||
|
||||
gs := NewGRPCServer(s, &GRPCServerConfig{
|
||||
ExhaustiveRequestLoggingEnabled: true,
|
||||
})
|
||||
fs.RepoClonedFunc.SetDefaultHook(func(repo api.RepoName) (bool, error) {
|
||||
if repo == "github.com/gorilla/mux" || repo == "my-mux" {
|
||||
return true, nil
|
||||
}
|
||||
err := s.cloneRepo(context.Background(), repo, NewMockRepositoryLock())
|
||||
require.NoError(t, err)
|
||||
return false, nil
|
||||
})
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
ss := gitserver.NewMockGitserverService_ExecServer()
|
||||
ss.ContextFunc.SetDefaultReturn(context.Background())
|
||||
var receivedData []byte
|
||||
ss.SendFunc.SetDefaultHook(func(er *v1.ExecResponse) error {
|
||||
receivedData = append(receivedData, er.GetData()...)
|
||||
return nil
|
||||
})
|
||||
err := gs.Exec(test.Request, ss)
|
||||
|
||||
if test.ExpectedCode == codes.OK && err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test.ExpectedCode != codes.OK {
|
||||
if err == nil {
|
||||
t.Fatal("expected error to be returned")
|
||||
}
|
||||
s, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, test.ExpectedCode, s.Code(), "wrong error code: expected %v, got %v %v", test.ExpectedCode, s.Code(), err)
|
||||
|
||||
if len(test.ExpectedDetails) > 0 {
|
||||
if diff := cmp.Diff(test.ExpectedDetails, s.Details(), cmpopts.IgnoreUnexported(v1.ExecStatusPayload{}, v1.RepoNotFoundPayload{})); diff != "" {
|
||||
t.Fatalf("unexpected error details (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.TrimSpace(s.Message()) != test.ExpectedError {
|
||||
t.Errorf("wrong error body: expected %q, got %q", test.ExpectedError, s.Message())
|
||||
}
|
||||
}
|
||||
|
||||
if strings.TrimSpace(string(receivedData)) != test.ExpectedBody {
|
||||
t.Errorf("wrong body: expected %q, got %q", test.ExpectedBody, string(receivedData))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type errorReader struct {
|
||||
io.ReadCloser
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
func (ec *errorReader) Read(p []byte) (int, error) {
|
||||
n, err := ec.ReadCloser.Read(p)
|
||||
if err == nil {
|
||||
return n, nil
|
||||
}
|
||||
if err == io.EOF {
|
||||
return n, ec.err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// makeSingleCommitRepo make create a new repo with a single commit and returns
|
||||
// the HEAD SHA
|
||||
func makeSingleCommitRepo(cmd func(string, ...string) string) string {
|
||||
|
||||
@ -32,13 +32,6 @@ func convertGRPCErrorToGitDomainError(err error) error {
|
||||
for _, detail := range st.Details() {
|
||||
switch payload := detail.(type) {
|
||||
|
||||
case *proto.ExecStatusPayload:
|
||||
return &CommandStatusError{
|
||||
Message: st.Message(),
|
||||
Stderr: payload.GetStderr(),
|
||||
StatusCode: payload.GetStatusCode(),
|
||||
}
|
||||
|
||||
case *proto.RepoNotFoundPayload:
|
||||
return &gitdomain.RepoNotExistError{
|
||||
Repo: api.RepoName(payload.GetRepo()),
|
||||
@ -105,24 +98,6 @@ func (r *errorTranslatingCreateCommitFromPatchBinaryClient) CloseAndRecv() (*pro
|
||||
return res, convertGRPCErrorToGitDomainError(err)
|
||||
}
|
||||
|
||||
func (r *errorTranslatingClient) Exec(ctx context.Context, in *proto.ExecRequest, opts ...grpc.CallOption) (proto.GitserverService_ExecClient, error) {
|
||||
//lint:ignore SA1019 existing usage of deprecated functionality. We are just logging an existing field.
|
||||
cc, err := r.base.Exec(ctx, in, opts...)
|
||||
if err != nil {
|
||||
return nil, convertGRPCErrorToGitDomainError(err)
|
||||
}
|
||||
return &errorTranslatingExecClient{cc}, nil
|
||||
}
|
||||
|
||||
type errorTranslatingExecClient struct {
|
||||
proto.GitserverService_ExecClient
|
||||
}
|
||||
|
||||
func (r *errorTranslatingExecClient) Recv() (*proto.ExecResponse, error) {
|
||||
res, err := r.GitserverService_ExecClient.Recv()
|
||||
return res, convertGRPCErrorToGitDomainError(err)
|
||||
}
|
||||
|
||||
func (r *errorTranslatingClient) DiskInfo(ctx context.Context, in *proto.DiskInfoRequest, opts ...grpc.CallOption) (*proto.DiskInfoResponse, error) {
|
||||
res, err := r.base.DiskInfo(ctx, in, opts...)
|
||||
return res, convertGRPCErrorToGitDomainError(err)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -30,16 +30,6 @@ func (r *automaticRetryClient) CreateCommitFromPatchBinary(ctx context.Context,
|
||||
|
||||
// Idempotent methods.
|
||||
|
||||
func (r *automaticRetryClient) Exec(ctx context.Context, in *proto.ExecRequest, opts ...grpc.CallOption) (proto.GitserverService_ExecClient, error) {
|
||||
// We specify the specific raw git commands that we allow in internal/gitserver/gitdomain/exec.go.
|
||||
// For all of these commands, we know that they are either:
|
||||
//
|
||||
// - 1. non-destructive (making them safe to retry)
|
||||
// - 2. not used in Exec directly, but instead only via a specific RPC (like CreateCommitFromPatchBinary) where the caller is responsible for retrying.
|
||||
opts = append(defaults.RetryPolicy, opts...)
|
||||
return r.base.Exec(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (r *automaticRetryClient) DiskInfo(ctx context.Context, in *proto.DiskInfoRequest, opts ...grpc.CallOption) (*proto.DiskInfoResponse, error) {
|
||||
opts = append(defaults.RetryPolicy, opts...)
|
||||
return r.base.DiskInfo(ctx, in, opts...)
|
||||
|
||||
1546
internal/gitserver/v1/gitserver.pb.go
generated
1546
internal/gitserver/v1/gitserver.pb.go
generated
File diff suppressed because it is too large
Load Diff
@ -77,10 +77,6 @@ service GitserverService {
|
||||
rpc DiskInfo(DiskInfoRequest) returns (DiskInfoResponse) {
|
||||
option idempotency_level = NO_SIDE_EFFECTS;
|
||||
}
|
||||
rpc Exec(ExecRequest) returns (stream ExecResponse) {
|
||||
option deprecated = true;
|
||||
}
|
||||
|
||||
// GetObject returns the object with the given OID in the given repository.
|
||||
//
|
||||
// If the object is not found, an error with a RevisionNotFoundPayload is
|
||||
@ -815,18 +811,6 @@ message CreateCommitFromPatchBinaryResponse {
|
||||
string changelist_id = 3;
|
||||
}
|
||||
|
||||
message ExecRequest {
|
||||
string repo = 1 [deprecated = true];
|
||||
bytes ensure_revision = 2 [deprecated = true];
|
||||
repeated bytes args = 3 [deprecated = true];
|
||||
bytes stdin = 4 [deprecated = true];
|
||||
bool no_timeout = 5 [deprecated = true];
|
||||
}
|
||||
|
||||
message ExecResponse {
|
||||
bytes data = 1 [deprecated = true];
|
||||
}
|
||||
|
||||
message RepoNotFoundPayload {
|
||||
string repo = 1;
|
||||
bool clone_in_progress = 2;
|
||||
@ -844,11 +828,6 @@ message FileNotFoundPayload {
|
||||
bytes path = 3;
|
||||
}
|
||||
|
||||
message ExecStatusPayload {
|
||||
int32 status_code = 1;
|
||||
string stderr = 2;
|
||||
}
|
||||
|
||||
message SearchRequest {
|
||||
// repo is the name of the repo to be searched
|
||||
string repo = 1;
|
||||
|
||||
85
internal/gitserver/v1/gitserver_grpc.pb.go
generated
85
internal/gitserver/v1/gitserver_grpc.pb.go
generated
@ -196,7 +196,6 @@ var GitserverRepositoryService_ServiceDesc = grpc.ServiceDesc{
|
||||
const (
|
||||
GitserverService_CreateCommitFromPatchBinary_FullMethodName = "/gitserver.v1.GitserverService/CreateCommitFromPatchBinary"
|
||||
GitserverService_DiskInfo_FullMethodName = "/gitserver.v1.GitserverService/DiskInfo"
|
||||
GitserverService_Exec_FullMethodName = "/gitserver.v1.GitserverService/Exec"
|
||||
GitserverService_GetObject_FullMethodName = "/gitserver.v1.GitserverService/GetObject"
|
||||
GitserverService_IsRepoCloneable_FullMethodName = "/gitserver.v1.GitserverService/IsRepoCloneable"
|
||||
GitserverService_ListGitolite_FullMethodName = "/gitserver.v1.GitserverService/ListGitolite"
|
||||
@ -235,8 +234,6 @@ const (
|
||||
type GitserverServiceClient interface {
|
||||
CreateCommitFromPatchBinary(ctx context.Context, opts ...grpc.CallOption) (GitserverService_CreateCommitFromPatchBinaryClient, error)
|
||||
DiskInfo(ctx context.Context, in *DiskInfoRequest, opts ...grpc.CallOption) (*DiskInfoResponse, error)
|
||||
// Deprecated: Do not use.
|
||||
Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (GitserverService_ExecClient, error)
|
||||
// GetObject returns the object with the given OID in the given repository.
|
||||
//
|
||||
// If the object is not found, an error with a RevisionNotFoundPayload is
|
||||
@ -495,39 +492,6 @@ func (c *gitserverServiceClient) DiskInfo(ctx context.Context, in *DiskInfoReque
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func (c *gitserverServiceClient) Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (GitserverService_ExecClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[1], GitserverService_Exec_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &gitserverServiceExecClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type GitserverService_ExecClient interface {
|
||||
Recv() (*ExecResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type gitserverServiceExecClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *gitserverServiceExecClient) Recv() (*ExecResponse, error) {
|
||||
m := new(ExecResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) GetObject(ctx context.Context, in *GetObjectRequest, opts ...grpc.CallOption) (*GetObjectResponse, error) {
|
||||
out := new(GetObjectResponse)
|
||||
err := c.cc.Invoke(ctx, GitserverService_GetObject_FullMethodName, in, out, opts...)
|
||||
@ -556,7 +520,7 @@ func (c *gitserverServiceClient) ListGitolite(ctx context.Context, in *ListGitol
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (GitserverService_SearchClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[2], GitserverService_Search_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[1], GitserverService_Search_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -588,7 +552,7 @@ func (x *gitserverServiceSearchClient) Recv() (*SearchResponse, error) {
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) Archive(ctx context.Context, in *ArchiveRequest, opts ...grpc.CallOption) (GitserverService_ArchiveClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[3], GitserverService_Archive_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[2], GitserverService_Archive_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -710,7 +674,7 @@ func (c *gitserverServiceClient) MergeBase(ctx context.Context, in *MergeBaseReq
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) Blame(ctx context.Context, in *BlameRequest, opts ...grpc.CallOption) (GitserverService_BlameClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[4], GitserverService_Blame_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[3], GitserverService_Blame_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -751,7 +715,7 @@ func (c *gitserverServiceClient) DefaultBranch(ctx context.Context, in *DefaultB
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) ReadFile(ctx context.Context, in *ReadFileRequest, opts ...grpc.CallOption) (GitserverService_ReadFileClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[5], GitserverService_ReadFile_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[4], GitserverService_ReadFile_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -801,7 +765,7 @@ func (c *gitserverServiceClient) ResolveRevision(ctx context.Context, in *Resolv
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) ListRefs(ctx context.Context, in *ListRefsRequest, opts ...grpc.CallOption) (GitserverService_ListRefsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[6], GitserverService_ListRefs_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[5], GitserverService_ListRefs_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -842,7 +806,7 @@ func (c *gitserverServiceClient) RevAtTime(ctx context.Context, in *RevAtTimeReq
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) RawDiff(ctx context.Context, in *RawDiffRequest, opts ...grpc.CallOption) (GitserverService_RawDiffClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[7], GitserverService_RawDiff_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[6], GitserverService_RawDiff_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -901,7 +865,7 @@ func (c *gitserverServiceClient) BehindAhead(ctx context.Context, in *BehindAhea
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) ChangedFiles(ctx context.Context, in *ChangedFilesRequest, opts ...grpc.CallOption) (GitserverService_ChangedFilesClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[8], GitserverService_ChangedFiles_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[7], GitserverService_ChangedFiles_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -942,7 +906,7 @@ func (c *gitserverServiceClient) Stat(ctx context.Context, in *StatRequest, opts
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) ReadDir(ctx context.Context, in *ReadDirRequest, opts ...grpc.CallOption) (GitserverService_ReadDirClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[9], GitserverService_ReadDir_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[8], GitserverService_ReadDir_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -974,7 +938,7 @@ func (x *gitserverServiceReadDirClient) Recv() (*ReadDirResponse, error) {
|
||||
}
|
||||
|
||||
func (c *gitserverServiceClient) CommitLog(ctx context.Context, in *CommitLogRequest, opts ...grpc.CallOption) (GitserverService_CommitLogClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[10], GitserverService_CommitLog_FullMethodName, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[9], GitserverService_CommitLog_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1011,8 +975,6 @@ func (x *gitserverServiceCommitLogClient) Recv() (*CommitLogResponse, error) {
|
||||
type GitserverServiceServer interface {
|
||||
CreateCommitFromPatchBinary(GitserverService_CreateCommitFromPatchBinaryServer) error
|
||||
DiskInfo(context.Context, *DiskInfoRequest) (*DiskInfoResponse, error)
|
||||
// Deprecated: Do not use.
|
||||
Exec(*ExecRequest, GitserverService_ExecServer) error
|
||||
// GetObject returns the object with the given OID in the given repository.
|
||||
//
|
||||
// If the object is not found, an error with a RevisionNotFoundPayload is
|
||||
@ -1231,9 +1193,6 @@ func (UnimplementedGitserverServiceServer) CreateCommitFromPatchBinary(Gitserver
|
||||
func (UnimplementedGitserverServiceServer) DiskInfo(context.Context, *DiskInfoRequest) (*DiskInfoResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DiskInfo not implemented")
|
||||
}
|
||||
func (UnimplementedGitserverServiceServer) Exec(*ExecRequest, GitserverService_ExecServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Exec not implemented")
|
||||
}
|
||||
func (UnimplementedGitserverServiceServer) GetObject(context.Context, *GetObjectRequest) (*GetObjectResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetObject not implemented")
|
||||
}
|
||||
@ -1381,27 +1340,6 @@ func _GitserverService_DiskInfo_Handler(srv interface{}, ctx context.Context, de
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _GitserverService_Exec_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(ExecRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(GitserverServiceServer).Exec(m, &gitserverServiceExecServer{stream})
|
||||
}
|
||||
|
||||
type GitserverService_ExecServer interface {
|
||||
Send(*ExecResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type gitserverServiceExecServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *gitserverServiceExecServer) Send(m *ExecResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _GitserverService_GetObject_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetObjectRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -2071,11 +2009,6 @@ var GitserverService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _GitserverService_CreateCommitFromPatchBinary_Handler,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "Exec",
|
||||
Handler: _GitserverService_Exec_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "Search",
|
||||
Handler: _GitserverService_Search_Handler,
|
||||
|
||||
@ -135,7 +135,6 @@
|
||||
path: github.com/sourcegraph/sourcegraph/internal/gitserver/v1
|
||||
interfaces:
|
||||
- GitserverServiceClient
|
||||
- GitserverService_ExecServer
|
||||
- GitserverService_ArchiveServer
|
||||
- GitserverService_ArchiveClient
|
||||
- GitserverService_BlameServer
|
||||
|
||||
Loading…
Reference in New Issue
Block a user