Fix live-restore w/ restart policies + volume refs
Before this change restarting the daemon in live-restore with running containers + a restart policy meant that volume refs were not restored. This specifically happens when the container is still running *and* there is a restart policy that would make sure the container was running again on restart.

The bug allows volumes to be removed even though containers are referencing them. 😱

Fixes #41686
integration/daemon/daemon_test.go
package daemon // import "github.com/docker/docker/integration/daemon"

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"testing"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/daemon/config"
	"github.com/docker/docker/testutil/daemon"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
	"gotest.tools/v3/skip"
)

const (
	libtrustKey   = `{"crv":"P-256","d":"dm28PH4Z4EbyUN8L0bPonAciAQa1QJmmyYd876mnypY","kid":"WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB","kty":"EC","x":"Mh5-JINSjaa_EZdXDttri255Z5fbCEOTQIZjAcScFTk","y":"eUyuAjfxevb07hCCpvi4Zi334Dy4GDWQvEToGEX4exQ"}`
	libtrustKeyID = "WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB"
)

func TestConfigDaemonLibtrustID(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	defer d.Stop(t)

	trustKey := filepath.Join(d.RootDir(), "key.json")
	err := os.WriteFile(trustKey, []byte(libtrustKey), 0644)
	assert.NilError(t, err)

	cfg := filepath.Join(d.RootDir(), "daemon.json")
	err = os.WriteFile(cfg, []byte(`{"deprecated-key-path": "`+trustKey+`"}`), 0644)
	assert.NilError(t, err)

	d.Start(t, "--config-file", cfg)
	info := d.Info(t)
	assert.Equal(t, info.ID, libtrustKeyID)
}

func TestConfigDaemonID(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	defer d.Stop(t)

	trustKey := filepath.Join(d.RootDir(), "key.json")
	err := os.WriteFile(trustKey, []byte(libtrustKey), 0644)
	assert.NilError(t, err)

	cfg := filepath.Join(d.RootDir(), "daemon.json")
	err = os.WriteFile(cfg, []byte(`{"deprecated-key-path": "`+trustKey+`"}`), 0644)
	assert.NilError(t, err)

	// Verify that on an installation with a trust-key present, the ID matches
	// the trust-key ID, and that the ID has been migrated to the engine-id file.
	d.Start(t, "--config-file", cfg, "--iptables=false")
	info := d.Info(t)
	assert.Equal(t, info.ID, libtrustKeyID)

	idFile := filepath.Join(d.RootDir(), "engine-id")
	id, err := os.ReadFile(idFile)
	assert.NilError(t, err)
	assert.Equal(t, string(id), libtrustKeyID)
	d.Stop(t)

	// Verify that (if present) the engine-id file takes precedence
	const engineID = "this-is-the-engine-id"
	err = os.WriteFile(idFile, []byte(engineID), 0600)
	assert.NilError(t, err)

	d.Start(t, "--config-file", cfg, "--iptables=false")
	info = d.Info(t)
	assert.Equal(t, info.ID, engineID)
	d.Stop(t)
}

func TestDaemonConfigValidation(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	dockerBinary, err := d.BinaryPath()
	assert.NilError(t, err)
	params := []string{"--validate", "--config-file"}

	dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
	if dest == "" {
		dest = os.Getenv("DEST")
	}
	testdata := filepath.Join(dest, "..", "..", "integration", "daemon", "testdata")

	const (
		validOut  = "configuration OK"
		failedOut = "unable to configure the Docker daemon with file"
	)

	tests := []struct {
		name        string
		args        []string
		expectedOut string
	}{
		{
			name:        "config with no content",
			args:        append(params, filepath.Join(testdata, "empty-config-1.json")),
			expectedOut: validOut,
		},
		{
			name:        "config with {}",
			args:        append(params, filepath.Join(testdata, "empty-config-2.json")),
			expectedOut: validOut,
		},
		{
			name:        "invalid config",
			args:        append(params, filepath.Join(testdata, "invalid-config-1.json")),
			expectedOut: failedOut,
		},
		{
			name:        "malformed config",
			args:        append(params, filepath.Join(testdata, "malformed-config.json")),
			expectedOut: failedOut,
		},
		{
			name:        "valid config",
			args:        append(params, filepath.Join(testdata, "valid-config-1.json")),
			expectedOut: validOut,
		},
	}
	for _, tc := range tests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			cmd := exec.Command(dockerBinary, tc.args...)
			out, err := cmd.CombinedOutput()
			assert.Check(t, is.Contains(string(out), tc.expectedOut))
			if tc.expectedOut == failedOut {
				assert.ErrorContains(t, err, "", "expected an error, but got none")
			} else {
				assert.NilError(t, err)
			}
		})
	}
}

