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
Commit 033c81d5 authored by Igor Drozdov's avatar Igor Drozdov Committed by Nick Thomas
Browse files

Go implementation for git-receive-pack

parent 12ca54c2
No related branches found
No related tags found
No related merge requests found
Showing
with 727 additions and 49 deletions
Loading
Loading
@@ -115,8 +115,7 @@ func setupWithRequests(t *testing.T, config *config.Config) (*GitlabClient, func
},
}
cleanup, url, err := testserver.StartHttpsServer(requests)
require.NoError(t, err)
url, cleanup := testserver.StartHttpsServer(t, requests)
config.GitlabUrl = url
client, err := GetClient(config)
Loading
Loading
package testserver
import (
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
pb "gitlab.com/gitlab-org/gitaly-proto/go/gitalypb"
)
type testGitalyServer struct{}
func (s *testGitalyServer) SSHReceivePack(stream pb.SSHService_SSHReceivePackServer) error {
req, err := stream.Recv()
if err != nil {
return err
}
response := []byte("ReceivePack: " + req.GlId + " " + req.Repository.GlRepository)
stream.Send(&pb.SSHReceivePackResponse{Stdout: response})
return nil
}
func (s *testGitalyServer) SSHUploadPack(stream pb.SSHService_SSHUploadPackServer) error {
return nil
}
func (s *testGitalyServer) SSHUploadArchive(stream pb.SSHService_SSHUploadArchiveServer) error {
return nil
}
func StartGitalyServer(t *testing.T) (string, func()) {
tempDir, _ := ioutil.TempDir("", "gitlab-shell-test-api")
gitalySocketPath := path.Join(tempDir, "gitaly.sock")
err := os.MkdirAll(filepath.Dir(gitalySocketPath), 0700)
require.NoError(t, err)
server := grpc.NewServer()
listener, err := net.Listen("unix", gitalySocketPath)
require.NoError(t, err)
pb.RegisterSSHServiceServer(server, &testGitalyServer{})
go server.Serve(listener)
gitalySocketUrl := "unix:" + gitalySocketPath
cleanup := func() {
server.Stop()
os.RemoveAll(tempDir)
}
return gitalySocketUrl, cleanup
}
Loading
Loading
@@ -10,6 +10,9 @@ import (
"os"
"path"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper"
)
Loading
Loading
@@ -24,15 +27,12 @@ type TestRequestHandler struct {
Handler func(w http.ResponseWriter, r *http.Request)
}
func StartSocketHttpServer(handlers []TestRequestHandler) (func(), string, error) {
if err := os.MkdirAll(filepath.Dir(testSocket), 0700); err != nil {
return nil, "", err
}
func StartSocketHttpServer(t *testing.T, handlers []TestRequestHandler) (string, func()) {
err := os.MkdirAll(filepath.Dir(testSocket), 0700)
require.NoError(t, err)
socketListener, err := net.Listen("unix", testSocket)
if err != nil {
return nil, "", err
}
require.NoError(t, err)
server := http.Server{
Handler: buildHandler(handlers),
Loading
Loading
@@ -44,30 +44,27 @@ func StartSocketHttpServer(handlers []TestRequestHandler) (func(), string, error
url := "http+unix://" + testSocket
return cleanupSocket, url, nil
return url, cleanupSocket
}
func StartHttpServer(handlers []TestRequestHandler) (func(), string, error) {
func StartHttpServer(t *testing.T, handlers []TestRequestHandler) (string, func()) {
server := httptest.NewServer(buildHandler(handlers))
return server.Close, server.URL, nil
return server.URL, server.Close
}
func StartHttpsServer(handlers []TestRequestHandler) (func(), string, error) {
func StartHttpsServer(t *testing.T, handlers []TestRequestHandler) (string, func()) {
crt := path.Join(testhelper.TestRoot, "certs/valid/server.crt")
key := path.Join(testhelper.TestRoot, "certs/valid/server.key")
server := httptest.NewUnstartedServer(buildHandler(handlers))
cer, err := tls.LoadX509KeyPair(crt, key)
if err != nil {
return nil, "", err
}
require.NoError(t, err)
server.TLS = &tls.Config{Certificates: []tls.Certificate{cer}}
server.StartTLS()
return server.Close, server.URL, nil
return server.URL, server.Close
}
func cleanupSocket() {
Loading
Loading
Loading
Loading
@@ -149,8 +149,7 @@ func TestErrorResponses(t *testing.T) {
func setup(t *testing.T) (*Client, func()) {
initialize(t)
cleanup, url, err := testserver.StartSocketHttpServer(requests)
require.NoError(t, err)
url, cleanup := testserver.StartSocketHttpServer(t, requests)
client, err := NewClient(&config.Config{GitlabUrl: url})
require.NoError(t, err)
Loading
Loading
Loading
Loading
@@ -14,11 +14,29 @@ import (
"google.golang.org/grpc"
)
// GitalyHandlerFunc implementations are responsible for deserializing
// GitalyHandlerFuncWithJSON implementations are responsible for deserializing
// the request JSON into a GRPC request message, making an appropriate Gitaly
// call with the request, using the provided client, and returning the exit code
// or error from the Gitaly call.
type GitalyHandlerFunc func(ctx context.Context, client *grpc.ClientConn, requestJSON string) (int32, error)
type GitalyHandlerFuncWithJSON func(ctx context.Context, client *grpc.ClientConn, requestJSON string) (int32, error)
// 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
}
// RunGitalyCommand provides a bootstrap for Gitaly commands executed
// through GitLab-Shell. It ensures that logging, tracing and other
Loading
Loading
@@ -26,7 +44,7 @@ type GitalyHandlerFunc func(ctx context.Context, client *grpc.ClientConn, reques
// RunGitalyCommand will handle errors internally and call
// `os.Exit()` on completion. This method will never return to
// the caller.
func RunGitalyCommand(handler GitalyHandlerFunc) {
func RunGitalyCommand(handler GitalyHandlerFuncWithJSON) {
exitCode, err := internalRunGitalyCommand(os.Args, handler)
if err != nil {
Loading
Loading
@@ -36,10 +54,25 @@ func RunGitalyCommand(handler GitalyHandlerFunc) {
os.Exit(exitCode)
}
// internalRunGitalyCommand is like RunGitalyCommand, except that since it doesn't
// call os.Exit, we can rely on its deferred handlers executing correctly
func internalRunGitalyCommand(args []string, handler GitalyHandlerFunc) (int, error) {
// 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
}
_, err = handler(gitalyConn.ctx, gitalyConn.conn)
gitalyConn.close()
return err
}
// internalRunGitalyCommand runs Gitaly's command by particular Gitaly address and token
func internalRunGitalyCommand(args []string, handler GitalyHandlerFuncWithJSON) (int, error) {
if len(args) != 3 {
return 1, fmt.Errorf("expected 2 arguments, got %v", args)
}
Loading
Loading
@@ -53,13 +86,44 @@ func internalRunGitalyCommand(args []string, handler GitalyHandlerFunc) (int, er
return 1, err
}
gc := &GitalyCommand{
Config: cfg,
ServiceName: args[0],
Address: args[1],
Token: os.Getenv("GITALY_TOKEN"),
}
requestJSON := string(args[2])
gitalyConn, err := getConn(gc)
if err != nil {
return 1, err
}
exitCode, err := handler(gitalyConn.ctx, gitalyConn.conn, requestJSON)
gitalyConn.close()
return int(exitCode), 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)))
}
// Use a working directory that won't get removed or unmounted.
if err := os.Chdir("/"); err != nil {
return 1, err
return nil, err
}
// Configure distributed tracing
serviceName := fmt.Sprintf("gitlab-shell-%v", args[0])
serviceName := fmt.Sprintf("gitlab-shell-%v", gc.ServiceName)
closer := tracing.Initialize(
tracing.WithServiceName(serviceName),
Loading
Loading
@@ -71,34 +135,21 @@ func internalRunGitalyCommand(args []string, handler GitalyHandlerFunc) (int, er
// Processes are spawned as children of the SSH daemon, which tightly
// controls environment variables; doing this means we don't have to
// enable PermitUserEnvironment
tracing.WithConnectionString(cfg.GitlabTracing),
tracing.WithConnectionString(gc.Config.GitlabTracing),
)
defer closer.Close()
ctx, finished := tracing.ExtractFromEnv(context.Background())
defer finished()
gitalyAddress := args[1]
if gitalyAddress == "" {
return 1, fmt.Errorf("no gitaly_address given")
}
conn, err := client.Dial(gitalyAddress, dialOpts())
conn, err := client.Dial(gc.Address, connOpts)
if err != nil {
return 1, err
return nil, err
}
defer conn.Close()
requestJSON := string(args[2])
exitCode, err := handler(ctx, conn, requestJSON)
return int(exitCode), err
}
func dialOpts() []grpc.DialOption {
connOpts := client.DefaultDialOpts
if token := os.Getenv("GITALY_TOKEN"); token != "" {
connOpts = append(client.DefaultDialOpts, grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2(token)))
finish := func() {
finished()
closer.Close()
conn.Close()
}
return connOpts
return &GitalyConn{ctx: ctx, conn: conn, close: finish}, nil
}
package requesthandlers
import (
"encoding/json"
"net/http"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver"
)
func BuildAllowedWithGitalyHandlers(t *testing.T, gitalyAddress string) []testserver.TestRequestHandler {
requests := []testserver.TestRequestHandler{
{
Path: "/api/v4/internal/allowed",
Handler: func(w http.ResponseWriter, r *http.Request) {
body := map[string]interface{}{
"status": true,
"gl_id": "1",
"gitaly": map[string]interface{}{
"repository": map[string]interface{}{
"storage_name": "storage_name",
"relative_path": "relative_path",
"git_object_directory": "path/to/git_object_directory",
"git_alternate_object_directories": []string{"path/to/git_alternate_object_directory"},
"gl_repository": "group/repo",
"gl_project_path": "group/project-path",
},
"address": gitalyAddress,
"token": "token",
},
}
require.NoError(t, json.NewEncoder(w).Encode(body))
},
},
}
return requests
}
{
"status": true,
"gl_repository": "project-26",
"gl_project_path": "group/private",
"gl_id": "user-1",
"gl_username": "root",
"git_config_options": ["option"],
"gitaly": {
"repository": {
"storage_name": "default",
"relative_path": "@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git",
"git_object_directory": "path/to/git_object_directory",
"git_alternate_object_directories": ["path/to/git_alternate_object_directory"],
"gl_repository": "project-26",
"gl_project_path": "group/private"
},
"address": "unix:gitaly.socket",
"token": "token"
},
"git_protocol": "protocol",
"gl_console_messages": ["console", "message"]
}
{
"status": true,
"gl_repository": "project-26",
"gl_project_path": "group/private",
"gl_id": "user-1",
"gl_username": "root",
"git_config_options": ["option"],
"gitaly": {
"repository": {
"storage_name": "default",
"relative_path": "@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git",
"git_object_directory": "path/to/git_object_directory",
"git_alternate_object_directories": ["path/to/git_alternate_object_directory"],
"gl_repository": "project-26",
"gl_project_path": "group/private"
},
"address": "unix:gitaly.socket",
"token": "token"
},
"payload" : {
"action": "geo_proxy_to_primary",
"data": {
"api_endpoints": ["geo/proxy_git_push_ssh/info_refs", "geo/proxy_git_push_ssh/push"],
"gl_username": "custom",
"primary_repo": "https://repo/path",
"info_message": "message"
}
},
"git_protocol": "protocol",
"gl_console_messages": ["console", "message"]
}
The MIT License (MIT)
Copyright (c) 2017 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# go-shellwords
[![Coverage Status](https://coveralls.io/repos/mattn/go-shellwords/badge.png?branch=master)](https://coveralls.io/r/mattn/go-shellwords?branch=master)
[![Build Status](https://travis-ci.org/mattn/go-shellwords.svg?branch=master)](https://travis-ci.org/mattn/go-shellwords)
Parse line as shell words.
## Usage
```go
args, err := shellwords.Parse("./foo --bar=baz")
// args should be ["./foo", "--bar=baz"]
```
```go
os.Setenv("FOO", "bar")
p := shellwords.NewParser()
p.ParseEnv = true
args, err := p.Parse("./foo $FOO")
// args should be ["./foo", "bar"]
```
```go
p := shellwords.NewParser()
p.ParseBacktick = true
args, err := p.Parse("./foo `echo $SHELL`")
// args should be ["./foo", "/bin/bash"]
```
```go
shellwords.ParseBacktick = true
p := shellwords.NewParser()
args, err := p.Parse("./foo `echo $SHELL`")
// args should be ["./foo", "/bin/bash"]
```
# Thanks
This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine).
# License
under the MIT License: http://mattn.mit-license.org/2017
# Author
Yasuhiro Matsumoto (a.k.a mattn)
module github.com/mattn/go-shellwords
package shellwords
import (
"errors"
"os"
"regexp"
"strings"
)
var (
ParseEnv bool = false
ParseBacktick bool = false
)
var envRe = regexp.MustCompile(`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`)
func isSpace(r rune) bool {
switch r {
case ' ', '\t', '\r', '\n':
return true
}
return false
}
func replaceEnv(getenv func(string) string, s string) string {
if getenv == nil {
getenv = os.Getenv
}
return envRe.ReplaceAllStringFunc(s, func(s string) string {
s = s[1:]
if s[0] == '{' {
s = s[1 : len(s)-1]
}
return getenv(s)
})
}
type Parser struct {
ParseEnv bool
ParseBacktick bool
Position int
// If ParseEnv is true, use this for getenv.
// If nil, use os.Getenv.
Getenv func(string) string
}
func NewParser() *Parser {
return &Parser{
ParseEnv: ParseEnv,
ParseBacktick: ParseBacktick,
Position: 0,
}
}
func (p *Parser) Parse(line string) ([]string, error) {
args := []string{}
buf := ""
var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
backtick := ""
pos := -1
got := false
loop:
for i, r := range line {
if escaped {
buf += string(r)
escaped = false
continue
}
if r == '\\' {
if singleQuoted {
buf += string(r)
} else {
escaped = true
}
continue
}
if isSpace(r) {
if singleQuoted || doubleQuoted || backQuote || dollarQuote {
buf += string(r)
backtick += string(r)
} else if got {
if p.ParseEnv {
buf = replaceEnv(p.Getenv, buf)
}
args = append(args, buf)
buf = ""
got = false
}
continue
}
switch r {
case '`':
if !singleQuoted && !doubleQuoted && !dollarQuote {
if p.ParseBacktick {
if backQuote {
out, err := shellRun(backtick)
if err != nil {
return nil, err
}
buf = buf[:len(buf)-len(backtick)] + out
}
backtick = ""
backQuote = !backQuote
continue
}
backtick = ""
backQuote = !backQuote
}
case ')':
if !singleQuoted && !doubleQuoted && !backQuote {
if p.ParseBacktick {
if dollarQuote {
out, err := shellRun(backtick)
if err != nil {
return nil, err
}
if r == ')' {
buf = buf[:len(buf)-len(backtick)-2] + out
} else {
buf = buf[:len(buf)-len(backtick)-1] + out
}
}
backtick = ""
dollarQuote = !dollarQuote
continue
}
backtick = ""
dollarQuote = !dollarQuote
}
case '(':
if !singleQuoted && !doubleQuoted && !backQuote {
if !dollarQuote && strings.HasSuffix(buf, "$") {
dollarQuote = true
buf += "("
continue
} else {
return nil, errors.New("invalid command line string")
}
}
case '"':
if !singleQuoted && !dollarQuote {
doubleQuoted = !doubleQuoted
continue
}
case '\'':
if !doubleQuoted && !dollarQuote {
singleQuoted = !singleQuoted
continue
}
case ';', '&', '|', '<', '>':
if !(escaped || singleQuoted || doubleQuoted || backQuote) {
if r == '>' && len(buf) > 0 {
if c := buf[0]; '0' <= c && c <= '9' {
i -= 1
got = false
}
}
pos = i
break loop
}
}
got = true
buf += string(r)
if backQuote || dollarQuote {
backtick += string(r)
}
}
if got {
if p.ParseEnv {
buf = replaceEnv(p.Getenv, buf)
}
args = append(args, buf)
}
if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
return nil, errors.New("invalid command line string")
}
p.Position = pos
return args, nil
}
func Parse(line string) ([]string, error) {
return NewParser().Parse(line)
}
// +build !go1.6
package shellwords
import (
"os"
"os/exec"
"runtime"
"strings"
)
func shellRun(line string) (string, error) {
var b []byte
var err error
if runtime.GOOS == "windows" {
b, err = exec.Command(os.Getenv("COMSPEC"), "/c", line).Output()
} else {
b, err = exec.Command(os.Getenv("SHELL"), "-c", line).Output()
}
if err != nil {
return "", err
}
return strings.TrimSpace(string(b)), nil
}
// +build !windows,go1.6
package shellwords
import (
"errors"
"os"
"os/exec"
"strings"
)
func shellRun(line string) (string, error) {
shell := os.Getenv("SHELL")
b, err := exec.Command(shell, "-c", line).Output()
if err != nil {
if eerr, ok := err.(*exec.ExitError); ok {
b = eerr.Stderr
}
return "", errors.New(err.Error() + ":" + string(b))
}
return strings.TrimSpace(string(b)), nil
}
// +build windows,go1.6
package shellwords
import (
"errors"
"os"
"os/exec"
"strings"
)
func shellRun(line string) (string, error) {
shell := os.Getenv("COMSPEC")
b, err := exec.Command(shell, "/c", line).Output()
if err != nil {
if eerr, ok := err.(*exec.ExitError); ok {
b = eerr.Stderr
}
return "", errors.New(err.Error() + ":" + string(b))
}
return strings.TrimSpace(string(b)), nil
}
Loading
Loading
@@ -83,6 +83,12 @@
"path": "github.com/lightstep/lightstep-tracer-go/lightsteppb",
"revision": "c0184d44cb322c9d9abcaebb2139dc754b3c0145"
},
{
"checksumSHA1": "AZtrc8Chiynrp48VQzRDH5vBR3M=",
"path": "github.com/mattn/go-shellwords",
"revision": "2444a32a19f450fabaa0bb3e96a703f15d9a97d2",
"revisionTime": "2019-04-25T16:15:01Z"
},
{
"checksumSHA1": "a/DHmc9bdsYlZZcwp6i3xhvV7Pk=",
"path": "github.com/opentracing/opentracing-go",
Loading
Loading
@@ -639,6 +645,10 @@
"revision": "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b",
"revisionTime": "2017-04-07T17:21:22Z"
},
{
"path": "https://github.com/mattn/go-shellwords",
"revision": ""
},
{
"path": "https://github.com/otiai10/copy",
"revision": ""
Loading
Loading
require_relative 'spec_helper'
require 'open3'
require 'json'
require 'base64'
describe 'Custom bin/gitlab-shell git-receive-pack' do
include_context 'gitlab shell'
def mock_server(server)
server.mount_proc('/geo/proxy_git_push_ssh/info_refs') do |req, res|
res.content_type = 'application/json'
res.status = 200
res.body = {"result" => "#{Base64.encode64('custom')}"}.to_json
end
server.mount_proc('/geo/proxy_git_push_ssh/push') do |req, res|
res.content_type = 'application/json'
res.status = 200
output = JSON.parse(req.body)['output']
res.body = {"result" => output}.to_json
end
server.mount_proc('/api/v4/internal/allowed') do |req, res|
res.content_type = 'application/json'
key_id = req.query['key_id'] || req.query['username']
unless key_id
body = JSON.parse(req.body)
key_id = body['key_id'] || body['username'].to_s
end
case key_id
when '100', 'someone' then
res.status = 300
body = {
"gl_id" => "user-100",
"status" => true,
"payload" => {
"action" => "geo_proxy_to_primary",
"data" => {
"api_endpoints" => ["/geo/proxy_git_push_ssh/info_refs", "/geo/proxy_git_push_ssh/push"],
"gl_username" => "custom",
"primary_repo" => "https://repo/path",
"info_message" => "info_message\nanother_message",
},
},
"gl_console_messages" => ["console", "message"]
}
res.body = body.to_json
else
res.status = 403
end
end
end
shared_examples 'dialog for performing a custom action' do
context 'when API calls perform successfully' do
def verify_successful_call!(cmd)
Open3.popen3(env, cmd) do |stdin, stdout, stderr|
expect(stderr.gets).to eq("> GitLab: console\n")
expect(stderr.gets).to eq("> GitLab: message\n")
expect(stderr.gets).to eq("> GitLab: info_message\n")
expect(stderr.gets).to eq("> GitLab: another_message\n")
expect(stdout.gets(6)).to eq("custom")
stdin.puts("input")
stdin.close
expect(stdout.flush.read).to eq("input\n")
end
end
context 'when key is provided' do
let(:cmd) { "#{gitlab_shell_path} key-100" }
it 'custom action is performed' do
verify_successful_call!(cmd)
end
end
context 'when username is provided' do
let(:cmd) { "#{gitlab_shell_path} username-someone" }
it 'custom action is performed' do
verify_successful_call!(cmd)
end
end
end
context 'when API error occurs' do
let(:cmd) { "#{gitlab_shell_path} key-101" }
it 'custom action is not performed' do
Open3.popen2e(env, cmd) do |stdin, stdout|
expect(stdout.gets).to eq(inaccessible_error)
end
end
end
end
let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => 'git-receive-pack group/repo' } }
describe 'without go features' do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
)
end
it_behaves_like 'dialog for performing a custom action' do
let(:inaccessible_error) { "> GitLab: API is not accessible\n" }
end
end
describe 'with go features', :go do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
"migration" => { "enabled" => true,
"features" => ["git-receive-pack"] }
)
end
it_behaves_like 'dialog for performing a custom action' do
let(:inaccessible_error) { "Internal API error (403)\n" }
end
end
end
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