فهرست منبع

daemon: read-copy-update the daemon config

Ensure data-race-free access to the daemon configuration without
locking by mutating a deep copy of the config and atomically storing
a pointer to the copy into the daemon-wide configStore value. Any
operations which need to read from the daemon config must capture the
configStore value only once and pass it around to guarantee a consistent
view of the config.

Signed-off-by: Cory Snider <csnider@mirantis.com>
Cory Snider 2 سال پیش
والد
کامیت
0b592467d9
62فایلهای تغییر یافته به همراه1818 افزوده شده و 567 حذف شده
  1. 2 2
      cmd/dockerd/daemon.go
  2. 4 15
      daemon/config/config.go
  3. 1 8
      daemon/config/config_linux.go
  4. 5 4
      daemon/container.go
  5. 29 28
      daemon/container_operations.go
  6. 3 2
      daemon/container_operations_unix.go
  7. 2 1
      daemon/container_operations_windows.go
  8. 3 2
      daemon/container_unix_test.go
  9. 10 9
      daemon/create.go
  10. 62 53
      daemon/daemon.go
  11. 5 5
      daemon/daemon_linux.go
  12. 7 6
      daemon/daemon_linux_test.go
  13. 2 1
      daemon/daemon_test.go
  14. 34 34
      daemon/daemon_unix.go
  15. 16 14
      daemon/daemon_unix_test.go
  16. 0 4
      daemon/daemon_unsupported.go
  17. 20 24
      daemon/daemon_windows.go
  18. 10 5
      daemon/delete.go
  19. 2 1
      daemon/exec.go
  20. 3 2
      daemon/exec_linux.go
  21. 4 2
      daemon/exec_linux_test.go
  22. 2 1
      daemon/exec_windows.go
  23. 26 25
      daemon/info.go
  24. 18 17
      daemon/info_unix.go
  25. 6 5
      daemon/info_windows.go
  26. 5 4
      daemon/inspect.go
  27. 1 1
      daemon/inspect_linux.go
  28. 6 4
      daemon/inspect_test.go
  29. 9 12
      daemon/logs.go
  30. 3 2
      daemon/metrics_unix.go
  31. 5 2
      daemon/metrics_unsupported.go
  32. 9 5
      daemon/monitor.go
  33. 8 7
      daemon/network.go
  34. 36 36
      daemon/oci_linux.go
  35. 8 10
      daemon/oci_linux_test.go
  36. 4 3
      daemon/oci_windows.go
  37. 2 1
      daemon/prune.go
  38. 162 110
      daemon/reload.go
  39. 5 9
      daemon/reload_test.go
  40. 28 44
      daemon/reload_unix.go
  41. 2 2
      daemon/reload_windows.go
  42. 4 3
      daemon/restart.go
  43. 12 12
      daemon/runtime_unix.go
  44. 17 12
      daemon/runtime_unix_test.go
  45. 3 1
      daemon/runtime_windows.go
  46. 12 10
      daemon/start.go
  47. 4 3
      daemon/start_unix.go
  48. 2 1
      daemon/start_windows.go
  49. 2 1
      daemon/update.go
  50. 4 0
      vendor.mod
  51. 8 2
      vendor.sum
  52. 21 0
      vendor/github.com/mitchellh/copystructure/LICENSE
  53. 21 0
      vendor/github.com/mitchellh/copystructure/README.md
  54. 15 0
      vendor/github.com/mitchellh/copystructure/copier_time.go
  55. 631 0
      vendor/github.com/mitchellh/copystructure/copystructure.go
  56. 1 0
      vendor/github.com/mitchellh/reflectwalk/.travis.yml
  57. 21 0
      vendor/github.com/mitchellh/reflectwalk/LICENSE
  58. 6 0
      vendor/github.com/mitchellh/reflectwalk/README.md
  59. 19 0
      vendor/github.com/mitchellh/reflectwalk/location.go
  60. 16 0
      vendor/github.com/mitchellh/reflectwalk/location_string.go
  61. 420 0
      vendor/github.com/mitchellh/reflectwalk/reflectwalk.go
  62. 10 0
      vendor/modules.txt

+ 2 - 2
cmd/dockerd/daemon.go

@@ -268,7 +268,7 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
 	// Restart all autostart containers which has a swarm endpoint
 	// Restart all autostart containers which has a swarm endpoint
 	// and is not yet running now that we have successfully
 	// and is not yet running now that we have successfully
 	// initialized the cluster.
 	// initialized the cluster.
-	d.RestartSwarmContainers()
+	d.RestartSwarmContainers(cli.Config)
 
 
 	logrus.Info("Daemon has completed initialization")
 	logrus.Info("Daemon has completed initialization")
 
 
@@ -371,7 +371,7 @@ func newRouterOptions(ctx context.Context, config *config.Config, d *daemon.Daem
 		DefaultCgroupParent: cgroupParent,
 		DefaultCgroupParent: cgroupParent,
 		RegistryHosts:       d.RegistryHosts,
 		RegistryHosts:       d.RegistryHosts,
 		BuilderConfig:       config.Builder,
 		BuilderConfig:       config.Builder,
-		Rootless:            d.Rootless(),
+		Rootless:            daemon.Rootless(config),
 		IdentityMapping:     d.IdentityMapping(),
 		IdentityMapping:     d.IdentityMapping(),
 		DNSConfig:           config.DNSConfig,
 		DNSConfig:           config.DNSConfig,
 		ApparmorProfile:     daemon.DefaultApparmorProfile(),
 		ApparmorProfile:     daemon.DefaultApparmorProfile(),

+ 4 - 15
daemon/config/config.go

@@ -9,7 +9,6 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
-	"sync"
 
 
 	"golang.org/x/text/encoding"
 	"golang.org/x/text/encoding"
 	"golang.org/x/text/encoding/unicode"
 	"golang.org/x/text/encoding/unicode"
@@ -227,7 +226,6 @@ type CommonConfig struct {
 	NetworkConfig
 	NetworkConfig
 	registry.ServiceOptions
 	registry.ServiceOptions
 
 
-	sync.Mutex
 	// FIXME(vdemeester) This part is not that clear and is mainly dependent on cli flags
 	// FIXME(vdemeester) This part is not that clear and is mainly dependent on cli flags
 	// It should probably be handled outside this package.
 	// It should probably be handled outside this package.
 	ValuesSet map[string]interface{} `json:"-"`
 	ValuesSet map[string]interface{} `json:"-"`
@@ -650,11 +648,11 @@ func Validate(config *Config) error {
 		return err
 		return err
 	}
 	}
 
 
-	if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" {
-		if !builtinRuntimes[defaultRuntime] {
+	if config.DefaultRuntime != "" {
+		if !builtinRuntimes[config.DefaultRuntime] {
 			runtimes := config.GetAllRuntimes()
 			runtimes := config.GetAllRuntimes()
-			if _, ok := runtimes[defaultRuntime]; !ok && !IsPermissibleC8dRuntimeName(defaultRuntime) {
-				return errors.Errorf("specified default runtime '%s' does not exist", defaultRuntime)
+			if _, ok := runtimes[config.DefaultRuntime]; !ok && !IsPermissibleC8dRuntimeName(config.DefaultRuntime) {
+				return fmt.Errorf("specified default runtime '%s' does not exist", config.DefaultRuntime)
 			}
 			}
 		}
 		}
 	}
 	}
@@ -669,15 +667,6 @@ func Validate(config *Config) error {
 	return config.ValidatePlatformConfig()
 	return config.ValidatePlatformConfig()
 }
 }
 
 
-// GetDefaultRuntimeName returns the current default runtime
-func (conf *Config) GetDefaultRuntimeName() string {
-	conf.Lock()
-	rt := conf.DefaultRuntime
-	conf.Unlock()
-
-	return rt
-}
-
 // MaskCredentials masks credentials that are in an URL.
 // MaskCredentials masks credentials that are in an URL.
 func MaskCredentials(rawURL string) string {
 func MaskCredentials(rawURL string) string {
 	parsedURL, err := url.Parse(rawURL)
 	parsedURL, err := url.Parse(rawURL)

+ 1 - 8
daemon/config/config_linux.go

@@ -84,8 +84,6 @@ type Config struct {
 // GetRuntime returns the runtime path and arguments for a given
 // GetRuntime returns the runtime path and arguments for a given
 // runtime name
 // runtime name
 func (conf *Config) GetRuntime(name string) *types.Runtime {
 func (conf *Config) GetRuntime(name string) *types.Runtime {
-	conf.Lock()
-	defer conf.Unlock()
 	if rt, ok := conf.Runtimes[name]; ok {
 	if rt, ok := conf.Runtimes[name]; ok {
 		return &rt
 		return &rt
 	}
 	}
@@ -94,10 +92,7 @@ func (conf *Config) GetRuntime(name string) *types.Runtime {
 
 
 // GetAllRuntimes returns a copy of the runtimes map
 // GetAllRuntimes returns a copy of the runtimes map
 func (conf *Config) GetAllRuntimes() map[string]types.Runtime {
 func (conf *Config) GetAllRuntimes() map[string]types.Runtime {
-	conf.Lock()
-	rts := conf.Runtimes
-	conf.Unlock()
-	return rts
+	return conf.Runtimes
 }
 }
 
 
 // GetExecRoot returns the user configured Exec-root
 // GetExecRoot returns the user configured Exec-root
@@ -107,8 +102,6 @@ func (conf *Config) GetExecRoot() string {
 
 
 // GetInitPath returns the configured docker-init path
 // GetInitPath returns the configured docker-init path
 func (conf *Config) GetInitPath() string {
 func (conf *Config) GetInitPath() string {
-	conf.Lock()
-	defer conf.Unlock()
 	if conf.InitPath != "" {
 	if conf.InitPath != "" {
 		return conf.InitPath
 		return conf.InitPath
 	}
 	}

+ 5 - 4
daemon/container.go

@@ -10,6 +10,7 @@ import (
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
@@ -206,10 +207,10 @@ func (daemon *Daemon) generateHostname(id string, config *containertypes.Config)
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error {
+func (daemon *Daemon) setSecurityOptions(cfg *config.Config, container *container.Container, hostConfig *containertypes.HostConfig) error {
 	container.Lock()
 	container.Lock()
 	defer container.Unlock()
 	defer container.Unlock()
-	return daemon.parseSecurityOpt(&container.SecurityOptions, hostConfig)
+	return daemon.parseSecurityOpt(cfg, &container.SecurityOptions, hostConfig)
 }
 }
 
 
 func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error {
 func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error {
@@ -234,7 +235,7 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *
 
 
 // verifyContainerSettings performs validation of the hostconfig and config
 // verifyContainerSettings performs validation of the hostconfig and config
 // structures.
 // structures.
-func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) (warnings []string, err error) {
+func (daemon *Daemon) verifyContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) (warnings []string, err error) {
 	// First perform verification of settings common across all platforms.
 	// First perform verification of settings common across all platforms.
 	if err = validateContainerConfig(config); err != nil {
 	if err = validateContainerConfig(config); err != nil {
 		return warnings, err
 		return warnings, err
@@ -244,7 +245,7 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon
 	}
 	}
 
 
 	// Now do platform-specific verification
 	// Now do platform-specific verification
-	warnings, err = verifyPlatformContainerSettings(daemon, hostConfig, update)
+	warnings, err = verifyPlatformContainerSettings(daemon, daemonCfg, hostConfig, update)
 	for _, w := range warnings {
 	for _, w := range warnings {
 		logrus.Warn(w)
 		logrus.Warn(w)
 	}
 	}

+ 29 - 28
daemon/container_operations.go

@@ -12,6 +12,7 @@ import (
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	networktypes "github.com/docker/docker/api/types/network"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libnetwork"
 	"github.com/docker/docker/libnetwork"
@@ -26,19 +27,19 @@ import (
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 )
 )
 
 
-func (daemon *Daemon) getDNSSearchSettings(container *container.Container) []string {
+func (daemon *Daemon) getDNSSearchSettings(cfg *config.Config, container *container.Container) []string {
 	if len(container.HostConfig.DNSSearch) > 0 {
 	if len(container.HostConfig.DNSSearch) > 0 {
 		return container.HostConfig.DNSSearch
 		return container.HostConfig.DNSSearch
 	}
 	}
 
 
-	if len(daemon.configStore.DNSSearch) > 0 {
-		return daemon.configStore.DNSSearch
+	if len(cfg.DNSSearch) > 0 {
+		return cfg.DNSSearch
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]libnetwork.SandboxOption, error) {
+func (daemon *Daemon) buildSandboxOptions(cfg *config.Config, container *container.Container) ([]libnetwork.SandboxOption, error) {
 	var (
 	var (
 		sboxOptions []libnetwork.SandboxOption
 		sboxOptions []libnetwork.SandboxOption
 		err         error
 		err         error
@@ -61,21 +62,21 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
 		sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey())
 		sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey())
 	}
 	}
 
 
-	if err = daemon.setupPathsAndSandboxOptions(container, &sboxOptions); err != nil {
+	if err = daemon.setupPathsAndSandboxOptions(container, cfg, &sboxOptions); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	if len(container.HostConfig.DNS) > 0 {
 	if len(container.HostConfig.DNS) > 0 {
 		dns = container.HostConfig.DNS
 		dns = container.HostConfig.DNS
-	} else if len(daemon.configStore.DNS) > 0 {
-		dns = daemon.configStore.DNS
+	} else if len(cfg.DNS) > 0 {
+		dns = cfg.DNS
 	}
 	}
 
 
 	for _, d := range dns {
 	for _, d := range dns {
 		sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d))
 		sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d))
 	}
 	}
 
 
-	dnsSearch := daemon.getDNSSearchSettings(container)
+	dnsSearch := daemon.getDNSSearchSettings(cfg, container)
 
 
 	for _, ds := range dnsSearch {
 	for _, ds := range dnsSearch {
 		sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds))
 		sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds))
@@ -83,8 +84,8 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
 
 
 	if len(container.HostConfig.DNSOptions) > 0 {
 	if len(container.HostConfig.DNSOptions) > 0 {
 		dnsOptions = container.HostConfig.DNSOptions
 		dnsOptions = container.HostConfig.DNSOptions
-	} else if len(daemon.configStore.DNSOptions) > 0 {
-		dnsOptions = daemon.configStore.DNSOptions
+	} else if len(cfg.DNSOptions) > 0 {
+		dnsOptions = cfg.DNSOptions
 	}
 	}
 
 
 	for _, ds := range dnsOptions {
 	for _, ds := range dnsOptions {
@@ -112,7 +113,7 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
 		// value with the IP address stored in the daemon level HostGatewayIP
 		// value with the IP address stored in the daemon level HostGatewayIP
 		// config variable
 		// config variable
 		if ip == opts.HostGatewayName {
 		if ip == opts.HostGatewayName {
-			gateway := daemon.configStore.HostGatewayIP.String()
+			gateway := cfg.HostGatewayIP.String()
 			if gateway == "" {
 			if gateway == "" {
 				return nil, fmt.Errorf("unable to derive the IP value for host-gateway")
 				return nil, fmt.Errorf("unable to derive the IP value for host-gateway")
 			}
 			}
@@ -218,7 +219,7 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
 	}
 	}
 
 
 	for alias, parent := range daemon.parents(container) {
 	for alias, parent := range daemon.parents(container) {
-		if daemon.configStore.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() {
+		if cfg.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() {
 			continue
 			continue
 		}
 		}
 
 
@@ -291,13 +292,13 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) updateEndpointNetworkSettings(container *container.Container, n libnetwork.Network, ep *libnetwork.Endpoint) error {
+func (daemon *Daemon) updateEndpointNetworkSettings(cfg *config.Config, container *container.Container, n libnetwork.Network, ep *libnetwork.Endpoint) error {
 	if err := buildEndpointInfo(container.NetworkSettings, n, ep); err != nil {
 	if err := buildEndpointInfo(container.NetworkSettings, n, ep); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	if container.HostConfig.NetworkMode == runconfig.DefaultDaemonNetworkMode() {
 	if container.HostConfig.NetworkMode == runconfig.DefaultDaemonNetworkMode() {
-		container.NetworkSettings.Bridge = daemon.configStore.BridgeConfig.Iface
+		container.NetworkSettings.Bridge = cfg.BridgeConfig.Iface
 	}
 	}
 
 
 	return nil
 	return nil
@@ -305,7 +306,7 @@ func (daemon *Daemon) updateEndpointNetworkSettings(container *container.Contain
 
 
 // UpdateNetwork is used to update the container's network (e.g. when linked containers
 // UpdateNetwork is used to update the container's network (e.g. when linked containers
 // get removed/unlinked).
 // get removed/unlinked).
-func (daemon *Daemon) updateNetwork(container *container.Container) error {
+func (daemon *Daemon) updateNetwork(cfg *config.Config, container *container.Container) error {
 	var (
 	var (
 		start = time.Now()
 		start = time.Now()
 		ctrl  = daemon.netController
 		ctrl  = daemon.netController
@@ -335,7 +336,7 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
 		return nil
 		return nil
 	}
 	}
 
 
-	sbOptions, err := daemon.buildSandboxOptions(container)
+	sbOptions, err := daemon.buildSandboxOptions(cfg, container)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("Update network failed: %v", err)
 		return fmt.Errorf("Update network failed: %v", err)
 	}
 	}
@@ -519,7 +520,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr error) {
+func (daemon *Daemon) allocateNetwork(cfg *config.Config, container *container.Container) (retErr error) {
 	if daemon.netController == nil {
 	if daemon.netController == nil {
 		return nil
 		return nil
 	}
 	}
@@ -552,7 +553,7 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr er
 	defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
 	defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
 	if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok {
 	if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok {
 		cleanOperationalData(nConf)
 		cleanOperationalData(nConf)
-		if err := daemon.connectToNetwork(container, defaultNetName, nConf.EndpointSettings, updateSettings); err != nil {
+		if err := daemon.connectToNetwork(cfg, container, defaultNetName, nConf.EndpointSettings, updateSettings); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -569,7 +570,7 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr er
 
 
 	for netName, epConf := range networks {
 	for netName, epConf := range networks {
 		cleanOperationalData(epConf)
 		cleanOperationalData(epConf)
-		if err := daemon.connectToNetwork(container, netName, epConf.EndpointSettings, updateSettings); err != nil {
+		if err := daemon.connectToNetwork(cfg, container, netName, epConf.EndpointSettings, updateSettings); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -578,7 +579,7 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr er
 	// create its network sandbox now if not present
 	// create its network sandbox now if not present
 	if len(networks) == 0 {
 	if len(networks) == 0 {
 		if nil == daemon.getNetworkSandbox(container) {
 		if nil == daemon.getNetworkSandbox(container) {
-			sbOptions, err := daemon.buildSandboxOptions(container)
+			sbOptions, err := daemon.buildSandboxOptions(cfg, container)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
@@ -722,13 +723,13 @@ func (daemon *Daemon) updateNetworkConfig(container *container.Container, n libn
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
+func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
 	start := time.Now()
 	start := time.Now()
 	if container.HostConfig.NetworkMode.IsContainer() {
 	if container.HostConfig.NetworkMode.IsContainer() {
 		return runconfig.ErrConflictSharedNetwork
 		return runconfig.ErrConflictSharedNetwork
 	}
 	}
 	if containertypes.NetworkMode(idOrName).IsBridge() &&
 	if containertypes.NetworkMode(idOrName).IsBridge() &&
-		daemon.configStore.DisableBridge {
+		cfg.DisableBridge {
 		container.Config.NetworkDisabled = true
 		container.Config.NetworkDisabled = true
 		return nil
 		return nil
 	}
 	}
@@ -766,7 +767,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
 
 
 	controller := daemon.netController
 	controller := daemon.netController
 	sb := daemon.getNetworkSandbox(container)
 	sb := daemon.getNetworkSandbox(container)
-	createOptions, err := buildCreateEndpointOptions(container, n, endpointConfig, sb, daemon.configStore.DNS)
+	createOptions, err := buildCreateEndpointOptions(container, n, endpointConfig, sb, cfg.DNS)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -790,12 +791,12 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
 
 
 	delete(container.NetworkSettings.Networks, n.ID())
 	delete(container.NetworkSettings.Networks, n.ID())
 
 
-	if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil {
+	if err := daemon.updateEndpointNetworkSettings(cfg, container, n, ep); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	if sb == nil {
 	if sb == nil {
-		sbOptions, err := daemon.buildSandboxOptions(container)
+		sbOptions, err := daemon.buildSandboxOptions(cfg, container)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -946,7 +947,7 @@ func (daemon *Daemon) tryDetachContainerFromClusterNetwork(network libnetwork.Ne
 	daemon.LogNetworkEventWithAttributes(network, "disconnect", attributes)
 	daemon.LogNetworkEventWithAttributes(network, "disconnect", attributes)
 }
 }
 
 
-func (daemon *Daemon) initializeNetworking(container *container.Container) error {
+func (daemon *Daemon) initializeNetworking(cfg *config.Config, container *container.Container) error {
 	var err error
 	var err error
 
 
 	if container.HostConfig.NetworkMode.IsContainer() {
 	if container.HostConfig.NetworkMode.IsContainer() {
@@ -975,7 +976,7 @@ func (daemon *Daemon) initializeNetworking(container *container.Container) error
 		}
 		}
 	}
 	}
 
 
-	if err := daemon.allocateNetwork(container); err != nil {
+	if err := daemon.allocateNetwork(cfg, container); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -1074,7 +1075,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
 			}
 			}
 		}
 		}
 	} else {
 	} else {
-		if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil {
+		if err := daemon.connectToNetwork(daemon.config(), container, idOrName, endpointConfig, true); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 3 - 2
daemon/container_operations_unix.go

@@ -10,6 +10,7 @@ import (
 	"syscall"
 	"syscall"
 
 
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/links"
 	"github.com/docker/docker/daemon/links"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libnetwork"
 	"github.com/docker/docker/libnetwork"
@@ -380,7 +381,7 @@ func serviceDiscoveryOnDefaultNetwork() bool {
 	return false
 	return false
 }
 }
 
 
-func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
+func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
 	var err error
 	var err error
 
 
 	// Set the correct paths for /etc/hosts and /etc/resolv.conf, based on the
 	// Set the correct paths for /etc/hosts and /etc/resolv.conf, based on the
@@ -427,7 +428,7 @@ func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container
 		// Copy the host's resolv.conf for the container (/run/systemd/resolve/resolv.conf or /etc/resolv.conf)
 		// Copy the host's resolv.conf for the container (/run/systemd/resolve/resolv.conf or /etc/resolv.conf)
 		*sboxOptions = append(
 		*sboxOptions = append(
 			*sboxOptions,
 			*sboxOptions,
-			libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf()),
+			libnetwork.OptionOriginResolvConfPath(cfg.GetResolvConf()),
 		)
 		)
 	}
 	}
 
 

+ 2 - 1
daemon/container_operations_windows.go

@@ -5,6 +5,7 @@ import (
 	"os"
 	"os"
 
 
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/libnetwork"
 	"github.com/docker/docker/libnetwork"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
@@ -161,7 +162,7 @@ func serviceDiscoveryOnDefaultNetwork() bool {
 	return true
 	return true
 }
 }
 
 
-func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
+func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
 	return nil
 	return nil
 }
 }
 
 

+ 3 - 2
daemon/container_unix_test.go

@@ -33,8 +33,9 @@ func TestContainerWarningHostAndPublishPorts(t *testing.T) {
 		}
 		}
 		cs := &config.Config{}
 		cs := &config.Config{}
 		configureRuntimes(cs)
 		configureRuntimes(cs)
-		d := &Daemon{configStore: cs}
-		wrns, err := d.verifyContainerSettings(hostConfig, &containertypes.Config{}, false)
+		d := &Daemon{}
+		d.configStore.Store(cs)
+		wrns, err := d.verifyContainerSettings(cs, hostConfig, &containertypes.Config{}, false)
 		assert.NilError(t, err)
 		assert.NilError(t, err)
 		assert.DeepEqual(t, tc.warnings, wrns)
 		assert.DeepEqual(t, tc.warnings, wrns)
 	}
 	}

+ 10 - 9
daemon/create.go

@@ -14,6 +14,7 @@ import (
 	imagetypes "github.com/docker/docker/api/types/image"
 	imagetypes "github.com/docker/docker/api/types/image"
 	networktypes "github.com/docker/docker/api/types/network"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/images"
 	"github.com/docker/docker/daemon/images"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
@@ -34,7 +35,7 @@ type createOpts struct {
 
 
 // CreateManagedContainer creates a container that is managed by a Service
 // CreateManagedContainer creates a container that is managed by a Service
 func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
 func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
-	return daemon.containerCreate(ctx, createOpts{
+	return daemon.containerCreate(ctx, daemon.config(), createOpts{
 		params:  params,
 		params:  params,
 		managed: true,
 		managed: true,
 	})
 	})
@@ -42,7 +43,7 @@ func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.C
 
 
 // ContainerCreate creates a regular container
 // ContainerCreate creates a regular container
 func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
 func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
-	return daemon.containerCreate(ctx, createOpts{
+	return daemon.containerCreate(ctx, daemon.config(), createOpts{
 		params: params,
 		params: params,
 	})
 	})
 }
 }
@@ -50,19 +51,19 @@ func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.Containe
 // ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case
 // ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case
 // and ensures that we do not take the images ArgsEscaped
 // and ensures that we do not take the images ArgsEscaped
 func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
 func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
-	return daemon.containerCreate(ctx, createOpts{
+	return daemon.containerCreate(ctx, daemon.config(), createOpts{
 		params:                  params,
 		params:                  params,
 		ignoreImagesArgsEscaped: true,
 		ignoreImagesArgsEscaped: true,
 	})
 	})
 }
 }
 
 
-func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (containertypes.CreateResponse, error) {
+func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *config.Config, opts createOpts) (containertypes.CreateResponse, error) {
 	start := time.Now()
 	start := time.Now()
 	if opts.params.Config == nil {
 	if opts.params.Config == nil {
 		return containertypes.CreateResponse{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container"))
 		return containertypes.CreateResponse{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container"))
 	}
 	}
 
 
-	warnings, err := daemon.verifyContainerSettings(opts.params.HostConfig, opts.params.Config, false)
+	warnings, err := daemon.verifyContainerSettings(daemonCfg, opts.params.HostConfig, opts.params.Config, false)
 	if err != nil {
 	if err != nil {
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 	}
@@ -94,12 +95,12 @@ func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (con
 	if opts.params.HostConfig == nil {
 	if opts.params.HostConfig == nil {
 		opts.params.HostConfig = &containertypes.HostConfig{}
 		opts.params.HostConfig = &containertypes.HostConfig{}
 	}
 	}
-	err = daemon.adaptContainerSettings(opts.params.HostConfig, opts.params.AdjustCPUShares)
+	err = daemon.adaptContainerSettings(daemonCfg, opts.params.HostConfig, opts.params.AdjustCPUShares)
 	if err != nil {
 	if err != nil {
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 	}
 
 
-	ctr, err := daemon.create(ctx, opts)
+	ctr, err := daemon.create(ctx, daemonCfg, opts)
 	if err != nil {
 	if err != nil {
 		return containertypes.CreateResponse{Warnings: warnings}, err
 		return containertypes.CreateResponse{Warnings: warnings}, err
 	}
 	}
@@ -113,7 +114,7 @@ func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (con
 }
 }
 
 
 // Create creates a new container from the given configuration with a given name.
 // Create creates a new container from the given configuration with a given name.
-func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *container.Container, retErr error) {
+func (daemon *Daemon) create(ctx context.Context, daemonCfg *config.Config, opts createOpts) (retC *container.Container, retErr error) {
 	var (
 	var (
 		ctr         *container.Container
 		ctr         *container.Container
 		img         *image.Image
 		img         *image.Image
@@ -175,7 +176,7 @@ func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *contai
 		}
 		}
 	}()
 	}()
 
 
-	if err := daemon.setSecurityOptions(ctr, opts.params.HostConfig); err != nil {
+	if err := daemon.setSecurityOptions(daemonCfg, ctr, opts.params.HostConfig); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 

+ 62 - 53
daemon/daemon.go

@@ -16,6 +16,7 @@ import (
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
+	"sync/atomic"
 	"time"
 	"time"
 
 
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd"
@@ -84,7 +85,8 @@ type Daemon struct {
 	containersReplica     *container.ViewDB
 	containersReplica     *container.ViewDB
 	execCommands          *container.ExecStore
 	execCommands          *container.ExecStore
 	imageService          ImageService
 	imageService          ImageService
-	configStore           *config.Config
+	configStore           atomic.Pointer[config.Config]
+	configReload          sync.Mutex
 	statsCollector        *stats.Collector
 	statsCollector        *stats.Collector
 	defaultLogConfig      containertypes.LogConfig
 	defaultLogConfig      containertypes.LogConfig
 	registryService       *registry.Service
 	registryService       *registry.Service
@@ -148,20 +150,31 @@ func (daemon *Daemon) StoreHosts(hosts []string) {
 	}
 	}
 }
 }
 
 
+// config returns an immutable snapshot of the current daemon configuration.
+// Multiple calls to this function will return the same pointer until the
+// configuration is reloaded so callers must take care not to modify the
+// returned value.
+//
+// To ensure that the configuration used remains consistent throughout the
+// lifetime of an operation, the configuration pointer should be passed down the
+// call stack, like one would a [context.Context] value. Only the entrypoints
+// for operations, the outermost functions, should call this function.
+func (daemon *Daemon) config() *config.Config {
+	cfg := daemon.configStore.Load()
+	if cfg == nil {
+		return &config.Config{}
+	}
+	return cfg
+}
+
 // HasExperimental returns whether the experimental features of the daemon are enabled or not
 // HasExperimental returns whether the experimental features of the daemon are enabled or not
 func (daemon *Daemon) HasExperimental() bool {
 func (daemon *Daemon) HasExperimental() bool {
-	return daemon.configStore != nil && daemon.configStore.Experimental
+	return daemon.config().Experimental
 }
 }
 
 
 // Features returns the features map from configStore
 // Features returns the features map from configStore
 func (daemon *Daemon) Features() map[string]bool {
 func (daemon *Daemon) Features() map[string]bool {
-	daemon.configStore.Lock()
-	defer daemon.configStore.Unlock()
-	f := make(map[string]bool, len(daemon.configStore.Features))
-	for k, v := range daemon.configStore.Features {
-		f[k] = v
-	}
-	return f
+	return daemon.config().Features
 }
 }
 
 
 // UsesSnapshotter returns true if feature flag to use containerd snapshotter is enabled
 // UsesSnapshotter returns true if feature flag to use containerd snapshotter is enabled
@@ -172,17 +185,14 @@ func (daemon *Daemon) UsesSnapshotter() bool {
 // RegistryHosts returns the registry hosts configuration for the host component
 // RegistryHosts returns the registry hosts configuration for the host component
 // of a distribution image reference.
 // of a distribution image reference.
 func (daemon *Daemon) RegistryHosts(host string) ([]docker.RegistryHost, error) {
 func (daemon *Daemon) RegistryHosts(host string) ([]docker.RegistryHost, error) {
-	daemon.configStore.Lock()
-	serviceOpts := daemon.configStore.ServiceOptions
-	daemon.configStore.Unlock()
-
 	var (
 	var (
+		conf        = daemon.config()
 		registryKey = "docker.io"
 		registryKey = "docker.io"
-		mirrors     = make([]string, len(serviceOpts.Mirrors))
+		mirrors     = make([]string, len(conf.Mirrors))
 		m           = map[string]resolverconfig.RegistryConfig{}
 		m           = map[string]resolverconfig.RegistryConfig{}
 	)
 	)
 	// must trim "https://" or "http://" prefix
 	// must trim "https://" or "http://" prefix
-	for i, v := range serviceOpts.Mirrors {
+	for i, v := range conf.Mirrors {
 		if uri, err := url.Parse(v); err == nil {
 		if uri, err := url.Parse(v); err == nil {
 			v = uri.Host
 			v = uri.Host
 		}
 		}
@@ -191,7 +201,7 @@ func (daemon *Daemon) RegistryHosts(host string) ([]docker.RegistryHost, error)
 	// set mirrors for default registry
 	// set mirrors for default registry
 	m[registryKey] = resolverconfig.RegistryConfig{Mirrors: mirrors}
 	m[registryKey] = resolverconfig.RegistryConfig{Mirrors: mirrors}
 
 
-	for _, v := range serviceOpts.InsecureRegistries {
+	for _, v := range conf.InsecureRegistries {
 		u, err := url.Parse(v)
 		u, err := url.Parse(v)
 		if err != nil && !strings.HasPrefix(v, "http://") && !strings.HasPrefix(v, "https://") {
 		if err != nil && !strings.HasPrefix(v, "http://") && !strings.HasPrefix(v, "https://") {
 			originalErr := err
 			originalErr := err
@@ -237,7 +247,7 @@ type layerAccessor interface {
 	GetLayerByID(cid string) (layer.RWLayer, error)
 	GetLayerByID(cid string) (layer.RWLayer, error)
 }
 }
 
 
-func (daemon *Daemon) restore() error {
+func (daemon *Daemon) restore(cfg *config.Config) error {
 	var mapLock sync.Mutex
 	var mapLock sync.Mutex
 	containers := make(map[string]*container.Container)
 	containers := make(map[string]*container.Container)
 
 
@@ -377,7 +387,7 @@ func (daemon *Daemon) restore() error {
 							logger(c).WithError(err).Error("failed to delete task from containerd")
 							logger(c).WithError(err).Error("failed to delete task from containerd")
 							return
 							return
 						}
 						}
-					} else if !daemon.configStore.LiveRestoreEnabled {
+					} else if !cfg.LiveRestoreEnabled {
 						logger(c).Debug("shutting down container considered alive by containerd")
 						logger(c).Debug("shutting down container considered alive by containerd")
 						if err := daemon.shutdownContainer(c); err != nil && !errdefs.IsNotFound(err) {
 						if err := daemon.shutdownContainer(c); err != nil && !errdefs.IsNotFound(err) {
 							log.WithError(err).Error("error shutting down container")
 							log.WithError(err).Error("error shutting down container")
@@ -457,7 +467,7 @@ func (daemon *Daemon) restore() error {
 
 
 				c.ResetRestartManager(false)
 				c.ResetRestartManager(false)
 				if !c.HostConfig.NetworkMode.IsContainer() && c.IsRunning() {
 				if !c.HostConfig.NetworkMode.IsContainer() && c.IsRunning() {
-					options, err := daemon.buildSandboxOptions(c)
+					options, err := daemon.buildSandboxOptions(cfg, c)
 					if err != nil {
 					if err != nil {
 						logger(c).WithError(err).Warn("failed to build sandbox option to restore container")
 						logger(c).WithError(err).Warn("failed to build sandbox option to restore container")
 					}
 					}
@@ -475,7 +485,7 @@ func (daemon *Daemon) restore() error {
 			// not initialized yet. We will start
 			// not initialized yet. We will start
 			// it after the cluster is
 			// it after the cluster is
 			// initialized.
 			// initialized.
-			if daemon.configStore.AutoRestart && c.ShouldRestart() && !c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
+			if cfg.AutoRestart && c.ShouldRestart() && !c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
 				mapLock.Lock()
 				mapLock.Lock()
 				restartContainers[c] = make(chan struct{})
 				restartContainers[c] = make(chan struct{})
 				mapLock.Unlock()
 				mapLock.Unlock()
@@ -513,7 +523,7 @@ func (daemon *Daemon) restore() error {
 	//
 	//
 	// Note that we cannot initialize the network controller earlier, as it
 	// Note that we cannot initialize the network controller earlier, as it
 	// needs to know if there's active sandboxes (running containers).
 	// needs to know if there's active sandboxes (running containers).
-	if err = daemon.initNetworkController(activeSandboxes); err != nil {
+	if err = daemon.initNetworkController(cfg, activeSandboxes); err != nil {
 		return fmt.Errorf("Error initializing network controller: %v", err)
 		return fmt.Errorf("Error initializing network controller: %v", err)
 	}
 	}
 
 
@@ -560,7 +570,7 @@ func (daemon *Daemon) restore() error {
 			if err := daemon.prepareMountPoints(c); err != nil {
 			if err := daemon.prepareMountPoints(c); err != nil {
 				log.WithError(err).Error("failed to prepare mount points for container")
 				log.WithError(err).Error("failed to prepare mount points for container")
 			}
 			}
-			if err := daemon.containerStart(context.Background(), c, "", "", true); err != nil {
+			if err := daemon.containerStart(context.Background(), cfg, c, "", "", true); err != nil {
 				log.WithError(err).Error("failed to start container")
 				log.WithError(err).Error("failed to start container")
 			}
 			}
 			close(chNotify)
 			close(chNotify)
@@ -576,7 +586,7 @@ func (daemon *Daemon) restore() error {
 		go func(cid string) {
 		go func(cid string) {
 			_ = sem.Acquire(context.Background(), 1)
 			_ = sem.Acquire(context.Background(), 1)
 
 
-			if err := daemon.ContainerRm(cid, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
+			if err := daemon.containerRm(cfg, cid, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
 				logrus.WithField("container", cid).WithError(err).Error("failed to remove container")
 				logrus.WithField("container", cid).WithError(err).Error("failed to remove container")
 			}
 			}
 
 
@@ -624,7 +634,7 @@ func (daemon *Daemon) restore() error {
 
 
 // RestartSwarmContainers restarts any autostart container which has a
 // RestartSwarmContainers restarts any autostart container which has a
 // swarm endpoint.
 // swarm endpoint.
-func (daemon *Daemon) RestartSwarmContainers() {
+func (daemon *Daemon) RestartSwarmContainers(cfg *config.Config) {
 	ctx := context.Background()
 	ctx := context.Background()
 
 
 	// parallelLimit is the maximum number of parallel startup jobs that we
 	// parallelLimit is the maximum number of parallel startup jobs that we
@@ -642,7 +652,7 @@ func (daemon *Daemon) RestartSwarmContainers() {
 			// Autostart all the containers which has a
 			// Autostart all the containers which has a
 			// swarm endpoint now that the cluster is
 			// swarm endpoint now that the cluster is
 			// initialized.
 			// initialized.
-			if daemon.configStore.AutoRestart && c.ShouldRestart() && c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
+			if cfg.AutoRestart && c.ShouldRestart() && c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
 				group.Add(1)
 				group.Add(1)
 				go func(c *container.Container) {
 				go func(c *container.Container) {
 					if err := sem.Acquire(ctx, 1); err != nil {
 					if err := sem.Acquire(ctx, 1); err != nil {
@@ -651,7 +661,7 @@ func (daemon *Daemon) RestartSwarmContainers() {
 						return
 						return
 					}
 					}
 
 
-					if err := daemon.containerStart(ctx, c, "", "", true); err != nil {
+					if err := daemon.containerStart(ctx, cfg, c, "", "", true); err != nil {
 						logrus.WithField("container", c.ID).WithError(err).Error("failed to start swarm container")
 						logrus.WithField("container", c.ID).WithError(err).Error("failed to start swarm container")
 					}
 					}
 
 
@@ -735,10 +745,7 @@ func (daemon *Daemon) setClusterProvider(clusterProvider cluster.Provider) {
 // IsSwarmCompatible verifies if the current daemon
 // IsSwarmCompatible verifies if the current daemon
 // configuration is compatible with the swarm mode
 // configuration is compatible with the swarm mode
 func (daemon *Daemon) IsSwarmCompatible() error {
 func (daemon *Daemon) IsSwarmCompatible() error {
-	if daemon.configStore == nil {
-		return nil
-	}
-	return daemon.configStore.IsSwarmCompatible()
+	return daemon.config().IsSwarmCompatible()
 }
 }
 
 
 // NewDaemon sets up everything for the daemon to be able to service
 // NewDaemon sets up everything for the daemon to be able to service
@@ -800,10 +807,10 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 	}
 	}
 
 
 	d := &Daemon{
 	d := &Daemon{
-		configStore: config,
 		PluginStore: pluginStore,
 		PluginStore: pluginStore,
 		startupDone: make(chan struct{}),
 		startupDone: make(chan struct{}),
 	}
 	}
+	d.configStore.Store(config)
 
 
 	// TEST_INTEGRATION_USE_SNAPSHOTTER is used for integration tests only.
 	// TEST_INTEGRATION_USE_SNAPSHOTTER is used for integration tests only.
 	if os.Getenv("TEST_INTEGRATION_USE_SNAPSHOTTER") != "" {
 	if os.Getenv("TEST_INTEGRATION_USE_SNAPSHOTTER") != "" {
@@ -834,12 +841,12 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 	}
 	}
 	d.setupDumpStackTrap(stackDumpDir)
 	d.setupDumpStackTrap(stackDumpDir)
 
 
-	if err := d.setupSeccompProfile(); err != nil {
+	if err := d.setupSeccompProfile(config); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	// Set the default isolation mode (only applicable on Windows)
 	// Set the default isolation mode (only applicable on Windows)
-	if err := d.setDefaultIsolation(); err != nil {
+	if err := d.setDefaultIsolation(config); err != nil {
 		return nil, fmt.Errorf("error setting default isolation mode: %v", err)
 		return nil, fmt.Errorf("error setting default isolation mode: %v", err)
 	}
 	}
 
 
@@ -881,7 +888,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 	d.registryService = registryService
 	d.registryService = registryService
 	dlogger.RegisterPluginGetter(d.PluginStore)
 	dlogger.RegisterPluginGetter(d.PluginStore)
 
 
-	metricsSockPath, err := d.listenMetricsSock()
+	metricsSockPath, err := d.listenMetricsSock(config)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -942,7 +949,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 			shimOpts interface{}
 			shimOpts interface{}
 		)
 		)
 		if runtime.GOOS != "windows" {
 		if runtime.GOOS != "windows" {
-			shim, shimOpts, err = d.getRuntime(config.GetDefaultRuntimeName())
+			shim, shimOpts, err = d.getRuntime(config, config.DefaultRuntime)
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
 			}
 			}
@@ -965,9 +972,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 		return nil, errors.Wrap(err, "couldn't create plugin manager")
 		return nil, errors.Wrap(err, "couldn't create plugin manager")
 	}
 	}
 
 
-	if err := d.setupDefaultLogConfig(); err != nil {
-		return nil, err
+	d.defaultLogConfig, err = defaultLogConfig(config)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to set log opts")
 	}
 	}
+	logrus.Debugf("Using default logging driver %s", d.defaultLogConfig.Type)
 
 
 	d.volumes, err = volumesservice.NewVolumeService(config.Root, d.PluginStore, rootIDs, d)
 	d.volumes, err = volumesservice.NewVolumeService(config.Root, d.PluginStore, rootIDs, d)
 	if err != nil {
 	if err != nil {
@@ -982,7 +991,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 	// at this point.
 	// at this point.
 	//
 	//
 	// TODO(thaJeztah) add a utility to only collect the CgroupDevicesEnabled information
 	// TODO(thaJeztah) add a utility to only collect the CgroupDevicesEnabled information
-	if runtime.GOOS == "linux" && !userns.RunningInUserNS() && !getSysInfo(d).CgroupDevicesEnabled {
+	if runtime.GOOS == "linux" && !userns.RunningInUserNS() && !getSysInfo(config).CgroupDevicesEnabled {
 		return nil, errors.New("Devices cgroup isn't mounted")
 		return nil, errors.New("Devices cgroup isn't mounted")
 	}
 	}
 
 
@@ -1135,11 +1144,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 
 
 	go d.execCommandGC()
 	go d.execCommandGC()
 
 
-	if err := d.initLibcontainerd(ctx); err != nil {
+	if err := d.initLibcontainerd(ctx, config); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	if err := d.restore(); err != nil {
+	if err := d.restore(config); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	close(d.startupDone)
 	close(d.startupDone)
@@ -1201,7 +1210,11 @@ func (daemon *Daemon) shutdownContainer(c *container.Container) error {
 // A negative (-1) timeout means "indefinitely", which means that containers
 // A negative (-1) timeout means "indefinitely", which means that containers
 // are not forcibly killed, and the daemon shuts down after all containers exit.
 // are not forcibly killed, and the daemon shuts down after all containers exit.
 func (daemon *Daemon) ShutdownTimeout() int {
 func (daemon *Daemon) ShutdownTimeout() int {
-	shutdownTimeout := daemon.configStore.ShutdownTimeout
+	return daemon.shutdownTimeout(daemon.config())
+}
+
+func (daemon *Daemon) shutdownTimeout(cfg *config.Config) int {
+	shutdownTimeout := cfg.ShutdownTimeout
 	if shutdownTimeout < 0 {
 	if shutdownTimeout < 0 {
 		return -1
 		return -1
 	}
 	}
@@ -1228,7 +1241,8 @@ func (daemon *Daemon) Shutdown(ctx context.Context) error {
 	// Keep mounts and networking running on daemon shutdown if
 	// Keep mounts and networking running on daemon shutdown if
 	// we are to keep containers running and restore them.
 	// we are to keep containers running and restore them.
 
 
-	if daemon.configStore.LiveRestoreEnabled && daemon.containers != nil {
+	cfg := daemon.config()
+	if cfg.LiveRestoreEnabled && daemon.containers != nil {
 		// check if there are any running containers, if none we should do some cleanup
 		// check if there are any running containers, if none we should do some cleanup
 		if ls, err := daemon.Containers(ctx, &types.ContainerListOptions{}); len(ls) != 0 || err != nil {
 		if ls, err := daemon.Containers(ctx, &types.ContainerListOptions{}); len(ls) != 0 || err != nil {
 			// metrics plugins still need some cleanup
 			// metrics plugins still need some cleanup
@@ -1238,8 +1252,8 @@ func (daemon *Daemon) Shutdown(ctx context.Context) error {
 	}
 	}
 
 
 	if daemon.containers != nil {
 	if daemon.containers != nil {
-		logrus.Debugf("daemon configured with a %d seconds minimum shutdown timeout", daemon.configStore.ShutdownTimeout)
-		logrus.Debugf("start clean shutdown of all containers with a %d seconds timeout...", daemon.ShutdownTimeout())
+		logrus.Debugf("daemon configured with a %d seconds minimum shutdown timeout", cfg.ShutdownTimeout)
+		logrus.Debugf("start clean shutdown of all containers with a %d seconds timeout...", daemon.shutdownTimeout(cfg))
 		daemon.containers.ApplyAll(func(c *container.Container) {
 		daemon.containers.ApplyAll(func(c *container.Container) {
 			if !c.IsRunning() {
 			if !c.IsRunning() {
 				return
 				return
@@ -1293,7 +1307,7 @@ func (daemon *Daemon) Shutdown(ctx context.Context) error {
 		daemon.mdDB.Close()
 		daemon.mdDB.Close()
 	}
 	}
 
 
-	return daemon.cleanupMounts()
+	return daemon.cleanupMounts(cfg)
 }
 }
 
 
 // Mount sets container.BaseFS
 // Mount sets container.BaseFS
@@ -1374,15 +1388,10 @@ func isBridgeNetworkDisabled(conf *config.Config) bool {
 	return conf.BridgeConfig.Iface == config.DisableNetworkBridge
 	return conf.BridgeConfig.Iface == config.DisableNetworkBridge
 }
 }
 
 
-func (daemon *Daemon) networkOptions(pg plugingetter.PluginGetter, activeSandboxes map[string]interface{}) ([]nwconfig.Option, error) {
-	options := []nwconfig.Option{}
-	if daemon.configStore == nil {
-		return options, nil
-	}
-	conf := daemon.configStore
+func (daemon *Daemon) networkOptions(conf *config.Config, pg plugingetter.PluginGetter, activeSandboxes map[string]interface{}) ([]nwconfig.Option, error) {
 	dd := runconfig.DefaultDaemonNetworkMode()
 	dd := runconfig.DefaultDaemonNetworkMode()
 
 
-	options = []nwconfig.Option{
+	options := []nwconfig.Option{
 		nwconfig.OptionDataDir(conf.Root),
 		nwconfig.OptionDataDir(conf.Root),
 		nwconfig.OptionExecRoot(conf.GetExecRoot()),
 		nwconfig.OptionExecRoot(conf.GetExecRoot()),
 		nwconfig.OptionDefaultDriver(string(dd)),
 		nwconfig.OptionDefaultDriver(string(dd)),
@@ -1514,7 +1523,7 @@ func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo {
 		// We check if sysInfo is not set here, to allow some test to
 		// We check if sysInfo is not set here, to allow some test to
 		// override the actual sysInfo.
 		// override the actual sysInfo.
 		if daemon.sysInfo == nil {
 		if daemon.sysInfo == nil {
-			daemon.sysInfo = getSysInfo(daemon)
+			daemon.sysInfo = getSysInfo(daemon.config())
 		}
 		}
 	})
 	})
 
 

+ 5 - 5
daemon/daemon_linux.go

@@ -76,7 +76,7 @@ func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, u
 }
 }
 
 
 // cleanupMounts umounts used by container resources and the daemon root mount
 // cleanupMounts umounts used by container resources and the daemon root mount
-func (daemon *Daemon) cleanupMounts() error {
+func (daemon *Daemon) cleanupMounts(cfg *config.Config) error {
 	if err := daemon.cleanupMountsByID(""); err != nil {
 	if err := daemon.cleanupMountsByID(""); err != nil {
 		return err
 		return err
 	}
 	}
@@ -100,7 +100,7 @@ func (daemon *Daemon) cleanupMounts() error {
 		return nil
 		return nil
 	}
 	}
 
 
-	unmountFile := getUnmountOnShutdownPath(daemon.configStore)
+	unmountFile := getUnmountOnShutdownPath(cfg)
 	if _, err := os.Stat(unmountFile); err != nil {
 	if _, err := os.Stat(unmountFile); err != nil {
 		return nil
 		return nil
 	}
 	}
@@ -239,14 +239,14 @@ func kernelSupportsRecursivelyReadOnly() error {
 	return kernelSupportsRROErr
 	return kernelSupportsRROErr
 }
 }
 
 
-func (daemon *Daemon) supportsRecursivelyReadOnly(runtime string) error {
+func supportsRecursivelyReadOnly(cfg *config.Config, runtime string) error {
 	if err := kernelSupportsRecursivelyReadOnly(); err != nil {
 	if err := kernelSupportsRecursivelyReadOnly(); err != nil {
 		return fmt.Errorf("rro is not supported: %w (kernel is older than 5.12?)", err)
 		return fmt.Errorf("rro is not supported: %w (kernel is older than 5.12?)", err)
 	}
 	}
 	if runtime == "" {
 	if runtime == "" {
-		runtime = daemon.configStore.GetDefaultRuntimeName()
+		runtime = cfg.DefaultRuntime
 	}
 	}
-	rt := daemon.configStore.GetRuntime(runtime)
+	rt := cfg.GetRuntime(runtime)
 	if rt.Features == nil {
 	if rt.Features == nil {
 		return fmt.Errorf("rro is not supported by runtime %q: OCI features struct is not available", runtime)
 		return fmt.Errorf("rro is not supported by runtime %q: OCI features struct is not available", runtime)
 	}
 	}

+ 7 - 6
daemon/daemon_linux_test.go

@@ -178,7 +178,7 @@ func TestNotCleanupMounts(t *testing.T) {
 func TestValidateContainerIsolationLinux(t *testing.T) {
 func TestValidateContainerIsolationLinux(t *testing.T) {
 	d := Daemon{}
 	d := Daemon{}
 
 
-	_, err := d.verifyContainerSettings(&containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false)
+	_, err := d.verifyContainerSettings(&config.Config{}, &containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false)
 	assert.Check(t, is.Error(err, "invalid isolation 'hyperv' on linux"))
 	assert.Check(t, is.Error(err, "invalid isolation 'hyperv' on linux"))
 }
 }
 
 
@@ -264,7 +264,8 @@ func TestRootMountCleanup(t *testing.T) {
 	err = os.Mkdir(cfg.Root, 0755)
 	err = os.Mkdir(cfg.Root, 0755)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	d := &Daemon{configStore: cfg, root: cfg.Root}
+	d := &Daemon{root: cfg.Root}
+	d.configStore.Store(cfg)
 	unmountFile := getUnmountOnShutdownPath(cfg)
 	unmountFile := getUnmountOnShutdownPath(cfg)
 
 
 	t.Run("regular dir no mountpoint", func(t *testing.T) {
 	t.Run("regular dir no mountpoint", func(t *testing.T) {
@@ -274,7 +275,7 @@ func TestRootMountCleanup(t *testing.T) {
 		assert.NilError(t, err)
 		assert.NilError(t, err)
 		checkMounted(t, cfg.Root, true)
 		checkMounted(t, cfg.Root, true)
 
 
-		assert.Assert(t, d.cleanupMounts())
+		assert.Assert(t, d.cleanupMounts(cfg))
 		checkMounted(t, cfg.Root, false)
 		checkMounted(t, cfg.Root, false)
 
 
 		_, err = os.Stat(unmountFile)
 		_, err = os.Stat(unmountFile)
@@ -292,7 +293,7 @@ func TestRootMountCleanup(t *testing.T) {
 
 
 		_, err = os.Stat(unmountFile)
 		_, err = os.Stat(unmountFile)
 		assert.Assert(t, os.IsNotExist(err))
 		assert.Assert(t, os.IsNotExist(err))
-		assert.Assert(t, d.cleanupMounts())
+		assert.Assert(t, d.cleanupMounts(cfg))
 		checkMounted(t, cfg.Root, true)
 		checkMounted(t, cfg.Root, true)
 	})
 	})
 
 
@@ -309,7 +310,7 @@ func TestRootMountCleanup(t *testing.T) {
 			t.Fatal("unmount file should not exist")
 			t.Fatal("unmount file should not exist")
 		}
 		}
 
 
-		assert.Assert(t, d.cleanupMounts())
+		assert.Assert(t, d.cleanupMounts(cfg))
 		checkMounted(t, cfg.Root, true)
 		checkMounted(t, cfg.Root, true)
 		assert.Assert(t, mount.Unmount(cfg.Root))
 		assert.Assert(t, mount.Unmount(cfg.Root))
 	})
 	})
@@ -328,7 +329,7 @@ func TestRootMountCleanup(t *testing.T) {
 		_, err = os.Stat(unmountFile)
 		_, err = os.Stat(unmountFile)
 		assert.Check(t, os.IsNotExist(err), err)
 		assert.Check(t, os.IsNotExist(err), err)
 		checkMounted(t, cfg.Root, false)
 		checkMounted(t, cfg.Root, false)
-		assert.Assert(t, d.cleanupMounts())
+		assert.Assert(t, d.cleanupMounts(cfg))
 	})
 	})
 }
 }
 
 

+ 2 - 1
daemon/daemon_test.go

@@ -8,6 +8,7 @@ import (
 
 
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libnetwork"
 	"github.com/docker/docker/libnetwork"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
@@ -300,7 +301,7 @@ func TestMerge(t *testing.T) {
 func TestValidateContainerIsolation(t *testing.T) {
 func TestValidateContainerIsolation(t *testing.T) {
 	d := Daemon{}
 	d := Daemon{}
 
 
-	_, err := d.verifyContainerSettings(&containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false)
+	_, err := d.verifyContainerSettings(&config.Config{}, &containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false)
 	assert.Check(t, is.Error(err, "invalid isolation 'invalid' on "+runtime.GOOS))
 	assert.Check(t, is.Error(err, "invalid isolation 'invalid' on "+runtime.GOOS))
 }
 }
 
 

+ 34 - 34
daemon/daemon_unix.go

@@ -189,8 +189,8 @@ func getBlkioWeightDevices(config containertypes.Resources) ([]specs.LinuxWeight
 	return blkioWeightDevices, nil
 	return blkioWeightDevices, nil
 }
 }
 
 
-func (daemon *Daemon) parseSecurityOpt(securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
-	securityOptions.NoNewPrivileges = daemon.configStore.NoNewPrivileges
+func (daemon *Daemon) parseSecurityOpt(cfg *config.Config, securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
+	securityOptions.NoNewPrivileges = cfg.NoNewPrivileges
 	return parseSecurityOpt(securityOptions, hostConfig)
 	return parseSecurityOpt(securityOptions, hostConfig)
 }
 }
 
 
@@ -299,7 +299,7 @@ func adjustParallelLimit(n int, limit int) int {
 
 
 // adaptContainerSettings is called during container creation to modify any
 // adaptContainerSettings is called during container creation to modify any
 // settings necessary in the HostConfig structure.
 // settings necessary in the HostConfig structure.
-func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
+func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
 	if adjustCPUShares && hostConfig.CPUShares > 0 {
 	if adjustCPUShares && hostConfig.CPUShares > 0 {
 		// Handle unsupported CPUShares
 		// Handle unsupported CPUShares
 		if hostConfig.CPUShares < linuxMinCPUShares {
 		if hostConfig.CPUShares < linuxMinCPUShares {
@@ -316,15 +316,15 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
 	}
 	}
 	if hostConfig.ShmSize == 0 {
 	if hostConfig.ShmSize == 0 {
 		hostConfig.ShmSize = config.DefaultShmSize
 		hostConfig.ShmSize = config.DefaultShmSize
-		if daemon.configStore != nil {
-			hostConfig.ShmSize = int64(daemon.configStore.ShmSize)
+		if daemonCfg != nil {
+			hostConfig.ShmSize = int64(daemonCfg.ShmSize)
 		}
 		}
 	}
 	}
 	// Set default IPC mode, if unset for container
 	// Set default IPC mode, if unset for container
 	if hostConfig.IpcMode.IsEmpty() {
 	if hostConfig.IpcMode.IsEmpty() {
 		m := config.DefaultIpcMode
 		m := config.DefaultIpcMode
-		if daemon.configStore != nil {
-			m = containertypes.IpcMode(daemon.configStore.IpcMode)
+		if daemonCfg != nil {
+			m = containertypes.IpcMode(daemonCfg.IpcMode)
 		}
 		}
 		hostConfig.IpcMode = m
 		hostConfig.IpcMode = m
 	}
 	}
@@ -340,8 +340,8 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
 			if cgroups.Mode() == cgroups.Unified {
 			if cgroups.Mode() == cgroups.Unified {
 				m = containertypes.CgroupnsModePrivate
 				m = containertypes.CgroupnsModePrivate
 			}
 			}
-			if daemon.configStore != nil {
-				m = containertypes.CgroupnsMode(daemon.configStore.CgroupNamespaceMode)
+			if daemonCfg != nil {
+				m = containertypes.CgroupnsMode(daemonCfg.CgroupNamespaceMode)
 			}
 			}
 			hostConfig.CgroupnsMode = m
 			hostConfig.CgroupnsMode = m
 		}
 		}
@@ -566,11 +566,11 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, sysIn
 	return warnings, nil
 	return warnings, nil
 }
 }
 
 
-func (daemon *Daemon) getCgroupDriver() string {
-	if UsingSystemd(daemon.configStore) {
+func cgroupDriver(cfg *config.Config) string {
+	if UsingSystemd(cfg) {
 		return cgroupSystemdDriver
 		return cgroupSystemdDriver
 	}
 	}
-	if daemon.Rootless() {
+	if cfg.Rootless {
 		return cgroupNoneDriver
 		return cgroupNoneDriver
 	}
 	}
 	return cgroupFsDriver
 	return cgroupFsDriver
@@ -639,7 +639,7 @@ func isRunningSystemd() bool {
 
 
 // verifyPlatformContainerSettings performs platform-specific validation of the
 // verifyPlatformContainerSettings performs platform-specific validation of the
 // hostconfig and config structures.
 // hostconfig and config structures.
-func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
+func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *config.Config, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
 	if hostConfig == nil {
 	if hostConfig == nil {
 		return nil, nil
 		return nil, nil
 	}
 	}
@@ -680,7 +680,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
 	}
 	}
 
 
 	// check for various conflicting options with user namespaces
 	// check for various conflicting options with user namespaces
-	if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() {
+	if daemonCfg.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() {
 		if hostConfig.Privileged {
 		if hostConfig.Privileged {
 			return warnings, fmt.Errorf("privileged mode is incompatible with user namespaces.  You must run the container in the host namespace when running privileged mode")
 			return warnings, fmt.Errorf("privileged mode is incompatible with user namespaces.  You must run the container in the host namespace when running privileged mode")
 		}
 		}
@@ -691,17 +691,17 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
 			return warnings, fmt.Errorf("cannot share the host PID namespace when user namespaces are enabled")
 			return warnings, fmt.Errorf("cannot share the host PID namespace when user namespaces are enabled")
 		}
 		}
 	}
 	}
-	if hostConfig.CgroupParent != "" && UsingSystemd(daemon.configStore) {
+	if hostConfig.CgroupParent != "" && UsingSystemd(daemonCfg) {
 		// CgroupParent for systemd cgroup should be named as "xxx.slice"
 		// CgroupParent for systemd cgroup should be named as "xxx.slice"
 		if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") {
 		if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") {
 			return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
 			return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
 		}
 		}
 	}
 	}
 	if hostConfig.Runtime == "" {
 	if hostConfig.Runtime == "" {
-		hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
+		hostConfig.Runtime = daemonCfg.DefaultRuntime
 	}
 	}
 
 
-	if _, _, err := daemon.getRuntime(hostConfig.Runtime); err != nil {
+	if _, _, err := daemon.getRuntime(daemonCfg, hostConfig.Runtime); err != nil {
 		return warnings, err
 		return warnings, err
 	}
 	}
 
 
@@ -756,7 +756,7 @@ func verifyDaemonSettings(conf *config.Config) error {
 	}
 	}
 
 
 	configureRuntimes(conf)
 	configureRuntimes(conf)
-	if rtName := conf.GetDefaultRuntimeName(); rtName != "" {
+	if rtName := conf.DefaultRuntime; rtName != "" {
 		if conf.GetRuntime(rtName) == nil {
 		if conf.GetRuntime(rtName) == nil {
 			if !config.IsPermissibleC8dRuntimeName(rtName) {
 			if !config.IsPermissibleC8dRuntimeName(rtName) {
 				return fmt.Errorf("specified default runtime '%s' does not exist", rtName)
 				return fmt.Errorf("specified default runtime '%s' does not exist", rtName)
@@ -837,8 +837,8 @@ func configureKernelSecuritySupport(config *config.Config, driverName string) er
 // initNetworkController initializes the libnetwork controller and configures
 // initNetworkController initializes the libnetwork controller and configures
 // network settings. If there's active sandboxes, configuration changes will not
 // network settings. If there's active sandboxes, configuration changes will not
 // take effect.
 // take effect.
-func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface{}) error {
-	netOptions, err := daemon.networkOptions(daemon.PluginStore, activeSandboxes)
+func (daemon *Daemon) initNetworkController(cfg *config.Config, activeSandboxes map[string]interface{}) error {
+	netOptions, err := daemon.networkOptions(cfg, daemon.PluginStore, activeSandboxes)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -850,12 +850,12 @@ func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface
 
 
 	if len(activeSandboxes) > 0 {
 	if len(activeSandboxes) > 0 {
 		logrus.Info("there are running containers, updated network configuration will not take affect")
 		logrus.Info("there are running containers, updated network configuration will not take affect")
-	} else if err := configureNetworking(daemon.netController, daemon.configStore); err != nil {
+	} else if err := configureNetworking(daemon.netController, cfg); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// Set HostGatewayIP to the default bridge's IP if it is empty
 	// Set HostGatewayIP to the default bridge's IP if it is empty
-	setHostGatewayIP(daemon.netController, daemon.configStore)
+	setHostGatewayIP(daemon.netController, cfg)
 	return nil
 	return nil
 }
 }
 
 
@@ -1410,7 +1410,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container
 
 
 // setDefaultIsolation determines the default isolation mode for the
 // setDefaultIsolation determines the default isolation mode for the
 // daemon to run in. This is only applicable on Windows
 // daemon to run in. This is only applicable on Windows
-func (daemon *Daemon) setDefaultIsolation() error {
+func (daemon *Daemon) setDefaultIsolation(*config.Config) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -1443,14 +1443,14 @@ func setMayDetachMounts() error {
 	return err
 	return err
 }
 }
 
 
-func (daemon *Daemon) initCPURtController(mnt, path string) error {
+func (daemon *Daemon) initCPURtController(cfg *config.Config, mnt, path string) error {
 	if path == "/" || path == "." {
 	if path == "/" || path == "." {
 		return nil
 		return nil
 	}
 	}
 
 
 	// Recursively create cgroup to ensure that the system and all parent cgroups have values set
 	// Recursively create cgroup to ensure that the system and all parent cgroups have values set
 	// for the period and runtime as this limits what the children can be set to.
 	// for the period and runtime as this limits what the children can be set to.
-	if err := daemon.initCPURtController(mnt, filepath.Dir(path)); err != nil {
+	if err := daemon.initCPURtController(cfg, mnt, filepath.Dir(path)); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -1458,10 +1458,10 @@ func (daemon *Daemon) initCPURtController(mnt, path string) error {
 	if err := os.MkdirAll(path, 0755); err != nil {
 	if err := os.MkdirAll(path, 0755); err != nil {
 		return err
 		return err
 	}
 	}
-	if err := maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
+	if err := maybeCreateCPURealTimeFile(cfg.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
 		return err
 		return err
 	}
 	}
-	return maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path)
+	return maybeCreateCPURealTimeFile(cfg.CPURealtimeRuntime, "cpu.rt_runtime_us", path)
 }
 }
 
 
 func maybeCreateCPURealTimeFile(configValue int64, file string, path string) error {
 func maybeCreateCPURealTimeFile(configValue int64, file string, path string) error {
@@ -1471,8 +1471,8 @@ func maybeCreateCPURealTimeFile(configValue int64, file string, path string) err
 	return os.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700)
 	return os.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700)
 }
 }
 
 
-func (daemon *Daemon) setupSeccompProfile() error {
-	switch profile := daemon.configStore.SeccompProfile; profile {
+func (daemon *Daemon) setupSeccompProfile(cfg *config.Config) error {
+	switch profile := cfg.SeccompProfile; profile {
 	case "", config.SeccompProfileDefault:
 	case "", config.SeccompProfileDefault:
 		daemon.seccompProfilePath = config.SeccompProfileDefault
 		daemon.seccompProfilePath = config.SeccompProfileDefault
 	case config.SeccompProfileUnconfined:
 	case config.SeccompProfileUnconfined:
@@ -1488,9 +1488,9 @@ func (daemon *Daemon) setupSeccompProfile() error {
 	return nil
 	return nil
 }
 }
 
 
-func getSysInfo(daemon *Daemon) *sysinfo.SysInfo {
+func getSysInfo(cfg *config.Config) *sysinfo.SysInfo {
 	var siOpts []sysinfo.Opt
 	var siOpts []sysinfo.Opt
-	if daemon.getCgroupDriver() == cgroupSystemdDriver {
+	if cgroupDriver(cfg) == cgroupSystemdDriver {
 		if euid := os.Getenv("ROOTLESSKIT_PARENT_EUID"); euid != "" {
 		if euid := os.Getenv("ROOTLESSKIT_PARENT_EUID"); euid != "" {
 			siOpts = append(siOpts, sysinfo.WithCgroup2GroupPath("/user.slice/user-"+euid+".slice"))
 			siOpts = append(siOpts, sysinfo.WithCgroup2GroupPath("/user.slice/user-"+euid+".slice"))
 		}
 		}
@@ -1498,13 +1498,13 @@ func getSysInfo(daemon *Daemon) *sysinfo.SysInfo {
 	return sysinfo.New(siOpts...)
 	return sysinfo.New(siOpts...)
 }
 }
 
 
-func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
+func (daemon *Daemon) initLibcontainerd(ctx context.Context, cfg *config.Config) error {
 	var err error
 	var err error
 	daemon.containerd, err = remote.NewClient(
 	daemon.containerd, err = remote.NewClient(
 		ctx,
 		ctx,
 		daemon.containerdCli,
 		daemon.containerdCli,
-		filepath.Join(daemon.configStore.ExecRoot, "containerd"),
-		daemon.configStore.ContainerdNamespace,
+		filepath.Join(cfg.ExecRoot, "containerd"),
+		cfg.ContainerdNamespace,
 		daemon,
 		daemon,
 	)
 	)
 	return err
 	return err

+ 16 - 14
daemon/daemon_unix_test.go

@@ -68,30 +68,31 @@ func TestAdjustCPUShares(t *testing.T) {
 		repository: tmp,
 		repository: tmp,
 		root:       tmp,
 		root:       tmp,
 	}
 	}
+	cfg := &config.Config{}
 	muteLogs()
 	muteLogs()
 
 
 	hostConfig := &containertypes.HostConfig{
 	hostConfig := &containertypes.HostConfig{
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
 	}
 	}
-	daemon.adaptContainerSettings(hostConfig, true)
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
 	if hostConfig.CPUShares != linuxMinCPUShares {
 	if hostConfig.CPUShares != linuxMinCPUShares {
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
 	}
 	}
 
 
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
-	daemon.adaptContainerSettings(hostConfig, true)
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
 	if hostConfig.CPUShares != linuxMaxCPUShares {
 	if hostConfig.CPUShares != linuxMaxCPUShares {
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
 	}
 	}
 
 
 	hostConfig.CPUShares = 0
 	hostConfig.CPUShares = 0
-	daemon.adaptContainerSettings(hostConfig, true)
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
 	if hostConfig.CPUShares != 0 {
 	if hostConfig.CPUShares != 0 {
 		t.Error("Expected CPUShares to be unchanged")
 		t.Error("Expected CPUShares to be unchanged")
 	}
 	}
 
 
 	hostConfig.CPUShares = 1024
 	hostConfig.CPUShares = 1024
-	daemon.adaptContainerSettings(hostConfig, true)
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
 	if hostConfig.CPUShares != 1024 {
 	if hostConfig.CPUShares != 1024 {
 		t.Error("Expected CPUShares to be unchanged")
 		t.Error("Expected CPUShares to be unchanged")
 	}
 	}
@@ -108,29 +109,30 @@ func TestAdjustCPUSharesNoAdjustment(t *testing.T) {
 		repository: tmp,
 		repository: tmp,
 		root:       tmp,
 		root:       tmp,
 	}
 	}
+	cfg := &config.Config{}
 
 
 	hostConfig := &containertypes.HostConfig{
 	hostConfig := &containertypes.HostConfig{
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
 	}
 	}
-	daemon.adaptContainerSettings(hostConfig, false)
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
 	if hostConfig.CPUShares != linuxMinCPUShares-1 {
 	if hostConfig.CPUShares != linuxMinCPUShares-1 {
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
 	}
 	}
 
 
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
-	daemon.adaptContainerSettings(hostConfig, false)
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
 	if hostConfig.CPUShares != linuxMaxCPUShares+1 {
 	if hostConfig.CPUShares != linuxMaxCPUShares+1 {
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
 	}
 	}
 
 
 	hostConfig.CPUShares = 0
 	hostConfig.CPUShares = 0
-	daemon.adaptContainerSettings(hostConfig, false)
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
 	if hostConfig.CPUShares != 0 {
 	if hostConfig.CPUShares != 0 {
 		t.Error("Expected CPUShares to be unchanged")
 		t.Error("Expected CPUShares to be unchanged")
 	}
 	}
 
 
 	hostConfig.CPUShares = 1024
 	hostConfig.CPUShares = 1024
-	daemon.adaptContainerSettings(hostConfig, false)
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
 	if hostConfig.CPUShares != 1024 {
 	if hostConfig.CPUShares != 1024 {
 		t.Error("Expected CPUShares to be unchanged")
 		t.Error("Expected CPUShares to be unchanged")
 	}
 	}
@@ -243,16 +245,16 @@ func TestParseSecurityOpt(t *testing.T) {
 }
 }
 
 
 func TestParseNNPSecurityOptions(t *testing.T) {
 func TestParseNNPSecurityOptions(t *testing.T) {
-	daemon := &Daemon{
-		configStore: &config.Config{NoNewPrivileges: true},
-	}
+	daemonCfg := &config.Config{NoNewPrivileges: true}
+	daemon := &Daemon{}
+	daemon.configStore.Store(daemonCfg)
 	opts := &container.SecurityOptions{}
 	opts := &container.SecurityOptions{}
 	cfg := &containertypes.HostConfig{}
 	cfg := &containertypes.HostConfig{}
 
 
 	// test NNP when "daemon:true" and "no-new-privileges=false""
 	// test NNP when "daemon:true" and "no-new-privileges=false""
 	cfg.SecurityOpt = []string{"no-new-privileges=false"}
 	cfg.SecurityOpt = []string{"no-new-privileges=false"}
 
 
-	if err := daemon.parseSecurityOpt(opts, cfg); err != nil {
+	if err := daemon.parseSecurityOpt(daemonCfg, opts, cfg); err != nil {
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
 	}
 	}
 	if opts.NoNewPrivileges {
 	if opts.NoNewPrivileges {
@@ -260,10 +262,10 @@ func TestParseNNPSecurityOptions(t *testing.T) {
 	}
 	}
 
 
 	// test NNP when "daemon:false" and "no-new-privileges=true""
 	// test NNP when "daemon:false" and "no-new-privileges=true""
-	daemon.configStore.NoNewPrivileges = false
+	daemonCfg.NoNewPrivileges = false
 	cfg.SecurityOpt = []string{"no-new-privileges=true"}
 	cfg.SecurityOpt = []string{"no-new-privileges=true"}
 
 
-	if err := daemon.parseSecurityOpt(opts, cfg); err != nil {
+	if err := daemon.parseSecurityOpt(daemonCfg, opts, cfg); err != nil {
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
 	}
 	}
 	if !opts.NoNewPrivileges {
 	if !opts.NoNewPrivileges {

+ 0 - 4
daemon/daemon_unsupported.go

@@ -17,7 +17,3 @@ func setupResolvConf(_ *interface{}) {}
 func getSysInfo(_ *Daemon) *sysinfo.SysInfo {
 func getSysInfo(_ *Daemon) *sysinfo.SysInfo {
 	return sysinfo.New()
 	return sysinfo.New()
 }
 }
-
-func (daemon *Daemon) supportsRecursivelyReadOnly(_ string) error {
-	return nil
-}

+ 20 - 24
daemon/daemon_windows.go

@@ -55,7 +55,7 @@ func getPluginExecRoot(cfg *config.Config) string {
 	return filepath.Join(cfg.Root, "plugins")
 	return filepath.Join(cfg.Root, "plugins")
 }
 }
 
 
-func (daemon *Daemon) parseSecurityOpt(securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
+func (daemon *Daemon) parseSecurityOpt(daemonCfg *config.Config, securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -65,7 +65,7 @@ func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error {
 
 
 // adaptContainerSettings is called during container creation to modify any
 // adaptContainerSettings is called during container creation to modify any
 // settings necessary in the HostConfig structure.
 // settings necessary in the HostConfig structure.
-func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
+func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -171,7 +171,7 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, isHyp
 
 
 // verifyPlatformContainerSettings performs platform-specific validation of the
 // verifyPlatformContainerSettings performs platform-specific validation of the
 // hostconfig and config structures.
 // hostconfig and config structures.
-func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
+func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *config.Config, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
 	if hostConfig == nil {
 	if hostConfig == nil {
 		return nil, nil
 		return nil, nil
 	}
 	}
@@ -232,8 +232,8 @@ func configureMaxThreads(config *config.Config) error {
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface{}) error {
-	netOptions, err := daemon.networkOptions(nil, nil)
+func (daemon *Daemon) initNetworkController(daemonCfg *config.Config, activeSandboxes map[string]interface{}) error {
+	netOptions, err := daemon.networkOptions(daemonCfg, nil, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -396,9 +396,9 @@ func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface
 		}
 		}
 	}
 	}
 
 
-	if !daemon.configStore.DisableBridge {
+	if !daemonCfg.DisableBridge {
 		// Initialize default driver "bridge"
 		// Initialize default driver "bridge"
-		if err := initBridgeDriver(daemon.netController, daemon.configStore); err != nil {
+		if err := initBridgeDriver(daemon.netController, daemonCfg); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -452,7 +452,7 @@ func (daemon *Daemon) cleanupMountsByID(in string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) cleanupMounts() error {
+func (daemon *Daemon) cleanupMounts(*config.Config) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -512,7 +512,7 @@ func driverOptions(_ *config.Config) nwconfig.Option {
 
 
 // setDefaultIsolation determine the default isolation mode for the
 // setDefaultIsolation determine the default isolation mode for the
 // daemon to run in. This is only applicable on Windows
 // daemon to run in. This is only applicable on Windows
-func (daemon *Daemon) setDefaultIsolation() error {
+func (daemon *Daemon) setDefaultIsolation(config *config.Config) error {
 	// On client SKUs, default to Hyper-V. @engine maintainers. This
 	// On client SKUs, default to Hyper-V. @engine maintainers. This
 	// should not be removed. Ping Microsoft folks is there are PRs to
 	// should not be removed. Ping Microsoft folks is there are PRs to
 	// to change this.
 	// to change this.
@@ -521,7 +521,7 @@ func (daemon *Daemon) setDefaultIsolation() error {
 	} else {
 	} else {
 		daemon.defaultIsolation = containertypes.IsolationProcess
 		daemon.defaultIsolation = containertypes.IsolationProcess
 	}
 	}
-	for _, option := range daemon.configStore.ExecOptions {
+	for _, option := range config.ExecOptions {
 		key, val, err := parsers.ParseKeyValueOpt(option)
 		key, val, err := parsers.ParseKeyValueOpt(option)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -552,7 +552,7 @@ func setMayDetachMounts() error {
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) setupSeccompProfile() error {
+func (daemon *Daemon) setupSeccompProfile(*config.Config) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -562,16 +562,16 @@ func (daemon *Daemon) loadRuntimes() error {
 
 
 func setupResolvConf(config *config.Config) {}
 func setupResolvConf(config *config.Config) {}
 
 
-func getSysInfo(daemon *Daemon) *sysinfo.SysInfo {
+func getSysInfo(*config.Config) *sysinfo.SysInfo {
 	return sysinfo.New()
 	return sysinfo.New()
 }
 }
 
 
-func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
+func (daemon *Daemon) initLibcontainerd(ctx context.Context, cfg *config.Config) error {
 	var err error
 	var err error
 
 
-	rt := daemon.configStore.GetDefaultRuntimeName()
+	rt := cfg.DefaultRuntime
 	if rt == "" {
 	if rt == "" {
-		if daemon.configStore.ContainerdAddr == "" {
+		if cfg.ContainerdAddr == "" {
 			rt = windowsV1RuntimeName
 			rt = windowsV1RuntimeName
 		} else {
 		} else {
 			rt = windowsV2RuntimeName
 			rt = windowsV2RuntimeName
@@ -583,19 +583,19 @@ func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
 		daemon.containerd, err = local.NewClient(
 		daemon.containerd, err = local.NewClient(
 			ctx,
 			ctx,
 			daemon.containerdCli,
 			daemon.containerdCli,
-			filepath.Join(daemon.configStore.ExecRoot, "containerd"),
-			daemon.configStore.ContainerdNamespace,
+			filepath.Join(cfg.ExecRoot, "containerd"),
+			cfg.ContainerdNamespace,
 			daemon,
 			daemon,
 		)
 		)
 	case windowsV2RuntimeName:
 	case windowsV2RuntimeName:
-		if daemon.configStore.ContainerdAddr == "" {
+		if cfg.ContainerdAddr == "" {
 			return fmt.Errorf("cannot use the specified runtime %q without containerd", rt)
 			return fmt.Errorf("cannot use the specified runtime %q without containerd", rt)
 		}
 		}
 		daemon.containerd, err = remote.NewClient(
 		daemon.containerd, err = remote.NewClient(
 			ctx,
 			ctx,
 			daemon.containerdCli,
 			daemon.containerdCli,
-			filepath.Join(daemon.configStore.ExecRoot, "containerd"),
-			daemon.configStore.ContainerdNamespace,
+			filepath.Join(cfg.ExecRoot, "containerd"),
+			cfg.ContainerdNamespace,
 			daemon,
 			daemon,
 		)
 		)
 	default:
 	default:
@@ -604,7 +604,3 @@ func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
 
 
 	return err
 	return err
 }
 }
-
-func (daemon *Daemon) supportsRecursivelyReadOnly(_ string) error {
-	return nil
-}

+ 10 - 5
daemon/delete.go

@@ -12,6 +12,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/opencontainers/selinux/go-selinux"
 	"github.com/opencontainers/selinux/go-selinux"
@@ -24,6 +25,10 @@ import (
 // fails. If the remove succeeds, the container name is released, and
 // fails. If the remove succeeds, the container name is released, and
 // network links are removed.
 // network links are removed.
 func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error {
 func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error {
+	return daemon.containerRm(daemon.config(), name, config)
+}
+
+func (daemon *Daemon) containerRm(cfg *config.Config, name string, opts *types.ContainerRmConfig) error {
 	start := time.Now()
 	start := time.Now()
 	ctr, err := daemon.GetContainer(name)
 	ctr, err := daemon.GetContainer(name)
 	if err != nil {
 	if err != nil {
@@ -42,17 +47,17 @@ func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig)
 		return nil
 		return nil
 	}
 	}
 
 
-	if config.RemoveLink {
-		return daemon.rmLink(ctr, name)
+	if opts.RemoveLink {
+		return daemon.rmLink(cfg, ctr, name)
 	}
 	}
 
 
-	err = daemon.cleanupContainer(ctr, *config)
+	err = daemon.cleanupContainer(ctr, *opts)
 	containerActions.WithValues("delete").UpdateSince(start)
 	containerActions.WithValues("delete").UpdateSince(start)
 
 
 	return err
 	return err
 }
 }
 
 
-func (daemon *Daemon) rmLink(container *container.Container, name string) error {
+func (daemon *Daemon) rmLink(cfg *config.Config, container *container.Container, name string) error {
 	if name[0] != '/' {
 	if name[0] != '/' {
 		name = "/" + name
 		name = "/" + name
 	}
 	}
@@ -71,7 +76,7 @@ func (daemon *Daemon) rmLink(container *container.Container, name string) error
 	parentContainer, _ := daemon.GetContainer(pe)
 	parentContainer, _ := daemon.GetContainer(pe)
 	if parentContainer != nil {
 	if parentContainer != nil {
 		daemon.linkIndex.unlink(name, container, parentContainer)
 		daemon.linkIndex.unlink(name, container, parentContainer)
-		if err := daemon.updateNetwork(parentContainer); err != nil {
+		if err := daemon.updateNetwork(cfg, parentContainer); err != nil {
 			logrus.Debugf("Could not update network to remove link %s: %v", n, err)
 			logrus.Debugf("Could not update network to remove link %s: %v", n, err)
 		}
 		}
 	}
 	}

+ 2 - 1
daemon/exec.go

@@ -252,7 +252,8 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio
 		p.Cwd = "/"
 		p.Cwd = "/"
 	}
 	}
 
 
-	if err := daemon.execSetPlatformOpt(ctx, ec, p); err != nil {
+	daemonCfg := daemon.config()
+	if err := daemon.execSetPlatformOpt(ctx, daemonCfg, ec, p); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 3 - 2
daemon/exec_linux.go

@@ -9,6 +9,7 @@ import (
 	coci "github.com/containerd/containerd/oci"
 	coci "github.com/containerd/containerd/oci"
 	"github.com/containerd/containerd/pkg/apparmor"
 	"github.com/containerd/containerd/pkg/apparmor"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/oci/caps"
 	"github.com/docker/docker/oci/caps"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 )
 )
@@ -50,7 +51,7 @@ func getUserFromContainerd(ctx context.Context, containerdCli *containerd.Client
 	return spec.Process.User, nil
 	return spec.Process.User, nil
 }
 }
 
 
-func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, ec *container.ExecConfig, p *specs.Process) error {
+func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, daemonCfg *config.Config, ec *container.ExecConfig, p *specs.Process) error {
 	if len(ec.User) > 0 {
 	if len(ec.User) > 0 {
 		var err error
 		var err error
 		if daemon.UsesSnapshotter() {
 		if daemon.UsesSnapshotter() {
@@ -100,5 +101,5 @@ func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, ec *container.Exec
 		p.ApparmorProfile = appArmorProfile
 		p.ApparmorProfile = appArmorProfile
 	}
 	}
 	s := &specs.Spec{Process: p}
 	s := &specs.Spec{Process: p}
-	return WithRlimits(daemon, ec.Container)(ctx, nil, nil, s)
+	return withRlimits(daemon, daemonCfg, ec.Container)(ctx, nil, nil, s)
 }
 }

+ 4 - 2
daemon/exec_linux_test.go

@@ -50,7 +50,9 @@ func TestExecSetPlatformOptAppArmor(t *testing.T) {
 		},
 		},
 	}
 	}
 
 
-	d := &Daemon{configStore: &config.Config{}}
+	cfg := &config.Config{}
+	d := &Daemon{}
+	d.configStore.Store(cfg)
 
 
 	// Currently, `docker exec --privileged` inherits the Privileged configuration
 	// Currently, `docker exec --privileged` inherits the Privileged configuration
 	// of the container, and does not disable AppArmor.
 	// of the container, and does not disable AppArmor.
@@ -81,7 +83,7 @@ func TestExecSetPlatformOptAppArmor(t *testing.T) {
 				ec := &container.ExecConfig{Container: c, Privileged: execPrivileged}
 				ec := &container.ExecConfig{Container: c, Privileged: execPrivileged}
 				p := &specs.Process{}
 				p := &specs.Process{}
 
 
-				err := d.execSetPlatformOpt(context.Background(), ec, p)
+				err := d.execSetPlatformOpt(context.Background(), cfg, ec, p)
 				assert.NilError(t, err)
 				assert.NilError(t, err)
 				assert.Equal(t, p.ApparmorProfile, tc.expectedProfile)
 				assert.Equal(t, p.ApparmorProfile, tc.expectedProfile)
 			})
 			})

+ 2 - 1
daemon/exec_windows.go

@@ -4,10 +4,11 @@ import (
 	"context"
 	"context"
 
 
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 )
 )
 
 
-func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, ec *container.ExecConfig, p *specs.Process) error {
+func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, daemonCfg *config.Config, ec *container.ExecConfig, p *specs.Process) error {
 	if ec.Container.OS == "windows" {
 	if ec.Container.OS == "windows" {
 		p.User.Username = ec.User
 		p.User.Username = ec.User
 	}
 	}

+ 26 - 25
daemon/info.go

@@ -30,6 +30,7 @@ func (daemon *Daemon) SystemInfo() *types.Info {
 	defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))()
 	defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))()
 
 
 	sysInfo := daemon.RawSysInfo()
 	sysInfo := daemon.RawSysInfo()
+	cfg := daemon.config()
 
 
 	v := &types.Info{
 	v := &types.Info{
 		ID:                 daemon.id,
 		ID:                 daemon.id,
@@ -50,27 +51,27 @@ func (daemon *Daemon) SystemInfo() *types.Info {
 		NCPU:               sysinfo.NumCPU(),
 		NCPU:               sysinfo.NumCPU(),
 		MemTotal:           memInfo().MemTotal,
 		MemTotal:           memInfo().MemTotal,
 		GenericResources:   daemon.genericResources,
 		GenericResources:   daemon.genericResources,
-		DockerRootDir:      daemon.configStore.Root,
-		Labels:             daemon.configStore.Labels,
-		ExperimentalBuild:  daemon.configStore.Experimental,
+		DockerRootDir:      cfg.Root,
+		Labels:             cfg.Labels,
+		ExperimentalBuild:  cfg.Experimental,
 		ServerVersion:      dockerversion.Version,
 		ServerVersion:      dockerversion.Version,
-		HTTPProxy:          config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPProxy, "HTTP_PROXY", "http_proxy")),
-		HTTPSProxy:         config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPSProxy, "HTTPS_PROXY", "https_proxy")),
-		NoProxy:            getConfigOrEnv(daemon.configStore.NoProxy, "NO_PROXY", "no_proxy"),
-		LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled,
+		HTTPProxy:          config.MaskCredentials(getConfigOrEnv(cfg.HTTPProxy, "HTTP_PROXY", "http_proxy")),
+		HTTPSProxy:         config.MaskCredentials(getConfigOrEnv(cfg.HTTPSProxy, "HTTPS_PROXY", "https_proxy")),
+		NoProxy:            getConfigOrEnv(cfg.NoProxy, "NO_PROXY", "no_proxy"),
+		LiveRestoreEnabled: cfg.LiveRestoreEnabled,
 		Isolation:          daemon.defaultIsolation,
 		Isolation:          daemon.defaultIsolation,
 	}
 	}
 
 
 	daemon.fillContainerStates(v)
 	daemon.fillContainerStates(v)
 	daemon.fillDebugInfo(v)
 	daemon.fillDebugInfo(v)
-	daemon.fillAPIInfo(v)
+	daemon.fillAPIInfo(v, cfg)
 	// Retrieve platform specific info
 	// Retrieve platform specific info
-	daemon.fillPlatformInfo(v, sysInfo)
+	daemon.fillPlatformInfo(v, sysInfo, cfg)
 	daemon.fillDriverInfo(v)
 	daemon.fillDriverInfo(v)
-	daemon.fillPluginsInfo(v)
-	daemon.fillSecurityOptions(v, sysInfo)
+	daemon.fillPluginsInfo(v, cfg)
+	daemon.fillSecurityOptions(v, sysInfo, cfg)
 	daemon.fillLicense(v)
 	daemon.fillLicense(v)
-	daemon.fillDefaultAddressPools(v)
+	daemon.fillDefaultAddressPools(v, cfg)
 
 
 	return v
 	return v
 }
 }
@@ -80,6 +81,7 @@ func (daemon *Daemon) SystemVersion() types.Version {
 	defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))()
 	defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))()
 
 
 	kernelVersion := kernelVersion()
 	kernelVersion := kernelVersion()
+	cfg := daemon.config()
 
 
 	v := types.Version{
 	v := types.Version{
 		Components: []types.ComponentVersion{
 		Components: []types.ComponentVersion{
@@ -95,7 +97,7 @@ func (daemon *Daemon) SystemVersion() types.Version {
 					"Arch":          runtime.GOARCH,
 					"Arch":          runtime.GOARCH,
 					"BuildTime":     dockerversion.BuildTime,
 					"BuildTime":     dockerversion.BuildTime,
 					"KernelVersion": kernelVersion,
 					"KernelVersion": kernelVersion,
-					"Experimental":  fmt.Sprintf("%t", daemon.configStore.Experimental),
+					"Experimental":  fmt.Sprintf("%t", cfg.Experimental),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -110,12 +112,12 @@ func (daemon *Daemon) SystemVersion() types.Version {
 		Arch:          runtime.GOARCH,
 		Arch:          runtime.GOARCH,
 		BuildTime:     dockerversion.BuildTime,
 		BuildTime:     dockerversion.BuildTime,
 		KernelVersion: kernelVersion,
 		KernelVersion: kernelVersion,
-		Experimental:  daemon.configStore.Experimental,
+		Experimental:  cfg.Experimental,
 	}
 	}
 
 
 	v.Platform.Name = dockerversion.PlatformName
 	v.Platform.Name = dockerversion.PlatformName
 
 
-	daemon.fillPlatformVersion(&v)
+	daemon.fillPlatformVersion(&v, cfg)
 	return v
 	return v
 }
 }
 
 
@@ -135,19 +137,19 @@ WARNING: The %s storage-driver is deprecated, and will be removed in a future re
 	fillDriverWarnings(v)
 	fillDriverWarnings(v)
 }
 }
 
 
-func (daemon *Daemon) fillPluginsInfo(v *types.Info) {
+func (daemon *Daemon) fillPluginsInfo(v *types.Info, cfg *config.Config) {
 	v.Plugins = types.PluginsInfo{
 	v.Plugins = types.PluginsInfo{
 		Volume:  daemon.volumes.GetDriverList(),
 		Volume:  daemon.volumes.GetDriverList(),
 		Network: daemon.GetNetworkDriverList(),
 		Network: daemon.GetNetworkDriverList(),
 
 
 		// The authorization plugins are returned in the order they are
 		// The authorization plugins are returned in the order they are
 		// used as they constitute a request/response modification chain.
 		// used as they constitute a request/response modification chain.
-		Authorization: daemon.configStore.AuthorizationPlugins,
+		Authorization: cfg.AuthorizationPlugins,
 		Log:           logger.ListDrivers(),
 		Log:           logger.ListDrivers(),
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInfo) {
+func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
 	var securityOptions []string
 	var securityOptions []string
 	if sysInfo.AppArmor {
 	if sysInfo.AppArmor {
 		securityOptions = append(securityOptions, "name=apparmor")
 		securityOptions = append(securityOptions, "name=apparmor")
@@ -164,13 +166,13 @@ func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInf
 	if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
 	if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
 		securityOptions = append(securityOptions, "name=userns")
 		securityOptions = append(securityOptions, "name=userns")
 	}
 	}
-	if daemon.Rootless() {
+	if Rootless(cfg) {
 		securityOptions = append(securityOptions, "name=rootless")
 		securityOptions = append(securityOptions, "name=rootless")
 	}
 	}
-	if daemon.cgroupNamespacesEnabled(sysInfo) {
+	if cgroupNamespacesEnabled(sysInfo, cfg) {
 		securityOptions = append(securityOptions, "name=cgroupns")
 		securityOptions = append(securityOptions, "name=cgroupns")
 	}
 	}
-	if daemon.noNewPrivileges() {
+	if noNewPrivileges(cfg) {
 		securityOptions = append(securityOptions, "name=no-new-privileges")
 		securityOptions = append(securityOptions, "name=no-new-privileges")
 	}
 	}
 
 
@@ -200,13 +202,12 @@ func (daemon *Daemon) fillDebugInfo(v *types.Info) {
 	v.NEventsListener = daemon.EventsService.SubscribersCount()
 	v.NEventsListener = daemon.EventsService.SubscribersCount()
 }
 }
 
 
-func (daemon *Daemon) fillAPIInfo(v *types.Info) {
+func (daemon *Daemon) fillAPIInfo(v *types.Info, cfg *config.Config) {
 	const warn string = `
 	const warn string = `
          Access to the remote API is equivalent to root access on the host. Refer
          Access to the remote API is equivalent to root access on the host. Refer
          to the 'Docker daemon attack surface' section in the documentation for
          to the 'Docker daemon attack surface' section in the documentation for
          more information: https://docs.docker.com/go/attack-surface/`
          more information: https://docs.docker.com/go/attack-surface/`
 
 
-	cfg := daemon.configStore
 	for _, host := range cfg.Hosts {
 	for _, host := range cfg.Hosts {
 		// cnf.Hosts is normalized during startup, so should always have a scheme/proto
 		// cnf.Hosts is normalized during startup, so should always have a scheme/proto
 		proto, addr, _ := strings.Cut(host, "://")
 		proto, addr, _ := strings.Cut(host, "://")
@@ -224,8 +225,8 @@ func (daemon *Daemon) fillAPIInfo(v *types.Info) {
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) fillDefaultAddressPools(v *types.Info) {
-	for _, pool := range daemon.configStore.DefaultAddressPools.Value() {
+func (daemon *Daemon) fillDefaultAddressPools(v *types.Info, cfg *config.Config) {
+	for _, pool := range cfg.DefaultAddressPools.Value() {
 		v.DefaultAddressPools = append(v.DefaultAddressPools, types.NetworkAddressPool{
 		v.DefaultAddressPools = append(v.DefaultAddressPools, types.NetworkAddressPool{
 			Base: pool.Base,
 			Base: pool.Base,
 			Size: pool.Size,
 			Size: pool.Size,

+ 18 - 17
daemon/info_unix.go

@@ -11,6 +11,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/pkg/rootless"
 	"github.com/docker/docker/pkg/rootless"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
@@ -18,8 +19,8 @@ import (
 )
 )
 
 
 // fillPlatformInfo fills the platform related info.
 // fillPlatformInfo fills the platform related info.
-func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) {
-	v.CgroupDriver = daemon.getCgroupDriver()
+func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
+	v.CgroupDriver = cgroupDriver(cfg)
 	v.CgroupVersion = "1"
 	v.CgroupVersion = "1"
 	if sysInfo.CgroupUnified {
 	if sysInfo.CgroupUnified {
 		v.CgroupVersion = "2"
 		v.CgroupVersion = "2"
@@ -37,13 +38,13 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
 		v.CPUSet = sysInfo.Cpuset
 		v.CPUSet = sysInfo.Cpuset
 		v.PidsLimit = sysInfo.PidsLimit
 		v.PidsLimit = sysInfo.PidsLimit
 	}
 	}
-	v.Runtimes = daemon.configStore.GetAllRuntimes()
-	v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName()
+	v.Runtimes = cfg.GetAllRuntimes()
+	v.DefaultRuntime = cfg.DefaultRuntime
 	v.RuncCommit.ID = "N/A"
 	v.RuncCommit.ID = "N/A"
 	v.ContainerdCommit.ID = "N/A"
 	v.ContainerdCommit.ID = "N/A"
 	v.InitCommit.ID = "N/A"
 	v.InitCommit.ID = "N/A"
 
 
-	if rt := daemon.configStore.GetRuntime(v.DefaultRuntime); rt != nil {
+	if rt := cfg.GetRuntime(v.DefaultRuntime); rt != nil {
 		if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
 		if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
 			if _, _, commit, err := parseRuntimeVersion(string(rv)); err != nil {
 			if _, _, commit, err := parseRuntimeVersion(string(rv)); err != nil {
 				logrus.Warnf("failed to parse %s version: %v", rt.Path, err)
 				logrus.Warnf("failed to parse %s version: %v", rt.Path, err)
@@ -61,8 +62,8 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
 		logrus.Warnf("failed to retrieve containerd version: %v", err)
 		logrus.Warnf("failed to retrieve containerd version: %v", err)
 	}
 	}
 
 
-	v.InitBinary = daemon.configStore.GetInitPath()
-	if initBinary, err := daemon.configStore.LookupInitPath(); err != nil {
+	v.InitBinary = cfg.GetInitPath()
+	if initBinary, err := cfg.LookupInitPath(); err != nil {
 		logrus.Warnf("failed to find docker-init: %s", err)
 		logrus.Warnf("failed to find docker-init: %s", err)
 	} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
 	} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
 		if _, commit, err := parseInitVersion(string(rv)); err != nil {
 		if _, commit, err := parseInitVersion(string(rv)); err != nil {
@@ -165,7 +166,7 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
+func (daemon *Daemon) fillPlatformVersion(v *types.Version, cfg *config.Config) {
 	if rv, err := daemon.containerd.Version(context.Background()); err == nil {
 	if rv, err := daemon.containerd.Version(context.Background()); err == nil {
 		v.Components = append(v.Components, types.ComponentVersion{
 		v.Components = append(v.Components, types.ComponentVersion{
 			Name:    "containerd",
 			Name:    "containerd",
@@ -176,8 +177,8 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
 		})
 		})
 	}
 	}
 
 
-	defaultRuntime := daemon.configStore.GetDefaultRuntimeName()
-	if rt := daemon.configStore.GetRuntime(defaultRuntime); rt != nil {
+	defaultRuntime := cfg.DefaultRuntime
+	if rt := cfg.GetRuntime(defaultRuntime); rt != nil {
 		if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
 		if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
 			if _, ver, commit, err := parseRuntimeVersion(string(rv)); err != nil {
 			if _, ver, commit, err := parseRuntimeVersion(string(rv)); err != nil {
 				logrus.Warnf("failed to parse %s version: %v", rt.Path, err)
 				logrus.Warnf("failed to parse %s version: %v", rt.Path, err)
@@ -195,7 +196,7 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
 		}
 		}
 	}
 	}
 
 
-	if initBinary, err := daemon.configStore.LookupInitPath(); err != nil {
+	if initBinary, err := cfg.LookupInitPath(); err != nil {
 		logrus.Warnf("failed to find docker-init: %s", err)
 		logrus.Warnf("failed to find docker-init: %s", err)
 	} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
 	} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
 		if ver, commit, err := parseInitVersion(string(rv)); err != nil {
 		if ver, commit, err := parseInitVersion(string(rv)); err != nil {
@@ -337,15 +338,15 @@ func parseRuntimeVersion(v string) (runtime string, version string, commit strin
 	return runtime, version, commit, err
 	return runtime, version, commit, err
 }
 }
 
 
-func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool {
-	return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(daemon.configStore.CgroupNamespaceMode).IsPrivate()
+func cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo, cfg *config.Config) bool {
+	return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(cfg.CgroupNamespaceMode).IsPrivate()
 }
 }
 
 
 // Rootless returns true if daemon is running in rootless mode
 // Rootless returns true if daemon is running in rootless mode
-func (daemon *Daemon) Rootless() bool {
-	return daemon.configStore.Rootless
+func Rootless(cfg *config.Config) bool {
+	return cfg.Rootless
 }
 }
 
 
-func (daemon *Daemon) noNewPrivileges() bool {
-	return daemon.configStore.NoNewPrivileges
+func noNewPrivileges(cfg *config.Config) bool {
+	return cfg.NoNewPrivileges
 }
 }

+ 6 - 5
daemon/info_windows.go

@@ -2,27 +2,28 @@ package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
 )
 )
 
 
 // fillPlatformInfo fills the platform related info.
 // fillPlatformInfo fills the platform related info.
-func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) {
+func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
 }
 }
 
 
-func (daemon *Daemon) fillPlatformVersion(v *types.Version) {}
+func (daemon *Daemon) fillPlatformVersion(v *types.Version, cfg *config.Config) {}
 
 
 func fillDriverWarnings(v *types.Info) {
 func fillDriverWarnings(v *types.Info) {
 }
 }
 
 
-func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool {
+func cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo, cfg *config.Config) bool {
 	return false
 	return false
 }
 }
 
 
 // Rootless returns true if daemon is running in rootless mode
 // Rootless returns true if daemon is running in rootless mode
-func (daemon *Daemon) Rootless() bool {
+func Rootless(*config.Config) bool {
 	return false
 	return false
 }
 }
 
 
-func (daemon *Daemon) noNewPrivileges() bool {
+func noNewPrivileges(*config.Config) bool {
 	return false
 	return false
 }
 }

+ 5 - 4
daemon/inspect.go

@@ -12,6 +12,7 @@ import (
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/versions/v1p20"
 	"github.com/docker/docker/api/types/versions/v1p20"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/go-connections/nat"
@@ -40,7 +41,7 @@ func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string,
 
 
 	ctr.Lock()
 	ctr.Lock()
 
 
-	base, err := daemon.getInspectData(ctr)
+	base, err := daemon.getInspectData(daemon.config(), ctr)
 	if err != nil {
 	if err != nil {
 		ctr.Unlock()
 		ctr.Unlock()
 		return nil, err
 		return nil, err
@@ -105,7 +106,7 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
 	ctr.Lock()
 	ctr.Lock()
 	defer ctr.Unlock()
 	defer ctr.Unlock()
 
 
-	base, err := daemon.getInspectData(ctr)
+	base, err := daemon.getInspectData(daemon.config(), ctr)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -124,7 +125,7 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
 	}, nil
 	}, nil
 }
 }
 
 
-func (daemon *Daemon) getInspectData(container *container.Container) (*types.ContainerJSONBase, error) {
+func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *container.Container) (*types.ContainerJSONBase, error) {
 	// make a copy to play with
 	// make a copy to play with
 	hostConfig := *container.HostConfig
 	hostConfig := *container.HostConfig
 
 
@@ -135,7 +136,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con
 	}
 	}
 
 
 	// We merge the Ulimits from hostConfig with daemon default
 	// We merge the Ulimits from hostConfig with daemon default
-	daemon.mergeUlimits(&hostConfig)
+	daemon.mergeUlimits(&hostConfig, daemonCfg)
 
 
 	var containerHealth *types.Health
 	var containerHealth *types.Health
 	if container.State.Health != nil {
 	if container.State.Health != nil {

+ 1 - 1
daemon/inspect_linux.go

@@ -29,7 +29,7 @@ func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (
 	ctr.Lock()
 	ctr.Lock()
 	defer ctr.Unlock()
 	defer ctr.Unlock()
 
 
-	base, err := daemon.getInspectData(ctr)
+	base, err := daemon.getInspectData(daemon.config(), ctr)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 6 - 4
daemon/inspect_test.go

@@ -19,16 +19,18 @@ func TestGetInspectData(t *testing.T) {
 	}
 	}
 
 
 	d := &Daemon{
 	d := &Daemon{
-		linkIndex:   newLinkIndex(),
-		configStore: &config.Config{},
+		linkIndex: newLinkIndex(),
 	}
 	}
 	if d.UsesSnapshotter() {
 	if d.UsesSnapshotter() {
 		t.Skip("does not apply to containerd snapshotters, which don't have RWLayer set")
 		t.Skip("does not apply to containerd snapshotters, which don't have RWLayer set")
 	}
 	}
-	_, err := d.getInspectData(c)
+	cfg := &config.Config{}
+	d.configStore.Store(cfg)
+
+	_, err := d.getInspectData(cfg, c)
 	assert.Check(t, is.ErrorContains(err, "RWLayer of container inspect-me is unexpectedly nil"))
 	assert.Check(t, is.ErrorContains(err, "RWLayer of container inspect-me is unexpectedly nil"))
 
 
 	c.Dead = true
 	c.Dead = true
-	_, err = d.getInspectData(c)
+	_, err = d.getInspectData(cfg, c)
 	assert.Check(t, err)
 	assert.Check(t, err)
 }
 }

+ 9 - 12
daemon/logs.go

@@ -10,6 +10,7 @@ import (
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	timetypes "github.com/docker/docker/api/types/time"
 	timetypes "github.com/docker/docker/api/types/time"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger"
 	logcache "github.com/docker/docker/daemon/logger/loggerutils/cache"
 	logcache "github.com/docker/docker/daemon/logger/loggerutils/cache"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
@@ -196,18 +197,14 @@ func (daemon *Daemon) mergeAndVerifyLogConfig(cfg *containertypes.LogConfig) err
 	return logger.ValidateLogOpts(cfg.Type, cfg.Config)
 	return logger.ValidateLogOpts(cfg.Type, cfg.Config)
 }
 }
 
 
-func (daemon *Daemon) setupDefaultLogConfig() error {
-	config := daemon.configStore
-	if len(config.LogConfig.Config) > 0 {
-		if err := logger.ValidateLogOpts(config.LogConfig.Type, config.LogConfig.Config); err != nil {
-			return errors.Wrap(err, "failed to set log opts")
+func defaultLogConfig(cfg *config.Config) (containertypes.LogConfig, error) {
+	if len(cfg.LogConfig.Config) > 0 {
+		if err := logger.ValidateLogOpts(cfg.LogConfig.Type, cfg.LogConfig.Config); err != nil {
+			return containertypes.LogConfig{}, errors.Wrap(err, "failed to set log opts")
 		}
 		}
 	}
 	}
-	daemon.defaultLogConfig = containertypes.LogConfig{
-		Type:   config.LogConfig.Type,
-		Config: config.LogConfig.Config,
-	}
-
-	logrus.Debugf("Using default logging driver %s", daemon.defaultLogConfig.Type)
-	return nil
+	return containertypes.LogConfig{
+		Type:   cfg.LogConfig.Type,
+		Config: cfg.LogConfig.Config,
+	}, nil
 }
 }

+ 3 - 2
daemon/metrics_unix.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/plugin"
 	"github.com/docker/docker/plugin"
@@ -19,8 +20,8 @@ import (
 	"golang.org/x/sys/unix"
 	"golang.org/x/sys/unix"
 )
 )
 
 
-func (daemon *Daemon) listenMetricsSock() (string, error) {
-	path := filepath.Join(daemon.configStore.ExecRoot, "metrics.sock")
+func (daemon *Daemon) listenMetricsSock(cfg *config.Config) (string, error) {
+	path := filepath.Join(cfg.ExecRoot, "metrics.sock")
 	unix.Unlink(path)
 	unix.Unlink(path)
 	l, err := net.Listen("unix", path)
 	l, err := net.Listen("unix", path)
 	if err != nil {
 	if err != nil {

+ 5 - 2
daemon/metrics_unsupported.go

@@ -2,11 +2,14 @@
 
 
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
-import "github.com/docker/docker/pkg/plugingetter"
+import (
+	"github.com/docker/docker/daemon/config"
+	"github.com/docker/docker/pkg/plugingetter"
+)
 
 
 func registerMetricsPluginCallback(getter plugingetter.PluginGetter, sockPath string) {
 func registerMetricsPluginCallback(getter plugingetter.PluginGetter, sockPath string) {
 }
 }
 
 
-func (daemon *Daemon) listenMetricsSock() (string, error) {
+func (daemon *Daemon) listenMetricsSock(*config.Config) (string, error) {
 	return "", nil
 	return "", nil
 }
 }

+ 9 - 5
daemon/monitor.go

@@ -7,6 +7,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
 	"github.com/docker/docker/restartmanager"
 	"github.com/docker/docker/restartmanager"
@@ -29,6 +30,8 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
 	var exitStatus container.ExitStatus
 	var exitStatus container.ExitStatus
 	c.Lock()
 	c.Lock()
 
 
+	cfg := daemon.config()
+
 	// Health checks will be automatically restarted if/when the
 	// Health checks will be automatically restarted if/when the
 	// container is started again.
 	// container is started again.
 	daemon.stopHealthchecks(c)
 	daemon.stopHealthchecks(c)
@@ -99,7 +102,7 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
 	} else {
 	} else {
 		c.SetStopped(&exitStatus)
 		c.SetStopped(&exitStatus)
 		if !c.HasBeenManuallyRestarted {
 		if !c.HasBeenManuallyRestarted {
-			defer daemon.autoRemove(c)
+			defer daemon.autoRemove(cfg, c)
 		}
 		}
 	}
 	}
 	defer c.Unlock() // needs to be called before autoRemove
 	defer c.Unlock() // needs to be called before autoRemove
@@ -117,7 +120,8 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
 				// But containerStart will use daemon.netController segment.
 				// But containerStart will use daemon.netController segment.
 				// So to avoid panic at startup process, here must wait util daemon restore done.
 				// So to avoid panic at startup process, here must wait util daemon restore done.
 				daemon.waitForStartupDone()
 				daemon.waitForStartupDone()
-				if err = daemon.containerStart(context.Background(), c, "", "", false); err != nil {
+				cfg := daemon.config() // Apply the most up-to-date daemon config to the restarted container.
+				if err = daemon.containerStart(context.Background(), cfg, c, "", "", false); err != nil {
 					logrus.Debugf("failed to restart container: %+v", err)
 					logrus.Debugf("failed to restart container: %+v", err)
 				}
 				}
 			}
 			}
@@ -127,7 +131,7 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
 				daemon.setStateCounter(c)
 				daemon.setStateCounter(c)
 				c.CheckpointTo(daemon.containersReplica)
 				c.CheckpointTo(daemon.containersReplica)
 				c.Unlock()
 				c.Unlock()
-				defer daemon.autoRemove(c)
+				defer daemon.autoRemove(cfg, c)
 				if err != restartmanager.ErrRestartCanceled {
 				if err != restartmanager.ErrRestartCanceled {
 					logrus.Errorf("restartmanger wait error: %+v", err)
 					logrus.Errorf("restartmanger wait error: %+v", err)
 				}
 				}
@@ -280,7 +284,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) autoRemove(c *container.Container) {
+func (daemon *Daemon) autoRemove(cfg *config.Config, c *container.Container) {
 	c.Lock()
 	c.Lock()
 	ar := c.HostConfig.AutoRemove
 	ar := c.HostConfig.AutoRemove
 	c.Unlock()
 	c.Unlock()
@@ -288,7 +292,7 @@ func (daemon *Daemon) autoRemove(c *container.Container) {
 		return
 		return
 	}
 	}
 
 
-	err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true})
+	err := daemon.containerRm(cfg, c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true})
 	if err == nil {
 	if err == nil {
 		return
 		return
 	}
 	}

+ 8 - 7
daemon/network.go

@@ -15,6 +15,7 @@ import (
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
+	"github.com/docker/docker/daemon/config"
 	internalnetwork "github.com/docker/docker/daemon/network"
 	internalnetwork "github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libnetwork"
 	"github.com/docker/docker/libnetwork"
@@ -160,7 +161,7 @@ func (daemon *Daemon) startIngressWorker() {
 			select {
 			select {
 			case r := <-ingressJobsChannel:
 			case r := <-ingressJobsChannel:
 				if r.create != nil {
 				if r.create != nil {
-					daemon.setupIngress(r.create, r.ip, ingressID)
+					daemon.setupIngress(daemon.config(), r.create, r.ip, ingressID)
 					ingressID = r.create.ID
 					ingressID = r.create.ID
 				} else {
 				} else {
 					daemon.releaseIngress(ingressID)
 					daemon.releaseIngress(ingressID)
@@ -199,7 +200,7 @@ func (daemon *Daemon) ReleaseIngress() (<-chan struct{}, error) {
 	return done, nil
 	return done, nil
 }
 }
 
 
-func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip net.IP, staleID string) {
+func (daemon *Daemon) setupIngress(cfg *config.Config, create *clustertypes.NetworkCreateRequest, ip net.IP, staleID string) {
 	controller := daemon.netController
 	controller := daemon.netController
 	controller.AgentInitWait()
 	controller.AgentInitWait()
 
 
@@ -207,7 +208,7 @@ func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip
 		daemon.releaseIngress(staleID)
 		daemon.releaseIngress(staleID)
 	}
 	}
 
 
-	if _, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true); err != nil {
+	if _, err := daemon.createNetwork(cfg, create.NetworkCreateRequest, create.ID, true); err != nil {
 		// If it is any other error other than already
 		// If it is any other error other than already
 		// exists error log error and return.
 		// exists error log error and return.
 		if _, ok := err.(libnetwork.NetworkNameError); !ok {
 		if _, ok := err.(libnetwork.NetworkNameError); !ok {
@@ -277,16 +278,16 @@ func (daemon *Daemon) WaitForDetachment(ctx context.Context, networkName, networ
 
 
 // CreateManagedNetwork creates an agent network.
 // CreateManagedNetwork creates an agent network.
 func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
 func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
-	_, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true)
+	_, err := daemon.createNetwork(daemon.config(), create.NetworkCreateRequest, create.ID, true)
 	return err
 	return err
 }
 }
 
 
 // CreateNetwork creates a network with the given name, driver and other optional parameters
 // CreateNetwork creates a network with the given name, driver and other optional parameters
 func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
 func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
-	return daemon.createNetwork(create, "", false)
+	return daemon.createNetwork(daemon.config(), create, "", false)
 }
 }
 
 
-func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
+func (daemon *Daemon) createNetwork(cfg *config.Config, create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
 	if runconfig.IsPreDefinedNetwork(create.Name) {
 	if runconfig.IsPreDefinedNetwork(create.Name) {
 		return nil, PredefinedNetworkError(create.Name)
 		return nil, PredefinedNetworkError(create.Name)
 	}
 	}
@@ -319,7 +320,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
 	for k, v := range create.Options {
 	for k, v := range create.Options {
 		networkOptions[k] = v
 		networkOptions[k] = v
 	}
 	}
-	if defaultOpts, ok := daemon.configStore.DefaultNetworkOpts[driver]; create.ConfigFrom == nil && ok {
+	if defaultOpts, ok := cfg.DefaultNetworkOpts[driver]; create.ConfigFrom == nil && ok {
 		for k, v := range defaultOpts {
 		for k, v := range defaultOpts {
 			if _, ok := networkOptions[k]; !ok {
 			if _, ok := networkOptions[k]; !ok {
 				logrus.WithFields(logrus.Fields{"driver": driver, "network": id, k: v}).Debug("Applying network default option")
 				logrus.WithFields(logrus.Fields{"driver": driver, "network": id, k: v}).Debug("Applying network default option")

+ 36 - 36
daemon/oci_linux.go

@@ -36,15 +36,15 @@ import (
 
 
 const inContainerInitPath = "/sbin/" + dconfig.DefaultInitBinary
 const inContainerInitPath = "/sbin/" + dconfig.DefaultInitBinary
 
 
-// WithRlimits sets the container's rlimits along with merging the daemon's rlimits
-func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts {
+// withRlimits sets the container's rlimits along with merging the daemon's rlimits
+func withRlimits(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 		var rlimits []specs.POSIXRlimit
 		var rlimits []specs.POSIXRlimit
 
 
 		// We want to leave the original HostConfig alone so make a copy here
 		// We want to leave the original HostConfig alone so make a copy here
 		hostConfig := *c.HostConfig
 		hostConfig := *c.HostConfig
 		// Merge with the daemon defaults
 		// Merge with the daemon defaults
-		daemon.mergeUlimits(&hostConfig)
+		daemon.mergeUlimits(&hostConfig, daemonCfg)
 		for _, ul := range hostConfig.Ulimits {
 		for _, ul := range hostConfig.Ulimits {
 			rlimits = append(rlimits, specs.POSIXRlimit{
 			rlimits = append(rlimits, specs.POSIXRlimit{
 				Type: "RLIMIT_" + strings.ToUpper(ul.Name),
 				Type: "RLIMIT_" + strings.ToUpper(ul.Name),
@@ -58,8 +58,8 @@ func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts {
 	}
 	}
 }
 }
 
 
-// WithLibnetwork sets the libnetwork hook
-func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
+// withLibnetwork sets the libnetwork hook
+func withLibnetwork(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 		if s.Hooks == nil {
 		if s.Hooks == nil {
 			s.Hooks = &specs.Hooks{}
 			s.Hooks = &specs.Hooks{}
@@ -72,7 +72,7 @@ func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
 					Path: target,
 					Path: target,
 					Args: []string{
 					Args: []string{
 						"libnetwork-setkey",
 						"libnetwork-setkey",
-						"-exec-root=" + daemon.configStore.GetExecRoot(),
+						"-exec-root=" + daemonCfg.GetExecRoot(),
 						c.ID,
 						c.ID,
 						shortNetCtlrID,
 						shortNetCtlrID,
 					},
 					},
@@ -83,11 +83,11 @@ func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
 	}
 	}
 }
 }
 
 
-// WithRootless sets the spec to the rootless configuration
-func WithRootless(daemon *Daemon) coci.SpecOpts {
+// withRootless sets the spec to the rootless configuration
+func withRootless(daemon *Daemon, daemonCfg *dconfig.Config) coci.SpecOpts {
 	return func(_ context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 	return func(_ context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 		var v2Controllers []string
 		var v2Controllers []string
-		if daemon.getCgroupDriver() == cgroupSystemdDriver {
+		if cgroupDriver(daemonCfg) == cgroupSystemdDriver {
 			if cdcgroups.Mode() != cdcgroups.Unified {
 			if cdcgroups.Mode() != cdcgroups.Unified {
 				return errors.New("rootless systemd driver doesn't support cgroup v1")
 				return errors.New("rootless systemd driver doesn't support cgroup v1")
 			}
 			}
@@ -488,8 +488,8 @@ func inSlice(slice []string, s string) bool {
 	return false
 	return false
 }
 }
 
 
-// WithMounts sets the container's mounts
-func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
+// withMounts sets the container's mounts
+func withMounts(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) (err error) {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) (err error) {
 		if err := daemon.setupContainerMountsRoot(c); err != nil {
 		if err := daemon.setupContainerMountsRoot(c); err != nil {
 			return err
 			return err
@@ -652,7 +652,7 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
 						return errors.New("mount options conflict: ReadOnlyNonRecursive && ReadOnlyForceRecursive")
 						return errors.New("mount options conflict: ReadOnlyNonRecursive && ReadOnlyForceRecursive")
 					}
 					}
 				}
 				}
-				if rroErr := daemon.supportsRecursivelyReadOnly(c.HostConfig.Runtime); rroErr != nil {
+				if rroErr := supportsRecursivelyReadOnly(daemonCfg, c.HostConfig.Runtime); rroErr != nil {
 					rro = false
 					rro = false
 					if m.ReadOnlyForceRecursive {
 					if m.ReadOnlyForceRecursive {
 						return rroErr
 						return rroErr
@@ -673,7 +673,7 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
 			// "mount" when we bind-mount. The reason for this is that at the point
 			// "mount" when we bind-mount. The reason for this is that at the point
 			// when runc sets up the root filesystem, it is already inside a user
 			// when runc sets up the root filesystem, it is already inside a user
 			// namespace, and thus cannot change any flags that are locked.
 			// namespace, and thus cannot change any flags that are locked.
-			if daemon.configStore.RemappedRoot != "" || userns.RunningInUserNS() {
+			if daemonCfg.RemappedRoot != "" || userns.RunningInUserNS() {
 				unprivOpts, err := getUnprivilegedMountFlags(m.Source)
 				unprivOpts, err := getUnprivilegedMountFlags(m.Source)
 				if err != nil {
 				if err != nil {
 					return err
 					return err
@@ -732,8 +732,8 @@ func sysctlExists(s string) bool {
 	return err == nil
 	return err == nil
 }
 }
 
 
-// WithCommonOptions sets common docker options
-func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
+// withCommonOptions sets common docker options
+func withCommonOptions(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 		if c.BaseFS == "" && !daemon.UsesSnapshotter() {
 		if c.BaseFS == "" && !daemon.UsesSnapshotter() {
 			return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly empty")
 			return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly empty")
@@ -762,9 +762,9 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
 		// host namespace or another container's pid namespace where we already have an init
 		// host namespace or another container's pid namespace where we already have an init
 		if c.HostConfig.PidMode.IsPrivate() {
 		if c.HostConfig.PidMode.IsPrivate() {
 			if (c.HostConfig.Init != nil && *c.HostConfig.Init) ||
 			if (c.HostConfig.Init != nil && *c.HostConfig.Init) ||
-				(c.HostConfig.Init == nil && daemon.configStore.Init) {
+				(c.HostConfig.Init == nil && daemonCfg.Init) {
 				s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...)
 				s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...)
-				path, err := daemon.configStore.LookupInitPath() // this will fall back to DefaultInitBinary and return an absolute path
+				path, err := daemonCfg.LookupInitPath() // this will fall back to DefaultInitBinary and return an absolute path
 				if err != nil {
 				if err != nil {
 					return err
 					return err
 				}
 				}
@@ -790,7 +790,7 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
 		// joining an existing namespace, only if we create a new net namespace.
 		// joining an existing namespace, only if we create a new net namespace.
 		if c.HostConfig.NetworkMode.IsPrivate() {
 		if c.HostConfig.NetworkMode.IsPrivate() {
 			// We cannot set up ping socket support in a user namespace
 			// We cannot set up ping socket support in a user namespace
-			userNS := daemon.configStore.RemappedRoot != "" && c.HostConfig.UsernsMode.IsPrivate()
+			userNS := daemonCfg.RemappedRoot != "" && c.HostConfig.UsernsMode.IsPrivate()
 			if !userNS && !userns.RunningInUserNS() && sysctlExists("net.ipv4.ping_group_range") {
 			if !userNS && !userns.RunningInUserNS() && sysctlExists("net.ipv4.ping_group_range") {
 				// allow unprivileged ICMP echo sockets without CAP_NET_RAW
 				// allow unprivileged ICMP echo sockets without CAP_NET_RAW
 				s.Linux.Sysctl["net.ipv4.ping_group_range"] = "0 2147483647"
 				s.Linux.Sysctl["net.ipv4.ping_group_range"] = "0 2147483647"
@@ -805,24 +805,24 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
 	}
 	}
 }
 }
 
 
-// WithCgroups sets the container's cgroups
-func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
+// withCgroups sets the container's cgroups
+func withCgroups(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
 		var cgroupsPath string
 		var cgroupsPath string
 		scopePrefix := "docker"
 		scopePrefix := "docker"
 		parent := "/docker"
 		parent := "/docker"
-		useSystemd := UsingSystemd(daemon.configStore)
+		useSystemd := UsingSystemd(daemonCfg)
 		if useSystemd {
 		if useSystemd {
 			parent = "system.slice"
 			parent = "system.slice"
-			if daemon.configStore.Rootless {
+			if daemonCfg.Rootless {
 				parent = "user.slice"
 				parent = "user.slice"
 			}
 			}
 		}
 		}
 
 
 		if c.HostConfig.CgroupParent != "" {
 		if c.HostConfig.CgroupParent != "" {
 			parent = c.HostConfig.CgroupParent
 			parent = c.HostConfig.CgroupParent
-		} else if daemon.configStore.CgroupParent != "" {
-			parent = daemon.configStore.CgroupParent
+		} else if daemonCfg.CgroupParent != "" {
+			parent = daemonCfg.CgroupParent
 		}
 		}
 
 
 		if useSystemd {
 		if useSystemd {
@@ -835,7 +835,7 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
 
 
 		// the rest is only needed for CPU RT controller
 		// the rest is only needed for CPU RT controller
 
 
-		if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 {
+		if daemonCfg.CPURealtimePeriod == 0 && daemonCfg.CPURealtimeRuntime == 0 {
 			return nil
 			return nil
 		}
 		}
 
 
@@ -869,7 +869,7 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
 		}
 		}
 		mnt = filepath.Join(mnt, root)
 		mnt = filepath.Join(mnt, root)
 
 
-		if err := daemon.initCPURtController(mnt, parentPath); err != nil {
+		if err := daemon.initCPURtController(daemonCfg, mnt, parentPath); err != nil {
 			return errors.Wrap(err, "unable to init CPU RT controller")
 			return errors.Wrap(err, "unable to init CPU RT controller")
 		}
 		}
 		return nil
 		return nil
@@ -1019,23 +1019,23 @@ func WithUser(c *container.Container) coci.SpecOpts {
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (retSpec *specs.Spec, err error) {
+func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *dconfig.Config, c *container.Container) (retSpec *specs.Spec, err error) {
 	var (
 	var (
 		opts []coci.SpecOpts
 		opts []coci.SpecOpts
 		s    = oci.DefaultSpec()
 		s    = oci.DefaultSpec()
 	)
 	)
 	opts = append(opts,
 	opts = append(opts,
-		WithCommonOptions(daemon, c),
-		WithCgroups(daemon, c),
+		withCommonOptions(daemon, daemonCfg, c),
+		withCgroups(daemon, daemonCfg, c),
 		WithResources(c),
 		WithResources(c),
 		WithSysctls(c),
 		WithSysctls(c),
 		WithDevices(daemon, c),
 		WithDevices(daemon, c),
-		WithRlimits(daemon, c),
+		withRlimits(daemon, daemonCfg, c),
 		WithNamespaces(daemon, c),
 		WithNamespaces(daemon, c),
 		WithCapabilities(c),
 		WithCapabilities(c),
 		WithSeccomp(daemon, c),
 		WithSeccomp(daemon, c),
-		WithMounts(daemon, c),
-		WithLibnetwork(daemon, c),
+		withMounts(daemon, daemonCfg, c),
+		withLibnetwork(daemon, daemonCfg, c),
 		WithApparmor(c),
 		WithApparmor(c),
 		WithSelinux(c),
 		WithSelinux(c),
 		WithOOMScore(&c.HostConfig.OomScoreAdj),
 		WithOOMScore(&c.HostConfig.OomScoreAdj),
@@ -1068,8 +1068,8 @@ func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (r
 	if c.HostConfig.ReadonlyPaths != nil {
 	if c.HostConfig.ReadonlyPaths != nil {
 		opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
 		opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
 	}
 	}
-	if daemon.configStore.Rootless {
-		opts = append(opts, WithRootless(daemon))
+	if daemonCfg.Rootless {
+		opts = append(opts, withRootless(daemon, daemonCfg))
 	}
 	}
 
 
 	var snapshotter, snapshotKey string
 	var snapshotter, snapshotKey string
@@ -1096,14 +1096,14 @@ func clearReadOnly(m *specs.Mount) {
 }
 }
 
 
 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
-func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) {
+func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig, daemonCfg *dconfig.Config) {
 	ulimits := c.Ulimits
 	ulimits := c.Ulimits
 	// Merge ulimits with daemon defaults
 	// Merge ulimits with daemon defaults
 	ulIdx := make(map[string]struct{})
 	ulIdx := make(map[string]struct{})
 	for _, ul := range ulimits {
 	for _, ul := range ulimits {
 		ulIdx[ul.Name] = struct{}{}
 		ulIdx[ul.Name] = struct{}{}
 	}
 	}
-	for name, ul := range daemon.configStore.Ulimits {
+	for name, ul := range daemonCfg.Ulimits {
 		if _, exists := ulIdx[name]; !exists {
 		if _, exists := ulIdx[name]; !exists {
 			ulimits = append(ulimits, ul)
 			ulimits = append(ulimits, ul)
 		}
 		}

+ 8 - 10
daemon/oci_linux_test.go

@@ -30,7 +30,6 @@ func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon {
 	d := &Daemon{
 	d := &Daemon{
 		// some empty structs to avoid getting a panic
 		// some empty structs to avoid getting a panic
 		// caused by a null pointer dereference
 		// caused by a null pointer dereference
-		configStore:   &config.Config{},
 		linkIndex:     newLinkIndex(),
 		linkIndex:     newLinkIndex(),
 		netController: netController,
 		netController: netController,
 		imageService:  &fakeImageService{},
 		imageService:  &fakeImageService{},
@@ -83,7 +82,7 @@ func TestTmpfsDevShmNoDupMount(t *testing.T) {
 	d := setupFakeDaemon(t, c)
 	d := setupFakeDaemon(t, c)
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
-	_, err := d.createSpec(context.TODO(), c)
+	_, err := d.createSpec(context.TODO(), &config.Config{}, c)
 	assert.Check(t, err)
 	assert.Check(t, err)
 }
 }
 
 
@@ -102,7 +101,7 @@ func TestIpcPrivateVsReadonly(t *testing.T) {
 	d := setupFakeDaemon(t, c)
 	d := setupFakeDaemon(t, c)
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
-	s, err := d.createSpec(context.TODO(), c)
+	s, err := d.createSpec(context.TODO(), &config.Config{}, c)
 	assert.Check(t, err)
 	assert.Check(t, err)
 
 
 	// Find the /dev/shm mount in ms, check it does not have ro
 	// Find the /dev/shm mount in ms, check it does not have ro
@@ -132,7 +131,7 @@ func TestSysctlOverride(t *testing.T) {
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
 	// Ensure that the implicit sysctl is set correctly.
 	// Ensure that the implicit sysctl is set correctly.
-	s, err := d.createSpec(context.TODO(), c)
+	s, err := d.createSpec(context.TODO(), &config.Config{}, c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname)
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname)
@@ -148,15 +147,14 @@ func TestSysctlOverride(t *testing.T) {
 	assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname)
 	assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname)
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 
 
-	s, err = d.createSpec(context.TODO(), c)
+	s, err = d.createSpec(context.TODO(), &config.Config{}, c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"])
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"])
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
 
 
 	// Ensure the ping_group_range is not set on a daemon with user-namespaces enabled
 	// Ensure the ping_group_range is not set on a daemon with user-namespaces enabled
-	d.configStore.RemappedRoot = "dummy:dummy"
-	s, err = d.createSpec(context.TODO(), c)
+	s, err = d.createSpec(context.TODO(), &config.Config{RemappedRoot: "dummy:dummy"}, c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
 	_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
 	assert.Assert(t, !ok)
 	assert.Assert(t, !ok)
@@ -164,7 +162,7 @@ func TestSysctlOverride(t *testing.T) {
 	// Ensure the ping_group_range is set on a container in "host" userns mode
 	// Ensure the ping_group_range is set on a container in "host" userns mode
 	// on a daemon with user-namespaces enabled
 	// on a daemon with user-namespaces enabled
 	c.HostConfig.UsernsMode = "host"
 	c.HostConfig.UsernsMode = "host"
-	s, err = d.createSpec(context.TODO(), c)
+	s, err = d.createSpec(context.TODO(), &config.Config{RemappedRoot: "dummy:dummy"}, c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
 }
 }
@@ -184,7 +182,7 @@ func TestSysctlOverrideHost(t *testing.T) {
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
 	// Ensure that the implicit sysctl is not set
 	// Ensure that the implicit sysctl is not set
-	s, err := d.createSpec(context.TODO(), c)
+	s, err := d.createSpec(context.TODO(), &config.Config{}, c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "")
@@ -192,7 +190,7 @@ func TestSysctlOverrideHost(t *testing.T) {
 	// Set an explicit sysctl.
 	// Set an explicit sysctl.
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 
 
-	s, err = d.createSpec(context.TODO(), c)
+	s, err = d.createSpec(context.TODO(), &config.Config{}, c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
 }
 }

+ 4 - 3
daemon/oci_windows.go

@@ -12,6 +12,7 @@ import (
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	imagetypes "github.com/docker/docker/api/types/image"
 	imagetypes "github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/oci"
 	"github.com/docker/docker/oci"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
@@ -27,7 +28,7 @@ const (
 	credentialSpecFileLocation     = "CredentialSpecs"
 	credentialSpecFileLocation     = "CredentialSpecs"
 )
 )
 
 
-func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (*specs.Spec, error) {
+func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *config.Config, c *container.Container) (*specs.Spec, error) {
 	img, err := daemon.imageService.GetImage(ctx, string(c.ImageID), imagetypes.GetImageOpts{})
 	img, err := daemon.imageService.GetImage(ctx, string(c.ImageID), imagetypes.GetImageOpts{})
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -142,7 +143,7 @@ func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (*
 		return nil, errors.Wrapf(err, "container %s", c.ID)
 		return nil, errors.Wrapf(err, "container %s", c.ID)
 	}
 	}
 
 
-	dnsSearch := daemon.getDNSSearchSettings(c)
+	dnsSearch := daemon.getDNSSearchSettings(daemonCfg, c)
 
 
 	// Get endpoints for the libnetwork allocated networks to the container
 	// Get endpoints for the libnetwork allocated networks to the container
 	var epList []string
 	var epList []string
@@ -404,7 +405,7 @@ func setResourcesInSpec(c *container.Container, s *specs.Spec, isHyperV bool) {
 
 
 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
 // It will do nothing on non-Linux platform
 // It will do nothing on non-Linux platform
-func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) {
+func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig, daemonCfg *config.Config) {
 	return
 	return
 }
 }
 
 

+ 2 - 1
daemon/prune.go

@@ -56,6 +56,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	cfg := daemon.config()
 	allContainers := daemon.List()
 	allContainers := daemon.List()
 	for _, c := range allContainers {
 	for _, c := range allContainers {
 		select {
 		select {
@@ -77,7 +78,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
 				return nil, err
 				return nil, err
 			}
 			}
 			// TODO: sets RmLink to true?
 			// TODO: sets RmLink to true?
-			err = daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
+			err = daemon.containerRm(cfg, c.ID, &types.ContainerRmConfig{})
 			if err != nil {
 			if err != nil {
 				logrus.Warnf("failed to prune container %s: %v", c.ID, err)
 				logrus.Warnf("failed to prune container %s: %v", c.ID, err)
 				continue
 				continue

+ 162 - 110
daemon/reload.go

@@ -5,10 +5,55 @@ import (
 	"fmt"
 	"fmt"
 	"strconv"
 	"strconv"
 
 
-	"github.com/docker/docker/daemon/config"
+	"github.com/hashicorp/go-multierror"
+	"github.com/mitchellh/copystructure"
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
+
+	"github.com/docker/docker/daemon/config"
 )
 )
 
 
+// reloadTxn is used to defer side effects of a config reload.
+type reloadTxn struct {
+	onCommit, onRollback []func() error
+}
+
+// OnCommit defers a function to be called when a config reload is being finalized.
+// The error returned from cb is purely informational.
+func (tx *reloadTxn) OnCommit(cb func() error) {
+	tx.onCommit = append(tx.onCommit, cb)
+}
+
+// OnRollback defers a function to be called when a config reload is aborted.
+// The error returned from cb is purely informational.
+func (tx *reloadTxn) OnRollback(cb func() error) {
+	tx.onCommit = append(tx.onRollback, cb)
+}
+
+func (tx *reloadTxn) run(cbs []func() error) error {
+	tx.onCommit = nil
+	tx.onRollback = nil
+
+	var res *multierror.Error
+	for _, cb := range cbs {
+		res = multierror.Append(res, cb())
+	}
+	return res.ErrorOrNil()
+}
+
+// Commit calls all functions registered with OnCommit.
+// Any errors returned by the functions are collated into a
+// *github.com/hashicorp/go-multierror.Error value.
+func (tx *reloadTxn) Commit() error {
+	return tx.run(tx.onCommit)
+}
+
+// Rollback calls all functions registered with OnRollback.
+// Any errors returned by the functions are collated into a
+// *github.com/hashicorp/go-multierror.Error value.
+func (tx *reloadTxn) Rollback() error {
+	return tx.run(tx.onRollback)
+}
+
 // Reload modifies the live daemon configuration from conf.
 // Reload modifies the live daemon configuration from conf.
 // conf is assumed to be a validated configuration.
 // conf is assumed to be a validated configuration.
 //
 //
@@ -24,66 +69,63 @@ import (
 // - Insecure registries
 // - Insecure registries
 // - Registry mirrors
 // - Registry mirrors
 // - Daemon live restore
 // - Daemon live restore
-func (daemon *Daemon) Reload(conf *config.Config) (err error) {
-	daemon.configStore.Lock()
-	attributes := map[string]string{}
+func (daemon *Daemon) Reload(conf *config.Config) error {
+	daemon.configReload.Lock()
+	defer daemon.configReload.Unlock()
+	copied, err := copystructure.Copy(daemon.config())
+	if err != nil {
+		return err
+	}
+	newCfg := copied.(*config.Config)
 
 
-	defer func() {
-		if err == nil {
-			jsonString, _ := json.Marshal(&struct {
-				*config.Config
-				config.Proxies `json:"proxies"`
-			}{
-				Config: daemon.configStore,
-				Proxies: config.Proxies{
-					HTTPProxy:  config.MaskCredentials(daemon.configStore.HTTPProxy),
-					HTTPSProxy: config.MaskCredentials(daemon.configStore.HTTPSProxy),
-					NoProxy:    config.MaskCredentials(daemon.configStore.NoProxy),
-				},
-			})
-			logrus.Infof("Reloaded configuration: %s", jsonString)
-		}
-		daemon.configStore.Unlock()
-		if err == nil {
-			daemon.LogDaemonEventWithAttributes("reload", attributes)
-		}
-	}()
+	attributes := map[string]string{}
 
 
 	// Ideally reloading should be transactional: the reload either completes
 	// Ideally reloading should be transactional: the reload either completes
 	// successfully, or the daemon config and state are left untouched. We use a
 	// successfully, or the daemon config and state are left untouched. We use a
-	// simplified two-phase commit protocol to achieve this. Any fallible reload
-	// operation is split into two phases. The first phase performs all the fallible
-	// operations without mutating daemon state and returns a closure: its second
-	// phase. The second phase applies the changes to the daemon state. If any
-	// first-phase returns an error, the reload transaction is "rolled back" by
-	// discarding the second-phase closures.
-
-	type TxnCommitter = func(attributes map[string]string)
-	var txns []TxnCommitter
-	for _, prepare := range []func(*config.Config) (TxnCommitter, error){
+	// two-phase commit protocol to achieve this. Any fallible reload operation is
+	// split into two phases. The first phase performs all the fallible operations
+	// and mutates the newCfg copy. The second phase atomically swaps newCfg into
+	// the live daemon configuration and executes any commit functions the first
+	// phase registered to apply the side effects. If any first-phase returns an
+	// error, the reload transaction is rolled back by discarding newCfg and
+	// executing any registered rollback functions.
+
+	var txn reloadTxn
+	for _, reload := range []func(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error{
 		daemon.reloadPlatform,
 		daemon.reloadPlatform,
+		daemon.reloadDebug,
+		daemon.reloadMaxConcurrentDownloadsAndUploads,
+		daemon.reloadMaxDownloadAttempts,
+		daemon.reloadShutdownTimeout,
+		daemon.reloadFeatures,
+		daemon.reloadLabels,
 		daemon.reloadRegistryConfig,
 		daemon.reloadRegistryConfig,
+		daemon.reloadLiveRestore,
+		daemon.reloadNetworkDiagnosticPort,
 	} {
 	} {
-		commit, err := prepare(conf)
-		if err != nil {
+		if err := reload(&txn, newCfg, conf, attributes); err != nil {
+			if rollbackErr := txn.Rollback(); rollbackErr != nil {
+				return multierror.Append(nil, err, rollbackErr)
+			}
 			return err
 			return err
 		}
 		}
-		txns = append(txns, commit)
 	}
 	}
 
 
-	daemon.reloadDebug(conf, attributes)
-	daemon.reloadMaxConcurrentDownloadsAndUploads(conf, attributes)
-	daemon.reloadMaxDownloadAttempts(conf, attributes)
-	daemon.reloadShutdownTimeout(conf, attributes)
-	daemon.reloadFeatures(conf, attributes)
-	daemon.reloadLabels(conf, attributes)
-	daemon.reloadLiveRestore(conf, attributes)
-	daemon.reloadNetworkDiagnosticPort(conf, attributes)
-
-	for _, tx := range txns {
-		tx(attributes)
-	}
-	return nil
+	jsonString, _ := json.Marshal(&struct {
+		*config.Config
+		config.Proxies `json:"proxies"`
+	}{
+		Config: newCfg,
+		Proxies: config.Proxies{
+			HTTPProxy:  config.MaskCredentials(newCfg.HTTPProxy),
+			HTTPSProxy: config.MaskCredentials(newCfg.HTTPSProxy),
+			NoProxy:    config.MaskCredentials(newCfg.NoProxy),
+		},
+	})
+	logrus.Infof("Reloaded configuration: %s", jsonString)
+	daemon.configStore.Store(newCfg)
+	daemon.LogDaemonEventWithAttributes("reload", attributes)
+	return txn.Commit()
 }
 }
 
 
 func marshalAttributeSlice(v []string) string {
 func marshalAttributeSlice(v []string) string {
@@ -99,145 +141,155 @@ func marshalAttributeSlice(v []string) string {
 
 
 // reloadDebug updates configuration with Debug option
 // reloadDebug updates configuration with Debug option
 // and updates the passed attributes
 // and updates the passed attributes
-func (daemon *Daemon) reloadDebug(conf *config.Config, attributes map[string]string) {
+func (daemon *Daemon) reloadDebug(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// update corresponding configuration
 	// update corresponding configuration
 	if conf.IsValueSet("debug") {
 	if conf.IsValueSet("debug") {
-		daemon.configStore.Debug = conf.Debug
+		newCfg.Debug = conf.Debug
 	}
 	}
 	// prepare reload event attributes with updatable configurations
 	// prepare reload event attributes with updatable configurations
-	attributes["debug"] = strconv.FormatBool(daemon.configStore.Debug)
+	attributes["debug"] = strconv.FormatBool(newCfg.Debug)
+	return nil
 }
 }
 
 
 // reloadMaxConcurrentDownloadsAndUploads updates configuration with max concurrent
 // reloadMaxConcurrentDownloadsAndUploads updates configuration with max concurrent
 // download and upload options and updates the passed attributes
 // download and upload options and updates the passed attributes
-func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(conf *config.Config, attributes map[string]string) {
+func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// We always "reset" as the cost is lightweight and easy to maintain.
 	// We always "reset" as the cost is lightweight and easy to maintain.
-	daemon.configStore.MaxConcurrentDownloads = config.DefaultMaxConcurrentDownloads
-	daemon.configStore.MaxConcurrentUploads = config.DefaultMaxConcurrentUploads
+	newCfg.MaxConcurrentDownloads = config.DefaultMaxConcurrentDownloads
+	newCfg.MaxConcurrentUploads = config.DefaultMaxConcurrentUploads
 
 
 	if conf.IsValueSet("max-concurrent-downloads") && conf.MaxConcurrentDownloads != 0 {
 	if conf.IsValueSet("max-concurrent-downloads") && conf.MaxConcurrentDownloads != 0 {
-		daemon.configStore.MaxConcurrentDownloads = conf.MaxConcurrentDownloads
+		newCfg.MaxConcurrentDownloads = conf.MaxConcurrentDownloads
 	}
 	}
 	if conf.IsValueSet("max-concurrent-uploads") && conf.MaxConcurrentUploads != 0 {
 	if conf.IsValueSet("max-concurrent-uploads") && conf.MaxConcurrentUploads != 0 {
-		daemon.configStore.MaxConcurrentUploads = conf.MaxConcurrentUploads
-	}
-	if daemon.imageService != nil {
-		daemon.imageService.UpdateConfig(
-			daemon.configStore.MaxConcurrentDownloads,
-			daemon.configStore.MaxConcurrentUploads,
-		)
+		newCfg.MaxConcurrentUploads = conf.MaxConcurrentUploads
 	}
 	}
+	txn.OnCommit(func() error {
+		if daemon.imageService != nil {
+			daemon.imageService.UpdateConfig(
+				newCfg.MaxConcurrentDownloads,
+				newCfg.MaxConcurrentUploads,
+			)
+		}
+		return nil
+	})
 
 
 	// prepare reload event attributes with updatable configurations
 	// prepare reload event attributes with updatable configurations
-	attributes["max-concurrent-downloads"] = strconv.Itoa(daemon.configStore.MaxConcurrentDownloads)
-	attributes["max-concurrent-uploads"] = strconv.Itoa(daemon.configStore.MaxConcurrentUploads)
+	attributes["max-concurrent-downloads"] = strconv.Itoa(newCfg.MaxConcurrentDownloads)
+	attributes["max-concurrent-uploads"] = strconv.Itoa(newCfg.MaxConcurrentUploads)
 	logrus.Debug("Reset Max Concurrent Downloads: ", attributes["max-concurrent-downloads"])
 	logrus.Debug("Reset Max Concurrent Downloads: ", attributes["max-concurrent-downloads"])
 	logrus.Debug("Reset Max Concurrent Uploads: ", attributes["max-concurrent-uploads"])
 	logrus.Debug("Reset Max Concurrent Uploads: ", attributes["max-concurrent-uploads"])
+	return nil
 }
 }
 
 
 // reloadMaxDownloadAttempts updates configuration with max concurrent
 // reloadMaxDownloadAttempts updates configuration with max concurrent
 // download attempts when a connection is lost and updates the passed attributes
 // download attempts when a connection is lost and updates the passed attributes
-func (daemon *Daemon) reloadMaxDownloadAttempts(conf *config.Config, attributes map[string]string) {
+func (daemon *Daemon) reloadMaxDownloadAttempts(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// We always "reset" as the cost is lightweight and easy to maintain.
 	// We always "reset" as the cost is lightweight and easy to maintain.
-	daemon.configStore.MaxDownloadAttempts = config.DefaultDownloadAttempts
+	newCfg.MaxDownloadAttempts = config.DefaultDownloadAttempts
 	if conf.IsValueSet("max-download-attempts") && conf.MaxDownloadAttempts != 0 {
 	if conf.IsValueSet("max-download-attempts") && conf.MaxDownloadAttempts != 0 {
-		daemon.configStore.MaxDownloadAttempts = conf.MaxDownloadAttempts
+		newCfg.MaxDownloadAttempts = conf.MaxDownloadAttempts
 	}
 	}
 
 
 	// prepare reload event attributes with updatable configurations
 	// prepare reload event attributes with updatable configurations
-	attributes["max-download-attempts"] = strconv.Itoa(daemon.configStore.MaxDownloadAttempts)
+	attributes["max-download-attempts"] = strconv.Itoa(newCfg.MaxDownloadAttempts)
 	logrus.Debug("Reset Max Download Attempts: ", attributes["max-download-attempts"])
 	logrus.Debug("Reset Max Download Attempts: ", attributes["max-download-attempts"])
+	return nil
 }
 }
 
 
 // reloadShutdownTimeout updates configuration with daemon shutdown timeout option
 // reloadShutdownTimeout updates configuration with daemon shutdown timeout option
 // and updates the passed attributes
 // and updates the passed attributes
-func (daemon *Daemon) reloadShutdownTimeout(conf *config.Config, attributes map[string]string) {
+func (daemon *Daemon) reloadShutdownTimeout(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// update corresponding configuration
 	// update corresponding configuration
 	if conf.IsValueSet("shutdown-timeout") {
 	if conf.IsValueSet("shutdown-timeout") {
-		daemon.configStore.ShutdownTimeout = conf.ShutdownTimeout
-		logrus.Debugf("Reset Shutdown Timeout: %d", daemon.configStore.ShutdownTimeout)
+		newCfg.ShutdownTimeout = conf.ShutdownTimeout
+		logrus.Debugf("Reset Shutdown Timeout: %d", newCfg.ShutdownTimeout)
 	}
 	}
 
 
 	// prepare reload event attributes with updatable configurations
 	// prepare reload event attributes with updatable configurations
-	attributes["shutdown-timeout"] = strconv.Itoa(daemon.configStore.ShutdownTimeout)
+	attributes["shutdown-timeout"] = strconv.Itoa(newCfg.ShutdownTimeout)
+	return nil
 }
 }
 
 
 // reloadLabels updates configuration with engine labels
 // reloadLabels updates configuration with engine labels
 // and updates the passed attributes
 // and updates the passed attributes
-func (daemon *Daemon) reloadLabels(conf *config.Config, attributes map[string]string) {
+func (daemon *Daemon) reloadLabels(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// update corresponding configuration
 	// update corresponding configuration
 	if conf.IsValueSet("labels") {
 	if conf.IsValueSet("labels") {
-		daemon.configStore.Labels = conf.Labels
+		newCfg.Labels = conf.Labels
 	}
 	}
 
 
 	// prepare reload event attributes with updatable configurations
 	// prepare reload event attributes with updatable configurations
-	attributes["labels"] = marshalAttributeSlice(daemon.configStore.Labels)
+	attributes["labels"] = marshalAttributeSlice(newCfg.Labels)
+	return nil
 }
 }
 
 
 // reloadRegistryConfig updates the configuration with registry options
 // reloadRegistryConfig updates the configuration with registry options
 // and updates the passed attributes.
 // and updates the passed attributes.
-func (daemon *Daemon) reloadRegistryConfig(conf *config.Config) (func(map[string]string), error) {
+func (daemon *Daemon) reloadRegistryConfig(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// Update corresponding configuration.
 	// Update corresponding configuration.
-	opts := daemon.configStore.ServiceOptions
-
 	if conf.IsValueSet("allow-nondistributable-artifacts") {
 	if conf.IsValueSet("allow-nondistributable-artifacts") {
-		opts.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
+		newCfg.ServiceOptions.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
 	}
 	}
 	if conf.IsValueSet("insecure-registries") {
 	if conf.IsValueSet("insecure-registries") {
-		opts.InsecureRegistries = conf.InsecureRegistries
+		newCfg.ServiceOptions.InsecureRegistries = conf.InsecureRegistries
 	}
 	}
 	if conf.IsValueSet("registry-mirrors") {
 	if conf.IsValueSet("registry-mirrors") {
-		opts.Mirrors = conf.Mirrors
+		newCfg.ServiceOptions.Mirrors = conf.Mirrors
 	}
 	}
 
 
-	commit, err := daemon.registryService.ReplaceConfig(opts)
+	commit, err := daemon.registryService.ReplaceConfig(newCfg.ServiceOptions)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
+	txn.OnCommit(func() error { commit(); return nil })
+
+	attributes["allow-nondistributable-artifacts"] = marshalAttributeSlice(newCfg.ServiceOptions.AllowNondistributableArtifacts)
+	attributes["insecure-registries"] = marshalAttributeSlice(newCfg.ServiceOptions.InsecureRegistries)
+	attributes["registry-mirrors"] = marshalAttributeSlice(newCfg.ServiceOptions.Mirrors)
 
 
-	return func(attributes map[string]string) {
-		commit()
-		daemon.configStore.ServiceOptions = opts
-		// Prepare reload event attributes with updatable configurations.
-		attributes["allow-nondistributable-artifacts"] = marshalAttributeSlice(daemon.configStore.AllowNondistributableArtifacts)
-		attributes["insecure-registries"] = marshalAttributeSlice(daemon.configStore.InsecureRegistries)
-		attributes["registry-mirrors"] = marshalAttributeSlice(daemon.configStore.Mirrors)
-	}, nil
+	return nil
 }
 }
 
 
 // reloadLiveRestore updates configuration with live restore option
 // reloadLiveRestore updates configuration with live restore option
 // and updates the passed attributes
 // and updates the passed attributes
-func (daemon *Daemon) reloadLiveRestore(conf *config.Config, attributes map[string]string) {
+func (daemon *Daemon) reloadLiveRestore(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// update corresponding configuration
 	// update corresponding configuration
 	if conf.IsValueSet("live-restore") {
 	if conf.IsValueSet("live-restore") {
-		daemon.configStore.LiveRestoreEnabled = conf.LiveRestoreEnabled
+		newCfg.LiveRestoreEnabled = conf.LiveRestoreEnabled
 	}
 	}
 
 
 	// prepare reload event attributes with updatable configurations
 	// prepare reload event attributes with updatable configurations
-	attributes["live-restore"] = strconv.FormatBool(daemon.configStore.LiveRestoreEnabled)
+	attributes["live-restore"] = strconv.FormatBool(newCfg.LiveRestoreEnabled)
+	return nil
 }
 }
 
 
 // reloadNetworkDiagnosticPort updates the network controller starting the diagnostic if the config is valid
 // reloadNetworkDiagnosticPort updates the network controller starting the diagnostic if the config is valid
-func (daemon *Daemon) reloadNetworkDiagnosticPort(conf *config.Config, attributes map[string]string) {
-	if conf == nil || daemon.netController == nil || !conf.IsValueSet("network-diagnostic-port") ||
-		conf.NetworkDiagnosticPort < 1 || conf.NetworkDiagnosticPort > 65535 {
-		// If there is no config make sure that the diagnostic is off
-		if daemon.netController != nil {
-			daemon.netController.StopDiagnostic()
+func (daemon *Daemon) reloadNetworkDiagnosticPort(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
+	txn.OnCommit(func() error {
+		if conf == nil || daemon.netController == nil || !conf.IsValueSet("network-diagnostic-port") ||
+			conf.NetworkDiagnosticPort < 1 || conf.NetworkDiagnosticPort > 65535 {
+			// If there is no config make sure that the diagnostic is off
+			if daemon.netController != nil {
+				daemon.netController.StopDiagnostic()
+			}
+			return nil
 		}
 		}
-		return
-	}
-	// Enable the network diagnostic if the flag is set with a valid port within the range
-	logrus.WithFields(logrus.Fields{"port": conf.NetworkDiagnosticPort, "ip": "127.0.0.1"}).Warn("Starting network diagnostic server")
-	daemon.netController.StartDiagnostic(conf.NetworkDiagnosticPort)
+		// Enable the network diagnostic if the flag is set with a valid port within the range
+		logrus.WithFields(logrus.Fields{"port": conf.NetworkDiagnosticPort, "ip": "127.0.0.1"}).Warn("Starting network diagnostic server")
+		daemon.netController.StartDiagnostic(conf.NetworkDiagnosticPort)
+		return nil
+	})
+	return nil
 }
 }
 
 
 // reloadFeatures updates configuration with enabled/disabled features
 // reloadFeatures updates configuration with enabled/disabled features
-func (daemon *Daemon) reloadFeatures(conf *config.Config, attributes map[string]string) {
+func (daemon *Daemon) reloadFeatures(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
 	// update corresponding configuration
 	// update corresponding configuration
 	// note that we allow features option to be entirely unset
 	// note that we allow features option to be entirely unset
-	daemon.configStore.Features = conf.Features
+	newCfg.Features = conf.Features
 
 
 	// prepare reload event attributes with updatable configurations
 	// prepare reload event attributes with updatable configurations
-	attributes["features"] = fmt.Sprintf("%v", daemon.configStore.Features)
+	attributes["features"] = fmt.Sprintf("%v", newCfg.Features)
+	return nil
 }
 }

+ 5 - 9
daemon/reload_test.go

@@ -22,12 +22,12 @@ func muteLogs() {
 func newDaemonForReloadT(t *testing.T, cfg *config.Config) *Daemon {
 func newDaemonForReloadT(t *testing.T, cfg *config.Config) *Daemon {
 	t.Helper()
 	t.Helper()
 	daemon := &Daemon{
 	daemon := &Daemon{
-		configStore:  cfg,
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
 	}
 	}
 	var err error
 	var err error
 	daemon.registryService, err = registry.NewService(registry.ServiceOptions{})
 	daemon.registryService, err = registry.NewService(registry.ServiceOptions{})
 	assert.Assert(t, err)
 	assert.Assert(t, err)
+	daemon.configStore.Store(cfg)
 	return daemon
 	return daemon
 }
 }
 
 
@@ -52,7 +52,7 @@ func TestDaemonReloadLabels(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	label := daemon.configStore.Labels[0]
+	label := daemon.config().Labels[0]
 	if label != "foo:baz" {
 	if label != "foo:baz" {
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
 	}
 	}
@@ -131,8 +131,6 @@ func TestDaemonReloadMirrors(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	daemon.configStore = &config.Config{}
-
 	type pair struct {
 	type pair struct {
 		valid   bool
 		valid   bool
 		mirrors []string
 		mirrors []string
@@ -234,8 +232,6 @@ func TestDaemonReloadInsecureRegistries(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	daemon.configStore = &config.Config{}
-
 	insecureRegistries := []string{
 	insecureRegistries := []string{
 		"127.0.0.0/8",         // this will be kept
 		"127.0.0.0/8",         // this will be kept
 		"10.10.1.11:5000",     // this will be kept
 		"10.10.1.11:5000",     // this will be kept
@@ -335,11 +331,11 @@ func TestDaemonReloadNotAffectOthers(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	label := daemon.configStore.Labels[0]
+	label := daemon.config().Labels[0]
 	if label != "foo:baz" {
 	if label != "foo:baz" {
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
 	}
 	}
-	debug := daemon.configStore.Debug
+	debug := daemon.config().Debug
 	if !debug {
 	if !debug {
 		t.Fatal("Expected debug 'enabled', got 'disabled'")
 		t.Fatal("Expected debug 'enabled', got 'disabled'")
 	}
 	}
@@ -360,7 +356,7 @@ func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
 		},
 		},
 	}
 	}
 
 
-	netOptions, err := daemon.networkOptions(nil, nil)
+	netOptions, err := daemon.networkOptions(&config.Config{}, nil, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 28 - 44
daemon/reload_unix.go

@@ -6,62 +6,46 @@ import (
 	"bytes"
 	"bytes"
 	"strconv"
 	"strconv"
 
 
-	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/config"
 )
 )
 
 
 // reloadPlatform updates configuration with platform specific options
 // reloadPlatform updates configuration with platform specific options
 // and updates the passed attributes
 // and updates the passed attributes
-func (daemon *Daemon) reloadPlatform(conf *config.Config) (func(attributes map[string]string), error) {
-	var txns []func()
-
+func (daemon *Daemon) reloadPlatform(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
+	if conf.DefaultRuntime != "" {
+		newCfg.DefaultRuntime = conf.DefaultRuntime
+	}
 	if conf.IsValueSet("runtimes") {
 	if conf.IsValueSet("runtimes") {
-		// Always set the default one
-		conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: config.DefaultRuntimeBinary}
-		if err := daemon.initRuntimes(conf.Runtimes); err != nil {
-			return nil, err
-		}
-		txns = append(txns, func() {
-			daemon.configStore.Runtimes = conf.Runtimes
-		})
+		newCfg.Runtimes = conf.Runtimes
+		txn.OnCommit(func() error { return daemon.initRuntimes(newCfg) })
 	}
 	}
+	configureRuntimes(newCfg)
 
 
-	if conf.DefaultRuntime != "" {
-		txns = append(txns, func() {
-			daemon.configStore.DefaultRuntime = conf.DefaultRuntime
-		})
+	if conf.IsValueSet("default-shm-size") {
+		newCfg.ShmSize = conf.ShmSize
 	}
 	}
 
 
-	return func(attributes map[string]string) {
-		for _, commit := range txns {
-			commit()
-		}
-
-		if conf.IsValueSet("default-shm-size") {
-			daemon.configStore.ShmSize = conf.ShmSize
-		}
-
-		if conf.CgroupNamespaceMode != "" {
-			daemon.configStore.CgroupNamespaceMode = conf.CgroupNamespaceMode
-		}
+	if conf.CgroupNamespaceMode != "" {
+		newCfg.CgroupNamespaceMode = conf.CgroupNamespaceMode
+	}
 
 
-		if conf.IpcMode != "" {
-			daemon.configStore.IpcMode = conf.IpcMode
-		}
+	if conf.IpcMode != "" {
+		newCfg.IpcMode = conf.IpcMode
+	}
 
 
-		// Update attributes
-		var runtimeList bytes.Buffer
-		for name, rt := range daemon.configStore.Runtimes {
-			if runtimeList.Len() > 0 {
-				runtimeList.WriteRune(' ')
-			}
-			runtimeList.WriteString(name + ":" + rt.Path)
+	// Update attributes
+	var runtimeList bytes.Buffer
+	for name, rt := range newCfg.Runtimes {
+		if runtimeList.Len() > 0 {
+			runtimeList.WriteRune(' ')
 		}
 		}
+		runtimeList.WriteString(name + ":" + rt.Path)
+	}
 
 
-		attributes["runtimes"] = runtimeList.String()
-		attributes["default-runtime"] = daemon.configStore.DefaultRuntime
-		attributes["default-shm-size"] = strconv.FormatInt(int64(daemon.configStore.ShmSize), 10)
-		attributes["default-ipc-mode"] = daemon.configStore.IpcMode
-		attributes["default-cgroupns-mode"] = daemon.configStore.CgroupNamespaceMode
-	}, nil
+	attributes["runtimes"] = runtimeList.String()
+	attributes["default-runtime"] = newCfg.DefaultRuntime
+	attributes["default-shm-size"] = strconv.FormatInt(int64(newCfg.ShmSize), 10)
+	attributes["default-ipc-mode"] = newCfg.IpcMode
+	attributes["default-cgroupns-mode"] = newCfg.CgroupNamespaceMode
+	return nil
 }
 }

+ 2 - 2
daemon/reload_windows.go

@@ -4,6 +4,6 @@ import "github.com/docker/docker/daemon/config"
 
 
 // reloadPlatform updates configuration with platform specific options
 // reloadPlatform updates configuration with platform specific options
 // and updates the passed attributes
 // and updates the passed attributes
-func (daemon *Daemon) reloadPlatform(conf *config.Config) (func(attributes map[string]string), error) {
-	return func(map[string]string) {}, nil
+func (daemon *Daemon) reloadPlatform(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
+	return nil
 }
 }

+ 4 - 3
daemon/restart.go

@@ -6,6 +6,7 @@ import (
 
 
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 )
 )
 
 
 // ContainerRestart stops and starts a container. It attempts to
 // ContainerRestart stops and starts a container. It attempts to
@@ -19,7 +20,7 @@ func (daemon *Daemon) ContainerRestart(ctx context.Context, name string, options
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	err = daemon.containerRestart(ctx, ctr, options)
+	err = daemon.containerRestart(ctx, daemon.config(), ctr, options)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("Cannot restart container %s: %v", name, err)
 		return fmt.Errorf("Cannot restart container %s: %v", name, err)
 	}
 	}
@@ -30,7 +31,7 @@ func (daemon *Daemon) ContainerRestart(ctx context.Context, name string, options
 // container. When stopping, wait for the given duration in seconds to
 // container. When stopping, wait for the given duration in seconds to
 // gracefully stop, before forcefully terminating the container. If
 // gracefully stop, before forcefully terminating the container. If
 // given a negative duration, wait forever for a graceful stop.
 // given a negative duration, wait forever for a graceful stop.
-func (daemon *Daemon) containerRestart(ctx context.Context, container *container.Container, options containertypes.StopOptions) error {
+func (daemon *Daemon) containerRestart(ctx context.Context, daemonCfg *config.Config, container *container.Container, options containertypes.StopOptions) error {
 	// Determine isolation. If not specified in the hostconfig, use daemon default.
 	// Determine isolation. If not specified in the hostconfig, use daemon default.
 	actualIsolation := container.HostConfig.Isolation
 	actualIsolation := container.HostConfig.Isolation
 	if containertypes.Isolation.IsDefault(actualIsolation) {
 	if containertypes.Isolation.IsDefault(actualIsolation) {
@@ -61,7 +62,7 @@ func (daemon *Daemon) containerRestart(ctx context.Context, container *container
 		}
 		}
 	}
 	}
 
 
-	if err := daemon.containerStart(ctx, container, "", "", true); err != nil {
+	if err := daemon.containerStart(ctx, daemonCfg, container, "", "", true); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 12 - 12
daemon/runtime_unix.go

@@ -51,15 +51,15 @@ func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimCon
 }
 }
 
 
 func (daemon *Daemon) loadRuntimes() error {
 func (daemon *Daemon) loadRuntimes() error {
-	return daemon.initRuntimes(daemon.configStore.Runtimes)
+	return daemon.initRuntimes(daemon.config())
 }
 }
 
 
-func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) {
-	runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes")
+func (daemon *Daemon) initRuntimes(cfg *config.Config) (err error) {
+	runtimeDir := filepath.Join(cfg.Root, "runtimes")
 	runtimeOldDir := runtimeDir + "-old"
 	runtimeOldDir := runtimeDir + "-old"
 	// Remove old temp directory if any
 	// Remove old temp directory if any
 	os.RemoveAll(runtimeOldDir)
 	os.RemoveAll(runtimeOldDir)
-	tmpDir, err := os.MkdirTemp(daemon.configStore.Root, "gen-runtimes")
+	tmpDir, err := os.MkdirTemp(cfg.Root, "gen-runtimes")
 	if err != nil {
 	if err != nil {
 		return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
 		return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
 	}
 	}
@@ -91,8 +91,8 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
 		}
 		}
 	}()
 	}()
 
 
-	for name := range runtimes {
-		rt := runtimes[name]
+	for name := range cfg.Runtimes {
+		rt := cfg.Runtimes[name]
 		if rt.Path == "" && rt.Type == "" {
 		if rt.Path == "" && rt.Type == "" {
 			return errors.Errorf("runtime %s: either a runtimeType or a path must be configured", name)
 			return errors.Errorf("runtime %s: either a runtimeType or a path must be configured", name)
 		}
 		}
@@ -111,7 +111,7 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
 					return err
 					return err
 				}
 				}
 			}
 			}
-			rt.ShimConfig = defaultV2ShimConfig(daemon.configStore, daemon.rewriteRuntimePath(name, rt.Path, rt.Args))
+			rt.ShimConfig = defaultV2ShimConfig(cfg, daemon.rewriteRuntimePath(cfg, name, rt.Path, rt.Args))
 			var featuresStderr bytes.Buffer
 			var featuresStderr bytes.Buffer
 			featuresCmd := exec.Command(rt.Path, append(rt.Args, "features")...)
 			featuresCmd := exec.Command(rt.Path, append(rt.Args, "features")...)
 			featuresCmd.Stderr = &featuresStderr
 			featuresCmd.Stderr = &featuresStderr
@@ -139,7 +139,7 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
 				}
 				}
 			}
 			}
 		}
 		}
-		runtimes[name] = rt
+		cfg.Runtimes[name] = rt
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -147,16 +147,16 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
 // rewriteRuntimePath is used for runtimes which have custom arguments supplied.
 // rewriteRuntimePath is used for runtimes which have custom arguments supplied.
 // This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments.
 // This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments.
 // To support this case, the daemon wraps the specified runtime in a script that passes through those arguments.
 // To support this case, the daemon wraps the specified runtime in a script that passes through those arguments.
-func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) string {
+func (daemon *Daemon) rewriteRuntimePath(cfg *config.Config, name, p string, args []string) string {
 	if len(args) == 0 {
 	if len(args) == 0 {
 		return p
 		return p
 	}
 	}
 
 
-	return filepath.Join(daemon.configStore.Root, "runtimes", name)
+	return filepath.Join(cfg.Root, "runtimes", name)
 }
 }
 
 
-func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
-	rt := daemon.configStore.GetRuntime(name)
+func (daemon *Daemon) getRuntime(cfg *config.Config, name string) (shim string, opts interface{}, err error) {
+	rt := cfg.GetRuntime(name)
 	if rt == nil {
 	if rt == nil {
 		if !config.IsPermissibleC8dRuntimeName(name) {
 		if !config.IsPermissibleC8dRuntimeName(name) {
 			return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
 			return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))

+ 17 - 12
daemon/runtime_unix_test.go

@@ -86,11 +86,13 @@ func TestInitRuntimes_InvalidConfigs(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			cfg, err := config.New()
 			cfg, err := config.New()
 			assert.NilError(t, err)
 			assert.NilError(t, err)
-			d := &Daemon{configStore: cfg}
-			d.configStore.Root = t.TempDir()
-			assert.Assert(t, os.Mkdir(filepath.Join(d.configStore.Root, "runtimes"), 0700))
+			cfg.Root = t.TempDir()
+			cfg.Runtimes["myruntime"] = tt.runtime
+			d := &Daemon{}
+			d.configStore.Store(cfg)
+			assert.Assert(t, os.Mkdir(filepath.Join(d.config().Root, "runtimes"), 0700))
 
 
-			err = d.initRuntimes(map[string]types.Runtime{"myruntime": tt.runtime})
+			err = d.initRuntimes(d.config())
 			assert.Check(t, is.ErrorContains(err, tt.expectErr))
 			assert.Check(t, is.ErrorContains(err, tt.expectErr))
 		})
 		})
 	}
 	}
@@ -124,20 +126,22 @@ func TestGetRuntime(t *testing.T) {
 	cfg, err := config.New()
 	cfg, err := config.New()
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	d := &Daemon{configStore: cfg}
-	d.configStore.Root = t.TempDir()
-	assert.Assert(t, os.Mkdir(filepath.Join(d.configStore.Root, "runtimes"), 0700))
-	d.configStore.Runtimes = map[string]types.Runtime{
+	cfg.Root = t.TempDir()
+	assert.Assert(t, os.Mkdir(filepath.Join(cfg.Root, "runtimes"), 0700))
+	cfg.Runtimes = map[string]types.Runtime{
 		configuredRtName:         configuredRuntime,
 		configuredRtName:         configuredRuntime,
 		rtWithArgsName:           rtWithArgs,
 		rtWithArgsName:           rtWithArgs,
 		shimWithOptsName:         shimWithOpts,
 		shimWithOptsName:         shimWithOpts,
 		shimAliasName:            shimAlias,
 		shimAliasName:            shimAlias,
 		configuredShimByPathName: configuredShimByPath,
 		configuredShimByPathName: configuredShimByPath,
 	}
 	}
-	configureRuntimes(d.configStore)
+	configureRuntimes(cfg)
+
+	d := &Daemon{}
+	d.configStore.Store(cfg)
 	assert.Assert(t, d.loadRuntimes())
 	assert.Assert(t, d.loadRuntimes())
 
 
-	stockRuntime, ok := d.configStore.Runtimes[config.StockRuntimeName]
+	stockRuntime, ok := cfg.Runtimes[config.StockRuntimeName]
 	assert.Assert(t, ok, "stock runtime could not be found (test needs to be updated)")
 	assert.Assert(t, ok, "stock runtime could not be found (test needs to be updated)")
 
 
 	configdOpts := *stockRuntime.ShimConfig.Opts.(*v2runcoptions.Options)
 	configdOpts := *stockRuntime.ShimConfig.Opts.(*v2runcoptions.Options)
@@ -199,8 +203,9 @@ func TestGetRuntime(t *testing.T) {
 			runtime:  rtWithArgsName,
 			runtime:  rtWithArgsName,
 			wantShim: stockRuntime.ShimConfig.Binary,
 			wantShim: stockRuntime.ShimConfig.Binary,
 			wantOpts: defaultV2ShimConfig(
 			wantOpts: defaultV2ShimConfig(
-				d.configStore,
+				d.config(),
 				d.rewriteRuntimePath(
 				d.rewriteRuntimePath(
+					d.config(),
 					rtWithArgsName,
 					rtWithArgsName,
 					rtWithArgs.Path,
 					rtWithArgs.Path,
 					rtWithArgs.Args)).Opts,
 					rtWithArgs.Args)).Opts,
@@ -224,7 +229,7 @@ func TestGetRuntime(t *testing.T) {
 	} {
 	} {
 		tt := tt
 		tt := tt
 		t.Run(tt.name, func(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
-			gotShim, gotOpts, err := d.getRuntime(tt.runtime)
+			gotShim, gotOpts, err := d.getRuntime(cfg, tt.runtime)
 			assert.Check(t, is.Equal(gotShim, tt.wantShim))
 			assert.Check(t, is.Equal(gotShim, tt.wantShim))
 			assert.Check(t, is.DeepEqual(gotOpts, tt.wantOpts))
 			assert.Check(t, is.DeepEqual(gotOpts, tt.wantOpts))
 			if tt.wantShim != "" {
 			if tt.wantShim != "" {

+ 3 - 1
daemon/runtime_windows.go

@@ -2,8 +2,10 @@ package daemon
 
 
 import (
 import (
 	"errors"
 	"errors"
+
+	"github.com/docker/docker/daemon/config"
 )
 )
 
 
-func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
+func (daemon *Daemon) getRuntime(cfg *config.Config, name string) (shim string, opts interface{}, err error) {
 	return "", nil, errors.New("not implemented")
 	return "", nil, errors.New("not implemented")
 }
 }

+ 12 - 10
daemon/start.go

@@ -9,6 +9,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libcontainerd"
 	"github.com/docker/docker/libcontainerd"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
@@ -17,7 +18,8 @@ import (
 
 
 // ContainerStart starts a container.
 // ContainerStart starts a container.
 func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
 func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
-	if checkpoint != "" && !daemon.HasExperimental() {
+	daemonCfg := daemon.config()
+	if checkpoint != "" && !daemonCfg.Experimental {
 		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
 		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
 	}
 	}
 
 
@@ -55,7 +57,7 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
 		if hostConfig != nil {
 		if hostConfig != nil {
 			logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
 			logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
 			oldNetworkMode := ctr.HostConfig.NetworkMode
 			oldNetworkMode := ctr.HostConfig.NetworkMode
-			if err := daemon.setSecurityOptions(ctr, hostConfig); err != nil {
+			if err := daemon.setSecurityOptions(daemonCfg, ctr, hostConfig); err != nil {
 				return errdefs.InvalidParameter(err)
 				return errdefs.InvalidParameter(err)
 			}
 			}
 			if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
 			if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
@@ -83,24 +85,24 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
 
 
 	// check if hostConfig is in line with the current system settings.
 	// check if hostConfig is in line with the current system settings.
 	// It may happen cgroups are umounted or the like.
 	// It may happen cgroups are umounted or the like.
-	if _, err = daemon.verifyContainerSettings(ctr.HostConfig, nil, false); err != nil {
+	if _, err = daemon.verifyContainerSettings(daemonCfg, ctr.HostConfig, nil, false); err != nil {
 		return errdefs.InvalidParameter(err)
 		return errdefs.InvalidParameter(err)
 	}
 	}
 	// Adapt for old containers in case we have updates in this function and
 	// Adapt for old containers in case we have updates in this function and
 	// old containers never have chance to call the new function in create stage.
 	// old containers never have chance to call the new function in create stage.
 	if hostConfig != nil {
 	if hostConfig != nil {
-		if err := daemon.adaptContainerSettings(ctr.HostConfig, false); err != nil {
+		if err := daemon.adaptContainerSettings(daemonCfg, ctr.HostConfig, false); err != nil {
 			return errdefs.InvalidParameter(err)
 			return errdefs.InvalidParameter(err)
 		}
 		}
 	}
 	}
-	return daemon.containerStart(ctx, ctr, checkpoint, checkpointDir, true)
+	return daemon.containerStart(ctx, daemonCfg, ctr, checkpoint, checkpointDir, true)
 }
 }
 
 
 // containerStart prepares the container to run by setting up everything the
 // containerStart prepares the container to run by setting up everything the
 // container needs, such as storage and networking, as well as links
 // container needs, such as storage and networking, as well as links
 // between containers. The container is left waiting for a signal to
 // between containers. The container is left waiting for a signal to
 // begin running.
 // begin running.
-func (daemon *Daemon) containerStart(ctx context.Context, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
+func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *config.Config, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
 	start := time.Now()
 	start := time.Now()
 	container.Lock()
 	container.Lock()
 	defer container.Unlock()
 	defer container.Unlock()
@@ -136,7 +138,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
 			// if containers AutoRemove flag is set, remove it after clean up
 			// if containers AutoRemove flag is set, remove it after clean up
 			if container.HostConfig.AutoRemove {
 			if container.HostConfig.AutoRemove {
 				container.Unlock()
 				container.Unlock()
-				if err := daemon.ContainerRm(container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
+				if err := daemon.containerRm(daemonCfg, container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
 					logrus.Errorf("can't remove container %s: %v", container.ID, err)
 					logrus.Errorf("can't remove container %s: %v", container.ID, err)
 				}
 				}
 				container.Lock()
 				container.Lock()
@@ -148,11 +150,11 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
 		return err
 		return err
 	}
 	}
 
 
-	if err := daemon.initializeNetworking(container); err != nil {
+	if err := daemon.initializeNetworking(daemonCfg, container); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	spec, err := daemon.createSpec(ctx, container)
+	spec, err := daemon.createSpec(ctx, daemonCfg, container)
 	if err != nil {
 	if err != nil {
 		return errdefs.System(err)
 		return errdefs.System(err)
 	}
 	}
@@ -173,7 +175,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
 		}
 		}
 	}
 	}
 
 
-	shim, createOptions, err := daemon.getLibcontainerdCreateOptions(container)
+	shim, createOptions, err := daemon.getLibcontainerdCreateOptions(daemonCfg, container)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 4 - 3
daemon/start_unix.go

@@ -4,17 +4,18 @@ package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 )
 )
 
 
 // getLibcontainerdCreateOptions callers must hold a lock on the container
 // getLibcontainerdCreateOptions callers must hold a lock on the container
-func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (string, interface{}, error) {
+func (daemon *Daemon) getLibcontainerdCreateOptions(daemonCfg *config.Config, container *container.Container) (string, interface{}, error) {
 	// Ensure a runtime has been assigned to this container
 	// Ensure a runtime has been assigned to this container
 	if container.HostConfig.Runtime == "" {
 	if container.HostConfig.Runtime == "" {
-		container.HostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
+		container.HostConfig.Runtime = daemonCfg.DefaultRuntime
 		container.CheckpointTo(daemon.containersReplica)
 		container.CheckpointTo(daemon.containersReplica)
 	}
 	}
 
 
-	binary, opts, err := daemon.getRuntime(container.HostConfig.Runtime)
+	binary, opts, err := daemon.getRuntime(daemonCfg, container.HostConfig.Runtime)
 	if err != nil {
 	if err != nil {
 		return "", nil, setExitCodeFromError(container.SetExitCode, err)
 		return "", nil, setExitCodeFromError(container.SetExitCode, err)
 	}
 	}

+ 2 - 1
daemon/start_windows.go

@@ -3,10 +3,11 @@ package daemon // import "github.com/docker/docker/daemon"
 import (
 import (
 	"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
 	"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 )
 )
 
 
-func (daemon *Daemon) getLibcontainerdCreateOptions(_ *container.Container) (string, interface{}, error) {
+func (daemon *Daemon) getLibcontainerdCreateOptions(*config.Config, *container.Container) (string, interface{}, error) {
 	if system.ContainerdRuntimeSupported() {
 	if system.ContainerdRuntimeSupported() {
 		opts := &options.Options{}
 		opts := &options.Options{}
 		return "io.containerd.runhcs.v1", opts, nil
 		return "io.containerd.runhcs.v1", opts, nil

+ 2 - 1
daemon/update.go

@@ -13,7 +13,8 @@ import (
 func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error) {
 func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error) {
 	var warnings []string
 	var warnings []string
 
 
-	warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true)
+	daemonCfg := daemon.config()
+	warnings, err := daemon.verifyContainerSettings(daemonCfg, hostConfig, nil, true)
 	if err != nil {
 	if err != nil {
 		return container.ContainerUpdateOKBody{Warnings: warnings}, errdefs.InvalidParameter(err)
 		return container.ContainerUpdateOKBody{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 	}

+ 4 - 0
vendor.mod

@@ -58,6 +58,7 @@ require (
 	github.com/klauspost/compress v1.16.3
 	github.com/klauspost/compress v1.16.3
 	github.com/miekg/dns v1.1.43
 	github.com/miekg/dns v1.1.43
 	github.com/mistifyio/go-zfs/v3 v3.0.1
 	github.com/mistifyio/go-zfs/v3 v3.0.1
+	github.com/mitchellh/copystructure v1.2.0
 	github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f
 	github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f
 	github.com/moby/ipvs v1.1.0
 	github.com/moby/ipvs v1.1.0
 	github.com/moby/locker v1.0.1
 	github.com/moby/locker v1.0.1
@@ -154,6 +155,9 @@ require (
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
 	github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
+	github.com/mitchellh/reflectwalk v1.0.2 // indirect
+	github.com/onsi/ginkgo/v2 v2.1.4 // indirect
+	github.com/onsi/gomega v1.20.1 // indirect
 	github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
 	github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
 	github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170 // indirect
 	github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170 // indirect
 	github.com/philhofer/fwd v1.1.2 // indirect
 	github.com/philhofer/fwd v1.1.2 // indirect

+ 8 - 2
vendor.sum

@@ -1034,6 +1034,8 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go
 github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
 github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
 github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
 github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
 github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
@@ -1048,6 +1050,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
 github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
 github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
 github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
 github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
 github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f h1:9wobL03Y6U8azuDLUqYblbUdVU9jpjqecDdW7w4wZtI=
 github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f h1:9wobL03Y6U8azuDLUqYblbUdVU9jpjqecDdW7w4wZtI=
@@ -1121,8 +1125,9 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
 github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
-github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
+github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
+github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
 github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -1133,8 +1138,9 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT
 github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
 github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
-github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
+github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=

+ 21 - 0
vendor/github.com/mitchellh/copystructure/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Mitchell Hashimoto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 21 - 0
vendor/github.com/mitchellh/copystructure/README.md

@@ -0,0 +1,21 @@
+# copystructure
+
+copystructure is a Go library for deep copying values in Go.
+
+This allows you to copy Go values that may contain reference values
+such as maps, slices, or pointers, and copy their data as well instead
+of just their references.
+
+## Installation
+
+Standard `go get`:
+
+```
+$ go get github.com/mitchellh/copystructure
+```
+
+## Usage & Example
+
+For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/copystructure).
+
+The `Copy` function has examples associated with it there.

+ 15 - 0
vendor/github.com/mitchellh/copystructure/copier_time.go

@@ -0,0 +1,15 @@
+package copystructure
+
+import (
+	"reflect"
+	"time"
+)
+
+func init() {
+	Copiers[reflect.TypeOf(time.Time{})] = timeCopier
+}
+
+func timeCopier(v interface{}) (interface{}, error) {
+	// Just... copy it.
+	return v.(time.Time), nil
+}

+ 631 - 0
vendor/github.com/mitchellh/copystructure/copystructure.go

@@ -0,0 +1,631 @@
+package copystructure
+
+import (
+	"errors"
+	"reflect"
+	"sync"
+
+	"github.com/mitchellh/reflectwalk"
+)
+
+const tagKey = "copy"
+
+// Copy returns a deep copy of v.
+//
+// Copy is unable to copy unexported fields in a struct (lowercase field names).
+// Unexported fields can't be reflected by the Go runtime and therefore
+// copystructure can't perform any data copies.
+//
+// For structs, copy behavior can be controlled with struct tags. For example:
+//
+//   struct {
+//     Name string
+//     Data *bytes.Buffer `copy:"shallow"`
+//   }
+//
+// The available tag values are:
+//
+// * "ignore" - The field will be ignored, effectively resulting in it being
+//   assigned the zero value in the copy.
+//
+// * "shallow" - The field will be be shallow copied. This means that references
+//   values such as pointers, maps, slices, etc. will be directly assigned
+//   versus deep copied.
+//
+func Copy(v interface{}) (interface{}, error) {
+	return Config{}.Copy(v)
+}
+
+// CopierFunc is a function that knows how to deep copy a specific type.
+// Register these globally with the Copiers variable.
+type CopierFunc func(interface{}) (interface{}, error)
+
+// Copiers is a map of types that behave specially when they are copied.
+// If a type is found in this map while deep copying, this function
+// will be called to copy it instead of attempting to copy all fields.
+//
+// The key should be the type, obtained using: reflect.TypeOf(value with type).
+//
+// It is unsafe to write to this map after Copies have started. If you
+// are writing to this map while also copying, wrap all modifications to
+// this map as well as to Copy in a mutex.
+var Copiers map[reflect.Type]CopierFunc = make(map[reflect.Type]CopierFunc)
+
+// ShallowCopiers is a map of pointer types that behave specially
+// when they are copied.  If a type is found in this map while deep
+// copying, the pointer value will be shallow copied and not walked
+// into.
+//
+// The key should be the type, obtained using: reflect.TypeOf(value
+// with type).
+//
+// It is unsafe to write to this map after Copies have started. If you
+// are writing to this map while also copying, wrap all modifications to
+// this map as well as to Copy in a mutex.
+var ShallowCopiers map[reflect.Type]struct{} = make(map[reflect.Type]struct{})
+
+// Must is a helper that wraps a call to a function returning
+// (interface{}, error) and panics if the error is non-nil. It is intended
+// for use in variable initializations and should only be used when a copy
+// error should be a crashing case.
+func Must(v interface{}, err error) interface{} {
+	if err != nil {
+		panic("copy error: " + err.Error())
+	}
+
+	return v
+}
+
+var errPointerRequired = errors.New("Copy argument must be a pointer when Lock is true")
+
+type Config struct {
+	// Lock any types that are a sync.Locker and are not a mutex while copying.
+	// If there is an RLocker method, use that to get the sync.Locker.
+	Lock bool
+
+	// Copiers is a map of types associated with a CopierFunc. Use the global
+	// Copiers map if this is nil.
+	Copiers map[reflect.Type]CopierFunc
+
+	// ShallowCopiers is a map of pointer types that when they are
+	// shallow copied no matter where they are encountered. Use the
+	// global ShallowCopiers if this is nil.
+	ShallowCopiers map[reflect.Type]struct{}
+}
+
+func (c Config) Copy(v interface{}) (interface{}, error) {
+	if c.Lock && reflect.ValueOf(v).Kind() != reflect.Ptr {
+		return nil, errPointerRequired
+	}
+
+	w := new(walker)
+	if c.Lock {
+		w.useLocks = true
+	}
+
+	if c.Copiers == nil {
+		c.Copiers = Copiers
+	}
+	w.copiers = c.Copiers
+
+	if c.ShallowCopiers == nil {
+		c.ShallowCopiers = ShallowCopiers
+	}
+	w.shallowCopiers = c.ShallowCopiers
+
+	err := reflectwalk.Walk(v, w)
+	if err != nil {
+		return nil, err
+	}
+
+	// Get the result. If the result is nil, then we want to turn it
+	// into a typed nil if we can.
+	result := w.Result
+	if result == nil {
+		val := reflect.ValueOf(v)
+		result = reflect.Indirect(reflect.New(val.Type())).Interface()
+	}
+
+	return result, nil
+}
+
+// Return the key used to index interfaces types we've seen. Store the number
+// of pointers in the upper 32bits, and the depth in the lower 32bits. This is
+// easy to calculate, easy to match a key with our current depth, and we don't
+// need to deal with initializing and cleaning up nested maps or slices.
+func ifaceKey(pointers, depth int) uint64 {
+	return uint64(pointers)<<32 | uint64(depth)
+}
+
+type walker struct {
+	Result interface{}
+
+	copiers        map[reflect.Type]CopierFunc
+	shallowCopiers map[reflect.Type]struct{}
+	depth          int
+	ignoreDepth    int
+	vals           []reflect.Value
+	cs             []reflect.Value
+
+	// This stores the number of pointers we've walked over, indexed by depth.
+	ps []int
+
+	// If an interface is indirected by a pointer, we need to know the type of
+	// interface to create when creating the new value.  Store the interface
+	// types here, indexed by both the walk depth and the number of pointers
+	// already seen at that depth. Use ifaceKey to calculate the proper uint64
+	// value.
+	ifaceTypes map[uint64]reflect.Type
+
+	// any locks we've taken, indexed by depth
+	locks []sync.Locker
+	// take locks while walking the structure
+	useLocks bool
+}
+
+func (w *walker) Enter(l reflectwalk.Location) error {
+	w.depth++
+
+	// ensure we have enough elements to index via w.depth
+	for w.depth >= len(w.locks) {
+		w.locks = append(w.locks, nil)
+	}
+
+	for len(w.ps) < w.depth+1 {
+		w.ps = append(w.ps, 0)
+	}
+
+	return nil
+}
+
+func (w *walker) Exit(l reflectwalk.Location) error {
+	locker := w.locks[w.depth]
+	w.locks[w.depth] = nil
+	if locker != nil {
+		defer locker.Unlock()
+	}
+
+	// clear out pointers and interfaces as we exit the stack
+	w.ps[w.depth] = 0
+
+	for k := range w.ifaceTypes {
+		mask := uint64(^uint32(0))
+		if k&mask == uint64(w.depth) {
+			delete(w.ifaceTypes, k)
+		}
+	}
+
+	w.depth--
+	if w.ignoreDepth > w.depth {
+		w.ignoreDepth = 0
+	}
+
+	if w.ignoring() {
+		return nil
+	}
+
+	switch l {
+	case reflectwalk.Array:
+		fallthrough
+	case reflectwalk.Map:
+		fallthrough
+	case reflectwalk.Slice:
+		w.replacePointerMaybe()
+
+		// Pop map off our container
+		w.cs = w.cs[:len(w.cs)-1]
+	case reflectwalk.MapValue:
+		// Pop off the key and value
+		mv := w.valPop()
+		mk := w.valPop()
+		m := w.cs[len(w.cs)-1]
+
+		// If mv is the zero value, SetMapIndex deletes the key form the map,
+		// or in this case never adds it. We need to create a properly typed
+		// zero value so that this key can be set.
+		if !mv.IsValid() {
+			mv = reflect.Zero(m.Elem().Type().Elem())
+		}
+		m.Elem().SetMapIndex(mk, mv)
+	case reflectwalk.ArrayElem:
+		// Pop off the value and the index and set it on the array
+		v := w.valPop()
+		i := w.valPop().Interface().(int)
+		if v.IsValid() {
+			a := w.cs[len(w.cs)-1]
+			ae := a.Elem().Index(i) // storing array as pointer on stack - so need Elem() call
+			if ae.CanSet() {
+				ae.Set(v)
+			}
+		}
+	case reflectwalk.SliceElem:
+		// Pop off the value and the index and set it on the slice
+		v := w.valPop()
+		i := w.valPop().Interface().(int)
+		if v.IsValid() {
+			s := w.cs[len(w.cs)-1]
+			se := s.Elem().Index(i)
+			if se.CanSet() {
+				se.Set(v)
+			}
+		}
+	case reflectwalk.Struct:
+		w.replacePointerMaybe()
+
+		// Remove the struct from the container stack
+		w.cs = w.cs[:len(w.cs)-1]
+	case reflectwalk.StructField:
+		// Pop off the value and the field
+		v := w.valPop()
+		f := w.valPop().Interface().(reflect.StructField)
+		if v.IsValid() {
+			s := w.cs[len(w.cs)-1]
+			sf := reflect.Indirect(s).FieldByName(f.Name)
+
+			if sf.CanSet() {
+				sf.Set(v)
+			}
+		}
+	case reflectwalk.WalkLoc:
+		// Clear out the slices for GC
+		w.cs = nil
+		w.vals = nil
+	}
+
+	return nil
+}
+
+func (w *walker) Map(m reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+	w.lock(m)
+
+	// Create the map. If the map itself is nil, then just make a nil map
+	var newMap reflect.Value
+	if m.IsNil() {
+		newMap = reflect.New(m.Type())
+	} else {
+		newMap = wrapPtr(reflect.MakeMap(m.Type()))
+	}
+
+	w.cs = append(w.cs, newMap)
+	w.valPush(newMap)
+	return nil
+}
+
+func (w *walker) MapElem(m, k, v reflect.Value) error {
+	return nil
+}
+
+func (w *walker) PointerEnter(v bool) error {
+	if v {
+		w.ps[w.depth]++
+	}
+	return nil
+}
+
+func (w *walker) PointerExit(v bool) error {
+	if v {
+		w.ps[w.depth]--
+	}
+	return nil
+}
+
+func (w *walker) Pointer(v reflect.Value) error {
+	if _, ok := w.shallowCopiers[v.Type()]; ok {
+		// Shallow copy this value. Use the same logic as primitive, then
+		// return skip.
+		if err := w.Primitive(v); err != nil {
+			return err
+		}
+
+		return reflectwalk.SkipEntry
+	}
+
+	return nil
+}
+
+func (w *walker) Interface(v reflect.Value) error {
+	if !v.IsValid() {
+		return nil
+	}
+	if w.ifaceTypes == nil {
+		w.ifaceTypes = make(map[uint64]reflect.Type)
+	}
+
+	w.ifaceTypes[ifaceKey(w.ps[w.depth], w.depth)] = v.Type()
+	return nil
+}
+
+func (w *walker) Primitive(v reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+	w.lock(v)
+
+	// IsValid verifies the v is non-zero and CanInterface verifies
+	// that we're allowed to read this value (unexported fields).
+	var newV reflect.Value
+	if v.IsValid() && v.CanInterface() {
+		newV = reflect.New(v.Type())
+		newV.Elem().Set(v)
+	}
+
+	w.valPush(newV)
+	w.replacePointerMaybe()
+	return nil
+}
+
+func (w *walker) Slice(s reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+	w.lock(s)
+
+	var newS reflect.Value
+	if s.IsNil() {
+		newS = reflect.New(s.Type())
+	} else {
+		newS = wrapPtr(reflect.MakeSlice(s.Type(), s.Len(), s.Cap()))
+	}
+
+	w.cs = append(w.cs, newS)
+	w.valPush(newS)
+	return nil
+}
+
+func (w *walker) SliceElem(i int, elem reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+
+	// We don't write the slice here because elem might still be
+	// arbitrarily complex. Just record the index and continue on.
+	w.valPush(reflect.ValueOf(i))
+
+	return nil
+}
+
+func (w *walker) Array(a reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+	w.lock(a)
+
+	newA := reflect.New(a.Type())
+
+	w.cs = append(w.cs, newA)
+	w.valPush(newA)
+	return nil
+}
+
+func (w *walker) ArrayElem(i int, elem reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+
+	// We don't write the array here because elem might still be
+	// arbitrarily complex. Just record the index and continue on.
+	w.valPush(reflect.ValueOf(i))
+
+	return nil
+}
+
+func (w *walker) Struct(s reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+	w.lock(s)
+
+	var v reflect.Value
+	if c, ok := w.copiers[s.Type()]; ok {
+		// We have a Copier for this struct, so we use that copier to
+		// get the copy, and we ignore anything deeper than this.
+		w.ignoreDepth = w.depth
+
+		dup, err := c(s.Interface())
+		if err != nil {
+			return err
+		}
+
+		// We need to put a pointer to the value on the value stack,
+		// so allocate a new pointer and set it.
+		v = reflect.New(s.Type())
+		reflect.Indirect(v).Set(reflect.ValueOf(dup))
+	} else {
+		// No copier, we copy ourselves and allow reflectwalk to guide
+		// us deeper into the structure for copying.
+		v = reflect.New(s.Type())
+	}
+
+	// Push the value onto the value stack for setting the struct field,
+	// and add the struct itself to the containers stack in case we walk
+	// deeper so that its own fields can be modified.
+	w.valPush(v)
+	w.cs = append(w.cs, v)
+
+	return nil
+}
+
+func (w *walker) StructField(f reflect.StructField, v reflect.Value) error {
+	if w.ignoring() {
+		return nil
+	}
+
+	// If PkgPath is non-empty, this is a private (unexported) field.
+	// We do not set this unexported since the Go runtime doesn't allow us.
+	if f.PkgPath != "" {
+		return reflectwalk.SkipEntry
+	}
+
+	switch f.Tag.Get(tagKey) {
+	case "shallow":
+		// If we're shallow copying then assign the value directly to the
+		// struct and skip the entry.
+		if v.IsValid() {
+			s := w.cs[len(w.cs)-1]
+			sf := reflect.Indirect(s).FieldByName(f.Name)
+			if sf.CanSet() {
+				sf.Set(v)
+			}
+		}
+
+		return reflectwalk.SkipEntry
+
+	case "ignore":
+		// Do nothing
+		return reflectwalk.SkipEntry
+	}
+
+	// Push the field onto the stack, we'll handle it when we exit
+	// the struct field in Exit...
+	w.valPush(reflect.ValueOf(f))
+
+	return nil
+}
+
+// ignore causes the walker to ignore any more values until we exit this on
+func (w *walker) ignore() {
+	w.ignoreDepth = w.depth
+}
+
+func (w *walker) ignoring() bool {
+	return w.ignoreDepth > 0 && w.depth >= w.ignoreDepth
+}
+
+func (w *walker) pointerPeek() bool {
+	return w.ps[w.depth] > 0
+}
+
+func (w *walker) valPop() reflect.Value {
+	result := w.vals[len(w.vals)-1]
+	w.vals = w.vals[:len(w.vals)-1]
+
+	// If we're out of values, that means we popped everything off. In
+	// this case, we reset the result so the next pushed value becomes
+	// the result.
+	if len(w.vals) == 0 {
+		w.Result = nil
+	}
+
+	return result
+}
+
+func (w *walker) valPush(v reflect.Value) {
+	w.vals = append(w.vals, v)
+
+	// If we haven't set the result yet, then this is the result since
+	// it is the first (outermost) value we're seeing.
+	if w.Result == nil && v.IsValid() {
+		w.Result = v.Interface()
+	}
+}
+
+func (w *walker) replacePointerMaybe() {
+	// Determine the last pointer value. If it is NOT a pointer, then
+	// we need to push that onto the stack.
+	if !w.pointerPeek() {
+		w.valPush(reflect.Indirect(w.valPop()))
+		return
+	}
+
+	v := w.valPop()
+
+	// If the expected type is a pointer to an interface of any depth,
+	// such as *interface{}, **interface{}, etc., then we need to convert
+	// the value "v" from *CONCRETE to *interface{} so types match for
+	// Set.
+	//
+	// Example if v is type *Foo where Foo is a struct, v would become
+	// *interface{} instead. This only happens if we have an interface expectation
+	// at this depth.
+	//
+	// For more info, see GH-16
+	if iType, ok := w.ifaceTypes[ifaceKey(w.ps[w.depth], w.depth)]; ok && iType.Kind() == reflect.Interface {
+		y := reflect.New(iType)           // Create *interface{}
+		y.Elem().Set(reflect.Indirect(v)) // Assign "Foo" to interface{} (dereferenced)
+		v = y                             // v is now typed *interface{} (where *v = Foo)
+	}
+
+	for i := 1; i < w.ps[w.depth]; i++ {
+		if iType, ok := w.ifaceTypes[ifaceKey(w.ps[w.depth]-i, w.depth)]; ok {
+			iface := reflect.New(iType).Elem()
+			iface.Set(v)
+			v = iface
+		}
+
+		p := reflect.New(v.Type())
+		p.Elem().Set(v)
+		v = p
+	}
+
+	w.valPush(v)
+}
+
+// if this value is a Locker, lock it and add it to the locks slice
+func (w *walker) lock(v reflect.Value) {
+	if !w.useLocks {
+		return
+	}
+
+	if !v.IsValid() || !v.CanInterface() {
+		return
+	}
+
+	type rlocker interface {
+		RLocker() sync.Locker
+	}
+
+	var locker sync.Locker
+
+	// We can't call Interface() on a value directly, since that requires
+	// a copy. This is OK, since the pointer to a value which is a sync.Locker
+	// is also a sync.Locker.
+	if v.Kind() == reflect.Ptr {
+		switch l := v.Interface().(type) {
+		case rlocker:
+			// don't lock a mutex directly
+			if _, ok := l.(*sync.RWMutex); !ok {
+				locker = l.RLocker()
+			}
+		case sync.Locker:
+			locker = l
+		}
+	} else if v.CanAddr() {
+		switch l := v.Addr().Interface().(type) {
+		case rlocker:
+			// don't lock a mutex directly
+			if _, ok := l.(*sync.RWMutex); !ok {
+				locker = l.RLocker()
+			}
+		case sync.Locker:
+			locker = l
+		}
+	}
+
+	// still no callable locker
+	if locker == nil {
+		return
+	}
+
+	// don't lock a mutex directly
+	switch locker.(type) {
+	case *sync.Mutex, *sync.RWMutex:
+		return
+	}
+
+	locker.Lock()
+	w.locks[w.depth] = locker
+}
+
+// wrapPtr is a helper that takes v and always make it *v. copystructure
+// stores things internally as pointers until the last moment before unwrapping
+func wrapPtr(v reflect.Value) reflect.Value {
+	if !v.IsValid() {
+		return v
+	}
+	vPtr := reflect.New(v.Type())
+	vPtr.Elem().Set(v)
+	return vPtr
+}

+ 1 - 0
vendor/github.com/mitchellh/reflectwalk/.travis.yml

@@ -0,0 +1 @@
+language: go

+ 21 - 0
vendor/github.com/mitchellh/reflectwalk/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Mitchell Hashimoto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 6 - 0
vendor/github.com/mitchellh/reflectwalk/README.md

@@ -0,0 +1,6 @@
+# reflectwalk
+
+reflectwalk is a Go library for "walking" a value in Go using reflection,
+in the same way a directory tree can be "walked" on the filesystem. Walking
+a complex structure can allow you to do manipulations on unknown structures
+such as those decoded from JSON.

+ 19 - 0
vendor/github.com/mitchellh/reflectwalk/location.go

@@ -0,0 +1,19 @@
+package reflectwalk
+
+//go:generate stringer -type=Location location.go
+
+type Location uint
+
+const (
+	None Location = iota
+	Map
+	MapKey
+	MapValue
+	Slice
+	SliceElem
+	Array
+	ArrayElem
+	Struct
+	StructField
+	WalkLoc
+)

+ 16 - 0
vendor/github.com/mitchellh/reflectwalk/location_string.go

@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=Location location.go"; DO NOT EDIT.
+
+package reflectwalk
+
+import "fmt"
+
+const _Location_name = "NoneMapMapKeyMapValueSliceSliceElemArrayArrayElemStructStructFieldWalkLoc"
+
+var _Location_index = [...]uint8{0, 4, 7, 13, 21, 26, 35, 40, 49, 55, 66, 73}
+
+func (i Location) String() string {
+	if i >= Location(len(_Location_index)-1) {
+		return fmt.Sprintf("Location(%d)", i)
+	}
+	return _Location_name[_Location_index[i]:_Location_index[i+1]]
+}

+ 420 - 0
vendor/github.com/mitchellh/reflectwalk/reflectwalk.go

@@ -0,0 +1,420 @@
+// reflectwalk is a package that allows you to "walk" complex structures
+// similar to how you may "walk" a filesystem: visiting every element one
+// by one and calling callback functions allowing you to handle and manipulate
+// those elements.
+package reflectwalk
+
+import (
+	"errors"
+	"reflect"
+)
+
+// PrimitiveWalker implementations are able to handle primitive values
+// within complex structures. Primitive values are numbers, strings,
+// booleans, funcs, chans.
+//
+// These primitive values are often members of more complex
+// structures (slices, maps, etc.) that are walkable by other interfaces.
+type PrimitiveWalker interface {
+	Primitive(reflect.Value) error
+}
+
+// InterfaceWalker implementations are able to handle interface values as they
+// are encountered during the walk.
+type InterfaceWalker interface {
+	Interface(reflect.Value) error
+}
+
+// MapWalker implementations are able to handle individual elements
+// found within a map structure.
+type MapWalker interface {
+	Map(m reflect.Value) error
+	MapElem(m, k, v reflect.Value) error
+}
+
+// SliceWalker implementations are able to handle slice elements found
+// within complex structures.
+type SliceWalker interface {
+	Slice(reflect.Value) error
+	SliceElem(int, reflect.Value) error
+}
+
+// ArrayWalker implementations are able to handle array elements found
+// within complex structures.
+type ArrayWalker interface {
+	Array(reflect.Value) error
+	ArrayElem(int, reflect.Value) error
+}
+
+// StructWalker is an interface that has methods that are called for
+// structs when a Walk is done.
+type StructWalker interface {
+	Struct(reflect.Value) error
+	StructField(reflect.StructField, reflect.Value) error
+}
+
+// EnterExitWalker implementations are notified before and after
+// they walk deeper into complex structures (into struct fields,
+// into slice elements, etc.)
+type EnterExitWalker interface {
+	Enter(Location) error
+	Exit(Location) error
+}
+
+// PointerWalker implementations are notified when the value they're
+// walking is a pointer or not. Pointer is called for _every_ value whether
+// it is a pointer or not.
+type PointerWalker interface {
+	PointerEnter(bool) error
+	PointerExit(bool) error
+}
+
+// PointerValueWalker implementations are notified with the value of
+// a particular pointer when a pointer is walked. Pointer is called
+// right before PointerEnter.
+type PointerValueWalker interface {
+	Pointer(reflect.Value) error
+}
+
+// SkipEntry can be returned from walk functions to skip walking
+// the value of this field. This is only valid in the following functions:
+//
+//   - Struct: skips all fields from being walked
+//   - StructField: skips walking the struct value
+//
+var SkipEntry = errors.New("skip this entry")
+
+// Walk takes an arbitrary value and an interface and traverses the
+// value, calling callbacks on the interface if they are supported.
+// The interface should implement one or more of the walker interfaces
+// in this package, such as PrimitiveWalker, StructWalker, etc.
+func Walk(data, walker interface{}) (err error) {
+	v := reflect.ValueOf(data)
+	ew, ok := walker.(EnterExitWalker)
+	if ok {
+		err = ew.Enter(WalkLoc)
+	}
+
+	if err == nil {
+		err = walk(v, walker)
+	}
+
+	if ok && err == nil {
+		err = ew.Exit(WalkLoc)
+	}
+
+	return
+}
+
+func walk(v reflect.Value, w interface{}) (err error) {
+	// Determine if we're receiving a pointer and if so notify the walker.
+	// The logic here is convoluted but very important (tests will fail if
+	// almost any part is changed). I will try to explain here.
+	//
+	// First, we check if the value is an interface, if so, we really need
+	// to check the interface's VALUE to see whether it is a pointer.
+	//
+	// Check whether the value is then a pointer. If so, then set pointer
+	// to true to notify the user.
+	//
+	// If we still have a pointer or an interface after the indirections, then
+	// we unwrap another level
+	//
+	// At this time, we also set "v" to be the dereferenced value. This is
+	// because once we've unwrapped the pointer we want to use that value.
+	pointer := false
+	pointerV := v
+
+	for {
+		if pointerV.Kind() == reflect.Interface {
+			if iw, ok := w.(InterfaceWalker); ok {
+				if err = iw.Interface(pointerV); err != nil {
+					return
+				}
+			}
+
+			pointerV = pointerV.Elem()
+		}
+
+		if pointerV.Kind() == reflect.Ptr {
+			if pw, ok := w.(PointerValueWalker); ok {
+				if err = pw.Pointer(pointerV); err != nil {
+					if err == SkipEntry {
+						// Skip the rest of this entry but clear the error
+						return nil
+					}
+
+					return
+				}
+			}
+
+			pointer = true
+			v = reflect.Indirect(pointerV)
+		}
+		if pw, ok := w.(PointerWalker); ok {
+			if err = pw.PointerEnter(pointer); err != nil {
+				return
+			}
+
+			defer func(pointer bool) {
+				if err != nil {
+					return
+				}
+
+				err = pw.PointerExit(pointer)
+			}(pointer)
+		}
+
+		if pointer {
+			pointerV = v
+		}
+		pointer = false
+
+		// If we still have a pointer or interface we have to indirect another level.
+		switch pointerV.Kind() {
+		case reflect.Ptr, reflect.Interface:
+			continue
+		}
+		break
+	}
+
+	// We preserve the original value here because if it is an interface
+	// type, we want to pass that directly into the walkPrimitive, so that
+	// we can set it.
+	originalV := v
+	if v.Kind() == reflect.Interface {
+		v = v.Elem()
+	}
+
+	k := v.Kind()
+	if k >= reflect.Int && k <= reflect.Complex128 {
+		k = reflect.Int
+	}
+
+	switch k {
+	// Primitives
+	case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
+		err = walkPrimitive(originalV, w)
+		return
+	case reflect.Map:
+		err = walkMap(v, w)
+		return
+	case reflect.Slice:
+		err = walkSlice(v, w)
+		return
+	case reflect.Struct:
+		err = walkStruct(v, w)
+		return
+	case reflect.Array:
+		err = walkArray(v, w)
+		return
+	default:
+		panic("unsupported type: " + k.String())
+	}
+}
+
+func walkMap(v reflect.Value, w interface{}) error {
+	ew, ewok := w.(EnterExitWalker)
+	if ewok {
+		ew.Enter(Map)
+	}
+
+	if mw, ok := w.(MapWalker); ok {
+		if err := mw.Map(v); err != nil {
+			return err
+		}
+	}
+
+	for _, k := range v.MapKeys() {
+		kv := v.MapIndex(k)
+
+		if mw, ok := w.(MapWalker); ok {
+			if err := mw.MapElem(v, k, kv); err != nil {
+				return err
+			}
+		}
+
+		ew, ok := w.(EnterExitWalker)
+		if ok {
+			ew.Enter(MapKey)
+		}
+
+		if err := walk(k, w); err != nil {
+			return err
+		}
+
+		if ok {
+			ew.Exit(MapKey)
+			ew.Enter(MapValue)
+		}
+
+		// get the map value again as it may have changed in the MapElem call
+		if err := walk(v.MapIndex(k), w); err != nil {
+			return err
+		}
+
+		if ok {
+			ew.Exit(MapValue)
+		}
+	}
+
+	if ewok {
+		ew.Exit(Map)
+	}
+
+	return nil
+}
+
+func walkPrimitive(v reflect.Value, w interface{}) error {
+	if pw, ok := w.(PrimitiveWalker); ok {
+		return pw.Primitive(v)
+	}
+
+	return nil
+}
+
+func walkSlice(v reflect.Value, w interface{}) (err error) {
+	ew, ok := w.(EnterExitWalker)
+	if ok {
+		ew.Enter(Slice)
+	}
+
+	if sw, ok := w.(SliceWalker); ok {
+		if err := sw.Slice(v); err != nil {
+			return err
+		}
+	}
+
+	for i := 0; i < v.Len(); i++ {
+		elem := v.Index(i)
+
+		if sw, ok := w.(SliceWalker); ok {
+			if err := sw.SliceElem(i, elem); err != nil {
+				return err
+			}
+		}
+
+		ew, ok := w.(EnterExitWalker)
+		if ok {
+			ew.Enter(SliceElem)
+		}
+
+		if err := walk(elem, w); err != nil {
+			return err
+		}
+
+		if ok {
+			ew.Exit(SliceElem)
+		}
+	}
+
+	ew, ok = w.(EnterExitWalker)
+	if ok {
+		ew.Exit(Slice)
+	}
+
+	return nil
+}
+
+func walkArray(v reflect.Value, w interface{}) (err error) {
+	ew, ok := w.(EnterExitWalker)
+	if ok {
+		ew.Enter(Array)
+	}
+
+	if aw, ok := w.(ArrayWalker); ok {
+		if err := aw.Array(v); err != nil {
+			return err
+		}
+	}
+
+	for i := 0; i < v.Len(); i++ {
+		elem := v.Index(i)
+
+		if aw, ok := w.(ArrayWalker); ok {
+			if err := aw.ArrayElem(i, elem); err != nil {
+				return err
+			}
+		}
+
+		ew, ok := w.(EnterExitWalker)
+		if ok {
+			ew.Enter(ArrayElem)
+		}
+
+		if err := walk(elem, w); err != nil {
+			return err
+		}
+
+		if ok {
+			ew.Exit(ArrayElem)
+		}
+	}
+
+	ew, ok = w.(EnterExitWalker)
+	if ok {
+		ew.Exit(Array)
+	}
+
+	return nil
+}
+
+func walkStruct(v reflect.Value, w interface{}) (err error) {
+	ew, ewok := w.(EnterExitWalker)
+	if ewok {
+		ew.Enter(Struct)
+	}
+
+	skip := false
+	if sw, ok := w.(StructWalker); ok {
+		err = sw.Struct(v)
+		if err == SkipEntry {
+			skip = true
+			err = nil
+		}
+		if err != nil {
+			return
+		}
+	}
+
+	if !skip {
+		vt := v.Type()
+		for i := 0; i < vt.NumField(); i++ {
+			sf := vt.Field(i)
+			f := v.FieldByIndex([]int{i})
+
+			if sw, ok := w.(StructWalker); ok {
+				err = sw.StructField(sf, f)
+
+				// SkipEntry just pretends this field doesn't even exist
+				if err == SkipEntry {
+					continue
+				}
+
+				if err != nil {
+					return
+				}
+			}
+
+			ew, ok := w.(EnterExitWalker)
+			if ok {
+				ew.Enter(StructField)
+			}
+
+			err = walk(f, w)
+			if err != nil {
+				return
+			}
+
+			if ok {
+				ew.Exit(StructField)
+			}
+		}
+	}
+
+	if ewok {
+		ew.Exit(Struct)
+	}
+
+	return nil
+}

+ 10 - 0
vendor/modules.txt

@@ -611,9 +611,15 @@ github.com/miekg/dns
 # github.com/mistifyio/go-zfs/v3 v3.0.1
 # github.com/mistifyio/go-zfs/v3 v3.0.1
 ## explicit; go 1.14
 ## explicit; go 1.14
 github.com/mistifyio/go-zfs/v3
 github.com/mistifyio/go-zfs/v3
+# github.com/mitchellh/copystructure v1.2.0
+## explicit; go 1.15
+github.com/mitchellh/copystructure
 # github.com/mitchellh/hashstructure/v2 v2.0.2
 # github.com/mitchellh/hashstructure/v2 v2.0.2
 ## explicit; go 1.14
 ## explicit; go 1.14
 github.com/mitchellh/hashstructure/v2
 github.com/mitchellh/hashstructure/v2
+# github.com/mitchellh/reflectwalk v1.0.2
+## explicit
+github.com/mitchellh/reflectwalk
 # github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f
 # github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f
 ## explicit; go 1.18
 ## explicit; go 1.18
 github.com/moby/buildkit/api/services/control
 github.com/moby/buildkit/api/services/control
@@ -864,6 +870,10 @@ github.com/moby/term/windows
 # github.com/morikuni/aec v1.0.0
 # github.com/morikuni/aec v1.0.0
 ## explicit
 ## explicit
 github.com/morikuni/aec
 github.com/morikuni/aec
+# github.com/onsi/ginkgo/v2 v2.1.4
+## explicit; go 1.18
+# github.com/onsi/gomega v1.20.1
+## explicit; go 1.18
 # github.com/opencontainers/go-digest v1.0.0
 # github.com/opencontainers/go-digest v1.0.0
 ## explicit; go 1.13
 ## explicit; go 1.13
 github.com/opencontainers/go-digest
 github.com/opencontainers/go-digest