func TestConfigDaemonSeccompProfiles(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	defer d.Stop(t)

	tests := []struct {
		doc             string
		profile         string
		expectedProfile string
	}{
		{
			doc:             "empty profile set",
			profile:         "",
			expectedProfile: config.SeccompProfileDefault,
		},
		{
			doc:             "default profile",
			profile:         config.SeccompProfileDefault,
			expectedProfile: config.SeccompProfileDefault,
		},
		{
			doc:             "unconfined profile",
			profile:         config.SeccompProfileUnconfined,
			expectedProfile: config.SeccompProfileUnconfined,
		},
	}

	for _, tc := range tests {
		tc := tc
		t.Run(tc.doc, func(t *testing.T) {
			d.Start(t, "--seccomp-profile="+tc.profile)
			info := d.Info(t)
			assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
			d.Stop(t)

			cfg := filepath.Join(d.RootDir(), "daemon.json")
			err := os.WriteFile(cfg, []byte(`{"seccomp-profile": "`+tc.profile+`"}`), 0644)
			assert.NilError(t, err)

			d.Start(t, "--config-file", cfg)
			info = d.Info(t)
			assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
			d.Stop(t)
		})
	}
}

func TestDaemonProxy(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows", "cannot start multiple daemons on windows")
	skip.If(t, os.Getenv("DOCKER_ROOTLESS") != "", "cannot connect to localhost proxy in rootless environment")

	var received string
	proxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		received = r.Host
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte("OK"))
	}))
	defer proxyServer.Close()

	const userPass = "myuser:mypassword@"

	// Configure proxy through env-vars
	t.Run("environment variables", func(t *testing.T) {
		t.Setenv("HTTP_PROXY", proxyServer.URL)
		t.Setenv("HTTPS_PROXY", proxyServer.URL)
		t.Setenv("NO_PROXY", "example.com")

		d := daemon.New(t)
		c := d.NewClientT(t)
		defer func() { _ = c.Close() }()
		ctx := context.Background()
		d.Start(t)

		_, err := c.ImagePull(ctx, "example.org:5000/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5000")

		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5000", "should not have used proxy")

		info := d.Info(t)
		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
		assert.Equal(t, info.NoProxy, "example.com")
		d.Stop(t)
	})

	// Configure proxy through command-line flags
	t.Run("command-line options", func(t *testing.T) {
		t.Setenv("HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("http_proxy", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("NO_PROXY", "ignore.invalid")
		t.Setenv("no_proxy", "ignore.invalid")

		d := daemon.New(t)
		d.Start(t, "--http-proxy", proxyServer.URL, "--https-proxy", proxyServer.URL, "--no-proxy", "example.com")

		logs, err := d.ReadLogFile()
		assert.NilError(t, err)
		assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
		for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
			assert.Assert(t, is.Contains(string(logs), "name="+v))
			assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
		}

		c := d.NewClientT(t)
		defer func() { _ = c.Close() }()
		ctx := context.Background()

		_, err = c.ImagePull(ctx, "example.org:5001/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5001")

		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5001", "should not have used proxy")

		info := d.Info(t)
		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
		assert.Equal(t, info.NoProxy, "example.com")

		d.Stop(t)
	})

	// Configure proxy through configuration file
	t.Run("configuration file", func(t *testing.T) {
		t.Setenv("HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("http_proxy", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("NO_PROXY", "ignore.invalid")
		t.Setenv("no_proxy", "ignore.invalid")

		d := daemon.New(t)
		c := d.NewClientT(t)
		defer func() { _ = c.Close() }()
		ctx := context.Background()

		configFile := filepath.Join(d.RootDir(), "daemon.json")
		configJSON := fmt.Sprintf(`{"proxies":{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}}`, proxyServer.URL)
		assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644))

		d.Start(t, "--config-file", configFile)

		logs, err := d.ReadLogFile()
		assert.NilError(t, err)
		assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
		for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
			assert.Assert(t, is.Contains(string(logs), "name="+v))
			assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
		}

		_, err = c.ImagePull(ctx, "example.org:5002/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5002")

		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5002", "should not have used proxy")

		info := d.Info(t)
		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
		assert.Equal(t, info.NoProxy, "example.com")

		d.Stop(t)
	})

	// Conflicting options (passed both through command-line options and config file)
	t.Run("conflicting options", func(t *testing.T) {
		const (
			proxyRawURL = "https://" + userPass + "example.org"
			proxyURL    = "https://xxxxx:xxxxx@example.org"
		)

		d := daemon.New(t)

		configFile := filepath.Join(d.RootDir(), "daemon.json")
		configJSON := fmt.Sprintf(`{"proxies":{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}}`, proxyRawURL)
		assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644))

		err := d.StartWithError("--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com", "--config-file", configFile, "--validate")
		assert.ErrorContains(t, err, "daemon exited during startup")
		logs, err := d.ReadLogFile()
		assert.NilError(t, err)
		expected := fmt.Sprintf(
			`the following directives are specified both as a flag and in the configuration file: http-proxy: (from flag: %[1]s, from file: %[1]s), https-proxy: (from flag: %[1]s, from file: %[1]s), no-proxy: (from flag: example.com, from file: example.com)`,
			proxyURL,
		)
		assert.Assert(t, is.Contains(string(logs), expected))
	})

	// Make sure values are sanitized when reloading the daemon-config
	t.Run("reload sanitized", func(t *testing.T) {
		const (
			proxyRawURL = "https://" + userPass + "example.org"
			proxyURL    = "https://xxxxx:xxxxx@example.org"
		)

		d := daemon.New(t)
		d.Start(t, "--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com")
		defer d.Stop(t)
		err := d.Signal(syscall.SIGHUP)
		assert.NilError(t, err)

		logs, err := d.ReadLogFile()
		assert.NilError(t, err)

		// FIXME: there appears to ba a race condition, which causes ReadLogFile
		//        to not contain the full logs after signaling the daemon to reload,
		//        causing the test to fail here. As a workaround, check if we
		//        received the "reloaded" message after signaling, and only then
		//        check that it's sanitized properly. For more details on this
		//        issue, see https://github.com/moby/moby/pull/42835/files#r713120315
		if !strings.Contains(string(logs), "Reloaded configuration:") {
			t.Skip("Skipping test, because we did not find 'Reloaded configuration' in the logs")
		}

		assert.Assert(t, is.Contains(string(logs), proxyURL))
		assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
	})
}

