As we reevaluate how to best support and maintain Staging Ref in the future, we encourage development teams using this environment to highlight their use cases in the following issue: https://gitlab.com/gitlab-com/gl-infra/software-delivery/framework/software-delivery-framework-issue-tracker/-/issues/36.

Skip to content
Snippets Groups Projects
Unverified Commit e7e6a3c8 authored by Igor Drozdov's avatar Igor Drozdov Committed by GitLab
Browse files

Merge branch '762_replace_geo_pull_push' into 'main'

Geo: Replace Git over HTTP calls with Workhorse HTTP endpoint for SSH push

See merge request https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/1076



Merged-by: default avatarIgor Drozdov <idrozdov@gitlab.com>
Approved-by: default avatarAsh McKenzie <amckenzie@gitlab.com>
Approved-by: default avatarIgor Drozdov <idrozdov@gitlab.com>
Reviewed-by: default avatarAsh McKenzie <amckenzie@gitlab.com>
Co-authored-by: default avatarVasilii Iakliushin <viakliushin@gitlab.com>
parents 1f9a2170 c8de0838
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -6,11 +6,13 @@ import (
"fmt"
"io"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/accessverifier"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/git"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/pktline"
"gitlab.com/gitlab-org/labkit/log"
)
const service = "git-receive-pack"
Loading
Loading
@@ -21,6 +23,7 @@ type PushCommand struct {
Config *config.Config
ReadWriter *readwriter.ReadWriter
Response *accessverifier.Response
Args *commandargs.Shell
}
// See Uploading Data > HTTP(S) section at:
Loading
Loading
@@ -35,6 +38,15 @@ type PushCommand struct {
func (c *PushCommand) Execute(ctx context.Context) error {
data := c.Response.Payload.Data
client := &git.Client{URL: data.PrimaryRepo, Headers: data.RequestHeaders}
// For Git over SSH routing
if data.GeoProxyPushSSHDirectToPrimary {
log.ContextLogger(ctx).Info("Using Git over SSH receive pack")
client.Headers["Git-Protocol"] = c.Args.Env.GitProtocolVersion
return c.requestSSHReceivePack(ctx, client)
}
if err := c.requestInfoRefs(ctx, client); err != nil {
return err
}
Loading
Loading
@@ -62,6 +74,18 @@ func (c *PushCommand) requestInfoRefs(ctx context.Context, client *git.Client) e
return err
}
func (c *PushCommand) requestSSHReceivePack(ctx context.Context, client *git.Client) error {
response, err := client.SSHReceivePack(ctx, io.NopCloser(c.ReadWriter.In))
if err != nil {
return err
}
defer response.Body.Close()
_, err = io.Copy(c.ReadWriter.Out, response.Body)
return err
}
func (c *PushCommand) requestReceivePack(ctx context.Context, client *git.Client) error {
pipeReader, pipeWriter := io.Pipe()
go c.readFromStdin(pipeWriter)
Loading
Loading
Loading
Loading
@@ -11,9 +11,11 @@ import (
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/v14/client/testserver"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/accessverifier"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/sshenv"
)
var (
Loading
Loading
@@ -116,6 +118,35 @@ func TestExecuteWithFailedReceivePack(t *testing.T) {
require.Equal(t, "Remote repository is unavailable", err.Error())
}
func TestPushExecuteWithSSHReceivePack(t *testing.T) {
url := setupSSHPush(t, http.StatusOK)
output := &bytes.Buffer{}
input := strings.NewReader(cloneResponse + "0009done\n")
cmd := &PushCommand{
Config: &config.Config{GitlabUrl: url},
ReadWriter: &readwriter.ReadWriter{Out: output, In: input},
Response: &accessverifier.Response{
Payload: accessverifier.CustomPayload{
Data: accessverifier.CustomPayloadData{
PrimaryRepo: url,
GeoProxyDirectToPrimary: true,
GeoProxyPushSSHDirectToPrimary: true,
RequestHeaders: map[string]string{"Authorization": "token"},
},
},
},
Args: &commandargs.Shell{
Env: sshenv.Env{
GitProtocolVersion: "version=2",
},
},
}
require.NoError(t, cmd.Execute(context.Background()))
require.Equal(t, "receive-pack-response", output.String())
}
func setup(t *testing.T, receivePackStatusCode int) (string, io.Reader) {
infoRefs := "001f# service=git-receive-pack\n" + flush + infoRefsWithoutPrefix
receivePackPrefix := "00ab4c9d98d7750fa65db8ddcc60a89ef919f7a179f9 df505c066e4e63a801268a84627d7e8f7e033c7a " +
Loading
Loading
@@ -153,3 +184,25 @@ func setup(t *testing.T, receivePackStatusCode int) (string, io.Reader) {
return testserver.StartHttpServer(t, requests), input
}
func setupSSHPush(t *testing.T, uploadPackStatusCode int) string {
requests := []testserver.TestRequestHandler{
{
Path: "/ssh-receive-pack",
Handler: func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
defer r.Body.Close()
require.True(t, strings.HasSuffix(string(body), "0009done\n"))
require.Equal(t, "version=2", r.Header.Get("Git-Protocol"))
require.Equal(t, "token", r.Header.Get("Authorization"))
w.WriteHeader(uploadPackStatusCode)
w.Write([]byte("receive-pack-response"))
},
},
}
return testserver.StartHttpServer(t, requests)
}
Loading
Loading
@@ -54,6 +54,7 @@ func (c *Command) Execute(ctx context.Context) (context.Context, error) {
Config: c.Config,
ReadWriter: c.ReadWriter,
Response: response,
Args: c.Args,
}
return ctxWithLogData, cmd.Execute(ctx)
Loading
Loading
Loading
Loading
@@ -57,6 +57,7 @@ type CustomPayloadData struct {
GeoProxyFetchDirectToPrimary bool `json:"geo_proxy_fetch_direct_to_primary"`
GeoProxyFetchDirectToPrimaryWithOptions bool `json:"geo_proxy_fetch_direct_to_primary_with_options"`
GeoProxyFetchSSHDirectToPrimary bool `json:"geo_proxy_fetch_ssh_direct_to_primary"`
GeoProxyPushSSHDirectToPrimary bool `json:"geo_proxy_push_ssh_direct_to_primary"`
}
// CustomPayload represents a custom payload
Loading
Loading
Loading
Loading
@@ -17,6 +17,7 @@ var httpClient = &http.Client{
const (
repoUnavailableErrMsg = "Remote repository is unavailable"
sshUploadPackPath = "/ssh-upload-pack"
sshReceivePackPath = "/ssh-receive-pack"
)
// Client represents a client for interacting with Git repositories.
Loading
Loading
@@ -69,6 +70,16 @@ func (c *Client) SSHUploadPack(ctx context.Context, body io.Reader) (*http.Respo
return c.do(request)
}
// SSHReceivePack sends a SSH Git push request to the server.
func (c *Client) SSHReceivePack(ctx context.Context, body io.Reader) (*http.Response, error) {
request, err := http.NewRequestWithContext(ctx, http.MethodPost, c.URL+sshReceivePackPath, body)
if err != nil {
return nil, err
}
return c.do(request)
}
func (c *Client) do(request *http.Request) (*http.Response, error) {
for k, v := range c.Headers {
request.Header.Add(k, v)
Loading
Loading
Loading
Loading
@@ -78,6 +78,20 @@ func TestSSHUploadPack(t *testing.T) {
require.Equal(t, "ssh-upload-pack: content", string(body))
}
func TestSSHReceivePack(t *testing.T) {
client := setup(t)
refsBody := "0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7\n"
response, err := client.SSHReceivePack(context.Background(), bytes.NewReader([]byte(refsBody)))
require.NoError(t, err)
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
require.NoError(t, err)
require.Equal(t, "ssh-receive-pack: content", string(body))
}
func TestFailedHTTPRequest(t *testing.T) {
requests := []testserver.TestRequestHandler{
{
Loading
Loading
@@ -193,6 +207,19 @@ func setup(t *testing.T) *Client {
w.Write([]byte("ssh-upload-pack: content"))
},
},
{
Path: sshReceivePackPath,
Handler: func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, customHeaders["Authorization"], r.Header.Get("Authorization"))
require.Equal(t, customHeaders["Header-One"], r.Header.Get("Header-One"))
_, err := io.ReadAll(r.Body)
require.NoError(t, err)
defer r.Body.Close()
w.Write([]byte("ssh-receive-pack: content"))
},
},
}
client := &Client{
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment