daemon: reload runtimes w/o breaking containers
The existing runtimes reload logic went to great lengths to replace the directory containing runtime wrapper scripts as atomically as possible within the limitations of the Linux filesystem ABI. Trouble is, atomically swapping the wrapper scripts directory solves the wrong problem! The runtime configuration is "locked in" when a container is started, including the path to the runC binary. If a container is started with a runtime which requires a daemon-managed wrapper script and then the daemon is reloaded with a config which no longer requires the wrapper script (i.e. some args -> no args, or the runtime is dropped from the config), that container would become unmanageable. Any attempts to stop, exec or otherwise perform lifecycle management operations on the container are likely to fail due to the wrapper script no longer existing at its original path. Atomically swapping the wrapper scripts is also incompatible with the read-copy-update paradigm for reloading configuration. A handler in the daemon could retain a reference to the pre-reload configuration for an indeterminate amount of time after the daemon configuration has been reloaded and updated. It is possible for the daemon to attempt to start a container using a deleted wrapper script if a request to run a container races a reload. Solve the problem of deleting referenced wrapper scripts by ensuring that all wrapper scripts are *immutable* for the lifetime of the daemon process. Any given runtime wrapper script must always exist with the same contents, no matter how many times the daemon config is reloaded, or what changes are made to the config. This is accomplished by using everyone's favourite design pattern: content-addressable storage. Each wrapper script file name is suffixed with the SHA-256 digest of its contents to (probabilistically) guarantee immutability without needing any concurrency control. Stale runtime wrapper scripts are only cleaned up on the next daemon restart. Split the derived runtimes configuration from the user-supplied configuration to have a place to store derived state without mutating the user-supplied configuration or exposing daemon internals in API struct types. Hold the derived state and the user-supplied configuration in a single struct value so that they can be updated as an atomic unit. Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
parent
0b592467d9
commit
d222bf097c
40 changed files with 518 additions and 395 deletions
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/api/types/volume"
|
"github.com/docker/docker/api/types/volume"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -657,16 +656,6 @@ type Runtime struct {
|
||||||
|
|
||||||
Type string `json:"runtimeType,omitempty"`
|
Type string `json:"runtimeType,omitempty"`
|
||||||
Options map[string]interface{} `json:"options,omitempty"`
|
Options map[string]interface{} `json:"options,omitempty"`
|
||||||
|
|
||||||
// This is exposed here only for internal use
|
|
||||||
ShimConfig *ShimConfig `json:"-"`
|
|
||||||
Features *features.Features `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShimConfig is used by runtime to configure containerd shims
|
|
||||||
type ShimConfig struct {
|
|
||||||
Binary string
|
|
||||||
Opts interface{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiskUsageObject represents an object type used for disk usage query filtering.
|
// DiskUsageObject represents an object type used for disk usage query filtering.
|
||||||
|
|
|
@ -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(cli.Config)
|
d.RestartSwarmContainers()
|
||||||
|
|
||||||
logrus.Info("Daemon has completed initialization")
|
logrus.Info("Daemon has completed initialization")
|
||||||
|
|
||||||
|
|
|
@ -81,15 +81,6 @@ type Config struct {
|
||||||
Rootless bool `json:"rootless,omitempty"`
|
Rootless bool `json:"rootless,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRuntime returns the runtime path and arguments for a given
|
|
||||||
// runtime name
|
|
||||||
func (conf *Config) GetRuntime(name string) *types.Runtime {
|
|
||||||
if rt, ok := conf.Runtimes[name]; ok {
|
|
||||||
return &rt
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
||||||
return conf.Runtimes
|
return conf.Runtimes
|
||||||
|
|
|
@ -30,12 +30,6 @@ type Config struct {
|
||||||
// for the Windows daemon.)
|
// for the Windows daemon.)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRuntime returns the runtime path and arguments for a given
|
|
||||||
// runtime name
|
|
||||||
func (conf *Config) GetRuntime(name string) *types.Runtime {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
||||||
return map[string]types.Runtime{}
|
return map[string]types.Runtime{}
|
||||||
|
|
|
@ -235,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(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) (warnings []string, err error) {
|
func (daemon *Daemon) verifyContainerSettings(daemonCfg *configStore, 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
|
||||||
|
|
|
@ -1075,7 +1075,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := daemon.connectToNetwork(daemon.config(), container, idOrName, endpointConfig, true); err != nil {
|
if err := daemon.connectToNetwork(&daemon.config().Config, container, idOrName, endpointConfig, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,11 +31,14 @@ func TestContainerWarningHostAndPublishPorts(t *testing.T) {
|
||||||
NetworkMode: "host",
|
NetworkMode: "host",
|
||||||
PortBindings: tc.ports,
|
PortBindings: tc.ports,
|
||||||
}
|
}
|
||||||
cs := &config.Config{}
|
|
||||||
configureRuntimes(cs)
|
|
||||||
d := &Daemon{}
|
d := &Daemon{}
|
||||||
d.configStore.Store(cs)
|
cfg, err := config.New()
|
||||||
wrns, err := d.verifyContainerSettings(cs, hostConfig, &containertypes.Config{}, false)
|
assert.NilError(t, err)
|
||||||
|
configureRuntimes(cfg)
|
||||||
|
runtimes, err := setupRuntimes(cfg)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
daemonCfg := &configStore{Config: *cfg, Runtimes: runtimes}
|
||||||
|
wrns, err := d.verifyContainerSettings(daemonCfg, hostConfig, &containertypes.Config{}, false)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, tc.warnings, wrns)
|
assert.DeepEqual(t, tc.warnings, wrns)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *config.Config, opts createOpts) (containertypes.CreateResponse, error) {
|
func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *configStore, 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"))
|
||||||
|
@ -95,12 +95,12 @@ func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *config.Con
|
||||||
if opts.params.HostConfig == nil {
|
if opts.params.HostConfig == nil {
|
||||||
opts.params.HostConfig = &containertypes.HostConfig{}
|
opts.params.HostConfig = &containertypes.HostConfig{}
|
||||||
}
|
}
|
||||||
err = daemon.adaptContainerSettings(daemonCfg, opts.params.HostConfig, opts.params.AdjustCPUShares)
|
err = daemon.adaptContainerSettings(&daemonCfg.Config, 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, daemonCfg, opts)
|
ctr, err := daemon.create(ctx, &daemonCfg.Config, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return containertypes.CreateResponse{Warnings: warnings}, err
|
return containertypes.CreateResponse{Warnings: warnings}, err
|
||||||
}
|
}
|
||||||
|
|
125
daemon/daemon.go
125
daemon/daemon.go
|
@ -77,6 +77,12 @@ import (
|
||||||
"resenje.org/singleflight"
|
"resenje.org/singleflight"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type configStore struct {
|
||||||
|
config.Config
|
||||||
|
|
||||||
|
Runtimes runtimes
|
||||||
|
}
|
||||||
|
|
||||||
// Daemon holds information about the Docker daemon.
|
// Daemon holds information about the Docker daemon.
|
||||||
type Daemon struct {
|
type Daemon struct {
|
||||||
id string
|
id string
|
||||||
|
@ -85,7 +91,7 @@ type Daemon struct {
|
||||||
containersReplica *container.ViewDB
|
containersReplica *container.ViewDB
|
||||||
execCommands *container.ExecStore
|
execCommands *container.ExecStore
|
||||||
imageService ImageService
|
imageService ImageService
|
||||||
configStore atomic.Pointer[config.Config]
|
configStore atomic.Pointer[configStore]
|
||||||
configReload sync.Mutex
|
configReload sync.Mutex
|
||||||
statsCollector *stats.Collector
|
statsCollector *stats.Collector
|
||||||
defaultLogConfig containertypes.LogConfig
|
defaultLogConfig containertypes.LogConfig
|
||||||
|
@ -159,10 +165,10 @@ func (daemon *Daemon) StoreHosts(hosts []string) {
|
||||||
// lifetime of an operation, the configuration pointer should be passed down 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
|
// call stack, like one would a [context.Context] value. Only the entrypoints
|
||||||
// for operations, the outermost functions, should call this function.
|
// for operations, the outermost functions, should call this function.
|
||||||
func (daemon *Daemon) config() *config.Config {
|
func (daemon *Daemon) config() *configStore {
|
||||||
cfg := daemon.configStore.Load()
|
cfg := daemon.configStore.Load()
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return &config.Config{}
|
return &configStore{}
|
||||||
}
|
}
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
@ -247,7 +253,7 @@ type layerAccessor interface {
|
||||||
GetLayerByID(cid string) (layer.RWLayer, error)
|
GetLayerByID(cid string) (layer.RWLayer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) restore(cfg *config.Config) error {
|
func (daemon *Daemon) restore(cfg *configStore) error {
|
||||||
var mapLock sync.Mutex
|
var mapLock sync.Mutex
|
||||||
containers := make(map[string]*container.Container)
|
containers := make(map[string]*container.Container)
|
||||||
|
|
||||||
|
@ -467,7 +473,7 @@ func (daemon *Daemon) restore(cfg *config.Config) 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(cfg, c)
|
options, err := daemon.buildSandboxOptions(&cfg.Config, 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")
|
||||||
}
|
}
|
||||||
|
@ -523,7 +529,7 @@ func (daemon *Daemon) restore(cfg *config.Config) 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(cfg, activeSandboxes); err != nil {
|
if err = daemon.initNetworkController(&cfg.Config, activeSandboxes); err != nil {
|
||||||
return fmt.Errorf("Error initializing network controller: %v", err)
|
return fmt.Errorf("Error initializing network controller: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +592,7 @@ func (daemon *Daemon) restore(cfg *config.Config) error {
|
||||||
go func(cid string) {
|
go func(cid string) {
|
||||||
_ = sem.Acquire(context.Background(), 1)
|
_ = sem.Acquire(context.Background(), 1)
|
||||||
|
|
||||||
if err := daemon.containerRm(cfg, cid, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
|
if err := daemon.containerRm(&cfg.Config, 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,9 +640,11 @@ func (daemon *Daemon) restore(cfg *config.Config) 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(cfg *config.Config) {
|
func (daemon *Daemon) RestartSwarmContainers() {
|
||||||
ctx := context.Background()
|
daemon.restartSwarmContainers(context.Background(), daemon.config())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) restartSwarmContainers(ctx context.Context, cfg *configStore) {
|
||||||
// parallelLimit is the maximum number of parallel startup jobs that we
|
// parallelLimit is the maximum number of parallel startup jobs that we
|
||||||
// allow (this is the limited used for all startup semaphores). The multipler
|
// allow (this is the limited used for all startup semaphores). The multipler
|
||||||
// (128) was chosen after some fairly significant benchmarking -- don't change
|
// (128) was chosen after some fairly significant benchmarking -- don't change
|
||||||
|
@ -806,11 +814,23 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
os.Setenv("TMPDIR", realTmp)
|
os.Setenv("TMPDIR", realTmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := initRuntimesDir(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
runtimes, err := setupRuntimes(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
d := &Daemon{
|
d := &Daemon{
|
||||||
PluginStore: pluginStore,
|
PluginStore: pluginStore,
|
||||||
startupDone: make(chan struct{}),
|
startupDone: make(chan struct{}),
|
||||||
}
|
}
|
||||||
d.configStore.Store(config)
|
configStore := &configStore{
|
||||||
|
Config: *config,
|
||||||
|
Runtimes: runtimes,
|
||||||
|
}
|
||||||
|
d.configStore.Store(configStore)
|
||||||
|
|
||||||
// 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") != "" {
|
||||||
|
@ -830,27 +850,27 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := d.setGenericResources(config); err != nil {
|
if err := d.setGenericResources(&configStore.Config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// set up SIGUSR1 handler on Unix-like systems, or a Win32 global event
|
// set up SIGUSR1 handler on Unix-like systems, or a Win32 global event
|
||||||
// on Windows to dump Go routine stacks
|
// on Windows to dump Go routine stacks
|
||||||
stackDumpDir := config.Root
|
stackDumpDir := configStore.Root
|
||||||
if execRoot := config.GetExecRoot(); execRoot != "" {
|
if execRoot := configStore.GetExecRoot(); execRoot != "" {
|
||||||
stackDumpDir = execRoot
|
stackDumpDir = execRoot
|
||||||
}
|
}
|
||||||
d.setupDumpStackTrap(stackDumpDir)
|
d.setupDumpStackTrap(stackDumpDir)
|
||||||
|
|
||||||
if err := d.setupSeccompProfile(config); err != nil {
|
if err := d.setupSeccompProfile(&configStore.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(config); err != nil {
|
if err := d.setDefaultIsolation(&configStore.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := configureMaxThreads(config); err != nil {
|
if err := configureMaxThreads(&configStore.Config); err != nil {
|
||||||
logrus.Warnf("Failed to configure golang's threads limit: %v", err)
|
logrus.Warnf("Failed to configure golang's threads limit: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -859,7 +879,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
logrus.Errorf(err.Error())
|
logrus.Errorf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
daemonRepo := filepath.Join(config.Root, "containers")
|
daemonRepo := filepath.Join(configStore.Root, "containers")
|
||||||
if err := idtools.MkdirAllAndChown(daemonRepo, 0o710, idtools.Identity{
|
if err := idtools.MkdirAllAndChown(daemonRepo, 0o710, idtools.Identity{
|
||||||
UID: idtools.CurrentIdentity().UID,
|
UID: idtools.CurrentIdentity().UID,
|
||||||
GID: rootIDs.GID,
|
GID: rootIDs.GID,
|
||||||
|
@ -867,20 +887,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the directory where we'll store the runtime scripts (i.e. in
|
|
||||||
// order to support runtimeArgs)
|
|
||||||
if err = os.Mkdir(filepath.Join(config.Root, "runtimes"), 0o700); err != nil && !errors.Is(err, os.ErrExist) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := d.loadRuntimes(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isWindows {
|
if isWindows {
|
||||||
// Note that permissions (0o700) are ignored on Windows; passing them to
|
// Note that permissions (0o700) are ignored on Windows; passing them to
|
||||||
// show intent only. We could consider using idtools.MkdirAndChown here
|
// show intent only. We could consider using idtools.MkdirAndChown here
|
||||||
// to apply an ACL.
|
// to apply an ACL.
|
||||||
if err = os.Mkdir(filepath.Join(config.Root, "credentialspecs"), 0o700); err != nil && !errors.Is(err, os.ErrExist) {
|
if err = os.Mkdir(filepath.Join(configStore.Root, "credentialspecs"), 0o700); err != nil && !errors.Is(err, os.ErrExist) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -888,7 +899,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(config)
|
metricsSockPath, err := d.listenMetricsSock(&configStore.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -927,20 +938,20 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
|
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ContainerdAddr != "" {
|
if configStore.ContainerdAddr != "" {
|
||||||
d.containerdCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
|
d.containerdCli, err = containerd.New(configStore.ContainerdAddr, containerd.WithDefaultNamespace(configStore.ContainerdNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
|
return nil, errors.Wrapf(err, "failed to dial %q", configStore.ContainerdAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createPluginExec := func(m *plugin.Manager) (plugin.Executor, error) {
|
createPluginExec := func(m *plugin.Manager) (plugin.Executor, error) {
|
||||||
var pluginCli *containerd.Client
|
var pluginCli *containerd.Client
|
||||||
|
|
||||||
if config.ContainerdAddr != "" {
|
if configStore.ContainerdAddr != "" {
|
||||||
pluginCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdPluginNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
|
pluginCli, err = containerd.New(configStore.ContainerdAddr, containerd.WithDefaultNamespace(configStore.ContainerdPluginNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
|
return nil, errors.Wrapf(err, "failed to dial %q", configStore.ContainerdAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,22 +960,22 @@ 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, config.DefaultRuntime)
|
shim, shimOpts, err = runtimes.Get(configStore.DefaultRuntime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pluginexec.New(ctx, getPluginExecRoot(config), pluginCli, config.ContainerdPluginNamespace, m, shim, shimOpts)
|
return pluginexec.New(ctx, getPluginExecRoot(&configStore.Config), pluginCli, configStore.ContainerdPluginNamespace, m, shim, shimOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugin system initialization should happen before restore. Do not change order.
|
// Plugin system initialization should happen before restore. Do not change order.
|
||||||
d.pluginManager, err = plugin.NewManager(plugin.ManagerConfig{
|
d.pluginManager, err = plugin.NewManager(plugin.ManagerConfig{
|
||||||
Root: filepath.Join(config.Root, "plugins"),
|
Root: filepath.Join(configStore.Root, "plugins"),
|
||||||
ExecRoot: getPluginExecRoot(config),
|
ExecRoot: getPluginExecRoot(&configStore.Config),
|
||||||
Store: d.PluginStore,
|
Store: d.PluginStore,
|
||||||
CreateExecutor: createPluginExec,
|
CreateExecutor: createPluginExec,
|
||||||
RegistryService: registryService,
|
RegistryService: registryService,
|
||||||
LiveRestoreEnabled: config.LiveRestoreEnabled,
|
LiveRestoreEnabled: configStore.LiveRestoreEnabled,
|
||||||
LogPluginEvent: d.LogPluginEvent, // todo: make private
|
LogPluginEvent: d.LogPluginEvent, // todo: make private
|
||||||
AuthzMiddleware: authzMiddleware,
|
AuthzMiddleware: authzMiddleware,
|
||||||
})
|
})
|
||||||
|
@ -972,13 +983,13 @@ 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
d.defaultLogConfig, err = defaultLogConfig(config)
|
d.defaultLogConfig, err = defaultLogConfig(&configStore.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to set log opts")
|
return nil, errors.Wrap(err, "failed to set log opts")
|
||||||
}
|
}
|
||||||
logrus.Debugf("Using default logging driver %s", d.defaultLogConfig.Type)
|
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(configStore.Root, d.PluginStore, rootIDs, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -991,11 +1002,11 @@ 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(config).CgroupDevicesEnabled {
|
if runtime.GOOS == "linux" && !userns.RunningInUserNS() && !getSysInfo(&configStore.Config).CgroupDevicesEnabled {
|
||||||
return nil, errors.New("Devices cgroup isn't mounted")
|
return nil, errors.New("Devices cgroup isn't mounted")
|
||||||
}
|
}
|
||||||
|
|
||||||
d.id, err = loadOrCreateID(filepath.Join(config.Root, "engine-id"))
|
d.id, err = loadOrCreateID(filepath.Join(configStore.Root, "engine-id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1008,7 +1019,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
d.statsCollector = d.newStatsCollector(1 * time.Second)
|
d.statsCollector = d.newStatsCollector(1 * time.Second)
|
||||||
|
|
||||||
d.EventsService = events.New()
|
d.EventsService = events.New()
|
||||||
d.root = config.Root
|
d.root = configStore.Root
|
||||||
d.idMapping = idMapping
|
d.idMapping = idMapping
|
||||||
|
|
||||||
d.linkIndex = newLinkIndex()
|
d.linkIndex = newLinkIndex()
|
||||||
|
@ -1023,7 +1034,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
} else if driverName != "" {
|
} else if driverName != "" {
|
||||||
logrus.Infof("Setting the storage driver from the $DOCKER_DRIVER environment variable (%s)", driverName)
|
logrus.Infof("Setting the storage driver from the $DOCKER_DRIVER environment variable (%s)", driverName)
|
||||||
} else {
|
} else {
|
||||||
driverName = config.GraphDriver
|
driverName = configStore.GraphDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.UsesSnapshotter() {
|
if d.UsesSnapshotter() {
|
||||||
|
@ -1039,7 +1050,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
|
|
||||||
// Configure and validate the kernels security support. Note this is a Linux/FreeBSD
|
// Configure and validate the kernels security support. Note this is a Linux/FreeBSD
|
||||||
// operation only, so it is safe to pass *just* the runtime OS graphdriver.
|
// operation only, so it is safe to pass *just* the runtime OS graphdriver.
|
||||||
if err := configureKernelSecuritySupport(config, driverName); err != nil {
|
if err := configureKernelSecuritySupport(&configStore.Config, driverName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.imageService = ctrd.NewService(ctrd.ImageServiceConfig{
|
d.imageService = ctrd.NewService(ctrd.ImageServiceConfig{
|
||||||
|
@ -1052,13 +1063,13 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
layerStore, err := layer.NewStoreFromOptions(layer.StoreOptions{
|
layerStore, err := layer.NewStoreFromOptions(layer.StoreOptions{
|
||||||
Root: config.Root,
|
Root: configStore.Root,
|
||||||
MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
|
MetadataStorePathTemplate: filepath.Join(configStore.Root, "image", "%s", "layerdb"),
|
||||||
GraphDriver: driverName,
|
GraphDriver: driverName,
|
||||||
GraphDriverOptions: config.GraphOptions,
|
GraphDriverOptions: configStore.GraphOptions,
|
||||||
IDMapping: idMapping,
|
IDMapping: idMapping,
|
||||||
PluginGetter: d.PluginStore,
|
PluginGetter: d.PluginStore,
|
||||||
ExperimentalEnabled: config.Experimental,
|
ExperimentalEnabled: configStore.Experimental,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1066,11 +1077,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
|
|
||||||
// Configure and validate the kernels security support. Note this is a Linux/FreeBSD
|
// Configure and validate the kernels security support. Note this is a Linux/FreeBSD
|
||||||
// operation only, so it is safe to pass *just* the runtime OS graphdriver.
|
// operation only, so it is safe to pass *just* the runtime OS graphdriver.
|
||||||
if err := configureKernelSecuritySupport(config, layerStore.DriverName()); err != nil {
|
if err := configureKernelSecuritySupport(&configStore.Config, layerStore.DriverName()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
imageRoot := filepath.Join(config.Root, "image", layerStore.DriverName())
|
imageRoot := filepath.Join(configStore.Root, "image", layerStore.DriverName())
|
||||||
ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
|
ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1144,11 +1155,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||||
|
|
||||||
go d.execCommandGC()
|
go d.execCommandGC()
|
||||||
|
|
||||||
if err := d.initLibcontainerd(ctx, config); err != nil {
|
if err := d.initLibcontainerd(ctx, &configStore.Config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.restore(config); err != nil {
|
if err := d.restore(configStore); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
close(d.startupDone)
|
close(d.startupDone)
|
||||||
|
@ -1210,7 +1221,7 @@ 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 {
|
||||||
return daemon.shutdownTimeout(daemon.config())
|
return daemon.shutdownTimeout(&daemon.config().Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) shutdownTimeout(cfg *config.Config) int {
|
func (daemon *Daemon) shutdownTimeout(cfg *config.Config) int {
|
||||||
|
@ -1241,7 +1252,7 @@ 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.
|
||||||
|
|
||||||
cfg := daemon.config()
|
cfg := &daemon.config().Config
|
||||||
if cfg.LiveRestoreEnabled && daemon.containers != nil {
|
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 {
|
||||||
|
@ -1523,7 +1534,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.config())
|
daemon.sysInfo = getSysInfo(&daemon.config().Config)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -239,18 +239,18 @@ func kernelSupportsRecursivelyReadOnly() error {
|
||||||
return kernelSupportsRROErr
|
return kernelSupportsRROErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func supportsRecursivelyReadOnly(cfg *config.Config, runtime string) error {
|
func supportsRecursivelyReadOnly(cfg *configStore, 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 = cfg.DefaultRuntime
|
runtime = cfg.DefaultRuntime
|
||||||
}
|
}
|
||||||
rt := cfg.GetRuntime(runtime)
|
features := cfg.Runtimes.Features(runtime)
|
||||||
if rt.Features == nil {
|
if 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)
|
||||||
}
|
}
|
||||||
for _, s := range rt.Features.MountOptions {
|
for _, s := range features.MountOptions {
|
||||||
if s == "rro" {
|
if s == "rro" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
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/libnetwork/testutils"
|
"github.com/docker/docker/libnetwork/testutils"
|
||||||
"github.com/docker/docker/libnetwork/types"
|
"github.com/docker/docker/libnetwork/types"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
@ -178,7 +177,7 @@ func TestNotCleanupMounts(t *testing.T) {
|
||||||
func TestValidateContainerIsolationLinux(t *testing.T) {
|
func TestValidateContainerIsolationLinux(t *testing.T) {
|
||||||
d := Daemon{}
|
d := Daemon{}
|
||||||
|
|
||||||
_, err := d.verifyContainerSettings(&config.Config{}, &containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false)
|
_, err := d.verifyContainerSettings(&configStore{}, &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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +249,7 @@ func TestRootMountCleanup(t *testing.T) {
|
||||||
testRoot, err := os.MkdirTemp("", t.Name())
|
testRoot, err := os.MkdirTemp("", t.Name())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer os.RemoveAll(testRoot)
|
defer os.RemoveAll(testRoot)
|
||||||
cfg := &config.Config{}
|
cfg := &configStore{}
|
||||||
|
|
||||||
err = mount.MakePrivate(testRoot)
|
err = mount.MakePrivate(testRoot)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
@ -266,16 +265,16 @@ func TestRootMountCleanup(t *testing.T) {
|
||||||
|
|
||||||
d := &Daemon{root: cfg.Root}
|
d := &Daemon{root: cfg.Root}
|
||||||
d.configStore.Store(cfg)
|
d.configStore.Store(cfg)
|
||||||
unmountFile := getUnmountOnShutdownPath(cfg)
|
unmountFile := getUnmountOnShutdownPath(&cfg.Config)
|
||||||
|
|
||||||
t.Run("regular dir no mountpoint", func(t *testing.T) {
|
t.Run("regular dir no mountpoint", func(t *testing.T) {
|
||||||
err = setupDaemonRootPropagation(cfg)
|
err = setupDaemonRootPropagation(&cfg.Config)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
_, err = os.Stat(unmountFile)
|
_, err = os.Stat(unmountFile)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
checkMounted(t, cfg.Root, true)
|
checkMounted(t, cfg.Root, true)
|
||||||
|
|
||||||
assert.Assert(t, d.cleanupMounts(cfg))
|
assert.Assert(t, d.cleanupMounts(&cfg.Config))
|
||||||
checkMounted(t, cfg.Root, false)
|
checkMounted(t, cfg.Root, false)
|
||||||
|
|
||||||
_, err = os.Stat(unmountFile)
|
_, err = os.Stat(unmountFile)
|
||||||
|
@ -287,13 +286,13 @@ func TestRootMountCleanup(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer mount.Unmount(cfg.Root)
|
defer mount.Unmount(cfg.Root)
|
||||||
|
|
||||||
err = setupDaemonRootPropagation(cfg)
|
err = setupDaemonRootPropagation(&cfg.Config)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, ensureShared(cfg.Root))
|
assert.Check(t, ensureShared(cfg.Root))
|
||||||
|
|
||||||
_, 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(cfg))
|
assert.Assert(t, d.cleanupMounts(&cfg.Config))
|
||||||
checkMounted(t, cfg.Root, true)
|
checkMounted(t, cfg.Root, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -303,14 +302,14 @@ func TestRootMountCleanup(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer mount.Unmount(cfg.Root)
|
defer mount.Unmount(cfg.Root)
|
||||||
|
|
||||||
err = setupDaemonRootPropagation(cfg)
|
err = setupDaemonRootPropagation(&cfg.Config)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
if _, err := os.Stat(unmountFile); err == nil {
|
if _, err := os.Stat(unmountFile); err == nil {
|
||||||
t.Fatal("unmount file should not exist")
|
t.Fatal("unmount file should not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Assert(t, d.cleanupMounts(cfg))
|
assert.Assert(t, d.cleanupMounts(&cfg.Config))
|
||||||
checkMounted(t, cfg.Root, true)
|
checkMounted(t, cfg.Root, true)
|
||||||
assert.Assert(t, mount.Unmount(cfg.Root))
|
assert.Assert(t, mount.Unmount(cfg.Root))
|
||||||
})
|
})
|
||||||
|
@ -323,13 +322,13 @@ func TestRootMountCleanup(t *testing.T) {
|
||||||
err = os.WriteFile(unmountFile, nil, 0644)
|
err = os.WriteFile(unmountFile, nil, 0644)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = setupDaemonRootPropagation(cfg)
|
err = setupDaemonRootPropagation(&cfg.Config)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
_, 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(cfg))
|
assert.Assert(t, d.cleanupMounts(&cfg.Config))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ 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"
|
||||||
|
@ -301,7 +300,7 @@ func TestMerge(t *testing.T) {
|
||||||
func TestValidateContainerIsolation(t *testing.T) {
|
func TestValidateContainerIsolation(t *testing.T) {
|
||||||
d := Daemon{}
|
d := Daemon{}
|
||||||
|
|
||||||
_, err := d.verifyContainerSettings(&config.Config{}, &containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false)
|
_, err := d.verifyContainerSettings(&configStore{}, &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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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, daemonCfg *config.Config, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
|
func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *configStore, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
|
||||||
if hostConfig == nil {
|
if hostConfig == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -691,7 +691,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *config.Config, h
|
||||||
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(daemonCfg) {
|
if hostConfig.CgroupParent != "" && UsingSystemd(&daemonCfg.Config) {
|
||||||
// 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\"")
|
||||||
|
@ -701,7 +701,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *config.Config, h
|
||||||
hostConfig.Runtime = daemonCfg.DefaultRuntime
|
hostConfig.Runtime = daemonCfg.DefaultRuntime
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, _, err := daemon.getRuntime(daemonCfg, hostConfig.Runtime); err != nil {
|
if _, _, err := daemonCfg.Runtimes.Get(hostConfig.Runtime); err != nil {
|
||||||
return warnings, err
|
return warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,7 +757,7 @@ func verifyDaemonSettings(conf *config.Config) error {
|
||||||
|
|
||||||
configureRuntimes(conf)
|
configureRuntimes(conf)
|
||||||
if rtName := conf.DefaultRuntime; rtName != "" {
|
if rtName := conf.DefaultRuntime; rtName != "" {
|
||||||
if conf.GetRuntime(rtName) == nil {
|
if _, ok := conf.Runtimes[rtName]; !ok {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,7 @@ func TestParseSecurityOpt(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseNNPSecurityOptions(t *testing.T) {
|
func TestParseNNPSecurityOptions(t *testing.T) {
|
||||||
daemonCfg := &config.Config{NoNewPrivileges: true}
|
daemonCfg := &configStore{Config: config.Config{NoNewPrivileges: true}}
|
||||||
daemon := &Daemon{}
|
daemon := &Daemon{}
|
||||||
daemon.configStore.Store(daemonCfg)
|
daemon.configStore.Store(daemonCfg)
|
||||||
opts := &container.SecurityOptions{}
|
opts := &container.SecurityOptions{}
|
||||||
|
@ -254,7 +254,7 @@ func TestParseNNPSecurityOptions(t *testing.T) {
|
||||||
// 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(daemonCfg, opts, cfg); err != nil {
|
if err := daemon.parseSecurityOpt(&daemonCfg.Config, 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 {
|
||||||
|
@ -265,7 +265,7 @@ func TestParseNNPSecurityOptions(t *testing.T) {
|
||||||
daemonCfg.NoNewPrivileges = false
|
daemonCfg.NoNewPrivileges = false
|
||||||
cfg.SecurityOpt = []string{"no-new-privileges=true"}
|
cfg.SecurityOpt = []string{"no-new-privileges=true"}
|
||||||
|
|
||||||
if err := daemon.parseSecurityOpt(daemonCfg, opts, cfg); err != nil {
|
if err := daemon.parseSecurityOpt(&daemonCfg.Config, 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 {
|
||||||
|
|
|
@ -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, daemonCfg *config.Config, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
|
func verifyPlatformContainerSettings(daemon *Daemon, daemonCfg *configStore, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
|
||||||
if hostConfig == nil {
|
if hostConfig == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -556,10 +556,6 @@ func (daemon *Daemon) setupSeccompProfile(*config.Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) loadRuntimes() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupResolvConf(config *config.Config) {}
|
func setupResolvConf(config *config.Config) {}
|
||||||
|
|
||||||
func getSysInfo(*config.Config) *sysinfo.SysInfo {
|
func getSysInfo(*config.Config) *sysinfo.SysInfo {
|
||||||
|
|
|
@ -25,7 +25,7 @@ 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)
|
return daemon.containerRm(&daemon.config().Config, name, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) containerRm(cfg *config.Config, name string, opts *types.ContainerRmConfig) error {
|
func (daemon *Daemon) containerRm(cfg *config.Config, name string, opts *types.ContainerRmConfig) error {
|
||||||
|
|
|
@ -252,7 +252,7 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio
|
||||||
p.Cwd = "/"
|
p.Cwd = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
daemonCfg := daemon.config()
|
daemonCfg := &daemon.config().Config
|
||||||
if err := daemon.execSetPlatformOpt(ctx, daemonCfg, ec, p); err != nil {
|
if err := daemon.execSetPlatformOpt(ctx, daemonCfg, ec, p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/containerd/containerd/pkg/apparmor"
|
"github.com/containerd/containerd/pkg/apparmor"
|
||||||
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"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
)
|
)
|
||||||
|
@ -50,7 +49,7 @@ func TestExecSetPlatformOptAppArmor(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &config.Config{}
|
cfg := &configStore{}
|
||||||
d := &Daemon{}
|
d := &Daemon{}
|
||||||
d.configStore.Store(cfg)
|
d.configStore.Store(cfg)
|
||||||
|
|
||||||
|
@ -83,7 +82,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(), cfg, ec, p)
|
err := d.execSetPlatformOpt(context.Background(), &cfg.Config, ec, p)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, p.ApparmorProfile, tc.expectedProfile)
|
assert.Equal(t, p.ApparmorProfile, tc.expectedProfile)
|
||||||
})
|
})
|
||||||
|
|
|
@ -64,14 +64,14 @@ func (daemon *Daemon) SystemInfo() *types.Info {
|
||||||
|
|
||||||
daemon.fillContainerStates(v)
|
daemon.fillContainerStates(v)
|
||||||
daemon.fillDebugInfo(v)
|
daemon.fillDebugInfo(v)
|
||||||
daemon.fillAPIInfo(v, cfg)
|
daemon.fillAPIInfo(v, &cfg.Config)
|
||||||
// Retrieve platform specific info
|
// Retrieve platform specific info
|
||||||
daemon.fillPlatformInfo(v, sysInfo, cfg)
|
daemon.fillPlatformInfo(v, sysInfo, &cfg.Config)
|
||||||
daemon.fillDriverInfo(v)
|
daemon.fillDriverInfo(v)
|
||||||
daemon.fillPluginsInfo(v, cfg)
|
daemon.fillPluginsInfo(v, &cfg.Config)
|
||||||
daemon.fillSecurityOptions(v, sysInfo, cfg)
|
daemon.fillSecurityOptions(v, sysInfo, &cfg.Config)
|
||||||
daemon.fillLicense(v)
|
daemon.fillLicense(v)
|
||||||
daemon.fillDefaultAddressPools(v, cfg)
|
daemon.fillDefaultAddressPools(v, &cfg.Config)
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ func (daemon *Daemon) SystemVersion() types.Version {
|
||||||
|
|
||||||
v.Platform.Name = dockerversion.PlatformName
|
v.Platform.Name = dockerversion.PlatformName
|
||||||
|
|
||||||
daemon.fillPlatformVersion(&v, cfg)
|
daemon.fillPlatformVersion(&v, &cfg.Config)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,22 +38,22 @@ 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 = cfg.GetAllRuntimes()
|
v.Runtimes = make(map[string]types.Runtime)
|
||||||
|
for n, r := range cfg.Runtimes {
|
||||||
|
v.Runtimes[n] = types.Runtime{
|
||||||
|
Path: r.Path,
|
||||||
|
Args: append([]string(nil), r.Args...),
|
||||||
|
}
|
||||||
|
}
|
||||||
v.DefaultRuntime = cfg.DefaultRuntime
|
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 := cfg.GetRuntime(v.DefaultRuntime); rt != nil {
|
if _, _, commit, err := parseDefaultRuntimeVersion(cfg); err != nil {
|
||||||
if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
|
logrus.Warnf(err.Error())
|
||||||
if _, _, commit, err := parseRuntimeVersion(string(rv)); err != nil {
|
} else {
|
||||||
logrus.Warnf("failed to parse %s version: %v", rt.Path, err)
|
v.RuncCommit.ID = commit
|
||||||
} else {
|
|
||||||
v.RuncCommit.ID = commit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logrus.Warnf("failed to retrieve %s version: %v", rt.Path, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if rv, err := daemon.containerd.Version(context.Background()); err == nil {
|
if rv, err := daemon.containerd.Version(context.Background()); err == nil {
|
||||||
|
@ -177,23 +177,16 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version, cfg *config.Config)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultRuntime := cfg.DefaultRuntime
|
if _, ver, commit, err := parseDefaultRuntimeVersion(cfg); err != nil {
|
||||||
if rt := cfg.GetRuntime(defaultRuntime); rt != nil {
|
logrus.Warnf(err.Error())
|
||||||
if rv, err := exec.Command(rt.Path, "--version").Output(); err == nil {
|
} else {
|
||||||
if _, ver, commit, err := parseRuntimeVersion(string(rv)); err != nil {
|
v.Components = append(v.Components, types.ComponentVersion{
|
||||||
logrus.Warnf("failed to parse %s version: %v", rt.Path, err)
|
Name: cfg.DefaultRuntime,
|
||||||
} else {
|
Version: ver,
|
||||||
v.Components = append(v.Components, types.ComponentVersion{
|
Details: map[string]string{
|
||||||
Name: defaultRuntime,
|
"GitCommit": commit,
|
||||||
Version: ver,
|
},
|
||||||
Details: map[string]string{
|
})
|
||||||
"GitCommit": commit,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logrus.Warnf("failed to retrieve %s version: %v", rt.Path, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if initBinary, err := cfg.LookupInitPath(); err != nil {
|
if initBinary, err := cfg.LookupInitPath(); err != nil {
|
||||||
|
@ -318,7 +311,7 @@ func parseInitVersion(v string) (version string, commit string, err error) {
|
||||||
// runc version 1.0.0-rc5+dev
|
// runc version 1.0.0-rc5+dev
|
||||||
// commit: 69663f0bd4b60df09991c08812a60108003fa340
|
// commit: 69663f0bd4b60df09991c08812a60108003fa340
|
||||||
// spec: 1.0.0
|
// spec: 1.0.0
|
||||||
func parseRuntimeVersion(v string) (runtime string, version string, commit string, err error) {
|
func parseRuntimeVersion(v string) (runtime, version, commit string, err error) {
|
||||||
lines := strings.Split(strings.TrimSpace(v), "\n")
|
lines := strings.Split(strings.TrimSpace(v), "\n")
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
if strings.Contains(line, "version") {
|
if strings.Contains(line, "version") {
|
||||||
|
@ -338,6 +331,21 @@ func parseRuntimeVersion(v string) (runtime string, version string, commit strin
|
||||||
return runtime, version, commit, err
|
return runtime, version, commit, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseDefaultRuntimeVersion(cfg *config.Config) (runtime, version, commit string, err error) {
|
||||||
|
if rt, ok := cfg.Runtimes[cfg.DefaultRuntime]; ok {
|
||||||
|
rv, err := exec.Command(rt.Path, "--version").Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", fmt.Errorf("failed to retrieve %s version: %w", rt.Path, err)
|
||||||
|
}
|
||||||
|
runtime, version, commit, err := parseRuntimeVersion(string(rv))
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", fmt.Errorf("failed to parse %s version: %w", rt.Path, err)
|
||||||
|
}
|
||||||
|
return runtime, version, commit, err
|
||||||
|
}
|
||||||
|
return "", "", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
func cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo, cfg *config.Config) bool {
|
func cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo, cfg *config.Config) bool {
|
||||||
return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(cfg.CgroupNamespaceMode).IsPrivate()
|
return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(cfg.CgroupNamespaceMode).IsPrivate()
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string,
|
||||||
|
|
||||||
ctr.Lock()
|
ctr.Lock()
|
||||||
|
|
||||||
base, err := daemon.getInspectData(daemon.config(), ctr)
|
base, err := daemon.getInspectData(&daemon.config().Config, ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctr.Unlock()
|
ctr.Unlock()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -106,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(daemon.config(), ctr)
|
base, err := daemon.getInspectData(&daemon.config().Config, ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(daemon.config(), ctr)
|
base, err := daemon.getInspectData(&daemon.config().Config, ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ 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"
|
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
)
|
)
|
||||||
|
@ -24,13 +23,13 @@ func TestGetInspectData(t *testing.T) {
|
||||||
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")
|
||||||
}
|
}
|
||||||
cfg := &config.Config{}
|
cfg := &configStore{}
|
||||||
d.configStore.Store(cfg)
|
d.configStore.Store(cfg)
|
||||||
|
|
||||||
_, err := d.getInspectData(cfg, c)
|
_, err := d.getInspectData(&cfg.Config, 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(cfg, c)
|
_, err = d.getInspectData(&cfg.Config, c)
|
||||||
assert.Check(t, err)
|
assert.Check(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,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(cfg, c)
|
defer daemon.autoRemove(&cfg.Config, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer c.Unlock() // needs to be called before autoRemove
|
defer c.Unlock() // needs to be called before autoRemove
|
||||||
|
@ -131,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(cfg, c)
|
defer daemon.autoRemove(&cfg.Config, c)
|
||||||
if err != restartmanager.ErrRestartCanceled {
|
if err != restartmanager.ErrRestartCanceled {
|
||||||
logrus.Errorf("restartmanger wait error: %+v", err)
|
logrus.Errorf("restartmanger wait error: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,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(daemon.config(), r.create, r.ip, ingressID)
|
daemon.setupIngress(&daemon.config().Config, r.create, r.ip, ingressID)
|
||||||
ingressID = r.create.ID
|
ingressID = r.create.ID
|
||||||
} else {
|
} else {
|
||||||
daemon.releaseIngress(ingressID)
|
daemon.releaseIngress(ingressID)
|
||||||
|
@ -278,13 +278,13 @@ 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(daemon.config(), create.NetworkCreateRequest, create.ID, true)
|
_, err := daemon.createNetwork(&daemon.config().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(daemon.config(), create, "", false)
|
return daemon.createNetwork(&daemon.config().Config, create, "", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) createNetwork(cfg *config.Config, 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) {
|
||||||
|
|
|
@ -489,7 +489,7 @@ func inSlice(slice []string, s string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// withMounts sets the container's mounts
|
// withMounts sets the container's mounts
|
||||||
func withMounts(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Container) coci.SpecOpts {
|
func withMounts(daemon *Daemon, daemonCfg *configStore, 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
|
||||||
|
@ -1019,23 +1019,23 @@ func WithUser(c *container.Container) coci.SpecOpts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *dconfig.Config, c *container.Container) (retSpec *specs.Spec, err error) {
|
func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *configStore, 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, daemonCfg, c),
|
withCommonOptions(daemon, &daemonCfg.Config, c),
|
||||||
withCgroups(daemon, daemonCfg, c),
|
withCgroups(daemon, &daemonCfg.Config, c),
|
||||||
WithResources(c),
|
WithResources(c),
|
||||||
WithSysctls(c),
|
WithSysctls(c),
|
||||||
WithDevices(daemon, c),
|
WithDevices(daemon, c),
|
||||||
withRlimits(daemon, daemonCfg, c),
|
withRlimits(daemon, &daemonCfg.Config, c),
|
||||||
WithNamespaces(daemon, c),
|
WithNamespaces(daemon, c),
|
||||||
WithCapabilities(c),
|
WithCapabilities(c),
|
||||||
WithSeccomp(daemon, c),
|
WithSeccomp(daemon, c),
|
||||||
withMounts(daemon, daemonCfg, c),
|
withMounts(daemon, daemonCfg, c),
|
||||||
withLibnetwork(daemon, daemonCfg, c),
|
withLibnetwork(daemon, &daemonCfg.Config, c),
|
||||||
WithApparmor(c),
|
WithApparmor(c),
|
||||||
WithSelinux(c),
|
WithSelinux(c),
|
||||||
WithOOMScore(&c.HostConfig.OomScoreAdj),
|
WithOOMScore(&c.HostConfig.OomScoreAdj),
|
||||||
|
@ -1069,7 +1069,7 @@ func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *dconfig.Config,
|
||||||
opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
|
opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
|
||||||
}
|
}
|
||||||
if daemonCfg.Rootless {
|
if daemonCfg.Rootless {
|
||||||
opts = append(opts, withRootless(daemon, daemonCfg))
|
opts = append(opts, withRootless(daemon, &daemonCfg.Config))
|
||||||
}
|
}
|
||||||
|
|
||||||
var snapshotter, snapshotKey string
|
var snapshotter, snapshotKey string
|
||||||
|
|
|
@ -82,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(), &config.Config{}, c)
|
_, err := d.createSpec(context.TODO(), &configStore{}, c)
|
||||||
assert.Check(t, err)
|
assert.Check(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,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(), &config.Config{}, c)
|
s, err := d.createSpec(context.TODO(), &configStore{}, 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
|
||||||
|
@ -131,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(), &config.Config{}, c)
|
s, err := d.createSpec(context.TODO(), &configStore{}, 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)
|
||||||
|
@ -147,14 +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(), &config.Config{}, c)
|
s, err = d.createSpec(context.TODO(), &configStore{}, 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
|
||||||
s, err = d.createSpec(context.TODO(), &config.Config{RemappedRoot: "dummy:dummy"}, c)
|
s, err = d.createSpec(context.TODO(), &configStore{Config: config.Config{RemappedRoot: "dummy:dummy"}}, c)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
|
_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
|
||||||
assert.Assert(t, !ok)
|
assert.Assert(t, !ok)
|
||||||
|
@ -162,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(), &config.Config{RemappedRoot: "dummy:dummy"}, c)
|
s, err = d.createSpec(context.TODO(), &configStore{Config: 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")
|
||||||
}
|
}
|
||||||
|
@ -182,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(), &config.Config{}, c)
|
s, err := d.createSpec(context.TODO(), &configStore{}, 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"], "")
|
||||||
|
@ -190,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(), &config.Config{}, c)
|
s, err = d.createSpec(context.TODO(), &configStore{}, 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"])
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ const (
|
||||||
credentialSpecFileLocation = "CredentialSpecs"
|
credentialSpecFileLocation = "CredentialSpecs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *config.Config, c *container.Container) (*specs.Spec, error) {
|
func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *configStore, 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
|
||||||
|
@ -143,7 +143,7 @@ func (daemon *Daemon) createSpec(ctx context.Context, daemonCfg *config.Config,
|
||||||
return nil, errors.Wrapf(err, "container %s", c.ID)
|
return nil, errors.Wrapf(err, "container %s", c.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsSearch := daemon.getDNSSearchSettings(daemonCfg, c)
|
dnsSearch := daemon.getDNSSearchSettings(&daemonCfg.Config, 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
|
||||||
|
|
|
@ -56,7 +56,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := daemon.config()
|
cfg := &daemon.config().Config
|
||||||
allContainers := daemon.List()
|
allContainers := daemon.List()
|
||||||
for _, c := range allContainers {
|
for _, c := range allContainers {
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -72,11 +72,13 @@ func (tx *reloadTxn) Rollback() error {
|
||||||
func (daemon *Daemon) Reload(conf *config.Config) error {
|
func (daemon *Daemon) Reload(conf *config.Config) error {
|
||||||
daemon.configReload.Lock()
|
daemon.configReload.Lock()
|
||||||
defer daemon.configReload.Unlock()
|
defer daemon.configReload.Unlock()
|
||||||
copied, err := copystructure.Copy(daemon.config())
|
copied, err := copystructure.Copy(daemon.config().Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newCfg := copied.(*config.Config)
|
newCfg := &configStore{
|
||||||
|
Config: copied.(config.Config),
|
||||||
|
}
|
||||||
|
|
||||||
attributes := map[string]string{}
|
attributes := map[string]string{}
|
||||||
|
|
||||||
|
@ -91,7 +93,7 @@ func (daemon *Daemon) Reload(conf *config.Config) error {
|
||||||
// executing any registered rollback functions.
|
// executing any registered rollback functions.
|
||||||
|
|
||||||
var txn reloadTxn
|
var txn reloadTxn
|
||||||
for _, reload := range []func(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error{
|
for _, reload := range []func(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error{
|
||||||
daemon.reloadPlatform,
|
daemon.reloadPlatform,
|
||||||
daemon.reloadDebug,
|
daemon.reloadDebug,
|
||||||
daemon.reloadMaxConcurrentDownloadsAndUploads,
|
daemon.reloadMaxConcurrentDownloadsAndUploads,
|
||||||
|
@ -115,7 +117,7 @@ func (daemon *Daemon) Reload(conf *config.Config) error {
|
||||||
*config.Config
|
*config.Config
|
||||||
config.Proxies `json:"proxies"`
|
config.Proxies `json:"proxies"`
|
||||||
}{
|
}{
|
||||||
Config: newCfg,
|
Config: &newCfg.Config,
|
||||||
Proxies: config.Proxies{
|
Proxies: config.Proxies{
|
||||||
HTTPProxy: config.MaskCredentials(newCfg.HTTPProxy),
|
HTTPProxy: config.MaskCredentials(newCfg.HTTPProxy),
|
||||||
HTTPSProxy: config.MaskCredentials(newCfg.HTTPSProxy),
|
HTTPSProxy: config.MaskCredentials(newCfg.HTTPSProxy),
|
||||||
|
@ -141,7 +143,7 @@ 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadDebug(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
// update corresponding configuration
|
// update corresponding configuration
|
||||||
if conf.IsValueSet("debug") {
|
if conf.IsValueSet("debug") {
|
||||||
newCfg.Debug = conf.Debug
|
newCfg.Debug = conf.Debug
|
||||||
|
@ -153,7 +155,7 @@ func (daemon *Daemon) reloadDebug(txn *reloadTxn, newCfg, conf *config.Config, a
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(txn *reloadTxn, newCfg *configStore, 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.
|
||||||
newCfg.MaxConcurrentDownloads = config.DefaultMaxConcurrentDownloads
|
newCfg.MaxConcurrentDownloads = config.DefaultMaxConcurrentDownloads
|
||||||
newCfg.MaxConcurrentUploads = config.DefaultMaxConcurrentUploads
|
newCfg.MaxConcurrentUploads = config.DefaultMaxConcurrentUploads
|
||||||
|
@ -184,7 +186,7 @@ func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(txn *reloadTxn, new
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadMaxDownloadAttempts(txn *reloadTxn, newCfg *configStore, 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.
|
||||||
newCfg.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 {
|
||||||
|
@ -199,7 +201,7 @@ func (daemon *Daemon) reloadMaxDownloadAttempts(txn *reloadTxn, newCfg, conf *co
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadShutdownTimeout(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
// update corresponding configuration
|
// update corresponding configuration
|
||||||
if conf.IsValueSet("shutdown-timeout") {
|
if conf.IsValueSet("shutdown-timeout") {
|
||||||
newCfg.ShutdownTimeout = conf.ShutdownTimeout
|
newCfg.ShutdownTimeout = conf.ShutdownTimeout
|
||||||
|
@ -213,7 +215,7 @@ func (daemon *Daemon) reloadShutdownTimeout(txn *reloadTxn, newCfg, conf *config
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadLabels(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
// update corresponding configuration
|
// update corresponding configuration
|
||||||
if conf.IsValueSet("labels") {
|
if conf.IsValueSet("labels") {
|
||||||
newCfg.Labels = conf.Labels
|
newCfg.Labels = conf.Labels
|
||||||
|
@ -226,7 +228,7 @@ func (daemon *Daemon) reloadLabels(txn *reloadTxn, newCfg, conf *config.Config,
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadRegistryConfig(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
// Update corresponding configuration.
|
// Update corresponding configuration.
|
||||||
if conf.IsValueSet("allow-nondistributable-artifacts") {
|
if conf.IsValueSet("allow-nondistributable-artifacts") {
|
||||||
newCfg.ServiceOptions.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
|
newCfg.ServiceOptions.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
|
||||||
|
@ -253,7 +255,7 @@ func (daemon *Daemon) reloadRegistryConfig(txn *reloadTxn, newCfg, conf *config.
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadLiveRestore(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
// update corresponding configuration
|
// update corresponding configuration
|
||||||
if conf.IsValueSet("live-restore") {
|
if conf.IsValueSet("live-restore") {
|
||||||
newCfg.LiveRestoreEnabled = conf.LiveRestoreEnabled
|
newCfg.LiveRestoreEnabled = conf.LiveRestoreEnabled
|
||||||
|
@ -265,7 +267,7 @@ func (daemon *Daemon) reloadLiveRestore(txn *reloadTxn, newCfg, conf *config.Con
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadNetworkDiagnosticPort(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
txn.OnCommit(func() error {
|
txn.OnCommit(func() error {
|
||||||
if conf == nil || daemon.netController == nil || !conf.IsValueSet("network-diagnostic-port") ||
|
if conf == nil || daemon.netController == nil || !conf.IsValueSet("network-diagnostic-port") ||
|
||||||
conf.NetworkDiagnosticPort < 1 || conf.NetworkDiagnosticPort > 65535 {
|
conf.NetworkDiagnosticPort < 1 || conf.NetworkDiagnosticPort > 65535 {
|
||||||
|
@ -284,7 +286,7 @@ func (daemon *Daemon) reloadNetworkDiagnosticPort(txn *reloadTxn, newCfg, conf *
|
||||||
}
|
}
|
||||||
|
|
||||||
// reloadFeatures updates configuration with enabled/disabled features
|
// reloadFeatures updates configuration with enabled/disabled features
|
||||||
func (daemon *Daemon) reloadFeatures(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadFeatures(txn *reloadTxn, newCfg *configStore, 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
|
||||||
newCfg.Features = conf.Features
|
newCfg.Features = conf.Features
|
||||||
|
|
|
@ -27,7 +27,7 @@ func newDaemonForReloadT(t *testing.T, cfg *config.Config) *Daemon {
|
||||||
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)
|
daemon.configStore.Store(&configStore{Config: *cfg})
|
||||||
return daemon
|
return daemon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,15 +11,19 @@ import (
|
||||||
|
|
||||||
// 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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadPlatform(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
if conf.DefaultRuntime != "" {
|
if conf.DefaultRuntime != "" {
|
||||||
newCfg.DefaultRuntime = conf.DefaultRuntime
|
newCfg.DefaultRuntime = conf.DefaultRuntime
|
||||||
}
|
}
|
||||||
if conf.IsValueSet("runtimes") {
|
if conf.IsValueSet("runtimes") {
|
||||||
newCfg.Runtimes = conf.Runtimes
|
newCfg.Config.Runtimes = conf.Runtimes
|
||||||
txn.OnCommit(func() error { return daemon.initRuntimes(newCfg) })
|
}
|
||||||
|
configureRuntimes(&newCfg.Config)
|
||||||
|
var err error
|
||||||
|
newCfg.Runtimes, err = setupRuntimes(&newCfg.Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
configureRuntimes(newCfg)
|
|
||||||
|
|
||||||
if conf.IsValueSet("default-shm-size") {
|
if conf.IsValueSet("default-shm-size") {
|
||||||
newCfg.ShmSize = conf.ShmSize
|
newCfg.ShmSize = conf.ShmSize
|
||||||
|
@ -35,7 +39,7 @@ func (daemon *Daemon) reloadPlatform(txn *reloadTxn, newCfg, conf *config.Config
|
||||||
|
|
||||||
// Update attributes
|
// Update attributes
|
||||||
var runtimeList bytes.Buffer
|
var runtimeList bytes.Buffer
|
||||||
for name, rt := range newCfg.Runtimes {
|
for name, rt := range newCfg.Config.Runtimes {
|
||||||
if runtimeList.Len() > 0 {
|
if runtimeList.Len() > 0 {
|
||||||
runtimeList.WriteRune(' ')
|
runtimeList.WriteRune(' ')
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(txn *reloadTxn, newCfg, conf *config.Config, attributes map[string]string) error {
|
func (daemon *Daemon) reloadPlatform(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ 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
|
||||||
|
@ -31,7 +30,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, daemonCfg *config.Config, container *container.Container, options containertypes.StopOptions) error {
|
func (daemon *Daemon) containerRestart(ctx context.Context, daemonCfg *configStore, 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) {
|
||||||
|
|
|
@ -4,18 +4,24 @@ package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base32"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
|
v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/docker/docker/daemon/config"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/docker/docker/libcontainerd/shimopts"
|
"github.com/docker/docker/libcontainerd/shimopts"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go/features"
|
"github.com/opencontainers/runtime-spec/specs-go/features"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -23,10 +29,21 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultRuntimeName = "runc"
|
defaultRuntimeName = "runc"
|
||||||
|
|
||||||
linuxShimV2 = "io.containerd.runc.v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type shimConfig struct {
|
||||||
|
Shim string
|
||||||
|
Opts interface{}
|
||||||
|
Features *features.Features
|
||||||
|
|
||||||
|
// Check if the ShimConfig is valid given the current state of the system.
|
||||||
|
PreflightCheck func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type runtimes struct {
|
||||||
|
configured map[string]*shimConfig
|
||||||
|
}
|
||||||
|
|
||||||
func configureRuntimes(conf *config.Config) {
|
func configureRuntimes(conf *config.Config) {
|
||||||
if conf.DefaultRuntime == "" {
|
if conf.DefaultRuntime == "" {
|
||||||
conf.DefaultRuntime = config.StockRuntimeName
|
conf.DefaultRuntime = config.StockRuntimeName
|
||||||
|
@ -34,13 +51,13 @@ func configureRuntimes(conf *config.Config) {
|
||||||
if conf.Runtimes == nil {
|
if conf.Runtimes == nil {
|
||||||
conf.Runtimes = make(map[string]types.Runtime)
|
conf.Runtimes = make(map[string]types.Runtime)
|
||||||
}
|
}
|
||||||
conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, ShimConfig: defaultV2ShimConfig(conf, defaultRuntimeName)}
|
conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName}
|
||||||
conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName]
|
conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName]
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig {
|
func defaultV2ShimConfig(conf *config.Config, runtimePath string) *shimConfig {
|
||||||
return &types.ShimConfig{
|
shim := &shimConfig{
|
||||||
Binary: linuxShimV2,
|
Shim: plugin.RuntimeRuncV2,
|
||||||
Opts: &v2runcoptions.Options{
|
Opts: &v2runcoptions.Options{
|
||||||
BinaryName: runtimePath,
|
BinaryName: runtimePath,
|
||||||
Root: filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName),
|
Root: filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName),
|
||||||
|
@ -48,138 +65,142 @@ func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimCon
|
||||||
NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
|
NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (daemon *Daemon) loadRuntimes() error {
|
var featuresStderr bytes.Buffer
|
||||||
return daemon.initRuntimes(daemon.config())
|
featuresCmd := exec.Command(runtimePath, "features")
|
||||||
}
|
featuresCmd.Stderr = &featuresStderr
|
||||||
|
if featuresB, err := featuresCmd.Output(); err != nil {
|
||||||
func (daemon *Daemon) initRuntimes(cfg *config.Config) (err error) {
|
logrus.WithError(err).Warnf("Failed to run %v: %q", featuresCmd.Args, featuresStderr.String())
|
||||||
runtimeDir := filepath.Join(cfg.Root, "runtimes")
|
} else {
|
||||||
runtimeOldDir := runtimeDir + "-old"
|
var features features.Features
|
||||||
// Remove old temp directory if any
|
if jsonErr := json.Unmarshal(featuresB, &features); jsonErr != nil {
|
||||||
os.RemoveAll(runtimeOldDir)
|
logrus.WithError(err).Warnf("Failed to unmarshal the output of %v as a JSON", featuresCmd.Args)
|
||||||
tmpDir, err := os.MkdirTemp(cfg.Root, "gen-runtimes")
|
} else {
|
||||||
if err != nil {
|
shim.Features = &features
|
||||||
return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
|
}
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if err1 := os.RemoveAll(tmpDir); err1 != nil {
|
|
||||||
logrus.WithError(err1).WithField("dir", tmpDir).
|
|
||||||
Warn("failed to remove tmp dir")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = os.Rename(runtimeDir, runtimeOldDir); err != nil {
|
return shim
|
||||||
logrus.WithError(err).WithField("dir", runtimeDir).
|
}
|
||||||
Warn("failed to rename runtimes dir to old. Will try to removing it")
|
|
||||||
if err = os.RemoveAll(runtimeDir); err != nil {
|
|
||||||
logrus.WithError(err).WithField("dir", runtimeDir).
|
|
||||||
Warn("failed to remove old runtimes dir")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = os.Rename(tmpDir, runtimeDir); err != nil {
|
|
||||||
err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = os.RemoveAll(runtimeOldDir); err != nil {
|
|
||||||
logrus.WithError(err).WithField("dir", runtimeOldDir).
|
|
||||||
Warn("failed to remove old runtimes dir")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for name := range cfg.Runtimes {
|
func runtimeScriptsDir(cfg *config.Config) string {
|
||||||
rt := cfg.Runtimes[name]
|
return filepath.Join(cfg.Root, "runtimes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// initRuntimesDir creates a fresh directory where we'll store the runtime
|
||||||
|
// scripts (i.e. in order to support runtimeArgs).
|
||||||
|
func initRuntimesDir(cfg *config.Config) error {
|
||||||
|
runtimeDir := runtimeScriptsDir(cfg)
|
||||||
|
if err := os.RemoveAll(runtimeDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return system.MkdirAll(runtimeDir, 0700)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRuntimes(cfg *config.Config) (runtimes, error) {
|
||||||
|
newrt := runtimes{
|
||||||
|
configured: make(map[string]*shimConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := runtimeScriptsDir(cfg)
|
||||||
|
for name, rt := range cfg.Runtimes {
|
||||||
|
var c *shimConfig
|
||||||
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 runtimes{}, errors.Errorf("runtime %s: either a runtimeType or a path must be configured", name)
|
||||||
}
|
}
|
||||||
if rt.Path != "" {
|
if rt.Path != "" {
|
||||||
if rt.Type != "" {
|
if rt.Type != "" {
|
||||||
return errors.Errorf("runtime %s: cannot configure both path and runtimeType for the same runtime", name)
|
return runtimes{}, errors.Errorf("runtime %s: cannot configure both path and runtimeType for the same runtime", name)
|
||||||
}
|
}
|
||||||
if len(rt.Options) > 0 {
|
if len(rt.Options) > 0 {
|
||||||
return errors.Errorf("runtime %s: options cannot be used with a path runtime", name)
|
return runtimes{}, errors.Errorf("runtime %s: options cannot be used with a path runtime", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rt.Args) > 0 {
|
binaryName := rt.Path
|
||||||
script := filepath.Join(tmpDir, name)
|
needsWrapper := len(rt.Args) > 0
|
||||||
content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
|
if needsWrapper {
|
||||||
if err := os.WriteFile(script, []byte(content), 0700); err != nil {
|
var err error
|
||||||
return err
|
binaryName, err = wrapRuntime(dir, name, rt.Path, rt.Args)
|
||||||
|
if err != nil {
|
||||||
|
return runtimes{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rt.ShimConfig = defaultV2ShimConfig(cfg, daemon.rewriteRuntimePath(cfg, name, rt.Path, rt.Args))
|
c = defaultV2ShimConfig(cfg, binaryName)
|
||||||
var featuresStderr bytes.Buffer
|
if needsWrapper {
|
||||||
featuresCmd := exec.Command(rt.Path, append(rt.Args, "features")...)
|
path := rt.Path
|
||||||
featuresCmd.Stderr = &featuresStderr
|
c.PreflightCheck = func() error {
|
||||||
if featuresB, err := featuresCmd.Output(); err != nil {
|
// Check that the runtime path actually exists so that we can return a well known error.
|
||||||
logrus.WithError(err).Warnf("Failed to run %v: %q", featuresCmd.Args, featuresStderr.String())
|
_, err := exec.LookPath(path)
|
||||||
} else {
|
return errors.Wrap(err, "error while looking up the specified runtime path")
|
||||||
var features features.Features
|
|
||||||
if jsonErr := json.Unmarshal(featuresB, &features); jsonErr != nil {
|
|
||||||
logrus.WithError(err).Warnf("Failed to unmarshal the output of %v as a JSON", featuresCmd.Args)
|
|
||||||
} else {
|
|
||||||
rt.Features = &features
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(rt.Args) > 0 {
|
if len(rt.Args) > 0 {
|
||||||
return errors.Errorf("runtime %s: args cannot be used with a runtimeType runtime", name)
|
return runtimes{}, errors.Errorf("runtime %s: args cannot be used with a runtimeType runtime", name)
|
||||||
}
|
}
|
||||||
// Unlike implicit runtimes, there is no restriction on configuring a shim by path.
|
// Unlike implicit runtimes, there is no restriction on configuring a shim by path.
|
||||||
rt.ShimConfig = &types.ShimConfig{Binary: rt.Type}
|
c = &shimConfig{Shim: rt.Type}
|
||||||
if len(rt.Options) > 0 {
|
if len(rt.Options) > 0 {
|
||||||
// It has to be a pointer type or there'll be a panic in containerd/typeurl when we try to start the container.
|
// It has to be a pointer type or there'll be a panic in containerd/typeurl when we try to start the container.
|
||||||
rt.ShimConfig.Opts, err = shimopts.Generate(rt.Type, rt.Options)
|
var err error
|
||||||
|
c.Opts, err = shimopts.Generate(rt.Type, rt.Options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "runtime %v", name)
|
return runtimes{}, errors.Wrapf(err, "runtime %v", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cfg.Runtimes[name] = rt
|
newrt.configured[name] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return newrt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A non-standard Base32 encoding which lacks vowels to avoid accidentally
|
||||||
|
// spelling naughty words. Don't use this to encode any data which requires
|
||||||
|
// compatibility with anything outside of the currently-running process.
|
||||||
|
var base32Disemvoweled = base32.NewEncoding("0123456789BCDFGHJKLMNPQRSTVWXYZ-")
|
||||||
|
|
||||||
|
// wrapRuntime writes a shell script to dir which will execute binary with args
|
||||||
|
// concatenated to the script's argv. This is needed because the
|
||||||
|
// io.containerd.runc.v2 shim has no options for passing extra arguments to the
|
||||||
|
// runtime binary.
|
||||||
|
func wrapRuntime(dir, name, binary string, args []string) (string, error) {
|
||||||
|
var wrapper bytes.Buffer
|
||||||
|
sum := sha256.New()
|
||||||
|
_, _ = fmt.Fprintf(io.MultiWriter(&wrapper, sum), "#!/bin/sh\n%s %s $@\n", binary, strings.Join(args, " "))
|
||||||
|
// Generate a consistent name for the wrapper script derived from the
|
||||||
|
// contents so that multiple wrapper scripts can coexist with the same
|
||||||
|
// base name. The existing scripts might still be referenced by running
|
||||||
|
// containers.
|
||||||
|
suffix := base32Disemvoweled.EncodeToString(sum.Sum(nil))
|
||||||
|
scriptPath := filepath.Join(dir, name+"."+suffix)
|
||||||
|
if err := ioutils.AtomicWriteFile(scriptPath, wrapper.Bytes(), 0700); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return scriptPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtimes) Get(name string) (string, interface{}, error) {
|
||||||
|
rt := r.configured[name]
|
||||||
|
if rt != nil {
|
||||||
|
if rt.PreflightCheck != nil {
|
||||||
|
if err := rt.PreflightCheck(); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rt.Shim, rt.Opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.IsPermissibleC8dRuntimeName(name) {
|
||||||
|
return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
|
||||||
|
}
|
||||||
|
return name, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtimes) Features(name string) *features.Features {
|
||||||
|
rt := r.configured[name]
|
||||||
|
if rt != nil {
|
||||||
|
return rt.Features
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
|
||||||
// To support this case, the daemon wraps the specified runtime in a script that passes through those arguments.
|
|
||||||
func (daemon *Daemon) rewriteRuntimePath(cfg *config.Config, name, p string, args []string) string {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(cfg.Root, "runtimes", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (daemon *Daemon) getRuntime(cfg *config.Config, name string) (shim string, opts interface{}, err error) {
|
|
||||||
rt := cfg.GetRuntime(name)
|
|
||||||
if rt == nil {
|
|
||||||
if !config.IsPermissibleC8dRuntimeName(name) {
|
|
||||||
return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
|
|
||||||
}
|
|
||||||
return name, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rt.Args) > 0 {
|
|
||||||
// Check that the path of the runtime which the script wraps actually exists so
|
|
||||||
// that we can return a well known error which references the configured path
|
|
||||||
// instead of the wrapper script's.
|
|
||||||
if _, err := exec.LookPath(rt.Path); err != nil {
|
|
||||||
return "", nil, errors.Wrap(err, "error while looking up the specified runtime path")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rt.ShimConfig == nil {
|
|
||||||
// Should never happen as daemon.initRuntimes always sets
|
|
||||||
// ShimConfig and config reloading is synchronized.
|
|
||||||
err := errdefs.System(errors.Errorf("BUG: runtime %s: rt.ShimConfig == nil", name))
|
|
||||||
logrus.Error(err)
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return rt.ShimConfig.Binary, rt.ShimConfig.Opts, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
@ -88,11 +89,9 @@ func TestInitRuntimes_InvalidConfigs(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
cfg.Root = t.TempDir()
|
cfg.Root = t.TempDir()
|
||||||
cfg.Runtimes["myruntime"] = tt.runtime
|
cfg.Runtimes["myruntime"] = tt.runtime
|
||||||
d := &Daemon{}
|
assert.Assert(t, initRuntimesDir(cfg))
|
||||||
d.configStore.Store(cfg)
|
|
||||||
assert.Assert(t, os.Mkdir(filepath.Join(d.config().Root, "runtimes"), 0700))
|
|
||||||
|
|
||||||
err = d.initRuntimes(d.config())
|
_, err = setupRuntimes(cfg)
|
||||||
assert.Check(t, is.ErrorContains(err, tt.expectErr))
|
assert.Check(t, is.ErrorContains(err, tt.expectErr))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -127,7 +126,6 @@ func TestGetRuntime(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
cfg.Root = t.TempDir()
|
cfg.Root = t.TempDir()
|
||||||
assert.Assert(t, os.Mkdir(filepath.Join(cfg.Root, "runtimes"), 0700))
|
|
||||||
cfg.Runtimes = map[string]types.Runtime{
|
cfg.Runtimes = map[string]types.Runtime{
|
||||||
configuredRtName: configuredRuntime,
|
configuredRtName: configuredRuntime,
|
||||||
rtWithArgsName: rtWithArgs,
|
rtWithArgsName: rtWithArgs,
|
||||||
|
@ -136,41 +134,43 @@ func TestGetRuntime(t *testing.T) {
|
||||||
configuredShimByPathName: configuredShimByPath,
|
configuredShimByPathName: configuredShimByPath,
|
||||||
}
|
}
|
||||||
configureRuntimes(cfg)
|
configureRuntimes(cfg)
|
||||||
|
assert.NilError(t, initRuntimesDir(cfg))
|
||||||
|
runtimes, err := setupRuntimes(cfg)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
d := &Daemon{}
|
stockRuntime, ok := runtimes.configured[config.StockRuntimeName]
|
||||||
d.configStore.Store(cfg)
|
|
||||||
assert.Assert(t, d.loadRuntimes())
|
|
||||||
|
|
||||||
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)")
|
||||||
|
stockRuntime.Features = nil
|
||||||
|
|
||||||
configdOpts := *stockRuntime.ShimConfig.Opts.(*v2runcoptions.Options)
|
configdOpts := *stockRuntime.Opts.(*v2runcoptions.Options)
|
||||||
configdOpts.BinaryName = configuredRuntime.Path
|
configdOpts.BinaryName = configuredRuntime.Path
|
||||||
|
wantConfigdRuntime := &shimConfig{
|
||||||
|
Shim: stockRuntime.Shim,
|
||||||
|
Opts: &configdOpts,
|
||||||
|
}
|
||||||
|
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
name, runtime string
|
name, runtime string
|
||||||
wantShim string
|
want *shimConfig
|
||||||
wantOpts interface{}
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "StockRuntime",
|
name: "StockRuntime",
|
||||||
runtime: config.StockRuntimeName,
|
runtime: config.StockRuntimeName,
|
||||||
wantShim: stockRuntime.ShimConfig.Binary,
|
want: stockRuntime,
|
||||||
wantOpts: stockRuntime.ShimConfig.Opts,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ShimName",
|
name: "ShimName",
|
||||||
runtime: "io.containerd.my-shim.v42",
|
runtime: "io.containerd.my-shim.v42",
|
||||||
wantShim: "io.containerd.my-shim.v42",
|
want: &shimConfig{Shim: "io.containerd.my-shim.v42"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// containerd is pretty loose about the format of runtime names. Perhaps too
|
// containerd is pretty loose about the format of runtime names. Perhaps too
|
||||||
// loose. The only requirements are that the name contain a dot and (depending
|
// loose. The only requirements are that the name contain a dot and (depending
|
||||||
// on the containerd version) not start with a dot. It does not enforce any
|
// on the containerd version) not start with a dot. It does not enforce any
|
||||||
// particular format of the dot-delimited components of the name.
|
// particular format of the dot-delimited components of the name.
|
||||||
name: "VersionlessShimName",
|
name: "VersionlessShimName",
|
||||||
runtime: "io.containerd.my-shim",
|
runtime: "io.containerd.my-shim",
|
||||||
wantShim: "io.containerd.my-shim",
|
want: &shimConfig{Shim: "io.containerd.my-shim"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "IllformedShimName",
|
name: "IllformedShimName",
|
||||||
|
@ -193,50 +193,152 @@ func TestGetRuntime(t *testing.T) {
|
||||||
runtime: "my/io.containerd.runc.v2",
|
runtime: "my/io.containerd.runc.v2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ConfiguredRuntime",
|
name: "ConfiguredRuntime",
|
||||||
runtime: configuredRtName,
|
runtime: configuredRtName,
|
||||||
wantShim: stockRuntime.ShimConfig.Binary,
|
want: wantConfigdRuntime,
|
||||||
wantOpts: &configdOpts,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "RuntimeWithArgs",
|
name: "ShimWithOpts",
|
||||||
runtime: rtWithArgsName,
|
runtime: shimWithOptsName,
|
||||||
wantShim: stockRuntime.ShimConfig.Binary,
|
want: &shimConfig{
|
||||||
wantOpts: defaultV2ShimConfig(
|
Shim: shimWithOpts.Type,
|
||||||
d.config(),
|
Opts: &v2runcoptions.Options{IoUid: 42},
|
||||||
d.rewriteRuntimePath(
|
},
|
||||||
d.config(),
|
|
||||||
rtWithArgsName,
|
|
||||||
rtWithArgs.Path,
|
|
||||||
rtWithArgs.Args)).Opts,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ShimWithOpts",
|
name: "ShimAlias",
|
||||||
runtime: shimWithOptsName,
|
runtime: shimAliasName,
|
||||||
wantShim: shimWithOpts.Type,
|
want: &shimConfig{Shim: shimAlias.Type},
|
||||||
wantOpts: &v2runcoptions.Options{IoUid: 42},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ShimAlias",
|
name: "ConfiguredShimByPath",
|
||||||
runtime: shimAliasName,
|
runtime: configuredShimByPathName,
|
||||||
wantShim: shimAlias.Type,
|
want: &shimConfig{Shim: configuredShimByPath.Type},
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ConfiguredShimByPath",
|
|
||||||
runtime: configuredShimByPathName,
|
|
||||||
wantShim: configuredShimByPath.Type,
|
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
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(cfg, tt.runtime)
|
shim, opts, err := runtimes.Get(tt.runtime)
|
||||||
assert.Check(t, is.Equal(gotShim, tt.wantShim))
|
if tt.want != nil {
|
||||||
assert.Check(t, is.DeepEqual(gotOpts, tt.wantOpts))
|
|
||||||
if tt.wantShim != "" {
|
|
||||||
assert.Check(t, err)
|
assert.Check(t, err)
|
||||||
|
got := &shimConfig{Shim: shim, Opts: opts}
|
||||||
|
assert.Check(t, is.DeepEqual(got, tt.want))
|
||||||
} else {
|
} else {
|
||||||
assert.Check(t, errdefs.IsInvalidParameter(err))
|
assert.Check(t, is.Equal(shim, ""))
|
||||||
|
assert.Check(t, is.Nil(opts))
|
||||||
|
assert.Check(t, errdefs.IsInvalidParameter(err), "[%T] %[1]v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
t.Run("RuntimeWithArgs", func(t *testing.T) {
|
||||||
|
shim, opts, err := runtimes.Get(rtWithArgsName)
|
||||||
|
assert.Check(t, err)
|
||||||
|
assert.Check(t, is.Equal(shim, stockRuntime.Shim))
|
||||||
|
runcopts, ok := opts.(*v2runcoptions.Options)
|
||||||
|
if assert.Check(t, ok, "runtimes.Get() opts = type %T, want *v2runcoptions.Options", opts) {
|
||||||
|
wrapper, err := os.ReadFile(runcopts.BinaryName)
|
||||||
|
if assert.Check(t, err) {
|
||||||
|
assert.Check(t, is.Contains(string(wrapper),
|
||||||
|
strings.Join(append([]string{rtWithArgs.Path}, rtWithArgs.Args...), " ")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRuntime_PreflightCheck(t *testing.T) {
|
||||||
|
cfg, err := config.New()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
cfg.Root = t.TempDir()
|
||||||
|
cfg.Runtimes = map[string]types.Runtime{
|
||||||
|
"path-only": {
|
||||||
|
Path: "/usr/local/bin/file-not-found",
|
||||||
|
},
|
||||||
|
"with-args": {
|
||||||
|
Path: "/usr/local/bin/file-not-found",
|
||||||
|
Args: []string{"--arg"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.NilError(t, initRuntimesDir(cfg))
|
||||||
|
runtimes, err := setupRuntimes(cfg)
|
||||||
|
assert.NilError(t, err, "runtime paths should not be validated during setupRuntimes()")
|
||||||
|
|
||||||
|
t.Run("PathOnly", func(t *testing.T) {
|
||||||
|
_, _, err := runtimes.Get("path-only")
|
||||||
|
assert.NilError(t, err, "custom runtimes without wrapper scripts should not have pre-flight checks")
|
||||||
|
})
|
||||||
|
t.Run("WithArgs", func(t *testing.T) {
|
||||||
|
_, _, err := runtimes.Get("with-args")
|
||||||
|
assert.ErrorIs(t, err, fs.ErrNotExist)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRuntimeWrapping checks that reloading runtime config does not delete or
|
||||||
|
// modify existing wrapper scripts, which could break lifecycle management of
|
||||||
|
// existing containers.
|
||||||
|
func TestRuntimeWrapping(t *testing.T) {
|
||||||
|
cfg, err := config.New()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
cfg.Root = t.TempDir()
|
||||||
|
cfg.Runtimes = map[string]types.Runtime{
|
||||||
|
"change-args": {
|
||||||
|
Path: "/bin/true",
|
||||||
|
Args: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
|
"dupe": {
|
||||||
|
Path: "/bin/true",
|
||||||
|
Args: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
|
"change-path": {
|
||||||
|
Path: "/bin/true",
|
||||||
|
Args: []string{"baz"},
|
||||||
|
},
|
||||||
|
"drop-args": {
|
||||||
|
Path: "/bin/true",
|
||||||
|
Args: []string{"some", "arguments"},
|
||||||
|
},
|
||||||
|
"goes-away": {
|
||||||
|
Path: "/bin/true",
|
||||||
|
Args: []string{"bye"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.NilError(t, initRuntimesDir(cfg))
|
||||||
|
rt, err := setupRuntimes(cfg)
|
||||||
|
assert.Check(t, err)
|
||||||
|
|
||||||
|
type WrapperInfo struct{ BinaryName, Content string }
|
||||||
|
wrappers := make(map[string]WrapperInfo)
|
||||||
|
for name := range cfg.Runtimes {
|
||||||
|
_, opts, err := rt.Get(name)
|
||||||
|
if assert.Check(t, err, "rt.Get(%q)", name) {
|
||||||
|
binary := opts.(*v2runcoptions.Options).BinaryName
|
||||||
|
content, err := os.ReadFile(binary)
|
||||||
|
assert.Check(t, err, "could not read wrapper script contents for runtime %q", binary)
|
||||||
|
wrappers[name] = WrapperInfo{BinaryName: binary, Content: string(content)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Runtimes["change-args"] = types.Runtime{
|
||||||
|
Path: cfg.Runtimes["change-args"].Path,
|
||||||
|
Args: []string{"baz", "quux"},
|
||||||
|
}
|
||||||
|
cfg.Runtimes["change-path"] = types.Runtime{
|
||||||
|
Path: "/bin/false",
|
||||||
|
Args: cfg.Runtimes["change-path"].Args,
|
||||||
|
}
|
||||||
|
cfg.Runtimes["drop-args"] = types.Runtime{
|
||||||
|
Path: cfg.Runtimes["drop-args"].Path,
|
||||||
|
}
|
||||||
|
delete(cfg.Runtimes, "goes-away")
|
||||||
|
|
||||||
|
_, err = setupRuntimes(cfg)
|
||||||
|
assert.Check(t, err)
|
||||||
|
|
||||||
|
for name, info := range wrappers {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
content, err := os.ReadFile(info.BinaryName)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, info.Content, string(content))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,16 @@ import (
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/docker/docker/daemon/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (daemon *Daemon) getRuntime(cfg *config.Config, name string) (shim string, opts interface{}, err error) {
|
type runtimes struct{}
|
||||||
|
|
||||||
|
func (r *runtimes) Get(name string) (string, interface{}, error) {
|
||||||
return "", nil, errors.New("not implemented")
|
return "", nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initRuntimesDir(*config.Config) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRuntimes(*config.Config) (runtimes, error) {
|
||||||
|
return runtimes{}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ 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"
|
||||||
|
@ -57,7 +56,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(daemonCfg, ctr, hostConfig); err != nil {
|
if err := daemon.setSecurityOptions(&daemonCfg.Config, 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 {
|
||||||
|
@ -91,7 +90,7 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
|
||||||
// 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(daemonCfg, ctr.HostConfig, false); err != nil {
|
if err := daemon.adaptContainerSettings(&daemonCfg.Config, ctr.HostConfig, false); err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +101,7 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
|
||||||
// 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, daemonCfg *config.Config, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
|
func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore, 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()
|
||||||
|
@ -138,7 +137,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *config.Conf
|
||||||
// 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(daemonCfg, container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
|
if err := daemon.containerRm(&daemonCfg.Config, 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()
|
||||||
|
@ -150,7 +149,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *config.Conf
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := daemon.initializeNetworking(daemonCfg, container); err != nil {
|
if err := daemon.initializeNetworking(&daemonCfg.Config, container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,20 @@ 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(daemonCfg *config.Config, container *container.Container) (string, interface{}, error) {
|
func (daemon *Daemon) getLibcontainerdCreateOptions(daemonCfg *configStore, 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 = daemonCfg.DefaultRuntime
|
container.HostConfig.Runtime = daemonCfg.DefaultRuntime
|
||||||
container.CheckpointTo(daemon.containersReplica)
|
container.CheckpointTo(daemon.containersReplica)
|
||||||
}
|
}
|
||||||
|
|
||||||
binary, opts, err := daemon.getRuntime(daemonCfg, container.HostConfig.Runtime)
|
shim, opts, err := daemonCfg.Runtimes.Get(container.HostConfig.Runtime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, setExitCodeFromError(container.SetExitCode, err)
|
return "", nil, setExitCodeFromError(container.SetExitCode, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return binary, opts, nil
|
return shim, opts, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ 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(*config.Config, *container.Container) (string, interface{}, error) {
|
func (daemon *Daemon) getLibcontainerdCreateOptions(*configStore, *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
|
||||||
|
|
Loading…
Reference in a new issue