250 lines
6.5 KiB
Go
250 lines
6.5 KiB
Go
|
package client
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"crypto/tls"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
"gitlab.com/gitlab-org/gitaly/internal/testhelper"
|
||
|
"google.golang.org/grpc"
|
||
|
"google.golang.org/grpc/codes"
|
||
|
"google.golang.org/grpc/credentials"
|
||
|
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
||
|
"google.golang.org/grpc/status"
|
||
|
)
|
||
|
|
||
|
var proxyEnvironmentKeys = []string{"http_proxy", "https_proxy", "no_proxy"}
|
||
|
|
||
|
func doDialAndExecuteCall(addr string) error {
|
||
|
conn, err := Dial(addr, nil)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("dial: %v", err)
|
||
|
}
|
||
|
defer conn.Close()
|
||
|
|
||
|
client := healthpb.NewHealthClient(conn)
|
||
|
_, err = client.Check(context.Background(), &healthpb.HealthCheckRequest{})
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func TestDial(t *testing.T) {
|
||
|
if emitProxyWarning() {
|
||
|
t.Log("WARNING. Proxy configuration detected from environment settings. This test failure may be related to proxy configuration. Please process with caution")
|
||
|
}
|
||
|
|
||
|
stop, connectionMap, err := startListeners()
|
||
|
require.NoError(t, err, "start listeners: %v. %s", err)
|
||
|
defer stop()
|
||
|
|
||
|
unixSocketAbsPath := connectionMap["unix"]
|
||
|
|
||
|
unixSocketRelPath := "testdata/gitaly.socket"
|
||
|
require.NoError(t, os.RemoveAll(unixSocketRelPath))
|
||
|
require.NoError(t, os.Symlink(unixSocketAbsPath, unixSocketRelPath))
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
rawAddress string
|
||
|
envSSLCertFile string
|
||
|
expectFailure bool
|
||
|
}{
|
||
|
{
|
||
|
name: "tcp localhost with prefix",
|
||
|
rawAddress: "tcp://localhost:" + connectionMap["tcp"], // "tcp://localhost:1234"
|
||
|
expectFailure: false,
|
||
|
},
|
||
|
{
|
||
|
name: "tls localhost",
|
||
|
rawAddress: "tls://localhost:" + connectionMap["tls"], // "tls://localhost:1234"
|
||
|
envSSLCertFile: "./testdata/gitalycert.pem",
|
||
|
expectFailure: false,
|
||
|
},
|
||
|
{
|
||
|
name: "unix absolute",
|
||
|
rawAddress: "unix:" + unixSocketAbsPath, // "unix:/tmp/temp-socket"
|
||
|
expectFailure: false,
|
||
|
},
|
||
|
{
|
||
|
name: "unix relative",
|
||
|
rawAddress: "unix:" + unixSocketRelPath, // "unix:../../tmp/temp-socket"
|
||
|
expectFailure: false,
|
||
|
},
|
||
|
{
|
||
|
name: "unix absolute does not exist",
|
||
|
rawAddress: "unix:" + unixSocketAbsPath + ".does_not_exist", // "unix:/tmp/temp-socket.does_not_exist"
|
||
|
expectFailure: true,
|
||
|
},
|
||
|
{
|
||
|
name: "unix relative does not exist",
|
||
|
rawAddress: "unix:" + unixSocketRelPath + ".does_not_exist", // "unix:../../tmp/temp-socket.does_not_exist"
|
||
|
expectFailure: true,
|
||
|
},
|
||
|
{
|
||
|
// Gitaly does not support connections that do not have a scheme.
|
||
|
name: "tcp localhost no prefix",
|
||
|
rawAddress: "localhost:" + connectionMap["tcp"], // "localhost:1234"
|
||
|
expectFailure: true,
|
||
|
},
|
||
|
{
|
||
|
name: "invalid",
|
||
|
rawAddress: ".",
|
||
|
expectFailure: true,
|
||
|
},
|
||
|
{
|
||
|
name: "empty",
|
||
|
rawAddress: "",
|
||
|
expectFailure: true,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
if emitProxyWarning() {
|
||
|
t.Log("WARNING. Proxy configuration detected from environment settings. This test failure may be related to proxy configuration. Please process with caution")
|
||
|
}
|
||
|
|
||
|
if tt.envSSLCertFile != "" {
|
||
|
restore := modifyEnvironment("SSL_CERT_FILE", tt.envSSLCertFile)
|
||
|
defer restore()
|
||
|
}
|
||
|
|
||
|
err := doDialAndExecuteCall(tt.rawAddress)
|
||
|
if tt.expectFailure {
|
||
|
require.Error(t, err)
|
||
|
return
|
||
|
}
|
||
|
require.NoError(t, err)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// healthServer provide a basic GRPC health service endpoint for testing purposes
|
||
|
type healthServer struct {
|
||
|
}
|
||
|
|
||
|
func (*healthServer) Check(context.Context, *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) {
|
||
|
return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVING}, nil
|
||
|
}
|
||
|
|
||
|
func (*healthServer) Watch(*healthpb.HealthCheckRequest, healthpb.Health_WatchServer) error {
|
||
|
return status.Errorf(codes.Unimplemented, "Not implemented")
|
||
|
}
|
||
|
|
||
|
// startTCPListener will start a insecure TCP listener on a random unused port
|
||
|
func startTCPListener() (func(), string, error) {
|
||
|
listener, err := net.Listen("tcp", ":0")
|
||
|
if err != nil {
|
||
|
return nil, "", err
|
||
|
}
|
||
|
tcpPort := listener.Addr().(*net.TCPAddr).Port
|
||
|
address := fmt.Sprintf("%d", tcpPort)
|
||
|
|
||
|
grpcServer := grpc.NewServer()
|
||
|
healthpb.RegisterHealthServer(grpcServer, &healthServer{})
|
||
|
go grpcServer.Serve(listener)
|
||
|
|
||
|
return func() {
|
||
|
grpcServer.Stop()
|
||
|
}, address, nil
|
||
|
}
|
||
|
|
||
|
// startUnixListener will start a unix socket listener using a temporary file
|
||
|
func startUnixListener() (func(), string, error) {
|
||
|
serverSocketPath := testhelper.GetTemporaryGitalySocketFileName()
|
||
|
|
||
|
listener, err := net.Listen("unix", serverSocketPath)
|
||
|
if err != nil {
|
||
|
return nil, "", err
|
||
|
}
|
||
|
|
||
|
grpcServer := grpc.NewServer()
|
||
|
healthpb.RegisterHealthServer(grpcServer, &healthServer{})
|
||
|
go grpcServer.Serve(listener)
|
||
|
|
||
|
return func() {
|
||
|
grpcServer.Stop()
|
||
|
}, serverSocketPath, nil
|
||
|
}
|
||
|
|
||
|
// startTLSListener will start a secure TLS listener on a random unused port
|
||
|
func startTLSListener() (func(), string, error) {
|
||
|
listener, err := net.Listen("tcp", ":0")
|
||
|
if err != nil {
|
||
|
return nil, "", err
|
||
|
}
|
||
|
tcpPort := listener.Addr().(*net.TCPAddr).Port
|
||
|
address := fmt.Sprintf("%d", tcpPort)
|
||
|
|
||
|
cert, err := tls.LoadX509KeyPair("./testdata/gitalycert.pem", "./testdata/gitalykey.pem")
|
||
|
if err != nil {
|
||
|
return nil, "", err
|
||
|
}
|
||
|
|
||
|
grpcServer := grpc.NewServer(grpc.Creds(credentials.NewServerTLSFromCert(&cert)))
|
||
|
healthpb.RegisterHealthServer(grpcServer, &healthServer{})
|
||
|
go grpcServer.Serve(listener)
|
||
|
|
||
|
return func() {
|
||
|
grpcServer.Stop()
|
||
|
}, address, nil
|
||
|
}
|
||
|
|
||
|
var listeners = map[string]func() (func(), string, error){
|
||
|
"tcp": startTCPListener,
|
||
|
"unix": startUnixListener,
|
||
|
"tls": startTLSListener,
|
||
|
}
|
||
|
|
||
|
// startListeners will start all the different listeners used in this test
|
||
|
func startListeners() (func(), map[string]string, error) {
|
||
|
var closers []func()
|
||
|
connectionMap := map[string]string{}
|
||
|
for k, v := range listeners {
|
||
|
closer, address, err := v()
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
closers = append(closers, closer)
|
||
|
connectionMap[k] = address
|
||
|
}
|
||
|
|
||
|
return func() {
|
||
|
for _, v := range closers {
|
||
|
v()
|
||
|
}
|
||
|
}, connectionMap, nil
|
||
|
}
|
||
|
|
||
|
// modifyEnvironment will change an environment variable and return a func suitable
|
||
|
// for `defer` to change the value back.
|
||
|
func modifyEnvironment(key string, value string) func() {
|
||
|
oldValue, hasOldValue := os.LookupEnv(key)
|
||
|
os.Setenv(key, value)
|
||
|
return func() {
|
||
|
if hasOldValue {
|
||
|
os.Setenv(key, oldValue)
|
||
|
} else {
|
||
|
os.Unsetenv(key)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func emitProxyWarning() bool {
|
||
|
for _, key := range proxyEnvironmentKeys {
|
||
|
value := os.Getenv(key)
|
||
|
if value != "" {
|
||
|
return true
|
||
|
}
|
||
|
value = os.Getenv(strings.ToUpper(key))
|
||
|
if value != "" {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|