This commit is contained in:
PÉAU Clément 2025-10-06 07:23:53 +00:00 committed by GitHub
commit a8693ed8c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 53 additions and 101 deletions

View file

@ -10,15 +10,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Unshallow repo - name: Unshallow repo
run: git fetch --prune --unshallow run: git fetch --prune --unshallow
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v1 uses: actions/setup-go@v4
with: with:
go-version: 1.21.x go-version: 1.21.x
- name: Run goreleaser - name: Run goreleaser
uses: goreleaser/goreleaser-action@v1 uses: goreleaser/goreleaser-action@v5
with: with:
distribution: goreleaser distribution: goreleaser
version: v1.17.2 version: v1.17.2

View file

@ -23,13 +23,13 @@ jobs:
GOFLAGS: -mod=vendor GOFLAGS: -mod=vendor
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v1 uses: actions/setup-go@v4
with: with:
go-version: 1.21.x go-version: 1.21.x
- name: Cache build - name: Cache build
uses: actions/cache@v1 uses: actions/cache@v4
with: with:
path: ~/.cache/go-build path: ~/.cache/go-build
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
@ -45,13 +45,13 @@ jobs:
GOARCH: amd64 GOARCH: amd64
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v1 uses: actions/setup-go@v4
with: with:
go-version: 1.21.x go-version: 1.21.x
- name: Cache build - name: Cache build
uses: actions/cache@v1 uses: actions/cache@v4
with: with:
path: ~/.cache/go-build path: ~/.cache/go-build
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
@ -73,13 +73,13 @@ jobs:
GOARCH: amd64 GOARCH: amd64
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v1 uses: actions/setup-go@v4
with: with:
go-version: 1.21.x go-version: 1.21.x
- name: Cache build - name: Cache build
uses: actions/cache@v1 uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/go-build ~/.cache/go-build
@ -100,13 +100,13 @@ jobs:
GOFLAGS: -mod=vendor GOFLAGS: -mod=vendor
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v1 uses: actions/setup-go@v4
with: with:
go-version: 1.21.x go-version: 1.21.x
- name: Cache build - name: Cache build
uses: actions/cache@v1 uses: actions/cache@v4
with: with:
path: ~/.cache/go-build path: ~/.cache/go-build
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test

View file

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout 🛎️ - name: Checkout 🛎️
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Generate Sponsors 💖 - name: Generate Sponsors 💖
uses: JamesIves/github-sponsors-readme-action@v1.2.2 uses: JamesIves/github-sponsors-readme-action@v1.2.2

View file

@ -1,13 +1,11 @@
linters: linters:
disable:
- structcheck # gives false positives
enable: enable:
- gofumpt - gofumpt
- thelper - thelper
- goimports - goimports
- tparallel - tparallel
- wastedassign - wastedassign
- exportloopref - copyloopvar
- unparam - unparam
- prealloc - prealloc
- unconvert - unconvert

File diff suppressed because one or more lines are too long

View file

