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

create_from_snapshot.go « repository « service « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e2cf897584195a2799924477f242d01561d03882 (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
package repository

import (
	"context"
	"net"
	"net/http"
	"os"
	"os/exec"
	"time"

	"gitlab.com/gitlab-org/gitaly/internal/command"
	"gitlab.com/gitlab-org/gitaly/internal/helper"
	"gitlab.com/gitlab-org/gitaly/internal/tempdir"
	"gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
	"gitlab.com/gitlab-org/labkit/correlation"
	"gitlab.com/gitlab-org/labkit/tracing"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// httpTransport defines a http.Transport with values that are more restrictive
// than for http.DefaultTransport.
//
// They define shorter TLS Handshake, and more aggressive connection closing
// to prevent the connection hanging and reduce FD usage.
var httpTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 10 * time.Second,
	}).DialContext,
	MaxIdleConns:          2,
	IdleConnTimeout:       30 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 10 * time.Second,
	ResponseHeaderTimeout: 30 * time.Second,
}

// httpClient defines a http.Client that uses the specialized httpTransport
// (above). It also disables following redirects, as we don't expect this to be
// required for this RPC.
var httpClient = &http.Client{
	Transport: correlation.NewInstrumentedRoundTripper(tracing.NewRoundTripper(httpTransport)),
	CheckRedirect: func(*http.Request, []*http.Request) error {
		return http.ErrUseLastResponse
	},
}

func untar(ctx context.Context, path string, in *gitalypb.CreateRepositoryFromSnapshotRequest) error {
	req, err := http.NewRequest("GET", in.HttpUrl, nil)
	if err != nil {
		return status.Errorf(codes.InvalidArgument, "Bad HTTP URL: %v", err)
	}

	if in.HttpAuth != "" {
		req.Header.Set("Authorization", in.HttpAuth)
	}

	rsp, err := httpClient.Do(req)
	if err != nil {
		return status.Errorf(codes.Internal, "HTTP request failed: %v", err)
	}
	defer rsp.Body.Close()

	if rsp.StatusCode < http.StatusOK || rsp.StatusCode >= http.StatusMultipleChoices {
		return status.Errorf(codes.Internal, "HTTP server: %v", rsp.Status)
	}

	cmd, err := command.New(ctx, exec.Command("tar", "-C", path, "-xvf", "-"), rsp.Body, nil, nil)
	if err != nil {
		return err
	}

	return cmd.Wait()
}

func (s *server) CreateRepositoryFromSnapshot(ctx context.Context, in *gitalypb.CreateRepositoryFromSnapshotRequest) (*gitalypb.CreateRepositoryFromSnapshotResponse, error) {
	realPath, err := helper.GetPath(in.Repository)
	if err != nil {
		return nil, err
	}

	if _, err := os.Stat(realPath); !os.IsNotExist(err) {
		return nil, status.Errorf(codes.InvalidArgument, "destination directory exists")
	}

	// Perform all operations against a temporary directory, only moving it to
	// the canonical location if retrieving and unpacking the snapshot is a
	// success
	tempRepo, tempPath, err := tempdir.NewAsRepository(ctx, in.Repository)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "couldn't create temporary directory: %v", err)
	}

	// The archive contains a partial git repository, missing a config file and
	// other important items. Initializing a new bare one and extracting the
	// archive on top of it ensures the created git repository has everything
	// it needs (especially, the config file and hooks directory).
	//
	// NOTE: The received archive is trusted *a lot*. Before pointing this RPC
	// at endpoints not under our control, it should undergo a lot of hardning.
	crr := &gitalypb.CreateRepositoryRequest{Repository: tempRepo}
	if _, err := s.CreateRepository(ctx, crr); err != nil {
		return nil, status.Errorf(codes.Internal, "couldn't create empty bare repository: %v", err)
	}

	if err := untar(ctx, tempPath, in); err != nil {
		return nil, err
	}

	if err := os.Rename(tempPath, realPath); err != nil {
		return nil, status.Errorf(codes.Internal, "Promoting temporary directory failed: %v", err)
	}

	return &gitalypb.CreateRepositoryFromSnapshotResponse{}, nil
}