package sshx import ( "errors" "io" "net" "strings" "testing" "time" ) // TestDialAPICommand pins the command the tunnel runs on the source host: it is // the same one `docker -H ssh://…` uses, and the sudo / custom binary settings // have to reach it. func TestDialAPICommand(t *testing.T) { for _, tc := range []struct { name string rd *RemoteDocker want string }{ {"plain", &RemoteDocker{binary: "docker"}, "docker system dial-stdio"}, {"sudo", &RemoteDocker{binary: "docker", sudo: true}, "sudo -n docker system dial-stdio"}, {"podman", &RemoteDocker{binary: "podman"}, "podman system dial-stdio"}, {"path with a space", &RemoteDocker{binary: "/opt/my docker/bin/docker"}, "'/opt/my docker/bin/docker' system dial-stdio"}, } { t.Run(tc.name, func(t *testing.T) { if got := tc.rd.Cmd("system", "dial-stdio"); got != tc.want { t.Fatalf("command = %q, want %q", got, tc.want) } }) } } // TestAPIConnSurfacesRemoteStderr covers the failure that would otherwise reach // the Docker client as a bare EOF: the remote docker printing a reason and // exiting. func TestAPIConnSurfacesRemoteStderr(t *testing.T) { errBuf := &syncBuffer{} errBuf.Write([]byte("docker: 'system dial-stdio' is not a docker command\n")) c := &apiConn{ stdout: strings.NewReader(""), stdin: nopWriteCloser{io.Discard}, stderr: errBuf, } _, err := c.Read(make([]byte, 8)) if err == nil { t.Fatal("a closed stream with remote stderr should be an error") } if !strings.Contains(err.Error(), "is not a docker command") { t.Fatalf("error = %v, want the remote stderr in it", err) } // Without stderr the plain EOF must survive, or the HTTP transport cannot // tell a finished response from a broken one. quiet := &apiConn{stdout: strings.NewReader(""), stdin: nopWriteCloser{io.Discard}, stderr: &syncBuffer{}} if _, err := quiet.Read(make([]byte, 8)); !errors.Is(err, io.EOF) { t.Fatalf("error = %v, want io.EOF", err) } } func TestAPIConnDeadlinesAreNoops(t *testing.T) { var c net.Conn = &apiConn{stdout: strings.NewReader(""), stdin: nopWriteCloser{io.Discard}, stderr: &syncBuffer{}} now := time.Now() if err := c.SetDeadline(now); err != nil { t.Fatalf("SetDeadline: %v", err) } if err := c.SetReadDeadline(now); err != nil { t.Fatalf("SetReadDeadline: %v", err) } if err := c.SetWriteDeadline(now); err != nil { t.Fatalf("SetWriteDeadline: %v", err) } if c.RemoteAddr().Network() != "ssh" { t.Fatalf("network = %q, want ssh", c.RemoteAddr().Network()) } } type nopWriteCloser struct{ io.Writer } func (nopWriteCloser) Close() error { return nil }