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>
| ... | ... |
@@ -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 {
|
| ... | ... |
@@ -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 | 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 |