Files
moby/daemon/container/container_windows.go
Sebastiaan van Stijn c5991341eb remove support for deprecated kernel memory limit
kernel-memory limits are not supported in cgroups v2, and were obsoleted in
[kernel v5.4], producing a `ENOTSUP` in kernel v5.16. Support for this option
was removed in runc and other runtimes, as various LTS kernels contained a
broken implementation, resulting in unpredictable behavior.

We deprecated this option in [moby@b8ca7de], producing a warning when used,
and actively ignore the option since [moby@0798f5f].

Given that setting this option had no effect in most situations, we should
just remove this option instead of continuing to handle it with the expectation
that a runtime may still support it.

Note that we still support RHEL 8 (kernel 4.18) and RHEL 9 (kernel 5.14). We
no longer build packages for Ubuntu 20.04 (kernel 5.4) and Debian Bullseye 11
(kernel 5.10), which still have an LTS / ESM programme, but for those it would
only impact situations where a runtime is used that still supports it, and
an old API version was used.

[kernel v5.4]: https://github.com/torvalds/linux/commit/0158115f702b0ba208ab0
[moby@b8ca7de]: b8ca7de823
[moby@0798f5f]: 0798f5f5cf

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-09-16 13:08:36 +02:00

209 lines
6.5 KiB
Go

package container
import (
"context"
"errors"
"os"
"path/filepath"
containertypes "github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/events"
swarmtypes "github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/v2/errdefs"
)
const (
containerConfigMountPath = `C:\`
containerSecretMountPath = `C:\ProgramData\Docker\secrets`
containerInternalSecretMountPath = `C:\ProgramData\Docker\internal\secrets`
containerInternalConfigsDirPath = `C:\ProgramData\Docker\internal\configs`
// defaultStopTimeout is the timeout (in seconds) for the shutdown call on a container
defaultStopTimeout = 30
)
// UnmountIpcMount unmounts Ipc related mounts.
// This is a NOOP on windows.
func (container *Container) UnmountIpcMount() error {
return nil
}
// IpcMounts returns the list of Ipc related mounts.
func (container *Container) IpcMounts() []Mount {
return nil
}
// CreateSecretSymlinks creates symlinks to files in the secret mount.
func (container *Container) CreateSecretSymlinks() error {
for _, r := range container.SecretReferences {
if r.File == nil {
continue
}
resolvedPath, _, err := container.ResolvePath(getSecretTargetPath(r))
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil {
return err
}
if err := os.Symlink(filepath.Join(containerInternalSecretMountPath, r.SecretID), resolvedPath); err != nil {
return err
}
}
return nil
}
// SecretMounts returns the mount for the secret path.
// All secrets are stored in a single mount on Windows. Target symlinks are
// created for each secret, pointing to the files in this mount.
func (container *Container) SecretMounts() ([]Mount, error) {
var mounts []Mount
if len(container.SecretReferences) > 0 {
src, err := container.SecretMountPath()
if err != nil {
return nil, err
}
mounts = append(mounts, Mount{
Source: src,
Destination: containerInternalSecretMountPath,
Writable: false,
})
}
return mounts, nil
}
// UnmountSecrets unmounts the fs for secrets
func (container *Container) UnmountSecrets() error {
p, err := container.SecretMountPath()
if err != nil {
return err
}
return os.RemoveAll(p)
}
// CreateConfigSymlinks creates symlinks to files in the config mount.
func (container *Container) CreateConfigSymlinks() error {
for _, configRef := range container.ConfigReferences {
if configRef.File == nil {
continue
}
resolvedPath, _, err := container.ResolvePath(getConfigTargetPath(configRef))
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil {
return err
}
if err := os.Symlink(filepath.Join(containerInternalConfigsDirPath, configRef.ConfigID), resolvedPath); err != nil {
return err
}
}
return nil
}
// ConfigMounts returns the mount for configs.
// TODO: Right now Windows doesn't really have a "secure" storage for secrets,
// however some configs may contain secrets. Once secure storage is worked out,
// configs and secret handling should be merged.
func (container *Container) ConfigMounts() []Mount {
var mounts []Mount
if len(container.ConfigReferences) > 0 {
mounts = append(mounts, Mount{
Source: container.ConfigsDirPath(),
Destination: containerInternalConfigsDirPath,
Writable: false,
})
}
return mounts
}
// DetachAndUnmount unmounts all volumes.
// On Windows it only delegates to `UnmountVolumes` since there is nothing to
// force unmount.
func (container *Container) DetachAndUnmount(volumeEventLog func(name string, action events.Action, attributes map[string]string)) error {
return container.UnmountVolumes(context.TODO(), volumeEventLog)
}
// TmpfsMounts returns the list of tmpfs mounts
func (container *Container) TmpfsMounts() ([]Mount, error) {
var mounts []Mount
return mounts, nil
}
// UpdateContainer updates configuration of a container. Callers must hold a Lock on the Container.
func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error {
resources := hostConfig.Resources
if resources.CPUShares != 0 ||
resources.Memory != 0 ||
resources.NanoCPUs != 0 ||
resources.CgroupParent != "" ||
resources.BlkioWeight != 0 ||
len(resources.BlkioWeightDevice) != 0 ||
len(resources.BlkioDeviceReadBps) != 0 ||
len(resources.BlkioDeviceWriteBps) != 0 ||
len(resources.BlkioDeviceReadIOps) != 0 ||
len(resources.BlkioDeviceWriteIOps) != 0 ||
resources.CPUPeriod != 0 ||
resources.CPUQuota != 0 ||
resources.CPURealtimePeriod != 0 ||
resources.CPURealtimeRuntime != 0 ||
resources.CpusetCpus != "" ||
resources.CpusetMems != "" ||
len(resources.Devices) != 0 ||
len(resources.DeviceCgroupRules) != 0 ||
resources.MemoryReservation != 0 ||
resources.MemorySwap != 0 ||
resources.MemorySwappiness != nil ||
resources.OomKillDisable != nil ||
(resources.PidsLimit != nil && *resources.PidsLimit != 0) ||
len(resources.Ulimits) != 0 ||
resources.CPUCount != 0 ||
resources.CPUPercent != 0 ||
resources.IOMaximumIOps != 0 ||
resources.IOMaximumBandwidth != 0 {
return errdefs.InvalidParameter(errors.New("resource updating isn't supported on Windows"))
}
// update HostConfig of container
if hostConfig.RestartPolicy.Name != "" {
if container.HostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() {
return conflictingUpdateOptions("Restart policy cannot be updated because AutoRemove is enabled for the container")
}
container.HostConfig.RestartPolicy = hostConfig.RestartPolicy
}
return nil
}
// BuildHostnameFile writes the container's hostname file.
func (container *Container) BuildHostnameFile() error {
return nil
}
// GetMountPoints gives a platform specific transformation to types.MountPoint. Callers must hold a Container lock.
func (container *Container) GetMountPoints() []containertypes.MountPoint {
mountPoints := make([]containertypes.MountPoint, 0, len(container.MountPoints))
for _, m := range container.MountPoints {
mountPoints = append(mountPoints, containertypes.MountPoint{
Type: m.Type,
Name: m.Name,
Source: m.Path(),
Destination: m.Destination,
Driver: m.Driver,
RW: m.RW,
})
}
return mountPoints
}
func (container *Container) ConfigsDirPath() string {
return filepath.Join(container.Root, "configs")
}
// ConfigFilePath returns the path to the on-disk location of a config.
func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference) (string, error) {
return filepath.Join(container.ConfigsDirPath(), configRef.ConfigID), nil
}