Browse code

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 authored on 2022/08/18 06:13:49
Showing 62 changed files
... ...
@@ -268,7 +268,7 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
268 268
 	// Restart all autostart containers which has a swarm endpoint
269 269
 	// and is not yet running now that we have successfully
270 270
 	// initialized the cluster.
271
-	d.RestartSwarmContainers()
271
+	d.RestartSwarmContainers(cli.Config)
272 272
 
273 273
 	logrus.Info("Daemon has completed initialization")
274 274
 
... ...
@@ -371,7 +371,7 @@ func newRouterOptions(ctx context.Context, config *config.Config, d *daemon.Daem
371 371
 		DefaultCgroupParent: cgroupParent,
372 372
 		RegistryHosts:       d.RegistryHosts,
373 373
 		BuilderConfig:       config.Builder,
374
-		Rootless:            d.Rootless(),
374
+		Rootless:            daemon.Rootless(config),
375 375
 		IdentityMapping:     d.IdentityMapping(),
376 376
 		DNSConfig:           config.DNSConfig,
377 377
 		ApparmorProfile:     daemon.DefaultApparmorProfile(),
... ...
@@ -9,7 +9,6 @@ import (
9 9
 	"os"
10 10
 	"path/filepath"
11 11
 	"strings"
12
-	"sync"
13 12
 
14 13
 	"golang.org/x/text/encoding"
15 14
 	"golang.org/x/text/encoding/unicode"
... ...
@@ -227,7 +226,6 @@ type CommonConfig struct {
227 227
 	NetworkConfig
228 228
 	registry.ServiceOptions
229 229
 
230
-	sync.Mutex
231 230
 	// FIXME(vdemeester) This part is not that clear and is mainly dependent on cli flags
232 231
 	// It should probably be handled outside this package.
233 232
 	ValuesSet map[string]interface{} `json:"-"`
... ...
@@ -650,11 +648,11 @@ func Validate(config *Config) error {
650 650
 		return err
651 651
 	}
652 652
 
653
-	if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" {
654
-		if !builtinRuntimes[defaultRuntime] {
653
+	if config.DefaultRuntime != "" {
654
+		if !builtinRuntimes[config.DefaultRuntime] {
655 655
 			runtimes := config.GetAllRuntimes()
656
-			if _, ok := runtimes[defaultRuntime]; !ok && !IsPermissibleC8dRuntimeName(defaultRuntime) {
657
-				return errors.Errorf("specified default runtime '%s' does not exist", defaultRuntime)
656
+			if _, ok := runtimes[config.DefaultRuntime]; !ok && !IsPermissibleC8dRuntimeName(config.DefaultRuntime) {
657
+				return fmt.Errorf("specified default runtime '%s' does not exist", config.DefaultRuntime)
658 658
 			}
659 659
 		}
660 660
 	}
... ...
@@ -669,15 +667,6 @@ func Validate(config *Config) error {
669 669
 	return config.ValidatePlatformConfig()
670 670
 }
671 671
 
672
-// GetDefaultRuntimeName returns the current default runtime
673
-func (conf *Config) GetDefaultRuntimeName() string {
674
-	conf.Lock()
675
-	rt := conf.DefaultRuntime
676
-	conf.Unlock()
677
-
678
-	return rt
679
-}
680
-
681 672
 // MaskCredentials masks credentials that are in an URL.
682 673
 func MaskCredentials(rawURL string) string {
683 674
 	parsedURL, err := url.Parse(rawURL)
... ...
@@ -84,8 +84,6 @@ type Config struct {
84 84
 // GetRuntime returns the runtime path and arguments for a given
85 85
 // runtime name
86 86
 func (conf *Config) GetRuntime(name string) *types.Runtime {
87
-	conf.Lock()
88
-	defer conf.Unlock()
89 87
 	if rt, ok := conf.Runtimes[name]; ok {
90 88
 		return &rt
91 89
 	}
... ...
@@ -94,10 +92,7 @@ func (conf *Config) GetRuntime(name string) *types.Runtime {
94 94
 
95 95
 // GetAllRuntimes returns a copy of the runtimes map
96 96
 func (conf *Config) GetAllRuntimes() map[string]types.Runtime {
97
-	conf.Lock()
98
-	rts := conf.Runtimes
99
-	conf.Unlock()
100
-	return rts
97
+	return conf.Runtimes
101 98
 }
102 99
 
103 100
 // GetExecRoot returns the user configured Exec-root
... ...
@@ -107,8 +102,6 @@ func (conf *Config) GetExecRoot() string {
107 107
 
108 108
 // GetInitPath returns the configured docker-init path
109 109
 func (conf *Config) GetInitPath() string {
110
-	conf.Lock()
111
-	defer conf.Unlock()
112 110
 	if conf.InitPath != "" {
113 111
 		return conf.InitPath
114 112
 	}
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	containertypes "github.com/docker/docker/api/types/container"
11 11
 	"github.com/docker/docker/api/types/strslice"
12 12
 	"github.com/docker/docker/container"
13
+	"github.com/docker/docker/daemon/config"
13 14
 	"github.com/docker/docker/daemon/network"
14 15
 	"github.com/docker/docker/errdefs"
15 16
 	"github.com/docker/docker/image"
... ...
@@ -206,10 +207,10 @@ func (daemon *Daemon) generateHostname(id string, config *containertypes.Config)
206 206
 	}
207 207
 }
208 208
 
209
-func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error {
209
+func (daemon *Daemon) setSecurityOptions(cfg *config.Config, container *container.Container, hostConfig *containertypes.HostConfig) error {
210 210
 	container.Lock()
211 211
 	defer container.Unlock()
212
-	return daemon.parseSecurityOpt(&container.SecurityOptions, hostConfig)
212
+	return daemon.parseSecurityOpt(cfg, &container.SecurityOptions, hostConfig)
213 213
 }
214 214
 
215 215
 func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error {
... ...
@@ -234,7 +235,7 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *
234 234
 
235 235
 // verifyContainerSettings performs validation of the hostconfig and config
236 236
 // structures.
237
-func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) (warnings []string, err error) {
237
+func (daemon *Daemon) verifyContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) (warnings []string, err error) {
238 238
 	// First perform verification of settings common across all platforms.
239 239
 	if err = validateContainerConfig(config); err != nil {
240 240
 		return warnings, err
... ...
@@ -244,7 +245,7 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon
244 244
 	}
245 245
 
246 246
 	// Now do platform-specific verification
247
-	warnings, err = verifyPlatformContainerSettings(daemon, hostConfig, update)
247
+	warnings, err = verifyPlatformContainerSettings(daemon, daemonCfg, hostConfig, update)
248 248
 	for _, w := range warnings {
249 249
 		logrus.Warn(w)
250 250
 	}
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	containertypes "github.com/docker/docker/api/types/container"
13 13
 	networktypes "github.com/docker/docker/api/types/network"
14 14
 	"github.com/docker/docker/container"
15
+	"github.com/docker/docker/daemon/config"
15 16
 	"github.com/docker/docker/daemon/network"
16 17
 	"github.com/docker/docker/errdefs"
17 18
 	"github.com/docker/docker/libnetwork"
... ...
@@ -26,19 +27,19 @@ import (
26 26
 	"github.com/sirupsen/logrus"
27 27
 )
28 28
 
29
-func (daemon *Daemon) getDNSSearchSettings(container *container.Container) []string {
29
+func (daemon *Daemon) getDNSSearchSettings(cfg *config.Config, container *container.Container) []string {
30 30
 	if len(container.HostConfig.DNSSearch) > 0 {
31 31
 		return container.HostConfig.DNSSearch
32 32
 	}
33 33
 
34
-	if len(daemon.configStore.DNSSearch) > 0 {
35
-		return daemon.configStore.DNSSearch
34
+	if len(cfg.DNSSearch) > 0 {
35
+		return cfg.DNSSearch
36 36
 	}
37 37
 
38 38
 	return nil
39 39
 }
40 40
 
41
-func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]libnetwork.SandboxOption, error) {
41
+func (daemon *Daemon) buildSandboxOptions(cfg *config.Config, container *container.Container) ([]libnetwork.SandboxOption, error) {
42 42
 	var (
43 43
 		sboxOptions []libnetwork.SandboxOption
44 44
 		err         error
... ...
@@ -61,21 +62,21 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
61 61
 		sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey())
62 62
 	}
63 63
 
64
-	if err = daemon.setupPathsAndSandboxOptions(container, &sboxOptions); err != nil {
64
+	if err = daemon.setupPathsAndSandboxOptions(container, cfg, &sboxOptions); err != nil {
65 65
 		return nil, err
66 66
 	}
67 67
 
68 68
 	if len(container.HostConfig.DNS) > 0 {
69 69
 		dns = container.HostConfig.DNS
70
-	} else if len(daemon.configStore.DNS) > 0 {
71
-		dns = daemon.configStore.DNS
70
+	} else if len(cfg.DNS) > 0 {
71
+		dns = cfg.DNS
72 72
 	}
73 73
 
74 74
 	for _, d := range dns {
75 75
 		sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d))
76 76
 	}
77 77
 
78
-	dnsSearch := daemon.getDNSSearchSettings(container)
78
+	dnsSearch := daemon.getDNSSearchSettings(cfg, container)
79 79
 
80 80
 	for _, ds := range dnsSearch {
81 81
 		sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds))
... ...
@@ -83,8 +84,8 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
83 83
 
84 84
 	if len(container.HostConfig.DNSOptions) > 0 {
85 85
 		dnsOptions = container.HostConfig.DNSOptions
86
-	} else if len(daemon.configStore.DNSOptions) > 0 {
87
-		dnsOptions = daemon.configStore.DNSOptions
86
+	} else if len(cfg.DNSOptions) > 0 {
87
+		dnsOptions = cfg.DNSOptions
88 88
 	}
89 89
 
90 90
 	for _, ds := range dnsOptions {
... ...
@@ -112,7 +113,7 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
112 112
 		// value with the IP address stored in the daemon level HostGatewayIP
113 113
 		// config variable
114 114
 		if ip == opts.HostGatewayName {
115
-			gateway := daemon.configStore.HostGatewayIP.String()
115
+			gateway := cfg.HostGatewayIP.String()
116 116
 			if gateway == "" {
117 117
 				return nil, fmt.Errorf("unable to derive the IP value for host-gateway")
118 118
 			}
... ...
@@ -218,7 +219,7 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
218 218
 	}
219 219
 
220 220
 	for alias, parent := range daemon.parents(container) {
221
-		if daemon.configStore.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() {
221
+		if cfg.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() {
222 222
 			continue
223 223
 		}
224 224
 
... ...
@@ -291,13 +292,13 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li
291 291
 	return nil
292 292
 }
293 293
 
294
-func (daemon *Daemon) updateEndpointNetworkSettings(container *container.Container, n libnetwork.Network, ep *libnetwork.Endpoint) error {
294
+func (daemon *Daemon) updateEndpointNetworkSettings(cfg *config.Config, container *container.Container, n libnetwork.Network, ep *libnetwork.Endpoint) error {
295 295
 	if err := buildEndpointInfo(container.NetworkSettings, n, ep); err != nil {
296 296
 		return err
297 297
 	}
298 298
 
299 299
 	if container.HostConfig.NetworkMode == runconfig.DefaultDaemonNetworkMode() {
300
-		container.NetworkSettings.Bridge = daemon.configStore.BridgeConfig.Iface
300
+		container.NetworkSettings.Bridge = cfg.BridgeConfig.Iface
301 301
 	}
302 302
 
303 303
 	return nil
... ...
@@ -305,7 +306,7 @@ func (daemon *Daemon) updateEndpointNetworkSettings(container *container.Contain
305 305
 
306 306
 // UpdateNetwork is used to update the container's network (e.g. when linked containers
307 307
 // get removed/unlinked).
308
-func (daemon *Daemon) updateNetwork(container *container.Container) error {
308
+func (daemon *Daemon) updateNetwork(cfg *config.Config, container *container.Container) error {
309 309
 	var (
310 310
 		start = time.Now()
311 311
 		ctrl  = daemon.netController
... ...
@@ -335,7 +336,7 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
335 335
 		return nil
336 336
 	}
337 337
 
338
-	sbOptions, err := daemon.buildSandboxOptions(container)
338
+	sbOptions, err := daemon.buildSandboxOptions(cfg, container)
339 339
 	if err != nil {
340 340
 		return fmt.Errorf("Update network failed: %v", err)
341 341
 	}
... ...
@@ -519,7 +520,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
519 519
 	}
520 520
 }
521 521
 
522
-func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr error) {
522
+func (daemon *Daemon) allocateNetwork(cfg *config.Config, container *container.Container) (retErr error) {
523 523
 	if daemon.netController == nil {
524 524
 		return nil
525 525
 	}
... ...
@@ -552,7 +553,7 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr er
552 552
 	defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
553 553
 	if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok {
554 554
 		cleanOperationalData(nConf)
555
-		if err := daemon.connectToNetwork(container, defaultNetName, nConf.EndpointSettings, updateSettings); err != nil {
555
+		if err := daemon.connectToNetwork(cfg, container, defaultNetName, nConf.EndpointSettings, updateSettings); err != nil {
556 556
 			return err
557 557
 		}
558 558
 	}
... ...
@@ -569,7 +570,7 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr er
569 569
 
570 570
 	for netName, epConf := range networks {
571 571
 		cleanOperationalData(epConf)
572
-		if err := daemon.connectToNetwork(container, netName, epConf.EndpointSettings, updateSettings); err != nil {
572
+		if err := daemon.connectToNetwork(cfg, container, netName, epConf.EndpointSettings, updateSettings); err != nil {
573 573
 			return err
574 574
 		}
575 575
 	}
... ...
@@ -578,7 +579,7 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr er
578 578
 	// create its network sandbox now if not present
579 579
 	if len(networks) == 0 {
580 580
 		if nil == daemon.getNetworkSandbox(container) {
581
-			sbOptions, err := daemon.buildSandboxOptions(container)
581
+			sbOptions, err := daemon.buildSandboxOptions(cfg, container)
582 582
 			if err != nil {
583 583
 				return err
584 584
 			}
... ...
@@ -722,13 +723,13 @@ func (daemon *Daemon) updateNetworkConfig(container *container.Container, n libn
722 722
 	return nil
723 723
 }
724 724
 
725
-func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
725
+func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
726 726
 	start := time.Now()
727 727
 	if container.HostConfig.NetworkMode.IsContainer() {
728 728
 		return runconfig.ErrConflictSharedNetwork
729 729
 	}
730 730
 	if containertypes.NetworkMode(idOrName).IsBridge() &&
731
-		daemon.configStore.DisableBridge {
731
+		cfg.DisableBridge {
732 732
 		container.Config.NetworkDisabled = true
733 733
 		return nil
734 734
 	}
... ...
@@ -766,7 +767,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
766 766
 
767 767
 	controller := daemon.netController
768 768
 	sb := daemon.getNetworkSandbox(container)
769
-	createOptions, err := buildCreateEndpointOptions(container, n, endpointConfig, sb, daemon.configStore.DNS)
769
+	createOptions, err := buildCreateEndpointOptions(container, n, endpointConfig, sb, cfg.DNS)
770 770
 	if err != nil {
771 771
 		return err
772 772
 	}
... ...
@@ -790,12 +791,12 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
790 790
 
791 791
 	delete(container.NetworkSettings.Networks, n.ID())
792 792
 
793
-	if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil {
793
+	if err := daemon.updateEndpointNetworkSettings(cfg, container, n, ep); err != nil {
794 794
 		return err
795 795
 	}
796 796
 
797 797
 	if sb == nil {
798
-		sbOptions, err := daemon.buildSandboxOptions(container)
798
+		sbOptions, err := daemon.buildSandboxOptions(cfg, container)
799 799
 		if err != nil {
800 800
 			return err
801 801
 		}
... ...
@@ -946,7 +947,7 @@ func (daemon *Daemon) tryDetachContainerFromClusterNetwork(network libnetwork.Ne
946 946
 	daemon.LogNetworkEventWithAttributes(network, "disconnect", attributes)
947 947
 }
948 948
 
949
-func (daemon *Daemon) initializeNetworking(container *container.Container) error {
949
+func (daemon *Daemon) initializeNetworking(cfg *config.Config, container *container.Container) error {
950 950
 	var err error
951 951
 
952 952
 	if container.HostConfig.NetworkMode.IsContainer() {
... ...
@@ -975,7 +976,7 @@ func (daemon *Daemon) initializeNetworking(container *container.Container) error
975 975
 		}
976 976
 	}
977 977
 
978
-	if err := daemon.allocateNetwork(container); err != nil {
978
+	if err := daemon.allocateNetwork(cfg, container); err != nil {
979 979
 		return err
980 980
 	}
981 981
 
... ...
@@ -1074,7 +1075,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
1074 1074
 			}
1075 1075
 		}
1076 1076
 	} else {
1077
-		if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil {
1077
+		if err := daemon.connectToNetwork(daemon.config(), container, idOrName, endpointConfig, true); err != nil {
1078 1078
 			return err
1079 1079
 		}
1080 1080
 	}
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"syscall"
11 11
 
12 12
 	"github.com/docker/docker/container"
13
+	"github.com/docker/docker/daemon/config"
13 14
 	"github.com/docker/docker/daemon/links"
14 15
 	"github.com/docker/docker/errdefs"
15 16
 	"github.com/docker/docker/libnetwork"
... ...
@@ -380,7 +381,7 @@ func serviceDiscoveryOnDefaultNetwork() bool {
380 380
 	return false
381 381
 }
382 382
 
383
-func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
383
+func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
384 384
 	var err error
385 385
 
386 386
 	// 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
427 427
 		// Copy the host's resolv.conf for the container (/run/systemd/resolve/resolv.conf or /etc/resolv.conf)
428 428
 		*sboxOptions = append(
429 429
 			*sboxOptions,
430
-			libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf()),
430
+			libnetwork.OptionOriginResolvConfPath(cfg.GetResolvConf()),
431 431
 		)
432 432
 	}
433 433
 
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"os"
6 6
 
7 7
 	"github.com/docker/docker/container"
8
+	"github.com/docker/docker/daemon/config"
8 9
 	"github.com/docker/docker/libnetwork"
9 10
 	"github.com/docker/docker/pkg/system"
10 11
 	"github.com/pkg/errors"
... ...
@@ -161,7 +162,7 @@ func serviceDiscoveryOnDefaultNetwork() bool {
161 161
 	return true
162 162
 }
163 163
 
164
-func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
164
+func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
165 165
 	return nil
166 166
 }
167 167
 
... ...
@@ -33,8 +33,9 @@ func TestContainerWarningHostAndPublishPorts(t *testing.T) {
33 33
 		}
34 34
 		cs := &config.Config{}
35 35
 		configureRuntimes(cs)
36
-		d := &Daemon{configStore: cs}
37
-		wrns, err := d.verifyContainerSettings(hostConfig, &containertypes.Config{}, false)
36
+		d := &Daemon{}
37
+		d.configStore.Store(cs)
38
+		wrns, err := d.verifyContainerSettings(cs, hostConfig, &containertypes.Config{}, false)
38 39
 		assert.NilError(t, err)
39 40
 		assert.DeepEqual(t, tc.warnings, wrns)
40 41
 	}
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	imagetypes "github.com/docker/docker/api/types/image"
15 15
 	networktypes "github.com/docker/docker/api/types/network"
16 16
 	"github.com/docker/docker/container"
17
+	"github.com/docker/docker/daemon/config"
17 18
 	"github.com/docker/docker/daemon/images"
18 19
 	"github.com/docker/docker/errdefs"
19 20
 	"github.com/docker/docker/image"
... ...
@@ -34,7 +35,7 @@ type createOpts struct {
34 34
 
35 35
 // CreateManagedContainer creates a container that is managed by a Service
36 36
 func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
37
-	return daemon.containerCreate(ctx, createOpts{
37
+	return daemon.containerCreate(ctx, daemon.config(), createOpts{
38 38
 		params:  params,
39 39
 		managed: true,
40 40
 	})
... ...
@@ -42,7 +43,7 @@ func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.C
42 42
 
43 43
 // ContainerCreate creates a regular container
44 44
 func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
45
-	return daemon.containerCreate(ctx, createOpts{
45
+	return daemon.containerCreate(ctx, daemon.config(), createOpts{
46 46
 		params: params,
47 47
 	})
48 48
 }
... ...
@@ -50,19 +51,19 @@ func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.Containe
50 50
 // ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case
51 51
 // and ensures that we do not take the images ArgsEscaped
52 52
 func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
53
-	return daemon.containerCreate(ctx, createOpts{
53
+	return daemon.containerCreate(ctx, daemon.config(), createOpts{
54 54
 		params:                  params,
55 55
 		ignoreImagesArgsEscaped: true,
56 56
 	})
57 57
 }
58 58
 
59
-func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (containertypes.CreateResponse, error) {
59
+func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *config.Config, opts createOpts) (containertypes.CreateResponse, error) {
60 60
 	start := time.Now()
61 61
 	if opts.params.Config == nil {
62 62
 		return containertypes.CreateResponse{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container"))
63 63
 	}
64 64
 
65
-	warnings, err := daemon.verifyContainerSettings(opts.params.HostConfig, opts.params.Config, false)
65
+	warnings, err := daemon.verifyContainerSettings(daemonCfg, opts.params.HostConfig, opts.params.Config, false)
66 66
 	if err != nil {
67 67
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
68 68
 	}
... ...
@@ -94,12 +95,12 @@ func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (con
94 94
 	if opts.params.HostConfig == nil {
95 95
 		opts.params.HostConfig = &containertypes.HostConfig{}
96 96
 	}
97
-	err = daemon.adaptContainerSettings(opts.params.HostConfig, opts.params.AdjustCPUShares)
97
+	err = daemon.adaptContainerSettings(daemonCfg, opts.params.HostConfig, opts.params.AdjustCPUShares)
98 98
 	if err != nil {
99 99
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
100 100
 	}
101 101
 
102
-	ctr, err := daemon.create(ctx, opts)
102
+	ctr, err := daemon.create(ctx, daemonCfg, opts)
103 103
 	if err != nil {
104 104
 		return containertypes.CreateResponse{Warnings: warnings}, err
105 105
 	}
... ...
@@ -113,7 +114,7 @@ func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (con
113 113
 }
114 114
 
115 115
 // Create creates a new container from the given configuration with a given name.
116
-func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *container.Container, retErr error) {
116
+func (daemon *Daemon) create(ctx context.Context, daemonCfg *config.Config, opts createOpts) (retC *container.Container, retErr error) {
117 117
 	var (
118 118
 		ctr         *container.Container
119 119
 		img         *image.Image
... ...
@@ -175,7 +176,7 @@ func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *contai
175 175
 		}
176 176
 	}()
177 177
 
178
-	if err := daemon.setSecurityOptions(ctr, opts.params.HostConfig); err != nil {
178
+	if err := daemon.setSecurityOptions(daemonCfg, ctr, opts.params.HostConfig); err != nil {
179 179
 		return nil, err
180 180
 	}
181 181
 
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	"runtime"
17 17
 	"strings"
18 18
 	"sync"
19
+	"sync/atomic"
19 20
 	"time"
20 21
 
21 22
 	"github.com/containerd/containerd"
... ...
@@ -84,7 +85,8 @@ type Daemon struct {
84 84
 	containersReplica     *container.ViewDB
85 85
 	execCommands          *container.ExecStore
86 86
 	imageService          ImageService
87
-	configStore           *config.Config
87
+	configStore           atomic.Pointer[config.Config]
88
+	configReload          sync.Mutex
88 89
 	statsCollector        *stats.Collector
89 90
 	defaultLogConfig      containertypes.LogConfig
90 91
 	registryService       *registry.Service
... ...
@@ -148,20 +150,31 @@ func (daemon *Daemon) StoreHosts(hosts []string) {
148 148
 	}
149 149
 }
150 150
 
151
+// config returns an immutable snapshot of the current daemon configuration.
152
+// Multiple calls to this function will return the same pointer until the
153
+// configuration is reloaded so callers must take care not to modify the
154
+// returned value.
155
+//
156
+// To ensure that the configuration used remains consistent throughout the
157
+// lifetime of an operation, the configuration pointer should be passed down the
158
+// call stack, like one would a [context.Context] value. Only the entrypoints
159
+// for operations, the outermost functions, should call this function.
160
+func (daemon *Daemon) config() *config.Config {
161
+	cfg := daemon.configStore.Load()
162
+	if cfg == nil {
163
+		return &config.Config{}
164
+	}
165
+	return cfg
166
+}
167
+
151 168
 // HasExperimental returns whether the experimental features of the daemon are enabled or not
152 169
 func (daemon *Daemon) HasExperimental() bool {
153
-	return daemon.configStore != nil && daemon.configStore.Experimental
170
+	return daemon.config().Experimental
154 171
 }
155 172
 
156 173
 // Features returns the features map from configStore
157 174
 func (daemon *Daemon) Features() map[string]bool {
158
-	daemon.configStore.Lock()
159
-	defer daemon.configStore.Unlock()
160
-	f := make(map[string]bool, len(daemon.configStore.Features))
161
-	for k, v := range daemon.configStore.Features {
162
-		f[k] = v
163
-	}
164
-	return f
175
+	return daemon.config().Features
165 176
 }
166 177
 
167 178
 // UsesSnapshotter returns true if feature flag to use containerd snapshotter is enabled
... ...
@@ -172,17 +185,14 @@ func (daemon *Daemon) UsesSnapshotter() bool {
172 172
 // RegistryHosts returns the registry hosts configuration for the host component
173 173
 // of a distribution image reference.
174 174
 func (daemon *Daemon) RegistryHosts(host string) ([]docker.RegistryHost, error) {
175
-	daemon.configStore.Lock()
176
-	serviceOpts := daemon.configStore.ServiceOptions
177
-	daemon.configStore.Unlock()
178
-
179 175
 	var (
176
+		conf        = daemon.config()
180 177
 		registryKey = "docker.io"
181
-		mirrors     = make([]string, len(serviceOpts.Mirrors))
178
+		mirrors     = make([]string, len(conf.Mirrors))
182 179
 		m           = map[string]resolverconfig.RegistryConfig{}
183 180
 	)
184 181
 	// must trim "https://" or "http://" prefix
185
-	for i, v := range serviceOpts.Mirrors {
182
+	for i, v := range conf.Mirrors {
186 183
 		if uri, err := url.Parse(v); err == nil {
187 184
 			v = uri.Host
188 185
 		}
... ...
@@ -191,7 +201,7 @@ func (daemon *Daemon) RegistryHosts(host string) ([]docker.RegistryHost, error)
191 191
 	// set mirrors for default registry
192 192
 	m[registryKey] = resolverconfig.RegistryConfig{Mirrors: mirrors}
193 193
 
194
-	for _, v := range serviceOpts.InsecureRegistries {
194
+	for _, v := range conf.InsecureRegistries {
195 195
 		u, err := url.Parse(v)
196 196
 		if err != nil && !strings.HasPrefix(v, "http://") && !strings.HasPrefix(v, "https://") {
197 197
 			originalErr := err
... ...
@@ -237,7 +247,7 @@ type layerAccessor interface {
237 237
 	GetLayerByID(cid string) (layer.RWLayer, error)
238 238
 }
239 239
 
240
-func (daemon *Daemon) restore() error {
240
+func (daemon *Daemon) restore(cfg *config.Config) error {
241 241
 	var mapLock sync.Mutex
242 242
 	containers := make(map[string]*container.Container)
243 243
 
... ...
@@ -377,7 +387,7 @@ func (daemon *Daemon) restore() error {
377 377
 							logger(c).WithError(err).Error("failed to delete task from containerd")
378 378
 							return
379 379
 						}
380
-					} else if !daemon.configStore.LiveRestoreEnabled {
380
+					} else if !cfg.LiveRestoreEnabled {
381 381
 						logger(c).Debug("shutting down container considered alive by containerd")
382 382
 						if err := daemon.shutdownContainer(c); err != nil && !errdefs.IsNotFound(err) {
383 383
 							log.WithError(err).Error("error shutting down container")
... ...
@@ -457,7 +467,7 @@ func (daemon *Daemon) restore() error {
457 457
 
458 458
 				c.ResetRestartManager(false)
459 459
 				if !c.HostConfig.NetworkMode.IsContainer() && c.IsRunning() {
460
-					options, err := daemon.buildSandboxOptions(c)
460
+					options, err := daemon.buildSandboxOptions(cfg, c)
461 461
 					if err != nil {
462 462
 						logger(c).WithError(err).Warn("failed to build sandbox option to restore container")
463 463
 					}
... ...
@@ -475,7 +485,7 @@ func (daemon *Daemon) restore() error {
475 475
 			// not initialized yet. We will start
476 476
 			// it after the cluster is
477 477
 			// initialized.
478
-			if daemon.configStore.AutoRestart && c.ShouldRestart() && !c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
478
+			if cfg.AutoRestart && c.ShouldRestart() && !c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
479 479
 				mapLock.Lock()
480 480
 				restartContainers[c] = make(chan struct{})
481 481
 				mapLock.Unlock()
... ...
@@ -513,7 +523,7 @@ func (daemon *Daemon) restore() error {
513 513
 	//
514 514
 	// Note that we cannot initialize the network controller earlier, as it
515 515
 	// needs to know if there's active sandboxes (running containers).
516
-	if err = daemon.initNetworkController(activeSandboxes); err != nil {
516
+	if err = daemon.initNetworkController(cfg, activeSandboxes); err != nil {
517 517
 		return fmt.Errorf("Error initializing network controller: %v", err)
518 518
 	}
519 519
 
... ...
@@ -560,7 +570,7 @@ func (daemon *Daemon) restore() error {
560 560
 			if err := daemon.prepareMountPoints(c); err != nil {
561 561
 				log.WithError(err).Error("failed to prepare mount points for container")
562 562
 			}
563
-			if err := daemon.containerStart(context.Background(), c, "", "", true); err != nil {
563
+			if err := daemon.containerStart(context.Background(), cfg, c, "", "", true); err != nil {
564 564
 				log.WithError(err).Error("failed to start container")
565 565
 			}
566 566
 			close(chNotify)
... ...
@@ -576,7 +586,7 @@ func (daemon *Daemon) restore() error {
576 576
 		go func(cid string) {
577 577
 			_ = sem.Acquire(context.Background(), 1)
578 578
 
579
-			if err := daemon.ContainerRm(cid, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
579
+			if err := daemon.containerRm(cfg, cid, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
580 580
 				logrus.WithField("container", cid).WithError(err).Error("failed to remove container")
581 581
 			}
582 582
 
... ...
@@ -624,7 +634,7 @@ func (daemon *Daemon) restore() error {
624 624
 
625 625
 // RestartSwarmContainers restarts any autostart container which has a
626 626
 // swarm endpoint.
627
-func (daemon *Daemon) RestartSwarmContainers() {
627
+func (daemon *Daemon) RestartSwarmContainers(cfg *config.Config) {
628 628
 	ctx := context.Background()
629 629
 
630 630
 	// parallelLimit is the maximum number of parallel startup jobs that we
... ...
@@ -642,7 +652,7 @@ func (daemon *Daemon) RestartSwarmContainers() {
642 642
 			// Autostart all the containers which has a
643 643
 			// swarm endpoint now that the cluster is
644 644
 			// initialized.
645
-			if daemon.configStore.AutoRestart && c.ShouldRestart() && c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
645
+			if cfg.AutoRestart && c.ShouldRestart() && c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore {
646 646
 				group.Add(1)
647 647
 				go func(c *container.Container) {
648 648
 					if err := sem.Acquire(ctx, 1); err != nil {
... ...
@@ -651,7 +661,7 @@ func (daemon *Daemon) RestartSwarmContainers() {
651 651
 						return
652 652
 					}
653 653
 
654
-					if err := daemon.containerStart(ctx, c, "", "", true); err != nil {
654
+					if err := daemon.containerStart(ctx, cfg, c, "", "", true); err != nil {
655 655
 						logrus.WithField("container", c.ID).WithError(err).Error("failed to start swarm container")
656 656
 					}
657 657
 
... ...
@@ -735,10 +745,7 @@ func (daemon *Daemon) setClusterProvider(clusterProvider cluster.Provider) {
735 735
 // IsSwarmCompatible verifies if the current daemon
736 736
 // configuration is compatible with the swarm mode
737 737
 func (daemon *Daemon) IsSwarmCompatible() error {
738
-	if daemon.configStore == nil {
739
-		return nil
740
-	}
741
-	return daemon.configStore.IsSwarmCompatible()
738
+	return daemon.config().IsSwarmCompatible()
742 739
 }
743 740
 
744 741
 // 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
800 800
 	}
801 801
 
802 802
 	d := &Daemon{
803
-		configStore: config,
804 803
 		PluginStore: pluginStore,
805 804
 		startupDone: make(chan struct{}),
806 805
 	}
806
+	d.configStore.Store(config)
807 807
 
808 808
 	// TEST_INTEGRATION_USE_SNAPSHOTTER is used for integration tests only.
809 809
 	if os.Getenv("TEST_INTEGRATION_USE_SNAPSHOTTER") != "" {
... ...
@@ -834,12 +841,12 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
834 834
 	}
835 835
 	d.setupDumpStackTrap(stackDumpDir)
836 836
 
837
-	if err := d.setupSeccompProfile(); err != nil {
837
+	if err := d.setupSeccompProfile(config); err != nil {
838 838
 		return nil, err
839 839
 	}
840 840
 
841 841
 	// Set the default isolation mode (only applicable on Windows)
842
-	if err := d.setDefaultIsolation(); err != nil {
842
+	if err := d.setDefaultIsolation(config); err != nil {
843 843
 		return nil, fmt.Errorf("error setting default isolation mode: %v", err)
844 844
 	}
845 845
 
... ...
@@ -881,7 +888,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
881 881
 	d.registryService = registryService
882 882
 	dlogger.RegisterPluginGetter(d.PluginStore)
883 883
 
884
-	metricsSockPath, err := d.listenMetricsSock()
884
+	metricsSockPath, err := d.listenMetricsSock(config)
885 885
 	if err != nil {
886 886
 		return nil, err
887 887
 	}
... ...
@@ -942,7 +949,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
942 942
 			shimOpts interface{}
943 943
 		)
944 944
 		if runtime.GOOS != "windows" {
945
-			shim, shimOpts, err = d.getRuntime(config.GetDefaultRuntimeName())
945
+			shim, shimOpts, err = d.getRuntime(config, config.DefaultRuntime)
946 946
 			if err != nil {
947 947
 				return nil, err
948 948
 			}
... ...
@@ -965,9 +972,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
965 965
 		return nil, errors.Wrap(err, "couldn't create plugin manager")
966 966
 	}
967 967
 
968
-	if err := d.setupDefaultLogConfig(); err != nil {
969
-		return nil, err
968
+	d.defaultLogConfig, err = defaultLogConfig(config)
969
+	if err != nil {
970
+		return nil, errors.Wrap(err, "failed to set log opts")
970 971
 	}
972
+	logrus.Debugf("Using default logging driver %s", d.defaultLogConfig.Type)
971 973
 
972 974
 	d.volumes, err = volumesservice.NewVolumeService(config.Root, d.PluginStore, rootIDs, d)
973 975
 	if err != nil {
... ...
@@ -982,7 +991,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
982 982
 	// at this point.
983 983
 	//
984 984
 	// TODO(thaJeztah) add a utility to only collect the CgroupDevicesEnabled information
985
-	if runtime.GOOS == "linux" && !userns.RunningInUserNS() && !getSysInfo(d).CgroupDevicesEnabled {
985
+	if runtime.GOOS == "linux" && !userns.RunningInUserNS() && !getSysInfo(config).CgroupDevicesEnabled {
986 986
 		return nil, errors.New("Devices cgroup isn't mounted")
987 987
 	}
988 988
 
... ...
@@ -1135,11 +1144,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
1135 1135
 
1136 1136
 	go d.execCommandGC()
1137 1137
 
1138
-	if err := d.initLibcontainerd(ctx); err != nil {
1138
+	if err := d.initLibcontainerd(ctx, config); err != nil {
1139 1139
 		return nil, err
1140 1140
 	}
1141 1141
 
1142
-	if err := d.restore(); err != nil {
1142
+	if err := d.restore(config); err != nil {
1143 1143
 		return nil, err
1144 1144
 	}
1145 1145
 	close(d.startupDone)
... ...
@@ -1201,7 +1210,11 @@ func (daemon *Daemon) shutdownContainer(c *container.Container) error {
1201 1201
 // A negative (-1) timeout means "indefinitely", which means that containers
1202 1202
 // are not forcibly killed, and the daemon shuts down after all containers exit.
1203 1203
 func (daemon *Daemon) ShutdownTimeout() int {
1204
-	shutdownTimeout := daemon.configStore.ShutdownTimeout
1204
+	return daemon.shutdownTimeout(daemon.config())
1205
+}
1206
+
1207
+func (daemon *Daemon) shutdownTimeout(cfg *config.Config) int {
1208
+	shutdownTimeout := cfg.ShutdownTimeout
1205 1209
 	if shutdownTimeout < 0 {
1206 1210
 		return -1
1207 1211
 	}
... ...
@@ -1228,7 +1241,8 @@ func (daemon *Daemon) Shutdown(ctx context.Context) error {
1228 1228
 	// Keep mounts and networking running on daemon shutdown if
1229 1229
 	// we are to keep containers running and restore them.
1230 1230
 
1231
-	if daemon.configStore.LiveRestoreEnabled && daemon.containers != nil {
1231
+	cfg := daemon.config()
1232
+	if cfg.LiveRestoreEnabled && daemon.containers != nil {
1232 1233
 		// check if there are any running containers, if none we should do some cleanup
1233 1234
 		if ls, err := daemon.Containers(ctx, &types.ContainerListOptions{}); len(ls) != 0 || err != nil {
1234 1235
 			// metrics plugins still need some cleanup
... ...
@@ -1238,8 +1252,8 @@ func (daemon *Daemon) Shutdown(ctx context.Context) error {
1238 1238
 	}
1239 1239
 
1240 1240
 	if daemon.containers != nil {
1241
-		logrus.Debugf("daemon configured with a %d seconds minimum shutdown timeout", daemon.configStore.ShutdownTimeout)
1242
-		logrus.Debugf("start clean shutdown of all containers with a %d seconds timeout...", daemon.ShutdownTimeout())
1241
+		logrus.Debugf("daemon configured with a %d seconds minimum shutdown timeout", cfg.ShutdownTimeout)
1242
+		logrus.Debugf("start clean shutdown of all containers with a %d seconds timeout...", daemon.shutdownTimeout(cfg))
1243 1243
 		daemon.containers.ApplyAll(func(c *container.Container) {
1244 1244
 			if !c.IsRunning() {
1245 1245
 				return
... ...
@@ -1293,7 +1307,7 @@ func (daemon *Daemon) Shutdown(ctx context.Context) error {
1293 1293
 		daemon.mdDB.Close()
1294 1294
 	}
1295 1295
 
1296
-	return daemon.cleanupMounts()
1296
+	return daemon.cleanupMounts(cfg)
1297 1297
 }
1298 1298
 
1299 1299
 // Mount sets container.BaseFS
... ...
@@ -1374,15 +1388,10 @@ func isBridgeNetworkDisabled(conf *config.Config) bool {
1374 1374
 	return conf.BridgeConfig.Iface == config.DisableNetworkBridge
1375 1375
 }
1376 1376
 
1377
-func (daemon *Daemon) networkOptions(pg plugingetter.PluginGetter, activeSandboxes map[string]interface{}) ([]nwconfig.Option, error) {
1378
-	options := []nwconfig.Option{}
1379
-	if daemon.configStore == nil {
1380
-		return options, nil
1381
-	}
1382
-	conf := daemon.configStore
1377
+func (daemon *Daemon) networkOptions(conf *config.Config, pg plugingetter.PluginGetter, activeSandboxes map[string]interface{}) ([]nwconfig.Option, error) {
1383 1378
 	dd := runconfig.DefaultDaemonNetworkMode()
1384 1379
 
1385
-	options = []nwconfig.Option{
1380
+	options := []nwconfig.Option{
1386 1381
 		nwconfig.OptionDataDir(conf.Root),
1387 1382
 		nwconfig.OptionExecRoot(conf.GetExecRoot()),
1388 1383
 		nwconfig.OptionDefaultDriver(string(dd)),
... ...
@@ -1514,7 +1523,7 @@ func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo {
1514 1514
 		// We check if sysInfo is not set here, to allow some test to
1515 1515
 		// override the actual sysInfo.
1516 1516
 		if daemon.sysInfo == nil {
1517
-			daemon.sysInfo = getSysInfo(daemon)
1517
+			daemon.sysInfo = getSysInfo(daemon.config())
1518 1518
 		}
1519 1519
 	})
1520 1520
 
... ...
@@ -76,7 +76,7 @@ func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, u
76 76
 }
77 77
 
78 78
 // cleanupMounts umounts used by container resources and the daemon root mount
79
-func (daemon *Daemon) cleanupMounts() error {
79
+func (daemon *Daemon) cleanupMounts(cfg *config.Config) error {
80 80
 	if err := daemon.cleanupMountsByID(""); err != nil {
81 81
 		return err
82 82
 	}
... ...
@@ -100,7 +100,7 @@ func (daemon *Daemon) cleanupMounts() error {
100 100
 		return nil
101 101
 	}
102 102
 
103
-	unmountFile := getUnmountOnShutdownPath(daemon.configStore)
103
+	unmountFile := getUnmountOnShutdownPath(cfg)
104 104
 	if _, err := os.Stat(unmountFile); err != nil {
105 105
 		return nil
106 106
 	}
... ...
@@ -239,14 +239,14 @@ func kernelSupportsRecursivelyReadOnly() error {
239 239
 	return kernelSupportsRROErr
240 240
 }
241 241
 
242
-func (daemon *Daemon) supportsRecursivelyReadOnly(runtime string) error {
242
+func supportsRecursivelyReadOnly(cfg *config.Config, runtime string) error {
243 243
 	if err := kernelSupportsRecursivelyReadOnly(); err != nil {
244 244
 		return fmt.Errorf("rro is not supported: %w (kernel is older than 5.12?)", err)
245 245
 	}
246 246
 	if runtime == "" {
247
-		runtime = daemon.configStore.GetDefaultRuntimeName()
247
+		runtime = cfg.DefaultRuntime
248 248
 	}
249
-	rt := daemon.configStore.GetRuntime(runtime)
249
+	rt := cfg.GetRuntime(runtime)
250 250
 	if rt.Features == nil {
251 251
 		return fmt.Errorf("rro is not supported by runtime %q: OCI features struct is not available", runtime)
252 252
 	}
... ...
@@ -178,7 +178,7 @@ func TestNotCleanupMounts(t *testing.T) {
178 178
 func TestValidateContainerIsolationLinux(t *testing.T) {
179 179
 	d := Daemon{}
180 180
 
181
-	_, err := d.verifyContainerSettings(&containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false)
181
+	_, err := d.verifyContainerSettings(&config.Config{}, &containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false)
182 182
 	assert.Check(t, is.Error(err, "invalid isolation 'hyperv' on linux"))
183 183
 }
184 184
 
... ...
@@ -264,7 +264,8 @@ func TestRootMountCleanup(t *testing.T) {
264 264
 	err = os.Mkdir(cfg.Root, 0755)
265 265
 	assert.NilError(t, err)
266 266
 
267
-	d := &Daemon{configStore: cfg, root: cfg.Root}
267
+	d := &Daemon{root: cfg.Root}
268
+	d.configStore.Store(cfg)
268 269
 	unmountFile := getUnmountOnShutdownPath(cfg)
269 270
 
270 271
 	t.Run("regular dir no mountpoint", func(t *testing.T) {
... ...
@@ -274,7 +275,7 @@ func TestRootMountCleanup(t *testing.T) {
274 274
 		assert.NilError(t, err)
275 275
 		checkMounted(t, cfg.Root, true)
276 276
 
277
-		assert.Assert(t, d.cleanupMounts())
277
+		assert.Assert(t, d.cleanupMounts(cfg))
278 278
 		checkMounted(t, cfg.Root, false)
279 279
 
280 280
 		_, err = os.Stat(unmountFile)
... ...
@@ -292,7 +293,7 @@ func TestRootMountCleanup(t *testing.T) {
292 292
 
293 293
 		_, err = os.Stat(unmountFile)
294 294
 		assert.Assert(t, os.IsNotExist(err))
295
-		assert.Assert(t, d.cleanupMounts())
295
+		assert.Assert(t, d.cleanupMounts(cfg))
296 296
 		checkMounted(t, cfg.Root, true)
297 297
 	})
298 298
 
... ...
@@ -309,7 +310,7 @@ func TestRootMountCleanup(t *testing.T) {
309 309
 			t.Fatal("unmount file should not exist")
310 310
 		}
311 311
 
312
-		assert.Assert(t, d.cleanupMounts())
312
+		assert.Assert(t, d.cleanupMounts(cfg))
313 313
 		checkMounted(t, cfg.Root, true)
314 314
 		assert.Assert(t, mount.Unmount(cfg.Root))
315 315
 	})
... ...
@@ -328,7 +329,7 @@ func TestRootMountCleanup(t *testing.T) {
328 328
 		_, err = os.Stat(unmountFile)
329 329
 		assert.Check(t, os.IsNotExist(err), err)
330 330
 		checkMounted(t, cfg.Root, false)
331
-		assert.Assert(t, d.cleanupMounts())
331
+		assert.Assert(t, d.cleanupMounts(cfg))
332 332
 	})
333 333
 }
334 334
 
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	containertypes "github.com/docker/docker/api/types/container"
10 10
 	"github.com/docker/docker/container"
11
+	"github.com/docker/docker/daemon/config"
11 12
 	"github.com/docker/docker/errdefs"
12 13
 	"github.com/docker/docker/libnetwork"
13 14
 	"github.com/docker/docker/pkg/idtools"
... ...
@@ -300,7 +301,7 @@ func TestMerge(t *testing.T) {
300 300
 func TestValidateContainerIsolation(t *testing.T) {
301 301
 	d := Daemon{}
302 302
 
303
-	_, err := d.verifyContainerSettings(&containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false)
303
+	_, err := d.verifyContainerSettings(&config.Config{}, &containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false)
304 304
 	assert.Check(t, is.Error(err, "invalid isolation 'invalid' on "+runtime.GOOS))
305 305
 }
306 306
 
... ...
@@ -189,8 +189,8 @@ func getBlkioWeightDevices(config containertypes.Resources) ([]specs.LinuxWeight
189 189
 	return blkioWeightDevices, nil
190 190
 }
191 191
 
192
-func (daemon *Daemon) parseSecurityOpt(securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
193
-	securityOptions.NoNewPrivileges = daemon.configStore.NoNewPrivileges
192
+func (daemon *Daemon) parseSecurityOpt(cfg *config.Config, securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
193
+	securityOptions.NoNewPrivileges = cfg.NoNewPrivileges
194 194
 	return parseSecurityOpt(securityOptions, hostConfig)
195 195
 }
196 196
 
... ...
@@ -299,7 +299,7 @@ func adjustParallelLimit(n int, limit int) int {
299 299
 
300 300
 // adaptContainerSettings is called during container creation to modify any
301 301
 // settings necessary in the HostConfig structure.
302
-func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
302
+func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
303 303
 	if adjustCPUShares && hostConfig.CPUShares > 0 {
304 304
 		// Handle unsupported CPUShares
305 305
 		if hostConfig.CPUShares < linuxMinCPUShares {
... ...
@@ -316,15 +316,15 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
316 316
 	}
317 317
 	if hostConfig.ShmSize == 0 {
318 318
 		hostConfig.ShmSize = config.DefaultShmSize
319
-		if daemon.configStore != nil {
320
-			hostConfig.ShmSize = int64(daemon.configStore.ShmSize)
319
+		if daemonCfg != nil {
320
+			hostConfig.ShmSize = int64(daemonCfg.ShmSize)
321 321
 		}
322 322
 	}
323 323
 	// Set default IPC mode, if unset for container
324 324
 	if hostConfig.IpcMode.IsEmpty() {
325 325
 		m := config.DefaultIpcMode
326
-		if daemon.configStore != nil {
327
-			m = containertypes.IpcMode(daemon.configStore.IpcMode)
326
+		if daemonCfg != nil {
327
+			m = containertypes.IpcMode(daemonCfg.IpcMode)
328 328
 		}
329 329
 		hostConfig.IpcMode = m
330 330
 	}
... ...
@@ -340,8 +340,8 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
340 340
 			if cgroups.Mode() == cgroups.Unified {
341 341
 				m = containertypes.CgroupnsModePrivate
342 342
 			}
343
-			if daemon.configStore != nil {
344
-				m = containertypes.CgroupnsMode(daemon.configStore.CgroupNamespaceMode)
343
+			if daemonCfg != nil {
344
+				m = containertypes.CgroupnsMode(daemonCfg.CgroupNamespaceMode)
345 345
 			}
346 346
 			hostConfig.CgroupnsMode = m
347 347
 		}
... ...
@@ -566,11 +566,11 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, sysIn
566 566
 	return warnings, nil
567 567
 }
568 568
 
569
-func (daemon *Daemon) getCgroupDriver() string {
570
-	if UsingSystemd(daemon.configStore) {
569
+func cgroupDriver(cfg *config.Config) string {
570
+	if UsingSystemd(cfg) {
571 571
 		return cgroupSystemdDriver
572 572
 	}
573
-	if daemon.Rootless() {
573
+	if cfg.Rootless {
574 574
 		return cgroupNoneDriver
575 575
 	}
576 576
 	return cgroupFsDriver
... ...
@@ -639,7 +639,7 @@ func isRunningSystemd() bool {
639 639
 
640 640
 // verifyPlatformContainerSettings performs platform-specific validation of the
641 641
 // hostconfig and config structures.
642
-func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
642
+func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *config.Config, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
643 643
 	if hostConfig == nil {
644 644
 		return nil, nil
645 645
 	}
... ...
@@ -680,7 +680,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
680 680
 	}
681 681
 
682 682
 	// check for various conflicting options with user namespaces
683
-	if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() {
683
+	if daemonCfg.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() {
684 684
 		if hostConfig.Privileged {
685 685
 			return warnings, fmt.Errorf("privileged mode is incompatible with user namespaces.  You must run the container in the host namespace when running privileged mode")
686 686
 		}
... ...
@@ -691,17 +691,17 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
691 691
 			return warnings, fmt.Errorf("cannot share the host PID namespace when user namespaces are enabled")
692 692
 		}
693 693
 	}
694
-	if hostConfig.CgroupParent != "" && UsingSystemd(daemon.configStore) {
694
+	if hostConfig.CgroupParent != "" && UsingSystemd(daemonCfg) {
695 695
 		// CgroupParent for systemd cgroup should be named as "xxx.slice"
696 696
 		if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") {
697 697
 			return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
698 698
 		}
699 699
 	}
700 700
 	if hostConfig.Runtime == "" {
701
-		hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
701
+		hostConfig.Runtime = daemonCfg.DefaultRuntime
702 702
 	}
703 703
 
704
-	if _, _, err := daemon.getRuntime(hostConfig.Runtime); err != nil {
704
+	if _, _, err := daemon.getRuntime(daemonCfg, hostConfig.Runtime); err != nil {
705 705
 		return warnings, err
706 706
 	}
707 707
 
... ...
@@ -756,7 +756,7 @@ func verifyDaemonSettings(conf *config.Config) error {
756 756
 	}
757 757
 
758 758
 	configureRuntimes(conf)
759
-	if rtName := conf.GetDefaultRuntimeName(); rtName != "" {
759
+	if rtName := conf.DefaultRuntime; rtName != "" {
760 760
 		if conf.GetRuntime(rtName) == nil {
761 761
 			if !config.IsPermissibleC8dRuntimeName(rtName) {
762 762
 				return fmt.Errorf("specified default runtime '%s' does not exist", rtName)
... ...
@@ -837,8 +837,8 @@ func configureKernelSecuritySupport(config *config.Config, driverName string) er
837 837
 // initNetworkController initializes the libnetwork controller and configures
838 838
 // network settings. If there's active sandboxes, configuration changes will not
839 839
 // take effect.
840
-func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface{}) error {
841
-	netOptions, err := daemon.networkOptions(daemon.PluginStore, activeSandboxes)
840
+func (daemon *Daemon) initNetworkController(cfg *config.Config, activeSandboxes map[string]interface{}) error {
841
+	netOptions, err := daemon.networkOptions(cfg, daemon.PluginStore, activeSandboxes)
842 842
 	if err != nil {
843 843
 		return err
844 844
 	}
... ...
@@ -850,12 +850,12 @@ func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface
850 850
 
851 851
 	if len(activeSandboxes) > 0 {
852 852
 		logrus.Info("there are running containers, updated network configuration will not take affect")
853
-	} else if err := configureNetworking(daemon.netController, daemon.configStore); err != nil {
853
+	} else if err := configureNetworking(daemon.netController, cfg); err != nil {
854 854
 		return err
855 855
 	}
856 856
 
857 857
 	// Set HostGatewayIP to the default bridge's IP if it is empty
858
-	setHostGatewayIP(daemon.netController, daemon.configStore)
858
+	setHostGatewayIP(daemon.netController, cfg)
859 859
 	return nil
860 860
 }
861 861
 
... ...
@@ -1410,7 +1410,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container
1410 1410
 
1411 1411
 // setDefaultIsolation determines the default isolation mode for the
1412 1412
 // daemon to run in. This is only applicable on Windows
1413
-func (daemon *Daemon) setDefaultIsolation() error {
1413
+func (daemon *Daemon) setDefaultIsolation(*config.Config) error {
1414 1414
 	return nil
1415 1415
 }
1416 1416
 
... ...
@@ -1443,14 +1443,14 @@ func setMayDetachMounts() error {
1443 1443
 	return err
1444 1444
 }
1445 1445
 
1446
-func (daemon *Daemon) initCPURtController(mnt, path string) error {
1446
+func (daemon *Daemon) initCPURtController(cfg *config.Config, mnt, path string) error {
1447 1447
 	if path == "/" || path == "." {
1448 1448
 		return nil
1449 1449
 	}
1450 1450
 
1451 1451
 	// Recursively create cgroup to ensure that the system and all parent cgroups have values set
1452 1452
 	// for the period and runtime as this limits what the children can be set to.
1453
-	if err := daemon.initCPURtController(mnt, filepath.Dir(path)); err != nil {
1453
+	if err := daemon.initCPURtController(cfg, mnt, filepath.Dir(path)); err != nil {
1454 1454
 		return err
1455 1455
 	}
1456 1456
 
... ...
@@ -1458,10 +1458,10 @@ func (daemon *Daemon) initCPURtController(mnt, path string) error {
1458 1458
 	if err := os.MkdirAll(path, 0755); err != nil {
1459 1459
 		return err
1460 1460
 	}
1461
-	if err := maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
1461
+	if err := maybeCreateCPURealTimeFile(cfg.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
1462 1462
 		return err
1463 1463
 	}
1464
-	return maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path)
1464
+	return maybeCreateCPURealTimeFile(cfg.CPURealtimeRuntime, "cpu.rt_runtime_us", path)
1465 1465
 }
1466 1466
 
1467 1467
 func maybeCreateCPURealTimeFile(configValue int64, file string, path string) error {
... ...
@@ -1471,8 +1471,8 @@ func maybeCreateCPURealTimeFile(configValue int64, file string, path string) err
1471 1471
 	return os.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700)
1472 1472
 }
1473 1473
 
1474
-func (daemon *Daemon) setupSeccompProfile() error {
1475
-	switch profile := daemon.configStore.SeccompProfile; profile {
1474
+func (daemon *Daemon) setupSeccompProfile(cfg *config.Config) error {
1475
+	switch profile := cfg.SeccompProfile; profile {
1476 1476
 	case "", config.SeccompProfileDefault:
1477 1477
 		daemon.seccompProfilePath = config.SeccompProfileDefault
1478 1478
 	case config.SeccompProfileUnconfined:
... ...
@@ -1488,9 +1488,9 @@ func (daemon *Daemon) setupSeccompProfile() error {
1488 1488
 	return nil
1489 1489
 }
1490 1490
 
1491
-func getSysInfo(daemon *Daemon) *sysinfo.SysInfo {
1491
+func getSysInfo(cfg *config.Config) *sysinfo.SysInfo {
1492 1492
 	var siOpts []sysinfo.Opt
1493
-	if daemon.getCgroupDriver() == cgroupSystemdDriver {
1493
+	if cgroupDriver(cfg) == cgroupSystemdDriver {
1494 1494
 		if euid := os.Getenv("ROOTLESSKIT_PARENT_EUID"); euid != "" {
1495 1495
 			siOpts = append(siOpts, sysinfo.WithCgroup2GroupPath("/user.slice/user-"+euid+".slice"))
1496 1496
 		}
... ...
@@ -1498,13 +1498,13 @@ func getSysInfo(daemon *Daemon) *sysinfo.SysInfo {
1498 1498
 	return sysinfo.New(siOpts...)
1499 1499
 }
1500 1500
 
1501
-func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
1501
+func (daemon *Daemon) initLibcontainerd(ctx context.Context, cfg *config.Config) error {
1502 1502
 	var err error
1503 1503
 	daemon.containerd, err = remote.NewClient(
1504 1504
 		ctx,
1505 1505
 		daemon.containerdCli,
1506
-		filepath.Join(daemon.configStore.ExecRoot, "containerd"),
1507
-		daemon.configStore.ContainerdNamespace,
1506
+		filepath.Join(cfg.ExecRoot, "containerd"),
1507
+		cfg.ContainerdNamespace,
1508 1508
 		daemon,
1509 1509
 	)
1510 1510
 	return err
... ...
@@ -68,30 +68,31 @@ func TestAdjustCPUShares(t *testing.T) {
68 68
 		repository: tmp,
69 69
 		root:       tmp,
70 70
 	}
71
+	cfg := &config.Config{}
71 72
 	muteLogs()
72 73
 
73 74
 	hostConfig := &containertypes.HostConfig{
74 75
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
75 76
 	}
76
-	daemon.adaptContainerSettings(hostConfig, true)
77
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
77 78
 	if hostConfig.CPUShares != linuxMinCPUShares {
78 79
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
79 80
 	}
80 81
 
81 82
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
82
-	daemon.adaptContainerSettings(hostConfig, true)
83
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
83 84
 	if hostConfig.CPUShares != linuxMaxCPUShares {
84 85
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
85 86
 	}
86 87
 
87 88
 	hostConfig.CPUShares = 0
88
-	daemon.adaptContainerSettings(hostConfig, true)
89
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
89 90
 	if hostConfig.CPUShares != 0 {
90 91
 		t.Error("Expected CPUShares to be unchanged")
91 92
 	}
92 93
 
93 94
 	hostConfig.CPUShares = 1024
94
-	daemon.adaptContainerSettings(hostConfig, true)
95
+	daemon.adaptContainerSettings(cfg, hostConfig, true)
95 96
 	if hostConfig.CPUShares != 1024 {
96 97
 		t.Error("Expected CPUShares to be unchanged")
97 98
 	}
... ...
@@ -108,29 +109,30 @@ func TestAdjustCPUSharesNoAdjustment(t *testing.T) {
108 108
 		repository: tmp,
109 109
 		root:       tmp,
110 110
 	}
111
+	cfg := &config.Config{}
111 112
 
112 113
 	hostConfig := &containertypes.HostConfig{
113 114
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
114 115
 	}
115
-	daemon.adaptContainerSettings(hostConfig, false)
116
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
116 117
 	if hostConfig.CPUShares != linuxMinCPUShares-1 {
117 118
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
118 119
 	}
119 120
 
120 121
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
121
-	daemon.adaptContainerSettings(hostConfig, false)
122
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
122 123
 	if hostConfig.CPUShares != linuxMaxCPUShares+1 {
123 124
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
124 125
 	}
125 126
 
126 127
 	hostConfig.CPUShares = 0
127
-	daemon.adaptContainerSettings(hostConfig, false)
128
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
128 129
 	if hostConfig.CPUShares != 0 {
129 130
 		t.Error("Expected CPUShares to be unchanged")
130 131
 	}
131 132
 
132 133
 	hostConfig.CPUShares = 1024
133
-	daemon.adaptContainerSettings(hostConfig, false)
134
+	daemon.adaptContainerSettings(cfg, hostConfig, false)
134 135
 	if hostConfig.CPUShares != 1024 {
135 136
 		t.Error("Expected CPUShares to be unchanged")
136 137
 	}
... ...
@@ -243,16 +245,16 @@ func TestParseSecurityOpt(t *testing.T) {
243 243
 }
244 244
 
245 245
 func TestParseNNPSecurityOptions(t *testing.T) {
246
-	daemon := &Daemon{
247
-		configStore: &config.Config{NoNewPrivileges: true},
248
-	}
246
+	daemonCfg := &config.Config{NoNewPrivileges: true}
247
+	daemon := &Daemon{}
248
+	daemon.configStore.Store(daemonCfg)
249 249
 	opts := &container.SecurityOptions{}
250 250
 	cfg := &containertypes.HostConfig{}
251 251
 
252 252
 	// test NNP when "daemon:true" and "no-new-privileges=false""
253 253
 	cfg.SecurityOpt = []string{"no-new-privileges=false"}
254 254
 
255
-	if err := daemon.parseSecurityOpt(opts, cfg); err != nil {
255
+	if err := daemon.parseSecurityOpt(daemonCfg, opts, cfg); err != nil {
256 256
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
257 257
 	}
258 258
 	if opts.NoNewPrivileges {
... ...
@@ -260,10 +262,10 @@ func TestParseNNPSecurityOptions(t *testing.T) {
260 260
 	}
261 261
 
262 262
 	// test NNP when "daemon:false" and "no-new-privileges=true""
263
-	daemon.configStore.NoNewPrivileges = false
263
+	daemonCfg.NoNewPrivileges = false
264 264
 	cfg.SecurityOpt = []string{"no-new-privileges=true"}
265 265
 
266
-	if err := daemon.parseSecurityOpt(opts, cfg); err != nil {
266
+	if err := daemon.parseSecurityOpt(daemonCfg, opts, cfg); err != nil {
267 267
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
268 268
 	}
269 269
 	if !opts.NoNewPrivileges {
... ...
@@ -17,7 +17,3 @@ func setupResolvConf(_ *interface{}) {}
17 17
 func getSysInfo(_ *Daemon) *sysinfo.SysInfo {
18 18
 	return sysinfo.New()
19 19
 }
20
-
21
-func (daemon *Daemon) supportsRecursivelyReadOnly(_ string) error {
22
-	return nil
23
-}
... ...
@@ -55,7 +55,7 @@ func getPluginExecRoot(cfg *config.Config) string {
55 55
 	return filepath.Join(cfg.Root, "plugins")
56 56
 }
57 57
 
58
-func (daemon *Daemon) parseSecurityOpt(securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
58
+func (daemon *Daemon) parseSecurityOpt(daemonCfg *config.Config, securityOptions *container.SecurityOptions, hostConfig *containertypes.HostConfig) error {
59 59
 	return nil
60 60
 }
61 61
 
... ...
@@ -65,7 +65,7 @@ func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error {
65 65
 
66 66
 // adaptContainerSettings is called during container creation to modify any
67 67
 // settings necessary in the HostConfig structure.
68
-func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
68
+func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
69 69
 	return nil
70 70
 }
71 71
 
... ...
@@ -171,7 +171,7 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, isHyp
171 171
 
172 172
 // verifyPlatformContainerSettings performs platform-specific validation of the
173 173
 // hostconfig and config structures.
174
-func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
174
+func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *config.Config, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
175 175
 	if hostConfig == nil {
176 176
 		return nil, nil
177 177
 	}
... ...
@@ -232,8 +232,8 @@ func configureMaxThreads(config *config.Config) error {
232 232
 	return nil
233 233
 }
234 234
 
235
-func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface{}) error {
236
-	netOptions, err := daemon.networkOptions(nil, nil)
235
+func (daemon *Daemon) initNetworkController(daemonCfg *config.Config, activeSandboxes map[string]interface{}) error {
236
+	netOptions, err := daemon.networkOptions(daemonCfg, nil, nil)
237 237
 	if err != nil {
238 238
 		return err
239 239
 	}
... ...
@@ -396,9 +396,9 @@ func (daemon *Daemon) initNetworkController(activeSandboxes map[string]interface
396 396
 		}
397 397
 	}
398 398
 
399
-	if !daemon.configStore.DisableBridge {
399
+	if !daemonCfg.DisableBridge {
400 400
 		// Initialize default driver "bridge"
401
-		if err := initBridgeDriver(daemon.netController, daemon.configStore); err != nil {
401
+		if err := initBridgeDriver(daemon.netController, daemonCfg); err != nil {
402 402
 			return err
403 403
 		}
404 404
 	}
... ...
@@ -452,7 +452,7 @@ func (daemon *Daemon) cleanupMountsByID(in string) error {
452 452
 	return nil
453 453
 }
454 454
 
455
-func (daemon *Daemon) cleanupMounts() error {
455
+func (daemon *Daemon) cleanupMounts(*config.Config) error {
456 456
 	return nil
457 457
 }
458 458
 
... ...
@@ -512,7 +512,7 @@ func driverOptions(_ *config.Config) nwconfig.Option {
512 512
 
513 513
 // setDefaultIsolation determine the default isolation mode for the
514 514
 // daemon to run in. This is only applicable on Windows
515
-func (daemon *Daemon) setDefaultIsolation() error {
515
+func (daemon *Daemon) setDefaultIsolation(config *config.Config) error {
516 516
 	// On client SKUs, default to Hyper-V. @engine maintainers. This
517 517
 	// should not be removed. Ping Microsoft folks is there are PRs to
518 518
 	// to change this.
... ...
@@ -521,7 +521,7 @@ func (daemon *Daemon) setDefaultIsolation() error {
521 521
 	} else {
522 522
 		daemon.defaultIsolation = containertypes.IsolationProcess
523 523
 	}
524
-	for _, option := range daemon.configStore.ExecOptions {
524
+	for _, option := range config.ExecOptions {
525 525
 		key, val, err := parsers.ParseKeyValueOpt(option)
526 526
 		if err != nil {
527 527
 			return err
... ...
@@ -552,7 +552,7 @@ func setMayDetachMounts() error {
552 552
 	return nil
553 553
 }
554 554
 
555
-func (daemon *Daemon) setupSeccompProfile() error {
555
+func (daemon *Daemon) setupSeccompProfile(*config.Config) error {
556 556
 	return nil
557 557
 }
558 558
 
... ...
@@ -562,16 +562,16 @@ func (daemon *Daemon) loadRuntimes() error {
562 562
 
563 563
 func setupResolvConf(config *config.Config) {}
564 564
 
565
-func getSysInfo(daemon *Daemon) *sysinfo.SysInfo {
565
+func getSysInfo(*config.Config) *sysinfo.SysInfo {
566 566
 	return sysinfo.New()
567 567
 }
568 568
 
569
-func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
569
+func (daemon *Daemon) initLibcontainerd(ctx context.Context, cfg *config.Config) error {
570 570
 	var err error
571 571
 
572
-	rt := daemon.configStore.GetDefaultRuntimeName()
572
+	rt := cfg.DefaultRuntime
573 573
 	if rt == "" {
574
-		if daemon.configStore.ContainerdAddr == "" {
574
+		if cfg.ContainerdAddr == "" {
575 575
 			rt = windowsV1RuntimeName
576 576
 		} else {
577 577
 			rt = windowsV2RuntimeName
... ...
@@ -583,19 +583,19 @@ func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
583 583
 		daemon.containerd, err = local.NewClient(
584 584
 			ctx,
585 585
 			daemon.containerdCli,
586
-			filepath.Join(daemon.configStore.ExecRoot, "containerd"),
587
-			daemon.configStore.ContainerdNamespace,
586
+			filepath.Join(cfg.ExecRoot, "containerd"),
587
+			cfg.ContainerdNamespace,
588 588
 			daemon,
589 589
 		)
590 590
 	case windowsV2RuntimeName:
591
-		if daemon.configStore.ContainerdAddr == "" {
591
+		if cfg.ContainerdAddr == "" {
592 592
 			return fmt.Errorf("cannot use the specified runtime %q without containerd", rt)
593 593
 		}
594 594
 		daemon.containerd, err = remote.NewClient(
595 595
 			ctx,
596 596
 			daemon.containerdCli,
597
-			filepath.Join(daemon.configStore.ExecRoot, "containerd"),
598
-			daemon.configStore.ContainerdNamespace,
597
+			filepath.Join(cfg.ExecRoot, "containerd"),
598
+			cfg.ContainerdNamespace,
599 599
 			daemon,
600 600
 		)
601 601
 	default:
... ...
@@ -604,7 +604,3 @@ func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
604 604
 
605 605
 	return err
606 606
 }
607
-
608
-func (daemon *Daemon) supportsRecursivelyReadOnly(_ string) error {
609
-	return nil
610
-}
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/docker/docker/api/types"
13 13
 	containertypes "github.com/docker/docker/api/types/container"
14 14
 	"github.com/docker/docker/container"
15
+	"github.com/docker/docker/daemon/config"
15 16
 	"github.com/docker/docker/errdefs"
16 17
 	"github.com/docker/docker/pkg/containerfs"
17 18
 	"github.com/opencontainers/selinux/go-selinux"
... ...
@@ -24,6 +25,10 @@ import (
24 24
 // fails. If the remove succeeds, the container name is released, and
25 25
 // network links are removed.
26 26
 func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error {
27
+	return daemon.containerRm(daemon.config(), name, config)
28
+}
29
+
30
+func (daemon *Daemon) containerRm(cfg *config.Config, name string, opts *types.ContainerRmConfig) error {
27 31
 	start := time.Now()
28 32
 	ctr, err := daemon.GetContainer(name)
29 33
 	if err != nil {
... ...
@@ -42,17 +47,17 @@ func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig)
42 42
 		return nil
43 43
 	}
44 44
 
45
-	if config.RemoveLink {
46
-		return daemon.rmLink(ctr, name)
45
+	if opts.RemoveLink {
46
+		return daemon.rmLink(cfg, ctr, name)
47 47
 	}
48 48
 
49
-	err = daemon.cleanupContainer(ctr, *config)
49
+	err = daemon.cleanupContainer(ctr, *opts)
50 50
 	containerActions.WithValues("delete").UpdateSince(start)
51 51
 
52 52
 	return err
53 53
 }
54 54
 
55
-func (daemon *Daemon) rmLink(container *container.Container, name string) error {
55
+func (daemon *Daemon) rmLink(cfg *config.Config, container *container.Container, name string) error {
56 56
 	if name[0] != '/' {
57 57
 		name = "/" + name
58 58
 	}
... ...
@@ -71,7 +76,7 @@ func (daemon *Daemon) rmLink(container *container.Container, name string) error
71 71
 	parentContainer, _ := daemon.GetContainer(pe)
72 72
 	if parentContainer != nil {
73 73
 		daemon.linkIndex.unlink(name, container, parentContainer)
74
-		if err := daemon.updateNetwork(parentContainer); err != nil {
74
+		if err := daemon.updateNetwork(cfg, parentContainer); err != nil {
75 75
 			logrus.Debugf("Could not update network to remove link %s: %v", n, err)
76 76
 		}
77 77
 	}
... ...
@@ -252,7 +252,8 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio
252 252
 		p.Cwd = "/"
253 253
 	}
254 254
 
255
-	if err := daemon.execSetPlatformOpt(ctx, ec, p); err != nil {
255
+	daemonCfg := daemon.config()
256
+	if err := daemon.execSetPlatformOpt(ctx, daemonCfg, ec, p); err != nil {
256 257
 		return err
257 258
 	}
258 259
 
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	coci "github.com/containerd/containerd/oci"
10 10
 	"github.com/containerd/containerd/pkg/apparmor"
11 11
 	"github.com/docker/docker/container"
12
+	"github.com/docker/docker/daemon/config"
12 13
 	"github.com/docker/docker/oci/caps"
13 14
 	specs "github.com/opencontainers/runtime-spec/specs-go"
14 15
 )
... ...
@@ -50,7 +51,7 @@ func getUserFromContainerd(ctx context.Context, containerdCli *containerd.Client
50 50
 	return spec.Process.User, nil
51 51
 }
52 52
 
53
-func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, ec *container.ExecConfig, p *specs.Process) error {
53
+func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, daemonCfg *config.Config, ec *container.ExecConfig, p *specs.Process) error {
54 54
 	if len(ec.User) > 0 {
55 55
 		var err error
56 56
 		if daemon.UsesSnapshotter() {
... ...
@@ -100,5 +101,5 @@ func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, ec *container.Exec
100 100
 		p.ApparmorProfile = appArmorProfile
101 101
 	}
102 102
 	s := &specs.Spec{Process: p}
103
-	return WithRlimits(daemon, ec.Container)(ctx, nil, nil, s)
103
+	return withRlimits(daemon, daemonCfg, ec.Container)(ctx, nil, nil, s)
104 104
 }
... ...
@@ -50,7 +50,9 @@ func TestExecSetPlatformOptAppArmor(t *testing.T) {
50 50
 		},
51 51
 	}
52 52
 
53
-	d := &Daemon{configStore: &config.Config{}}
53
+	cfg := &config.Config{}
54
+	d := &Daemon{}
55
+	d.configStore.Store(cfg)
54 56
 
55 57
 	// Currently, `docker exec --privileged` inherits the Privileged configuration
56 58
 	// of the container, and does not disable AppArmor.
... ...
@@ -81,7 +83,7 @@ func TestExecSetPlatformOptAppArmor(t *testing.T) {
81 81
 				ec := &container.ExecConfig{Container: c, Privileged: execPrivileged}
82 82
 				p := &specs.Process{}
83 83
 
84
-				err := d.execSetPlatformOpt(context.Background(), ec, p)
84
+				err := d.execSetPlatformOpt(context.Background(), cfg, ec, p)
85 85
 				assert.NilError(t, err)
86 86
 				assert.Equal(t, p.ApparmorProfile, tc.expectedProfile)
87 87
 			})
... ...
@@ -4,10 +4,11 @@ import (
4 4
 	"context"
5 5
 
6 6
 	"github.com/docker/docker/container"
7
+	"github.com/docker/docker/daemon/config"
7 8
 	specs "github.com/opencontainers/runtime-spec/specs-go"
8 9
 )
9 10
 
10
-func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, ec *container.ExecConfig, p *specs.Process) error {
11
+func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, daemonCfg *config.Config, ec *container.ExecConfig, p *specs.Process) error {
11 12
 	if ec.Container.OS == "windows" {
12 13
 		p.User.Username = ec.User
13 14
 	}
... ...
@@ -30,6 +30,7 @@ func (daemon *Daemon) SystemInfo() *types.Info {
30 30
 	defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))()
31 31
 
32 32
 	sysInfo := daemon.RawSysInfo()
33
+	cfg := daemon.config()
33 34
 
34 35
 	v := &types.Info{
35 36
 		ID:                 daemon.id,
... ...
@@ -50,27 +51,27 @@ func (daemon *Daemon) SystemInfo() *types.Info {
50 50
 		NCPU:               sysinfo.NumCPU(),
51 51
 		MemTotal:           memInfo().MemTotal,
52 52
 		GenericResources:   daemon.genericResources,
53
-		DockerRootDir:      daemon.configStore.Root,
54
-		Labels:             daemon.configStore.Labels,
55
-		ExperimentalBuild:  daemon.configStore.Experimental,
53
+		DockerRootDir:      cfg.Root,
54
+		Labels:             cfg.Labels,
55
+		ExperimentalBuild:  cfg.Experimental,
56 56
 		ServerVersion:      dockerversion.Version,
57
-		HTTPProxy:          config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPProxy, "HTTP_PROXY", "http_proxy")),
58
-		HTTPSProxy:         config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPSProxy, "HTTPS_PROXY", "https_proxy")),
59
-		NoProxy:            getConfigOrEnv(daemon.configStore.NoProxy, "NO_PROXY", "no_proxy"),
60
-		LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled,
57
+		HTTPProxy:          config.MaskCredentials(getConfigOrEnv(cfg.HTTPProxy, "HTTP_PROXY", "http_proxy")),
58
+		HTTPSProxy:         config.MaskCredentials(getConfigOrEnv(cfg.HTTPSProxy, "HTTPS_PROXY", "https_proxy")),
59
+		NoProxy:            getConfigOrEnv(cfg.NoProxy, "NO_PROXY", "no_proxy"),
60
+		LiveRestoreEnabled: cfg.LiveRestoreEnabled,
61 61
 		Isolation:          daemon.defaultIsolation,
62 62
 	}
63 63
 
64 64
 	daemon.fillContainerStates(v)
65 65
 	daemon.fillDebugInfo(v)
66
-	daemon.fillAPIInfo(v)
66
+	daemon.fillAPIInfo(v, cfg)
67 67
 	// Retrieve platform specific info
68
-	daemon.fillPlatformInfo(v, sysInfo)
68
+	daemon.fillPlatformInfo(v, sysInfo, cfg)
69 69
 	daemon.fillDriverInfo(v)
70
-	daemon.fillPluginsInfo(v)
71
-	daemon.fillSecurityOptions(v, sysInfo)
70
+	daemon.fillPluginsInfo(v, cfg)
71
+	daemon.fillSecurityOptions(v, sysInfo, cfg)
72 72
 	daemon.fillLicense(v)
73
-	daemon.fillDefaultAddressPools(v)
73
+	daemon.fillDefaultAddressPools(v, cfg)
74 74
 
75 75
 	return v
76 76
 }
... ...
@@ -80,6 +81,7 @@ func (daemon *Daemon) SystemVersion() types.Version {
80 80
 	defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))()
81 81
 
82 82
 	kernelVersion := kernelVersion()
83
+	cfg := daemon.config()
83 84
 
84 85
 	v := types.Version{
85 86
 		Components: []types.ComponentVersion{
... ...
@@ -95,7 +97,7 @@ func (daemon *Daemon) SystemVersion() types.Version {
95 95
 					"Arch":          runtime.GOARCH,
96 96
 					"BuildTime":     dockerversion.BuildTime,
97 97
 					"KernelVersion": kernelVersion,
98
-					"Experimental":  fmt.Sprintf("%t", daemon.configStore.Experimental),
98
+					"Experimental":  fmt.Sprintf("%t", cfg.Experimental),
99 99
 				},
100 100
 			},
101 101
 		},
... ...
@@ -110,12 +112,12 @@ func (daemon *Daemon) SystemVersion() types.Version {
110 110
 		Arch:          runtime.GOARCH,
111 111
 		BuildTime:     dockerversion.BuildTime,
112 112
 		KernelVersion: kernelVersion,
113
-		Experimental:  daemon.configStore.Experimental,
113
+		Experimental:  cfg.Experimental,
114 114
 	}
115 115
 
116 116
 	v.Platform.Name = dockerversion.PlatformName
117 117
 
118
-	daemon.fillPlatformVersion(&v)
118
+	daemon.fillPlatformVersion(&v, cfg)
119 119
 	return v
120 120
 }
121 121
 
... ...
@@ -135,19 +137,19 @@ WARNING: The %s storage-driver is deprecated, and will be removed in a future re
135 135
 	fillDriverWarnings(v)
136 136
 }
137 137
 
138
-func (daemon *Daemon) fillPluginsInfo(v *types.Info) {
138
+func (daemon *Daemon) fillPluginsInfo(v *types.Info, cfg *config.Config) {
139 139
 	v.Plugins = types.PluginsInfo{
140 140
 		Volume:  daemon.volumes.GetDriverList(),
141 141
 		Network: daemon.GetNetworkDriverList(),
142 142
 
143 143
 		// The authorization plugins are returned in the order they are
144 144
 		// used as they constitute a request/response modification chain.
145
-		Authorization: daemon.configStore.AuthorizationPlugins,
145
+		Authorization: cfg.AuthorizationPlugins,
146 146
 		Log:           logger.ListDrivers(),
147 147
 	}
148 148
 }
149 149
 
150
-func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInfo) {
150
+func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
151 151
 	var securityOptions []string
152 152
 	if sysInfo.AppArmor {
153 153
 		securityOptions = append(securityOptions, "name=apparmor")
... ...
@@ -164,13 +166,13 @@ func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInf
164 164
 	if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
165 165
 		securityOptions = append(securityOptions, "name=userns")
166 166
 	}
167
-	if daemon.Rootless() {
167
+	if Rootless(cfg) {
168 168
 		securityOptions = append(securityOptions, "name=rootless")
169 169
 	}
170
-	if daemon.cgroupNamespacesEnabled(sysInfo) {
170
+	if cgroupNamespacesEnabled(sysInfo, cfg) {
171 171
 		securityOptions = append(securityOptions, "name=cgroupns")
172 172
 	}
173
-	if daemon.noNewPrivileges() {
173
+	if noNewPrivileges(cfg) {
174 174
 		securityOptions = append(securityOptions, "name=no-new-privileges")
175 175
 	}
176 176
 
... ...
@@ -200,13 +202,12 @@ func (daemon *Daemon) fillDebugInfo(v *types.Info) {
200 200
 	v.NEventsListener = daemon.EventsService.SubscribersCount()
201 201
 }
202 202
 
203
-func (daemon *Daemon) fillAPIInfo(v *types.Info) {
203
+func (daemon *Daemon) fillAPIInfo(v *types.Info, cfg *config.Config) {
204 204
 	const warn string = `
205 205
          Access to the remote API is equivalent to root access on the host. Refer
206 206
          to the 'Docker daemon attack surface' section in the documentation for
207 207
          more information: https://docs.docker.com/go/attack-surface/`
208 208
 
209
-	cfg := daemon.configStore
210 209
 	for _, host := range cfg.Hosts {
211 210
 		// cnf.Hosts is normalized during startup, so should always have a scheme/proto
212 211
 		proto, addr, _ := strings.Cut(host, "://")
... ...
@@ -224,8 +225,8 @@ func (daemon *Daemon) fillAPIInfo(v *types.Info) {
224 224
 	}
225 225
 }
226 226
 
227
-func (daemon *Daemon) fillDefaultAddressPools(v *types.Info) {
228
-	for _, pool := range daemon.configStore.DefaultAddressPools.Value() {
227
+func (daemon *Daemon) fillDefaultAddressPools(v *types.Info, cfg *config.Config) {
228
+	for _, pool := range cfg.DefaultAddressPools.Value() {
229 229
 		v.DefaultAddressPools = append(v.DefaultAddressPools, types.NetworkAddressPool{
230 230
 			Base: pool.Base,
231 231
 			Size: pool.Size,
... ...
@@ -11,6 +11,7 @@ import (
11 11
 
12 12
 	"github.com/docker/docker/api/types"
13 13
 	containertypes "github.com/docker/docker/api/types/container"
14
+	"github.com/docker/docker/daemon/config"
14 15
 	"github.com/docker/docker/pkg/rootless"
15 16
 	"github.com/docker/docker/pkg/sysinfo"
16 17
 	"github.com/pkg/errors"
... ...
@@ -18,8 +19,8 @@ import (
18 18
 )
19 19
 
20 20
 // fillPlatformInfo fills the platform related info.
21
-func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) {
22
-	v.CgroupDriver = daemon.getCgroupDriver()
21
+func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
22
+	v.CgroupDriver = cgroupDriver(cfg)
23 23
 	v.CgroupVersion = "1"
24 24
 	if sysInfo.CgroupUnified {
25 25
 		v.CgroupVersion = "2"
... ...
@@ -37,13 +38,13 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
37 37
 		v.CPUSet = sysInfo.Cpuset
38 38
 		v.PidsLimit = sysInfo.PidsLimit
39 39
 	}
40
-	v.Runtimes = daemon.configStore.GetAllRuntimes()
41
-	v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName()
40
+	v.Runtimes = cfg.GetAllRuntimes()
41
+	v.DefaultRuntime = cfg.DefaultRuntime
42 42
 	v.RuncCommit.ID = "N/A"
43 43
 	v.ContainerdCommit.ID = "N/A"
44 44
 	v.InitCommit.ID = "N/A"
45 45
 
46
-	if rt := daemon.configStore.GetRuntime(v.DefaultRuntime); rt != nil {
46
+	if rt := cfg.GetRuntime(v.DefaultRuntime); rt != nil {
47 47
 		if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
48 48
 			if _, _, commit, err := parseRuntimeVersion(string(rv)); err != nil {
49 49
 				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)
61 61
 		logrus.Warnf("failed to retrieve containerd version: %v", err)
62 62
 	}
63 63
 
64
-	v.InitBinary = daemon.configStore.GetInitPath()
65
-	if initBinary, err := daemon.configStore.LookupInitPath(); err != nil {
64
+	v.InitBinary = cfg.GetInitPath()
65
+	if initBinary, err := cfg.LookupInitPath(); err != nil {
66 66
 		logrus.Warnf("failed to find docker-init: %s", err)
67 67
 	} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
68 68
 		if _, commit, err := parseInitVersion(string(rv)); err != nil {
... ...
@@ -165,7 +166,7 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
165 165
 	}
166 166
 }
167 167
 
168
-func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
168
+func (daemon *Daemon) fillPlatformVersion(v *types.Version, cfg *config.Config) {
169 169
 	if rv, err := daemon.containerd.Version(context.Background()); err == nil {
170 170
 		v.Components = append(v.Components, types.ComponentVersion{
171 171
 			Name:    "containerd",
... ...
@@ -176,8 +177,8 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
176 176
 		})
177 177
 	}
178 178
 
179
-	defaultRuntime := daemon.configStore.GetDefaultRuntimeName()
180
-	if rt := daemon.configStore.GetRuntime(defaultRuntime); rt != nil {
179
+	defaultRuntime := cfg.DefaultRuntime
180
+	if rt := cfg.GetRuntime(defaultRuntime); rt != nil {
181 181
 		if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
182 182
 			if _, ver, commit, err := parseRuntimeVersion(string(rv)); err != nil {
183 183
 				logrus.Warnf("failed to parse %s version: %v", rt.Path, err)
... ...
@@ -195,7 +196,7 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
195 195
 		}
196 196
 	}
197 197
 
198
-	if initBinary, err := daemon.configStore.LookupInitPath(); err != nil {
198
+	if initBinary, err := cfg.LookupInitPath(); err != nil {
199 199
 		logrus.Warnf("failed to find docker-init: %s", err)
200 200
 	} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
201 201
 		if ver, commit, err := parseInitVersion(string(rv)); err != nil {
... ...
@@ -337,15 +338,15 @@ func parseRuntimeVersion(v string) (runtime string, version string, commit strin
337 337
 	return runtime, version, commit, err
338 338
 }
339 339
 
340
-func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool {
341
-	return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(daemon.configStore.CgroupNamespaceMode).IsPrivate()
340
+func cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo, cfg *config.Config) bool {
341
+	return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(cfg.CgroupNamespaceMode).IsPrivate()
342 342
 }
343 343
 
344 344
 // Rootless returns true if daemon is running in rootless mode
345
-func (daemon *Daemon) Rootless() bool {
346
-	return daemon.configStore.Rootless
345
+func Rootless(cfg *config.Config) bool {
346
+	return cfg.Rootless
347 347
 }
348 348
 
349
-func (daemon *Daemon) noNewPrivileges() bool {
350
-	return daemon.configStore.NoNewPrivileges
349
+func noNewPrivileges(cfg *config.Config) bool {
350
+	return cfg.NoNewPrivileges
351 351
 }
... ...
@@ -2,27 +2,28 @@ package daemon // import "github.com/docker/docker/daemon"
2 2
 
3 3
 import (
4 4
 	"github.com/docker/docker/api/types"
5
+	"github.com/docker/docker/daemon/config"
5 6
 	"github.com/docker/docker/pkg/sysinfo"
6 7
 )
7 8
 
8 9
 // fillPlatformInfo fills the platform related info.
9
-func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) {
10
+func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
10 11
 }
11 12
 
12
-func (daemon *Daemon) fillPlatformVersion(v *types.Version) {}
13
+func (daemon *Daemon) fillPlatformVersion(v *types.Version, cfg *config.Config) {}
13 14
 
14 15
 func fillDriverWarnings(v *types.Info) {
15 16
 }
16 17
 
17
-func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool {
18
+func cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo, cfg *config.Config) bool {
18 19
 	return false
19 20
 }
20 21
 
21 22
 // Rootless returns true if daemon is running in rootless mode
22
-func (daemon *Daemon) Rootless() bool {
23
+func Rootless(*config.Config) bool {
23 24
 	return false
24 25
 }
25 26
 
26
-func (daemon *Daemon) noNewPrivileges() bool {
27
+func noNewPrivileges(*config.Config) bool {
27 28
 	return false
28 29
 }
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/docker/docker/api/types/versions"
13 13
 	"github.com/docker/docker/api/types/versions/v1p20"
14 14
 	"github.com/docker/docker/container"
15
+	"github.com/docker/docker/daemon/config"
15 16
 	"github.com/docker/docker/daemon/network"
16 17
 	"github.com/docker/docker/errdefs"
17 18
 	"github.com/docker/go-connections/nat"
... ...
@@ -40,7 +41,7 @@ func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string,
40 40
 
41 41
 	ctr.Lock()
42 42
 
43
-	base, err := daemon.getInspectData(ctr)
43
+	base, err := daemon.getInspectData(daemon.config(), ctr)
44 44
 	if err != nil {
45 45
 		ctr.Unlock()
46 46
 		return nil, err
... ...
@@ -105,7 +106,7 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
105 105
 	ctr.Lock()
106 106
 	defer ctr.Unlock()
107 107
 
108
-	base, err := daemon.getInspectData(ctr)
108
+	base, err := daemon.getInspectData(daemon.config(), ctr)
109 109
 	if err != nil {
110 110
 		return nil, err
111 111
 	}
... ...
@@ -124,7 +125,7 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
124 124
 	}, nil
125 125
 }
126 126
 
127
-func (daemon *Daemon) getInspectData(container *container.Container) (*types.ContainerJSONBase, error) {
127
+func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *container.Container) (*types.ContainerJSONBase, error) {
128 128
 	// make a copy to play with
129 129
 	hostConfig := *container.HostConfig
130 130
 
... ...
@@ -135,7 +136,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con
135 135
 	}
136 136
 
137 137
 	// We merge the Ulimits from hostConfig with daemon default
138
-	daemon.mergeUlimits(&hostConfig)
138
+	daemon.mergeUlimits(&hostConfig, daemonCfg)
139 139
 
140 140
 	var containerHealth *types.Health
141 141
 	if container.State.Health != nil {
... ...
@@ -29,7 +29,7 @@ func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (
29 29
 	ctr.Lock()
30 30
 	defer ctr.Unlock()
31 31
 
32
-	base, err := daemon.getInspectData(ctr)
32
+	base, err := daemon.getInspectData(daemon.config(), ctr)
33 33
 	if err != nil {
34 34
 		return nil, err
35 35
 	}
... ...
@@ -19,16 +19,18 @@ func TestGetInspectData(t *testing.T) {
19 19
 	}
20 20
 
21 21
 	d := &Daemon{
22
-		linkIndex:   newLinkIndex(),
23
-		configStore: &config.Config{},
22
+		linkIndex: newLinkIndex(),
24 23
 	}
25 24
 	if d.UsesSnapshotter() {
26 25
 		t.Skip("does not apply to containerd snapshotters, which don't have RWLayer set")
27 26
 	}
28
-	_, err := d.getInspectData(c)
27
+	cfg := &config.Config{}
28
+	d.configStore.Store(cfg)
29
+
30
+	_, err := d.getInspectData(cfg, c)
29 31
 	assert.Check(t, is.ErrorContains(err, "RWLayer of container inspect-me is unexpectedly nil"))
30 32
 
31 33
 	c.Dead = true
32
-	_, err = d.getInspectData(c)
34
+	_, err = d.getInspectData(cfg, c)
33 35
 	assert.Check(t, err)
34 36
 }
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	containertypes "github.com/docker/docker/api/types/container"
11 11
 	timetypes "github.com/docker/docker/api/types/time"
12 12
 	"github.com/docker/docker/container"
13
+	"github.com/docker/docker/daemon/config"
13 14
 	"github.com/docker/docker/daemon/logger"
14 15
 	logcache "github.com/docker/docker/daemon/logger/loggerutils/cache"
15 16
 	"github.com/docker/docker/errdefs"
... ...
@@ -196,18 +197,14 @@ func (daemon *Daemon) mergeAndVerifyLogConfig(cfg *containertypes.LogConfig) err
196 196
 	return logger.ValidateLogOpts(cfg.Type, cfg.Config)
197 197
 }
198 198
 
199
-func (daemon *Daemon) setupDefaultLogConfig() error {
200
-	config := daemon.configStore
201
-	if len(config.LogConfig.Config) > 0 {
202
-		if err := logger.ValidateLogOpts(config.LogConfig.Type, config.LogConfig.Config); err != nil {
203
-			return errors.Wrap(err, "failed to set log opts")
199
+func defaultLogConfig(cfg *config.Config) (containertypes.LogConfig, error) {
200
+	if len(cfg.LogConfig.Config) > 0 {
201
+		if err := logger.ValidateLogOpts(cfg.LogConfig.Type, cfg.LogConfig.Config); err != nil {
202
+			return containertypes.LogConfig{}, errors.Wrap(err, "failed to set log opts")
204 203
 		}
205 204
 	}
206
-	daemon.defaultLogConfig = containertypes.LogConfig{
207
-		Type:   config.LogConfig.Type,
208
-		Config: config.LogConfig.Config,
209
-	}
210
-
211
-	logrus.Debugf("Using default logging driver %s", daemon.defaultLogConfig.Type)
212
-	return nil
205
+	return containertypes.LogConfig{
206
+		Type:   cfg.LogConfig.Type,
207
+		Config: cfg.LogConfig.Config,
208
+	}, nil
213 209
 }
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"strings"
10 10
 	"time"
11 11
 
12
+	"github.com/docker/docker/daemon/config"
12 13
 	"github.com/docker/docker/pkg/plugingetter"
13 14
 	"github.com/docker/docker/pkg/plugins"
14 15
 	"github.com/docker/docker/plugin"
... ...
@@ -19,8 +20,8 @@ import (
19 19
 	"golang.org/x/sys/unix"
20 20
 )
21 21
 
22
-func (daemon *Daemon) listenMetricsSock() (string, error) {
23
-	path := filepath.Join(daemon.configStore.ExecRoot, "metrics.sock")
22
+func (daemon *Daemon) listenMetricsSock(cfg *config.Config) (string, error) {
23
+	path := filepath.Join(cfg.ExecRoot, "metrics.sock")
24 24
 	unix.Unlink(path)
25 25
 	l, err := net.Listen("unix", path)
26 26
 	if err != nil {
... ...
@@ -2,11 +2,14 @@
2 2
 
3 3
 package daemon // import "github.com/docker/docker/daemon"
4 4
 
5
-import "github.com/docker/docker/pkg/plugingetter"
5
+import (
6
+	"github.com/docker/docker/daemon/config"
7
+	"github.com/docker/docker/pkg/plugingetter"
8
+)
6 9
 
7 10
 func registerMetricsPluginCallback(getter plugingetter.PluginGetter, sockPath string) {
8 11
 }
9 12
 
10
-func (daemon *Daemon) listenMetricsSock() (string, error) {
13
+func (daemon *Daemon) listenMetricsSock(*config.Config) (string, error) {
11 14
 	return "", nil
12 15
 }
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9 9
 	"github.com/docker/docker/container"
10
+	"github.com/docker/docker/daemon/config"
10 11
 	"github.com/docker/docker/errdefs"
11 12
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
12 13
 	"github.com/docker/docker/restartmanager"
... ...
@@ -29,6 +30,8 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
29 29
 	var exitStatus container.ExitStatus
30 30
 	c.Lock()
31 31
 
32
+	cfg := daemon.config()
33
+
32 34
 	// Health checks will be automatically restarted if/when the
33 35
 	// container is started again.
34 36
 	daemon.stopHealthchecks(c)
... ...
@@ -99,7 +102,7 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
99 99
 	} else {
100 100
 		c.SetStopped(&exitStatus)
101 101
 		if !c.HasBeenManuallyRestarted {
102
-			defer daemon.autoRemove(c)
102
+			defer daemon.autoRemove(cfg, c)
103 103
 		}
104 104
 	}
105 105
 	defer c.Unlock() // needs to be called before autoRemove
... ...
@@ -117,7 +120,8 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
117 117
 				// But containerStart will use daemon.netController segment.
118 118
 				// So to avoid panic at startup process, here must wait util daemon restore done.
119 119
 				daemon.waitForStartupDone()
120
-				if err = daemon.containerStart(context.Background(), c, "", "", false); err != nil {
120
+				cfg := daemon.config() // Apply the most up-to-date daemon config to the restarted container.
121
+				if err = daemon.containerStart(context.Background(), cfg, c, "", "", false); err != nil {
121 122
 					logrus.Debugf("failed to restart container: %+v", err)
122 123
 				}
123 124
 			}
... ...
@@ -127,7 +131,7 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
127 127
 				daemon.setStateCounter(c)
128 128
 				c.CheckpointTo(daemon.containersReplica)
129 129
 				c.Unlock()
130
-				defer daemon.autoRemove(c)
130
+				defer daemon.autoRemove(cfg, c)
131 131
 				if err != restartmanager.ErrRestartCanceled {
132 132
 					logrus.Errorf("restartmanger wait error: %+v", err)
133 133
 				}
... ...
@@ -280,7 +284,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei
280 280
 	return nil
281 281
 }
282 282
 
283
-func (daemon *Daemon) autoRemove(c *container.Container) {
283
+func (daemon *Daemon) autoRemove(cfg *config.Config, c *container.Container) {
284 284
 	c.Lock()
285 285
 	ar := c.HostConfig.AutoRemove
286 286
 	c.Unlock()
... ...
@@ -288,7 +292,7 @@ func (daemon *Daemon) autoRemove(c *container.Container) {
288 288
 		return
289 289
 	}
290 290
 
291
-	err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true})
291
+	err := daemon.containerRm(cfg, c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true})
292 292
 	if err == nil {
293 293
 		return
294 294
 	}
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/docker/docker/api/types/network"
16 16
 	"github.com/docker/docker/container"
17 17
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
18
+	"github.com/docker/docker/daemon/config"
18 19
 	internalnetwork "github.com/docker/docker/daemon/network"
19 20
 	"github.com/docker/docker/errdefs"
20 21
 	"github.com/docker/docker/libnetwork"
... ...
@@ -160,7 +161,7 @@ func (daemon *Daemon) startIngressWorker() {
160 160
 			select {
161 161
 			case r := <-ingressJobsChannel:
162 162
 				if r.create != nil {
163
-					daemon.setupIngress(r.create, r.ip, ingressID)
163
+					daemon.setupIngress(daemon.config(), r.create, r.ip, ingressID)
164 164
 					ingressID = r.create.ID
165 165
 				} else {
166 166
 					daemon.releaseIngress(ingressID)
... ...
@@ -199,7 +200,7 @@ func (daemon *Daemon) ReleaseIngress() (<-chan struct{}, error) {
199 199
 	return done, nil
200 200
 }
201 201
 
202
-func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip net.IP, staleID string) {
202
+func (daemon *Daemon) setupIngress(cfg *config.Config, create *clustertypes.NetworkCreateRequest, ip net.IP, staleID string) {
203 203
 	controller := daemon.netController
204 204
 	controller.AgentInitWait()
205 205
 
... ...
@@ -207,7 +208,7 @@ func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip
207 207
 		daemon.releaseIngress(staleID)
208 208
 	}
209 209
 
210
-	if _, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true); err != nil {
210
+	if _, err := daemon.createNetwork(cfg, create.NetworkCreateRequest, create.ID, true); err != nil {
211 211
 		// If it is any other error other than already
212 212
 		// exists error log error and return.
213 213
 		if _, ok := err.(libnetwork.NetworkNameError); !ok {
... ...
@@ -277,16 +278,16 @@ func (daemon *Daemon) WaitForDetachment(ctx context.Context, networkName, networ
277 277
 
278 278
 // CreateManagedNetwork creates an agent network.
279 279
 func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
280
-	_, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true)
280
+	_, err := daemon.createNetwork(daemon.config(), create.NetworkCreateRequest, create.ID, true)
281 281
 	return err
282 282
 }
283 283
 
284 284
 // CreateNetwork creates a network with the given name, driver and other optional parameters
285 285
 func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
286
-	return daemon.createNetwork(create, "", false)
286
+	return daemon.createNetwork(daemon.config(), create, "", false)
287 287
 }
288 288
 
289
-func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
289
+func (daemon *Daemon) createNetwork(cfg *config.Config, create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
290 290
 	if runconfig.IsPreDefinedNetwork(create.Name) {
291 291
 		return nil, PredefinedNetworkError(create.Name)
292 292
 	}
... ...
@@ -319,7 +320,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
319 319
 	for k, v := range create.Options {
320 320
 		networkOptions[k] = v
321 321
 	}
322
-	if defaultOpts, ok := daemon.configStore.DefaultNetworkOpts[driver]; create.ConfigFrom == nil && ok {
322
+	if defaultOpts, ok := cfg.DefaultNetworkOpts[driver]; create.ConfigFrom == nil && ok {
323 323
 		for k, v := range defaultOpts {
324 324
 			if _, ok := networkOptions[k]; !ok {
325 325
 				logrus.WithFields(logrus.Fields{"driver": driver, "network": id, k: v}).Debug("Applying network default option")
... ...
@@ -36,15 +36,15 @@ import (
36 36
 
37 37
 const inContainerInitPath = "/sbin/" + dconfig.DefaultInitBinary
38 38
 
39
-// WithRlimits sets the container's rlimits along with merging the daemon's rlimits
40
-func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts {
39
+// withRlimits sets the container's rlimits along with merging the daemon's rlimits
40
+func withRlimits(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
41 41
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
42 42
 		var rlimits []specs.POSIXRlimit
43 43
 
44 44
 		// We want to leave the original HostConfig alone so make a copy here
45 45
 		hostConfig := *c.HostConfig
46 46
 		// Merge with the daemon defaults
47
-		daemon.mergeUlimits(&hostConfig)
47
+		daemon.mergeUlimits(&hostConfig, daemonCfg)
48 48
 		for _, ul := range hostConfig.Ulimits {
49 49
 			rlimits = append(rlimits, specs.POSIXRlimit{
50 50
 				Type: "RLIMIT_" + strings.ToUpper(ul.Name),
... ...
@@ -58,8 +58,8 @@ func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts {
58 58
 	}
59 59
 }
60 60
 
61
-// WithLibnetwork sets the libnetwork hook
62
-func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
61
+// withLibnetwork sets the libnetwork hook
62
+func withLibnetwork(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
63 63
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
64 64
 		if s.Hooks == nil {
65 65
 			s.Hooks = &specs.Hooks{}
... ...
@@ -72,7 +72,7 @@ func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
72 72
 					Path: target,
73 73
 					Args: []string{
74 74
 						"libnetwork-setkey",
75
-						"-exec-root=" + daemon.configStore.GetExecRoot(),
75
+						"-exec-root=" + daemonCfg.GetExecRoot(),
76 76
 						c.ID,
77 77
 						shortNetCtlrID,
78 78
 					},
... ...
@@ -83,11 +83,11 @@ func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
83 83
 	}
84 84
 }
85 85
 
86
-// WithRootless sets the spec to the rootless configuration
87
-func WithRootless(daemon *Daemon) coci.SpecOpts {
86
+// withRootless sets the spec to the rootless configuration
87
+func withRootless(daemon *Daemon, daemonCfg *dconfig.Config) coci.SpecOpts {
88 88
 	return func(_ context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
89 89
 		var v2Controllers []string
90
-		if daemon.getCgroupDriver() == cgroupSystemdDriver {
90
+		if cgroupDriver(daemonCfg) == cgroupSystemdDriver {
91 91
 			if cdcgroups.Mode() != cdcgroups.Unified {
92 92
 				return errors.New("rootless systemd driver doesn't support cgroup v1")
93 93
 			}
... ...
@@ -488,8 +488,8 @@ func inSlice(slice []string, s string) bool {
488 488
 	return false
489 489
 }
490 490
 
491
-// WithMounts sets the container's mounts
492
-func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
491
+// withMounts sets the container's mounts
492
+func withMounts(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
493 493
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) (err error) {
494 494
 		if err := daemon.setupContainerMountsRoot(c); err != nil {
495 495
 			return err
... ...
@@ -652,7 +652,7 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
652 652
 						return errors.New("mount options conflict: ReadOnlyNonRecursive && ReadOnlyForceRecursive")
653 653
 					}
654 654
 				}
655
-				if rroErr := daemon.supportsRecursivelyReadOnly(c.HostConfig.Runtime); rroErr != nil {
655
+				if rroErr := supportsRecursivelyReadOnly(daemonCfg, c.HostConfig.Runtime); rroErr != nil {
656 656
 					rro = false
657 657
 					if m.ReadOnlyForceRecursive {
658 658
 						return rroErr
... ...
@@ -673,7 +673,7 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
673 673
 			// "mount" when we bind-mount. The reason for this is that at the point
674 674
 			// when runc sets up the root filesystem, it is already inside a user
675 675
 			// namespace, and thus cannot change any flags that are locked.
676
-			if daemon.configStore.RemappedRoot != "" || userns.RunningInUserNS() {
676
+			if daemonCfg.RemappedRoot != "" || userns.RunningInUserNS() {
677 677
 				unprivOpts, err := getUnprivilegedMountFlags(m.Source)
678 678
 				if err != nil {
679 679
 					return err
... ...
@@ -732,8 +732,8 @@ func sysctlExists(s string) bool {
732 732
 	return err == nil
733 733
 }
734 734
 
735
-// WithCommonOptions sets common docker options
736
-func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
735
+// withCommonOptions sets common docker options
736
+func withCommonOptions(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
737 737
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
738 738
 		if c.BaseFS == "" && !daemon.UsesSnapshotter() {
739 739
 			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 {
762 762
 		// host namespace or another container's pid namespace where we already have an init
763 763
 		if c.HostConfig.PidMode.IsPrivate() {
764 764
 			if (c.HostConfig.Init != nil && *c.HostConfig.Init) ||
765
-				(c.HostConfig.Init == nil && daemon.configStore.Init) {
765
+				(c.HostConfig.Init == nil && daemonCfg.Init) {
766 766
 				s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...)
767
-				path, err := daemon.configStore.LookupInitPath() // this will fall back to DefaultInitBinary and return an absolute path
767
+				path, err := daemonCfg.LookupInitPath() // this will fall back to DefaultInitBinary and return an absolute path
768 768
 				if err != nil {
769 769
 					return err
770 770
 				}
... ...
@@ -790,7 +790,7 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
790 790
 		// joining an existing namespace, only if we create a new net namespace.
791 791
 		if c.HostConfig.NetworkMode.IsPrivate() {
792 792
 			// We cannot set up ping socket support in a user namespace
793
-			userNS := daemon.configStore.RemappedRoot != "" && c.HostConfig.UsernsMode.IsPrivate()
793
+			userNS := daemonCfg.RemappedRoot != "" && c.HostConfig.UsernsMode.IsPrivate()
794 794
 			if !userNS && !userns.RunningInUserNS() && sysctlExists("net.ipv4.ping_group_range") {
795 795
 				// allow unprivileged ICMP echo sockets without CAP_NET_RAW
796 796
 				s.Linux.Sysctl["net.ipv4.ping_group_range"] = "0 2147483647"
... ...
@@ -805,24 +805,24 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
805 805
 	}
806 806
 }
807 807
 
808
-// WithCgroups sets the container's cgroups
809
-func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
808
+// withCgroups sets the container's cgroups
809
+func withCgroups(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
810 810
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
811 811
 		var cgroupsPath string
812 812
 		scopePrefix := "docker"
813 813
 		parent := "/docker"
814
-		useSystemd := UsingSystemd(daemon.configStore)
814
+		useSystemd := UsingSystemd(daemonCfg)
815 815
 		if useSystemd {
816 816
 			parent = "system.slice"
817
-			if daemon.configStore.Rootless {
817
+			if daemonCfg.Rootless {
818 818
 				parent = "user.slice"
819 819
 			}
820 820
 		}
821 821
 
822 822
 		if c.HostConfig.CgroupParent != "" {
823 823
 			parent = c.HostConfig.CgroupParent
824
-		} else if daemon.configStore.CgroupParent != "" {
825
-			parent = daemon.configStore.CgroupParent
824
+		} else if daemonCfg.CgroupParent != "" {
825
+			parent = daemonCfg.CgroupParent
826 826
 		}
827 827
 
828 828
 		if useSystemd {
... ...
@@ -835,7 +835,7 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
835 835
 
836 836
 		// the rest is only needed for CPU RT controller
837 837
 
838
-		if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 {
838
+		if daemonCfg.CPURealtimePeriod == 0 && daemonCfg.CPURealtimeRuntime == 0 {
839 839
 			return nil
840 840
 		}
841 841
 
... ...
@@ -869,7 +869,7 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
869 869
 		}
870 870
 		mnt = filepath.Join(mnt, root)
871 871
 
872
-		if err := daemon.initCPURtController(mnt, parentPath); err != nil {
872
+		if err := daemon.initCPURtController(daemonCfg, mnt, parentPath); err != nil {
873 873
 			return errors.Wrap(err, "unable to init CPU RT controller")
874 874
 		}
875 875
 		return nil
... ...
@@ -1019,23 +1019,23 @@ func WithUser(c *container.Container) coci.SpecOpts {
1019 1019
 	}
1020 1020
 }
1021 1021
 
1022
-func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (retSpec *specs.Spec, err error) {
1022
+func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *dconfig.Config, c *container.Container) (retSpec *specs.Spec, err error) {
1023 1023
 	var (
1024 1024
 		opts []coci.SpecOpts
1025 1025
 		s    = oci.DefaultSpec()
1026 1026
 	)
1027 1027
 	opts = append(opts,
1028
-		WithCommonOptions(daemon, c),
1029
-		WithCgroups(daemon, c),
1028
+		withCommonOptions(daemon, daemonCfg, c),
1029
+		withCgroups(daemon, daemonCfg, c),
1030 1030
 		WithResources(c),
1031 1031
 		WithSysctls(c),
1032 1032
 		WithDevices(daemon, c),
1033
-		WithRlimits(daemon, c),
1033
+		withRlimits(daemon, daemonCfg, c),
1034 1034
 		WithNamespaces(daemon, c),
1035 1035
 		WithCapabilities(c),
1036 1036
 		WithSeccomp(daemon, c),
1037
-		WithMounts(daemon, c),
1038
-		WithLibnetwork(daemon, c),
1037
+		withMounts(daemon, daemonCfg, c),
1038
+		withLibnetwork(daemon, daemonCfg, c),
1039 1039
 		WithApparmor(c),
1040 1040
 		WithSelinux(c),
1041 1041
 		WithOOMScore(&c.HostConfig.OomScoreAdj),
... ...
@@ -1068,8 +1068,8 @@ func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (r
1068 1068
 	if c.HostConfig.ReadonlyPaths != nil {
1069 1069
 		opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
1070 1070
 	}
1071
-	if daemon.configStore.Rootless {
1072
-		opts = append(opts, WithRootless(daemon))
1071
+	if daemonCfg.Rootless {
1072
+		opts = append(opts, withRootless(daemon, daemonCfg))
1073 1073
 	}
1074 1074
 
1075 1075
 	var snapshotter, snapshotKey string
... ...
@@ -1096,14 +1096,14 @@ func clearReadOnly(m *specs.Mount) {
1096 1096
 }
1097 1097
 
1098 1098
 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
1099
-func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) {
1099
+func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig, daemonCfg *dconfig.Config) {
1100 1100
 	ulimits := c.Ulimits
1101 1101
 	// Merge ulimits with daemon defaults
1102 1102
 	ulIdx := make(map[string]struct{})
1103 1103
 	for _, ul := range ulimits {
1104 1104
 		ulIdx[ul.Name] = struct{}{}
1105 1105
 	}
1106
-	for name, ul := range daemon.configStore.Ulimits {
1106
+	for name, ul := range daemonCfg.Ulimits {
1107 1107
 		if _, exists := ulIdx[name]; !exists {
1108 1108
 			ulimits = append(ulimits, ul)
1109 1109
 		}
... ...
@@ -30,7 +30,6 @@ func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon {
30 30
 	d := &Daemon{
31 31
 		// some empty structs to avoid getting a panic
32 32
 		// caused by a null pointer dereference
33
-		configStore:   &config.Config{},
34 33
 		linkIndex:     newLinkIndex(),
35 34
 		netController: netController,
36 35
 		imageService:  &fakeImageService{},
... ...
@@ -83,7 +82,7 @@ func TestTmpfsDevShmNoDupMount(t *testing.T) {
83 83
 	d := setupFakeDaemon(t, c)
84 84
 	defer cleanupFakeContainer(c)
85 85
 
86
-	_, err := d.createSpec(context.TODO(), c)
86
+	_, err := d.createSpec(context.TODO(), &config.Config{}, c)
87 87
 	assert.Check(t, err)
88 88
 }
89 89
 
... ...
@@ -102,7 +101,7 @@ func TestIpcPrivateVsReadonly(t *testing.T) {
102 102
 	d := setupFakeDaemon(t, c)
103 103
 	defer cleanupFakeContainer(c)
104 104
 
105
-	s, err := d.createSpec(context.TODO(), c)
105
+	s, err := d.createSpec(context.TODO(), &config.Config{}, c)
106 106
 	assert.Check(t, err)
107 107
 
108 108
 	// Find the /dev/shm mount in ms, check it does not have ro
... ...
@@ -132,7 +131,7 @@ func TestSysctlOverride(t *testing.T) {
132 132
 	defer cleanupFakeContainer(c)
133 133
 
134 134
 	// Ensure that the implicit sysctl is set correctly.
135
-	s, err := d.createSpec(context.TODO(), c)
135
+	s, err := d.createSpec(context.TODO(), &config.Config{}, c)
136 136
 	assert.NilError(t, err)
137 137
 	assert.Equal(t, s.Hostname, "foobar")
138 138
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname)
... ...
@@ -148,15 +147,14 @@ func TestSysctlOverride(t *testing.T) {
148 148
 	assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname)
149 149
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
150 150
 
151
-	s, err = d.createSpec(context.TODO(), c)
151
+	s, err = d.createSpec(context.TODO(), &config.Config{}, c)
152 152
 	assert.NilError(t, err)
153 153
 	assert.Equal(t, s.Hostname, "foobar")
154 154
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"])
155 155
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
156 156
 
157 157
 	// Ensure the ping_group_range is not set on a daemon with user-namespaces enabled
158
-	d.configStore.RemappedRoot = "dummy:dummy"
159
-	s, err = d.createSpec(context.TODO(), c)
158
+	s, err = d.createSpec(context.TODO(), &config.Config{RemappedRoot: "dummy:dummy"}, c)
160 159
 	assert.NilError(t, err)
161 160
 	_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
162 161
 	assert.Assert(t, !ok)
... ...
@@ -164,7 +162,7 @@ func TestSysctlOverride(t *testing.T) {
164 164
 	// Ensure the ping_group_range is set on a container in "host" userns mode
165 165
 	// on a daemon with user-namespaces enabled
166 166
 	c.HostConfig.UsernsMode = "host"
167
-	s, err = d.createSpec(context.TODO(), c)
167
+	s, err = d.createSpec(context.TODO(), &config.Config{RemappedRoot: "dummy:dummy"}, c)
168 168
 	assert.NilError(t, err)
169 169
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
170 170
 }
... ...
@@ -184,7 +182,7 @@ func TestSysctlOverrideHost(t *testing.T) {
184 184
 	defer cleanupFakeContainer(c)
185 185
 
186 186
 	// Ensure that the implicit sysctl is not set
187
-	s, err := d.createSpec(context.TODO(), c)
187
+	s, err := d.createSpec(context.TODO(), &config.Config{}, c)
188 188
 	assert.NilError(t, err)
189 189
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "")
190 190
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "")
... ...
@@ -192,7 +190,7 @@ func TestSysctlOverrideHost(t *testing.T) {
192 192
 	// Set an explicit sysctl.
193 193
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
194 194
 
195
-	s, err = d.createSpec(context.TODO(), c)
195
+	s, err = d.createSpec(context.TODO(), &config.Config{}, c)
196 196
 	assert.NilError(t, err)
197 197
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
198 198
 }
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	containertypes "github.com/docker/docker/api/types/container"
13 13
 	imagetypes "github.com/docker/docker/api/types/image"
14 14
 	"github.com/docker/docker/container"
15
+	"github.com/docker/docker/daemon/config"
15 16
 	"github.com/docker/docker/errdefs"
16 17
 	"github.com/docker/docker/oci"
17 18
 	"github.com/docker/docker/pkg/sysinfo"
... ...
@@ -27,7 +28,7 @@ const (
27 27
 	credentialSpecFileLocation     = "CredentialSpecs"
28 28
 )
29 29
 
30
-func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (*specs.Spec, error) {
30
+func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *config.Config, c *container.Container) (*specs.Spec, error) {
31 31
 	img, err := daemon.imageService.GetImage(ctx, string(c.ImageID), imagetypes.GetImageOpts{})
32 32
 	if err != nil {
33 33
 		return nil, err
... ...
@@ -142,7 +143,7 @@ func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (*
142 142
 		return nil, errors.Wrapf(err, "container %s", c.ID)
143 143
 	}
144 144
 
145
-	dnsSearch := daemon.getDNSSearchSettings(c)
145
+	dnsSearch := daemon.getDNSSearchSettings(daemonCfg, c)
146 146
 
147 147
 	// Get endpoints for the libnetwork allocated networks to the container
148 148
 	var epList []string
... ...
@@ -404,7 +405,7 @@ func setResourcesInSpec(c *container.Container, s *specs.Spec, isHyperV bool) {
404 404
 
405 405
 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
406 406
 // It will do nothing on non-Linux platform
407
-func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) {
407
+func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig, daemonCfg *config.Config) {
408 408
 	return
409 409
 }
410 410
 
... ...
@@ -56,6 +56,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
56 56
 		return nil, err
57 57
 	}
58 58
 
59
+	cfg := daemon.config()
59 60
 	allContainers := daemon.List()
60 61
 	for _, c := range allContainers {
61 62
 		select {
... ...
@@ -77,7 +78,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
77 77
 				return nil, err
78 78
 			}
79 79
 			// TODO: sets RmLink to true?
80
-			err = daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
80
+			err = daemon.containerRm(cfg, c.ID, &types.ContainerRmConfig{})
81 81
 			if err != nil {
82 82
 				logrus.Warnf("failed to prune container %s: %v", c.ID, err)
83 83
 				continue
... ...
@@ -5,10 +5,55 @@ import (
5 5
 	"fmt"
6 6
 	"strconv"
7 7
 
8
-	"github.com/docker/docker/daemon/config"
8
+	"github.com/hashicorp/go-multierror"
9
+	"github.com/mitchellh/copystructure"
9 10
 	"github.com/sirupsen/logrus"
11
+
12
+	"github.com/docker/docker/daemon/config"
10 13
 )
11 14
 
15
+// reloadTxn is used to defer side effects of a config reload.
16
+type reloadTxn struct {
17
+	onCommit, onRollback []func() error
18
+}
19
+
20
+// OnCommit defers a function to be called when a config reload is being finalized.
21
+// The error returned from cb is purely informational.
22
+func (tx *reloadTxn) OnCommit(cb func() error) {
23
+	tx.onCommit = append(tx.onCommit, cb)
24
+}
25
+
26
+// OnRollback defers a function to be called when a config reload is aborted.
27
+// The error returned from cb is purely informational.
28
+func (tx *reloadTxn) OnRollback(cb func() error) {
29
+	tx.onCommit = append(tx.onRollback, cb)
30
+}
31
+
32
+func (tx *reloadTxn) run(cbs []func() error) error {
33
+	tx.onCommit = nil
34
+	tx.onRollback = nil
35
+
36
+	var res *multierror.Error
37
+	for _, cb := range cbs {
38
+		res = multierror.Append(res, cb())
39
+	}
40
+	return res.ErrorOrNil()
41
+}
42
+
43
+// Commit calls all functions registered with OnCommit.
44
+// Any errors returned by the functions are collated into a
45
+// *github.com/hashicorp/go-multierror.Error value.
46
+func (tx *reloadTxn) Commit() error {
47
+	return tx.run(tx.onCommit)
48
+}
49
+
50
+// Rollback calls all functions registered with OnRollback.
51
+// Any errors returned by the functions are collated into a
52
+// *github.com/hashicorp/go-multierror.Error value.
53
+func (tx *reloadTxn) Rollback() error {
54
+	return tx.run(tx.onRollback)
55
+}
56
+
12 57
 // Reload modifies the live daemon configuration from conf.
13 58
 // conf is assumed to be a validated configuration.
14 59
 //
... ...
@@ -24,66 +69,63 @@ import (
24 24
 // - Insecure registries
25 25
 // - Registry mirrors
26 26
 // - Daemon live restore
27
-func (daemon *Daemon) Reload(conf *config.Config) (err error) {
28
-	daemon.configStore.Lock()
29
-	attributes := map[string]string{}
27
+func (daemon *Daemon) Reload(conf *config.Config) error {
28
+	daemon.configReload.Lock()
29
+	defer daemon.configReload.Unlock()
30
+	copied, err := copystructure.Copy(daemon.config())
31
+	if err != nil {
32
+		return err
33
+	}
34
+	newCfg := copied.(*config.Config)
30 35
 
31
-	defer func() {
32
-		if err == nil {
33
-			jsonString, _ := json.Marshal(&struct {
34
-				*config.Config
35
-				config.Proxies `json:"proxies"`
36
-			}{
37
-				Config: daemon.configStore,
38
-				Proxies: config.Proxies{
39
-					HTTPProxy:  config.MaskCredentials(daemon.configStore.HTTPProxy),
40
-					HTTPSProxy: config.MaskCredentials(daemon.configStore.HTTPSProxy),
41
-					NoProxy:    config.MaskCredentials(daemon.configStore.NoProxy),
42
-				},
43
-			})
44
-			logrus.Infof("Reloaded configuration: %s", jsonString)
45
-		}
46
-		daemon.configStore.Unlock()
47
-		if err == nil {
48
-			daemon.LogDaemonEventWithAttributes("reload", attributes)
49
-		}
50
-	}()
36
+	attributes := map[string]string{}
51 37
 
52 38
 	// Ideally reloading should be transactional: the reload either completes
53 39
 	// successfully, or the daemon config and state are left untouched. We use a
54
-	// simplified two-phase commit protocol to achieve this. Any fallible reload
55
-	// operation is split into two phases. The first phase performs all the fallible
56
-	// operations without mutating daemon state and returns a closure: its second
57
-	// phase. The second phase applies the changes to the daemon state. If any
58
-	// first-phase returns an error, the reload transaction is "rolled back" by
59
-	// discarding the second-phase closures.
60
-
61
-	type TxnCommitter = func(attributes map[string]string)
62
-	var txns []TxnCommitter
63
-	for _, prepare := range []func(*config.Config) (TxnCommitter, error){
40
+	// two-phase commit protocol to achieve this. Any fallible reload operation is
41
+	// split into two phases. The first phase performs all the fallible operations
42
+	// and mutates the newCfg copy. The second phase atomically swaps newCfg into
43
+	// the live daemon configuration and executes any commit functions the first
44
+	// phase registered to apply the side effects. If any first-phase returns an
45
+	// error, the reload transaction is rolled back by discarding newCfg and
46
+	// executing any registered rollback functions.
47
+
48
+	var txn reloadTxn
49
+	for _, reload := range []func(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error{
64 50
 		daemon.reloadPlatform,
51
+		daemon.reloadDebug,
52
+		daemon.reloadMaxConcurrentDownloadsAndUploads,
53
+		daemon.reloadMaxDownloadAttempts,
54
+		daemon.reloadShutdownTimeout,
55
+		daemon.reloadFeatures,
56
+		daemon.reloadLabels,
65 57
 		daemon.reloadRegistryConfig,
58
+		daemon.reloadLiveRestore,
59
+		daemon.reloadNetworkDiagnosticPort,
66 60
 	} {
67
-		commit, err := prepare(conf)
68
-		if err != nil {
61
+		if err := reload(&txn, newCfg, conf, attributes); err != nil {
62
+			if rollbackErr := txn.Rollback(); rollbackErr != nil {
63
+				return multierror.Append(nil, err, rollbackErr)
64
+			}
69 65
 			return err
70 66
 		}
71
-		txns = append(txns, commit)
72 67
 	}
73 68
 
74
-	daemon.reloadDebug(conf, attributes)
75
-	daemon.reloadMaxConcurrentDownloadsAndUploads(conf, attributes)
76
-	daemon.reloadMaxDownloadAttempts(conf, attributes)
77
-	daemon.reloadShutdownTimeout(conf, attributes)
78
-	daemon.reloadFeatures(conf, attributes)
79
-	daemon.reloadLabels(conf, attributes)
80
-	daemon.reloadLiveRestore(conf, attributes)
81
-	daemon.reloadNetworkDiagnosticPort(conf, attributes)
82
-
83
-	for _, tx := range txns {
84
-		tx(attributes)
85
-	}
86
-	return nil
69
+	jsonString, _ := json.Marshal(&struct {
70
+		*config.Config
71
+		config.Proxies `json:"proxies"`
72
+	}{
73
+		Config: newCfg,
74
+		Proxies: config.Proxies{
75
+			HTTPProxy:  config.MaskCredentials(newCfg.HTTPProxy),
76
+			HTTPSProxy: config.MaskCredentials(newCfg.HTTPSProxy),
77
+			NoProxy:    config.MaskCredentials(newCfg.NoProxy),
78
+		},
79
+	})
80
+	logrus.Infof("Reloaded configuration: %s", jsonString)
81
+	daemon.configStore.Store(newCfg)
82
+	daemon.LogDaemonEventWithAttributes("reload", attributes)
83
+	return txn.Commit()
87 84
 }
88 85
 
89 86
 func marshalAttributeSlice(v []string) string {
... ...
@@ -99,145 +141,155 @@ func marshalAttributeSlice(v []string) string {
99 99
 
100 100
 // reloadDebug updates configuration with Debug option
101 101
 // and updates the passed attributes
102
-func (daemon *Daemon) reloadDebug(conf *config.Config, attributes map[string]string) {
102
+func (daemon *Daemon) reloadDebug(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
103 103
 	// update corresponding configuration
104 104
 	if conf.IsValueSet("debug") {
105
-		daemon.configStore.Debug = conf.Debug
105
+		newCfg.Debug = conf.Debug
106 106
 	}
107 107
 	// prepare reload event attributes with updatable configurations
108
-	attributes["debug"] = strconv.FormatBool(daemon.configStore.Debug)
108
+	attributes["debug"] = strconv.FormatBool(newCfg.Debug)
109
+	return nil
109 110
 }
110 111
 
111 112
 // reloadMaxConcurrentDownloadsAndUploads updates configuration with max concurrent
112 113
 // download and upload options and updates the passed attributes
113
-func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(conf *config.Config, attributes map[string]string) {
114
+func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
114 115
 	// We always "reset" as the cost is lightweight and easy to maintain.
115
-	daemon.configStore.MaxConcurrentDownloads = config.DefaultMaxConcurrentDownloads
116
-	daemon.configStore.MaxConcurrentUploads = config.DefaultMaxConcurrentUploads
116
+	newCfg.MaxConcurrentDownloads = config.DefaultMaxConcurrentDownloads
117
+	newCfg.MaxConcurrentUploads = config.DefaultMaxConcurrentUploads
117 118
 
118 119
 	if conf.IsValueSet("max-concurrent-downloads") && conf.MaxConcurrentDownloads != 0 {
119
-		daemon.configStore.MaxConcurrentDownloads = conf.MaxConcurrentDownloads
120
+		newCfg.MaxConcurrentDownloads = conf.MaxConcurrentDownloads
120 121
 	}
121 122
 	if conf.IsValueSet("max-concurrent-uploads") && conf.MaxConcurrentUploads != 0 {
122
-		daemon.configStore.MaxConcurrentUploads = conf.MaxConcurrentUploads
123
-	}
124
-	if daemon.imageService != nil {
125
-		daemon.imageService.UpdateConfig(
126
-			daemon.configStore.MaxConcurrentDownloads,
127
-			daemon.configStore.MaxConcurrentUploads,
128
-		)
123
+		newCfg.MaxConcurrentUploads = conf.MaxConcurrentUploads
129 124
 	}
125
+	txn.OnCommit(func() error {
126
+		if daemon.imageService != nil {
127
+			daemon.imageService.UpdateConfig(
128
+				newCfg.MaxConcurrentDownloads,
129
+				newCfg.MaxConcurrentUploads,
130
+			)
131
+		}
132
+		return nil
133
+	})
130 134
 
131 135
 	// prepare reload event attributes with updatable configurations
132
-	attributes["max-concurrent-downloads"] = strconv.Itoa(daemon.configStore.MaxConcurrentDownloads)
133
-	attributes["max-concurrent-uploads"] = strconv.Itoa(daemon.configStore.MaxConcurrentUploads)
136
+	attributes["max-concurrent-downloads"] = strconv.Itoa(newCfg.MaxConcurrentDownloads)
137
+	attributes["max-concurrent-uploads"] = strconv.Itoa(newCfg.MaxConcurrentUploads)
134 138
 	logrus.Debug("Reset Max Concurrent Downloads: ", attributes["max-concurrent-downloads"])
135 139
 	logrus.Debug("Reset Max Concurrent Uploads: ", attributes["max-concurrent-uploads"])
140
+	return nil
136 141
 }
137 142
 
138 143
 // reloadMaxDownloadAttempts updates configuration with max concurrent
139 144
 // download attempts when a connection is lost and updates the passed attributes
140
-func (daemon *Daemon) reloadMaxDownloadAttempts(conf *config.Config, attributes map[string]string) {
145
+func (daemon *Daemon) reloadMaxDownloadAttempts(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
141 146
 	// We always "reset" as the cost is lightweight and easy to maintain.
142
-	daemon.configStore.MaxDownloadAttempts = config.DefaultDownloadAttempts
147
+	newCfg.MaxDownloadAttempts = config.DefaultDownloadAttempts
143 148
 	if conf.IsValueSet("max-download-attempts") && conf.MaxDownloadAttempts != 0 {
144
-		daemon.configStore.MaxDownloadAttempts = conf.MaxDownloadAttempts
149
+		newCfg.MaxDownloadAttempts = conf.MaxDownloadAttempts
145 150
 	}
146 151
 
147 152
 	// prepare reload event attributes with updatable configurations
148
-	attributes["max-download-attempts"] = strconv.Itoa(daemon.configStore.MaxDownloadAttempts)
153
+	attributes["max-download-attempts"] = strconv.Itoa(newCfg.MaxDownloadAttempts)
149 154
 	logrus.Debug("Reset Max Download Attempts: ", attributes["max-download-attempts"])
155
+	return nil
150 156
 }
151 157
 
152 158
 // reloadShutdownTimeout updates configuration with daemon shutdown timeout option
153 159
 // and updates the passed attributes
154
-func (daemon *Daemon) reloadShutdownTimeout(conf *config.Config, attributes map[string]string) {
160
+func (daemon *Daemon) reloadShutdownTimeout(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
155 161
 	// update corresponding configuration
156 162
 	if conf.IsValueSet("shutdown-timeout") {
157
-		daemon.configStore.ShutdownTimeout = conf.ShutdownTimeout
158
-		logrus.Debugf("Reset Shutdown Timeout: %d", daemon.configStore.ShutdownTimeout)
163
+		newCfg.ShutdownTimeout = conf.ShutdownTimeout
164
+		logrus.Debugf("Reset Shutdown Timeout: %d", newCfg.ShutdownTimeout)
159 165
 	}
160 166
 
161 167
 	// prepare reload event attributes with updatable configurations
162
-	attributes["shutdown-timeout"] = strconv.Itoa(daemon.configStore.ShutdownTimeout)
168
+	attributes["shutdown-timeout"] = strconv.Itoa(newCfg.ShutdownTimeout)
169
+	return nil
163 170
 }
164 171
 
165 172
 // reloadLabels updates configuration with engine labels
166 173
 // and updates the passed attributes
167
-func (daemon *Daemon) reloadLabels(conf *config.Config, attributes map[string]string) {
174
+func (daemon *Daemon) reloadLabels(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
168 175
 	// update corresponding configuration
169 176
 	if conf.IsValueSet("labels") {
170
-		daemon.configStore.Labels = conf.Labels
177
+		newCfg.Labels = conf.Labels
171 178
 	}
172 179
 
173 180
 	// prepare reload event attributes with updatable configurations
174
-	attributes["labels"] = marshalAttributeSlice(daemon.configStore.Labels)
181
+	attributes["labels"] = marshalAttributeSlice(newCfg.Labels)
182
+	return nil
175 183
 }
176 184
 
177 185
 // reloadRegistryConfig updates the configuration with registry options
178 186
 // and updates the passed attributes.
179
-func (daemon *Daemon) reloadRegistryConfig(conf *config.Config) (func(map[string]string), error) {
187
+func (daemon *Daemon) reloadRegistryConfig(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
180 188
 	// Update corresponding configuration.
181
-	opts := daemon.configStore.ServiceOptions
182
-
183 189
 	if conf.IsValueSet("allow-nondistributable-artifacts") {
184
-		opts.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
190
+		newCfg.ServiceOptions.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
185 191
 	}
186 192
 	if conf.IsValueSet("insecure-registries") {
187
-		opts.InsecureRegistries = conf.InsecureRegistries
193
+		newCfg.ServiceOptions.InsecureRegistries = conf.InsecureRegistries
188 194
 	}
189 195
 	if conf.IsValueSet("registry-mirrors") {
190
-		opts.Mirrors = conf.Mirrors
196
+		newCfg.ServiceOptions.Mirrors = conf.Mirrors
191 197
 	}
192 198
 
193
-	commit, err := daemon.registryService.ReplaceConfig(opts)
199
+	commit, err := daemon.registryService.ReplaceConfig(newCfg.ServiceOptions)
194 200
 	if err != nil {
195
-		return nil, err
201
+		return err
196 202
 	}
203
+	txn.OnCommit(func() error { commit(); return nil })
204
+
205
+	attributes["allow-nondistributable-artifacts"] = marshalAttributeSlice(newCfg.ServiceOptions.AllowNondistributableArtifacts)
206
+	attributes["insecure-registries"] = marshalAttributeSlice(newCfg.ServiceOptions.InsecureRegistries)
207
+	attributes["registry-mirrors"] = marshalAttributeSlice(newCfg.ServiceOptions.Mirrors)
197 208
 
198
-	return func(attributes map[string]string) {
199
-		commit()
200
-		daemon.configStore.ServiceOptions = opts
201
-		// Prepare reload event attributes with updatable configurations.
202
-		attributes["allow-nondistributable-artifacts"] = marshalAttributeSlice(daemon.configStore.AllowNondistributableArtifacts)
203
-		attributes["insecure-registries"] = marshalAttributeSlice(daemon.configStore.InsecureRegistries)
204
-		attributes["registry-mirrors"] = marshalAttributeSlice(daemon.configStore.Mirrors)
205
-	}, nil
209
+	return nil
206 210
 }
207 211
 
208 212
 // reloadLiveRestore updates configuration with live restore option
209 213
 // and updates the passed attributes
210
-func (daemon *Daemon) reloadLiveRestore(conf *config.Config, attributes map[string]string) {
214
+func (daemon *Daemon) reloadLiveRestore(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
211 215
 	// update corresponding configuration
212 216
 	if conf.IsValueSet("live-restore") {
213
-		daemon.configStore.LiveRestoreEnabled = conf.LiveRestoreEnabled
217
+		newCfg.LiveRestoreEnabled = conf.LiveRestoreEnabled
214 218
 	}
215 219
 
216 220
 	// prepare reload event attributes with updatable configurations
217
-	attributes["live-restore"] = strconv.FormatBool(daemon.configStore.LiveRestoreEnabled)
221
+	attributes["live-restore"] = strconv.FormatBool(newCfg.LiveRestoreEnabled)
222
+	return nil
218 223
 }
219 224
 
220 225
 // reloadNetworkDiagnosticPort updates the network controller starting the diagnostic if the config is valid
221
-func (daemon *Daemon) reloadNetworkDiagnosticPort(conf *config.Config, attributes map[string]string) {
222
-	if conf == nil || daemon.netController == nil || !conf.IsValueSet("network-diagnostic-port") ||
223
-		conf.NetworkDiagnosticPort < 1 || conf.NetworkDiagnosticPort > 65535 {
224
-		// If there is no config make sure that the diagnostic is off
225
-		if daemon.netController != nil {
226
-			daemon.netController.StopDiagnostic()
226
+func (daemon *Daemon) reloadNetworkDiagnosticPort(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
227
+	txn.OnCommit(func() error {
228
+		if conf == nil || daemon.netController == nil || !conf.IsValueSet("network-diagnostic-port") ||
229
+			conf.NetworkDiagnosticPort < 1 || conf.NetworkDiagnosticPort > 65535 {
230
+			// If there is no config make sure that the diagnostic is off
231
+			if daemon.netController != nil {
232
+				daemon.netController.StopDiagnostic()
233
+			}
234
+			return nil
227 235
 		}
228
-		return
229
-	}
230
-	// Enable the network diagnostic if the flag is set with a valid port within the range
231
-	logrus.WithFields(logrus.Fields{"port": conf.NetworkDiagnosticPort, "ip": "127.0.0.1"}).Warn("Starting network diagnostic server")
232
-	daemon.netController.StartDiagnostic(conf.NetworkDiagnosticPort)
236
+		// Enable the network diagnostic if the flag is set with a valid port within the range
237
+		logrus.WithFields(logrus.Fields{"port": conf.NetworkDiagnosticPort, "ip": "127.0.0.1"}).Warn("Starting network diagnostic server")
238
+		daemon.netController.StartDiagnostic(conf.NetworkDiagnosticPort)
239
+		return nil
240
+	})
241
+	return nil
233 242
 }
234 243
 
235 244
 // reloadFeatures updates configuration with enabled/disabled features
236
-func (daemon *Daemon) reloadFeatures(conf *config.Config, attributes map[string]string) {
245
+func (daemon *Daemon) reloadFeatures(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
237 246
 	// update corresponding configuration
238 247
 	// note that we allow features option to be entirely unset
239
-	daemon.configStore.Features = conf.Features
248
+	newCfg.Features = conf.Features
240 249
 
241 250
 	// prepare reload event attributes with updatable configurations
242
-	attributes["features"] = fmt.Sprintf("%v", daemon.configStore.Features)
251
+	attributes["features"] = fmt.Sprintf("%v", newCfg.Features)
252
+	return nil
243 253
 }
... ...
@@ -22,12 +22,12 @@ func muteLogs() {
22 22
 func newDaemonForReloadT(t *testing.T, cfg *config.Config) *Daemon {
23 23
 	t.Helper()
24 24
 	daemon := &Daemon{
25
-		configStore:  cfg,
26 25
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
27 26
 	}
28 27
 	var err error
29 28
 	daemon.registryService, err = registry.NewService(registry.ServiceOptions{})
30 29
 	assert.Assert(t, err)
30
+	daemon.configStore.Store(cfg)
31 31
 	return daemon
32 32
 }
33 33
 
... ...
@@ -52,7 +52,7 @@ func TestDaemonReloadLabels(t *testing.T) {
52 52
 		t.Fatal(err)
53 53
 	}
54 54
 
55
-	label := daemon.configStore.Labels[0]
55
+	label := daemon.config().Labels[0]
56 56
 	if label != "foo:baz" {
57 57
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
58 58
 	}
... ...
@@ -131,8 +131,6 @@ func TestDaemonReloadMirrors(t *testing.T) {
131 131
 		t.Fatal(err)
132 132
 	}
133 133
 
134
-	daemon.configStore = &config.Config{}
135
-
136 134
 	type pair struct {
137 135
 		valid   bool
138 136
 		mirrors []string
... ...
@@ -234,8 +232,6 @@ func TestDaemonReloadInsecureRegistries(t *testing.T) {
234 234
 		t.Fatal(err)
235 235
 	}
236 236
 
237
-	daemon.configStore = &config.Config{}
238
-
239 237
 	insecureRegistries := []string{
240 238
 		"127.0.0.0/8",         // this will be kept
241 239
 		"10.10.1.11:5000",     // this will be kept
... ...
@@ -335,11 +331,11 @@ func TestDaemonReloadNotAffectOthers(t *testing.T) {
335 335
 		t.Fatal(err)
336 336
 	}
337 337
 
338
-	label := daemon.configStore.Labels[0]
338
+	label := daemon.config().Labels[0]
339 339
 	if label != "foo:baz" {
340 340
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
341 341
 	}
342
-	debug := daemon.configStore.Debug
342
+	debug := daemon.config().Debug
343 343
 	if !debug {
344 344
 		t.Fatal("Expected debug 'enabled', got 'disabled'")
345 345
 	}
... ...
@@ -360,7 +356,7 @@ func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
360 360
 		},
361 361
 	}
362 362
 
363
-	netOptions, err := daemon.networkOptions(nil, nil)
363
+	netOptions, err := daemon.networkOptions(&config.Config{}, nil, nil)
364 364
 	if err != nil {
365 365
 		t.Fatal(err)
366 366
 	}
... ...
@@ -6,62 +6,46 @@ import (
6 6
 	"bytes"
7 7
 	"strconv"
8 8
 
9
-	"github.com/docker/docker/api/types"
10 9
 	"github.com/docker/docker/daemon/config"
11 10
 )
12 11
 
13 12
 // reloadPlatform updates configuration with platform specific options
14 13
 // and updates the passed attributes
15
-func (daemon *Daemon) reloadPlatform(conf *config.Config) (func(attributes map[string]string), error) {
16
-	var txns []func()
17
-
14
+func (daemon *Daemon) reloadPlatform(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
15
+	if conf.DefaultRuntime != "" {
16
+		newCfg.DefaultRuntime = conf.DefaultRuntime
17
+	}
18 18
 	if conf.IsValueSet("runtimes") {
19
-		// Always set the default one
20
-		conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: config.DefaultRuntimeBinary}
21
-		if err := daemon.initRuntimes(conf.Runtimes); err != nil {
22
-			return nil, err
23
-		}
24
-		txns = append(txns, func() {
25
-			daemon.configStore.Runtimes = conf.Runtimes
26
-		})
19
+		newCfg.Runtimes = conf.Runtimes
20
+		txn.OnCommit(func() error { return daemon.initRuntimes(newCfg) })
27 21
 	}
22
+	configureRuntimes(newCfg)
28 23
 
29
-	if conf.DefaultRuntime != "" {
30
-		txns = append(txns, func() {
31
-			daemon.configStore.DefaultRuntime = conf.DefaultRuntime
32
-		})
24
+	if conf.IsValueSet("default-shm-size") {
25
+		newCfg.ShmSize = conf.ShmSize
33 26
 	}
34 27
 
35
-	return func(attributes map[string]string) {
36
-		for _, commit := range txns {
37
-			commit()
38
-		}
39
-
40
-		if conf.IsValueSet("default-shm-size") {
41
-			daemon.configStore.ShmSize = conf.ShmSize
42
-		}
43
-
44
-		if conf.CgroupNamespaceMode != "" {
45
-			daemon.configStore.CgroupNamespaceMode = conf.CgroupNamespaceMode
46
-		}
28
+	if conf.CgroupNamespaceMode != "" {
29
+		newCfg.CgroupNamespaceMode = conf.CgroupNamespaceMode
30
+	}
47 31
 
48
-		if conf.IpcMode != "" {
49
-			daemon.configStore.IpcMode = conf.IpcMode
50
-		}
32
+	if conf.IpcMode != "" {
33
+		newCfg.IpcMode = conf.IpcMode
34
+	}
51 35
 
52
-		// Update attributes
53
-		var runtimeList bytes.Buffer
54
-		for name, rt := range daemon.configStore.Runtimes {
55
-			if runtimeList.Len() > 0 {
56
-				runtimeList.WriteRune(' ')
57
-			}
58
-			runtimeList.WriteString(name + ":" + rt.Path)
36
+	// Update attributes
37
+	var runtimeList bytes.Buffer
38
+	for name, rt := range newCfg.Runtimes {
39
+		if runtimeList.Len() > 0 {
40
+			runtimeList.WriteRune(' ')
59 41
 		}
42
+		runtimeList.WriteString(name + ":" + rt.Path)
43
+	}
60 44
 
61
-		attributes["runtimes"] = runtimeList.String()
62
-		attributes["default-runtime"] = daemon.configStore.DefaultRuntime
63
-		attributes["default-shm-size"] = strconv.FormatInt(int64(daemon.configStore.ShmSize), 10)
64
-		attributes["default-ipc-mode"] = daemon.configStore.IpcMode
65
-		attributes["default-cgroupns-mode"] = daemon.configStore.CgroupNamespaceMode
66
-	}, nil
45
+	attributes["runtimes"] = runtimeList.String()
46
+	attributes["default-runtime"] = newCfg.DefaultRuntime
47
+	attributes["default-shm-size"] = strconv.FormatInt(int64(newCfg.ShmSize), 10)
48
+	attributes["default-ipc-mode"] = newCfg.IpcMode
49
+	attributes["default-cgroupns-mode"] = newCfg.CgroupNamespaceMode
50
+	return nil
67 51
 }
... ...
@@ -4,6 +4,6 @@ import "github.com/docker/docker/daemon/config"
4 4
 
5 5
 // reloadPlatform updates configuration with platform specific options
6 6
 // and updates the passed attributes
7
-func (daemon *Daemon) reloadPlatform(conf *config.Config) (func(attributes map[string]string), error) {
8
-	return func(map[string]string) {}, nil
7
+func (daemon *Daemon) reloadPlatform(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
8
+	return nil
9 9
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 
7 7
 	containertypes "github.com/docker/docker/api/types/container"
8 8
 	"github.com/docker/docker/container"
9
+	"github.com/docker/docker/daemon/config"
9 10
 )
10 11
 
11 12
 // ContainerRestart stops and starts a container. It attempts to
... ...
@@ -19,7 +20,7 @@ func (daemon *Daemon) ContainerRestart(ctx context.Context, name string, options
19 19
 	if err != nil {
20 20
 		return err
21 21
 	}
22
-	err = daemon.containerRestart(ctx, ctr, options)
22
+	err = daemon.containerRestart(ctx, daemon.config(), ctr, options)
23 23
 	if err != nil {
24 24
 		return fmt.Errorf("Cannot restart container %s: %v", name, err)
25 25
 	}
... ...
@@ -30,7 +31,7 @@ func (daemon *Daemon) ContainerRestart(ctx context.Context, name string, options
30 30
 // container. When stopping, wait for the given duration in seconds to
31 31
 // gracefully stop, before forcefully terminating the container. If
32 32
 // given a negative duration, wait forever for a graceful stop.
33
-func (daemon *Daemon) containerRestart(ctx context.Context, container *container.Container, options containertypes.StopOptions) error {
33
+func (daemon *Daemon) containerRestart(ctx context.Context, daemonCfg *config.Config, container *container.Container, options containertypes.StopOptions) error {
34 34
 	// Determine isolation. If not specified in the hostconfig, use daemon default.
35 35
 	actualIsolation := container.HostConfig.Isolation
36 36
 	if containertypes.Isolation.IsDefault(actualIsolation) {
... ...
@@ -61,7 +62,7 @@ func (daemon *Daemon) containerRestart(ctx context.Context, container *container
61 61
 		}
62 62
 	}
63 63
 
64
-	if err := daemon.containerStart(ctx, container, "", "", true); err != nil {
64
+	if err := daemon.containerStart(ctx, daemonCfg, container, "", "", true); err != nil {
65 65
 		return err
66 66
 	}
67 67
 
... ...
@@ -51,15 +51,15 @@ func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimCon
51 51
 }
52 52
 
53 53
 func (daemon *Daemon) loadRuntimes() error {
54
-	return daemon.initRuntimes(daemon.configStore.Runtimes)
54
+	return daemon.initRuntimes(daemon.config())
55 55
 }
56 56
 
57
-func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) {
58
-	runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes")
57
+func (daemon *Daemon) initRuntimes(cfg *config.Config) (err error) {
58
+	runtimeDir := filepath.Join(cfg.Root, "runtimes")
59 59
 	runtimeOldDir := runtimeDir + "-old"
60 60
 	// Remove old temp directory if any
61 61
 	os.RemoveAll(runtimeOldDir)
62
-	tmpDir, err := os.MkdirTemp(daemon.configStore.Root, "gen-runtimes")
62
+	tmpDir, err := os.MkdirTemp(cfg.Root, "gen-runtimes")
63 63
 	if err != nil {
64 64
 		return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
65 65
 	}
... ...
@@ -91,8 +91,8 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
91 91
 		}
92 92
 	}()
93 93
 
94
-	for name := range runtimes {
95
-		rt := runtimes[name]
94
+	for name := range cfg.Runtimes {
95
+		rt := cfg.Runtimes[name]
96 96
 		if rt.Path == "" && rt.Type == "" {
97 97
 			return errors.Errorf("runtime %s: either a runtimeType or a path must be configured", name)
98 98
 		}
... ...
@@ -111,7 +111,7 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
111 111
 					return err
112 112
 				}
113 113
 			}
114
-			rt.ShimConfig = defaultV2ShimConfig(daemon.configStore, daemon.rewriteRuntimePath(name, rt.Path, rt.Args))
114
+			rt.ShimConfig = defaultV2ShimConfig(cfg, daemon.rewriteRuntimePath(cfg, name, rt.Path, rt.Args))
115 115
 			var featuresStderr bytes.Buffer
116 116
 			featuresCmd := exec.Command(rt.Path, append(rt.Args, "features")...)
117 117
 			featuresCmd.Stderr = &featuresStderr
... ...
@@ -139,7 +139,7 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
139 139
 				}
140 140
 			}
141 141
 		}
142
-		runtimes[name] = rt
142
+		cfg.Runtimes[name] = rt
143 143
 	}
144 144
 	return nil
145 145
 }
... ...
@@ -147,16 +147,16 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
147 147
 // rewriteRuntimePath is used for runtimes which have custom arguments supplied.
148 148
 // This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments.
149 149
 // To support this case, the daemon wraps the specified runtime in a script that passes through those arguments.
150
-func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) string {
150
+func (daemon *Daemon) rewriteRuntimePath(cfg *config.Config, name, p string, args []string) string {
151 151
 	if len(args) == 0 {
152 152
 		return p
153 153
 	}
154 154
 
155
-	return filepath.Join(daemon.configStore.Root, "runtimes", name)
155
+	return filepath.Join(cfg.Root, "runtimes", name)
156 156
 }
157 157
 
158
-func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
159
-	rt := daemon.configStore.GetRuntime(name)
158
+func (daemon *Daemon) getRuntime(cfg *config.Config, name string) (shim string, opts interface{}, err error) {
159
+	rt := cfg.GetRuntime(name)
160 160
 	if rt == nil {
161 161
 		if !config.IsPermissibleC8dRuntimeName(name) {
162 162
 			return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
... ...
@@ -86,11 +86,13 @@ func TestInitRuntimes_InvalidConfigs(t *testing.T) {
86 86
 		t.Run(tt.name, func(t *testing.T) {
87 87
 			cfg, err := config.New()
88 88
 			assert.NilError(t, err)
89
-			d := &Daemon{configStore: cfg}
90
-			d.configStore.Root = t.TempDir()
91
-			assert.Assert(t, os.Mkdir(filepath.Join(d.configStore.Root, "runtimes"), 0700))
89
+			cfg.Root = t.TempDir()
90
+			cfg.Runtimes["myruntime"] = tt.runtime
91
+			d := &Daemon{}
92
+			d.configStore.Store(cfg)
93
+			assert.Assert(t, os.Mkdir(filepath.Join(d.config().Root, "runtimes"), 0700))
92 94
 
93
-			err = d.initRuntimes(map[string]types.Runtime{"myruntime": tt.runtime})
95
+			err = d.initRuntimes(d.config())
94 96
 			assert.Check(t, is.ErrorContains(err, tt.expectErr))
95 97
 		})
96 98
 	}
... ...
@@ -124,20 +126,22 @@ func TestGetRuntime(t *testing.T) {
124 124
 	cfg, err := config.New()
125 125
 	assert.NilError(t, err)
126 126
 
127
-	d := &Daemon{configStore: cfg}
128
-	d.configStore.Root = t.TempDir()
129
-	assert.Assert(t, os.Mkdir(filepath.Join(d.configStore.Root, "runtimes"), 0700))
130
-	d.configStore.Runtimes = map[string]types.Runtime{
127
+	cfg.Root = t.TempDir()
128
+	assert.Assert(t, os.Mkdir(filepath.Join(cfg.Root, "runtimes"), 0700))
129
+	cfg.Runtimes = map[string]types.Runtime{
131 130
 		configuredRtName:         configuredRuntime,
132 131
 		rtWithArgsName:           rtWithArgs,
133 132
 		shimWithOptsName:         shimWithOpts,
134 133
 		shimAliasName:            shimAlias,
135 134
 		configuredShimByPathName: configuredShimByPath,
136 135
 	}
137
-	configureRuntimes(d.configStore)
136
+	configureRuntimes(cfg)
137
+
138
+	d := &Daemon{}
139
+	d.configStore.Store(cfg)
138 140
 	assert.Assert(t, d.loadRuntimes())
139 141
 
140
-	stockRuntime, ok := d.configStore.Runtimes[config.StockRuntimeName]
142
+	stockRuntime, ok := cfg.Runtimes[config.StockRuntimeName]
141 143
 	assert.Assert(t, ok, "stock runtime could not be found (test needs to be updated)")
142 144
 
143 145
 	configdOpts := *stockRuntime.ShimConfig.Opts.(*v2runcoptions.Options)
... ...
@@ -199,8 +203,9 @@ func TestGetRuntime(t *testing.T) {
199 199
 			runtime:  rtWithArgsName,
200 200
 			wantShim: stockRuntime.ShimConfig.Binary,
201 201
 			wantOpts: defaultV2ShimConfig(
202
-				d.configStore,
202
+				d.config(),
203 203
 				d.rewriteRuntimePath(
204
+					d.config(),
204 205
 					rtWithArgsName,
205 206
 					rtWithArgs.Path,
206 207
 					rtWithArgs.Args)).Opts,
... ...
@@ -224,7 +229,7 @@ func TestGetRuntime(t *testing.T) {
224 224
 	} {
225 225
 		tt := tt
226 226
 		t.Run(tt.name, func(t *testing.T) {
227
-			gotShim, gotOpts, err := d.getRuntime(tt.runtime)
227
+			gotShim, gotOpts, err := d.getRuntime(cfg, tt.runtime)
228 228
 			assert.Check(t, is.Equal(gotShim, tt.wantShim))
229 229
 			assert.Check(t, is.DeepEqual(gotOpts, tt.wantOpts))
230 230
 			if tt.wantShim != "" {
... ...
@@ -2,8 +2,10 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"errors"
5
+
6
+	"github.com/docker/docker/daemon/config"
5 7
 )
6 8
 
7
-func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
9
+func (daemon *Daemon) getRuntime(cfg *config.Config, name string) (shim string, opts interface{}, err error) {
8 10
 	return "", nil, errors.New("not implemented")
9 11
 }
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/docker/docker/api/types"
10 10
 	containertypes "github.com/docker/docker/api/types/container"
11 11
 	"github.com/docker/docker/container"
12
+	"github.com/docker/docker/daemon/config"
12 13
 	"github.com/docker/docker/errdefs"
13 14
 	"github.com/docker/docker/libcontainerd"
14 15
 	"github.com/pkg/errors"
... ...
@@ -17,7 +18,8 @@ import (
17 17
 
18 18
 // ContainerStart starts a container.
19 19
 func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
20
-	if checkpoint != "" && !daemon.HasExperimental() {
20
+	daemonCfg := daemon.config()
21
+	if checkpoint != "" && !daemonCfg.Experimental {
21 22
 		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
22 23
 	}
23 24
 
... ...
@@ -55,7 +57,7 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
55 55
 		if hostConfig != nil {
56 56
 			logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
57 57
 			oldNetworkMode := ctr.HostConfig.NetworkMode
58
-			if err := daemon.setSecurityOptions(ctr, hostConfig); err != nil {
58
+			if err := daemon.setSecurityOptions(daemonCfg, ctr, hostConfig); err != nil {
59 59
 				return errdefs.InvalidParameter(err)
60 60
 			}
61 61
 			if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
... ...
@@ -83,24 +85,24 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
83 83
 
84 84
 	// check if hostConfig is in line with the current system settings.
85 85
 	// It may happen cgroups are umounted or the like.
86
-	if _, err = daemon.verifyContainerSettings(ctr.HostConfig, nil, false); err != nil {
86
+	if _, err = daemon.verifyContainerSettings(daemonCfg, ctr.HostConfig, nil, false); err != nil {
87 87
 		return errdefs.InvalidParameter(err)
88 88
 	}
89 89
 	// Adapt for old containers in case we have updates in this function and
90 90
 	// old containers never have chance to call the new function in create stage.
91 91
 	if hostConfig != nil {
92
-		if err := daemon.adaptContainerSettings(ctr.HostConfig, false); err != nil {
92
+		if err := daemon.adaptContainerSettings(daemonCfg, ctr.HostConfig, false); err != nil {
93 93
 			return errdefs.InvalidParameter(err)
94 94
 		}
95 95
 	}
96
-	return daemon.containerStart(ctx, ctr, checkpoint, checkpointDir, true)
96
+	return daemon.containerStart(ctx, daemonCfg, ctr, checkpoint, checkpointDir, true)
97 97
 }
98 98
 
99 99
 // containerStart prepares the container to run by setting up everything the
100 100
 // container needs, such as storage and networking, as well as links
101 101
 // between containers. The container is left waiting for a signal to
102 102
 // begin running.
103
-func (daemon *Daemon) containerStart(ctx context.Context, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
103
+func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *config.Config, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
104 104
 	start := time.Now()
105 105
 	container.Lock()
106 106
 	defer container.Unlock()
... ...
@@ -136,7 +138,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
136 136
 			// if containers AutoRemove flag is set, remove it after clean up
137 137
 			if container.HostConfig.AutoRemove {
138 138
 				container.Unlock()
139
-				if err := daemon.ContainerRm(container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
139
+				if err := daemon.containerRm(daemonCfg, container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
140 140
 					logrus.Errorf("can't remove container %s: %v", container.ID, err)
141 141
 				}
142 142
 				container.Lock()
... ...
@@ -148,11 +150,11 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
148 148
 		return err
149 149
 	}
150 150
 
151
-	if err := daemon.initializeNetworking(container); err != nil {
151
+	if err := daemon.initializeNetworking(daemonCfg, container); err != nil {
152 152
 		return err
153 153
 	}
154 154
 
155
-	spec, err := daemon.createSpec(ctx, container)
155
+	spec, err := daemon.createSpec(ctx, daemonCfg, container)
156 156
 	if err != nil {
157 157
 		return errdefs.System(err)
158 158
 	}
... ...
@@ -173,7 +175,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
173 173
 		}
174 174
 	}
175 175
 
176
-	shim, createOptions, err := daemon.getLibcontainerdCreateOptions(container)
176
+	shim, createOptions, err := daemon.getLibcontainerdCreateOptions(daemonCfg, container)
177 177
 	if err != nil {
178 178
 		return err
179 179
 	}
... ...
@@ -4,17 +4,18 @@ package daemon // import "github.com/docker/docker/daemon"
4 4
 
5 5
 import (
6 6
 	"github.com/docker/docker/container"
7
+	"github.com/docker/docker/daemon/config"
7 8
 )
8 9
 
9 10
 // getLibcontainerdCreateOptions callers must hold a lock on the container
10
-func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (string, interface{}, error) {
11
+func (daemon *Daemon) getLibcontainerdCreateOptions(daemonCfg *config.Config, container *container.Container) (string, interface{}, error) {
11 12
 	// Ensure a runtime has been assigned to this container
12 13
 	if container.HostConfig.Runtime == "" {
13
-		container.HostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
14
+		container.HostConfig.Runtime = daemonCfg.DefaultRuntime
14 15
 		container.CheckpointTo(daemon.containersReplica)
15 16
 	}
16 17
 
17
-	binary, opts, err := daemon.getRuntime(container.HostConfig.Runtime)
18
+	binary, opts, err := daemon.getRuntime(daemonCfg, container.HostConfig.Runtime)
18 19
 	if err != nil {
19 20
 		return "", nil, setExitCodeFromError(container.SetExitCode, err)
20 21
 	}
... ...
@@ -3,10 +3,11 @@ package daemon // import "github.com/docker/docker/daemon"
3 3
 import (
4 4
 	"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
5 5
 	"github.com/docker/docker/container"
6
+	"github.com/docker/docker/daemon/config"
6 7
 	"github.com/docker/docker/pkg/system"
7 8
 )
8 9
 
9
-func (daemon *Daemon) getLibcontainerdCreateOptions(_ *container.Container) (string, interface{}, error) {
10
+func (daemon *Daemon) getLibcontainerdCreateOptions(*config.Config, *container.Container) (string, interface{}, error) {
10 11
 	if system.ContainerdRuntimeSupported() {
11 12
 		opts := &options.Options{}
12 13
 		return "io.containerd.runhcs.v1", opts, nil
... ...
@@ -13,7 +13,8 @@ import (
13 13
 func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error) {
14 14
 	var warnings []string
15 15
 
16
-	warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true)
16
+	daemonCfg := daemon.config()
17
+	warnings, err := daemon.verifyContainerSettings(daemonCfg, hostConfig, nil, true)
17 18
 	if err != nil {
18 19
 		return container.ContainerUpdateOKBody{Warnings: warnings}, errdefs.InvalidParameter(err)
19 20
 	}
... ...
@@ -58,6 +58,7 @@ require (
58 58
 	github.com/klauspost/compress v1.16.3
59 59
 	github.com/miekg/dns v1.1.43
60 60
 	github.com/mistifyio/go-zfs/v3 v3.0.1
61
+	github.com/mitchellh/copystructure v1.2.0
61 62
 	github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f
62 63
 	github.com/moby/ipvs v1.1.0
63 64
 	github.com/moby/locker v1.0.1
... ...
@@ -154,6 +155,9 @@ require (
154 154
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
155 155
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
156 156
 	github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
157
+	github.com/mitchellh/reflectwalk v1.0.2 // indirect
158
+	github.com/onsi/ginkgo/v2 v2.1.4 // indirect
159
+	github.com/onsi/gomega v1.20.1 // indirect
157 160
 	github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
158 161
 	github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170 // indirect
159 162
 	github.com/philhofer/fwd v1.1.2 // indirect
... ...
@@ -1034,6 +1034,8 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go
1034 1034
 github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
1035 1035
 github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
1036 1036
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
1037
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
1038
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
1037 1039
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
1038 1040
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
1039 1041
 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
1048 1048
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
1049 1049
 github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
1050 1050
 github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
1051
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
1052
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
1051 1053
 github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
1052 1054
 github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
1053 1055
 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
1121 1121
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
1122 1122
 github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
1123 1123
 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
1124
-github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
1125 1124
 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
1125
+github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
1126
+github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
1126 1127
 github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
1127 1128
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
1128 1129
 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
1133 1133
 github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
1134 1134
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
1135 1135
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
1136
-github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
1137 1136
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
1137
+github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
1138
+github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
1138 1139
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
1139 1140
 github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
1140 1141
 github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
1141 1142
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+The MIT License (MIT)
1
+
2
+Copyright (c) 2014 Mitchell Hashimoto
3
+
4
+Permission is hereby granted, free of charge, to any person obtaining a copy
5
+of this software and associated documentation files (the "Software"), to deal
6
+in the Software without restriction, including without limitation the rights
7
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+copies of the Software, and to permit persons to whom the Software is
9
+furnished to do so, subject to the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be included in
12
+all copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+THE SOFTWARE.
0 21
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+# copystructure
1
+
2
+copystructure is a Go library for deep copying values in Go.
3
+
4
+This allows you to copy Go values that may contain reference values
5
+such as maps, slices, or pointers, and copy their data as well instead
6
+of just their references.
7
+
8
+## Installation
9
+
10
+Standard `go get`:
11
+
12
+```
13
+$ go get github.com/mitchellh/copystructure
14
+```
15
+
16
+## Usage & Example
17
+
18
+For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/copystructure).
19
+
20
+The `Copy` function has examples associated with it there.
0 21
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+package copystructure
1
+
2
+import (
3
+	"reflect"
4
+	"time"
5
+)
6
+
7
+func init() {
8
+	Copiers[reflect.TypeOf(time.Time{})] = timeCopier
9
+}
10
+
11
+func timeCopier(v interface{}) (interface{}, error) {
12
+	// Just... copy it.
13
+	return v.(time.Time), nil
14
+}
0 15
new file mode 100644
... ...
@@ -0,0 +1,631 @@
0
+package copystructure
1
+
2
+import (
3
+	"errors"
4
+	"reflect"
5
+	"sync"
6
+
7
+	"github.com/mitchellh/reflectwalk"
8
+)
9
+
10
+const tagKey = "copy"
11
+
12
+// Copy returns a deep copy of v.
13
+//
14
+// Copy is unable to copy unexported fields in a struct (lowercase field names).
15
+// Unexported fields can't be reflected by the Go runtime and therefore
16
+// copystructure can't perform any data copies.
17
+//
18
+// For structs, copy behavior can be controlled with struct tags. For example:
19
+//
20
+//   struct {
21
+//     Name string
22
+//     Data *bytes.Buffer `copy:"shallow"`
23
+//   }
24
+//
25
+// The available tag values are:
26
+//
27
+// * "ignore" - The field will be ignored, effectively resulting in it being
28
+//   assigned the zero value in the copy.
29
+//
30
+// * "shallow" - The field will be be shallow copied. This means that references
31
+//   values such as pointers, maps, slices, etc. will be directly assigned
32
+//   versus deep copied.
33
+//
34
+func Copy(v interface{}) (interface{}, error) {
35
+	return Config{}.Copy(v)
36
+}
37
+
38
+// CopierFunc is a function that knows how to deep copy a specific type.
39
+// Register these globally with the Copiers variable.
40
+type CopierFunc func(interface{}) (interface{}, error)
41
+
42
+// Copiers is a map of types that behave specially when they are copied.
43
+// If a type is found in this map while deep copying, this function
44
+// will be called to copy it instead of attempting to copy all fields.
45
+//
46
+// The key should be the type, obtained using: reflect.TypeOf(value with type).
47
+//
48
+// It is unsafe to write to this map after Copies have started. If you
49
+// are writing to this map while also copying, wrap all modifications to
50
+// this map as well as to Copy in a mutex.
51
+var Copiers map[reflect.Type]CopierFunc = make(map[reflect.Type]CopierFunc)
52
+
53
+// ShallowCopiers is a map of pointer types that behave specially
54
+// when they are copied.  If a type is found in this map while deep
55
+// copying, the pointer value will be shallow copied and not walked
56
+// into.
57
+//
58
+// The key should be the type, obtained using: reflect.TypeOf(value
59
+// with type).
60
+//
61
+// It is unsafe to write to this map after Copies have started. If you
62
+// are writing to this map while also copying, wrap all modifications to
63
+// this map as well as to Copy in a mutex.
64
+var ShallowCopiers map[reflect.Type]struct{} = make(map[reflect.Type]struct{})
65
+
66
+// Must is a helper that wraps a call to a function returning
67
+// (interface{}, error) and panics if the error is non-nil. It is intended
68
+// for use in variable initializations and should only be used when a copy
69
+// error should be a crashing case.
70
+func Must(v interface{}, err error) interface{} {
71
+	if err != nil {
72
+		panic("copy error: " + err.Error())
73
+	}
74
+
75
+	return v
76
+}
77
+
78
+var errPointerRequired = errors.New("Copy argument must be a pointer when Lock is true")
79
+
80
+type Config struct {
81
+	// Lock any types that are a sync.Locker and are not a mutex while copying.
82
+	// If there is an RLocker method, use that to get the sync.Locker.
83
+	Lock bool
84
+
85
+	// Copiers is a map of types associated with a CopierFunc. Use the global
86
+	// Copiers map if this is nil.
87
+	Copiers map[reflect.Type]CopierFunc
88
+
89
+	// ShallowCopiers is a map of pointer types that when they are
90
+	// shallow copied no matter where they are encountered. Use the
91
+	// global ShallowCopiers if this is nil.
92
+	ShallowCopiers map[reflect.Type]struct{}
93
+}
94
+
95
+func (c Config) Copy(v interface{}) (interface{}, error) {
96
+	if c.Lock && reflect.ValueOf(v).Kind() != reflect.Ptr {
97
+		return nil, errPointerRequired
98
+	}
99
+
100
+	w := new(walker)
101
+	if c.Lock {
102
+		w.useLocks = true
103
+	}
104
+
105
+	if c.Copiers == nil {
106
+		c.Copiers = Copiers
107
+	}
108
+	w.copiers = c.Copiers
109
+
110
+	if c.ShallowCopiers == nil {
111
+		c.ShallowCopiers = ShallowCopiers
112
+	}
113
+	w.shallowCopiers = c.ShallowCopiers
114
+
115
+	err := reflectwalk.Walk(v, w)
116
+	if err != nil {
117
+		return nil, err
118
+	}
119
+
120
+	// Get the result. If the result is nil, then we want to turn it
121
+	// into a typed nil if we can.
122
+	result := w.Result
123
+	if result == nil {
124
+		val := reflect.ValueOf(v)
125
+		result = reflect.Indirect(reflect.New(val.Type())).Interface()
126
+	}
127
+
128
+	return result, nil
129
+}
130
+
131
+// Return the key used to index interfaces types we've seen. Store the number
132
+// of pointers in the upper 32bits, and the depth in the lower 32bits. This is
133
+// easy to calculate, easy to match a key with our current depth, and we don't
134
+// need to deal with initializing and cleaning up nested maps or slices.
135
+func ifaceKey(pointers, depth int) uint64 {
136
+	return uint64(pointers)<<32 | uint64(depth)
137
+}
138
+
139
+type walker struct {
140
+	Result interface{}
141
+
142
+	copiers        map[reflect.Type]CopierFunc
143
+	shallowCopiers map[reflect.Type]struct{}
144
+	depth          int
145
+	ignoreDepth    int
146
+	vals           []reflect.Value
147
+	cs             []reflect.Value
148
+
149
+	// This stores the number of pointers we've walked over, indexed by depth.
150
+	ps []int
151
+
152
+	// If an interface is indirected by a pointer, we need to know the type of
153
+	// interface to create when creating the new value.  Store the interface
154
+	// types here, indexed by both the walk depth and the number of pointers
155
+	// already seen at that depth. Use ifaceKey to calculate the proper uint64
156
+	// value.
157
+	ifaceTypes map[uint64]reflect.Type
158
+
159
+	// any locks we've taken, indexed by depth
160
+	locks []sync.Locker
161
+	// take locks while walking the structure
162
+	useLocks bool
163
+}
164
+
165
+func (w *walker) Enter(l reflectwalk.Location) error {
166
+	w.depth++
167
+
168
+	// ensure we have enough elements to index via w.depth
169
+	for w.depth >= len(w.locks) {
170
+		w.locks = append(w.locks, nil)
171
+	}
172
+
173
+	for len(w.ps) < w.depth+1 {
174
+		w.ps = append(w.ps, 0)
175
+	}
176
+
177
+	return nil
178
+}
179
+
180
+func (w *walker) Exit(l reflectwalk.Location) error {
181
+	locker := w.locks[w.depth]
182
+	w.locks[w.depth] = nil
183
+	if locker != nil {
184
+		defer locker.Unlock()
185
+	}
186
+
187
+	// clear out pointers and interfaces as we exit the stack
188
+	w.ps[w.depth] = 0
189
+
190
+	for k := range w.ifaceTypes {
191
+		mask := uint64(^uint32(0))
192
+		if k&mask == uint64(w.depth) {
193
+			delete(w.ifaceTypes, k)
194
+		}
195
+	}
196
+
197
+	w.depth--
198
+	if w.ignoreDepth > w.depth {
199
+		w.ignoreDepth = 0
200
+	}
201
+
202
+	if w.ignoring() {
203
+		return nil
204
+	}
205
+
206
+	switch l {
207
+	case reflectwalk.Array:
208
+		fallthrough
209
+	case reflectwalk.Map:
210
+		fallthrough
211
+	case reflectwalk.Slice:
212
+		w.replacePointerMaybe()
213
+
214
+		// Pop map off our container
215
+		w.cs = w.cs[:len(w.cs)-1]
216
+	case reflectwalk.MapValue:
217
+		// Pop off the key and value
218
+		mv := w.valPop()
219
+		mk := w.valPop()
220
+		m := w.cs[len(w.cs)-1]
221
+
222
+		// If mv is the zero value, SetMapIndex deletes the key form the map,
223
+		// or in this case never adds it. We need to create a properly typed
224
+		// zero value so that this key can be set.
225
+		if !mv.IsValid() {
226
+			mv = reflect.Zero(m.Elem().Type().Elem())
227
+		}
228
+		m.Elem().SetMapIndex(mk, mv)
229
+	case reflectwalk.ArrayElem:
230
+		// Pop off the value and the index and set it on the array
231
+		v := w.valPop()
232
+		i := w.valPop().Interface().(int)
233
+		if v.IsValid() {
234
+			a := w.cs[len(w.cs)-1]
235
+			ae := a.Elem().Index(i) // storing array as pointer on stack - so need Elem() call
236
+			if ae.CanSet() {
237
+				ae.Set(v)
238
+			}
239
+		}
240
+	case reflectwalk.SliceElem:
241
+		// Pop off the value and the index and set it on the slice
242
+		v := w.valPop()
243
+		i := w.valPop().Interface().(int)
244
+		if v.IsValid() {
245
+			s := w.cs[len(w.cs)-1]
246
+			se := s.Elem().Index(i)
247
+			if se.CanSet() {
248
+				se.Set(v)
249
+			}
250
+		}
251
+	case reflectwalk.Struct:
252
+		w.replacePointerMaybe()
253
+
254
+		// Remove the struct from the container stack
255
+		w.cs = w.cs[:len(w.cs)-1]
256
+	case reflectwalk.StructField:
257
+		// Pop off the value and the field
258
+		v := w.valPop()
259
+		f := w.valPop().Interface().(reflect.StructField)
260
+		if v.IsValid() {
261
+			s := w.cs[len(w.cs)-1]
262
+			sf := reflect.Indirect(s).FieldByName(f.Name)
263
+
264
+			if sf.CanSet() {
265
+				sf.Set(v)
266
+			}
267
+		}
268
+	case reflectwalk.WalkLoc:
269
+		// Clear out the slices for GC
270
+		w.cs = nil
271
+		w.vals = nil
272
+	}
273
+
274
+	return nil
275
+}
276
+
277
+func (w *walker) Map(m reflect.Value) error {
278
+	if w.ignoring() {
279
+		return nil
280
+	}
281
+	w.lock(m)
282
+
283
+	// Create the map. If the map itself is nil, then just make a nil map
284
+	var newMap reflect.Value
285
+	if m.IsNil() {
286
+		newMap = reflect.New(m.Type())
287
+	} else {
288
+		newMap = wrapPtr(reflect.MakeMap(m.Type()))
289
+	}
290
+
291
+	w.cs = append(w.cs, newMap)
292
+	w.valPush(newMap)
293
+	return nil
294
+}
295
+
296
+func (w *walker) MapElem(m, k, v reflect.Value) error {
297
+	return nil
298
+}
299
+
300
+func (w *walker) PointerEnter(v bool) error {
301
+	if v {
302
+		w.ps[w.depth]++
303
+	}
304
+	return nil
305
+}
306
+
307
+func (w *walker) PointerExit(v bool) error {
308
+	if v {
309
+		w.ps[w.depth]--
310
+	}
311
+	return nil
312
+}
313
+
314
+func (w *walker) Pointer(v reflect.Value) error {
315
+	if _, ok := w.shallowCopiers[v.Type()]; ok {
316
+		// Shallow copy this value. Use the same logic as primitive, then
317
+		// return skip.
318
+		if err := w.Primitive(v); err != nil {
319
+			return err
320
+		}
321
+
322
+		return reflectwalk.SkipEntry
323
+	}
324
+
325
+	return nil
326
+}
327
+
328
+func (w *walker) Interface(v reflect.Value) error {
329
+	if !v.IsValid() {
330
+		return nil
331
+	}
332
+	if w.ifaceTypes == nil {
333
+		w.ifaceTypes = make(map[uint64]reflect.Type)
334
+	}
335
+
336
+	w.ifaceTypes[ifaceKey(w.ps[w.depth], w.depth)] = v.Type()
337
+	return nil
338
+}
339
+
340
+func (w *walker) Primitive(v reflect.Value) error {
341
+	if w.ignoring() {
342
+		return nil
343
+	}
344
+	w.lock(v)
345
+
346
+	// IsValid verifies the v is non-zero and CanInterface verifies
347
+	// that we're allowed to read this value (unexported fields).
348
+	var newV reflect.Value
349
+	if v.IsValid() && v.CanInterface() {
350
+		newV = reflect.New(v.Type())
351
+		newV.Elem().Set(v)
352
+	}
353
+
354
+	w.valPush(newV)
355
+	w.replacePointerMaybe()
356
+	return nil
357
+}
358
+
359
+func (w *walker) Slice(s reflect.Value) error {
360
+	if w.ignoring() {
361
+		return nil
362
+	}
363
+	w.lock(s)
364
+
365
+	var newS reflect.Value
366
+	if s.IsNil() {
367
+		newS = reflect.New(s.Type())
368
+	} else {
369
+		newS = wrapPtr(reflect.MakeSlice(s.Type(), s.Len(), s.Cap()))
370
+	}
371
+
372
+	w.cs = append(w.cs, newS)
373
+	w.valPush(newS)
374
+	return nil
375
+}
376
+
377
+func (w *walker) SliceElem(i int, elem reflect.Value) error {
378
+	if w.ignoring() {
379
+		return nil
380
+	}
381
+
382
+	// We don't write the slice here because elem might still be
383
+	// arbitrarily complex. Just record the index and continue on.
384
+	w.valPush(reflect.ValueOf(i))
385
+
386
+	return nil
387
+}
388
+
389
+func (w *walker) Array(a reflect.Value) error {
390
+	if w.ignoring() {
391
+		return nil
392
+	}
393
+	w.lock(a)
394
+
395
+	newA := reflect.New(a.Type())
396
+
397
+	w.cs = append(w.cs, newA)
398
+	w.valPush(newA)
399
+	return nil
400
+}
401
+
402
+func (w *walker) ArrayElem(i int, elem reflect.Value) error {
403
+	if w.ignoring() {
404
+		return nil
405
+	}
406
+
407
+	// We don't write the array here because elem might still be
408
+	// arbitrarily complex. Just record the index and continue on.
409
+	w.valPush(reflect.ValueOf(i))
410
+
411
+	return nil
412
+}
413
+
414
+func (w *walker) Struct(s reflect.Value) error {
415
+	if w.ignoring() {
416
+		return nil
417
+	}
418
+	w.lock(s)
419
+
420
+	var v reflect.Value
421
+	if c, ok := w.copiers[s.Type()]; ok {
422
+		// We have a Copier for this struct, so we use that copier to
423
+		// get the copy, and we ignore anything deeper than this.
424
+		w.ignoreDepth = w.depth
425
+
426
+		dup, err := c(s.Interface())
427
+		if err != nil {
428
+			return err
429
+		}
430
+
431
+		// We need to put a pointer to the value on the value stack,
432
+		// so allocate a new pointer and set it.
433
+		v = reflect.New(s.Type())
434
+		reflect.Indirect(v).Set(reflect.ValueOf(dup))
435
+	} else {
436
+		// No copier, we copy ourselves and allow reflectwalk to guide
437
+		// us deeper into the structure for copying.
438
+		v = reflect.New(s.Type())
439
+	}
440
+
441
+	// Push the value onto the value stack for setting the struct field,
442
+	// and add the struct itself to the containers stack in case we walk
443
+	// deeper so that its own fields can be modified.
444
+	w.valPush(v)
445
+	w.cs = append(w.cs, v)
446
+
447
+	return nil
448
+}
449
+
450
+func (w *walker) StructField(f reflect.StructField, v reflect.Value) error {
451
+	if w.ignoring() {
452
+		return nil
453
+	}
454
+
455
+	// If PkgPath is non-empty, this is a private (unexported) field.
456
+	// We do not set this unexported since the Go runtime doesn't allow us.
457
+	if f.PkgPath != "" {
458
+		return reflectwalk.SkipEntry
459
+	}
460
+
461
+	switch f.Tag.Get(tagKey) {
462
+	case "shallow":
463
+		// If we're shallow copying then assign the value directly to the
464
+		// struct and skip the entry.
465
+		if v.IsValid() {
466
+			s := w.cs[len(w.cs)-1]
467
+			sf := reflect.Indirect(s).FieldByName(f.Name)
468
+			if sf.CanSet() {
469
+				sf.Set(v)
470
+			}
471
+		}
472
+
473
+		return reflectwalk.SkipEntry
474
+
475
+	case "ignore":
476
+		// Do nothing
477
+		return reflectwalk.SkipEntry
478
+	}
479
+
480
+	// Push the field onto the stack, we'll handle it when we exit
481
+	// the struct field in Exit...
482
+	w.valPush(reflect.ValueOf(f))
483
+
484
+	return nil
485
+}
486
+
487
+// ignore causes the walker to ignore any more values until we exit this on
488
+func (w *walker) ignore() {
489
+	w.ignoreDepth = w.depth
490
+}
491
+
492
+func (w *walker) ignoring() bool {
493
+	return w.ignoreDepth > 0 && w.depth >= w.ignoreDepth
494
+}
495
+
496
+func (w *walker) pointerPeek() bool {
497
+	return w.ps[w.depth] > 0
498
+}
499
+
500
+func (w *walker) valPop() reflect.Value {
501
+	result := w.vals[len(w.vals)-1]
502
+	w.vals = w.vals[:len(w.vals)-1]
503
+
504
+	// If we're out of values, that means we popped everything off. In
505
+	// this case, we reset the result so the next pushed value becomes
506
+	// the result.
507
+	if len(w.vals) == 0 {
508
+		w.Result = nil
509
+	}
510
+
511
+	return result
512
+}
513
+
514
+func (w *walker) valPush(v reflect.Value) {
515
+	w.vals = append(w.vals, v)
516
+
517
+	// If we haven't set the result yet, then this is the result since
518
+	// it is the first (outermost) value we're seeing.
519
+	if w.Result == nil && v.IsValid() {
520
+		w.Result = v.Interface()
521
+	}
522
+}
523
+
524
+func (w *walker) replacePointerMaybe() {
525
+	// Determine the last pointer value. If it is NOT a pointer, then
526
+	// we need to push that onto the stack.
527
+	if !w.pointerPeek() {
528
+		w.valPush(reflect.Indirect(w.valPop()))
529
+		return
530
+	}
531
+
532
+	v := w.valPop()
533
+
534
+	// If the expected type is a pointer to an interface of any depth,
535
+	// such as *interface{}, **interface{}, etc., then we need to convert
536
+	// the value "v" from *CONCRETE to *interface{} so types match for
537
+	// Set.
538
+	//
539
+	// Example if v is type *Foo where Foo is a struct, v would become
540
+	// *interface{} instead. This only happens if we have an interface expectation
541
+	// at this depth.
542
+	//
543
+	// For more info, see GH-16
544
+	if iType, ok := w.ifaceTypes[ifaceKey(w.ps[w.depth], w.depth)]; ok && iType.Kind() == reflect.Interface {
545
+		y := reflect.New(iType)           // Create *interface{}
546
+		y.Elem().Set(reflect.Indirect(v)) // Assign "Foo" to interface{} (dereferenced)
547
+		v = y                             // v is now typed *interface{} (where *v = Foo)
548
+	}
549
+
550
+	for i := 1; i < w.ps[w.depth]; i++ {
551
+		if iType, ok := w.ifaceTypes[ifaceKey(w.ps[w.depth]-i, w.depth)]; ok {
552
+			iface := reflect.New(iType).Elem()
553
+			iface.Set(v)
554
+			v = iface
555
+		}
556
+
557
+		p := reflect.New(v.Type())
558
+		p.Elem().Set(v)
559
+		v = p
560
+	}
561
+
562
+	w.valPush(v)
563
+}
564
+
565
+// if this value is a Locker, lock it and add it to the locks slice
566
+func (w *walker) lock(v reflect.Value) {
567
+	if !w.useLocks {
568
+		return
569
+	}
570
+
571
+	if !v.IsValid() || !v.CanInterface() {
572
+		return
573
+	}
574
+
575
+	type rlocker interface {
576
+		RLocker() sync.Locker
577
+	}
578
+
579
+	var locker sync.Locker
580
+
581
+	// We can't call Interface() on a value directly, since that requires
582
+	// a copy. This is OK, since the pointer to a value which is a sync.Locker
583
+	// is also a sync.Locker.
584
+	if v.Kind() == reflect.Ptr {
585
+		switch l := v.Interface().(type) {
586
+		case rlocker:
587
+			// don't lock a mutex directly
588
+			if _, ok := l.(*sync.RWMutex); !ok {
589
+				locker = l.RLocker()
590
+			}
591
+		case sync.Locker:
592
+			locker = l
593
+		}
594
+	} else if v.CanAddr() {
595
+		switch l := v.Addr().Interface().(type) {
596
+		case rlocker:
597
+			// don't lock a mutex directly
598
+			if _, ok := l.(*sync.RWMutex); !ok {
599
+				locker = l.RLocker()
600
+			}
601
+		case sync.Locker:
602
+			locker = l
603
+		}
604
+	}
605
+
606
+	// still no callable locker
607
+	if locker == nil {
608
+		return
609
+	}
610
+
611
+	// don't lock a mutex directly
612
+	switch locker.(type) {
613
+	case *sync.Mutex, *sync.RWMutex:
614
+		return
615
+	}
616
+
617
+	locker.Lock()
618
+	w.locks[w.depth] = locker
619
+}
620
+
621
+// wrapPtr is a helper that takes v and always make it *v. copystructure
622
+// stores things internally as pointers until the last moment before unwrapping
623
+func wrapPtr(v reflect.Value) reflect.Value {
624
+	if !v.IsValid() {
625
+		return v
626
+	}
627
+	vPtr := reflect.New(v.Type())
628
+	vPtr.Elem().Set(v)
629
+	return vPtr
630
+}
0 631
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+language: go
0 1
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+The MIT License (MIT)
1
+
2
+Copyright (c) 2013 Mitchell Hashimoto
3
+
4
+Permission is hereby granted, free of charge, to any person obtaining a copy
5
+of this software and associated documentation files (the "Software"), to deal
6
+in the Software without restriction, including without limitation the rights
7
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+copies of the Software, and to permit persons to whom the Software is
9
+furnished to do so, subject to the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be included in
12
+all copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+THE SOFTWARE.
0 21
new file mode 100644
... ...
@@ -0,0 +1,6 @@
0
+# reflectwalk
1
+
2
+reflectwalk is a Go library for "walking" a value in Go using reflection,
3
+in the same way a directory tree can be "walked" on the filesystem. Walking
4
+a complex structure can allow you to do manipulations on unknown structures
5
+such as those decoded from JSON.
0 6
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+package reflectwalk
1
+
2
+//go:generate stringer -type=Location location.go
3
+
4
+type Location uint
5
+
6
+const (
7
+	None Location = iota
8
+	Map
9
+	MapKey
10
+	MapValue
11
+	Slice
12
+	SliceElem
13
+	Array
14
+	ArrayElem
15
+	Struct
16
+	StructField
17
+	WalkLoc
18
+)
0 19
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+// Code generated by "stringer -type=Location location.go"; DO NOT EDIT.
1
+
2
+package reflectwalk
3
+
4
+import "fmt"
5
+
6
+const _Location_name = "NoneMapMapKeyMapValueSliceSliceElemArrayArrayElemStructStructFieldWalkLoc"
7
+
8
+var _Location_index = [...]uint8{0, 4, 7, 13, 21, 26, 35, 40, 49, 55, 66, 73}
9
+
10
+func (i Location) String() string {
11
+	if i >= Location(len(_Location_index)-1) {
12
+		return fmt.Sprintf("Location(%d)", i)
13
+	}
14
+	return _Location_name[_Location_index[i]:_Location_index[i+1]]
15
+}
0 16
new file mode 100644
... ...
@@ -0,0 +1,420 @@
0
+// reflectwalk is a package that allows you to "walk" complex structures
1
+// similar to how you may "walk" a filesystem: visiting every element one
2
+// by one and calling callback functions allowing you to handle and manipulate
3
+// those elements.
4
+package reflectwalk
5
+
6
+import (
7
+	"errors"
8
+	"reflect"
9
+)
10
+
11
+// PrimitiveWalker implementations are able to handle primitive values
12
+// within complex structures. Primitive values are numbers, strings,
13
+// booleans, funcs, chans.
14
+//
15
+// These primitive values are often members of more complex
16
+// structures (slices, maps, etc.) that are walkable by other interfaces.
17
+type PrimitiveWalker interface {
18
+	Primitive(reflect.Value) error
19
+}
20
+
21
+// InterfaceWalker implementations are able to handle interface values as they
22
+// are encountered during the walk.
23
+type InterfaceWalker interface {
24
+	Interface(reflect.Value) error
25
+}
26
+
27
+// MapWalker implementations are able to handle individual elements
28
+// found within a map structure.
29
+type MapWalker interface {
30
+	Map(m reflect.Value) error
31
+	MapElem(m, k, v reflect.Value) error
32
+}
33
+
34
+// SliceWalker implementations are able to handle slice elements found
35
+// within complex structures.
36
+type SliceWalker interface {
37
+	Slice(reflect.Value) error
38
+	SliceElem(int, reflect.Value) error
39
+}
40
+
41
+// ArrayWalker implementations are able to handle array elements found
42
+// within complex structures.
43
+type ArrayWalker interface {
44
+	Array(reflect.Value) error
45
+	ArrayElem(int, reflect.Value) error
46
+}
47
+
48
+// StructWalker is an interface that has methods that are called for
49
+// structs when a Walk is done.
50
+type StructWalker interface {
51
+	Struct(reflect.Value) error
52
+	StructField(reflect.StructField, reflect.Value) error
53
+}
54
+
55
+// EnterExitWalker implementations are notified before and after
56
+// they walk deeper into complex structures (into struct fields,
57
+// into slice elements, etc.)
58
+type EnterExitWalker interface {
59
+	Enter(Location) error
60
+	Exit(Location) error
61
+}
62
+
63
+// PointerWalker implementations are notified when the value they're
64
+// walking is a pointer or not. Pointer is called for _every_ value whether
65
+// it is a pointer or not.
66
+type PointerWalker interface {
67
+	PointerEnter(bool) error
68
+	PointerExit(bool) error
69
+}
70
+
71
+// PointerValueWalker implementations are notified with the value of
72
+// a particular pointer when a pointer is walked. Pointer is called
73
+// right before PointerEnter.
74
+type PointerValueWalker interface {
75
+	Pointer(reflect.Value) error
76
+}
77
+
78
+// SkipEntry can be returned from walk functions to skip walking
79
+// the value of this field. This is only valid in the following functions:
80
+//
81
+//   - Struct: skips all fields from being walked
82
+//   - StructField: skips walking the struct value
83
+//
84
+var SkipEntry = errors.New("skip this entry")
85
+
86
+// Walk takes an arbitrary value and an interface and traverses the
87
+// value, calling callbacks on the interface if they are supported.
88
+// The interface should implement one or more of the walker interfaces
89
+// in this package, such as PrimitiveWalker, StructWalker, etc.
90
+func Walk(data, walker interface{}) (err error) {
91
+	v := reflect.ValueOf(data)
92
+	ew, ok := walker.(EnterExitWalker)
93
+	if ok {
94
+		err = ew.Enter(WalkLoc)
95
+	}
96
+
97
+	if err == nil {
98
+		err = walk(v, walker)
99
+	}
100
+
101
+	if ok && err == nil {
102
+		err = ew.Exit(WalkLoc)
103
+	}
104
+
105
+	return
106
+}
107
+
108
+func walk(v reflect.Value, w interface{}) (err error) {
109
+	// Determine if we're receiving a pointer and if so notify the walker.
110
+	// The logic here is convoluted but very important (tests will fail if
111
+	// almost any part is changed). I will try to explain here.
112
+	//
113
+	// First, we check if the value is an interface, if so, we really need
114
+	// to check the interface's VALUE to see whether it is a pointer.
115
+	//
116
+	// Check whether the value is then a pointer. If so, then set pointer
117
+	// to true to notify the user.
118
+	//
119
+	// If we still have a pointer or an interface after the indirections, then
120
+	// we unwrap another level
121
+	//
122
+	// At this time, we also set "v" to be the dereferenced value. This is
123
+	// because once we've unwrapped the pointer we want to use that value.
124
+	pointer := false
125
+	pointerV := v
126
+
127
+	for {
128
+		if pointerV.Kind() == reflect.Interface {
129
+			if iw, ok := w.(InterfaceWalker); ok {
130
+				if err = iw.Interface(pointerV); err != nil {
131
+					return
132
+				}
133
+			}
134
+
135
+			pointerV = pointerV.Elem()
136
+		}
137
+
138
+		if pointerV.Kind() == reflect.Ptr {
139
+			if pw, ok := w.(PointerValueWalker); ok {
140
+				if err = pw.Pointer(pointerV); err != nil {
141
+					if err == SkipEntry {
142
+						// Skip the rest of this entry but clear the error
143
+						return nil
144
+					}
145
+
146
+					return
147
+				}
148
+			}
149
+
150
+			pointer = true
151
+			v = reflect.Indirect(pointerV)
152
+		}
153
+		if pw, ok := w.(PointerWalker); ok {
154
+			if err = pw.PointerEnter(pointer); err != nil {
155
+				return
156
+			}
157
+
158
+			defer func(pointer bool) {
159
+				if err != nil {
160
+					return
161
+				}
162
+
163
+				err = pw.PointerExit(pointer)
164
+			}(pointer)
165
+		}
166
+
167
+		if pointer {
168
+			pointerV = v
169
+		}
170
+		pointer = false
171
+
172
+		// If we still have a pointer or interface we have to indirect another level.
173
+		switch pointerV.Kind() {
174
+		case reflect.Ptr, reflect.Interface:
175
+			continue
176
+		}
177
+		break
178
+	}
179
+
180
+	// We preserve the original value here because if it is an interface
181
+	// type, we want to pass that directly into the walkPrimitive, so that
182
+	// we can set it.
183
+	originalV := v
184
+	if v.Kind() == reflect.Interface {
185
+		v = v.Elem()
186
+	}
187
+
188
+	k := v.Kind()
189
+	if k >= reflect.Int && k <= reflect.Complex128 {
190
+		k = reflect.Int
191
+	}
192
+
193
+	switch k {
194
+	// Primitives
195
+	case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
196
+		err = walkPrimitive(originalV, w)
197
+		return
198
+	case reflect.Map:
199
+		err = walkMap(v, w)
200
+		return
201
+	case reflect.Slice:
202
+		err = walkSlice(v, w)
203
+		return
204
+	case reflect.Struct:
205
+		err = walkStruct(v, w)
206
+		return
207
+	case reflect.Array:
208
+		err = walkArray(v, w)
209
+		return
210
+	default:
211
+		panic("unsupported type: " + k.String())
212
+	}
213
+}
214
+
215
+func walkMap(v reflect.Value, w interface{}) error {
216
+	ew, ewok := w.(EnterExitWalker)
217
+	if ewok {
218
+		ew.Enter(Map)
219
+	}
220
+
221
+	if mw, ok := w.(MapWalker); ok {
222
+		if err := mw.Map(v); err != nil {
223
+			return err
224
+		}
225
+	}
226
+
227
+	for _, k := range v.MapKeys() {
228
+		kv := v.MapIndex(k)
229
+
230
+		if mw, ok := w.(MapWalker); ok {
231
+			if err := mw.MapElem(v, k, kv); err != nil {
232
+				return err
233
+			}
234
+		}
235
+
236
+		ew, ok := w.(EnterExitWalker)
237
+		if ok {
238
+			ew.Enter(MapKey)
239
+		}
240
+
241
+		if err := walk(k, w); err != nil {
242
+			return err
243
+		}
244
+
245
+		if ok {
246
+			ew.Exit(MapKey)
247
+			ew.Enter(MapValue)
248
+		}
249
+
250
+		// get the map value again as it may have changed in the MapElem call
251
+		if err := walk(v.MapIndex(k), w); err != nil {
252
+			return err
253
+		}
254
+
255
+		if ok {
256
+			ew.Exit(MapValue)
257
+		}
258
+	}
259
+
260
+	if ewok {
261
+		ew.Exit(Map)
262
+	}
263
+
264
+	return nil
265
+}
266
+
267
+func walkPrimitive(v reflect.Value, w interface{}) error {
268
+	if pw, ok := w.(PrimitiveWalker); ok {
269
+		return pw.Primitive(v)
270
+	}
271
+
272
+	return nil
273
+}
274
+
275
+func walkSlice(v reflect.Value, w interface{}) (err error) {
276
+	ew, ok := w.(EnterExitWalker)
277
+	if ok {
278
+		ew.Enter(Slice)
279
+	}
280
+
281
+	if sw, ok := w.(SliceWalker); ok {
282
+		if err := sw.Slice(v); err != nil {
283
+			return err
284
+		}
285
+	}
286
+
287
+	for i := 0; i < v.Len(); i++ {
288
+		elem := v.Index(i)
289
+
290
+		if sw, ok := w.(SliceWalker); ok {
291
+			if err := sw.SliceElem(i, elem); err != nil {
292
+				return err
293
+			}
294
+		}
295
+
296
+		ew, ok := w.(EnterExitWalker)
297
+		if ok {
298
+			ew.Enter(SliceElem)
299
+		}
300
+
301
+		if err := walk(elem, w); err != nil {
302
+			return err
303
+		}
304
+
305
+		if ok {
306
+			ew.Exit(SliceElem)
307
+		}
308
+	}
309
+
310
+	ew, ok = w.(EnterExitWalker)
311
+	if ok {
312
+		ew.Exit(Slice)
313
+	}
314
+
315
+	return nil
316
+}
317
+
318
+func walkArray(v reflect.Value, w interface{}) (err error) {
319
+	ew, ok := w.(EnterExitWalker)
320
+	if ok {
321
+		ew.Enter(Array)
322
+	}
323
+
324
+	if aw, ok := w.(ArrayWalker); ok {
325
+		if err := aw.Array(v); err != nil {
326
+			return err
327
+		}
328
+	}
329
+
330
+	for i := 0; i < v.Len(); i++ {
331
+		elem := v.Index(i)
332
+
333
+		if aw, ok := w.(ArrayWalker); ok {
334
+			if err := aw.ArrayElem(i, elem); err != nil {
335
+				return err
336
+			}
337
+		}
338
+
339
+		ew, ok := w.(EnterExitWalker)
340
+		if ok {
341
+			ew.Enter(ArrayElem)
342
+		}
343
+
344
+		if err := walk(elem, w); err != nil {
345
+			return err
346
+		}
347
+
348
+		if ok {
349
+			ew.Exit(ArrayElem)
350
+		}
351
+	}
352
+
353
+	ew, ok = w.(EnterExitWalker)
354
+	if ok {
355
+		ew.Exit(Array)
356
+	}
357
+
358
+	return nil
359
+}
360
+
361
+func walkStruct(v reflect.Value, w interface{}) (err error) {
362
+	ew, ewok := w.(EnterExitWalker)
363
+	if ewok {
364
+		ew.Enter(Struct)
365
+	}
366
+
367
+	skip := false
368
+	if sw, ok := w.(StructWalker); ok {
369
+		err = sw.Struct(v)
370
+		if err == SkipEntry {
371
+			skip = true
372
+			err = nil
373
+		}
374
+		if err != nil {
375
+			return
376
+		}
377
+	}
378
+
379
+	if !skip {
380
+		vt := v.Type()
381
+		for i := 0; i < vt.NumField(); i++ {
382
+			sf := vt.Field(i)
383
+			f := v.FieldByIndex([]int{i})
384
+
385
+			if sw, ok := w.(StructWalker); ok {
386
+				err = sw.StructField(sf, f)
387
+
388
+				// SkipEntry just pretends this field doesn't even exist
389
+				if err == SkipEntry {
390
+					continue
391
+				}
392
+
393
+				if err != nil {
394
+					return
395
+				}
396
+			}
397
+
398
+			ew, ok := w.(EnterExitWalker)
399
+			if ok {
400
+				ew.Enter(StructField)
401
+			}
402
+
403
+			err = walk(f, w)
404
+			if err != nil {
405
+				return
406
+			}
407
+
408
+			if ok {
409
+				ew.Exit(StructField)
410
+			}
411
+		}
412
+	}
413
+
414
+	if ewok {
415
+		ew.Exit(Struct)
416
+	}
417
+
418
+	return nil
419
+}
... ...
@@ -611,9 +611,15 @@ github.com/miekg/dns
611 611
 # github.com/mistifyio/go-zfs/v3 v3.0.1
612 612
 ## explicit; go 1.14
613 613
 github.com/mistifyio/go-zfs/v3
614
+# github.com/mitchellh/copystructure v1.2.0
615
+## explicit; go 1.15
616
+github.com/mitchellh/copystructure
614 617
 # github.com/mitchellh/hashstructure/v2 v2.0.2
615 618
 ## explicit; go 1.14
616 619
 github.com/mitchellh/hashstructure/v2
620
+# github.com/mitchellh/reflectwalk v1.0.2
621
+## explicit
622
+github.com/mitchellh/reflectwalk
617 623
 # github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f
618 624
 ## explicit; go 1.18
619 625
 github.com/moby/buildkit/api/services/control
... ...
@@ -864,6 +870,10 @@ github.com/moby/term/windows
864 864
 # github.com/morikuni/aec v1.0.0
865 865
 ## explicit
866 866
 github.com/morikuni/aec
867
+# github.com/onsi/ginkgo/v2 v2.1.4
868
+## explicit; go 1.18
869
+# github.com/onsi/gomega v1.20.1
870
+## explicit; go 1.18
867 871
 # github.com/opencontainers/go-digest v1.0.0
868 872
 ## explicit; go 1.13
869 873
 github.com/opencontainers/go-digest