mirror of
https://github.com/containers/podman.git
synced 2025-05-17 15:18:43 +08:00
implement podman update
podman update allows users to change the cgroup configuration of an existing container using the already defined resource limits flags from podman create/run. The supported flags in crun are: this command is also now supported in the libpod api via the /libpod/containers/<CID>/update endpoint where the resource limits are passed inthe request body and follow the OCI resource spec format –memory –cpus –cpuset-cpus –cpuset-mems –memory-swap –memory-reservation –cpu-shares –cpu-quota –cpu-period –blkio-weight –cpu-rt-period –cpu-rt-runtime -device-read-bps -device-write-bps -device-read-iops -device-write-iops -memory-swappiness -blkio-weight-device resolves #15067 Signed-off-by: Charlie Doern <cdoern@redhat.com>
This commit is contained in:
@ -28,10 +28,10 @@ func ContainerToPodOptions(containerCreate *entities.ContainerCreateOptions, pod
|
||||
}
|
||||
|
||||
// DefineCreateFlags declares and instantiates the container create flags
|
||||
func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, isInfra bool, clone bool) {
|
||||
func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, mode entities.ContainerMode) {
|
||||
createFlags := cmd.Flags()
|
||||
|
||||
if !isInfra && !clone { // regular create flags
|
||||
if mode == entities.CreateMode { // regular create flags
|
||||
annotationFlagName := "annotation"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.Annotation,
|
||||
@ -103,22 +103,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone)
|
||||
|
||||
deviceReadIopsFlagName := "device-read-iops"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.DeviceReadIOPs,
|
||||
deviceReadIopsFlagName, []string{},
|
||||
"Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault)
|
||||
|
||||
deviceWriteIopsFlagName := "device-write-iops"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.DeviceWriteIOPs,
|
||||
deviceWriteIopsFlagName, []string{},
|
||||
"Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault)
|
||||
|
||||
createFlags.Bool(
|
||||
"disable-content-trust", false,
|
||||
"This is a Docker specific option and is a NOOP",
|
||||
@ -597,7 +581,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
`If a container with the same name exists, replace it`,
|
||||
)
|
||||
}
|
||||
if isInfra || (!clone && !isInfra) { // infra container flags, create should also pick these up
|
||||
if mode == entities.InfraMode || (mode == entities.CreateMode) { // infra container flags, create should also pick these up
|
||||
shmSizeFlagName := "shm-size"
|
||||
createFlags.String(
|
||||
shmSizeFlagName, shmSize(),
|
||||
@ -677,7 +661,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault)
|
||||
var conmonPidfileFlagName string
|
||||
if !isInfra {
|
||||
if mode == entities.CreateMode {
|
||||
conmonPidfileFlagName = "conmon-pidfile"
|
||||
} else {
|
||||
conmonPidfileFlagName = "infra-conmon-pidfile"
|
||||
@ -690,7 +674,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
_ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault)
|
||||
|
||||
var entrypointFlagName string
|
||||
if !isInfra {
|
||||
if mode == entities.CreateMode {
|
||||
entrypointFlagName = "entrypoint"
|
||||
} else {
|
||||
entrypointFlagName = "infra-command"
|
||||
@ -725,7 +709,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault)
|
||||
|
||||
if isInfra {
|
||||
if mode == entities.InfraMode {
|
||||
nameFlagName := "infra-name"
|
||||
createFlags.StringVar(
|
||||
&cf.Name,
|
||||
@ -775,7 +759,8 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers)
|
||||
}
|
||||
if clone || !isInfra { // clone and create only flags, we need this level of separation so clone does not pick up all of the flags
|
||||
|
||||
if mode == entities.CloneMode || mode == entities.CreateMode {
|
||||
nameFlagName := "name"
|
||||
createFlags.StringVar(
|
||||
&cf.Name,
|
||||
@ -791,7 +776,8 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
"Run container in an existing pod",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods)
|
||||
|
||||
}
|
||||
if mode != entities.InfraMode { // clone create and update only flags, we need this level of separation so clone does not pick up all of the flags
|
||||
cpuPeriodFlagName := "cpu-period"
|
||||
createFlags.Uint64Var(
|
||||
&cf.CPUPeriod,
|
||||
@ -840,8 +826,24 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone)
|
||||
}
|
||||
// anyone can use these
|
||||
if mode == entities.CreateMode || mode == entities.UpdateMode {
|
||||
deviceReadIopsFlagName := "device-read-iops"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.DeviceReadIOPs,
|
||||
deviceReadIopsFlagName, []string{},
|
||||
"Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault)
|
||||
|
||||
deviceWriteIopsFlagName := "device-write-iops"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.DeviceWriteIOPs,
|
||||
deviceWriteIopsFlagName, []string{},
|
||||
"Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault)
|
||||
}
|
||||
// anyone can use these
|
||||
cpusFlagName := "cpus"
|
||||
createFlags.Float64Var(
|
||||
&cf.CPUS,
|
||||
|
@ -41,7 +41,7 @@ func cloneFlags(cmd *cobra.Command) {
|
||||
flags.BoolVarP(&ctrClone.Force, forceFlagName, "f", false, "force the existing container to be destroyed")
|
||||
|
||||
common.DefineCreateDefaults(&ctrClone.CreateOpts)
|
||||
common.DefineCreateFlags(cmd, &ctrClone.CreateOpts, false, true)
|
||||
common.DefineCreateFlags(cmd, &ctrClone.CreateOpts, entities.CloneMode)
|
||||
}
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
|
@ -72,7 +72,7 @@ func createFlags(cmd *cobra.Command) {
|
||||
|
||||
flags.SetInterspersed(false)
|
||||
common.DefineCreateDefaults(&cliVals)
|
||||
common.DefineCreateFlags(cmd, &cliVals, false, false)
|
||||
common.DefineCreateFlags(cmd, &cliVals, entities.CreateMode)
|
||||
common.DefineNetFlags(cmd)
|
||||
|
||||
flags.SetNormalizeFunc(utils.AliasFlags)
|
||||
|
@ -61,7 +61,7 @@ func runFlags(cmd *cobra.Command) {
|
||||
|
||||
flags.SetInterspersed(false)
|
||||
common.DefineCreateDefaults(&cliVals)
|
||||
common.DefineCreateFlags(cmd, &cliVals, false, false)
|
||||
common.DefineCreateFlags(cmd, &cliVals, entities.CreateMode)
|
||||
common.DefineNetFlags(cmd)
|
||||
|
||||
flags.SetNormalizeFunc(utils.AliasFlags)
|
||||
|
83
cmd/podman/containers/update.go
Normal file
83
cmd/podman/containers/update.go
Normal file
@ -0,0 +1,83 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/podman/v4/cmd/podman/common"
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||
"github.com/containers/podman/v4/pkg/specgen"
|
||||
"github.com/containers/podman/v4/pkg/specgenutil"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
updateDescription = `Updates the cgroup configuration of a given container`
|
||||
|
||||
updateCommand = &cobra.Command{
|
||||
Use: "update [options] CONTAINER",
|
||||
Short: "update an existing container",
|
||||
Long: updateDescription,
|
||||
RunE: update,
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: common.AutocompleteContainers,
|
||||
Example: `podman update --cpus=5 foobar_container`,
|
||||
}
|
||||
|
||||
containerUpdateCommand = &cobra.Command{
|
||||
Args: updateCommand.Args,
|
||||
Use: updateCommand.Use,
|
||||
Short: updateCommand.Short,
|
||||
Long: updateCommand.Long,
|
||||
RunE: updateCommand.RunE,
|
||||
ValidArgsFunction: updateCommand.ValidArgsFunction,
|
||||
Example: `podman container update --cpus=5 foobar_container`,
|
||||
}
|
||||
)
|
||||
var (
|
||||
updateOpts entities.ContainerCreateOptions
|
||||
)
|
||||
|
||||
func updateFlags(cmd *cobra.Command) {
|
||||
common.DefineCreateDefaults(&updateOpts)
|
||||
common.DefineCreateFlags(cmd, &updateOpts, entities.UpdateMode)
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Command: updateCommand,
|
||||
})
|
||||
updateFlags(updateCommand)
|
||||
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Command: containerUpdateCommand,
|
||||
Parent: containerCmd,
|
||||
})
|
||||
updateFlags(containerUpdateCommand)
|
||||
}
|
||||
|
||||
func update(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
// use a specgen since this is the easiest way to hold resource info
|
||||
s := &specgen.SpecGenerator{}
|
||||
s.ResourceLimits = &specs.LinuxResources{}
|
||||
|
||||
// we need to pass the whole specgen since throttle devices are parsed later due to cross compat.
|
||||
s.ResourceLimits, err = specgenutil.GetResources(s, &updateOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := &entities.ContainerUpdateOptions{
|
||||
NameOrID: args[0],
|
||||
Specgen: s,
|
||||
}
|
||||
rep, err := registry.ContainerEngine().ContainerUpdate(context.Background(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(rep)
|
||||
return nil
|
||||
}
|
@ -44,7 +44,7 @@ func cloneFlags(cmd *cobra.Command) {
|
||||
_ = podCloneCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone)
|
||||
|
||||
common.DefineCreateDefaults(&podClone.InfraOptions)
|
||||
common.DefineCreateFlags(cmd, &podClone.InfraOptions, true, false)
|
||||
common.DefineCreateFlags(cmd, &podClone.InfraOptions, entities.InfraMode)
|
||||
|
||||
podClone.InfraOptions.MemorySwappiness = -1 // this is not implemented for pods yet, need to set -1 default manually
|
||||
|
||||
|
@ -65,7 +65,7 @@ func init() {
|
||||
flags := createCommand.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
common.DefineCreateDefaults(&infraOptions)
|
||||
common.DefineCreateFlags(createCommand, &infraOptions, true, false)
|
||||
common.DefineCreateFlags(createCommand, &infraOptions, entities.InfraMode)
|
||||
common.DefineNetFlags(createCommand)
|
||||
|
||||
flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with")
|
||||
|
1
docs/source/markdown/.gitignore
vendored
1
docs/source/markdown/.gitignore
vendored
@ -27,3 +27,4 @@ podman-run.1.md
|
||||
podman-search.1.md
|
||||
podman-stop.1.md
|
||||
podman-unpause.1.md
|
||||
podman-update.1.md
|
||||
|
1
docs/source/markdown/links/podman-container-update.1
Normal file
1
docs/source/markdown/links/podman-container-update.1
Normal file
@ -0,0 +1 @@
|
||||
.so man1/podman-update.1
|
5
docs/source/markdown/options/device-read-bps.md
Normal file
5
docs/source/markdown/options/device-read-bps.md
Normal file
@ -0,0 +1,5 @@
|
||||
#### **--device-read-bps**=*path:rate*
|
||||
|
||||
Limit read rate (in bytes per second) from a device (e.g. **--device-read-bps=/dev/sda:1mb**).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
5
docs/source/markdown/options/device-read-iops.md
Normal file
5
docs/source/markdown/options/device-read-iops.md
Normal file
@ -0,0 +1,5 @@
|
||||
#### **--device-read-iops**=*path:rate*
|
||||
|
||||
Limit read rate (in IO operations per second) from a device (e.g. **--device-read-iops=/dev/sda:1000**).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
5
docs/source/markdown/options/device-write-bps.md
Normal file
5
docs/source/markdown/options/device-write-bps.md
Normal file
@ -0,0 +1,5 @@
|
||||
#### **--device-write-bps**=*path:rate*
|
||||
|
||||
Limit write rate (in bytes per second) to a device (e.g. **--device-write-bps=/dev/sda:1mb**).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
5
docs/source/markdown/options/device-write-iops.md
Normal file
5
docs/source/markdown/options/device-write-iops.md
Normal file
@ -0,0 +1,5 @@
|
||||
#### **--device-write-iops**=*path:rate*
|
||||
|
||||
Limit write rate (in IO operations per second) to a device (e.g. **--device-write-iops=/dev/sda:1000**).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
11
docs/source/markdown/options/memory-reservation.md
Normal file
11
docs/source/markdown/options/memory-reservation.md
Normal file
@ -0,0 +1,11 @@
|
||||
#### **--memory-reservation**=*number[unit]*
|
||||
|
||||
Memory soft limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
|
||||
|
||||
After setting memory reservation, when the system detects memory contention
|
||||
or low memory, containers are forced to restrict their consumption to their
|
||||
reservation. So you should always set the value below **--memory**, otherwise the
|
||||
hard limit will take precedence. By default, memory reservation will be the same
|
||||
as memory limit.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
13
docs/source/markdown/options/memory-swap.md
Normal file
13
docs/source/markdown/options/memory-swap.md
Normal file
@ -0,0 +1,13 @@
|
||||
#### **--memory-swap**=*number[unit]*
|
||||
|
||||
A limit value equal to memory plus swap.
|
||||
A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
|
||||
|
||||
Must be used with the **-m** (**--memory**) flag.
|
||||
The argument value should always be larger than that of
|
||||
**-m** (**--memory**) By default, it is set to double
|
||||
the value of **--memory**.
|
||||
|
||||
Set _number_ to **-1** to enable unlimited swap.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
11
docs/source/markdown/options/memory.md
Normal file
11
docs/source/markdown/options/memory.md
Normal file
@ -0,0 +1,11 @@
|
||||
#### **--memory**, **-m**=*number[unit]*
|
||||
|
||||
Memory limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
|
||||
|
||||
Allows the memory available to a container to be constrained. If the host
|
||||
supports swap memory, then the **-m** memory setting can be larger than physical
|
||||
RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
|
||||
not limited. The actual limit may be rounded up to a multiple of the operating
|
||||
system's page size (the value would be very large, that's millions of trillions).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
@ -52,36 +52,18 @@ If none are specified, the original container's CPU memory nodes are used.
|
||||
|
||||
@@option destroy
|
||||
|
||||
#### **--device-read-bps**=*path*
|
||||
@@option device-read-bps
|
||||
|
||||
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--device-write-bps**=*path*
|
||||
|
||||
Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option device-write-bps
|
||||
|
||||
#### **--force**, **-f**
|
||||
|
||||
Force removal of the original container that we are cloning. Can only be used in conjunction with **--destroy**.
|
||||
|
||||
#### **--memory**, **-m**=*limit*
|
||||
|
||||
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
|
||||
|
||||
Allows the memory available to a container to be constrained. If the host
|
||||
supports swap memory, then the **-m** memory setting can be larger than physical
|
||||
RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
|
||||
not limited. The actual limit may be rounded up to a multiple of the operating
|
||||
system's page size (the value would be very large, that's millions of trillions).
|
||||
@@option memory
|
||||
|
||||
If no memory limits are specified, the original container's will be used.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--memory-reservation**=*limit*
|
||||
|
||||
Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
|
||||
@ -92,8 +74,6 @@ reservation. So you should always set the value below **--memory**, otherwise th
|
||||
hard limit will take precedence. By default, memory reservation will be the same
|
||||
as memory limit from the container being cloned.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--memory-swap**=*limit*
|
||||
|
||||
A limit value equal to memory plus swap. Must be used with the **-m**
|
||||
|
@ -46,6 +46,7 @@ The container command allows you to manage containers
|
||||
| top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
|
||||
| unmount | [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem.(Alias unmount) |
|
||||
| unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
|
||||
| update | [podman-update(1)](podman-update.1.md) | Updates the cgroup configuration of a given container. |
|
||||
| wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
|
||||
|
||||
## SEE ALSO
|
||||
|
@ -87,9 +87,7 @@ each of stdin, stdout, and stderr.
|
||||
|
||||
@@option blkio-weight
|
||||
|
||||
#### **--blkio-weight-device**=*device:weight*
|
||||
|
||||
Block IO relative device weight.
|
||||
@@option blkio-weight-device
|
||||
|
||||
@@option cap-add
|
||||
|
||||
@ -148,27 +146,11 @@ device. The devices that podman will load modules when necessary are:
|
||||
|
||||
#### **--device-read-bps**=*path*
|
||||
|
||||
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
|
||||
@@option device-read-iops
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option device-write-bps
|
||||
|
||||
#### **--device-read-iops**=*path*
|
||||
|
||||
Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--device-write-bps**=*path*
|
||||
|
||||
Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--device-write-iops**=*path*
|
||||
|
||||
Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option device-write-iops
|
||||
|
||||
@@option disable-content-trust
|
||||
|
||||
@ -307,42 +289,11 @@ This option is currently supported only by the **journald** log driver.
|
||||
|
||||
@@option mac-address
|
||||
|
||||
#### **--memory**, **-m**=*limit*
|
||||
@@option memory
|
||||
|
||||
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
|
||||
@@option memory-reservation
|
||||
|
||||
Allows you to constrain the memory available to a container. If the host
|
||||
supports swap memory, then the **-m** memory setting can be larger than physical
|
||||
RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
|
||||
not limited. The actual limit may be rounded up to a multiple of the operating
|
||||
system's page size (the value would be very large, that's millions of trillions).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--memory-reservation**=*limit*
|
||||
|
||||
Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
|
||||
|
||||
After setting memory reservation, when the system detects memory contention
|
||||
or low memory, containers are forced to restrict their consumption to their
|
||||
reservation. So you should always set the value below **--memory**, otherwise the
|
||||
hard limit will take precedence. By default, memory reservation will be the same
|
||||
as memory limit.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--memory-swap**=*limit*
|
||||
|
||||
A limit value equal to memory plus swap. Must be used with the **-m**
|
||||
(**--memory**) flag. The swap `LIMIT` should always be larger than **-m**
|
||||
(**--memory**) value. By default, the swap `LIMIT` will be set to double
|
||||
the value of --memory.
|
||||
|
||||
The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),
|
||||
`k` (kibibytes), `m` (mebibytes), or `g` (gibibytes). If you don't specify a
|
||||
unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option memory-swap
|
||||
|
||||
@@option memory-swappiness
|
||||
|
||||
|
@ -48,13 +48,9 @@ Podman may load kernel modules required for using the specified
|
||||
device. The devices that Podman will load modules for when necessary are:
|
||||
/dev/fuse.
|
||||
|
||||
#### **--device-read-bps**=*path*
|
||||
@@option device-read-bps
|
||||
|
||||
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb).
|
||||
|
||||
#### **--device-write-bps**=*path*
|
||||
|
||||
Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)
|
||||
@@option device-write-bps
|
||||
|
||||
@@option gidmap.pod
|
||||
|
||||
|
@ -65,13 +65,9 @@ Podman may load kernel modules required for using the specified
|
||||
device. The devices that Podman will load modules for when necessary are:
|
||||
/dev/fuse.
|
||||
|
||||
#### **--device-read-bps**=*path*
|
||||
@@option device-read-bps
|
||||
|
||||
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
|
||||
|
||||
#### **--device-write-bps**=*path*
|
||||
|
||||
Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)
|
||||
@@option device-write-bps
|
||||
|
||||
#### **--dns**=*ipaddr*
|
||||
|
||||
|
@ -180,29 +180,13 @@ device. The devices that Podman will load modules when necessary are:
|
||||
|
||||
@@option device-cgroup-rule
|
||||
|
||||
#### **--device-read-bps**=*path:rate*
|
||||
@@option device-read-bps
|
||||
|
||||
Limit read rate (in bytes per second) from a device (e.g. **--device-read-bps=/dev/sda:1mb**).
|
||||
@@option device-read-iops
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option device-write-bps
|
||||
|
||||
#### **--device-read-iops**=*path:rate*
|
||||
|
||||
Limit read rate (in IO operations per second) from a device (e.g. **--device-read-iops=/dev/sda:1000**).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--device-write-bps**=*path:rate*
|
||||
|
||||
Limit write rate (in bytes per second) to a device (e.g. **--device-write-bps=/dev/sda:1mb**).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--device-write-iops**=*path:rate*
|
||||
|
||||
Limit write rate (in IO operations per second) to a device (e.g. **--device-write-iops=/dev/sda:1000**).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option device-write-iops
|
||||
|
||||
@@option disable-content-trust
|
||||
|
||||
@ -335,33 +319,9 @@ RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
|
||||
not limited. The actual limit may be rounded up to a multiple of the operating
|
||||
system's page size (the value would be very large, that's millions of trillions).
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option memory-reservation
|
||||
|
||||
#### **--memory-reservation**=*number[unit]*
|
||||
|
||||
Memory soft limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
|
||||
|
||||
After setting memory reservation, when the system detects memory contention
|
||||
or low memory, containers are forced to restrict their consumption to their
|
||||
reservation. So you should always set the value below **--memory**, otherwise the
|
||||
hard limit will take precedence. By default, memory reservation will be the same
|
||||
as memory limit.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
|
||||
#### **--memory-swap**=*number[unit]*
|
||||
|
||||
A limit value equal to memory plus swap.
|
||||
A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
|
||||
|
||||
Must be used with the **-m** (**--memory**) flag.
|
||||
The argument value should always be larger than that of
|
||||
**-m** (**--memory**) By default, it is set to double
|
||||
the value of **--memory**.
|
||||
|
||||
Set _number_ to **-1** to enable unlimited swap.
|
||||
|
||||
This option is not supported on cgroups V1 rootless systems.
|
||||
@@option memory-swap
|
||||
|
||||
@@option memory-swappiness
|
||||
|
||||
|
78
docs/source/markdown/podman-update.1.md.in
Normal file
78
docs/source/markdown/podman-update.1.md.in
Normal file
@ -0,0 +1,78 @@
|
||||
% podman-update(1)
|
||||
|
||||
## NAME
|
||||
podman\-update - Updates the cgroup configuration of a given container
|
||||
|
||||
## SYNOPSIS
|
||||
**podman update** [*options*] *container*
|
||||
|
||||
**podman container update** [*options*] *container*
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Updates the cgroup configuration of an already existing container. The currently supported options are a subset of the
|
||||
podman create/run resource limits options. These new options are non-persistent and only last for the current execution of the container; the configuration will be honored on its next run.
|
||||
This means that this command can only be executed on an already running container and the changes made will be erased the next time the container is stopped and restarted, this is to ensure immutability.
|
||||
This command takes one argument, a container name or ID, alongside the resource flags to modify the cgroup.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
@@option blkio-weight
|
||||
|
||||
@@option blkio-weight-device
|
||||
|
||||
@@option cpu-period
|
||||
|
||||
@@option cpu-quota
|
||||
|
||||
@@option cpu-rt-period
|
||||
|
||||
@@option cpu-rt-runtime
|
||||
|
||||
@@option cpu-shares
|
||||
|
||||
@@option cpus.container
|
||||
|
||||
@@option cpuset-cpus
|
||||
|
||||
@@option cpuset-mems
|
||||
|
||||
@@option device-read-bps
|
||||
|
||||
@@option device-read-iops
|
||||
|
||||
@@option device-write-bps
|
||||
|
||||
@@option device-write-iops
|
||||
|
||||
@@option memory
|
||||
|
||||
@@option memory-reservation
|
||||
|
||||
@@option memory-swap
|
||||
|
||||
@@option memory-swappiness
|
||||
|
||||
|
||||
## EXAMPLEs
|
||||
|
||||
update a container with a new cpu quota and period
|
||||
```
|
||||
podman update --cpus=5 myCtr
|
||||
```
|
||||
|
||||
update a container with all available options for cgroups v2
|
||||
```
|
||||
podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 --memory 1G --memory-swap 2G --memory-reservation 2G --blkio-weight-device /dev/zero:123 --blkio-weight 123 --device-read-bps /dev/zero:10mb --device-write-bps /dev/zero:10mb --device-read-iops /dev/zero:1000 --device-write-iops /dev/zero:1000 ctrID
|
||||
```
|
||||
|
||||
update a container with all available options for cgroups v1
|
||||
```
|
||||
podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 --memory 1G --memory-swap 2G --memory-reservation 2G --memory-swappiness 50 ctrID
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[podman-create(1)](podman-create.1.md)**, **[podman-run(1)](podman-run.1.md)**
|
||||
|
||||
## HISTORY
|
||||
August 2022, Originally written by Charlie Doern <cdoern@redhat.com>
|
@ -355,6 +355,7 @@ the exit codes follow the `chroot` standard, see below:
|
||||
| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
|
||||
| [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. |
|
||||
| [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. |
|
||||
| [podman-update(1)](podman-update.1.md) | Updates the cgroup configuration of a given container. |
|
||||
| [podman-version(1)](podman-version.1.md) | Display the Podman version information. |
|
||||
| [podman-volume(1)](podman-volume.1.md) | Simple management tool for volumes. |
|
||||
| [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/containers/podman/v4/libpod/events"
|
||||
"github.com/containers/podman/v4/pkg/signal"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -98,6 +99,15 @@ func (c *Container) Start(ctx context.Context, recursive bool) error {
|
||||
return c.start()
|
||||
}
|
||||
|
||||
// Update updates the given container.
|
||||
// only the cgroup config can be updated and therefore only a linux resource spec is passed.
|
||||
func (c *Container) Update(res *spec.LinuxResources) error {
|
||||
if err := c.syncContainer(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.update(res)
|
||||
}
|
||||
|
||||
// StartAndAttach starts a container and attaches to it.
|
||||
// This acts as a combination of the Start and Attach APIs, ensuring proper
|
||||
// ordering of the two such that no output from the container is lost (e.g. the
|
||||
|
@ -2352,3 +2352,12 @@ func (c *Container) extractSecretToCtrStorage(secr *ContainerSecret) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// update calls the ociRuntime update function to modify a cgroup config after container creation
|
||||
func (c *Container) update(resources *spec.LinuxResources) error {
|
||||
if err := c.ociRuntime.UpdateContainer(c, resources); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("updated container %s", c.ID())
|
||||
return nil
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/containers/common/pkg/resize"
|
||||
"github.com/containers/podman/v4/libpod/define"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// OCIRuntime is an implementation of an OCI runtime.
|
||||
@ -148,6 +149,9 @@ type OCIRuntime interface {
|
||||
|
||||
// RuntimeInfo returns verbose information about the runtime.
|
||||
RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error)
|
||||
|
||||
// UpdateContainer updates the given container's cgroup configuration.
|
||||
UpdateContainer(ctr *Container, res *specs.LinuxResources) error
|
||||
}
|
||||
|
||||
// AttachOptions are options used when attached to a container or an exec
|
||||
|
@ -307,6 +307,52 @@ func (r *ConmonOCIRuntime) StartContainer(ctr *Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateContainer updates the given container's cgroup configuration
|
||||
func (r *ConmonOCIRuntime) UpdateContainer(ctr *Container, resources *spec.LinuxResources) error {
|
||||
runtimeDir, err := util.GetRuntimeDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
|
||||
if path, ok := os.LookupEnv("PATH"); ok {
|
||||
env = append(env, fmt.Sprintf("PATH=%s", path))
|
||||
}
|
||||
args := r.runtimeFlags
|
||||
args = append(args, "update")
|
||||
tempFile, additionalArgs, err := generateResourceFile(resources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tempFile)
|
||||
|
||||
args = append(args, additionalArgs...)
|
||||
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, append(args, ctr.ID())...)
|
||||
}
|
||||
|
||||
func generateResourceFile(res *spec.LinuxResources) (string, []string, error) {
|
||||
flags := []string{}
|
||||
if res == nil {
|
||||
return "", flags, nil
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "podman")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
j, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
_, err = f.WriteString(string(j))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
flags = append(flags, "--resources="+f.Name())
|
||||
return f.Name(), flags, nil
|
||||
}
|
||||
|
||||
// KillContainer sends the given signal to the given container.
|
||||
// If all is set, send to all PIDs in the container.
|
||||
// All is only supported if the container created cgroups.
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/containers/common/pkg/resize"
|
||||
"github.com/containers/podman/v4/libpod/define"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -80,6 +81,11 @@ func (r *MissingRuntime) StartContainer(ctr *Container) error {
|
||||
return r.printError()
|
||||
}
|
||||
|
||||
// UpdateContainer is not available as the runtime is missing
|
||||
func (r *MissingRuntime) UpdateContainer(ctr *Container, resources *spec.LinuxResources) error {
|
||||
return r.printError()
|
||||
}
|
||||
|
||||
// KillContainer is not available as the runtime is missing
|
||||
// TODO: We could attempt to unix.Kill() the PID as recorded in the state if we
|
||||
// really want to smooth things out? Won't be perfect, but if the container has
|
||||
|
@ -1,6 +1,7 @@
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
|
||||
"github.com/containers/podman/v4/libpod"
|
||||
"github.com/containers/podman/v4/libpod/define"
|
||||
"github.com/containers/podman/v4/pkg/api/handlers"
|
||||
"github.com/containers/podman/v4/pkg/api/handlers/compat"
|
||||
"github.com/containers/podman/v4/pkg/api/handlers/utils"
|
||||
api "github.com/containers/podman/v4/pkg/api/types"
|
||||
@ -17,6 +19,7 @@ import (
|
||||
"github.com/containers/podman/v4/pkg/domain/infra/abi"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -391,6 +394,28 @@ func InitContainer(w http.ResponseWriter, r *http.Request) {
|
||||
utils.WriteResponse(w, http.StatusNoContent, "")
|
||||
}
|
||||
|
||||
func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
||||
name := utils.GetName(r)
|
||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
utils.ContainerNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
|
||||
options := &handlers.UpdateEntities{Resources: &specs.LinuxResources{}}
|
||||
if err := json.NewDecoder(r.Body).Decode(&options.Resources); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err))
|
||||
return
|
||||
}
|
||||
err = ctr.Update(options.Resources)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusCreated, ctr.ID())
|
||||
}
|
||||
|
||||
func ShouldRestart(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
// Now use the ABI implementation to prevent us from having duplicate
|
||||
|
@ -313,6 +313,11 @@ type containerCreateResponse struct {
|
||||
Body entities.ContainerCreateResponse
|
||||
}
|
||||
|
||||
type containerUpdateResponse struct {
|
||||
// in:body
|
||||
ID string
|
||||
}
|
||||
|
||||
// Wait container
|
||||
// swagger:response
|
||||
type containerWaitResponse struct {
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
dockerContainer "github.com/docker/docker/api/types/container"
|
||||
dockerNetwork "github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
type AuthConfig struct {
|
||||
@ -64,6 +65,12 @@ type LibpodContainersRmReport struct {
|
||||
RmError string `json:"Err,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateEntities used to wrap the oci resource spec in a swagger model
|
||||
// swagger:model
|
||||
type UpdateEntities struct {
|
||||
Resources *specs.LinuxResources
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
docker.Info
|
||||
BuildahVersion string
|
||||
|
@ -1626,5 +1626,33 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
||||
// 500:
|
||||
// $ref: "#/responses/internalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost)
|
||||
// swagger:operation POST /libpod/containers/{name}/update libpod ContainerUpdateLibpod
|
||||
// ---
|
||||
// tags:
|
||||
// - containers
|
||||
// summary: Update an existing containers cgroup configuration
|
||||
// description: Update an existing containers cgroup configuration.
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: Full or partial ID or full name of the container to update
|
||||
// - in: body
|
||||
// name: resources
|
||||
// description: attributes for updating the container
|
||||
// schema:
|
||||
// $ref: "#/definitions/UpdateEntities"
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// responses:
|
||||
// 201:
|
||||
// $ref: "#/responses/containerUpdateResponse"
|
||||
// 404:
|
||||
// $ref: "#/responses/containerNotFound"
|
||||
// 500:
|
||||
// $ref: "#/responses/internalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/containers/{name}/update"), s.APIHandler(libpod.UpdateContainer)).Methods(http.MethodPost)
|
||||
return nil
|
||||
}
|
||||
|
31
pkg/bindings/containers/update.go
Normal file
31
pkg/bindings/containers/update.go
Normal file
@ -0,0 +1,31 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/bindings"
|
||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func Update(ctx context.Context, options *entities.ContainerUpdateOptions) (string, error) {
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resources, err := jsoniter.MarshalToString(options.Specgen.ResourceLimits)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stringReader := strings.NewReader(resources)
|
||||
response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/containers/%s/update", nil, nil, options.NameOrID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return options.NameOrID, response.Process(nil)
|
||||
}
|
@ -495,3 +495,9 @@ type ContainerCloneOptions struct {
|
||||
Run bool
|
||||
Force bool
|
||||
}
|
||||
|
||||
// ContainerUpdateOptions containers options for updating an existing containers cgroup configuration
|
||||
type ContainerUpdateOptions struct {
|
||||
NameOrID string
|
||||
Specgen *specgen.SpecGenerator
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ type ContainerEngine interface {
|
||||
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
|
||||
ContainerUnmount(ctx context.Context, nameOrIDs []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error)
|
||||
ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
||||
ContainerUpdate(ctx context.Context, options *ContainerUpdateOptions) (string, error)
|
||||
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
||||
Diff(ctx context.Context, namesOrIds []string, options DiffOptions) (*DiffReport, error)
|
||||
Events(ctx context.Context, opts EventsOptions) error
|
||||
|
@ -164,6 +164,15 @@ type PodCloneOptions struct {
|
||||
Start bool
|
||||
}
|
||||
|
||||
type ContainerMode string
|
||||
|
||||
const (
|
||||
InfraMode = ContainerMode("infra")
|
||||
CloneMode = ContainerMode("clone")
|
||||
UpdateMode = ContainerMode("update")
|
||||
CreateMode = ContainerMode("create")
|
||||
)
|
||||
|
||||
type ContainerCreateOptions struct {
|
||||
Annotation []string
|
||||
Attach []string
|
||||
|
@ -1715,3 +1715,27 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti
|
||||
|
||||
return &entities.ContainerCreateReport{Id: ctr.ID()}, nil
|
||||
}
|
||||
|
||||
// ContainerUpdate finds and updates the given container's cgroup config with the specified options
|
||||
func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *entities.ContainerUpdateOptions) (string, error) {
|
||||
err := specgen.WeightDevices(updateOptions.Specgen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = specgen.FinishThrottleDevices(updateOptions.Specgen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ctrs, err := getContainersByContext(false, false, []string{updateOptions.NameOrID}, ic.Libpod)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(ctrs) != 1 {
|
||||
return "", fmt.Errorf("container not found")
|
||||
}
|
||||
|
||||
if err = ctrs[0].Update(updateOptions.Specgen.ResourceLimits); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ctrs[0].ID(), nil
|
||||
}
|
||||
|
@ -1024,3 +1024,16 @@ func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string,
|
||||
func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts entities.ContainerCloneOptions) (*entities.ContainerCreateReport, error) {
|
||||
return nil, errors.New("cloning a container is not supported on the remote client")
|
||||
}
|
||||
|
||||
// ContainerUpdate finds and updates the given container's cgroup config with the specified options
|
||||
func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *entities.ContainerUpdateOptions) (string, error) {
|
||||
err := specgen.WeightDevices(updateOptions.Specgen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = specgen.FinishThrottleDevices(updateOptions.Specgen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return containers.Update(ic.ClientCtx, updateOptions)
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/openshift/imagebuilder"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) (*libimage.Image, string, *libimage.ImageData, error) {
|
||||
@ -518,75 +517,6 @@ func mapSecurityConfig(c *libpod.ContainerConfig, s *specgen.SpecGenerator) {
|
||||
s.HostUsers = c.HostUsers
|
||||
}
|
||||
|
||||
// FinishThrottleDevices takes the temporary representation of the throttle
|
||||
// devices in the specgen and looks up the major and major minors. it then
|
||||
// sets the throttle devices proper in the specgen
|
||||
func FinishThrottleDevices(s *specgen.SpecGenerator) error {
|
||||
if s.ResourceLimits == nil {
|
||||
s.ResourceLimits = &spec.LinuxResources{}
|
||||
}
|
||||
if bps := s.ThrottleReadBpsDevice; len(bps) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range bps {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO)
|
||||
}
|
||||
s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v)
|
||||
}
|
||||
}
|
||||
if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range bps {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v)
|
||||
}
|
||||
}
|
||||
if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range iops {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v)
|
||||
}
|
||||
}
|
||||
if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range iops {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check name looks for existing containers/pods with the same name, and modifies the given string until a new name is found
|
||||
func CheckName(rt *libpod.Runtime, n string, kind bool) string {
|
||||
switch {
|
||||
|
@ -56,7 +56,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
|
||||
}
|
||||
}
|
||||
|
||||
if err := FinishThrottleDevices(s); err != nil {
|
||||
if err := specgen.FinishThrottleDevices(s); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) {
|
||||
}
|
||||
|
||||
if !p.PodSpecGen.NoInfra {
|
||||
err := FinishThrottleDevices(p.PodSpecGen.InfraContainerSpec)
|
||||
err := specgen.FinishThrottleDevices(p.PodSpecGen.InfraContainerSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -53,17 +53,11 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) {
|
||||
p.PodSpecGen.ResourceLimits.BlockIO = p.PodSpecGen.InfraContainerSpec.ResourceLimits.BlockIO
|
||||
}
|
||||
|
||||
weightDevices, err := WeightDevices(p.PodSpecGen.InfraContainerSpec.WeightDevice)
|
||||
err = specgen.WeightDevices(p.PodSpecGen.InfraContainerSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if p.PodSpecGen.ResourceLimits != nil && len(weightDevices) > 0 {
|
||||
if p.PodSpecGen.ResourceLimits.BlockIO == nil {
|
||||
p.PodSpecGen.ResourceLimits.BlockIO = &specs.LinuxBlockIO{}
|
||||
}
|
||||
p.PodSpecGen.ResourceLimits.BlockIO.WeightDevice = weightDevices
|
||||
}
|
||||
p.PodSpecGen.ResourceLimits = p.PodSpecGen.InfraContainerSpec.ResourceLimits
|
||||
}
|
||||
|
||||
options, err := createPodOptions(&p.PodSpecGen)
|
||||
|
14
pkg/specgen/utils.go
Normal file
14
pkg/specgen/utils.go
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package specgen
|
||||
|
||||
// FinishThrottleDevices cannot be called on non-linux OS' due to importing unix functions
|
||||
func FinishThrottleDevices(s *SpecGenerator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WeightDevices cannot be called on non-linux OS' due to importing unix functions
|
||||
func WeightDevices(s *SpecGenerator) error {
|
||||
return nil
|
||||
}
|
103
pkg/specgen/utils_linux.go
Normal file
103
pkg/specgen/utils_linux.go
Normal file
@ -0,0 +1,103 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package specgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// FinishThrottleDevices takes the temporary representation of the throttle
|
||||
// devices in the specgen and looks up the major and major minors. it then
|
||||
// sets the throttle devices proper in the specgen
|
||||
func FinishThrottleDevices(s *SpecGenerator) error {
|
||||
if s.ResourceLimits == nil {
|
||||
s.ResourceLimits = &spec.LinuxResources{}
|
||||
}
|
||||
if bps := s.ThrottleReadBpsDevice; len(bps) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range bps {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO)
|
||||
}
|
||||
s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v)
|
||||
}
|
||||
}
|
||||
if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range bps {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v)
|
||||
}
|
||||
}
|
||||
if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range iops {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v)
|
||||
}
|
||||
}
|
||||
if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 {
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
for k, v := range iops {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("could not parse throttle device at %s: %w", k, err)
|
||||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func WeightDevices(specgen *SpecGenerator) error {
|
||||
devs := []spec.LinuxWeightDevice{}
|
||||
if specgen.ResourceLimits == nil {
|
||||
specgen.ResourceLimits = &spec.LinuxResources{}
|
||||
}
|
||||
for k, v := range specgen.WeightDevice {
|
||||
statT := unix.Stat_t{}
|
||||
if err := unix.Stat(k, &statT); err != nil {
|
||||
return fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err)
|
||||
}
|
||||
dev := new(spec.LinuxWeightDevice)
|
||||
dev.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
dev.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert
|
||||
dev.Weight = v.Weight
|
||||
devs = append(devs, *dev)
|
||||
if specgen.ResourceLimits.BlockIO == nil {
|
||||
specgen.ResourceLimits.BlockIO = &spec.LinuxBlockIO{}
|
||||
}
|
||||
specgen.ResourceLimits.BlockIO.WeightDevice = devs
|
||||
}
|
||||
return nil
|
||||
}
|
@ -507,44 +507,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
||||
s.ResourceLimits = &specs.LinuxResources{}
|
||||
}
|
||||
|
||||
if s.ResourceLimits.Memory == nil || (len(c.Memory) != 0 || len(c.MemoryReservation) != 0 || len(c.MemorySwap) != 0 || c.MemorySwappiness != 0) {
|
||||
s.ResourceLimits.Memory, err = getMemoryLimits(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if s.ResourceLimits.BlockIO == nil || (len(c.BlkIOWeight) != 0 || len(c.BlkIOWeightDevice) != 0 || len(c.DeviceReadBPs) != 0 || len(c.DeviceWriteBPs) != 0) {
|
||||
s.ResourceLimits.BlockIO, err = getIOLimits(s, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.PIDsLimit != nil {
|
||||
pids := specs.LinuxPids{
|
||||
Limit: *c.PIDsLimit,
|
||||
}
|
||||
|
||||
s.ResourceLimits.Pids = &pids
|
||||
}
|
||||
|
||||
if s.ResourceLimits.CPU == nil || (c.CPUPeriod != 0 || c.CPUQuota != 0 || c.CPURTPeriod != 0 || c.CPURTRuntime != 0 || c.CPUS != 0 || len(c.CPUSetCPUs) != 0 || len(c.CPUSetMems) != 0 || c.CPUShares != 0) {
|
||||
s.ResourceLimits.CPU = getCPULimits(c)
|
||||
}
|
||||
|
||||
unifieds := make(map[string]string)
|
||||
for _, unified := range c.CgroupConf {
|
||||
splitUnified := strings.SplitN(unified, "=", 2)
|
||||
if len(splitUnified) < 2 {
|
||||
return errors.New("--cgroup-conf must be formatted KEY=VALUE")
|
||||
}
|
||||
unifieds[splitUnified[0]] = splitUnified[1]
|
||||
}
|
||||
if len(unifieds) > 0 {
|
||||
s.ResourceLimits.Unified = unifieds
|
||||
}
|
||||
|
||||
if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil && s.ResourceLimits.Unified == nil {
|
||||
s.ResourceLimits = nil
|
||||
s.ResourceLimits, err = GetResources(s, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.LogConfiguration == nil {
|
||||
@ -1171,3 +1136,47 @@ func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, er
|
||||
Access: access,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetResources(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) (*specs.LinuxResources, error) {
|
||||
var err error
|
||||
if s.ResourceLimits.Memory == nil || (len(c.Memory) != 0 || len(c.MemoryReservation) != 0 || len(c.MemorySwap) != 0 || c.MemorySwappiness != 0) {
|
||||
s.ResourceLimits.Memory, err = getMemoryLimits(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if s.ResourceLimits.BlockIO == nil || (len(c.BlkIOWeight) != 0 || len(c.BlkIOWeightDevice) != 0 || len(c.DeviceReadBPs) != 0 || len(c.DeviceWriteBPs) != 0) {
|
||||
s.ResourceLimits.BlockIO, err = getIOLimits(s, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if c.PIDsLimit != nil {
|
||||
pids := specs.LinuxPids{
|
||||
Limit: *c.PIDsLimit,
|
||||
}
|
||||
|
||||
s.ResourceLimits.Pids = &pids
|
||||
}
|
||||
|
||||
if s.ResourceLimits.CPU == nil || (c.CPUPeriod != 0 || c.CPUQuota != 0 || c.CPURTPeriod != 0 || c.CPURTRuntime != 0 || c.CPUS != 0 || len(c.CPUSetCPUs) != 0 || len(c.CPUSetMems) != 0 || c.CPUShares != 0) {
|
||||
s.ResourceLimits.CPU = getCPULimits(c)
|
||||
}
|
||||
|
||||
unifieds := make(map[string]string)
|
||||
for _, unified := range c.CgroupConf {
|
||||
splitUnified := strings.SplitN(unified, "=", 2)
|
||||
if len(splitUnified) < 2 {
|
||||
return nil, errors.New("--cgroup-conf must be formatted KEY=VALUE")
|
||||
}
|
||||
unifieds[splitUnified[0]] = splitUnified[1]
|
||||
}
|
||||
if len(unifieds) > 0 {
|
||||
s.ResourceLimits.Unified = unifieds
|
||||
}
|
||||
|
||||
if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil && s.ResourceLimits.Unified == nil {
|
||||
s.ResourceLimits = nil
|
||||
}
|
||||
return s.ResourceLimits, nil
|
||||
}
|
||||
|
@ -547,6 +547,21 @@ t GET libpod/containers/$cname/json 200 \
|
||||
.ImageName=$IMAGE \
|
||||
.Name=$cname
|
||||
|
||||
if root; then
|
||||
podman run -dt --name=updateCtr alpine
|
||||
echo '{"Memory":{"Limit":500000}, "CPU":{"Shares":123}}' >${TMPD}/update.json
|
||||
t POST libpod/containers/updateCtr/update ${TMPD}/update.json 201
|
||||
|
||||
# Verify
|
||||
echo '{ "AttachStdout":true,"Cmd":["cat","/sys/fs/cgroup/cpu.weight"]}' >${TMPD}/exec.json
|
||||
t POST containers/updateCtr/exec ${TMPD}/exec.json 201 .Id~[0-9a-f]\\{64\\}
|
||||
eid=$(jq -r '.Id' <<<"$output")
|
||||
# 002 is the byte length
|
||||
t POST exec/$eid/start 200 $'\001\0025'
|
||||
|
||||
podman rm -f updateCtr
|
||||
fi
|
||||
|
||||
rm -rf $TMPD
|
||||
|
||||
podman container rm -fa
|
||||
|
@ -359,8 +359,6 @@ class ContainerTestCase(APITestCase):
|
||||
self.assertEqual(2000, out["HostConfig"]["MemorySwap"])
|
||||
self.assertEqual(1000, out["HostConfig"]["Memory"])
|
||||
|
||||
|
||||
|
||||
def execute_process(cmd):
|
||||
return subprocess.run(
|
||||
cmd,
|
||||
|
200
test/e2e/update_test.go
Normal file
200
test/e2e/update_test.go
Normal file
@ -0,0 +1,200 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"github.com/containers/common/pkg/cgroupv2"
|
||||
. "github.com/containers/podman/v4/test/utils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
. "github.com/onsi/gomega/gexec"
|
||||
)
|
||||
|
||||
var _ = Describe("Podman update", func() {
|
||||
var (
|
||||
tempdir string
|
||||
err error
|
||||
podmanTest *PodmanTestIntegration
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
tempdir, err = CreateTempDirInTempDir()
|
||||
Expect(err).To(BeNil())
|
||||
podmanTest = PodmanTestCreate(tempdir)
|
||||
podmanTest.Setup()
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
podmanTest.Cleanup()
|
||||
f := CurrentGinkgoTestDescription()
|
||||
processTestResult(f)
|
||||
|
||||
})
|
||||
|
||||
It("podman update container all options v1", func() {
|
||||
SkipIfCgroupV2("testing flags that only work in cgroup v1")
|
||||
SkipIfRootless("many of these handlers are not enabled while rootless in CI")
|
||||
session := podmanTest.Podman([]string{"run", "-dt", ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
ctrID := session.OutputToString()
|
||||
|
||||
commonArgs := []string{
|
||||
"update",
|
||||
"--cpus", "5",
|
||||
"--cpuset-cpus", "0",
|
||||
"--cpu-shares", "123",
|
||||
"--cpuset-mems", "0",
|
||||
"--memory", "1G",
|
||||
"--memory-swap", "2G",
|
||||
"--memory-reservation", "2G",
|
||||
"--memory-swappiness", "50", ctrID}
|
||||
|
||||
session = podmanTest.Podman(commonArgs)
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
// checking cpu quota from --cpus
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("500000"))
|
||||
|
||||
// checking cpuset-cpus
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset/cpuset.cpus"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(Equal("0"))
|
||||
|
||||
// checking cpuset-mems
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset/cpuset.mems"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(Equal("0"))
|
||||
|
||||
// checking memory limit
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("1073741824"))
|
||||
|
||||
// checking memory-swap
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("2147483648"))
|
||||
|
||||
// checking cpu-shares
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.shares"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("123"))
|
||||
|
||||
})
|
||||
|
||||
It("podman update container all options v2", func() {
|
||||
SkipIfCgroupV1("testing flags that only work in cgroup v2")
|
||||
SkipIfRootless("many of these handlers are not enabled while rootless in CI")
|
||||
session := podmanTest.Podman([]string{"run", "-dt", ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
ctrID := session.OutputToString()
|
||||
|
||||
commonArgs := []string{
|
||||
"update",
|
||||
"--cpus", "5",
|
||||
"--cpuset-cpus", "0",
|
||||
"--cpu-shares", "123",
|
||||
"--cpuset-mems", "0",
|
||||
"--memory", "1G",
|
||||
"--memory-swap", "2G",
|
||||
"--memory-reservation", "2G",
|
||||
"--blkio-weight", "123",
|
||||
"--device-read-bps", "/dev/zero:10mb",
|
||||
"--device-write-bps", "/dev/zero:10mb",
|
||||
"--device-read-iops", "/dev/zero:1000",
|
||||
"--device-write-iops", "/dev/zero:1000",
|
||||
ctrID}
|
||||
|
||||
session = podmanTest.Podman(commonArgs)
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
ctrID = session.OutputToString()
|
||||
|
||||
// checking cpu quota and period
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.max"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("500000"))
|
||||
|
||||
// checking blkio weight
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/io.bfq.weight"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("123"))
|
||||
|
||||
// checking device-read/write-bps/iops
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/io.max"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("rbps=10485760 wbps=10485760 riops=1000 wiops=1000"))
|
||||
|
||||
// checking cpuset-cpus
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset.cpus"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(Equal("0"))
|
||||
|
||||
// checking cpuset-mems
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset.mems"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(Equal("0"))
|
||||
|
||||
// checking memory limit
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory.max"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("1073741824"))
|
||||
|
||||
// checking memory-swap
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory.swap.max"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("1073741824"))
|
||||
|
||||
// checking cpu-shares
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.weight"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("5"))
|
||||
})
|
||||
|
||||
It("podman update keep original resources if not overridden", func() {
|
||||
SkipIfRootless("many of these handlers are not enabled while rootless in CI")
|
||||
session := podmanTest.Podman([]string{"run", "-dt", "--cpus", "5", ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
session = podmanTest.Podman([]string{
|
||||
"update",
|
||||
"--memory", "1G",
|
||||
session.OutputToString(),
|
||||
})
|
||||
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
ctrID := session.OutputToString()
|
||||
|
||||
if v2, _ := cgroupv2.Enabled(); v2 {
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.max"})
|
||||
} else {
|
||||
session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"})
|
||||
}
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).Should(ContainSubstring("500000"))
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user