From 319bac952a9c8a141942d8b22b4c83d06c41a0d4 Mon Sep 17 00:00:00 2001 From: Máximo Cuadros Date: Mon, 20 Nov 2017 18:19:50 +0100 Subject: transport: ssh, mocked SSH server, fixes #332 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Máximo Cuadros --- .travis.yml | 9 --- plumbing/transport/ssh/upload_pack_test.go | 124 +++++++++++++++++++++++++---- 2 files changed, 108 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index ee4a591..ee975e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,15 +23,6 @@ before_install: - git config --global user.email "travis@example.com" - git config --global user.name "Travis CI" - # we only decrypt the SSH key when we aren't in a pull request - - > - if [ "$TRAVIS_PULL_REQUEST" = "false" ] ; then \ - bash .travis/install_key.sh; \ - export SSH_TEST_PRIVATE_KEY=$HOME/.travis/deploy.pem; \ - else \ - export SSH_AUTH_SOCK=""; \ - fi - install: - go get -v -t ./... diff --git a/plumbing/transport/ssh/upload_pack_test.go b/plumbing/transport/ssh/upload_pack_test.go index cb9baa5..04f7b1c 100644 --- a/plumbing/transport/ssh/upload_pack_test.go +++ b/plumbing/transport/ssh/upload_pack_test.go @@ -1,47 +1,139 @@ package ssh import ( + "fmt" + "io" + "io/ioutil" + "log" + "net" "os" + "os/exec" + "path/filepath" + "strings" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/test" + "github.com/gliderlabs/ssh" + "github.com/src-d/go-git-fixtures" + stdssh "golang.org/x/crypto/ssh" . "gopkg.in/check.v1" ) type UploadPackSuite struct { test.UploadPackSuite + fixtures.Suite + + port int + base string } var _ = Suite(&UploadPackSuite{}) func (s *UploadPackSuite) SetUpSuite(c *C) { - s.setAuthBuilder(c) - s.UploadPackSuite.Client = DefaultClient + s.Suite.SetUpSuite(c) + + l, err := net.Listen("tcp", "localhost:0") + c.Assert(err, IsNil) - ep, err := transport.NewEndpoint("git@github.com:git-fixtures/basic.git") + s.port = l.Addr().(*net.TCPAddr).Port + s.base, err = ioutil.TempDir(os.TempDir(), fmt.Sprintf("go-git-ssh-%d", s.port)) c.Assert(err, IsNil) - s.UploadPackSuite.Endpoint = ep - ep, err = transport.NewEndpoint("git@github.com:git-fixtures/empty.git") + DefaultAuthBuilder = func(user string) (AuthMethod, error) { + return &Password{User: user}, nil + } + + s.UploadPackSuite.Client = NewClient(&stdssh.ClientConfig{ + HostKeyCallback: stdssh.InsecureIgnoreHostKey(), + }) + + s.UploadPackSuite.Endpoint = s.prepareRepository(c, fixtures.Basic().One(), "basic.git") + s.UploadPackSuite.EmptyEndpoint = s.prepareRepository(c, fixtures.ByTag("empty").One(), "empty.git") + s.UploadPackSuite.NonExistentEndpoint = s.newEndpoint(c, "non-existent.git") + + server := &ssh.Server{Handler: handlerSSH} + go func() { + log.Fatal(server.Serve(l)) + }() +} + +func (s *UploadPackSuite) prepareRepository(c *C, f *fixtures.Fixture, name string) transport.Endpoint { + fs := f.DotGit() + + err := fixtures.EnsureIsBare(fs) c.Assert(err, IsNil) - s.UploadPackSuite.EmptyEndpoint = ep - ep, err = transport.NewEndpoint("git@github.com:git-fixtures/non-existent.git") + path := filepath.Join(s.base, name) + err = os.Rename(fs.Root(), path) c.Assert(err, IsNil) - s.UploadPackSuite.NonExistentEndpoint = ep + + return s.newEndpoint(c, name) +} + +func (s *UploadPackSuite) newEndpoint(c *C, name string) transport.Endpoint { + ep, err := transport.NewEndpoint(fmt.Sprintf( + "ssh://git@localhost:%d/%s/%s", s.port, filepath.ToSlash(s.base), name, + )) + + c.Assert(err, IsNil) + return ep } -func (s *UploadPackSuite) setAuthBuilder(c *C) { - privateKey := os.Getenv("SSH_TEST_PRIVATE_KEY") - if privateKey != "" { - DefaultAuthBuilder = func(user string) (AuthMethod, error) { - return NewPublicKeysFromFile(user, privateKey, "") - } +func handlerSSH(s ssh.Session) { + cmd, stdin, stderr, stdout, err := buildCommand(s.Command()) + if err != nil { + fmt.Println(err) + return } - if privateKey == "" && os.Getenv("SSH_AUTH_SOCK") == "" { - c.Skip("SSH_AUTH_SOCK or SSH_TEST_PRIVATE_KEY are required") + if err := cmd.Start(); err != nil { + fmt.Println(err) return } + + go func() { + defer stdin.Close() + io.Copy(stdin, s) + }() + + go func() { + defer stderr.Close() + io.Copy(s.Stderr(), stderr) + }() + + defer stdout.Close() + io.Copy(s, stdout) + + if err := cmd.Wait(); err != nil { + return + } +} + +func buildCommand(c []string) (cmd *exec.Cmd, stdin io.WriteCloser, stderr, stdout io.ReadCloser, err error) { + if len(c) != 2 { + err = fmt.Errorf("invalid command") + return + } + + // fix for Windows environments + path := strings.Replace(c[1], "/C:/", "C:/", 1) + + cmd = exec.Command(c[0], path) + stdout, err = cmd.StdoutPipe() + if err != nil { + return + } + + stdin, err = cmd.StdinPipe() + if err != nil { + return + } + + stderr, err = cmd.StderrPipe() + if err != nil { + return + } + + return } -- cgit