diff options
Diffstat (limited to 'plumbing/transport')
34 files changed, 678 insertions, 596 deletions
diff --git a/plumbing/transport/client/client.go b/plumbing/transport/client/client.go index 76c1469..90635a5 100644 --- a/plumbing/transport/client/client.go +++ b/plumbing/transport/client/client.go @@ -34,14 +34,14 @@ func InstallProtocol(scheme string, c transport.Transport) { // NewClient returns the appropriate client among of the set of known protocols: // http://, https://, ssh:// and file://. // See `InstallProtocol` to add or modify protocols. -func NewClient(endpoint transport.Endpoint) (transport.Transport, error) { - f, ok := Protocols[endpoint.Protocol()] +func NewClient(endpoint *transport.Endpoint) (transport.Transport, error) { + f, ok := Protocols[endpoint.Protocol] if !ok { - return nil, fmt.Errorf("unsupported scheme %q", endpoint.Protocol()) + return nil, fmt.Errorf("unsupported scheme %q", endpoint.Protocol) } if f == nil { - return nil, fmt.Errorf("malformed client for scheme %q, client is defined as nil", endpoint.Protocol()) + return nil, fmt.Errorf("malformed client for scheme %q, client is defined as nil", endpoint.Protocol) } return f, nil diff --git a/plumbing/transport/client/client_test.go b/plumbing/transport/client/client_test.go index 2b686b6..65cf574 100644 --- a/plumbing/transport/client/client_test.go +++ b/plumbing/transport/client/client_test.go @@ -59,12 +59,12 @@ type dummyClient struct { *http.Client } -func (*dummyClient) NewUploadPackSession(transport.Endpoint, transport.AuthMethod) ( +func (*dummyClient) NewUploadPackSession(*transport.Endpoint, transport.AuthMethod) ( transport.UploadPackSession, error) { return nil, nil } -func (*dummyClient) NewReceivePackSession(transport.Endpoint, transport.AuthMethod) ( +func (*dummyClient) NewReceivePackSession(*transport.Endpoint, transport.AuthMethod) ( transport.ReceivePackSession, error) { return nil, nil } diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go index 2088500..cc9682f 100644 --- a/plumbing/transport/common.go +++ b/plumbing/transport/common.go @@ -13,6 +13,7 @@ package transport import ( + "bytes" "context" "errors" "fmt" @@ -20,6 +21,7 @@ import ( "net/url" "regexp" "strconv" + "strings" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" @@ -45,9 +47,9 @@ const ( // It is implemented both by the client and the server, making this a RPC. type Transport interface { // NewUploadPackSession starts a git-upload-pack session for an endpoint. - NewUploadPackSession(Endpoint, AuthMethod) (UploadPackSession, error) + NewUploadPackSession(*Endpoint, AuthMethod) (UploadPackSession, error) // NewReceivePackSession starts a git-receive-pack session for an endpoint. - NewReceivePackSession(Endpoint, AuthMethod) (ReceivePackSession, error) + NewReceivePackSession(*Endpoint, AuthMethod) (ReceivePackSession, error) } type Session interface { @@ -91,26 +93,71 @@ type ReceivePackSession interface { } // Endpoint represents a Git URL in any supported protocol. -type Endpoint interface { - // Protocol returns the protocol (e.g. git, https, file). It should never - // return the empty string. - Protocol() string - // User returns the user or an empty string if none is given. - User() string - // Password returns the password or an empty string if none is given. - Password() string - // Host returns the host or an empty string if none is given. - Host() string - // Port returns the port or 0 if there is no port or a default should be - // used. - Port() int - // Path returns the repository path. - Path() string - // String returns a string representation of the Git URL. - String() string +type Endpoint struct { + // Protocol is the protocol of the endpoint (e.g. git, https, file). + Protocol string + // User is the user. + User string + // Password is the password. + Password string + // Host is the host. + Host string + // Port is the port to connect, if 0 the default port for the given protocol + // wil be used. + Port int + // Path is the repository path. + Path string } -func NewEndpoint(endpoint string) (Endpoint, error) { +var defaultPorts = map[string]int{ + "http": 80, + "https": 443, + "git": 9418, + "ssh": 22, +} + +// String returns a string representation of the Git URL. +func (u *Endpoint) String() string { + var buf bytes.Buffer + if u.Protocol != "" { + buf.WriteString(u.Protocol) + buf.WriteByte(':') + } + + if u.Protocol != "" || u.Host != "" || u.User != "" || u.Password != "" { + buf.WriteString("//") + + if u.User != "" || u.Password != "" { + buf.WriteString(u.User) + if u.Password != "" { + buf.WriteByte(':') + buf.WriteString(u.Password) + } + + buf.WriteByte('@') + } + + if u.Host != "" { + buf.WriteString(u.Host) + + if u.Port != 0 { + port, ok := defaultPorts[strings.ToLower(u.Protocol)] + if !ok || ok && port != u.Port { + fmt.Fprintf(&buf, ":%d", u.Port) + } + } + } + } + + if u.Path != "" && u.Path[0] != '/' && u.Host != "" { + buf.WriteByte('/') + } + + buf.WriteString(u.Path) + return buf.String() +} + +func NewEndpoint(endpoint string) (*Endpoint, error) { if e, ok := parseSCPLike(endpoint); ok { return e, nil } @@ -119,9 +166,13 @@ func NewEndpoint(endpoint string) (Endpoint, error) { return e, nil } + return parseURL(endpoint) +} + +func parseURL(endpoint string) (*Endpoint, error) { u, err := url.Parse(endpoint) if err != nil { - return nil, plumbing.NewPermanentError(err) + return nil, err } if !u.IsAbs() { @@ -130,40 +181,29 @@ func NewEndpoint(endpoint string) (Endpoint, error) { )) } - return urlEndpoint{u}, nil -} - -type urlEndpoint struct { - *url.URL -} - -func (e urlEndpoint) Protocol() string { return e.URL.Scheme } -func (e urlEndpoint) Host() string { return e.URL.Hostname() } - -func (e urlEndpoint) User() string { - if e.URL.User == nil { - return "" + var user, pass string + if u.User != nil { + user = u.User.Username() + pass, _ = u.User.Password() } - return e.URL.User.Username() + return &Endpoint{ + Protocol: u.Scheme, + User: user, + Password: pass, + Host: u.Hostname(), + Port: getPort(u), + Path: getPath(u), + }, nil } -func (e urlEndpoint) Password() string { - if e.URL.User == nil { - return "" - } - - p, _ := e.URL.User.Password() - return p -} - -func (e urlEndpoint) Port() int { - p := e.URL.Port() +func getPort(u *url.URL) int { + p := u.Port() if p == "" { return 0 } - i, err := strconv.Atoi(e.URL.Port()) + i, err := strconv.Atoi(p) if err != nil { return 0 } @@ -171,78 +211,55 @@ func (e urlEndpoint) Port() int { return i } -func (e urlEndpoint) Path() string { - var res string = e.URL.Path - if e.URL.RawQuery != "" { - res += "?" + e.URL.RawQuery +func getPath(u *url.URL) string { + var res string = u.Path + if u.RawQuery != "" { + res += "?" + u.RawQuery } - if e.URL.Fragment != "" { - res += "#" + e.URL.Fragment + if u.Fragment != "" { + res += "#" + u.Fragment } return res } -type scpEndpoint struct { - user string - host string - path string -} - -func (e *scpEndpoint) Protocol() string { return "ssh" } -func (e *scpEndpoint) User() string { return e.user } -func (e *scpEndpoint) Password() string { return "" } -func (e *scpEndpoint) Host() string { return e.host } -func (e *scpEndpoint) Port() int { return 22 } -func (e *scpEndpoint) Path() string { return e.path } - -func (e *scpEndpoint) String() string { - var user string - if e.user != "" { - user = fmt.Sprintf("%s@", e.user) - } - - return fmt.Sprintf("%s%s:%s", user, e.host, e.path) -} - -type fileEndpoint struct { - path string -} - -func (e *fileEndpoint) Protocol() string { return "file" } -func (e *fileEndpoint) User() string { return "" } -func (e *fileEndpoint) Password() string { return "" } -func (e *fileEndpoint) Host() string { return "" } -func (e *fileEndpoint) Port() int { return 0 } -func (e *fileEndpoint) Path() string { return e.path } -func (e *fileEndpoint) String() string { return e.path } - var ( isSchemeRegExp = regexp.MustCompile(`^[^:]+://`) - scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?P<path>[^\\].*)$`) + scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?:(?P<port>[0-9]{1,5})/)?(?P<path>[^\\].*)$`) ) -func parseSCPLike(endpoint string) (Endpoint, bool) { +func parseSCPLike(endpoint string) (*Endpoint, bool) { if isSchemeRegExp.MatchString(endpoint) || !scpLikeUrlRegExp.MatchString(endpoint) { return nil, false } m := scpLikeUrlRegExp.FindStringSubmatch(endpoint) - return &scpEndpoint{ - user: m[1], - host: m[2], - path: m[3], + + port, err := strconv.Atoi(m[3]) + if err != nil { + port = 22 + } + + return &Endpoint{ + Protocol: "ssh", + User: m[1], + Host: m[2], + Port: port, + Path: m[4], }, true } -func parseFile(endpoint string) (Endpoint, bool) { +func parseFile(endpoint string) (*Endpoint, bool) { if isSchemeRegExp.MatchString(endpoint) { return nil, false } path := endpoint - return &fileEndpoint{path}, true + return &Endpoint{ + Protocol: "file", + Path: path, + }, true } // UnsupportedCapabilities are the capabilities not supported by any client diff --git a/plumbing/transport/common_test.go b/plumbing/transport/common_test.go index ec617bd..4203ce9 100644 --- a/plumbing/transport/common_test.go +++ b/plumbing/transport/common_test.go @@ -17,108 +17,139 @@ var _ = Suite(&SuiteCommon{}) func (s *SuiteCommon) TestNewEndpointHTTP(c *C) { e, err := NewEndpoint("http://git:pass@github.com/user/repository.git?foo#bar") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "http") - c.Assert(e.User(), Equals, "git") - c.Assert(e.Password(), Equals, "pass") - c.Assert(e.Host(), Equals, "github.com") - c.Assert(e.Port(), Equals, 0) - c.Assert(e.Path(), Equals, "/user/repository.git?foo#bar") + c.Assert(e.Protocol, Equals, "http") + c.Assert(e.User, Equals, "git") + c.Assert(e.Password, Equals, "pass") + c.Assert(e.Host, Equals, "github.com") + c.Assert(e.Port, Equals, 0) + c.Assert(e.Path, Equals, "/user/repository.git?foo#bar") c.Assert(e.String(), Equals, "http://git:pass@github.com/user/repository.git?foo#bar") } +func (s *SuiteCommon) TestNewEndpointPorts(c *C) { + e, err := NewEndpoint("http://git:pass@github.com:8080/user/repository.git?foo#bar") + c.Assert(err, IsNil) + c.Assert(e.String(), Equals, "http://git:pass@github.com:8080/user/repository.git?foo#bar") + + e, err = NewEndpoint("https://git:pass@github.com:443/user/repository.git?foo#bar") + c.Assert(err, IsNil) + c.Assert(e.String(), Equals, "https://git:pass@github.com/user/repository.git?foo#bar") + + e, err = NewEndpoint("ssh://git:pass@github.com:22/user/repository.git?foo#bar") + c.Assert(err, IsNil) + c.Assert(e.String(), Equals, "ssh://git:pass@github.com/user/repository.git?foo#bar") + + e, err = NewEndpoint("git://github.com:9418/user/repository.git?foo#bar") + c.Assert(err, IsNil) + c.Assert(e.String(), Equals, "git://github.com/user/repository.git?foo#bar") + +} + func (s *SuiteCommon) TestNewEndpointSSH(c *C) { e, err := NewEndpoint("ssh://git@github.com/user/repository.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "ssh") - c.Assert(e.User(), Equals, "git") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "github.com") - c.Assert(e.Port(), Equals, 0) - c.Assert(e.Path(), Equals, "/user/repository.git") + c.Assert(e.Protocol, Equals, "ssh") + c.Assert(e.User, Equals, "git") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "github.com") + c.Assert(e.Port, Equals, 0) + c.Assert(e.Path, Equals, "/user/repository.git") c.Assert(e.String(), Equals, "ssh://git@github.com/user/repository.git") } func (s *SuiteCommon) TestNewEndpointSSHNoUser(c *C) { e, err := NewEndpoint("ssh://github.com/user/repository.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "ssh") - c.Assert(e.User(), Equals, "") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "github.com") - c.Assert(e.Port(), Equals, 0) - c.Assert(e.Path(), Equals, "/user/repository.git") + c.Assert(e.Protocol, Equals, "ssh") + c.Assert(e.User, Equals, "") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "github.com") + c.Assert(e.Port, Equals, 0) + c.Assert(e.Path, Equals, "/user/repository.git") c.Assert(e.String(), Equals, "ssh://github.com/user/repository.git") } func (s *SuiteCommon) TestNewEndpointSSHWithPort(c *C) { e, err := NewEndpoint("ssh://git@github.com:777/user/repository.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "ssh") - c.Assert(e.User(), Equals, "git") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "github.com") - c.Assert(e.Port(), Equals, 777) - c.Assert(e.Path(), Equals, "/user/repository.git") + c.Assert(e.Protocol, Equals, "ssh") + c.Assert(e.User, Equals, "git") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "github.com") + c.Assert(e.Port, Equals, 777) + c.Assert(e.Path, Equals, "/user/repository.git") c.Assert(e.String(), Equals, "ssh://git@github.com:777/user/repository.git") } func (s *SuiteCommon) TestNewEndpointSCPLike(c *C) { e, err := NewEndpoint("git@github.com:user/repository.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "ssh") - c.Assert(e.User(), Equals, "git") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "github.com") - c.Assert(e.Port(), Equals, 22) - c.Assert(e.Path(), Equals, "user/repository.git") - c.Assert(e.String(), Equals, "git@github.com:user/repository.git") + c.Assert(e.Protocol, Equals, "ssh") + c.Assert(e.User, Equals, "git") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "github.com") + c.Assert(e.Port, Equals, 22) + c.Assert(e.Path, Equals, "user/repository.git") + c.Assert(e.String(), Equals, "ssh://git@github.com/user/repository.git") +} + +func (s *SuiteCommon) TestNewEndpointSCPLikeWithPort(c *C) { + e, err := NewEndpoint("git@github.com:9999/user/repository.git") + c.Assert(err, IsNil) + c.Assert(e.Protocol, Equals, "ssh") + c.Assert(e.User, Equals, "git") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "github.com") + c.Assert(e.Port, Equals, 9999) + c.Assert(e.Path, Equals, "user/repository.git") + c.Assert(e.String(), Equals, "ssh://git@github.com:9999/user/repository.git") } func (s *SuiteCommon) TestNewEndpointFileAbs(c *C) { e, err := NewEndpoint("/foo.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "file") - c.Assert(e.User(), Equals, "") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "") - c.Assert(e.Port(), Equals, 0) - c.Assert(e.Path(), Equals, "/foo.git") - c.Assert(e.String(), Equals, "/foo.git") + c.Assert(e.Protocol, Equals, "file") + c.Assert(e.User, Equals, "") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "") + c.Assert(e.Port, Equals, 0) + c.Assert(e.Path, Equals, "/foo.git") + c.Assert(e.String(), Equals, "file:///foo.git") } func (s *SuiteCommon) TestNewEndpointFileRel(c *C) { e, err := NewEndpoint("foo.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "file") - c.Assert(e.User(), Equals, "") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "") - c.Assert(e.Port(), Equals, 0) - c.Assert(e.Path(), Equals, "foo.git") - c.Assert(e.String(), Equals, "foo.git") + c.Assert(e.Protocol, Equals, "file") + c.Assert(e.User, Equals, "") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "") + c.Assert(e.Port, Equals, 0) + c.Assert(e.Path, Equals, "foo.git") + c.Assert(e.String(), Equals, "file://foo.git") } func (s *SuiteCommon) TestNewEndpointFileWindows(c *C) { e, err := NewEndpoint("C:\\foo.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "file") - c.Assert(e.User(), Equals, "") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "") - c.Assert(e.Port(), Equals, 0) - c.Assert(e.Path(), Equals, "C:\\foo.git") - c.Assert(e.String(), Equals, "C:\\foo.git") + c.Assert(e.Protocol, Equals, "file") + c.Assert(e.User, Equals, "") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "") + c.Assert(e.Port, Equals, 0) + c.Assert(e.Path, Equals, "C:\\foo.git") + c.Assert(e.String(), Equals, "file://C:\\foo.git") } func (s *SuiteCommon) TestNewEndpointFileURL(c *C) { e, err := NewEndpoint("file:///foo.git") c.Assert(err, IsNil) - c.Assert(e.Protocol(), Equals, "file") - c.Assert(e.User(), Equals, "") - c.Assert(e.Password(), Equals, "") - c.Assert(e.Host(), Equals, "") - c.Assert(e.Port(), Equals, 0) - c.Assert(e.Path(), Equals, "/foo.git") + c.Assert(e.Protocol, Equals, "file") + c.Assert(e.User, Equals, "") + c.Assert(e.Password, Equals, "") + c.Assert(e.Host, Equals, "") + c.Assert(e.Port, Equals, 0) + c.Assert(e.Path, Equals, "/foo.git") c.Assert(e.String(), Equals, "file:///foo.git") } diff --git a/plumbing/transport/file/client.go b/plumbing/transport/file/client.go index d229fdd..e799ee1 100644 --- a/plumbing/transport/file/client.go +++ b/plumbing/transport/file/client.go @@ -73,7 +73,7 @@ func prefixExecPath(cmd string) (string, error) { return cmd, nil } -func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod, +func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod, ) (common.Command, error) { switch cmd { @@ -95,7 +95,7 @@ func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthM } } - return &command{cmd: exec.Command(cmd, ep.Path())}, nil + return &command{cmd: exec.Command(cmd, ep.Path)}, nil } type command struct { diff --git a/plumbing/transport/file/client_test.go b/plumbing/transport/file/client_test.go index 864cddc..25ea278 100644 --- a/plumbing/transport/file/client_test.go +++ b/plumbing/transport/file/client_test.go @@ -41,7 +41,7 @@ repositoryformatversion = 0 filemode = true bare = true` -func prepareRepo(c *C, path string) transport.Endpoint { +func prepareRepo(c *C, path string) *transport.Endpoint { ep, err := transport.NewEndpoint(path) c.Assert(err, IsNil) diff --git a/plumbing/transport/file/common_test.go b/plumbing/transport/file/common_test.go index 4f3ae8f..99866d7 100644 --- a/plumbing/transport/file/common_test.go +++ b/plumbing/transport/file/common_test.go @@ -6,9 +6,8 @@ import ( "os/exec" "path/filepath" - "github.com/src-d/go-git-fixtures" - . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type CommonSuite struct { diff --git a/plumbing/transport/file/receive_pack_test.go b/plumbing/transport/file/receive_pack_test.go index a7dc399..3e7b140 100644 --- a/plumbing/transport/file/receive_pack_test.go +++ b/plumbing/transport/file/receive_pack_test.go @@ -3,10 +3,10 @@ package file import ( "os" - "github.com/src-d/go-git-fixtures" "gopkg.in/src-d/go-git.v4/plumbing/transport/test" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type ReceivePackSuite struct { diff --git a/plumbing/transport/file/server_test.go b/plumbing/transport/file/server_test.go index 080beef..1793c0f 100644 --- a/plumbing/transport/file/server_test.go +++ b/plumbing/transport/file/server_test.go @@ -4,9 +4,8 @@ import ( "os" "os/exec" - "github.com/src-d/go-git-fixtures" - . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type ServerSuite struct { diff --git a/plumbing/transport/file/upload_pack_test.go b/plumbing/transport/file/upload_pack_test.go index 9a922d1..0b9b562 100644 --- a/plumbing/transport/file/upload_pack_test.go +++ b/plumbing/transport/file/upload_pack_test.go @@ -4,11 +4,11 @@ import ( "os" "path/filepath" - "github.com/src-d/go-git-fixtures" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/test" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type UploadPackSuite struct { diff --git a/plumbing/transport/git/common.go b/plumbing/transport/git/common.go index fcd02f8..78aaa3b 100644 --- a/plumbing/transport/git/common.go +++ b/plumbing/transport/git/common.go @@ -20,7 +20,7 @@ const DefaultPort = 9418 type runner struct{} // Command returns a new Command for the given cmd in the given Endpoint -func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod) (common.Command, error) { +func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod) (common.Command, error) { // auth not allowed since git protocol doesn't support authentication if auth != nil { return nil, transport.ErrInvalidAuthMethod @@ -36,7 +36,7 @@ type command struct { conn net.Conn connected bool command string - endpoint transport.Endpoint + endpoint *transport.Endpoint } // Start executes the command sending the required message to the TCP connection @@ -63,8 +63,8 @@ func (c *command) connect() error { } func (c *command) getHostWithPort() string { - host := c.endpoint.Host() - port := c.endpoint.Port() + host := c.endpoint.Host + port := c.endpoint.Port if port <= 0 { port = DefaultPort } @@ -89,13 +89,13 @@ func (c *command) StdoutPipe() (io.Reader, error) { return c.conn, nil } -func endpointToCommand(cmd string, ep transport.Endpoint) string { - host := ep.Host() - if ep.Port() != DefaultPort { - host = fmt.Sprintf("%s:%d", ep.Host(), ep.Port()) +func endpointToCommand(cmd string, ep *transport.Endpoint) string { + host := ep.Host + if ep.Port != DefaultPort { + host = fmt.Sprintf("%s:%d", ep.Host, ep.Port) } - return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path(), 0, host, 0) + return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path, 0, host, 0) } // Close closes the TCP connection and connection. diff --git a/plumbing/transport/git/common_test.go b/plumbing/transport/git/common_test.go index 3f25ad9..61097e7 100644 --- a/plumbing/transport/git/common_test.go +++ b/plumbing/transport/git/common_test.go @@ -1,9 +1,108 @@ package git import ( + "fmt" + "io/ioutil" + "net" + "os" + "os/exec" + "path/filepath" + "runtime" "testing" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing/transport" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) func Test(t *testing.T) { TestingT(t) } + +type BaseSuite struct { + fixtures.Suite + + base string + port int + daemon *exec.Cmd +} + +func (s *BaseSuite) SetUpTest(c *C) { + if runtime.GOOS == "windows" { + c.Skip(`git for windows has issues with write operations through git:// protocol. + See https://github.com/git-for-windows/git/issues/907`) + } + + var err error + s.port, err = freePort() + c.Assert(err, IsNil) + + s.base, err = ioutil.TempDir(os.TempDir(), fmt.Sprintf("go-git-protocol-%d", s.port)) + c.Assert(err, IsNil) +} + +func (s *BaseSuite) StartDaemon(c *C) { + s.daemon = exec.Command( + "git", + "daemon", + fmt.Sprintf("--base-path=%s", s.base), + "--export-all", + "--enable=receive-pack", + "--reuseaddr", + fmt.Sprintf("--port=%d", s.port), + // Unless max-connections is limited to 1, a git-receive-pack + // might not be seen by a subsequent operation. + "--max-connections=1", + ) + + // Environment must be inherited in order to acknowledge GIT_EXEC_PATH if set. + s.daemon.Env = os.Environ() + + err := s.daemon.Start() + c.Assert(err, IsNil) + + // Connections might be refused if we start sending request too early. + time.Sleep(time.Millisecond * 500) +} + +func (s *BaseSuite) newEndpoint(c *C, name string) *transport.Endpoint { + ep, err := transport.NewEndpoint(fmt.Sprintf("git://localhost:%d/%s", s.port, name)) + c.Assert(err, IsNil) + + return ep +} + +func (s *BaseSuite) prepareRepository(c *C, f *fixtures.Fixture, name string) *transport.Endpoint { + fs := f.DotGit() + + err := fixtures.EnsureIsBare(fs) + c.Assert(err, IsNil) + + path := filepath.Join(s.base, name) + err = os.Rename(fs.Root(), path) + c.Assert(err, IsNil) + + return s.newEndpoint(c, name) +} + +func (s *BaseSuite) TearDownTest(c *C) { + _ = s.daemon.Process.Signal(os.Kill) + _ = s.daemon.Wait() + + err := os.RemoveAll(s.base) + c.Assert(err, IsNil) +} + +func freePort() (int, error) { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0, err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0, err + } + + return l.Addr().(*net.TCPAddr).Port, l.Close() +} diff --git a/plumbing/transport/git/receive_pack_test.go b/plumbing/transport/git/receive_pack_test.go index 7b0fa46..fa10735 100644 --- a/plumbing/transport/git/receive_pack_test.go +++ b/plumbing/transport/git/receive_pack_test.go @@ -1,148 +1,26 @@ package git import ( - "fmt" - "io" - "io/ioutil" - "net" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "time" - - "github.com/src-d/go-git-fixtures" - "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/test" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type ReceivePackSuite struct { test.ReceivePackSuite - fixtures.Suite - - base string - daemon *exec.Cmd + BaseSuite } var _ = Suite(&ReceivePackSuite{}) func (s *ReceivePackSuite) SetUpTest(c *C) { - if runtime.GOOS == "windows" { - c.Skip(`git for windows has issues with write operations through git:// protocol. - See https://github.com/git-for-windows/git/issues/907`) - } + s.BaseSuite.SetUpTest(c) s.ReceivePackSuite.Client = DefaultClient + s.ReceivePackSuite.Endpoint = s.prepareRepository(c, fixtures.Basic().One(), "basic.git") + s.ReceivePackSuite.EmptyEndpoint = s.prepareRepository(c, fixtures.ByTag("empty").One(), "empty.git") + s.ReceivePackSuite.NonExistentEndpoint = s.newEndpoint(c, "non-existent.git") - port, err := freePort() - c.Assert(err, IsNil) - - base, err := ioutil.TempDir(os.TempDir(), "go-git-daemon-test") - c.Assert(err, IsNil) - s.base = base - - host := fmt.Sprintf("localhost_%d", port) - interpolatedBase := filepath.Join(base, host) - err = os.MkdirAll(interpolatedBase, 0755) - c.Assert(err, IsNil) - - dotgit := fixtures.Basic().One().DotGit().Root() - prepareRepo(c, dotgit) - err = os.Rename(dotgit, filepath.Join(interpolatedBase, "basic.git")) - c.Assert(err, IsNil) - - ep, err := transport.NewEndpoint(fmt.Sprintf("git://localhost:%d/basic.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.Endpoint = ep - - dotgit = fixtures.ByTag("empty").One().DotGit().Root() - prepareRepo(c, dotgit) - err = os.Rename(dotgit, filepath.Join(interpolatedBase, "empty.git")) - c.Assert(err, IsNil) - - ep, err = transport.NewEndpoint(fmt.Sprintf("git://localhost:%d/empty.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.EmptyEndpoint = ep - - ep, err = transport.NewEndpoint(fmt.Sprintf("git://localhost:%d/non-existent.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.NonExistentEndpoint = ep - - s.daemon = exec.Command( - "git", - "daemon", - fmt.Sprintf("--base-path=%s", base), - "--export-all", - "--enable=receive-pack", - "--reuseaddr", - fmt.Sprintf("--port=%d", port), - // Use interpolated paths to validate that clients are specifying - // host and port properly. - // Note that some git versions (e.g. v2.11.0) had a bug that prevented - // the use of repository paths containing colons (:), so we use - // underscore (_) instead of colon in the interpolation. - // See https://github.com/git/git/commit/fe050334074c5132d01e1df2c1b9a82c9b8d394c - fmt.Sprintf("--interpolated-path=%s/%%H_%%P%%D", base), - // Unless max-connections is limited to 1, a git-receive-pack - // might not be seen by a subsequent operation. - "--max-connections=1", - // Whitelist required for interpolated paths. - fmt.Sprintf("%s/%s", interpolatedBase, "basic.git"), - fmt.Sprintf("%s/%s", interpolatedBase, "empty.git"), - ) - - // Environment must be inherited in order to acknowledge GIT_EXEC_PATH if set. - s.daemon.Env = os.Environ() - - err = s.daemon.Start() - c.Assert(err, IsNil) - - // Connections might be refused if we start sending request too early. - time.Sleep(time.Millisecond * 500) -} - -func (s *ReceivePackSuite) TearDownTest(c *C) { - err := s.daemon.Process.Signal(os.Kill) - c.Assert(err, IsNil) - - _ = s.daemon.Wait() - - err = os.RemoveAll(s.base) - c.Assert(err, IsNil) -} - -func freePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return 0, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } - - return l.Addr().(*net.TCPAddr).Port, l.Close() -} - -const bareConfig = `[core] -repositoryformatversion = 0 -filemode = true -bare = true` - -func prepareRepo(c *C, path string) { - // git-receive-pack refuses to update refs/heads/master on non-bare repo - // so we ensure bare repo config. - config := filepath.Join(path, "config") - if _, err := os.Stat(config); err == nil { - f, err := os.OpenFile(config, os.O_TRUNC|os.O_WRONLY, 0) - c.Assert(err, IsNil) - content := strings.NewReader(bareConfig) - _, err = io.Copy(f, content) - c.Assert(err, IsNil) - c.Assert(f.Close(), IsNil) - } + s.StartDaemon(c) } diff --git a/plumbing/transport/git/upload_pack_test.go b/plumbing/transport/git/upload_pack_test.go index d367a7f..7058564 100644 --- a/plumbing/transport/git/upload_pack_test.go +++ b/plumbing/transport/git/upload_pack_test.go @@ -1,35 +1,26 @@ package git import ( - "github.com/src-d/go-git-fixtures" - "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/test" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type UploadPackSuite struct { test.UploadPackSuite - fixtures.Suite + BaseSuite } var _ = Suite(&UploadPackSuite{}) func (s *UploadPackSuite) SetUpSuite(c *C) { - s.Suite.SetUpSuite(c) + s.BaseSuite.SetUpTest(c) s.UploadPackSuite.Client = DefaultClient + 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") - ep, err := transport.NewEndpoint("git://github.com/git-fixtures/basic.git") - c.Assert(err, IsNil) - s.UploadPackSuite.Endpoint = ep - - ep, err = transport.NewEndpoint("git://github.com/git-fixtures/empty.git") - c.Assert(err, IsNil) - s.UploadPackSuite.EmptyEndpoint = ep - - ep, err = transport.NewEndpoint("git://github.com/git-fixtures/non-existent.git") - c.Assert(err, IsNil) - s.UploadPackSuite.NonExistentEndpoint = ep - + s.StartDaemon(c) } diff --git a/plumbing/transport/http/common.go b/plumbing/transport/http/common.go index 6b40d42..edf1c6c 100644 --- a/plumbing/transport/http/common.go +++ b/plumbing/transport/http/common.go @@ -10,6 +10,7 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" "gopkg.in/src-d/go-git.v4/plumbing/transport" + "gopkg.in/src-d/go-git.v4/utils/ioutil" ) // it requires a bytes.Buffer, because we need to know the length @@ -39,14 +40,15 @@ func advertisedReferences(s *session, serviceName string) (*packp.AdvRefs, error } s.applyAuthToRequest(req) - applyHeadersToRequest(req, nil, s.endpoint.Host(), serviceName) + applyHeadersToRequest(req, nil, s.endpoint.Host, serviceName) res, err := s.client.Do(req) if err != nil { return nil, err } + defer ioutil.CheckClose(res.Body, &err) + if err := NewErr(res); err != nil { - _ = res.Body.Close() return nil, err } @@ -90,13 +92,13 @@ func NewClient(c *http.Client) transport.Transport { } } -func (c *client) NewUploadPackSession(ep transport.Endpoint, auth transport.AuthMethod) ( +func (c *client) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) ( transport.UploadPackSession, error) { return newUploadPackSession(c.c, ep, auth) } -func (c *client) NewReceivePackSession(ep transport.Endpoint, auth transport.AuthMethod) ( +func (c *client) NewReceivePackSession(ep *transport.Endpoint, auth transport.AuthMethod) ( transport.ReceivePackSession, error) { return newReceivePackSession(c.c, ep, auth) @@ -105,11 +107,11 @@ func (c *client) NewReceivePackSession(ep transport.Endpoint, auth transport.Aut type session struct { auth AuthMethod client *http.Client - endpoint transport.Endpoint + endpoint *transport.Endpoint advRefs *packp.AdvRefs } -func newSession(c *http.Client, ep transport.Endpoint, auth transport.AuthMethod) (*session, error) { +func newSession(c *http.Client, ep *transport.Endpoint, auth transport.AuthMethod) (*session, error) { s := &session{ auth: basicAuthFromEndpoint(ep), client: c, @@ -145,23 +147,18 @@ type AuthMethod interface { setAuth(r *http.Request) } -func basicAuthFromEndpoint(ep transport.Endpoint) *BasicAuth { - u := ep.User() +func basicAuthFromEndpoint(ep *transport.Endpoint) *BasicAuth { + u := ep.User if u == "" { return nil } - return NewBasicAuth(u, ep.Password()) + return &BasicAuth{u, ep.Password} } // BasicAuth represent a HTTP basic auth type BasicAuth struct { - username, password string -} - -// NewBasicAuth returns a basicAuth base on the given user and password -func NewBasicAuth(username, password string) *BasicAuth { - return &BasicAuth{username, password} + Username, Password string } func (a *BasicAuth) setAuth(r *http.Request) { @@ -169,7 +166,7 @@ func (a *BasicAuth) setAuth(r *http.Request) { return } - r.SetBasicAuth(a.username, a.password) + r.SetBasicAuth(a.Username, a.Password) } // Name is name of the auth @@ -179,11 +176,11 @@ func (a *BasicAuth) Name() string { func (a *BasicAuth) String() string { masked := "*******" - if a.password == "" { + if a.Password == "" { masked = "<empty>" } - return fmt.Sprintf("%s - %s:%s", a.Name(), a.username, masked) + return fmt.Sprintf("%s - %s:%s", a.Name(), a.Username, masked) } // Err is a dedicated error to return errors based on status code diff --git a/plumbing/transport/http/common_test.go b/plumbing/transport/http/common_test.go index d1f36d3..8d57996 100644 --- a/plumbing/transport/http/common_test.go +++ b/plumbing/transport/http/common_test.go @@ -2,18 +2,28 @@ package http import ( "crypto/tls" + "fmt" + "io/ioutil" + "log" + "net" "net/http" + "net/http/cgi" + "os" + "os/exec" + "path/filepath" + "strings" "testing" "gopkg.in/src-d/go-git.v4/plumbing/transport" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) func Test(t *testing.T) { TestingT(t) } type ClientSuite struct { - Endpoint transport.Endpoint + Endpoint *transport.Endpoint EmptyAuth transport.AuthMethod } @@ -38,7 +48,7 @@ func (s *UploadPackSuite) TestNewClient(c *C) { } func (s *ClientSuite) TestNewBasicAuth(c *C) { - a := NewBasicAuth("foo", "qux") + a := &BasicAuth{"foo", "qux"} c.Assert(a.Name(), Equals, "http-basic-auth") c.Assert(a.String(), Equals, "http-basic-auth - foo:*******") @@ -95,3 +105,64 @@ func (s *ClientSuite) TestSetAuthWrongType(c *C) { _, err := DefaultClient.NewUploadPackSession(s.Endpoint, &mockAuth{}) c.Assert(err, Equals, transport.ErrInvalidAuthMethod) } + +type BaseSuite struct { + fixtures.Suite + + base string + host string + port int +} + +func (s *BaseSuite) SetUpTest(c *C) { + l, err := net.Listen("tcp", "localhost:0") + c.Assert(err, IsNil) + + base, err := ioutil.TempDir(os.TempDir(), fmt.Sprintf("go-git-http-%d", s.port)) + c.Assert(err, IsNil) + + s.port = l.Addr().(*net.TCPAddr).Port + s.base = filepath.Join(base, s.host) + + err = os.MkdirAll(s.base, 0755) + c.Assert(err, IsNil) + + cmd := exec.Command("git", "--exec-path") + out, err := cmd.CombinedOutput() + c.Assert(err, IsNil) + + server := &http.Server{ + Handler: &cgi.Handler{ + Path: filepath.Join(strings.Trim(string(out), "\n"), "git-http-backend"), + Env: []string{"GIT_HTTP_EXPORT_ALL=true", fmt.Sprintf("GIT_PROJECT_ROOT=%s", s.base)}, + }, + } + go func() { + log.Fatal(server.Serve(l)) + }() +} + +func (s *BaseSuite) prepareRepository(c *C, f *fixtures.Fixture, name string) *transport.Endpoint { + fs := f.DotGit() + + err := fixtures.EnsureIsBare(fs) + c.Assert(err, IsNil) + + path := filepath.Join(s.base, name) + err = os.Rename(fs.Root(), path) + c.Assert(err, IsNil) + + return s.newEndpoint(c, name) +} + +func (s *BaseSuite) newEndpoint(c *C, name string) *transport.Endpoint { + ep, err := transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/%s", s.port, name)) + c.Assert(err, IsNil) + + return ep +} + +func (s *BaseSuite) TearDownTest(c *C) { + err := os.RemoveAll(s.base) + c.Assert(err, IsNil) +} diff --git a/plumbing/transport/http/receive_pack.go b/plumbing/transport/http/receive_pack.go index d2dfeb7..e5cae28 100644 --- a/plumbing/transport/http/receive_pack.go +++ b/plumbing/transport/http/receive_pack.go @@ -19,7 +19,7 @@ type rpSession struct { *session } -func newReceivePackSession(c *http.Client, ep transport.Endpoint, auth transport.AuthMethod) (transport.ReceivePackSession, error) { +func newReceivePackSession(c *http.Client, ep *transport.Endpoint, auth transport.AuthMethod) (transport.ReceivePackSession, error) { s, err := newSession(c, ep, auth) return &rpSession{s}, err } @@ -89,7 +89,7 @@ func (s *rpSession) doRequest( return nil, plumbing.NewPermanentError(err) } - applyHeadersToRequest(req, content, s.endpoint.Host(), transport.ReceivePackServiceName) + applyHeadersToRequest(req, content, s.endpoint.Host, transport.ReceivePackServiceName) s.applyAuthToRequest(req) res, err := s.client.Do(req.WithContext(ctx)) diff --git a/plumbing/transport/http/receive_pack_test.go b/plumbing/transport/http/receive_pack_test.go index d870e5d..737d792 100644 --- a/plumbing/transport/http/receive_pack_test.go +++ b/plumbing/transport/http/receive_pack_test.go @@ -1,122 +1,24 @@ package http import ( - "fmt" - "io" - "io/ioutil" - "log" - "net" - "net/http" - "net/http/cgi" - "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/src-d/go-git-fixtures" + "gopkg.in/src-d/go-git-fixtures.v3" . "gopkg.in/check.v1" ) type ReceivePackSuite struct { test.ReceivePackSuite - fixtures.Suite - - base string + BaseSuite } var _ = Suite(&ReceivePackSuite{}) func (s *ReceivePackSuite) SetUpTest(c *C) { - s.ReceivePackSuite.Client = DefaultClient - - port, err := freePort() - c.Assert(err, IsNil) - - base, err := ioutil.TempDir(os.TempDir(), "go-git-http-backend-test") - c.Assert(err, IsNil) - s.base = base - - host := fmt.Sprintf("localhost_%d", port) - interpolatedBase := filepath.Join(base, host) - err = os.MkdirAll(interpolatedBase, 0755) - c.Assert(err, IsNil) - - dotgit := fixtures.Basic().One().DotGit().Root() - prepareRepo(c, dotgit) - err = os.Rename(dotgit, filepath.Join(interpolatedBase, "basic.git")) - c.Assert(err, IsNil) - - ep, err := transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/basic.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.Endpoint = ep - - dotgit = fixtures.ByTag("empty").One().DotGit().Root() - prepareRepo(c, dotgit) - err = os.Rename(dotgit, filepath.Join(interpolatedBase, "empty.git")) - c.Assert(err, IsNil) - - ep, err = transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/empty.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.EmptyEndpoint = ep - - ep, err = transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/non-existent.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.NonExistentEndpoint = ep - - cmd := exec.Command("git", "--exec-path") - out, err := cmd.CombinedOutput() - c.Assert(err, IsNil) - p := filepath.Join(strings.Trim(string(out), "\n"), "git-http-backend") + s.BaseSuite.SetUpTest(c) - h := &cgi.Handler{ - Path: p, - Env: []string{"GIT_HTTP_EXPORT_ALL=true", fmt.Sprintf("GIT_PROJECT_ROOT=%s", interpolatedBase)}, - } - - go func() { - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), h)) - }() -} - -func (s *ReceivePackSuite) TearDownTest(c *C) { - err := os.RemoveAll(s.base) - c.Assert(err, IsNil) -} - -func freePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return 0, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } - - return l.Addr().(*net.TCPAddr).Port, l.Close() -} - -const bareConfig = `[core] -repositoryformatversion = 0 -filemode = true -bare = true -[http] -receivepack = true` - -func prepareRepo(c *C, path string) { - // git-receive-pack refuses to update refs/heads/master on non-bare repo - // so we ensure bare repo config. - config := filepath.Join(path, "config") - if _, err := os.Stat(config); err == nil { - f, err := os.OpenFile(config, os.O_TRUNC|os.O_WRONLY, 0) - c.Assert(err, IsNil) - content := strings.NewReader(bareConfig) - _, err = io.Copy(f, content) - c.Assert(err, IsNil) - c.Assert(f.Close(), IsNil) - } + s.ReceivePackSuite.Client = DefaultClient + s.ReceivePackSuite.Endpoint = s.prepareRepository(c, fixtures.Basic().One(), "basic.git") + s.ReceivePackSuite.EmptyEndpoint = s.prepareRepository(c, fixtures.ByTag("empty").One(), "empty.git") + s.ReceivePackSuite.NonExistentEndpoint = s.newEndpoint(c, "non-existent.git") } diff --git a/plumbing/transport/http/upload_pack.go b/plumbing/transport/http/upload_pack.go index c5ac325..85a57a5 100644 --- a/plumbing/transport/http/upload_pack.go +++ b/plumbing/transport/http/upload_pack.go @@ -19,9 +19,8 @@ type upSession struct { *session } -func newUploadPackSession(c *http.Client, ep transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) { +func newUploadPackSession(c *http.Client, ep *transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) { s, err := newSession(c, ep, auth) - return &upSession{s}, err } @@ -88,7 +87,7 @@ func (s *upSession) doRequest( return nil, plumbing.NewPermanentError(err) } - applyHeadersToRequest(req, content, s.endpoint.Host(), transport.UploadPackServiceName) + applyHeadersToRequest(req, content, s.endpoint.Host, transport.UploadPackServiceName) s.applyAuthToRequest(req) res, err := s.client.Do(req.WithContext(ctx)) diff --git a/plumbing/transport/http/upload_pack_test.go b/plumbing/transport/http/upload_pack_test.go index 57d5f46..fbd28c7 100644 --- a/plumbing/transport/http/upload_pack_test.go +++ b/plumbing/transport/http/upload_pack_test.go @@ -1,7 +1,10 @@ package http import ( + "fmt" "io/ioutil" + "os" + "path/filepath" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" @@ -9,28 +12,22 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/transport/test" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type UploadPackSuite struct { test.UploadPackSuite + BaseSuite } var _ = Suite(&UploadPackSuite{}) func (s *UploadPackSuite) SetUpSuite(c *C) { + s.BaseSuite.SetUpTest(c) s.UploadPackSuite.Client = DefaultClient - - ep, err := transport.NewEndpoint("https://github.com/git-fixtures/basic.git") - c.Assert(err, IsNil) - s.UploadPackSuite.Endpoint = ep - - ep, err = transport.NewEndpoint("https://github.com/git-fixtures/empty.git") - c.Assert(err, IsNil) - s.UploadPackSuite.EmptyEndpoint = ep - - ep, err = transport.NewEndpoint("https://github.com/git-fixtures/non-existent.git") - c.Assert(err, IsNil) - s.UploadPackSuite.NonExistentEndpoint = ep + 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") } // Overwritten, different behaviour for HTTP. @@ -38,7 +35,7 @@ func (s *UploadPackSuite) TestAdvertisedReferencesNotExists(c *C) { r, err := s.Client.NewUploadPackSession(s.NonExistentEndpoint, s.EmptyAuth) c.Assert(err, IsNil) info, err := r.AdvertisedReferences() - c.Assert(err, Equals, transport.ErrAuthenticationRequired) + c.Assert(err, Equals, transport.ErrRepositoryNotFound) c.Assert(info, IsNil) } @@ -58,3 +55,23 @@ func (s *UploadPackSuite) TestuploadPackRequestToReader(c *C) { "0009done\n", ) } + +func (s *UploadPackSuite) prepareRepository(c *C, f *fixtures.Fixture, name string) *transport.Endpoint { + fs := f.DotGit() + + err := fixtures.EnsureIsBare(fs) + c.Assert(err, IsNil) + + path := filepath.Join(s.base, name) + err = os.Rename(fs.Root(), path) + c.Assert(err, IsNil) + + return s.newEndpoint(c, name) +} + +func (s *UploadPackSuite) newEndpoint(c *C, name string) *transport.Endpoint { + ep, err := transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/%s", s.port, name)) + c.Assert(err, IsNil) + + return ep +} diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go index 598c6b1..8ec1ea5 100644 --- a/plumbing/transport/internal/common/common.go +++ b/plumbing/transport/internal/common/common.go @@ -39,7 +39,7 @@ type Commander interface { // error should be returned if the endpoint is not supported or the // command cannot be created (e.g. binary does not exist, connection // cannot be established). - Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod) (Command, error) + Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod) (Command, error) } // Command is used for a single command execution. @@ -83,14 +83,14 @@ func NewClient(runner Commander) transport.Transport { } // NewUploadPackSession creates a new UploadPackSession. -func (c *client) NewUploadPackSession(ep transport.Endpoint, auth transport.AuthMethod) ( +func (c *client) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) ( transport.UploadPackSession, error) { return c.newSession(transport.UploadPackServiceName, ep, auth) } // NewReceivePackSession creates a new ReceivePackSession. -func (c *client) NewReceivePackSession(ep transport.Endpoint, auth transport.AuthMethod) ( +func (c *client) NewReceivePackSession(ep *transport.Endpoint, auth transport.AuthMethod) ( transport.ReceivePackSession, error) { return c.newSession(transport.ReceivePackServiceName, ep, auth) @@ -108,7 +108,7 @@ type session struct { firstErrLine chan string } -func (c *client) newSession(s string, ep transport.Endpoint, auth transport.AuthMethod) (*session, error) { +func (c *client) newSession(s string, ep *transport.Endpoint, auth transport.AuthMethod) (*session, error) { cmd, err := c.cmdr.Command(s, ep, auth) if err != nil { return nil, err diff --git a/plumbing/transport/server/loader.go b/plumbing/transport/server/loader.go index 028ead4..c83752c 100644 --- a/plumbing/transport/server/loader.go +++ b/plumbing/transport/server/loader.go @@ -5,8 +5,8 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/storage/filesystem" - "gopkg.in/src-d/go-billy.v3" - "gopkg.in/src-d/go-billy.v3/osfs" + "gopkg.in/src-d/go-billy.v4" + "gopkg.in/src-d/go-billy.v4/osfs" ) // DefaultLoader is a filesystem loader ignoring host and resolving paths to /. @@ -17,7 +17,7 @@ type Loader interface { // Load loads a storer.Storer given a transport.Endpoint. // Returns transport.ErrRepositoryNotFound if the repository does not // exist. - Load(ep transport.Endpoint) (storer.Storer, error) + Load(ep *transport.Endpoint) (storer.Storer, error) } type fsLoader struct { @@ -33,8 +33,8 @@ func NewFilesystemLoader(base billy.Filesystem) Loader { // Load looks up the endpoint's path in the base file system and returns a // storer for it. Returns transport.ErrRepositoryNotFound if a repository does // not exist in the given path. -func (l *fsLoader) Load(ep transport.Endpoint) (storer.Storer, error) { - fs, err := l.base.Chroot(ep.Path()) +func (l *fsLoader) Load(ep *transport.Endpoint) (storer.Storer, error) { + fs, err := l.base.Chroot(ep.Path) if err != nil { return nil, err } @@ -53,7 +53,7 @@ type MapLoader map[string]storer.Storer // Load returns a storer.Storer for given a transport.Endpoint by looking it up // in the map. Returns transport.ErrRepositoryNotFound if the endpoint does not // exist. -func (l MapLoader) Load(ep transport.Endpoint) (storer.Storer, error) { +func (l MapLoader) Load(ep *transport.Endpoint) (storer.Storer, error) { s, ok := l[ep.String()] if !ok { return nil, transport.ErrRepositoryNotFound diff --git a/plumbing/transport/server/loader_test.go b/plumbing/transport/server/loader_test.go index 38fabe3..f35511d 100644 --- a/plumbing/transport/server/loader_test.go +++ b/plumbing/transport/server/loader_test.go @@ -26,7 +26,7 @@ func (s *LoaderSuite) SetUpSuite(c *C) { c.Assert(exec.Command("git", "init", "--bare", s.RepoPath).Run(), IsNil) } -func (s *LoaderSuite) endpoint(c *C, url string) transport.Endpoint { +func (s *LoaderSuite) endpoint(c *C, url string) *transport.Endpoint { ep, err := transport.NewEndpoint(url) c.Assert(err, IsNil) return ep diff --git a/plumbing/transport/server/receive_pack_test.go b/plumbing/transport/server/receive_pack_test.go index 54c2fba..39fa979 100644 --- a/plumbing/transport/server/receive_pack_test.go +++ b/plumbing/transport/server/receive_pack_test.go @@ -2,14 +2,12 @@ package server_test import ( "gopkg.in/src-d/go-git.v4/plumbing/transport" - "gopkg.in/src-d/go-git.v4/plumbing/transport/test" . "gopkg.in/check.v1" ) type ReceivePackSuite struct { BaseSuite - test.ReceivePackSuite } var _ = Suite(&ReceivePackSuite{}) @@ -20,7 +18,7 @@ func (s *ReceivePackSuite) SetUpSuite(c *C) { } func (s *ReceivePackSuite) SetUpTest(c *C) { - s.prepareRepositories(c, &s.Endpoint, &s.EmptyEndpoint, &s.NonExistentEndpoint) + s.prepareRepositories(c) } func (s *ReceivePackSuite) TearDownTest(c *C) { diff --git a/plumbing/transport/server/server.go b/plumbing/transport/server/server.go index be36de5..2357bd6 100644 --- a/plumbing/transport/server/server.go +++ b/plumbing/transport/server/server.go @@ -43,7 +43,7 @@ func NewClient(loader Loader) transport.Transport { } } -func (s *server) NewUploadPackSession(ep transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) { +func (s *server) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) { sto, err := s.loader.Load(ep) if err != nil { return nil, err @@ -52,7 +52,7 @@ func (s *server) NewUploadPackSession(ep transport.Endpoint, auth transport.Auth return s.handler.NewUploadPackSession(sto) } -func (s *server) NewReceivePackSession(ep transport.Endpoint, auth transport.AuthMethod) (transport.ReceivePackSession, error) { +func (s *server) NewReceivePackSession(ep *transport.Endpoint, auth transport.AuthMethod) (transport.ReceivePackSession, error) { sto, err := s.loader.Load(ep) if err != nil { return nil, err @@ -165,7 +165,8 @@ func (s *upSession) UploadPack(ctx context.Context, req *packp.UploadPackRequest pr, pw := io.Pipe() e := packfile.NewEncoder(pw, s.storer, false) go func() { - _, err := e.Encode(objs) + // TODO: plumb through a pack window. + _, err := e.Encode(objs, 10) pw.CloseWithError(err) }() diff --git a/plumbing/transport/server/server_test.go b/plumbing/transport/server/server_test.go index 7912768..33d74d1 100644 --- a/plumbing/transport/server/server_test.go +++ b/plumbing/transport/server/server_test.go @@ -3,20 +3,23 @@ package server_test import ( "testing" - "github.com/src-d/go-git-fixtures" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/client" "gopkg.in/src-d/go-git.v4/plumbing/transport/server" + "gopkg.in/src-d/go-git.v4/plumbing/transport/test" "gopkg.in/src-d/go-git.v4/storage/filesystem" "gopkg.in/src-d/go-git.v4/storage/memory" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) func Test(t *testing.T) { TestingT(t) } type BaseSuite struct { fixtures.Suite + test.ReceivePackSuite + loader server.MapLoader client transport.Transport clientBackup transport.Transport @@ -44,27 +47,19 @@ func (s *BaseSuite) TearDownSuite(c *C) { } } -func (s *BaseSuite) prepareRepositories(c *C, basic *transport.Endpoint, - empty *transport.Endpoint, nonExistent *transport.Endpoint) { +func (s *BaseSuite) prepareRepositories(c *C) { + var err error - f := fixtures.Basic().One() - fs := f.DotGit() - path := fs.Root() - ep, err := transport.NewEndpoint(path) + fs := fixtures.Basic().One().DotGit() + s.Endpoint, err = transport.NewEndpoint(fs.Root()) c.Assert(err, IsNil) - *basic = ep - sto, err := filesystem.NewStorage(fs) + s.loader[s.Endpoint.String()], err = filesystem.NewStorage(fs) c.Assert(err, IsNil) - s.loader[ep.String()] = sto - path = "/empty.git" - ep, err = transport.NewEndpoint(path) + s.EmptyEndpoint, err = transport.NewEndpoint("/empty.git") c.Assert(err, IsNil) - *empty = ep - s.loader[ep.String()] = memory.NewStorage() + s.loader[s.EmptyEndpoint.String()] = memory.NewStorage() - path = "/non-existent.git" - ep, err = transport.NewEndpoint(path) + s.NonExistentEndpoint, err = transport.NewEndpoint("/non-existent.git") c.Assert(err, IsNil) - *nonExistent = ep } diff --git a/plumbing/transport/server/upload_pack_test.go b/plumbing/transport/server/upload_pack_test.go index 99473d3..f252a75 100644 --- a/plumbing/transport/server/upload_pack_test.go +++ b/plumbing/transport/server/upload_pack_test.go @@ -2,34 +2,23 @@ package server_test import ( "gopkg.in/src-d/go-git.v4/plumbing/transport" - "gopkg.in/src-d/go-git.v4/plumbing/transport/test" . "gopkg.in/check.v1" ) type UploadPackSuite struct { BaseSuite - test.UploadPackSuite } var _ = Suite(&UploadPackSuite{}) func (s *UploadPackSuite) SetUpSuite(c *C) { s.BaseSuite.SetUpSuite(c) - s.UploadPackSuite.Client = s.client + s.Client = s.client } func (s *UploadPackSuite) SetUpTest(c *C) { - s.prepareRepositories(c, &s.Endpoint, &s.EmptyEndpoint, &s.NonExistentEndpoint) -} - -// Overwritten, it's not an error in server-side. -func (s *UploadPackSuite) TestAdvertisedReferencesEmpty(c *C) { - r, err := s.Client.NewUploadPackSession(s.EmptyEndpoint, s.EmptyAuth) - c.Assert(err, IsNil) - ar, err := r.AdvertisedReferences() - c.Assert(err, IsNil) - c.Assert(len(ar.References), Equals, 0) + s.prepareRepositories(c) } // Overwritten, server returns error earlier. @@ -57,5 +46,5 @@ func (s *ClientLikeUploadPackSuite) SetUpSuite(c *C) { } func (s *ClientLikeUploadPackSuite) TestAdvertisedReferencesEmpty(c *C) { - s.UploadPackSuite.UploadPackSuite.TestAdvertisedReferencesEmpty(c) + s.UploadPackSuite.TestAdvertisedReferencesEmpty(c) } diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go index baae181..a092b29 100644 --- a/plumbing/transport/ssh/auth_method.go +++ b/plumbing/transport/ssh/auth_method.go @@ -25,8 +25,9 @@ const DefaultUsername = "git" // configuration needed to establish an ssh connection. type AuthMethod interface { transport.AuthMethod - clientConfig() *ssh.ClientConfig - hostKeyCallback() (ssh.HostKeyCallback, error) + // ClientConfig should return a valid ssh.ClientConfig to be used to create + // a connection to the SSH server. + ClientConfig() (*ssh.ClientConfig, error) } // The names of the AuthMethod implementations. To be returned by the @@ -45,7 +46,7 @@ const ( type KeyboardInteractive struct { User string Challenge ssh.KeyboardInteractiveChallenge - baseAuthMethod + HostKeyCallbackHelper } func (a *KeyboardInteractive) Name() string { @@ -56,18 +57,20 @@ func (a *KeyboardInteractive) String() string { return fmt.Sprintf("user: %s, name: %s", a.User, a.Name()) } -func (a *KeyboardInteractive) clientConfig() *ssh.ClientConfig { - return &ssh.ClientConfig{ +func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) { + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, - Auth: []ssh.AuthMethod{ssh.KeyboardInteractiveChallenge(a.Challenge)}, - } + Auth: []ssh.AuthMethod{ + ssh.KeyboardInteractiveChallenge(a.Challenge), + }, + }) } // Password implements AuthMethod by using the given password. type Password struct { - User string - Pass string - baseAuthMethod + User string + Password string + HostKeyCallbackHelper } func (a *Password) Name() string { @@ -78,11 +81,11 @@ func (a *Password) String() string { return fmt.Sprintf("user: %s, name: %s", a.User, a.Name()) } -func (a *Password) clientConfig() *ssh.ClientConfig { - return &ssh.ClientConfig{ +func (a *Password) ClientConfig() (*ssh.ClientConfig, error) { + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, - Auth: []ssh.AuthMethod{ssh.Password(a.Pass)}, - } + Auth: []ssh.AuthMethod{ssh.Password(a.Password)}, + }) } // PasswordCallback implements AuthMethod by using a callback @@ -90,7 +93,7 @@ func (a *Password) clientConfig() *ssh.ClientConfig { type PasswordCallback struct { User string Callback func() (pass string, err error) - baseAuthMethod + HostKeyCallbackHelper } func (a *PasswordCallback) Name() string { @@ -101,25 +104,25 @@ func (a *PasswordCallback) String() string { return fmt.Sprintf("user: %s, name: %s", a.User, a.Name()) } -func (a *PasswordCallback) clientConfig() *ssh.ClientConfig { - return &ssh.ClientConfig{ +func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) { + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)}, - } + }) } // PublicKeys implements AuthMethod by using the given key pairs. type PublicKeys struct { User string Signer ssh.Signer - baseAuthMethod + HostKeyCallbackHelper } // NewPublicKeys returns a PublicKeys from a PEM encoded private key. An // encryption password should be given if the pemBytes contains a password // encrypted PEM block otherwise password should be empty. It supports RSA // (PKCS#1), DSA (OpenSSL), and ECDSA private keys. -func NewPublicKeys(user string, pemBytes []byte, password string) (AuthMethod, error) { +func NewPublicKeys(user string, pemBytes []byte, password string) (*PublicKeys, error) { block, _ := pem.Decode(pemBytes) if x509.IsEncryptedPEMBlock(block) { key, err := x509.DecryptPEMBlock(block, []byte(password)) @@ -142,7 +145,7 @@ func NewPublicKeys(user string, pemBytes []byte, password string) (AuthMethod, e // NewPublicKeysFromFile returns a PublicKeys from a file containing a PEM // encoded private key. An encryption password should be given if the pemBytes // contains a password encrypted PEM block otherwise password should be empty. -func NewPublicKeysFromFile(user, pemFile, password string) (AuthMethod, error) { +func NewPublicKeysFromFile(user, pemFile, password string) (*PublicKeys, error) { bytes, err := ioutil.ReadFile(pemFile) if err != nil { return nil, err @@ -159,11 +162,11 @@ func (a *PublicKeys) String() string { return fmt.Sprintf("user: %s, name: %s", a.User, a.Name()) } -func (a *PublicKeys) clientConfig() *ssh.ClientConfig { - return &ssh.ClientConfig{ +func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) { + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)}, - } + }) } func username() (string, error) { @@ -173,9 +176,11 @@ func username() (string, error) { } else { username = os.Getenv("USER") } + if username == "" { return "", errors.New("failed to get username") } + return username, nil } @@ -184,13 +189,13 @@ func username() (string, error) { type PublicKeysCallback struct { User string Callback func() (signers []ssh.Signer, err error) - baseAuthMethod + HostKeyCallbackHelper } // NewSSHAgentAuth returns a PublicKeysCallback based on a SSH agent, it opens // a pipe with the SSH agent and uses the pipe as the implementer of the public // key callback function. -func NewSSHAgentAuth(u string) (AuthMethod, error) { +func NewSSHAgentAuth(u string) (*PublicKeysCallback, error) { var err error if u == "" { u, err = username() @@ -218,11 +223,11 @@ func (a *PublicKeysCallback) String() string { return fmt.Sprintf("user: %s, name: %s", a.User, a.Name()) } -func (a *PublicKeysCallback) clientConfig() *ssh.ClientConfig { - return &ssh.ClientConfig{ +func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) { + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)}, - } + }) } // NewKnownHostsCallback returns ssh.HostKeyCallback based on a file based on a @@ -287,17 +292,26 @@ func filterKnownHostsFiles(files ...string) ([]string, error) { return out, nil } -type baseAuthMethod struct { +// HostKeyCallbackHelper is a helper that provides common functionality to +// configure HostKeyCallback into a ssh.ClientConfig. +type HostKeyCallbackHelper struct { // HostKeyCallback is the function type used for verifying server keys. - // If nil default callback will be create using NewKnownHostsHostKeyCallback + // If nil default callback will be create using NewKnownHostsCallback // without argument. HostKeyCallback ssh.HostKeyCallback } -func (m *baseAuthMethod) hostKeyCallback() (ssh.HostKeyCallback, error) { +// SetHostKeyCallback sets the field HostKeyCallback in the given cfg. If +// HostKeyCallback is empty a default callback is created using +// NewKnownHostsCallback. +func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) { + var err error if m.HostKeyCallback == nil { - return NewKnownHostsCallback() + if m.HostKeyCallback, err = NewKnownHostsCallback(); err != nil { + return cfg, err + } } - return m.HostKeyCallback, nil + cfg.HostKeyCallback = m.HostKeyCallback + return cfg, nil } diff --git a/plumbing/transport/ssh/auth_method_test.go b/plumbing/transport/ssh/auth_method_test.go index 2ee5100..1e77ca0 100644 --- a/plumbing/transport/ssh/auth_method_test.go +++ b/plumbing/transport/ssh/auth_method_test.go @@ -32,16 +32,16 @@ func (s *SuiteCommon) TestKeyboardInteractiveString(c *C) { func (s *SuiteCommon) TestPasswordName(c *C) { a := &Password{ - User: "test", - Pass: "", + User: "test", + Password: "", } c.Assert(a.Name(), Equals, PasswordName) } func (s *SuiteCommon) TestPasswordString(c *C) { a := &Password{ - User: "test", - Pass: "", + User: "test", + Password: "", } c.Assert(a.String(), Equals, fmt.Sprintf("user: test, name: %s", PasswordName)) } diff --git a/plumbing/transport/ssh/common.go b/plumbing/transport/ssh/common.go index af79dfb..f5bc9a7 100644 --- a/plumbing/transport/ssh/common.go +++ b/plumbing/transport/ssh/common.go @@ -31,7 +31,7 @@ type runner struct { config *ssh.ClientConfig } -func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod) (common.Command, error) { +func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod) (common.Command, error) { c := &command{command: cmd, endpoint: ep, config: r.config} if auth != nil { c.setAuth(auth) @@ -47,7 +47,7 @@ type command struct { *ssh.Session connected bool command string - endpoint transport.Endpoint + endpoint *transport.Endpoint client *ssh.Client auth AuthMethod config *ssh.ClientConfig @@ -98,8 +98,7 @@ func (c *command) connect() error { } var err error - config := c.auth.clientConfig() - config.HostKeyCallback, err = c.auth.hostKeyCallback() + config, err := c.auth.ClientConfig() if err != nil { return err } @@ -122,8 +121,8 @@ func (c *command) connect() error { } func (c *command) getHostWithPort() string { - host := c.endpoint.Host() - port := c.endpoint.Port() + host := c.endpoint.Host + port := c.endpoint.Port if port <= 0 { port = DefaultPort } @@ -133,12 +132,12 @@ func (c *command) getHostWithPort() string { func (c *command) setAuthFromEndpoint() error { var err error - c.auth, err = DefaultAuthBuilder(c.endpoint.User()) + c.auth, err = DefaultAuthBuilder(c.endpoint.User) return err } -func endpointToCommand(cmd string, ep transport.Endpoint) string { - return fmt.Sprintf("%s '%s'", cmd, ep.Path()) +func endpointToCommand(cmd string, ep *transport.Endpoint) string { + return fmt.Sprintf("%s '%s'", cmd, ep.Path) } func overrideConfig(overrides *ssh.ClientConfig, c *ssh.ClientConfig) { @@ -154,14 +153,8 @@ func overrideConfig(overrides *ssh.ClientConfig, c *ssh.ClientConfig) { f := t.Field(i) vcf := vc.FieldByName(f.Name) vof := vo.FieldByName(f.Name) - if isZeroValue(vcf) { - vcf.Set(vof) - } + vcf.Set(vof) } *c = vc.Interface().(ssh.ClientConfig) } - -func isZeroValue(v reflect.Value) bool { - return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) -} diff --git a/plumbing/transport/ssh/common_test.go b/plumbing/transport/ssh/common_test.go index 1b07eee..5315e28 100644 --- a/plumbing/transport/ssh/common_test.go +++ b/plumbing/transport/ssh/common_test.go @@ -37,5 +37,5 @@ func (s *SuiteCommon) TestOverrideConfigKeep(c *C) { } overrideConfig(config, target) - c.Assert(target.User, Equals, "bar") + c.Assert(target.User, Equals, "foo") } diff --git a/plumbing/transport/ssh/upload_pack_test.go b/plumbing/transport/ssh/upload_pack_test.go index cb9baa5..56d1601 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" + "gopkg.in/src-d/go-git-fixtures.v3" + 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 } diff --git a/plumbing/transport/test/receive_pack.go b/plumbing/transport/test/receive_pack.go index d29d9ca..0f3352c 100644 --- a/plumbing/transport/test/receive_pack.go +++ b/plumbing/transport/test/receive_pack.go @@ -9,7 +9,6 @@ import ( "io" "io/ioutil" - "github.com/src-d/go-git-fixtures" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/format/packfile" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" @@ -18,12 +17,13 @@ import ( "gopkg.in/src-d/go-git.v4/storage/memory" . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git-fixtures.v3" ) type ReceivePackSuite struct { - Endpoint transport.Endpoint - EmptyEndpoint transport.Endpoint - NonExistentEndpoint transport.Endpoint + Endpoint *transport.Endpoint + EmptyEndpoint *transport.Endpoint + NonExistentEndpoint *transport.Endpoint EmptyAuth transport.AuthMethod Client transport.Transport } @@ -213,7 +213,7 @@ func (s *ReceivePackSuite) TestSendPackOnNonEmptyWithReportStatusWithError(c *C) s.checkRemoteHead(c, endpoint, fixture.Head) } -func (s *ReceivePackSuite) receivePackNoCheck(c *C, ep transport.Endpoint, +func (s *ReceivePackSuite) receivePackNoCheck(c *C, ep *transport.Endpoint, req *packp.ReferenceUpdateRequest, fixture *fixtures.Fixture, callAdvertisedReferences bool) (*packp.ReportStatus, error) { url := "" @@ -245,7 +245,7 @@ func (s *ReceivePackSuite) receivePackNoCheck(c *C, ep transport.Endpoint, return r.ReceivePack(context.Background(), req) } -func (s *ReceivePackSuite) receivePack(c *C, ep transport.Endpoint, +func (s *ReceivePackSuite) receivePack(c *C, ep *transport.Endpoint, req *packp.ReferenceUpdateRequest, fixture *fixtures.Fixture, callAdvertisedReferences bool) { @@ -269,11 +269,11 @@ func (s *ReceivePackSuite) receivePack(c *C, ep transport.Endpoint, } } -func (s *ReceivePackSuite) checkRemoteHead(c *C, ep transport.Endpoint, head plumbing.Hash) { +func (s *ReceivePackSuite) checkRemoteHead(c *C, ep *transport.Endpoint, head plumbing.Hash) { s.checkRemoteReference(c, ep, "refs/heads/master", head) } -func (s *ReceivePackSuite) checkRemoteReference(c *C, ep transport.Endpoint, +func (s *ReceivePackSuite) checkRemoteReference(c *C, ep *transport.Endpoint, refName string, head plumbing.Hash) { r, err := s.Client.NewUploadPackSession(ep, s.EmptyAuth) @@ -348,7 +348,7 @@ func (s *ReceivePackSuite) testSendPackDeleteReference(c *C) { func (s *ReceivePackSuite) emptyPackfile() io.ReadCloser { var buf bytes.Buffer e := packfile.NewEncoder(&buf, memory.NewStorage(), false) - _, err := e.Encode(nil) + _, err := e.Encode(nil, 10) if err != nil { panic(err) } diff --git a/plumbing/transport/test/upload_pack.go b/plumbing/transport/test/upload_pack.go index b3acc4f..70e4e56 100644 --- a/plumbing/transport/test/upload_pack.go +++ b/plumbing/transport/test/upload_pack.go @@ -21,9 +21,9 @@ import ( ) type UploadPackSuite struct { - Endpoint transport.Endpoint - EmptyEndpoint transport.Endpoint - NonExistentEndpoint transport.Endpoint + Endpoint *transport.Endpoint + EmptyEndpoint *transport.Endpoint + NonExistentEndpoint *transport.Endpoint EmptyAuth transport.AuthMethod Client transport.Transport } |