package daemon // import "github.com/docker/docker/integration/daemon"

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"testing"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/api/types/volume"
	"github.com/docker/docker/daemon/config"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/testutil/daemon"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
	"gotest.tools/v3/skip"
)

const (
	libtrustKey   = `{"crv":"P-256","d":"dm28PH4Z4EbyUN8L0bPonAciAQa1QJmmyYd876mnypY","kid":"WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB","kty":"EC","x":"Mh5-JINSjaa_EZdXDttri255Z5fbCEOTQIZjAcScFTk","y":"eUyuAjfxevb07hCCpvi4Zi334Dy4GDWQvEToGEX4exQ"}`
	libtrustKeyID = "WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB"
)

func TestConfigDaemonLibtrustID(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	defer d.Stop(t)

	trustKey := filepath.Join(d.RootDir(), "key.json")
	err := os.WriteFile(trustKey, []byte(libtrustKey), 0644)
	assert.NilError(t, err)

	cfg := filepath.Join(d.RootDir(), "daemon.json")
	err = os.WriteFile(cfg, []byte(`{"deprecated-key-path": "`+trustKey+`"}`), 0644)
	assert.NilError(t, err)

	d.Start(t, "--config-file", cfg)
	info := d.Info(t)
	assert.Equal(t, info.ID, libtrustKeyID)
}