@ -71,33 +71,30 @@ func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
return defaultObj return defaultObj
} }
// NewDockerCommand it runs docker commands // NewDockerCommand creates a DockerCommand struct that wraps the docker client.
// 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) { func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
dockerHost, err := determineDockerHost() dockerHost, err := determineDockerHost()
if err != nil { if err != nil {
ogLog.Printf("> could not determine host %v", err) ogLog.Printf("> could not determine host %v", err)
} }
// NOTE: Inject the determined docker host to the environment. This allows the tunnelResult, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost(dockerHost)
// `SSHHandler.HandleSSHDockerHost()` to create a local unix socket tunneled
// over SSH to the specified ssh host.
if strings.HasPrefix(dockerHost, "ssh://") {
os.Setenv(dockerHostEnvKey, dockerHost)
}
tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost()
if err != nil { if err != nil {
ogLog.Fatal(err) ogLog.Fatal(err)
} }
// If we created a tunnel to the remote ssh host, we then override the dockerhost to point to the tunnel
// Retrieve the docker host from the environment which could have been set by if tunnelResult.Created {
// the `SSHHandler.HandleSSHDockerHost()` and override `dockerHost`. dockerHost = tunnelResult.SocketPath
dockerHostFromEnv := os.Getenv(dockerHostEnvKey)
if dockerHostFromEnv != "" {
dockerHost = dockerHostFromEnv
} }
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(APIVersion), client.WithHost(dockerHost)) clientOpts := []client.Opt{
client.WithTLSClientConfigFromEnv(),
client.WithVersion(APIVersion),
client.WithHost(dockerHost),
}
cli, err := client.NewClientWithOpts(clientOpts...)
if err != nil { if err != nil {
ogLog.Fatal(err) ogLog.Fatal(err)
} }
@ -110,7 +107,7 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
Client: cli, Client: cli,
ErrorChan: errorChan, ErrorChan: errorChan,
InDockerComposeProject: true, InDockerComposeProject: true,
Closers: []io.Closer{tunnelCloser}, Closers: []io.Closer{tunnelResult.Closer},
} }
dockerCommand.setDockerComposeCommand(config) dockerCommand.setDockerComposeCommand(config)

View file

@ -42,31 +42,36 @@ func NewSSHHandler(oSCommand CmdKiller) *SSHHandler {
} }
} }
type TunnelResult struct {
Closer io.Closer
SocketPath string
Created bool
}
// HandleSSHDockerHost overrides the DOCKER_HOST environment variable // HandleSSHDockerHost overrides the DOCKER_HOST environment variable
// to point towards a local unix socket tunneled over SSH to the specified ssh host. // 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) (TunnelResult, error) {
const key = "DOCKER_HOST" u, err := url.Parse(dockerHost)
ctx := context.Background()
u, err := url.Parse(self.getenv(key))
if err != nil { if err != nil {
// if no or an invalid docker host is specified, continue nominally // 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 the docker host scheme is "ssh", forward the docker socket before creating the client
if u.Scheme == "ssh" { if u.Scheme == "ssh" {
tunnel, err := self.createDockerHostTunnel(ctx, u.Host) ctx := context.Background()
tunnel, err := self.createDockerHostTunnel(ctx, u.String())
if err != nil { if err != nil {
return noopCloser{}, fmt.Errorf("tunnel ssh docker host: %w", err) return TunnelResult{Closer: 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 tunnel, nil return TunnelResult{
Closer: tunnel,
SocketPath: tunnel.socketPath,
Created: true,
}, nil
} }
return noopCloser{}, nil return TunnelResult{Closer: noopCloser{}}, nil
} }
type noopCloser struct{} type noopCloser struct{}
@ -86,7 +91,7 @@ func (t *tunneledDockerHost) Close() error {
} }
func (self *SSHHandler) createDockerHostTunnel(ctx context.Context, remoteHost string) (*tunneledDockerHost, 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 { if err != nil {
return nil, fmt.Errorf("create ssh tunnel tmp file: %w", err) return nil, fmt.Errorf("create ssh tunnel tmp file: %w", err)
} }

View file

@ -51,7 +51,7 @@ func TestSSHHandlerHandleSSHDockerHost(t *testing.T) {
tempDir := func(dir string, pattern string) (string, error) { tempDir := func(dir string, pattern string) (string, error) {
assert.Equal(t, "/tmp", dir) 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 return "/tmp/lazydocker-ssh-tunnel-12345", nil
} }
@ -64,7 +64,7 @@ func TestSSHHandlerHandleSSHDockerHost(t *testing.T) {
startCmdCount := 0 startCmdCount := 0
startCmd := func(cmd *exec.Cmd) error { 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++ startCmdCount++
@ -91,7 +91,7 @@ func TestSSHHandlerHandleSSHDockerHost(t *testing.T) {
setenv: setenv, setenv: setenv,
} }
_, err := handler.HandleSSHDockerHost() _, err := handler.HandleSSHDockerHost(s.envVarValue)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, s.expectedDialContextCount, dialContextCount) assert.Equal(t, s.expectedDialContextCount, dialContextCount)