|
@@ -8,6 +8,7 @@ import (
|
|
"net/http"
|
|
"net/http"
|
|
"os"
|
|
"os"
|
|
"os/exec"
|
|
"os/exec"
|
|
|
|
+ "os/user"
|
|
"path/filepath"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strconv"
|
|
"strings"
|
|
"strings"
|
|
@@ -40,8 +41,9 @@ type nopLog struct{}
|
|
func (nopLog) Logf(string, ...interface{}) {}
|
|
func (nopLog) Logf(string, ...interface{}) {}
|
|
|
|
|
|
const (
|
|
const (
|
|
- defaultDockerdBinary = "dockerd"
|
|
|
|
- defaultContainerdSocket = "/var/run/docker/containerd/containerd.sock"
|
|
|
|
|
|
+ defaultDockerdBinary = "dockerd"
|
|
|
|
+ defaultContainerdSocket = "/var/run/docker/containerd/containerd.sock"
|
|
|
|
+ defaultDockerdRootlessBinary = "dockerd-rootless.sh"
|
|
)
|
|
)
|
|
|
|
|
|
var errDaemonNotStarted = errors.New("daemon not started")
|
|
var errDaemonNotStarted = errors.New("daemon not started")
|
|
@@ -77,6 +79,8 @@ type Daemon struct {
|
|
pidFile string
|
|
pidFile string
|
|
args []string
|
|
args []string
|
|
containerdSocket string
|
|
containerdSocket string
|
|
|
|
+ rootlessUser *user.User
|
|
|
|
+ rootlessXDGRuntimeDir string
|
|
|
|
|
|
// swarm related field
|
|
// swarm related field
|
|
swarmListenAddr string
|
|
swarmListenAddr string
|
|
@@ -134,6 +138,46 @@ func NewDaemon(workingDir string, ops ...Option) (*Daemon, error) {
|
|
op(d)
|
|
op(d)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if d.rootlessUser != nil {
|
|
|
|
+ if err := os.Chmod(SockRoot, 0777); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ uid, err := strconv.Atoi(d.rootlessUser.Uid)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ gid, err := strconv.Atoi(d.rootlessUser.Gid)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ if err := os.Chown(d.Folder, uid, gid); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ if err := os.Chown(d.Root, uid, gid); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ if err := os.MkdirAll(filepath.Dir(d.execRoot), 0700); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ if err := os.Chown(filepath.Dir(d.execRoot), uid, gid); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ if err := os.MkdirAll(d.execRoot, 0700); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ if err := os.Chown(d.execRoot, uid, gid); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ d.rootlessXDGRuntimeDir = filepath.Join(d.Folder, "xdgrun")
|
|
|
|
+ if err := os.MkdirAll(d.rootlessXDGRuntimeDir, 0700); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ if err := os.Chown(d.rootlessXDGRuntimeDir, uid, gid); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ d.containerdSocket = ""
|
|
|
|
+ }
|
|
|
|
+
|
|
return d, nil
|
|
return d, nil
|
|
}
|
|
}
|
|
|
|
|
|
@@ -152,11 +196,22 @@ func New(t testing.TB, ops ...Option) *Daemon {
|
|
assert.Check(t, dest != "", "Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable")
|
|
assert.Check(t, dest != "", "Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable")
|
|
|
|
|
|
if os.Getenv("DOCKER_ROOTLESS") != "" {
|
|
if os.Getenv("DOCKER_ROOTLESS") != "" {
|
|
- t.Skip("github.com/docker/docker/testutil/daemon.Daemon doesn't support DOCKER_ROOTLESS")
|
|
|
|
|
|
+ if os.Getenv("DOCKER_REMAP_ROOT") != "" {
|
|
|
|
+ t.Skip("DOCKER_ROOTLESS doesn't support DOCKER_REMAP_ROOT currently")
|
|
|
|
+ }
|
|
|
|
+ if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" {
|
|
|
|
+ if val, err := strconv.ParseBool(env); err == nil && !val {
|
|
|
|
+ t.Skip("DOCKER_ROOTLESS doesn't support DOCKER_USERLANDPROXY=false")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ ops = append(ops, WithRootlessUser("unprivilegeduser"), WithExperimental())
|
|
}
|
|
}
|
|
|
|
|
|
d, err := NewDaemon(dest, ops...)
|
|
d, err := NewDaemon(dest, ops...)
|
|
assert.NilError(t, err, "could not create daemon at %q", dest)
|
|
assert.NilError(t, err, "could not create daemon at %q", dest)
|
|
|
|
+ if d.rootlessUser != nil && d.dockerdBinary != defaultDockerdBinary {
|
|
|
|
+ t.Skipf("DOCKER_ROOTLESS doesn't support specifying non-default dockerd binary path %q", d.dockerdBinary)
|
|
|
|
+ }
|
|
|
|
|
|
return d
|
|
return d
|
|
}
|
|
}
|
|
@@ -231,9 +286,6 @@ func (d *Daemon) Cleanup(t testing.TB) {
|
|
// Start starts the daemon and return once it is ready to receive requests.
|
|
// Start starts the daemon and return once it is ready to receive requests.
|
|
func (d *Daemon) Start(t testing.TB, args ...string) {
|
|
func (d *Daemon) Start(t testing.TB, args ...string) {
|
|
t.Helper()
|
|
t.Helper()
|
|
- if os.Getenv("DOCKER_ROOTLESS") != "" {
|
|
|
|
- t.Skip("github.com/docker/docker/testutil/daemon.Daemon doesn't support DOCKER_ROOTLESS")
|
|
|
|
- }
|
|
|
|
if err := d.StartWithError(args...); err != nil {
|
|
if err := d.StartWithError(args...); err != nil {
|
|
t.Fatalf("[%s] failed to start daemon with arguments %v : %v", d.id, d.args, err)
|
|
t.Fatalf("[%s] failed to start daemon with arguments %v : %v", d.id, d.args, err)
|
|
}
|
|
}
|
|
@@ -262,14 +314,30 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
|
|
d.pidFile = filepath.Join(d.Folder, "docker.pid")
|
|
d.pidFile = filepath.Join(d.Folder, "docker.pid")
|
|
}
|
|
}
|
|
|
|
|
|
- d.args = []string{
|
|
|
|
|
|
+ d.args = []string{}
|
|
|
|
+ if d.rootlessUser != nil {
|
|
|
|
+ if d.dockerdBinary != defaultDockerdBinary {
|
|
|
|
+ return errors.Errorf("[%s] DOCKER_ROOTLESS doesn't support non-default dockerd binary path %q", d.id, d.dockerdBinary)
|
|
|
|
+ }
|
|
|
|
+ dockerdBinary = "sudo"
|
|
|
|
+ d.args = append(d.args,
|
|
|
|
+ "-u", d.rootlessUser.Username,
|
|
|
|
+ "-E", "XDG_RUNTIME_DIR="+d.rootlessXDGRuntimeDir,
|
|
|
|
+ "-E", "HOME="+d.rootlessUser.HomeDir,
|
|
|
|
+ "-E", "PATH="+os.Getenv("PATH"),
|
|
|
|
+ "--",
|
|
|
|
+ defaultDockerdRootlessBinary,
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ d.args = append(d.args,
|
|
"--data-root", d.Root,
|
|
"--data-root", d.Root,
|
|
"--exec-root", d.execRoot,
|
|
"--exec-root", d.execRoot,
|
|
"--pidfile", d.pidFile,
|
|
"--pidfile", d.pidFile,
|
|
fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
|
|
fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
|
|
"--containerd-namespace", d.id,
|
|
"--containerd-namespace", d.id,
|
|
- "--containerd-plugins-namespace", d.id + "p",
|
|
|
|
- }
|
|
|
|
|
|
+ "--containerd-plugins-namespace", d.id+"p",
|
|
|
|
+ )
|
|
if d.containerdSocket != "" {
|
|
if d.containerdSocket != "" {
|
|
d.args = append(d.args, "--containerd", d.containerdSocket)
|
|
d.args = append(d.args, "--containerd", d.containerdSocket)
|
|
}
|
|
}
|
|
@@ -315,6 +383,10 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
|
|
d.cmd.Stdout = out
|
|
d.cmd.Stdout = out
|
|
d.cmd.Stderr = out
|
|
d.cmd.Stderr = out
|
|
d.logFile = out
|
|
d.logFile = out
|
|
|
|
+ if d.rootlessUser != nil {
|
|
|
|
+ // sudo requires this for propagating signals
|
|
|
|
+ setsid(d.cmd)
|
|
|
|
+ }
|
|
|
|
|
|
if err := d.cmd.Start(); err != nil {
|
|
if err := d.cmd.Start(); err != nil {
|
|
return errors.Wrapf(err, "[%s] could not start daemon container", d.id)
|
|
return errors.Wrapf(err, "[%s] could not start daemon container", d.id)
|