mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-22 15:11:02 +00:00
Implement proper detection with Podman support and enhance error handling
Signed-off-by: DeiAsPie <93835541+DeiAsPie@users.noreply.github.com>
This commit is contained in:
parent
f4fc3669ca
commit
12a6289bef
7 changed files with 443 additions and 78 deletions
|
|
@ -13,9 +13,6 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
cliconfig "github.com/docker/cli/cli/config"
|
||||
ddocker "github.com/docker/cli/cli/context/docker"
|
||||
ctxstore "github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/imdario/mergo"
|
||||
|
|
@ -72,11 +69,15 @@ func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
|
|||
|
||||
// NewDockerCommand it runs docker commands
|
||||
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
|
||||
dockerHost, err := determineDockerHost()
|
||||
// Use new detection with caching and validation
|
||||
dockerHost, runtime, err := DetectDockerHost(log)
|
||||
if err != nil {
|
||||
ogLog.Printf("> could not determine host %v", err)
|
||||
// Provide user-friendly error
|
||||
return nil, fmt.Errorf("cannot connect to container runtime: %w; Troubleshooting: Docker - ensure Docker daemon is running; Podman - run 'systemctl --user enable --now podman.socket'; or set DOCKER_HOST environment variable explicitly", err)
|
||||
}
|
||||
|
||||
log.Infof("Detected %s runtime at %s", runtime, dockerHost)
|
||||
|
||||
// NOTE: Inject the determined docker host to the environment. This allows the
|
||||
// `SSHHandler.HandleSSHDockerHost()` to create a local unix socket tunneled
|
||||
// over SSH to the specified ssh host.
|
||||
|
|
@ -362,64 +363,3 @@ func (c *DockerCommand) DockerComposeConfig() string {
|
|||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// determineDockerHost tries to the determine the docker host that we should connect to
|
||||
// in the following order of decreasing precedence:
|
||||
// - value of "DOCKER_HOST" environment variable
|
||||
// - host retrieved from the current context (specified via DOCKER_CONTEXT)
|
||||
// - "default docker host" for the host operating system, otherwise
|
||||
func determineDockerHost() (string, error) {
|
||||
// If the docker host is explicitly set via the "DOCKER_HOST" environment variable,
|
||||
// then its a no-brainer :shrug:
|
||||
if os.Getenv("DOCKER_HOST") != "" {
|
||||
return os.Getenv("DOCKER_HOST"), nil
|
||||
}
|
||||
|
||||
currentContext := os.Getenv("DOCKER_CONTEXT")
|
||||
if currentContext == "" {
|
||||
cf, err := cliconfig.Load(cliconfig.Dir())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
currentContext = cf.CurrentContext
|
||||
}
|
||||
|
||||
// On some systems (windows) `default` is stored in the docker config as the currentContext.
|
||||
if currentContext == "" || currentContext == "default" {
|
||||
// If a docker context is neither specified via the "DOCKER_CONTEXT" environment variable nor via the
|
||||
// $HOME/.docker/config file, then we fall back to connecting to the "default docker host" meant for
|
||||
// the host operating system.
|
||||
return defaultDockerHost, nil
|
||||
}
|
||||
|
||||
storeConfig := ctxstore.NewConfig(
|
||||
func() interface{} { return &ddocker.EndpointMeta{} },
|
||||
ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
|
||||
)
|
||||
|
||||
st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
|
||||
md, err := st.GetMetadata(currentContext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
|
||||
if !ok {
|
||||
return "", err
|
||||
}
|
||||
dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
|
||||
}
|
||||
|
||||
if dockerEPMeta.Host != "" {
|
||||
return dockerEPMeta.Host, nil
|
||||
}
|
||||
|
||||
// We might end up here, if the context was created with the `host` set to an empty value (i.e. '').
|
||||
// For example:
|
||||
// ```sh
|
||||
// docker context create foo --docker "host="
|
||||
// ```
|
||||
// In such scenario, we mimic the `docker` cli and try to connect to the "default docker host".
|
||||
return defaultDockerHost, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package commands
|
||||
|
||||
const (
|
||||
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||
)
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package commands
|
||||
|
||||
const (
|
||||
defaultDockerHost = "npipe:////./pipe/docker_engine"
|
||||
)
|
||||
143
pkg/commands/socket_detection_common.go
Normal file
143
pkg/commands/socket_detection_common.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
cliconfig "github.com/docker/cli/cli/config"
|
||||
ddocker "github.com/docker/cli/cli/context/docker"
|
||||
ctxstore "github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Timeout for validating socket connectivity
|
||||
const socketValidationTimeout = 3 * time.Second
|
||||
|
||||
// Runtime type detection
|
||||
type ContainerRuntime string
|
||||
|
||||
const (
|
||||
RuntimeDocker ContainerRuntime = "docker"
|
||||
RuntimePodman ContainerRuntime = "podman"
|
||||
RuntimeUnknown ContainerRuntime = "unknown"
|
||||
)
|
||||
|
||||
// Cache for socket detection results
|
||||
var (
|
||||
cachedDockerHost string
|
||||
cachedRuntime ContainerRuntime
|
||||
dockerHostOnce sync.Once
|
||||
dockerHostErr error
|
||||
)
|
||||
|
||||
// DetectDockerHost finds a working Docker/Podman socket
|
||||
// Results are cached after first successful detection
|
||||
func DetectDockerHost(log *logrus.Entry) (string, ContainerRuntime, error) {
|
||||
dockerHostOnce.Do(func() {
|
||||
cachedDockerHost, cachedRuntime, dockerHostErr = detectDockerHostInternal(log)
|
||||
})
|
||||
return cachedDockerHost, cachedRuntime, dockerHostErr
|
||||
}
|
||||
|
||||
func detectDockerHostInternal(log *logrus.Entry) (string, ContainerRuntime, error) {
|
||||
// Priority 1: Explicit DOCKER_HOST environment variable
|
||||
if dockerHost := os.Getenv("DOCKER_HOST"); dockerHost != "" {
|
||||
log.Debugf("Using DOCKER_HOST from environment: %s", dockerHost)
|
||||
if !strings.HasPrefix(dockerHost, "ssh://") {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), socketValidationTimeout)
|
||||
defer cancel()
|
||||
if err := validateSocket(ctx, dockerHost, true); err != nil {
|
||||
log.Warnf("DOCKER_HOST=%s is set but not accessible: %v", dockerHost, err)
|
||||
}
|
||||
}
|
||||
return dockerHost, RuntimeUnknown, nil
|
||||
}
|
||||
|
||||
// Priority 2: Docker Context
|
||||
contextHost, err := getHostFromContext()
|
||||
if err != nil {
|
||||
// If DOCKER_CONTEXT was explicitly set, we should fail
|
||||
if os.Getenv("DOCKER_CONTEXT") != "" {
|
||||
return "", RuntimeUnknown, fmt.Errorf("failed to use DOCKER_CONTEXT: %w", err)
|
||||
}
|
||||
log.Debugf("Failed to get host from default context: %v", err)
|
||||
} else if contextHost != "" {
|
||||
log.Debugf("Using host from Docker context: %s", contextHost)
|
||||
if !strings.HasPrefix(contextHost, "ssh://") {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), socketValidationTimeout)
|
||||
defer cancel()
|
||||
if err := validateSocket(ctx, contextHost, false); err != nil {
|
||||
log.Warnf("Context host %s is not accessible: %v", contextHost, err)
|
||||
}
|
||||
}
|
||||
return contextHost, RuntimeUnknown, nil
|
||||
}
|
||||
|
||||
// Priority 3: Platform-specific candidates
|
||||
return detectPlatformCandidates(log)
|
||||
}
|
||||
|
||||
// getHostFromContext retrieves the host from the current Docker context
|
||||
func getHostFromContext() (string, error) {
|
||||
currentContext := os.Getenv("DOCKER_CONTEXT")
|
||||
if currentContext == "" {
|
||||
cf, err := cliconfig.Load(cliconfig.Dir())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
currentContext = cf.CurrentContext
|
||||
}
|
||||
|
||||
if currentContext == "" || currentContext == "default" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
storeConfig := ctxstore.NewConfig(
|
||||
func() interface{} { return &ddocker.EndpointMeta{} },
|
||||
ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
|
||||
)
|
||||
|
||||
st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
|
||||
md, err := st.GetMetadata(currentContext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
|
||||
}
|
||||
|
||||
return dockerEPMeta.Host, nil
|
||||
}
|
||||
|
||||
// validateSocket attempts to connect to the Docker API at the given host
|
||||
func validateSocket(ctx context.Context, host string, useEnv bool) error {
|
||||
var opts []client.Opt
|
||||
if useEnv {
|
||||
// If we're validating the host from the environment, use FromEnv to pick up TLS settings
|
||||
opts = append(opts, client.FromEnv)
|
||||
}
|
||||
opts = append(opts, client.WithHost(host), client.WithAPIVersionNegotiation())
|
||||
|
||||
cli, err := client.NewClientWithOpts(opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create client: %w", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
_, err = cli.Ping(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ping failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
102
pkg/commands/socket_detection_test.go
Normal file
102
pkg/commands/socket_detection_test.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
//go:build !windows
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetSocketCandidates(t *testing.T) {
|
||||
// Save env vars
|
||||
oldXdg := os.Getenv("XDG_RUNTIME_DIR")
|
||||
oldHome := os.Getenv("HOME")
|
||||
defer func() {
|
||||
os.Setenv("XDG_RUNTIME_DIR", oldXdg)
|
||||
os.Setenv("HOME", oldHome)
|
||||
}()
|
||||
|
||||
os.Setenv("XDG_RUNTIME_DIR", "/tmp/runtime")
|
||||
os.Setenv("HOME", "/home/user")
|
||||
|
||||
candidates := getSocketCandidates()
|
||||
|
||||
// Check some expected candidates
|
||||
foundDocker := false
|
||||
foundPodman := false
|
||||
for _, c := range candidates {
|
||||
if c.Path == "unix:///var/run/docker.sock" {
|
||||
foundDocker = true
|
||||
}
|
||||
if c.Path == "unix:///tmp/runtime/podman/podman.sock" {
|
||||
foundPodman = true
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, foundDocker, "Standard Docker socket should be in candidates")
|
||||
assert.True(t, foundPodman, "Rootless Podman socket should be in candidates")
|
||||
}
|
||||
|
||||
func TestDetectDockerHost_DOCKER_HOST_Priority(t *testing.T) {
|
||||
// Save env var
|
||||
oldDockerHost := os.Getenv("DOCKER_HOST")
|
||||
defer os.Setenv("DOCKER_HOST", oldDockerHost)
|
||||
|
||||
expectedHost := "unix:///tmp/custom.sock"
|
||||
os.Setenv("DOCKER_HOST", expectedHost)
|
||||
|
||||
// Reset cache for test
|
||||
dockerHostOnce = sync.Once{}
|
||||
cachedDockerHost = ""
|
||||
|
||||
log := logrus.NewEntry(logrus.New())
|
||||
host, _, err := DetectDockerHost(log)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedHost, host)
|
||||
}
|
||||
func TestDetectDockerHost_Caching(t *testing.T) {
|
||||
// Save env var
|
||||
oldDockerHost := os.Getenv("DOCKER_HOST")
|
||||
defer os.Setenv("DOCKER_HOST", oldDockerHost)
|
||||
|
||||
os.Setenv("DOCKER_HOST", "unix:///tmp/first.sock")
|
||||
|
||||
// Reset cache for test
|
||||
dockerHostOnce = sync.Once{}
|
||||
cachedDockerHost = ""
|
||||
|
||||
log := logrus.NewEntry(logrus.New())
|
||||
host1, _, _ := DetectDockerHost(log)
|
||||
|
||||
// Change env var - should still return first one from cache
|
||||
os.Setenv("DOCKER_HOST", "unix:///tmp/second.sock")
|
||||
host2, _, _ := DetectDockerHost(log)
|
||||
|
||||
assert.Equal(t, host1, host2)
|
||||
assert.Equal(t, "unix:///tmp/first.sock", host2)
|
||||
}
|
||||
func TestDetectDockerHost_Context_Invalid(t *testing.T) {
|
||||
// Save env vars
|
||||
oldDockerHost := os.Getenv("DOCKER_HOST")
|
||||
oldDockerContext := os.Getenv("DOCKER_CONTEXT")
|
||||
defer func() {
|
||||
os.Setenv("DOCKER_HOST", oldDockerHost)
|
||||
os.Setenv("DOCKER_CONTEXT", oldDockerContext)
|
||||
}()
|
||||
|
||||
os.Setenv("DOCKER_HOST", "")
|
||||
os.Setenv("DOCKER_CONTEXT", "nonexistent-context-12345")
|
||||
|
||||
// Reset cache for test
|
||||
dockerHostOnce = sync.Once{}
|
||||
cachedDockerHost = ""
|
||||
|
||||
log := logrus.NewEntry(logrus.New())
|
||||
_, _, err := DetectDockerHost(log)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to use DOCKER_CONTEXT")
|
||||
}
|
||||
151
pkg/commands/socket_detection_unix.go
Normal file
151
pkg/commands/socket_detection_unix.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
//go:build !windows
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
DockerSocketSchema = "unix://"
|
||||
DockerSocketPath = "/var/run/docker.sock"
|
||||
|
||||
defaultDockerHost = DockerSocketSchema + DockerSocketPath
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoDockerSocket = errors.New("no working Docker/Podman socket found")
|
||||
)
|
||||
|
||||
// SocketCandidate represents a potential socket to try
|
||||
type SocketCandidate struct {
|
||||
Path string
|
||||
Runtime ContainerRuntime
|
||||
}
|
||||
|
||||
// getSocketCandidates returns all possible socket paths in priority order
|
||||
func getSocketCandidates() []SocketCandidate {
|
||||
var candidates []SocketCandidate
|
||||
|
||||
// Helper to add candidate if path is valid
|
||||
addCandidate := func(path string, runtime ContainerRuntime) {
|
||||
if path != "" {
|
||||
candidates = append(candidates, SocketCandidate{
|
||||
Path: DockerSocketSchema + path,
|
||||
Runtime: runtime,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Standard Docker daemon socket
|
||||
addCandidate(DockerSocketPath, RuntimeDocker)
|
||||
|
||||
xdgRuntime := os.Getenv("XDG_RUNTIME_DIR")
|
||||
home, _ := os.UserHomeDir()
|
||||
uid := os.Getuid()
|
||||
|
||||
// 2. Rootless Docker: $XDG_RUNTIME_DIR/docker.sock
|
||||
if xdgRuntime != "" {
|
||||
addCandidate(filepath.Join(xdgRuntime, "docker.sock"), RuntimeDocker)
|
||||
}
|
||||
|
||||
// 3. Rootless Docker: ~/.docker/run/docker.sock
|
||||
if home != "" {
|
||||
addCandidate(filepath.Join(home, ".docker", "run", "docker.sock"), RuntimeDocker)
|
||||
// 4. Docker Desktop: ~/.docker/desktop/docker.sock
|
||||
addCandidate(filepath.Join(home, ".docker", "desktop", "docker.sock"), RuntimeDocker)
|
||||
}
|
||||
|
||||
// 5. Rootless Docker: /run/user/$UID/docker.sock
|
||||
addCandidate(filepath.Join("/run", "user", strconv.Itoa(uid), "docker.sock"), RuntimeDocker)
|
||||
|
||||
// 6. Colima
|
||||
if home != "" {
|
||||
addCandidate(filepath.Join(home, ".colima", "default", "docker.sock"), RuntimeDocker)
|
||||
addCandidate(filepath.Join(home, ".colima", "docker.sock"), RuntimeDocker)
|
||||
}
|
||||
|
||||
// 7. OrbStack
|
||||
if home != "" {
|
||||
addCandidate(filepath.Join(home, ".orbstack", "run", "docker.sock"), RuntimeDocker)
|
||||
}
|
||||
|
||||
// 8. Lima
|
||||
if home != "" {
|
||||
addCandidate(filepath.Join(home, ".lima", "default", "sock", "docker.sock"), RuntimeDocker)
|
||||
}
|
||||
|
||||
// 9. Rancher Desktop
|
||||
if home != "" {
|
||||
addCandidate(filepath.Join(home, ".rd", "docker.sock"), RuntimeDocker)
|
||||
}
|
||||
|
||||
// 10. Snap Docker
|
||||
addCandidate("/var/snap/docker/current/run/docker.sock", RuntimeDocker)
|
||||
|
||||
// 11. Rootless Podman: $XDG_RUNTIME_DIR/podman/podman.sock
|
||||
if xdgRuntime != "" {
|
||||
addCandidate(filepath.Join(xdgRuntime, "podman", "podman.sock"), RuntimePodman)
|
||||
}
|
||||
|
||||
// 12. Rootless Podman: /run/user/$UID/podman/podman.sock
|
||||
addCandidate(filepath.Join("/run", "user", strconv.Itoa(uid), "podman", "podman.sock"), RuntimePodman)
|
||||
|
||||
// 13. Rootless Podman: ~/.local/share/containers/podman/podman.sock
|
||||
if home != "" {
|
||||
addCandidate(filepath.Join(home, ".local", "share", "containers", "podman", "podman.sock"), RuntimePodman)
|
||||
}
|
||||
|
||||
// 14. Rootful Podman: /run/podman/podman.sock
|
||||
addCandidate("/run/podman/podman.sock", RuntimePodman)
|
||||
|
||||
return candidates
|
||||
}
|
||||
|
||||
func detectPlatformCandidates(log *logrus.Entry) (string, ContainerRuntime, error) {
|
||||
var lastErr error
|
||||
candidates := getSocketCandidates()
|
||||
|
||||
for _, candidate := range candidates {
|
||||
socketPath := strings.TrimPrefix(candidate.Path, DockerSocketSchema)
|
||||
|
||||
// Fast path: check if socket file exists
|
||||
if _, err := os.Stat(socketPath); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validate by actually connecting
|
||||
ctx, cancel := context.WithTimeout(context.Background(), socketValidationTimeout)
|
||||
err := validateSocket(ctx, candidate.Path, false)
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
log.Debugf("Socket %s exists but validation failed: %v", candidate.Path, err)
|
||||
if strings.Contains(err.Error(), "permission denied") {
|
||||
lastErr = fmt.Errorf("%s: permission denied (are you in the docker group?)", candidate.Path)
|
||||
} else {
|
||||
lastErr = fmt.Errorf("%s: %w", candidate.Path, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infof("Connected to %s runtime via %s", candidate.Runtime, candidate.Path)
|
||||
return candidate.Path, candidate.Runtime, nil
|
||||
}
|
||||
|
||||
// All candidates failed - provide actionable error
|
||||
if lastErr != nil {
|
||||
return "", RuntimeUnknown, fmt.Errorf("%w: last error: %v", ErrNoDockerSocket, lastErr)
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("%v: ensure Docker or Podman is running", ErrNoDockerSocket)
|
||||
return "", RuntimeUnknown, errors.New(msg)
|
||||
}
|
||||
41
pkg/commands/socket_detection_windows.go
Normal file
41
pkg/commands/socket_detection_windows.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//go:build windows
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
DockerSocketSchema = "npipe://"
|
||||
DockerSocketPath = "//./pipe/docker_engine"
|
||||
|
||||
defaultDockerHost = DockerSocketSchema + DockerSocketPath
|
||||
)
|
||||
|
||||
func detectPlatformCandidates(log *logrus.Entry) (string, ContainerRuntime, error) {
|
||||
// Try Docker Desktop first
|
||||
dockerHost := defaultDockerHost
|
||||
ctx, cancel := context.WithTimeout(context.Background(), socketValidationTimeout)
|
||||
err := validateSocket(ctx, dockerHost, false)
|
||||
cancel()
|
||||
|
||||
if err == nil {
|
||||
return dockerHost, RuntimeDocker, nil
|
||||
}
|
||||
|
||||
// Try Podman on Windows
|
||||
podmanHost := "npipe:////./pipe/podman-machine-default"
|
||||
ctx, cancel = context.WithTimeout(context.Background(), socketValidationTimeout)
|
||||
err = validateSocket(ctx, podmanHost, false)
|
||||
cancel()
|
||||
|
||||
if err == nil {
|
||||
return podmanHost, RuntimePodman, nil
|
||||
}
|
||||
|
||||
// Fallback to default Docker host
|
||||
return dockerHost, RuntimeDocker, nil
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue