Move much of SSH code into a separate package
In the future, we'll want to call into the SSH code from multiple packages, so let's move it out of the lfshttp package into its own package to avoid package import loops. While we're at it, rename the function names to remove the "ssh" prefix, since it's implied by the fact that they're in a package called "ssh". Move the tests to their own package to prevent an import loop and expose the private functions so we can test them there.
This commit is contained in:
parent
e9ffd5dc5c
commit
42e08e18b1
1
Makefile
1
Makefile
@ -126,6 +126,7 @@ PKGS += lfs
|
||||
PKGS += lfsapi
|
||||
PKGS += lfshttp
|
||||
PKGS += locking
|
||||
PKGS += ssh
|
||||
PKGS += subprocess
|
||||
PKGS += tasklog
|
||||
PKGS += tools
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/git-lfs/git-lfs/creds"
|
||||
"github.com/git-lfs/git-lfs/lfshttp"
|
||||
"github.com/git-lfs/git-lfs/ssh"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -557,7 +558,7 @@ func TestEndpointParsing(t *testing.T) {
|
||||
"git@github.com:git-lfs/git-lfs.git",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://github.com/git-lfs/git-lfs.git",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "git@github.com",
|
||||
Path: "git-lfs/git-lfs.git",
|
||||
Port: "",
|
||||
@ -569,7 +570,7 @@ func TestEndpointParsing(t *testing.T) {
|
||||
"[git@lfshttp.github.com:443]:git-lfs/git-lfs.git",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://lfshttp.github.com/git-lfs/git-lfs.git",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "git@lfshttp.github.com",
|
||||
Path: "git-lfs/git-lfs.git",
|
||||
Port: "443",
|
||||
@ -581,7 +582,7 @@ func TestEndpointParsing(t *testing.T) {
|
||||
"github.com:git-lfs/git-lfs.git",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://github.com/git-lfs/git-lfs.git",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "github.com",
|
||||
Path: "git-lfs/git-lfs.git",
|
||||
Port: "",
|
||||
@ -593,7 +594,7 @@ func TestEndpointParsing(t *testing.T) {
|
||||
"github:git-lfs/git-lfs.git",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://github/git-lfs/git-lfs.git",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "github",
|
||||
Path: "git-lfs/git-lfs.git",
|
||||
Port: "",
|
||||
@ -605,7 +606,7 @@ func TestEndpointParsing(t *testing.T) {
|
||||
"gh:git-lfs/git-lfs.git",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://github.com/git-lfs/git-lfs.git",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "",
|
||||
Path: "",
|
||||
Port: "",
|
||||
@ -617,7 +618,7 @@ func TestEndpointParsing(t *testing.T) {
|
||||
"remote::git-lfs/git-lfs.git",
|
||||
lfshttp.Endpoint{
|
||||
Url: "remote::git-lfs/git-lfs.git",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "",
|
||||
Path: "",
|
||||
Port: "",
|
||||
@ -656,7 +657,7 @@ func TestInsteadOf(t *testing.T) {
|
||||
"download",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://example.com/git-lfs/git-lfs.git/info/lfs",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "",
|
||||
Path: "",
|
||||
Port: "",
|
||||
@ -669,7 +670,7 @@ func TestInsteadOf(t *testing.T) {
|
||||
"upload",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://example.com/git-lfs/git-lfs.git/info/lfs",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "example.com",
|
||||
Path: "git-lfs/git-lfs.git",
|
||||
Port: "",
|
||||
@ -682,7 +683,7 @@ func TestInsteadOf(t *testing.T) {
|
||||
"download",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://example.com/git-lfs/git-lfs.git/info/lfs",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "example.com",
|
||||
Path: "git-lfs/git-lfs.git",
|
||||
Port: "",
|
||||
@ -695,7 +696,7 @@ func TestInsteadOf(t *testing.T) {
|
||||
"upload",
|
||||
lfshttp.Endpoint{
|
||||
Url: "https://example.com/git-lfs/git-lfs.git/info/lfs",
|
||||
SSHMetadata: lfshttp.SSHMetadata{
|
||||
SSHMetadata: ssh.SSHMetadata{
|
||||
UserAndHost: "example.com",
|
||||
Path: "git-lfs/git-lfs.git",
|
||||
Port: "",
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/ssh"
|
||||
)
|
||||
|
||||
const UrlUnknown = "<unknown>"
|
||||
@ -14,7 +15,7 @@ const UrlUnknown = "<unknown>"
|
||||
// An Endpoint describes how to access a Git LFS server.
|
||||
type Endpoint struct {
|
||||
Url string
|
||||
SSHMetadata SSHMetadata
|
||||
SSHMetadata ssh.SSHMetadata
|
||||
Operation string
|
||||
}
|
||||
|
||||
|
116
lfshttp/ssh.go
116
lfshttp/ssh.go
@ -3,13 +3,11 @@ package lfshttp
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/ssh"
|
||||
"github.com/git-lfs/git-lfs/subprocess"
|
||||
"github.com/git-lfs/git-lfs/tools"
|
||||
"github.com/rubyist/tracerx"
|
||||
@ -81,7 +79,7 @@ func (c *sshAuthClient) Resolve(e Endpoint, method string) (sshAuthResponse, err
|
||||
return res, nil
|
||||
}
|
||||
|
||||
exe, args := sshGetLFSExeAndArgs(c.os, c.git, &e.SSHMetadata, endpointOperation(e, method), method)
|
||||
exe, args := ssh.GetLFSExeAndArgs(c.os, c.git, &e.SSHMetadata, endpointOperation(e, method), method)
|
||||
cmd := subprocess.ExecCommand(exe, args...)
|
||||
|
||||
// Save stdout and stderr in separate buffers
|
||||
@ -114,113 +112,3 @@ func (c *sshAuthClient) Resolve(e Endpoint, method string) (sshAuthResponse, err
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
type SSHMetadata struct {
|
||||
UserAndHost string
|
||||
Port string
|
||||
Path string
|
||||
}
|
||||
|
||||
func sshFormatArgs(cmd string, args []string, needShell bool) (string, []string) {
|
||||
if !needShell {
|
||||
return cmd, args
|
||||
}
|
||||
|
||||
return subprocess.FormatForShellQuotedArgs(cmd, args)
|
||||
}
|
||||
|
||||
func sshGetLFSExeAndArgs(osEnv config.Environment, gitEnv config.Environment, meta *SSHMetadata, operation, method string) (string, []string) {
|
||||
exe, args, needShell := sshGetExeAndArgs(osEnv, gitEnv, meta)
|
||||
args = append(args, fmt.Sprintf("git-lfs-authenticate %s %s", meta.Path, operation))
|
||||
exe, args = sshFormatArgs(exe, args, needShell)
|
||||
tracerx.Printf("run_command: %s %s", exe, strings.Join(args, " "))
|
||||
return exe, args
|
||||
}
|
||||
|
||||
// Parse command, and if it looks like a valid command, return the ssh binary
|
||||
// name, the command to run, and whether we need a shell. If not, return
|
||||
// existing as the ssh binary name.
|
||||
func sshParseShellCommand(command string, existing string) (ssh string, cmd string, needShell bool) {
|
||||
ssh = existing
|
||||
if cmdArgs := tools.QuotedFields(command); len(cmdArgs) > 0 {
|
||||
needShell = true
|
||||
ssh = cmdArgs[0]
|
||||
cmd = command
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Return the executable name for ssh on this machine and the base args
|
||||
// Base args includes port settings, user/host, everything pre the command to execute
|
||||
func sshGetExeAndArgs(osEnv config.Environment, gitEnv config.Environment, meta *SSHMetadata) (exe string, baseargs []string, needShell bool) {
|
||||
var cmd string
|
||||
|
||||
isPlink := false
|
||||
isTortoise := false
|
||||
|
||||
ssh, _ := osEnv.Get("GIT_SSH")
|
||||
sshCmd, _ := osEnv.Get("GIT_SSH_COMMAND")
|
||||
ssh, cmd, needShell = sshParseShellCommand(sshCmd, ssh)
|
||||
|
||||
if ssh == "" {
|
||||
sshCmd, _ := gitEnv.Get("core.sshcommand")
|
||||
ssh, cmd, needShell = sshParseShellCommand(sshCmd, defaultSSHCmd)
|
||||
}
|
||||
|
||||
if cmd == "" {
|
||||
cmd = ssh
|
||||
}
|
||||
|
||||
basessh := filepath.Base(ssh)
|
||||
|
||||
if basessh != defaultSSHCmd {
|
||||
// Strip extension for easier comparison
|
||||
if ext := filepath.Ext(basessh); len(ext) > 0 {
|
||||
basessh = basessh[:len(basessh)-len(ext)]
|
||||
}
|
||||
isPlink = strings.EqualFold(basessh, "plink")
|
||||
isTortoise = strings.EqualFold(basessh, "tortoiseplink")
|
||||
}
|
||||
|
||||
args := make([]string, 0, 7)
|
||||
|
||||
if isTortoise {
|
||||
// TortoisePlink requires the -batch argument to behave like ssh/plink
|
||||
args = append(args, "-batch")
|
||||
}
|
||||
|
||||
if len(meta.Port) > 0 {
|
||||
if isPlink || isTortoise {
|
||||
args = append(args, "-P")
|
||||
} else {
|
||||
args = append(args, "-p")
|
||||
}
|
||||
args = append(args, meta.Port)
|
||||
}
|
||||
|
||||
if sep, ok := sshSeparators[basessh]; ok {
|
||||
// inserts a separator between cli -options and host/cmd commands
|
||||
// example: $ ssh -p 12345 -- user@host.com git-lfs-authenticate ...
|
||||
args = append(args, sep, meta.UserAndHost)
|
||||
} else {
|
||||
// no prefix supported, strip leading - off host to prevent cmd like:
|
||||
// $ git config lfs.url ssh://-proxycmd=whatever
|
||||
// $ plink -P 12345 -proxycmd=foo git-lfs-authenticate ...
|
||||
//
|
||||
// Instead, it'll attempt this, and eventually return an error
|
||||
// $ plink -P 12345 proxycmd=foo git-lfs-authenticate ...
|
||||
args = append(args, sshOptPrefixRE.ReplaceAllString(meta.UserAndHost, ""))
|
||||
}
|
||||
|
||||
return cmd, args, needShell
|
||||
}
|
||||
|
||||
const defaultSSHCmd = "ssh"
|
||||
|
||||
var (
|
||||
sshOptPrefixRE = regexp.MustCompile(`\A\-+`)
|
||||
sshSeparators = map[string]string{
|
||||
"ssh": "--",
|
||||
"lfs-ssh-echo": "--", // used in lfs integration tests only
|
||||
}
|
||||
)
|
||||
|
@ -1,14 +1,12 @@
|
||||
package lfshttp
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
sshp "github.com/git-lfs/git-lfs/ssh"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSSHCacheResolveFromCache(t *testing.T) {
|
||||
@ -21,7 +19,7 @@ func TestSSHCacheResolveFromCache(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -44,7 +42,7 @@ func TestSSHCacheResolveFromCacheWithFutureExpiresAt(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -67,7 +65,7 @@ func TestSSHCacheResolveFromCacheWithFutureExpiresIn(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -90,7 +88,7 @@ func TestSSHCacheResolveFromCacheWithPastExpiresAt(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -113,7 +111,7 @@ func TestSSHCacheResolveFromCacheWithPastExpiresIn(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -137,7 +135,7 @@ func TestSSHCacheResolveFromCacheWithAmbiguousExpirationInfo(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -158,7 +156,7 @@ func TestSSHCacheResolveWithoutError(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -190,7 +188,7 @@ func TestSSHCacheResolveWithError(t *testing.T) {
|
||||
ssh.responses["userandhost"] = sshAuthResponse{Message: "resolve error", Href: "real"}
|
||||
|
||||
e := Endpoint{
|
||||
SSHMetadata: SSHMetadata{
|
||||
SSHMetadata: sshp.SSHMetadata{
|
||||
UserAndHost: "userandhost",
|
||||
Port: "1",
|
||||
Path: "path",
|
||||
@ -227,433 +225,3 @@ func (r *fakeResolver) Resolve(e Endpoint, method string) (sshAuthResponse, erro
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func TestSSHGetLFSExeAndArgs(t *testing.T) {
|
||||
cli, err := NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
endpoint.SSHMetadata.Path = "user/repo"
|
||||
|
||||
exe, args := sshGetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata, endpoint.Operation, "GET")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{
|
||||
"--",
|
||||
"user@foo.com",
|
||||
"git-lfs-authenticate user/repo download",
|
||||
}, args)
|
||||
|
||||
exe, args = sshGetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata, endpoint.Operation, "HEAD")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{
|
||||
"--",
|
||||
"user@foo.com",
|
||||
"git-lfs-authenticate user/repo download",
|
||||
}, args)
|
||||
|
||||
// this is going by endpoint.Operation, implicitly set by Endpoint() on L15.
|
||||
exe, args = sshGetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata, endpoint.Operation, "POST")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{
|
||||
"--",
|
||||
"user@foo.com",
|
||||
"git-lfs-authenticate user/repo download",
|
||||
}, args)
|
||||
|
||||
endpoint.Operation = "upload"
|
||||
exe, args = sshGetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata, endpoint.Operation, "POST")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{
|
||||
"--",
|
||||
"user@foo.com",
|
||||
"git-lfs-authenticate user/repo upload",
|
||||
}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSsh(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": "",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCustomPort(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": "",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
endpoint.SSHMetadata.Port = "8888"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"-p", "8888", "--", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlink(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink.exe")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlinkCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
endpoint.SSHMetadata.Port = "8888"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"-P", "8888", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlink(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink.exe")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"-batch", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlinkCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
endpoint.SSHMetadata.Port = "8888"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"-batch", "-P", "8888", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandPrecedence(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd",
|
||||
"GIT_SSH": "bad",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandArgs(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd --args 1",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd --args 1 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandArgsWithMixedQuotes(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd foo 'bar \"baz\"'",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd foo 'bar \"baz\"' user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandCustomPort(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
endpoint.SSHMetadata.Port = "8888"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd -p 8888 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsCoreSshCommand(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd --args 2",
|
||||
}, map[string]string{
|
||||
"core.sshcommand": "sshcmd --args 1",
|
||||
}))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd --args 2 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsCoreSshCommandArgsWithMixedQuotes(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, nil, map[string]string{
|
||||
"core.sshcommand": "sshcmd foo 'bar \"baz\"'",
|
||||
}))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd foo 'bar \"baz\"' user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsConfigVersusEnv(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, nil, map[string]string{
|
||||
"core.sshcommand": "sshcmd --args 1",
|
||||
}))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd --args 1 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetLFSExeAndArgsWithCustomSSH(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH": "not-ssh",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://git@host.com:12345/repo")
|
||||
require.Nil(t, err)
|
||||
|
||||
e := EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "12345", e.SSHMetadata.Port)
|
||||
assert.Equal(t, "git@host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo", e.SSHMetadata.Path)
|
||||
|
||||
exe, args := sshGetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata, "download", "GET")
|
||||
assert.Equal(t, "not-ssh", exe)
|
||||
assert.Equal(t, []string{"-p", "12345", "git@host.com", "git-lfs-authenticate repo download"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetLFSExeAndArgsInvalidOptionsAsHost(t *testing.T) {
|
||||
cli, err := NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://-oProxyCommand=gnome-calculator/repo")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", u.Host)
|
||||
|
||||
e := EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo", e.SSHMetadata.Path)
|
||||
|
||||
exe, args := sshGetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata, "download", "GET")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "-oProxyCommand=gnome-calculator", "git-lfs-authenticate repo download"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetLFSExeAndArgsInvalidOptionsAsHostWithCustomSSH(t *testing.T) {
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH": "not-ssh",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://--oProxyCommand=gnome-calculator/repo")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "--oProxyCommand=gnome-calculator", u.Host)
|
||||
|
||||
e := EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "--oProxyCommand=gnome-calculator", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo", e.SSHMetadata.Path)
|
||||
|
||||
exe, args := sshGetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata, "download", "GET")
|
||||
assert.Equal(t, "not-ssh", exe)
|
||||
assert.Equal(t, []string{"oProxyCommand=gnome-calculator", "git-lfs-authenticate repo download"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsInvalidOptionsAsHost(t *testing.T) {
|
||||
cli, err := NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://-oProxyCommand=gnome-calculator")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", u.Host)
|
||||
|
||||
e := EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "", e.SSHMetadata.Path)
|
||||
|
||||
exe, args, needShell := sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata)
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "-oProxyCommand=gnome-calculator"}, args)
|
||||
assert.Equal(t, false, needShell)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsInvalidOptionsAsPath(t *testing.T) {
|
||||
cli, err := NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://git@git-host.com/-oProxyCommand=gnome-calculator")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "git-host.com", u.Host)
|
||||
|
||||
e := EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "git@git-host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.Path)
|
||||
|
||||
exe, args, needShell := sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata)
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "git@git-host.com"}, args)
|
||||
assert.Equal(t, false, needShell)
|
||||
}
|
||||
|
||||
func TestParseBareSSHUrl(t *testing.T) {
|
||||
e := EndpointFromBareSshUrl("git@git-host.com:repo.git")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "git@git-host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo.git", e.SSHMetadata.Path)
|
||||
|
||||
e = EndpointFromBareSshUrl("git@git-host.com/should-be-a-colon.git")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "", e.SSHMetadata.Path)
|
||||
|
||||
e = EndpointFromBareSshUrl("-oProxyCommand=gnome-calculator")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "", e.SSHMetadata.Path)
|
||||
|
||||
e = EndpointFromBareSshUrl("git@git-host.com:-oProxyCommand=gnome-calculator")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "git@git-host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.Path)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlinkCommand(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink.exe")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlinkCommandCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
endpoint.SSHMetadata.Port = "8888"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " -P 8888 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlinkCommand(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink.exe")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " -batch user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlinkCommandCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink")
|
||||
|
||||
cli, err := NewClient(NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
endpoint := Endpoint{Operation: "download"}
|
||||
endpoint.SSHMetadata.UserAndHost = "user@foo.com"
|
||||
endpoint.SSHMetadata.Port = "8888"
|
||||
|
||||
exe, args := sshFormatArgs(sshGetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &endpoint.SSHMetadata))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " -batch -P 8888 user@foo.com"}, args)
|
||||
}
|
||||
|
123
ssh/ssh.go
Normal file
123
ssh/ssh.go
Normal file
@ -0,0 +1,123 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/subprocess"
|
||||
"github.com/git-lfs/git-lfs/tools"
|
||||
"github.com/rubyist/tracerx"
|
||||
)
|
||||
|
||||
type SSHMetadata struct {
|
||||
UserAndHost string
|
||||
Port string
|
||||
Path string
|
||||
}
|
||||
|
||||
func FormatArgs(cmd string, args []string, needShell bool) (string, []string) {
|
||||
if !needShell {
|
||||
return cmd, args
|
||||
}
|
||||
|
||||
return subprocess.FormatForShellQuotedArgs(cmd, args)
|
||||
}
|
||||
|
||||
func GetLFSExeAndArgs(osEnv config.Environment, gitEnv config.Environment, meta *SSHMetadata, operation, method string) (string, []string) {
|
||||
exe, args, needShell := GetExeAndArgs(osEnv, gitEnv, meta)
|
||||
args = append(args, fmt.Sprintf("git-lfs-authenticate %s %s", meta.Path, operation))
|
||||
exe, args = FormatArgs(exe, args, needShell)
|
||||
tracerx.Printf("run_command: %s %s", exe, strings.Join(args, " "))
|
||||
return exe, args
|
||||
}
|
||||
|
||||
// Parse command, and if it looks like a valid command, return the ssh binary
|
||||
// name, the command to run, and whether we need a shell. If not, return
|
||||
// existing as the ssh binary name.
|
||||
func parseShellCommand(command string, existing string) (ssh string, cmd string, needShell bool) {
|
||||
ssh = existing
|
||||
if cmdArgs := tools.QuotedFields(command); len(cmdArgs) > 0 {
|
||||
needShell = true
|
||||
ssh = cmdArgs[0]
|
||||
cmd = command
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Return the executable name for ssh on this machine and the base args
|
||||
// Base args includes port settings, user/host, everything pre the command to execute
|
||||
func GetExeAndArgs(osEnv config.Environment, gitEnv config.Environment, meta *SSHMetadata) (exe string, baseargs []string, needShell bool) {
|
||||
var cmd string
|
||||
|
||||
isPlink := false
|
||||
isTortoise := false
|
||||
|
||||
ssh, _ := osEnv.Get("GIT_SSH")
|
||||
sshCmd, _ := osEnv.Get("GIT_SSH_COMMAND")
|
||||
ssh, cmd, needShell = parseShellCommand(sshCmd, ssh)
|
||||
|
||||
if ssh == "" {
|
||||
sshCmd, _ := gitEnv.Get("core.sshcommand")
|
||||
ssh, cmd, needShell = parseShellCommand(sshCmd, defaultSSHCmd)
|
||||
}
|
||||
|
||||
if cmd == "" {
|
||||
cmd = ssh
|
||||
}
|
||||
|
||||
basessh := filepath.Base(ssh)
|
||||
|
||||
if basessh != defaultSSHCmd {
|
||||
// Strip extension for easier comparison
|
||||
if ext := filepath.Ext(basessh); len(ext) > 0 {
|
||||
basessh = basessh[:len(basessh)-len(ext)]
|
||||
}
|
||||
isPlink = strings.EqualFold(basessh, "plink")
|
||||
isTortoise = strings.EqualFold(basessh, "tortoiseplink")
|
||||
}
|
||||
|
||||
args := make([]string, 0, 7)
|
||||
|
||||
if isTortoise {
|
||||
// TortoisePlink requires the -batch argument to behave like ssh/plink
|
||||
args = append(args, "-batch")
|
||||
}
|
||||
|
||||
if len(meta.Port) > 0 {
|
||||
if isPlink || isTortoise {
|
||||
args = append(args, "-P")
|
||||
} else {
|
||||
args = append(args, "-p")
|
||||
}
|
||||
args = append(args, meta.Port)
|
||||
}
|
||||
|
||||
if sep, ok := sshSeparators[basessh]; ok {
|
||||
// inserts a separator between cli -options and host/cmd commands
|
||||
// example: $ ssh -p 12345 -- user@host.com git-lfs-authenticate ...
|
||||
args = append(args, sep, meta.UserAndHost)
|
||||
} else {
|
||||
// no prefix supported, strip leading - off host to prevent cmd like:
|
||||
// $ git config lfs.url ssh://-proxycmd=whatever
|
||||
// $ plink -P 12345 -proxycmd=foo git-lfs-authenticate ...
|
||||
//
|
||||
// Instead, it'll attempt this, and eventually return an error
|
||||
// $ plink -P 12345 proxycmd=foo git-lfs-authenticate ...
|
||||
args = append(args, sshOptPrefixRE.ReplaceAllString(meta.UserAndHost, ""))
|
||||
}
|
||||
|
||||
return cmd, args, needShell
|
||||
}
|
||||
|
||||
const defaultSSHCmd = "ssh"
|
||||
|
||||
var (
|
||||
sshOptPrefixRE = regexp.MustCompile(`\A\-+`)
|
||||
sshSeparators = map[string]string{
|
||||
"ssh": "--",
|
||||
"lfs-ssh-echo": "--", // used in lfs integration tests only
|
||||
}
|
||||
)
|
424
ssh/ssh_test.go
Normal file
424
ssh/ssh_test.go
Normal file
@ -0,0 +1,424 @@
|
||||
package ssh_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/git-lfs/git-lfs/lfshttp"
|
||||
"github.com/git-lfs/git-lfs/ssh"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSSHGetLFSExeAndArgs(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
meta.Path = "user/repo"
|
||||
|
||||
exe, args := ssh.GetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta, "download", "GET")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{
|
||||
"--",
|
||||
"user@foo.com",
|
||||
"git-lfs-authenticate user/repo download",
|
||||
}, args)
|
||||
|
||||
exe, args = ssh.GetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta, "upload", "GET")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{
|
||||
"--",
|
||||
"user@foo.com",
|
||||
"git-lfs-authenticate user/repo upload",
|
||||
}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSsh(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": "",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCustomPort(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": "",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
meta.Port = "8888"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"-p", "8888", "--", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlink(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink.exe")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlinkCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
meta.Port = "8888"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"-P", "8888", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlink(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink.exe")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"-batch", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlinkCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "",
|
||||
"GIT_SSH": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
meta.Port = "8888"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, plink, exe)
|
||||
assert.Equal(t, []string{"-batch", "-P", "8888", "user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandPrecedence(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd",
|
||||
"GIT_SSH": "bad",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandArgs(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd --args 1",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd --args 1 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandArgsWithMixedQuotes(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd foo 'bar \"baz\"'",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd foo 'bar \"baz\"' user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsSshCommandCustomPort(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
meta.Port = "8888"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd -p 8888 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsCoreSshCommand(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": "sshcmd --args 2",
|
||||
}, map[string]string{
|
||||
"core.sshcommand": "sshcmd --args 1",
|
||||
}))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd --args 2 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsCoreSshCommandArgsWithMixedQuotes(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, nil, map[string]string{
|
||||
"core.sshcommand": "sshcmd foo 'bar \"baz\"'",
|
||||
}))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd foo 'bar \"baz\"' user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsConfigVersusEnv(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, nil, map[string]string{
|
||||
"core.sshcommand": "sshcmd --args 1",
|
||||
}))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", "sshcmd --args 1 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlinkCommand(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink.exe")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsPlinkCommandCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "plink")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
meta.Port = "8888"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " -P 8888 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlinkCommand(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink.exe")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " -batch user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsTortoisePlinkCommandCustomPort(t *testing.T) {
|
||||
plink := filepath.Join("Users", "joebloggs", "bin", "tortoiseplink")
|
||||
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH_COMMAND": plink,
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
meta := ssh.SSHMetadata{}
|
||||
meta.UserAndHost = "user@foo.com"
|
||||
meta.Port = "8888"
|
||||
|
||||
exe, args := ssh.FormatArgs(ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &meta))
|
||||
assert.Equal(t, "sh", exe)
|
||||
assert.Equal(t, []string{"-c", plink + " -batch -P 8888 user@foo.com"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetLFSExeAndArgsWithCustomSSH(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH": "not-ssh",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://git@host.com:12345/repo")
|
||||
require.Nil(t, err)
|
||||
|
||||
e := lfshttp.EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "12345", e.SSHMetadata.Port)
|
||||
assert.Equal(t, "git@host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo", e.SSHMetadata.Path)
|
||||
|
||||
exe, args := ssh.GetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata, "download", "GET")
|
||||
assert.Equal(t, "not-ssh", exe)
|
||||
assert.Equal(t, []string{"-p", "12345", "git@host.com", "git-lfs-authenticate repo download"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetLFSExeAndArgsInvalidOptionsAsHost(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://-oProxyCommand=gnome-calculator/repo")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", u.Host)
|
||||
|
||||
e := lfshttp.EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo", e.SSHMetadata.Path)
|
||||
|
||||
exe, args := ssh.GetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata, "download", "GET")
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "-oProxyCommand=gnome-calculator", "git-lfs-authenticate repo download"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetLFSExeAndArgsInvalidOptionsAsHostWithCustomSSH(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(lfshttp.NewContext(nil, map[string]string{
|
||||
"GIT_SSH": "not-ssh",
|
||||
}, nil))
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://--oProxyCommand=gnome-calculator/repo")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "--oProxyCommand=gnome-calculator", u.Host)
|
||||
|
||||
e := lfshttp.EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "--oProxyCommand=gnome-calculator", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo", e.SSHMetadata.Path)
|
||||
|
||||
exe, args := ssh.GetLFSExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata, "download", "GET")
|
||||
assert.Equal(t, "not-ssh", exe)
|
||||
assert.Equal(t, []string{"oProxyCommand=gnome-calculator", "git-lfs-authenticate repo download"}, args)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsInvalidOptionsAsHost(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://-oProxyCommand=gnome-calculator")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", u.Host)
|
||||
|
||||
e := lfshttp.EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "", e.SSHMetadata.Path)
|
||||
|
||||
exe, args, needShell := ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata)
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "-oProxyCommand=gnome-calculator"}, args)
|
||||
assert.Equal(t, false, needShell)
|
||||
}
|
||||
|
||||
func TestSSHGetExeAndArgsInvalidOptionsAsPath(t *testing.T) {
|
||||
cli, err := lfshttp.NewClient(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
u, err := url.Parse("ssh://git@git-host.com/-oProxyCommand=gnome-calculator")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "git-host.com", u.Host)
|
||||
|
||||
e := lfshttp.EndpointFromSshUrl(u)
|
||||
t.Logf("ENDPOINT: %+v", e)
|
||||
assert.Equal(t, "git@git-host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.Path)
|
||||
|
||||
exe, args, needShell := ssh.GetExeAndArgs(cli.OSEnv(), cli.GitEnv(), &e.SSHMetadata)
|
||||
assert.Equal(t, "ssh", exe)
|
||||
assert.Equal(t, []string{"--", "git@git-host.com"}, args)
|
||||
assert.Equal(t, false, needShell)
|
||||
}
|
||||
|
||||
func TestParseBareSSHUrl(t *testing.T) {
|
||||
e := lfshttp.EndpointFromBareSshUrl("git@git-host.com:repo.git")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "git@git-host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "repo.git", e.SSHMetadata.Path)
|
||||
|
||||
e = lfshttp.EndpointFromBareSshUrl("git@git-host.com/should-be-a-colon.git")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "", e.SSHMetadata.Path)
|
||||
|
||||
e = lfshttp.EndpointFromBareSshUrl("-oProxyCommand=gnome-calculator")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "", e.SSHMetadata.Path)
|
||||
|
||||
e = lfshttp.EndpointFromBareSshUrl("git@git-host.com:-oProxyCommand=gnome-calculator")
|
||||
t.Logf("endpoint: %+v", e)
|
||||
assert.Equal(t, "git@git-host.com", e.SSHMetadata.UserAndHost)
|
||||
assert.Equal(t, "-oProxyCommand=gnome-calculator", e.SSHMetadata.Path)
|
||||
}
|
Loading…
Reference in New Issue
Block a user