Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions cli/connhelper/connhelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
// ConnectionHelper allows to connect to a remote host with custom stream provider binary.
type ConnectionHelper struct {
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
Host string // dummy URL used for HTTP requests. e.g. "http://docker"
Host string // dummy URL used for HTTP requests. e.g. "http://docker". For ssh connections, the dummy URL includes the remote hostname so that connection error messages refer to the host the user intends to connect to.
}

// GetConnectionHelper returns Docker-specific connection helper for the given URL.
Expand Down Expand Up @@ -61,11 +61,21 @@ func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper
if err != nil {
return nil, err
}

// The Host is a dummy URL used for the API client to build requests;
// the actual connection is made through the Dialer above. Using the
// remote hostname in the dummy URL makes sure that error messages
// (e.g. "Cannot connect to the Docker daemon at ...") mention the
// host the user configured, instead of a non-descriptive dummy domain.
host := sp.Host
if sp.Port != "" && sp.Port != "22" {
host = net.JoinHostPort(host, sp.Port)
}
return &ConnectionHelper{
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
return commandconn.New(ctx, "ssh", sshArgs...)
},
Host: "http://docker.example.com",
Host: "http://" + host,
}, nil
}
// Future version may support plugins via ~/.docker/config.json. e.g. "dind"
Expand Down
42 changes: 42 additions & 0 deletions cli/connhelper/connhelper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,48 @@ func TestSSHFlags(t *testing.T) {
}
}

func TestConnectionHelperHostIncludesRemoteHost(t *testing.T) {
testCases := []struct {
name string
daemonURL string
expected string
}{
{
name: "ssh with user and default port",
daemonURL: "ssh://user@myserver.local",
expected: "http://myserver.local",
},
{
name: "ssh with custom port",
daemonURL: "ssh://user@myserver.local:2222",
expected: "http://myserver.local:2222",
},
{
name: "ssh with explicit default port",
daemonURL: "ssh://myserver.local:22",
expected: "http://myserver.local",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
helper, err := GetConnectionHelper(tc.daemonURL)
assert.NilError(t, err)
assert.Assert(t, helper != nil)
assert.Equal(t, helper.Host, tc.expected,
"dummy Host URL must include the remote hostname so connection errors point to the daemon the user configured (docker/cli#5604)")
assert.Assert(t, helper.Dialer != nil)
})
}
}

func TestGetCommandConnectionHelperHost(t *testing.T) {
helper, err := GetCommandConnectionHelper("fake-command")
assert.NilError(t, err)
assert.Assert(t, helper != nil)
assert.Equal(t, helper.Host, "http://docker.example.com")
}

func TestDisablePseudoTerminalAllocation(t *testing.T) {
testCases := []struct {
name string
Expand Down