Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-workhorse
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-workhorse
Commits
b0798c9b
Commit
b0798c9b
authored
Apr 06, 2018
by
Nick Thomas
Committed by
Jacob Vosmaer (GitLab)
Apr 06, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bridge between Gitaly and GitLab for a new repository snapshot endpoint
parent
9c54fcc3
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
510 additions
and
112 deletions
+510
-112
gitaly_test.go
gitaly_test.go
+103
-0
internal/git/snapshot.go
internal/git/snapshot.go
+67
-0
internal/gitaly/repository.go
internal/gitaly/repository.go
+15
-0
internal/testhelper/gitaly.go
internal/testhelper/gitaly.go
+33
-5
internal/upstream/routes.go
internal/upstream/routes.go
+1
-0
vendor/gitlab.com/gitlab-org/gitaly-proto/go/VERSION
vendor/gitlab.com/gitlab-org/gitaly-proto/go/VERSION
+1
-1
vendor/gitlab.com/gitlab-org/gitaly-proto/go/blob.pb.go
vendor/gitlab.com/gitlab-org/gitaly-proto/go/blob.pb.go
+4
-0
vendor/gitlab.com/gitlab-org/gitaly-proto/go/repository-service.pb.go
...b.com/gitlab-org/gitaly-proto/go/repository-service.pb.go
+281
-101
vendor/vendor.json
vendor/vendor.json
+5
-5
No files found.
gitaly_test.go
View file @
b0798c9b
...
...
@@ -2,6 +2,7 @@ package main
import
(
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
...
...
@@ -22,6 +23,8 @@ import (
pb
"gitlab.com/gitlab-org/gitaly-proto/go"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
...
...
@@ -579,6 +582,106 @@ func TestGetPatchProxiedToGitalyInterruptedStream(t *testing.T) {
}
}
func
TestGetSnapshotProxiedToGitalySuccessfully
(
t
*
testing
.
T
)
{
gitalyServer
,
socketPath
:=
startGitalyServer
(
t
,
codes
.
OK
)
defer
gitalyServer
.
Stop
()
gitalyAddress
:=
"unix://"
+
socketPath
expectedBody
:=
testhelper
.
GitalyGetSnapshotResponseMock
archiveLength
:=
len
(
expectedBody
)
params
:=
buildGetSnapshotParams
(
gitalyAddress
,
buildPbRepo
(
"default"
,
"foo/bar.git"
))
resp
,
body
,
err
:=
doSendDataRequest
(
"/api/v4/projects/:id/snapshot"
,
"git-snapshot"
,
params
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
http
.
StatusOK
,
resp
.
StatusCode
,
"GET %q: status code"
,
resp
.
Request
.
URL
)
assert
.
Equal
(
t
,
expectedBody
,
string
(
body
),
"GET %q: body"
,
resp
.
Request
.
URL
)
assert
.
Equal
(
t
,
archiveLength
,
len
(
body
),
"GET %q: body size"
,
resp
.
Request
.
URL
)
testhelper
.
AssertResponseHeader
(
t
,
resp
,
"Content-Disposition"
,
`attachment; filename="snapshot.tar"`
)
testhelper
.
AssertResponseHeader
(
t
,
resp
,
"Content-Type"
,
"application/x-tar"
)
testhelper
.
AssertResponseHeader
(
t
,
resp
,
"Content-Transfer-Encoding"
,
"binary"
)
testhelper
.
AssertResponseHeader
(
t
,
resp
,
"Cache-Control"
,
"private"
)
}
func
TestGetSnapshotProxiedToGitalyInterruptedStream
(
t
*
testing
.
T
)
{
gitalyServer
,
socketPath
:=
startGitalyServer
(
t
,
codes
.
OK
)
defer
gitalyServer
.
Stop
()
gitalyAddress
:=
"unix://"
+
socketPath
params
:=
buildGetSnapshotParams
(
gitalyAddress
,
buildPbRepo
(
"default"
,
"foo/bar.git"
))
resp
,
_
,
err
:=
doSendDataRequest
(
"/api/v4/projects/:id/snapshot"
,
"git-snapshot"
,
params
)
require
.
NoError
(
t
,
err
)
// This causes the server stream to be interrupted instead of consumed entirely.
resp
.
Body
.
Close
()
done
:=
make
(
chan
struct
{})
go
func
()
{
gitalyServer
.
WaitGroup
.
Wait
()
close
(
done
)
}()
select
{
case
<-
done
:
return
case
<-
time
.
After
(
10
*
time
.
Second
)
:
t
.
Fatal
(
"time out waiting for gitaly handler to return"
)
}
}
func
buildGetSnapshotParams
(
gitalyAddress
string
,
repo
*
pb
.
Repository
)
string
{
msg
:=
serializedMessage
(
"GetSnapshotRequest"
,
&
pb
.
GetSnapshotRequest
{
Repository
:
repo
})
return
buildGitalyRpcParams
(
gitalyAddress
,
msg
)
}
type
rpcArg
struct
{
k
string
v
interface
{}
}
// Gitlab asks workhorse to perform some long-running RPCs for it by sending
// the RPC arguments (which are protobuf messages) in HTTP response headers.
// The messages are encoded to JSON objects using pbjson, The strings are then
// re-encoded to JSON strings using json. We must replicate this behaviour here
func
buildGitalyRpcParams
(
gitalyAddress
string
,
rpcArgs
...
rpcArg
)
string
{
built
:=
map
[
string
]
interface
{}{
"GitalyServer"
:
map
[
string
]
string
{
"Address"
:
gitalyAddress
,
"Token"
:
""
,
},
}
for
_
,
arg
:=
range
rpcArgs
{
built
[
arg
.
k
]
=
arg
.
v
}
b
,
err
:=
json
.
Marshal
(
interface
{}(
built
))
if
err
!=
nil
{
panic
(
err
)
}
return
string
(
b
)
}
func
buildPbRepo
(
storageName
,
relativePath
string
)
*
pb
.
Repository
{
return
&
pb
.
Repository
{
StorageName
:
storageName
,
RelativePath
:
relativePath
,
}
}
func
serializedMessage
(
name
string
,
arg
proto
.
Message
)
rpcArg
{
m
:=
&
jsonpb
.
Marshaler
{}
str
,
err
:=
m
.
MarshalToString
(
arg
)
if
err
!=
nil
{
panic
(
err
)
}
return
rpcArg
{
name
,
str
}
}
type
combinedServer
struct
{
*
grpc
.
Server
*
testhelper
.
GitalyTestServer
...
...
internal/git/snapshot.go
0 → 100644
View file @
b0798c9b
package
git
import
(
"fmt"
"io"
"net/http"
"github.com/golang/protobuf/jsonpb"
pb
"gitlab.com/gitlab-org/gitaly-proto/go"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
)
type
snapshot
struct
{
senddata
.
Prefix
}
type
snapshotParams
struct
{
GitalyServer
gitaly
.
Server
GetSnapshotRequest
string
}
var
(
SendSnapshot
=
&
snapshot
{
"git-snapshot:"
}
)
func
(
s
*
snapshot
)
Inject
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
sendData
string
)
{
var
params
snapshotParams
if
err
:=
s
.
Unpack
(
&
params
,
sendData
);
err
!=
nil
{
helper
.
Fail500
(
w
,
r
,
fmt
.
Errorf
(
"SendSnapshot: unpack sendData: %v"
,
err
))
return
}
request
:=
&
pb
.
GetSnapshotRequest
{}
if
err
:=
jsonpb
.
UnmarshalString
(
params
.
GetSnapshotRequest
,
request
);
err
!=
nil
{
helper
.
Fail500
(
w
,
r
,
fmt
.
Errorf
(
"SendSnapshot: unmarshal GetSnapshotRequest: %v"
,
err
))
return
}
c
,
err
:=
gitaly
.
NewRepositoryClient
(
params
.
GitalyServer
)
if
err
!=
nil
{
helper
.
Fail500
(
w
,
r
,
fmt
.
Errorf
(
"SendSnapshot: gitaly.NewRepositoryClient: %v"
,
err
))
return
}
reader
,
err
:=
c
.
SnapshotReader
(
r
.
Context
(),
request
)
if
err
!=
nil
{
helper
.
Fail500
(
w
,
r
,
fmt
.
Errorf
(
"SendSnapshot: client.SnapshotReader: %v"
,
err
))
return
}
w
.
Header
()
.
Del
(
"Content-Length"
)
w
.
Header
()
.
Set
(
"Content-Disposition"
,
`attachment; filename="snapshot.tar"`
)
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/x-tar"
)
w
.
Header
()
.
Set
(
"Content-Transfer-Encoding"
,
"binary"
)
w
.
Header
()
.
Set
(
"Cache-Control"
,
"private"
)
w
.
WriteHeader
(
http
.
StatusOK
)
// Errors aren't detectable beyond this point
if
_
,
err
:=
io
.
Copy
(
w
,
reader
);
err
!=
nil
{
helper
.
LogError
(
r
,
fmt
.
Errorf
(
"SendSnapshot: copy gitaly output: %v"
,
err
))
}
return
}
internal/gitaly/repository.go
View file @
b0798c9b
...
...
@@ -28,3 +28,18 @@ func (client *RepositoryClient) ArchiveReader(ctx context.Context, request *pb.G
return
resp
.
GetData
(),
err
}),
nil
}
// SnapshotReader performs a GetSnapshot Gitaly request and returns an io.Reader
// for the response
func
(
client
*
RepositoryClient
)
SnapshotReader
(
ctx
context
.
Context
,
request
*
pb
.
GetSnapshotRequest
)
(
io
.
Reader
,
error
)
{
c
,
err
:=
client
.
GetSnapshot
(
ctx
,
request
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"RepositoryService::GetSnapshot: %v"
,
err
)
}
return
streamio
.
NewReader
(
func
()
([]
byte
,
error
)
{
resp
,
err
:=
c
.
Recv
()
return
resp
.
GetData
(),
err
}),
nil
}
internal/testhelper/gitaly.go
View file @
b0798c9b
...
...
@@ -23,11 +23,14 @@ type GitalyTestServer struct {
}
var
(
GitalyInfoRefsResponseMock
=
strings
.
Repeat
(
"Mock Gitaly InfoRefsResponse data"
,
100000
)
GitalyGetBlobResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetBlobResponse data"
,
100000
)
GitalyGetArchiveResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetArchiveResponse data"
,
100000
)
GitalyGetDiffResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetDiffResponse data"
,
100000
)
GitalyGetPatchResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetPatchResponse data"
,
100000
)
GitalyInfoRefsResponseMock
=
strings
.
Repeat
(
"Mock Gitaly InfoRefsResponse data"
,
100000
)
GitalyGetBlobResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetBlobResponse data"
,
100000
)
GitalyGetArchiveResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetArchiveResponse data"
,
100000
)
GitalyGetDiffResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetDiffResponse data"
,
100000
)
GitalyGetPatchResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetPatchResponse data"
,
100000
)
GitalyGetSnapshotResponseMock
=
strings
.
Repeat
(
"Mock Gitaly GetSnapshotResponse data"
,
100000
)
GitalyReceivePackResponseMock
[]
byte
GitalyUploadPackResponseMock
[]
byte
)
...
...
@@ -280,6 +283,27 @@ func (s *GitalyTestServer) RawPatch(in *pb.RawPatchRequest, stream pb.DiffServic
return
s
.
finalError
()
}
func
(
s
*
GitalyTestServer
)
GetSnapshot
(
in
*
pb
.
GetSnapshotRequest
,
stream
pb
.
RepositoryService_GetSnapshotServer
)
error
{
s
.
WaitGroup
.
Add
(
1
)
defer
s
.
WaitGroup
.
Done
()
if
err
:=
validateRepository
(
in
.
GetRepository
());
err
!=
nil
{
return
err
}
nSends
,
err
:=
sendBytes
([]
byte
(
GitalyGetSnapshotResponseMock
),
100
,
func
(
p
[]
byte
)
error
{
return
stream
.
Send
(
&
pb
.
GetSnapshotResponse
{
Data
:
p
})
})
if
err
!=
nil
{
return
err
}
if
nSends
<=
1
{
panic
(
"should have sent more than one message"
)
}
return
s
.
finalError
()
}
func
(
s
*
GitalyTestServer
)
RepositoryExists
(
context
.
Context
,
*
pb
.
RepositoryExistsRequest
)
(
*
pb
.
RepositoryExistsResponse
,
error
)
{
return
nil
,
nil
}
...
...
@@ -408,6 +432,10 @@ func (s *GitalyTestServer) Cleanup(context.Context, *pb.CleanupRequest) (*pb.Cle
return
nil
,
nil
}
func
(
s
*
GitalyTestServer
)
CreateRepositoryFromSnapshot
(
context
.
Context
,
*
pb
.
CreateRepositoryFromSnapshotRequest
)
(
*
pb
.
CreateRepositoryFromSnapshotResponse
,
error
)
{
return
nil
,
nil
}
// sendBytes returns the number of times the 'sender' function was called and an error.
func
sendBytes
(
data
[]
byte
,
chunkSize
int
,
sender
func
([]
byte
)
error
)
(
int
,
error
)
{
i
:=
0
...
...
internal/upstream/routes.go
View file @
b0798c9b
...
...
@@ -140,6 +140,7 @@ func (u *Upstream) configureRoutes() {
git
.
SendBlob
,
git
.
SendDiff
,
git
.
SendPatch
,
git
.
SendSnapshot
,
artifacts
.
SendEntry
,
sendurl
.
SendURL
,
)
...
...
vendor/gitlab.com/gitlab-org/gitaly-proto/go/VERSION
View file @
b0798c9b
0.9
5
.0
0.9
6
.0
vendor/gitlab.com/gitlab-org/gitaly-proto/go/blob.pb.go
View file @
b0798c9b
...
...
@@ -219,6 +219,10 @@ It has these top-level messages:
GetInfoAttributesResponse
CalculateChecksumRequest
CalculateChecksumResponse
GetSnapshotRequest
GetSnapshotResponse
CreateRepositoryFromSnapshotRequest
CreateRepositoryFromSnapshotResponse
ServerInfoRequest
ServerInfoResponse
Repository
...
...
vendor/gitlab.com/gitlab-org/gitaly-proto/go/repository-service.pb.go
View file @
b0798c9b
This diff is collapsed.
Click to expand it.
vendor/vendor.json
View file @
b0798c9b
...
...
@@ -197,12 +197,12 @@
"revisionTime"
:
"2016-11-17T07:43:51Z"
},
{
"checksumSHA1"
:
"
MVrf0BwLEpU3uGHmYOrIWVoJGCk
="
,
"checksumSHA1"
:
"
B+FjK1qIgELTplSb/hl3ZHGHjl0
="
,
"path"
:
"gitlab.com/gitlab-org/gitaly-proto/go"
,
"revision"
:
"
00d4006669b30b6a0fe66aaa9fa72285d18616fb
"
,
"revisionTime"
:
"2018-04-0
3T14:25:38
Z"
,
"version"
:
"v0.9
5
.0"
,
"versionExact"
:
"v0.9
5
.0"
"revision"
:
"
8680efed3cdd68f3a2d4ccd56fb6684a6761d59e
"
,
"revisionTime"
:
"2018-04-0
5T16:15:56
Z"
,
"version"
:
"v0.9
6
.0"
,
"versionExact"
:
"v0.9
6
.0"
},
{
"checksumSHA1"
:
"dUHJbKas746n5fLzlwxHb6FOCxs="
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment