mirror of
https://github.com/moby/moby.git
synced 2026-06-24 08:48:23 +00:00
The POST /containers/{id}/update API accepts BlkioWeightDevice,
BlkioDeviceReadBps, BlkioDeviceWriteBps, BlkioDeviceReadIOps, and
BlkioDeviceWriteIOps in its Resources body, but these five fields were
silently ignored when updating a running container.
The root cause was in toContainerdResources (daemon/update_linux.go):
only BlkioWeight was mapped into specs.LinuxBlockIO; the per-device
fields were never converted, so tsk.UpdateResources never wrote to
cgroupv2 io.max or the cgroupv1 blkio throttle files.
Fix by calling the existing getBlkioWeightDevices and
getBlkioThrottleDevices helpers (already used in oci_linux.go for
container creation) to populate all five fields. The function signature
is extended to return an error so that stat(2) failures on invalid
device paths are surfaced to the caller instead of being silently
dropped.
The API makes distinction between nil and zero-length slices while
doing. A nil per-device blkio field means the caller did not request an
update for that setting, while a non-nil empty slice means the caller
explicitly requested the setting to be cleared.
The Windows stub is updated to match the new signature.
Signed-off-by: Alexis Couvreur <alexiscouvreur.pro@gmail.com>
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
113 lines
3.0 KiB
Go
113 lines
3.0 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
|
|
cerrdefs "github.com/containerd/errdefs"
|
|
"github.com/moby/moby/api/types/container"
|
|
"github.com/moby/moby/api/types/events"
|
|
"github.com/moby/moby/v2/errdefs"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// ContainerUpdate updates configuration of the container
|
|
func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) (container.UpdateResponse, error) {
|
|
var warnings []string
|
|
|
|
daemonCfg := daemon.config()
|
|
warnings, err := daemon.verifyContainerSettings(daemonCfg, hostConfig, nil, true)
|
|
if err != nil {
|
|
return container.UpdateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
|
|
}
|
|
|
|
if err := daemon.update(name, hostConfig); err != nil {
|
|
return container.UpdateResponse{Warnings: warnings}, err
|
|
}
|
|
|
|
return container.UpdateResponse{Warnings: warnings}, nil
|
|
}
|
|
|
|
func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) error {
|
|
if hostConfig == nil {
|
|
return nil
|
|
}
|
|
|
|
ctr, err := daemon.GetContainer(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
restoreConfig := false
|
|
backupHostConfig := *ctr.HostConfig
|
|
|
|
defer func() {
|
|
if restoreConfig {
|
|
ctr.Lock()
|
|
if !ctr.State.RemovalInProgress && !ctr.State.Dead {
|
|
ctr.HostConfig = &backupHostConfig
|
|
ctr.CheckpointTo(context.WithoutCancel(context.TODO()), daemon.containersReplica)
|
|
}
|
|
ctr.Unlock()
|
|
}
|
|
}()
|
|
|
|
ctr.Lock()
|
|
|
|
if ctr.State.RemovalInProgress || ctr.State.Dead {
|
|
ctr.Unlock()
|
|
return errCannotUpdate(ctr.ID, errors.New(`container is marked for removal and cannot be "update"`))
|
|
}
|
|
|
|
if err := ctr.UpdateContainer(hostConfig); err != nil {
|
|
restoreConfig = true
|
|
ctr.Unlock()
|
|
return errCannotUpdate(ctr.ID, err)
|
|
}
|
|
if err := ctr.CheckpointTo(context.TODO(), daemon.containersReplica); err != nil {
|
|
restoreConfig = true
|
|
ctr.Unlock()
|
|
return errCannotUpdate(ctr.ID, err)
|
|
}
|
|
|
|
ctr.Unlock()
|
|
|
|
// if Restart Policy changed, we need to update container monitor
|
|
if hostConfig.RestartPolicy.Name != "" {
|
|
ctr.UpdateMonitor(hostConfig.RestartPolicy)
|
|
}
|
|
|
|
defer daemon.LogContainerEvent(ctr, events.ActionUpdate)
|
|
|
|
// If container is not running, update hostConfig struct is enough,
|
|
// resources will be updated when the container is started again.
|
|
// If container is running (including paused), we need to update configs
|
|
// to the real world.
|
|
ctr.Lock()
|
|
isRestarting := ctr.State.Restarting
|
|
tsk, err := ctr.GetRunningTask()
|
|
ctr.Unlock()
|
|
if cerrdefs.IsConflict(err) || isRestarting {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resources, err := toContainerdResources(hostConfig.Resources)
|
|
if err != nil {
|
|
restoreConfig = true
|
|
return errCannotUpdate(ctr.ID, err)
|
|
}
|
|
if err := tsk.UpdateResources(context.TODO(), resources); err != nil {
|
|
restoreConfig = true
|
|
// TODO: it would be nice if containerd responded with better errors here so we can classify this better.
|
|
return errCannotUpdate(ctr.ID, errdefs.System(err))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func errCannotUpdate(containerID string, err error) error {
|
|
return errors.Wrap(err, "Cannot update container "+containerID)
|
|
}
|