func TestConfigDaemonID(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	defer d.Stop(t)

	trustKey := filepath.Join(d.RootDir(), "key.json")
	err := os.WriteFile(trustKey, []byte(libtrustKey), 0644)
	assert.NilError(t, err)

	cfg := filepath.Join(d.RootDir(), "daemon.json")
	err = os.WriteFile(cfg, []byte(`{"deprecated-key-path": "`+trustKey+`"}`), 0644)
	assert.NilError(t, err)

	// Verify that on an installation with a trust-key present, the ID matches
	// the trust-key ID, and that the ID has been migrated to the engine-id file.
	d.Start(t, "--config-file", cfg, "--iptables=false")
	info := d.Info(t)
	assert.Equal(t, info.ID, libtrustKeyID)

	idFile := filepath.Join(d.RootDir(), "engine-id")
	id, err := os.ReadFile(idFile)
	assert.NilError(t, err)
	assert.Equal(t, string(id), libtrustKeyID)
	d.Stop(t)

	// Verify that (if present) the engine-id file takes precedence
	const engineID = "this-is-the-engine-id"
	err = os.WriteFile(idFile, []byte(engineID), 0600)
	assert.NilError(t, err)

	d.Start(t, "--config-file", cfg, "--iptables=false")
	info = d.Info(t)
	assert.Equal(t, info.ID, engineID)
	d.Stop(t)
}

func TestDaemonConfigValidation(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	dockerBinary, err := d.BinaryPath()
	assert.NilError(t, err)
	params := []string{"--validate", "--config-file"}

	dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
	if dest == "" {
		dest = os.Getenv("DEST")
	}
	testdata := filepath.Join(dest, "..", "..", "integration", "daemon", "testdata")

	const (
		validOut  = "configuration OK"
		failedOut = "unable to configure the Docker daemon with file"
	)

	tests := []struct {
		name        string
		args        []string
		expectedOut string
	}{
		{
			name:        "config with no content",
			args:        append(params, filepath.Join(testdata, "empty-config-1.json")),
			expectedOut: validOut,
		},
		{
			name:        "config with {}",
			args:        append(params, filepath.Join(testdata, "empty-config-2.json")),
			expectedOut: validOut,
		},
		{
			name:        "invalid config",
			args:        append(params, filepath.Join(testdata, "invalid-config-1.json")),
			expectedOut: failedOut,
		},
		{
			name:        "malformed config",
			args:        append(params, filepath.Join(testdata, "malformed-config.json")),
			expectedOut: failedOut,
		},
		{
			name:        "valid config",
			args:        append(params, filepath.Join(testdata, "valid-config-1.json")),
			expectedOut: validOut,
		},
	}
	for _, tc := range tests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			cmd := exec.Command(dockerBinary, tc.args...)
			out, err := cmd.CombinedOutput()
			assert.Check(t, is.Contains(string(out), tc.expectedOut))
			if tc.expectedOut == failedOut {
				assert.ErrorContains(t, err, "", "expected an error, but got none")
			} else {
				assert.NilError(t, err)
			}
		})
	}
}

