daemon: read-copy-update the daemon config

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

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider 2022-08-17 17:13:49 -04:00
parent 742ac6e275
commit 0b592467d9
62 changed files with 1819 additions and 568 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

21
vendor/github.com/mitchellh/copystructure/LICENSE generated vendored Normal file
View file

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

21
vendor/github.com/mitchellh/copystructure/README.md generated vendored Normal file
View file

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

View file

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

View file

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

1
vendor/github.com/mitchellh/reflectwalk/.travis.yml generated vendored Normal file
View file

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

21
vendor/github.com/mitchellh/reflectwalk/LICENSE generated vendored Normal file
View file

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

6
vendor/github.com/mitchellh/reflectwalk/README.md generated vendored Normal file
View file

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

19
vendor/github.com/mitchellh/reflectwalk/location.go generated vendored Normal file
View file

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

View file

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

420
vendor/github.com/mitchellh/reflectwalk/reflectwalk.go generated vendored Normal file
View file

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

10
vendor/modules.txt vendored
View file

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