exec.go 2.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
package handler

import (
	"context"
	"fmt"
	"os"

	"gitlab.com/gitlab-org/gitaly/auth"
	"gitlab.com/gitlab-org/gitaly/client"

	"gitlab.com/gitlab-org/gitlab-shell/go/internal/config"
	"gitlab.com/gitlab-org/labkit/tracing"
	"google.golang.org/grpc"
)

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// GitalyHandlerFunc implementations are responsible for making
// an appropriate Gitaly call using the provided client and context
// and returning an error from the Gitaly call.
type GitalyHandlerFunc func(ctx context.Context, client *grpc.ClientConn) (int32, error)

type GitalyConn struct {
	ctx   context.Context
	conn  *grpc.ClientConn
	close func()
}

type GitalyCommand struct {
	Config      *config.Config
	ServiceName string
	Address     string
	Token       string
}
33

34 35 36 37 38 39 40 41 42
// RunGitalyCommand provides a bootstrap for Gitaly commands executed
// through GitLab-Shell. It ensures that logging, tracing and other
// common concerns are configured before executing the `handler`.
func (gc *GitalyCommand) RunGitalyCommand(handler GitalyHandlerFunc) error {
	gitalyConn, err := getConn(gc)

	if err != nil {
		return err
	}
43

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
	_, err = handler(gitalyConn.ctx, gitalyConn.conn)

	gitalyConn.close()

	return err
}

func getConn(gc *GitalyCommand) (*GitalyConn, error) {
	if gc.Address == "" {
		return nil, fmt.Errorf("no gitaly_address given")
	}

	connOpts := client.DefaultDialOpts
	if gc.Token != "" {
		connOpts = append(client.DefaultDialOpts, grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2(gc.Token)))
	}

61 62
	// Use a working directory that won't get removed or unmounted.
	if err := os.Chdir("/"); err != nil {
63
		return nil, err
64 65 66
	}

	// Configure distributed tracing
67
	serviceName := fmt.Sprintf("gitlab-shell-%v", gc.ServiceName)
68 69 70 71 72 73 74 75 76 77 78
	closer := tracing.Initialize(
		tracing.WithServiceName(serviceName),

		// For GitLab-Shell, we explicitly initialize tracing from a config file
		// instead of the default environment variable (using GITLAB_TRACING)
		// This decision was made owing to the difficulty in passing environment
		// variables into GitLab-Shell processes.
		//
		// Processes are spawned as children of the SSH daemon, which tightly
		// controls environment variables; doing this means we don't have to
		// enable PermitUserEnvironment
79
		tracing.WithConnectionString(gc.Config.GitlabTracing),
80 81 82 83
	)

	ctx, finished := tracing.ExtractFromEnv(context.Background())

84
	conn, err := client.Dial(gc.Address, connOpts)
85
	if err != nil {
86
		return nil, err
87 88
	}

89 90 91 92
	finish := func() {
		finished()
		closer.Close()
		conn.Close()
93 94
	}

95
	return &GitalyConn{ctx: ctx, conn: conn, close: finish}, nil
96
}