func TestConfigDaemonSeccompProfiles(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	d := daemon.New(t)
	defer d.Stop(t)

	tests := []struct {
		doc             string
		profile         string
		expectedProfile string
	}{
		{
			doc:             "empty profile set",
			profile:         "",
			expectedProfile: config.SeccompProfileDefault,
		},
		{
			doc:             "default profile",
			profile:         config.SeccompProfileDefault,
			expectedProfile: config.SeccompProfileDefault,
		},
		{
			doc:             "unconfined profile",
			profile:         config.SeccompProfileUnconfined,
			expectedProfile: config.SeccompProfileUnconfined,
		},
	}

	for _, tc := range tests {
		tc := tc
		t.Run(tc.doc, func(t *testing.T) {
			d.Start(t, "--seccomp-profile="+tc.profile)
			info := d.Info(t)
			assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
			d.Stop(t)

			cfg := filepath.Join(d.RootDir(), "daemon.json")
			err := os.WriteFile(cfg, []byte(`{"seccomp-profile": "`+tc.profile+`"}`), 0644)
			assert.NilError(t, err)

			d.Start(t, "--config-file", cfg)
			info = d.Info(t)
			assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
			d.Stop(t)
		})
	}
}

func TestDaemonProxy(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows", "cannot start multiple daemons on windows")
	skip.If(t, os.Getenv("DOCKER_ROOTLESS") != "", "cannot connect to localhost proxy in rootless environment")

	var received string
	proxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		received = r.Host
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte("OK"))
	}))
	defer proxyServer.Close()

	const userPass = "myuser:mypassword@"

	// Configure proxy through env-vars
	t.Run("environment variables", func(t *testing.T) {
		t.Setenv("HTTP_PROXY", proxyServer.URL)
		t.Setenv("HTTPS_PROXY", proxyServer.URL)
		t.Setenv("NO_PROXY", "example.com")

		d := daemon.New(t)
		c := d.NewClientT(t)
		defer func() { _ = c.Close() }()
		ctx := context.Background()
		d.Start(t)

		_, err := c.ImagePull(ctx, "example.org:5000/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5000")

		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5000", "should not have used proxy")

		info := d.Info(t)
		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
		assert.Equal(t, info.NoProxy, "example.com")
		d.Stop(t)
	})

	// Configure proxy through command-line flags
	t.Run("command-line options", func(t *testing.T) {
		t.Setenv("HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("http_proxy", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("NO_PROXY", "ignore.invalid")
		t.Setenv("no_proxy", "ignore.invalid")

		d := daemon.New(t)
		d.Start(t, "--http-proxy", proxyServer.URL, "--https-proxy", proxyServer.URL, "--no-proxy", "example.com")

		logs, err := d.ReadLogFile()
		assert.NilError(t, err)
		assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
		for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
			assert.Assert(t, is.Contains(string(logs), "name="+v))
			assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
		}

		c := d.NewClientT(t)
		defer func() { _ = c.Close() }()
		ctx := context.Background()

		_, err = c.ImagePull(ctx, "example.org:5001/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5001")

		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5001", "should not have used proxy")

		info := d.Info(t)
		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
		assert.Equal(t, info.NoProxy, "example.com")

		d.Stop(t)
	})

	// Configure proxy through configuration file
	t.Run("configuration file", func(t *testing.T) {
		t.Setenv("HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("http_proxy", "http://"+userPass+"from-env-http.invalid")
		t.Setenv("HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")
		t.Setenv("NO_PROXY", "ignore.invalid")
		t.Setenv("no_proxy", "ignore.invalid")

		d := daemon.New(t)
		c := d.NewClientT(t)
		defer func() { _ = c.Close() }()
		ctx := context.Background()

		configFile := filepath.Join(d.RootDir(), "daemon.json")
		configJSON := fmt.Sprintf(`{"proxies":{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}}`, proxyServer.URL)
		assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644))

		d.Start(t, "--config-file", configFile)

		logs, err := d.ReadLogFile()
		assert.NilError(t, err)
		assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
		for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
			assert.Assert(t, is.Contains(string(logs), "name="+v))
			assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
		}

		_, err = c.ImagePull(ctx, "example.org:5002/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5002")

		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
		assert.ErrorContains(t, err, "", "pulling should have failed")
		assert.Equal(t, received, "example.org:5002", "should not have used proxy")

		info := d.Info(t)
		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
		assert.Equal(t, info.NoProxy, "example.com")

		d.Stop(t)
	})

	// Conflicting options (passed both through command-line options and config file)
	t.Run("conflicting options", func(t *testing.T) {
		const (
			proxyRawURL = "https://" + userPass + "example.org"
			proxyURL    = "https://xxxxx:xxxxx@example.org"
		)

		d := daemon.New(t)

		configFile := filepath.Join(d.RootDir(), "daemon.json")
		configJSON := fmt.Sprintf(`{"proxies":{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}}`, proxyRawURL)
		assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644))

		err := d.StartWithError("--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com", "--config-file", configFile, "--validate")
		assert.ErrorContains(t, err, "daemon exited during startup")
		logs, err := d.ReadLogFile()
		assert.NilError(t, err)
		expected := fmt.Sprintf(
			`the following directives are specified both as a flag and in the configuration file: http-proxy: (from flag: %[1]s, from file: %[1]s), https-proxy: (from flag: %[1]s, from file: %[1]s), no-proxy: (from flag: example.com, from file: example.com)`,
			proxyURL,
		)
		assert.Assert(t, is.Contains(string(logs), expected))
	})

	// Make sure values are sanitized when reloading the daemon-config
	t.Run("reload sanitized", func(t *testing.T) {
		const (
			proxyRawURL = "https://" + userPass + "example.org"
			proxyURL    = "https://xxxxx:xxxxx@example.org"
		)

		d := daemon.New(t)
		d.Start(t, "--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com")
		defer d.Stop(t)
		err := d.Signal(syscall.SIGHUP)
		assert.NilError(t, err)

		logs, err := d.ReadLogFile()
		assert.NilError(t, err)

		// FIXME: there appears to ba a race condition, which causes ReadLogFile
		//        to not contain the full logs after signaling the daemon to reload,
		//        causing the test to fail here. As a workaround, check if we
		//        received the "reloaded" message after signaling, and only then
		//        check that it's sanitized properly. For more details on this
		//        issue, see https://github.com/moby/moby/pull/42835/files#r713120315
		if !strings.Contains(string(logs), "Reloaded configuration:") {
			t.Skip("Skipping test, because we did not find 'Reloaded configuration' in the logs")
		}

		assert.Assert(t, is.Contains(string(logs), proxyURL))
		assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
	})
}

