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:
parent
742ac6e275
commit
0b592467d9
62 changed files with 1819 additions and 568 deletions
|
@ -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(),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
115
daemon/daemon.go
115
daemon/daemon.go
|
@ -16,6 +16,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
|
@ -84,7 +85,8 @@ type Daemon struct {
|
||||||
containersReplica *container.ViewDB
|
containersReplica *container.ViewDB
|
||||||
execCommands *container.ExecStore
|
execCommands *container.ExecStore
|
||||||
imageService ImageService
|
imageService ImageService
|
||||||
configStore *config.Config
|
configStore atomic.Pointer[config.Config]
|
||||||
|
configReload sync.Mutex
|
||||||
statsCollector *stats.Collector
|
statsCollector *stats.Collector
|
||||||
defaultLogConfig containertypes.LogConfig
|
defaultLogConfig containertypes.LogConfig
|
||||||
registryService *registry.Service
|
registryService *registry.Service
|
||||||
|
@ -148,20 +150,31 @@ func (daemon *Daemon) StoreHosts(hosts []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// config returns an immutable snapshot of the current daemon configuration.
|
||||||
|
// Multiple calls to this function will return the same pointer until the
|
||||||
|
// configuration is reloaded so callers must take care not to modify the
|
||||||
|
// returned value.
|
||||||
|
//
|
||||||
|
// To ensure that the configuration used remains consistent throughout the
|
||||||
|
// lifetime of an operation, the configuration pointer should be passed down the
|
||||||
|
// call stack, like one would a [context.Context] value. Only the entrypoints
|
||||||
|
// for operations, the outermost functions, should call this function.
|
||||||
|
func (daemon *Daemon) config() *config.Config {
|
||||||
|
cfg := daemon.configStore.Load()
|
||||||
|
if cfg == nil {
|
||||||
|
return &config.Config{}
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
// HasExperimental returns whether the experimental features of the daemon are enabled or not
|
// HasExperimental returns whether the experimental features of the daemon are enabled or not
|
||||||
func (daemon *Daemon) HasExperimental() bool {
|
func (daemon *Daemon) HasExperimental() bool {
|
||||||
return daemon.configStore != nil && daemon.configStore.Experimental
|
return daemon.config().Experimental
|
||||||
}
|
}
|
||||||
|
|
||||||
// Features returns the features map from configStore
|
// Features returns the features map from configStore
|
||||||
func (daemon *Daemon) Features() map[string]bool {
|
func (daemon *Daemon) Features() map[string]bool {
|
||||||
daemon.configStore.Lock()
|
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())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
clustertypes "github.com/docker/docker/daemon/cluster/provider"
|
clustertypes "github.com/docker/docker/daemon/cluster/provider"
|
||||||
|
"github.com/docker/docker/daemon/config"
|
||||||
internalnetwork "github.com/docker/docker/daemon/network"
|
internalnetwork "github.com/docker/docker/daemon/network"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/docker/docker/libnetwork"
|
"github.com/docker/docker/libnetwork"
|
||||||
|
@ -160,7 +161,7 @@ func (daemon *Daemon) startIngressWorker() {
|
||||||
select {
|
select {
|
||||||
case r := <-ingressJobsChannel:
|
case r := <-ingressJobsChannel:
|
||||||
if r.create != nil {
|
if r.create != nil {
|
||||||
daemon.setupIngress(r.create, r.ip, ingressID)
|
daemon.setupIngress(daemon.config(), r.create, r.ip, ingressID)
|
||||||
ingressID = r.create.ID
|
ingressID = r.create.ID
|
||||||
} else {
|
} else {
|
||||||
daemon.releaseIngress(ingressID)
|
daemon.releaseIngress(ingressID)
|
||||||
|
@ -199,7 +200,7 @@ func (daemon *Daemon) ReleaseIngress() (<-chan struct{}, error) {
|
||||||
return done, nil
|
return done, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip net.IP, staleID string) {
|
func (daemon *Daemon) setupIngress(cfg *config.Config, create *clustertypes.NetworkCreateRequest, ip net.IP, staleID string) {
|
||||||
controller := daemon.netController
|
controller := daemon.netController
|
||||||
controller.AgentInitWait()
|
controller.AgentInitWait()
|
||||||
|
|
||||||
|
@ -207,7 +208,7 @@ func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip
|
||||||
daemon.releaseIngress(staleID)
|
daemon.releaseIngress(staleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true); err != nil {
|
if _, err := daemon.createNetwork(cfg, create.NetworkCreateRequest, create.ID, true); err != nil {
|
||||||
// If it is any other error other than already
|
// If it is any other error other than already
|
||||||
// exists error log error and return.
|
// exists error log error and return.
|
||||||
if _, ok := err.(libnetwork.NetworkNameError); !ok {
|
if _, ok := err.(libnetwork.NetworkNameError); !ok {
|
||||||
|
@ -277,16 +278,16 @@ func (daemon *Daemon) WaitForDetachment(ctx context.Context, networkName, networ
|
||||||
|
|
||||||
// CreateManagedNetwork creates an agent network.
|
// CreateManagedNetwork creates an agent network.
|
||||||
func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
|
func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
|
||||||
_, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true)
|
_, err := daemon.createNetwork(daemon.config(), create.NetworkCreateRequest, create.ID, true)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNetwork creates a network with the given name, driver and other optional parameters
|
// CreateNetwork creates a network with the given name, driver and other optional parameters
|
||||||
func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
|
func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
|
||||||
return daemon.createNetwork(create, "", false)
|
return daemon.createNetwork(daemon.config(), create, "", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
|
func (daemon *Daemon) createNetwork(cfg *config.Config, create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
|
||||||
if runconfig.IsPreDefinedNetwork(create.Name) {
|
if runconfig.IsPreDefinedNetwork(create.Name) {
|
||||||
return nil, PredefinedNetworkError(create.Name)
|
return nil, PredefinedNetworkError(create.Name)
|
||||||
}
|
}
|
||||||
|
@ -319,7 +320,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
|
||||||
for k, v := range create.Options {
|
for k, v := range create.Options {
|
||||||
networkOptions[k] = v
|
networkOptions[k] = v
|
||||||
}
|
}
|
||||||
if defaultOpts, ok := daemon.configStore.DefaultNetworkOpts[driver]; create.ConfigFrom == nil && ok {
|
if defaultOpts, ok := cfg.DefaultNetworkOpts[driver]; create.ConfigFrom == nil && ok {
|
||||||
for k, v := range defaultOpts {
|
for k, v := range defaultOpts {
|
||||||
if _, ok := networkOptions[k]; !ok {
|
if _, ok := networkOptions[k]; !ok {
|
||||||
logrus.WithFields(logrus.Fields{"driver": driver, "network": id, k: v}).Debug("Applying network default option")
|
logrus.WithFields(logrus.Fields{"driver": driver, "network": id, k: v}).Debug("Applying network default option")
|
||||||
|
|
|
@ -36,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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"])
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
270
daemon/reload.go
270
daemon/reload.go
|
@ -5,10 +5,55 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/docker/docker/daemon/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// reloadTxn is used to defer side effects of a config reload.
|
||||||
|
type reloadTxn struct {
|
||||||
|
onCommit, onRollback []func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnCommit defers a function to be called when a config reload is being finalized.
|
||||||
|
// The error returned from cb is purely informational.
|
||||||
|
func (tx *reloadTxn) OnCommit(cb func() error) {
|
||||||
|
tx.onCommit = append(tx.onCommit, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRollback defers a function to be called when a config reload is aborted.
|
||||||
|
// The error returned from cb is purely informational.
|
||||||
|
func (tx *reloadTxn) OnRollback(cb func() error) {
|
||||||
|
tx.onCommit = append(tx.onRollback, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *reloadTxn) run(cbs []func() error) error {
|
||||||
|
tx.onCommit = nil
|
||||||
|
tx.onRollback = nil
|
||||||
|
|
||||||
|
var res *multierror.Error
|
||||||
|
for _, cb := range cbs {
|
||||||
|
res = multierror.Append(res, cb())
|
||||||
|
}
|
||||||
|
return res.ErrorOrNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit calls all functions registered with OnCommit.
|
||||||
|
// Any errors returned by the functions are collated into a
|
||||||
|
// *github.com/hashicorp/go-multierror.Error value.
|
||||||
|
func (tx *reloadTxn) Commit() error {
|
||||||
|
return tx.run(tx.onCommit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rollback calls all functions registered with OnRollback.
|
||||||
|
// Any errors returned by the functions are collated into a
|
||||||
|
// *github.com/hashicorp/go-multierror.Error value.
|
||||||
|
func (tx *reloadTxn) Rollback() error {
|
||||||
|
return tx.run(tx.onRollback)
|
||||||
|
}
|
||||||
|
|
||||||
// Reload modifies the live daemon configuration from conf.
|
// Reload modifies the live daemon configuration from conf.
|
||||||
// conf is assumed to be a validated configuration.
|
// conf is assumed to be a validated configuration.
|
||||||
//
|
//
|
||||||
|
@ -24,66 +69,63 @@ import (
|
||||||
// - Insecure registries
|
// - Insecure registries
|
||||||
// - Registry mirrors
|
// - Registry mirrors
|
||||||
// - Daemon live restore
|
// - Daemon live restore
|
||||||
func (daemon *Daemon) Reload(conf *config.Config) (err error) {
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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 != "" {
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
|
"github.com/docker/docker/daemon/config"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/docker/docker/libcontainerd"
|
"github.com/docker/docker/libcontainerd"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -17,7 +18,8 @@ import (
|
||||||
|
|
||||||
// ContainerStart starts a container.
|
// ContainerStart starts a container.
|
||||||
func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
|
func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
|
||||||
if checkpoint != "" && !daemon.HasExperimental() {
|
daemonCfg := daemon.config()
|
||||||
|
if checkpoint != "" && !daemonCfg.Experimental {
|
||||||
return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
|
return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +57,7 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
|
||||||
if hostConfig != nil {
|
if hostConfig != nil {
|
||||||
logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
|
logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
|
||||||
oldNetworkMode := ctr.HostConfig.NetworkMode
|
oldNetworkMode := ctr.HostConfig.NetworkMode
|
||||||
if err := daemon.setSecurityOptions(ctr, hostConfig); err != nil {
|
if err := daemon.setSecurityOptions(daemonCfg, ctr, hostConfig); err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
|
if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
|
||||||
|
@ -83,24 +85,24 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
|
||||||
|
|
||||||
// check if hostConfig is in line with the current system settings.
|
// check if hostConfig is in line with the current system settings.
|
||||||
// It may happen cgroups are umounted or the like.
|
// It may happen cgroups are umounted or the like.
|
||||||
if _, err = daemon.verifyContainerSettings(ctr.HostConfig, nil, false); err != nil {
|
if _, err = daemon.verifyContainerSettings(daemonCfg, ctr.HostConfig, nil, false); err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
// Adapt for old containers in case we have updates in this function and
|
// Adapt for old containers in case we have updates in this function and
|
||||||
// old containers never have chance to call the new function in create stage.
|
// old containers never have chance to call the new function in create stage.
|
||||||
if hostConfig != nil {
|
if hostConfig != nil {
|
||||||
if err := daemon.adaptContainerSettings(ctr.HostConfig, false); err != nil {
|
if err := daemon.adaptContainerSettings(daemonCfg, ctr.HostConfig, false); err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return daemon.containerStart(ctx, ctr, checkpoint, checkpointDir, true)
|
return daemon.containerStart(ctx, daemonCfg, ctr, checkpoint, checkpointDir, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerStart prepares the container to run by setting up everything the
|
// containerStart prepares the container to run by setting up everything the
|
||||||
// container needs, such as storage and networking, as well as links
|
// container needs, such as storage and networking, as well as links
|
||||||
// between containers. The container is left waiting for a signal to
|
// between containers. The container is left waiting for a signal to
|
||||||
// begin running.
|
// begin running.
|
||||||
func (daemon *Daemon) containerStart(ctx context.Context, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
|
func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *config.Config, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
@ -136,7 +138,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
|
||||||
// if containers AutoRemove flag is set, remove it after clean up
|
// if containers AutoRemove flag is set, remove it after clean up
|
||||||
if container.HostConfig.AutoRemove {
|
if container.HostConfig.AutoRemove {
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
if err := daemon.ContainerRm(container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
|
if err := daemon.containerRm(daemonCfg, container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
|
||||||
logrus.Errorf("can't remove container %s: %v", container.ID, err)
|
logrus.Errorf("can't remove container %s: %v", container.ID, err)
|
||||||
}
|
}
|
||||||
container.Lock()
|
container.Lock()
|
||||||
|
@ -148,11 +150,11 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := daemon.initializeNetworking(container); err != nil {
|
if err := daemon.initializeNetworking(daemonCfg, container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
spec, err := daemon.createSpec(ctx, container)
|
spec, err := daemon.createSpec(ctx, daemonCfg, container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errdefs.System(err)
|
return errdefs.System(err)
|
||||||
}
|
}
|
||||||
|
@ -173,7 +175,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shim, createOptions, err := daemon.getLibcontainerdCreateOptions(container)
|
shim, createOptions, err := daemon.getLibcontainerdCreateOptions(daemonCfg, container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
10
vendor.sum
10
vendor.sum
|
@ -1034,6 +1034,8 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go
|
||||||
github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
|
github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
|
||||||
github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
|
github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||||
|
@ -1048,6 +1050,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
|
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
|
||||||
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
|
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
|
||||||
github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f h1:9wobL03Y6U8azuDLUqYblbUdVU9jpjqecDdW7w4wZtI=
|
github.com/moby/buildkit v0.11.7-0.20230525183624-798ad6b0ce9f h1:9wobL03Y6U8azuDLUqYblbUdVU9jpjqecDdW7w4wZtI=
|
||||||
|
@ -1121,8 +1125,9 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
|
|
||||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
@ -1133,8 +1138,9 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT
|
||||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||||
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
|
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||||
|
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
|
|
21
vendor/github.com/mitchellh/copystructure/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mitchellh/copystructure/LICENSE
generated
vendored
Normal 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
21
vendor/github.com/mitchellh/copystructure/README.md
generated
vendored
Normal 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.
|
15
vendor/github.com/mitchellh/copystructure/copier_time.go
generated
vendored
Normal file
15
vendor/github.com/mitchellh/copystructure/copier_time.go
generated
vendored
Normal 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
|
||||||
|
}
|
631
vendor/github.com/mitchellh/copystructure/copystructure.go
generated
vendored
Normal file
631
vendor/github.com/mitchellh/copystructure/copystructure.go
generated
vendored
Normal 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
1
vendor/github.com/mitchellh/reflectwalk/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
language: go
|
21
vendor/github.com/mitchellh/reflectwalk/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mitchellh/reflectwalk/LICENSE
generated
vendored
Normal 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
6
vendor/github.com/mitchellh/reflectwalk/README.md
generated
vendored
Normal 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
19
vendor/github.com/mitchellh/reflectwalk/location.go
generated
vendored
Normal 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
|
||||||
|
)
|
16
vendor/github.com/mitchellh/reflectwalk/location_string.go
generated
vendored
Normal file
16
vendor/github.com/mitchellh/reflectwalk/location_string.go
generated
vendored
Normal 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
420
vendor/github.com/mitchellh/reflectwalk/reflectwalk.go
generated
vendored
Normal 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
10
vendor/modules.txt
vendored
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue