robocar-base/vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/docker_rootless.go

147 lines
4.5 KiB
Go

package testcontainersdocker
import (
"context"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"runtime"
)
var (
ErrRootlessDockerNotFound = errors.New("rootless Docker not found")
ErrRootlessDockerNotFoundHomeDesktopDir = errors.New("checked path: ~/.docker/desktop/docker.sock")
ErrRootlessDockerNotFoundHomeRunDir = errors.New("checked path: ~/.docker/run/docker.sock")
ErrRootlessDockerNotFoundRunDir = errors.New("checked path: /run/user/${uid}/docker.sock")
ErrRootlessDockerNotFoundXDGRuntimeDir = errors.New("checked path: $XDG_RUNTIME_DIR")
ErrRootlessDockerNotSupportedWindows = errors.New("rootless Docker is not supported on Windows")
ErrXDGRuntimeDirNotSet = errors.New("XDG_RUNTIME_DIR is not set")
)
// baseRunDir is the base directory for the "/run/user/${uid}" directory.
// It is a variable so it can be modified for testing.
var baseRunDir = "/run"
// IsWindows returns if the current OS is Windows. For that it checks the GOOS environment variable or the runtime.GOOS constant.
func IsWindows() bool {
return os.Getenv("GOOS") == "windows" || runtime.GOOS == "windows"
}
// rootlessDockerSocketPath returns if the path to the rootless Docker socket exists.
// The rootless socket path is determined by the following order:
//
// 1. XDG_RUNTIME_DIR environment variable.
// 2. ~/.docker/run/docker.sock file.
// 3. ~/.docker/desktop/docker.sock file.
// 4. /run/user/${uid}/docker.sock file.
// 5. Else, return ErrRootlessDockerNotFound, wrapping secific errors for each of the above paths.
//
// It should include the Docker socket schema (unix://) in the returned path.
func rootlessDockerSocketPath(_ context.Context) (string, error) {
// adding a manner to test it on non-windows machines, setting the GOOS env var to windows
// This is needed because runtime.GOOS is a constant that returns the OS of the machine running the test
if IsWindows() {
return "", ErrRootlessDockerNotSupportedWindows
}
socketPathFns := []func() (string, error){
rootlessSocketPathFromEnv,
rootlessSocketPathFromHomeRunDir,
rootlessSocketPathFromHomeDesktopDir,
rootlessSocketPathFromRunDir,
}
outerErr := ErrRootlessDockerNotFound
for _, socketPathFn := range socketPathFns {
s, err := socketPathFn()
if err != nil {
outerErr = fmt.Errorf("%w: %v", outerErr, err)
continue
}
return DockerSocketSchema + s, nil
}
return "", outerErr
}
func fileExists(f string) bool {
_, err := os.Stat(f)
return err == nil
}
func parseURL(s string) (string, error) {
var hostURL *url.URL
if u, err := url.Parse(s); err != nil {
return "", err
} else {
hostURL = u
}
switch hostURL.Scheme {
case "unix", "npipe":
return hostURL.Path, nil
case "tcp":
// return the original URL, as it is a valid TCP URL
return s, nil
default:
return "", ErrNoUnixSchema
}
}
// rootlessSocketPathFromEnv returns the path to the rootless Docker socket from the XDG_RUNTIME_DIR environment variable.
// It should include the Docker socket schema (unix://) in the returned path.
func rootlessSocketPathFromEnv() (string, error) {
xdgRuntimeDir, exists := os.LookupEnv("XDG_RUNTIME_DIR")
if exists {
f := filepath.Join(xdgRuntimeDir, "docker.sock")
if fileExists(f) {
return f, nil
}
return "", ErrRootlessDockerNotFoundXDGRuntimeDir
}
return "", ErrXDGRuntimeDirNotSet
}
// rootlessSocketPathFromHomeRunDir returns the path to the rootless Docker socket from the ~/.docker/run/docker.sock file.
func rootlessSocketPathFromHomeRunDir() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
f := filepath.Join(home, ".docker", "run", "docker.sock")
if fileExists(f) {
return f, nil
}
return "", ErrRootlessDockerNotFoundHomeRunDir
}
// rootlessSocketPathFromHomeDesktopDir returns the path to the rootless Docker socket from the ~/.docker/desktop/docker.sock file.
func rootlessSocketPathFromHomeDesktopDir() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
f := filepath.Join(home, ".docker", "desktop", "docker.sock")
if fileExists(f) {
return f, nil
}
return "", ErrRootlessDockerNotFoundHomeDesktopDir
}
// rootlessSocketPathFromRunDir returns the path to the rootless Docker socket from the /run/user/<uid>/docker.sock file.
func rootlessSocketPathFromRunDir() (string, error) {
uid := os.Getuid()
f := filepath.Join(baseRunDir, "user", fmt.Sprintf("%d", uid), "docker.sock")
if fileExists(f) {
return f, nil
}
return "", ErrRootlessDockerNotFoundRunDir
}