func TestLiveRestore(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows", "cannot start multiple daemons on windows")

	t.Run("volume references", testLiveRestoreVolumeReferences)
}

func testLiveRestoreVolumeReferences(t *testing.T) {
	t.Parallel()

	d := daemon.New(t)
	d.StartWithBusybox(t, "--live-restore", "--iptables=false")
	defer func() {
		d.Stop(t)
		d.Cleanup(t)
	}()

	c := d.NewClientT(t)
	ctx := context.Background()

	runTest := func(t *testing.T, policy string) {
		t.Run(policy, func(t *testing.T) {
			volName := "test-live-restore-volume-references-" + policy
			_, err := c.VolumeCreate(ctx, volume.CreateOptions{Name: volName})
			assert.NilError(t, err)

			// Create a container that uses the volume
			m := mount.Mount{
				Type:   mount.TypeVolume,
				Source: volName,
				Target: "/foo",
			}
			cID := container.Run(ctx, t, c, container.WithMount(m), container.WithCmd("top"), container.WithRestartPolicy(policy))
			defer c.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})

			// Stop the daemon
			d.Restart(t, "--live-restore", "--iptables=false")

			// Try to remove the volume
			err = c.VolumeRemove(ctx, volName, false)
			assert.ErrorContains(t, err, "volume is in use")

			_, err = c.VolumeInspect(ctx, volName)
			assert.NilError(t, err)
		})
	}

	t.Run("restartPolicy", func(t *testing.T) {
		runTest(t, "always")
		runTest(t, "unless-stopped")
		runTest(t, "on-failure")
		runTest(t, "no")
	})
}

Potential other live restore tests.
Just namespacing.