From 9bdc0cc27d6d5d35f2830c9366a366c9dd09be0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Mon, 2 Sep 2024 22:21:54 +0200 Subject: [PATCH 1/6] Fix ssh context not working --- pkg/commands/docker.go | 22 ++++++++++++++++------ pkg/commands/ssh/ssh.go | 7 ++++--- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go index e96c96533..234776cdf 100644 --- a/pkg/commands/docker.go +++ b/pkg/commands/docker.go @@ -69,19 +69,29 @@ func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject { return defaultObj } -// NewDockerCommand it runs docker commands +// NewDockerCommand creates a DockerCommand struct that will be able to run docker commands. func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) { - tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost() + dockerHost, err := determineDockerHost() if err != nil { - ogLog.Fatal(err) + ogLog.Printf("> could not determine host %v", err) } - dockerHost, err := determineDockerHost() + tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost(dockerHost) if err != nil { - ogLog.Printf("> could not determine host %v", err) + ogLog.Fatal(err) + } + + clientOpts := []client.Opt{ + client.FromEnv, + client.WithVersion(APIVersion), + } + // For an ssh connection the DOCKER_HOST env variable has been overridden. + // Discard the previously determined dockerHost + if !strings.HasPrefix(dockerHost, "ssh://") { + clientOpts = append(clientOpts, client.WithHost(dockerHost)) } - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(APIVersion), client.WithHost(dockerHost)) + cli, err := client.NewClientWithOpts(clientOpts...) if err != nil { ogLog.Fatal(err) } diff --git a/pkg/commands/ssh/ssh.go b/pkg/commands/ssh/ssh.go index ecc840459..14d13f652 100644 --- a/pkg/commands/ssh/ssh.go +++ b/pkg/commands/ssh/ssh.go @@ -44,10 +44,11 @@ func NewSSHHandler(oSCommand CmdKiller) *SSHHandler { // HandleSSHDockerHost overrides the DOCKER_HOST environment variable // to point towards a local unix socket tunneled over SSH to the specified ssh host. -func (self *SSHHandler) HandleSSHDockerHost() (io.Closer, error) { +func (self *SSHHandler) HandleSSHDockerHost(dockerHost string) (io.Closer, error) { const key = "DOCKER_HOST" ctx := context.Background() - u, err := url.Parse(self.getenv(key)) + + u, err := url.Parse(dockerHost) if err != nil { // if no or an invalid docker host is specified, continue nominally return noopCloser{}, nil @@ -55,7 +56,7 @@ func (self *SSHHandler) HandleSSHDockerHost() (io.Closer, error) { // if the docker host scheme is "ssh", forward the docker socket before creating the client if u.Scheme == "ssh" { - tunnel, err := self.createDockerHostTunnel(ctx, u.Host) + tunnel, err := self.createDockerHostTunnel(ctx, u.String()) if err != nil { return noopCloser{}, fmt.Errorf("tunnel ssh docker host: %w", err) } From 21e18fe7a5587931c0cb1728415a673e7b2bcd02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Mon, 2 Sep 2024 22:27:48 +0200 Subject: [PATCH 2/6] Update comment --- pkg/commands/docker.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go index 234776cdf..61d16879d 100644 --- a/pkg/commands/docker.go +++ b/pkg/commands/docker.go @@ -69,7 +69,8 @@ func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject { return defaultObj } -// NewDockerCommand creates a DockerCommand struct that will be able to run docker commands. +// NewDockerCommand creates a DockerCommand struct that wraps the docker client. +// Able to run docker commands. And handles func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) { dockerHost, err := determineDockerHost() if err != nil { From 94494348d032d663db6372cc5143d87d3b8cbe1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Sun, 8 Sep 2024 00:18:39 +0200 Subject: [PATCH 3/6] update test to reflect change to the queried ssh host --- pkg/commands/ssh/ssh_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/ssh/ssh_test.go b/pkg/commands/ssh/ssh_test.go index f1ea1b90d..3be7bb5e9 100644 --- a/pkg/commands/ssh/ssh_test.go +++ b/pkg/commands/ssh/ssh_test.go @@ -64,7 +64,7 @@ func TestSSHHandlerHandleSSHDockerHost(t *testing.T) { startCmdCount := 0 startCmd := func(cmd *exec.Cmd) error { - assert.EqualValues(t, []string{"ssh", "-L", "/tmp/lazydocker-ssh-tunnel-12345/dockerhost.sock:/var/run/docker.sock", "192.168.5.178", "-N"}, cmd.Args) + assert.EqualValues(t, []string{"ssh", "-L", "/tmp/lazydocker-ssh-tunnel-12345/dockerhost.sock:/var/run/docker.sock", s.envVarValue, "-N"}, cmd.Args) startCmdCount++ @@ -91,7 +91,7 @@ func TestSSHHandlerHandleSSHDockerHost(t *testing.T) { setenv: setenv, } - _, err := handler.HandleSSHDockerHost() + _, err := handler.HandleSSHDockerHost(s.envVarValue) assert.NoError(t, err) assert.Equal(t, s.expectedDialContextCount, dialContextCount) From efd7f4be54f1b94425adae26fca02c5040ba24b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Sat, 3 May 2025 20:42:45 +0200 Subject: [PATCH 4/6] finish comment --- pkg/commands/docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go index 0367ab24d..43beaaf9b 100644 --- a/pkg/commands/docker.go +++ b/pkg/commands/docker.go @@ -72,7 +72,7 @@ func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject { } // NewDockerCommand creates a DockerCommand struct that wraps the docker client. -// Able to run docker commands. And handles +// Able to run docker commands. And handles SSH docker hosts func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) { dockerHost, err := determineDockerHost() if err != nil { From 6acacc7a3e5f6a15dea497db4f10282f22ef3c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Sat, 3 May 2025 21:01:17 +0200 Subject: [PATCH 5/6] edit lazydocker's tunnel filename to make it more coherent with the rest of the structure --- pkg/commands/ssh/ssh.go | 2 +- pkg/commands/ssh/ssh_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/ssh/ssh.go b/pkg/commands/ssh/ssh.go index 14d13f652..4e78405d3 100644 --- a/pkg/commands/ssh/ssh.go +++ b/pkg/commands/ssh/ssh.go @@ -87,7 +87,7 @@ func (t *tunneledDockerHost) Close() error { } func (self *SSHHandler) createDockerHostTunnel(ctx context.Context, remoteHost string) (*tunneledDockerHost, error) { - socketDir, err := self.tempDir("/tmp", "lazydocker-sshtunnel-") + socketDir, err := self.tempDir("/tmp", "lazydocker-ssh-tunnel-") if err != nil { return nil, fmt.Errorf("create ssh tunnel tmp file: %w", err) } diff --git a/pkg/commands/ssh/ssh_test.go b/pkg/commands/ssh/ssh_test.go index 3be7bb5e9..72cc4fc29 100644 --- a/pkg/commands/ssh/ssh_test.go +++ b/pkg/commands/ssh/ssh_test.go @@ -51,7 +51,7 @@ func TestSSHHandlerHandleSSHDockerHost(t *testing.T) { tempDir := func(dir string, pattern string) (string, error) { assert.Equal(t, "/tmp", dir) - assert.Equal(t, "lazydocker-sshtunnel-", pattern) + assert.Equal(t, "lazydocker-ssh-tunnel-", pattern) return "/tmp/lazydocker-ssh-tunnel-12345", nil } From 099ae97e687b12d4aea53166504623e106f0ca17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Sun, 4 May 2025 13:39:11 +0200 Subject: [PATCH 6/6] rewrote ssh handling to make it clearer --- pkg/commands/docker.go | 18 +++++++++--------- pkg/commands/ssh/ssh.go | 28 ++++++++++++++++------------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go index 43beaaf9b..a6e702d73 100644 --- a/pkg/commands/docker.go +++ b/pkg/commands/docker.go @@ -72,26 +72,26 @@ func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject { } // NewDockerCommand creates a DockerCommand struct that wraps the docker client. -// Able to run docker commands. And handles SSH docker hosts +// Able to run docker commands and handles SSH docker hosts func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) { dockerHost, err := determineDockerHost() if err != nil { ogLog.Printf("> could not determine host %v", err) } - tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost(dockerHost) + tunnelResult, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost(dockerHost) if err != nil { ogLog.Fatal(err) } + // If we created a tunnel to the remote ssh host, we then override the dockerhost to point to the tunnel + if tunnelResult.Created { + dockerHost = tunnelResult.SocketPath + } clientOpts := []client.Opt{ - client.FromEnv, + client.WithTLSClientConfigFromEnv(), client.WithVersion(APIVersion), - } - // For an ssh connection the DOCKER_HOST env variable has been overridden. - // Discard the previously determined dockerHost - if !strings.HasPrefix(dockerHost, "ssh://") { - clientOpts = append(clientOpts, client.WithHost(dockerHost)) + client.WithHost(dockerHost), } cli, err := client.NewClientWithOpts(clientOpts...) @@ -107,7 +107,7 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat Client: cli, ErrorChan: errorChan, InDockerComposeProject: true, - Closers: []io.Closer{tunnelCloser}, + Closers: []io.Closer{tunnelResult.Closer}, } dockerCommand.setDockerComposeCommand(config) diff --git a/pkg/commands/ssh/ssh.go b/pkg/commands/ssh/ssh.go index 4e78405d3..fb85953c7 100644 --- a/pkg/commands/ssh/ssh.go +++ b/pkg/commands/ssh/ssh.go @@ -42,32 +42,36 @@ func NewSSHHandler(oSCommand CmdKiller) *SSHHandler { } } +type TunnelResult struct { + Closer io.Closer + SocketPath string + Created bool +} + // HandleSSHDockerHost overrides the DOCKER_HOST environment variable // to point towards a local unix socket tunneled over SSH to the specified ssh host. -func (self *SSHHandler) HandleSSHDockerHost(dockerHost string) (io.Closer, error) { - const key = "DOCKER_HOST" - ctx := context.Background() - +func (self *SSHHandler) HandleSSHDockerHost(dockerHost string) (TunnelResult, error) { u, err := url.Parse(dockerHost) if err != nil { // if no or an invalid docker host is specified, continue nominally - return noopCloser{}, nil + return TunnelResult{Closer: noopCloser{}}, nil } // if the docker host scheme is "ssh", forward the docker socket before creating the client if u.Scheme == "ssh" { + ctx := context.Background() tunnel, err := self.createDockerHostTunnel(ctx, u.String()) if err != nil { - return noopCloser{}, fmt.Errorf("tunnel ssh docker host: %w", err) - } - err = self.setenv(key, tunnel.socketPath) - if err != nil { - return noopCloser{}, fmt.Errorf("override DOCKER_HOST to tunneled socket: %w", err) + return TunnelResult{Closer: noopCloser{}}, fmt.Errorf("tunnel ssh docker host: %w", err) } - return tunnel, nil + return TunnelResult{ + Closer: tunnel, + SocketPath: tunnel.socketPath, + Created: true, + }, nil } - return noopCloser{}, nil + return TunnelResult{Closer: noopCloser{}}, nil } type noopCloser struct{}