mirror of https://github.com/containers/podman.git
Merge 34c284d367
into dfd205fa24
This commit is contained in:
commit
3d9aa32f1e
|
@ -440,14 +440,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||||
)
|
)
|
||||||
_ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone)
|
_ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
ulimitFlagName := "ulimit"
|
|
||||||
createFlags.StringSliceVar(
|
|
||||||
&cf.Ulimit,
|
|
||||||
ulimitFlagName, cf.Ulimit,
|
|
||||||
"Ulimit options",
|
|
||||||
)
|
|
||||||
_ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone)
|
|
||||||
|
|
||||||
userFlagName := "user"
|
userFlagName := "user"
|
||||||
createFlags.StringVarP(
|
createFlags.StringVarP(
|
||||||
&cf.User,
|
&cf.User,
|
||||||
|
@ -559,6 +551,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
||||||
)
|
)
|
||||||
_ = cmd.RegisterFlagCompletionFunc(unsetenvFlagName, completion.AutocompleteNone)
|
_ = cmd.RegisterFlagCompletionFunc(unsetenvFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
|
ulimitFlagName := "ulimit"
|
||||||
|
createFlags.StringSliceVar(
|
||||||
|
&cf.Ulimit,
|
||||||
|
ulimitFlagName, cf.Ulimit,
|
||||||
|
"Ulimit options",
|
||||||
|
)
|
||||||
|
_ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
healthCmdFlagName := "health-cmd"
|
healthCmdFlagName := "health-cmd"
|
||||||
createFlags.StringVar(
|
createFlags.StringVar(
|
||||||
&cf.HealthCmd,
|
&cf.HealthCmd,
|
||||||
|
|
|
@ -179,6 +179,18 @@ func update(cmd *cobra.Command, args []string) error {
|
||||||
opts.UnsetEnv = env
|
opts.UnsetEnv = env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Changed("ulimit") {
|
||||||
|
ulimits, err := cmd.Flags().GetStringSlice("ulimit")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rlimits, err := specgenutil.GenRlimits(ulimits)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opts.Rlimits = rlimits
|
||||||
|
}
|
||||||
|
|
||||||
rep, err := registry.ContainerEngine().ContainerUpdate(context.Background(), opts)
|
rep, err := registry.ContainerEngine().ContainerUpdate(context.Background(), opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
####> This option file is used in:
|
####> This option file is used in:
|
||||||
####> podman create, run
|
####> podman create, run, update
|
||||||
####> If file is edited, make sure the changes
|
####> If file is edited, make sure the changes
|
||||||
####> are applicable to all of those.
|
####> are applicable to all of those.
|
||||||
#### **--ulimit**=*option*
|
#### **--ulimit**=*option*
|
||||||
|
|
|
@ -92,6 +92,8 @@ Changing this setting resets the timer, depending on the state of the container.
|
||||||
|
|
||||||
@@option restart
|
@@option restart
|
||||||
|
|
||||||
|
@@option ulimit
|
||||||
|
|
||||||
@@option unsetenv.update
|
@@option unsetenv.update
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,6 +104,11 @@ Update a container with a new cpu quota and period:
|
||||||
podman update --cpus=0.5 ctrID
|
podman update --cpus=0.5 ctrID
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Update a container's ulimit:
|
||||||
|
```
|
||||||
|
podman update --ulimit nofile=1024:1024 myCtr
|
||||||
|
```
|
||||||
|
|
||||||
Update a container with multiple options at ones:
|
Update a container with multiple options at ones:
|
||||||
```
|
```
|
||||||
podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 \\
|
podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 \\
|
||||||
|
|
|
@ -2810,6 +2810,7 @@ func (c *Container) update(updateOptions *entities.ContainerUpdateOptions) error
|
||||||
}
|
}
|
||||||
oldRestart := c.config.RestartPolicy
|
oldRestart := c.config.RestartPolicy
|
||||||
oldRetries := c.config.RestartRetries
|
oldRetries := c.config.RestartRetries
|
||||||
|
oldRlimits := c.config.Spec.Process.Rlimits
|
||||||
|
|
||||||
if updateOptions.RestartPolicy != nil {
|
if updateOptions.RestartPolicy != nil {
|
||||||
if err := define.ValidateRestartPolicy(*updateOptions.RestartPolicy); err != nil {
|
if err := define.ValidateRestartPolicy(*updateOptions.RestartPolicy); err != nil {
|
||||||
|
@ -2857,16 +2858,21 @@ func (c *Container) update(updateOptions *entities.ContainerUpdateOptions) error
|
||||||
c.config.Spec.Process.Env = envLib.Slice(envMap)
|
c.config.Spec.Process.Env = envLib.Slice(envMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if updateOptions.Rlimits != nil {
|
||||||
|
c.config.Spec.Process.Rlimits = updateOptions.Rlimits
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.runtime.state.SafeRewriteContainerConfig(c, "", "", c.config); err != nil {
|
if err := c.runtime.state.SafeRewriteContainerConfig(c, "", "", c.config); err != nil {
|
||||||
// Assume DB write failed, revert to old resources block
|
// Assume DB write failed, revert to old resources block
|
||||||
c.config.Spec.Linux.Resources = oldResources
|
c.config.Spec.Linux.Resources = oldResources
|
||||||
c.config.RestartPolicy = oldRestart
|
c.config.RestartPolicy = oldRestart
|
||||||
c.config.RestartRetries = oldRetries
|
c.config.RestartRetries = oldRetries
|
||||||
|
c.config.Spec.Process.Rlimits = oldRlimits
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused) &&
|
if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused) &&
|
||||||
(updateOptions.Resources != nil || updateOptions.Env != nil || updateOptions.UnsetEnv != nil) {
|
(updateOptions.Resources != nil || updateOptions.Env != nil || updateOptions.UnsetEnv != nil || updateOptions.Rlimits != nil) {
|
||||||
// So `podman inspect` on running containers sources its OCI spec from disk.
|
// So `podman inspect` on running containers sources its OCI spec from disk.
|
||||||
// To keep inspect accurate we need to update the on-disk OCI spec.
|
// To keep inspect accurate we need to update the on-disk OCI spec.
|
||||||
onDiskSpec, err := c.specFromState()
|
onDiskSpec, err := c.specFromState()
|
||||||
|
@ -2882,6 +2888,9 @@ func (c *Container) update(updateOptions *entities.ContainerUpdateOptions) error
|
||||||
if len(updateOptions.Env) != 0 || len(updateOptions.UnsetEnv) != 0 {
|
if len(updateOptions.Env) != 0 || len(updateOptions.UnsetEnv) != 0 {
|
||||||
onDiskSpec.Process.Env = c.config.Spec.Process.Env
|
onDiskSpec.Process.Env = c.config.Spec.Process.Env
|
||||||
}
|
}
|
||||||
|
if updateOptions.Rlimits != nil {
|
||||||
|
onDiskSpec.Process.Rlimits = updateOptions.Rlimits
|
||||||
|
}
|
||||||
if err := c.saveSpec(onDiskSpec); err != nil {
|
if err := c.saveSpec(onDiskSpec); err != nil {
|
||||||
logrus.Errorf("Unable to update container %s OCI spec - `podman inspect` may not be accurate until container is restarted: %v", c.ID(), err)
|
logrus.Errorf("Unable to update container %s OCI spec - `podman inspect` may not be accurate until container is restarted: %v", c.ID(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -822,11 +822,25 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
restartRetries = &localRetries
|
restartRetries = &localRetries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rlimits
|
||||||
|
var rlimits []spec.POSIXRlimit
|
||||||
|
if len(options.Ulimits) > 0 {
|
||||||
|
for _, ulimit := range options.Ulimits {
|
||||||
|
rlimit := spec.POSIXRlimit{
|
||||||
|
Type: ulimit.Name,
|
||||||
|
Hard: uint64(ulimit.Hard),
|
||||||
|
Soft: uint64(ulimit.Soft),
|
||||||
|
}
|
||||||
|
rlimits = append(rlimits, rlimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateOptions := &entities.ContainerUpdateOptions{
|
updateOptions := &entities.ContainerUpdateOptions{
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
ChangedHealthCheckConfiguration: &define.UpdateHealthCheckConfig{},
|
ChangedHealthCheckConfiguration: &define.UpdateHealthCheckConfig{},
|
||||||
RestartPolicy: restartPolicy,
|
RestartPolicy: restartPolicy,
|
||||||
RestartRetries: restartRetries,
|
RestartRetries: restartRetries,
|
||||||
|
Rlimits: rlimits,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ctr.Update(updateOptions); err != nil {
|
if err := ctr.Update(updateOptions); err != nil {
|
||||||
|
|
|
@ -462,6 +462,7 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
RestartRetries: restartRetries,
|
RestartRetries: restartRetries,
|
||||||
Env: options.Env,
|
Env: options.Env,
|
||||||
UnsetEnv: options.UnsetEnv,
|
UnsetEnv: options.UnsetEnv,
|
||||||
|
Rlimits: options.Rlimits,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ctr.Update(updateOptions)
|
err = ctr.Update(updateOptions)
|
||||||
|
|
|
@ -81,6 +81,7 @@ type UpdateEntities struct {
|
||||||
define.UpdateContainerDevicesLimits
|
define.UpdateContainerDevicesLimits
|
||||||
Env []string
|
Env []string
|
||||||
UnsetEnv []string
|
UnsetEnv []string
|
||||||
|
Rlimits []specs.POSIXRlimit
|
||||||
}
|
}
|
||||||
|
|
||||||
type Info struct {
|
type Info struct {
|
||||||
|
|
|
@ -40,6 +40,9 @@ func Update(ctx context.Context, options *types.ContainerUpdateOptions) (string,
|
||||||
if options.DevicesLimits != nil {
|
if options.DevicesLimits != nil {
|
||||||
updateEntities.UpdateContainerDevicesLimits = *options.DevicesLimits
|
updateEntities.UpdateContainerDevicesLimits = *options.DevicesLimits
|
||||||
}
|
}
|
||||||
|
if options.Rlimits != nil {
|
||||||
|
updateEntities.Rlimits = options.Rlimits
|
||||||
|
}
|
||||||
|
|
||||||
requestData, err := jsoniter.MarshalToString(updateEntities)
|
requestData, err := jsoniter.MarshalToString(updateEntities)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -53,6 +53,7 @@ type ContainerUpdateOptions struct {
|
||||||
// - RestartRetries to change restart retries
|
// - RestartRetries to change restart retries
|
||||||
// - Env to change the environment variables.
|
// - Env to change the environment variables.
|
||||||
// - UntsetEnv to unset the environment variables.
|
// - UntsetEnv to unset the environment variables.
|
||||||
|
// - Rlimits to change POSIX resource limits.
|
||||||
Specgen *specgen.SpecGenerator
|
Specgen *specgen.SpecGenerator
|
||||||
Resources *specs.LinuxResources
|
Resources *specs.LinuxResources
|
||||||
DevicesLimits *define.UpdateContainerDevicesLimits
|
DevicesLimits *define.UpdateContainerDevicesLimits
|
||||||
|
@ -61,6 +62,7 @@ type ContainerUpdateOptions struct {
|
||||||
RestartRetries *uint
|
RestartRetries *uint
|
||||||
Env []string
|
Env []string
|
||||||
UnsetEnv []string
|
UnsetEnv []string
|
||||||
|
Rlimits []specs.POSIXRlimit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerUpdateOptions) ProcessSpecgen() {
|
func (u *ContainerUpdateOptions) ProcessSpecgen() {
|
||||||
|
@ -87,4 +89,8 @@ func (u *ContainerUpdateOptions) ProcessSpecgen() {
|
||||||
if u.RestartRetries == nil {
|
if u.RestartRetries == nil {
|
||||||
u.RestartRetries = u.Specgen.RestartRetries
|
u.RestartRetries = u.Specgen.RestartRetries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if u.Rlimits == nil {
|
||||||
|
u.Rlimits = u.Specgen.Rlimits
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1811,15 +1811,15 @@ func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *e
|
||||||
if len(containers) != 1 {
|
if len(containers) != 1 {
|
||||||
return "", fmt.Errorf("container not found")
|
return "", fmt.Errorf("container not found")
|
||||||
}
|
}
|
||||||
container := containers[0].Container
|
ctr := containers[0]
|
||||||
|
|
||||||
updateOptions.Resources, err = specgenutil.UpdateMajorAndMinorNumbers(updateOptions.Resources, updateOptions.DevicesLimits)
|
updateOptions.Resources, err = specgenutil.UpdateMajorAndMinorNumbers(updateOptions.Resources, updateOptions.DevicesLimits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = container.Update(updateOptions); err != nil {
|
if err = ctr.Container.Update(updateOptions); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return containers[0].ID(), nil
|
return ctr.ID(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,4 +306,48 @@ var _ = Describe("Podman update", func() {
|
||||||
Expect(env).ToNot(ContainSubstring("FOO"))
|
Expect(env).ToNot(ContainSubstring("FOO"))
|
||||||
Expect(env).To(ContainSubstring("PATH="))
|
Expect(env).To(ContainSubstring("PATH="))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman update sets ulimits", func() {
|
||||||
|
session := podmanTest.PodmanExitCleanly("run", "-dt", ALPINE)
|
||||||
|
ctrID := session.OutputToString()
|
||||||
|
|
||||||
|
// Get initial ulimit value
|
||||||
|
initialUlimit := podmanTest.PodmanExitCleanly("exec", ctrID, "sh", "-c", "ulimit -n")
|
||||||
|
Expect(initialUlimit.OutputToString()).ToNot(BeEmpty())
|
||||||
|
|
||||||
|
// Update with a single ulimit
|
||||||
|
podmanTest.PodmanExitCleanly("update", "--ulimit", "nofile=1024:1024", ctrID)
|
||||||
|
|
||||||
|
// Verify ulimit was updated
|
||||||
|
ulimit := podmanTest.PodmanExitCleanly("exec", ctrID, "sh", "-c", "ulimit -n")
|
||||||
|
Expect(ulimit.OutputToString()).To(Equal("1024"))
|
||||||
|
|
||||||
|
// Update with multiple ulimits
|
||||||
|
podmanTest.PodmanExitCleanly("update", "--ulimit", "nofile=2048:2048", "--ulimit", "nproc=512:512", ctrID)
|
||||||
|
|
||||||
|
// Verify nofile ulimit was updated
|
||||||
|
ulimit = podmanTest.PodmanExitCleanly("exec", ctrID, "sh", "-c", "ulimit -n")
|
||||||
|
Expect(ulimit.OutputToString()).To(Equal("2048"))
|
||||||
|
|
||||||
|
// Verify nproc ulimit was updated
|
||||||
|
ulimit = podmanTest.PodmanExitCleanly("exec", ctrID, "sh", "-c", "ulimit -u")
|
||||||
|
Expect(ulimit.OutputToString()).To(Equal("512"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman update with invalid ulimit fails", func() {
|
||||||
|
session := podmanTest.PodmanExitCleanly("run", "-dt", ALPINE)
|
||||||
|
ctrID := session.OutputToString()
|
||||||
|
|
||||||
|
// Try with invalid ulimit syntax (missing =)
|
||||||
|
update := podmanTest.Podman([]string{"update", "--ulimit", "nofile:1024:1024", ctrID})
|
||||||
|
update.WaitWithDefaultTimeout()
|
||||||
|
Expect(update).Should(Exit(125))
|
||||||
|
Expect(update.ErrorToString()).To(ContainSubstring("error"))
|
||||||
|
|
||||||
|
// Try with invalid ulimit value (soft > hard)
|
||||||
|
update = podmanTest.Podman([]string{"update", "--ulimit", "nofile=2048:1024", ctrID})
|
||||||
|
update.WaitWithDefaultTimeout()
|
||||||
|
Expect(update).Should(Exit(125))
|
||||||
|
Expect(update.ErrorToString()).To(ContainSubstring("error"))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -349,4 +349,54 @@ function nrand() {
|
||||||
|
|
||||||
run_podman rm -t 0 -f "$cid"
|
run_podman rm -t 0 -f "$cid"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=ci:parallel
|
||||||
|
@test "podman update - set ulimits" {
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
run_podman run -d --name $ctrname $IMAGE sleep 600
|
||||||
|
|
||||||
|
# Get initial nofile ulimit
|
||||||
|
run_podman exec $ctrname sh -c "ulimit -n"
|
||||||
|
initial_nofile="$output"
|
||||||
|
|
||||||
|
# Update with single ulimit
|
||||||
|
run_podman update --ulimit nofile=1024:1024 $ctrname
|
||||||
|
|
||||||
|
# Verify the nofile ulimit was updated
|
||||||
|
run_podman exec $ctrname sh -c "ulimit -n"
|
||||||
|
assert "$output" == "1024" "nofile ulimit updated to 1024"
|
||||||
|
assert "$output" != "$initial_nofile" "nofile ulimit should have changed"
|
||||||
|
|
||||||
|
# Update with multiple ulimits
|
||||||
|
run_podman update --ulimit nofile=2048:2048 --ulimit nproc=512:512 $ctrname
|
||||||
|
|
||||||
|
# Verify the nofile ulimit was updated again
|
||||||
|
run_podman exec $ctrname sh -c "ulimit -n"
|
||||||
|
assert "$output" == "2048" "nofile ulimit updated to 2048"
|
||||||
|
|
||||||
|
# Verify the nproc ulimit was updated
|
||||||
|
run_podman exec $ctrname sh -c "ulimit -u"
|
||||||
|
assert "$output" == "512" "nproc ulimit updated to 512"
|
||||||
|
|
||||||
|
# Test persists across container restart
|
||||||
|
run_podman restart $ctrname
|
||||||
|
|
||||||
|
# Verify the ulimits persist after restart
|
||||||
|
run_podman exec $ctrname sh -c "ulimit -n"
|
||||||
|
assert "$output" == "2048" "nofile ulimit persists after restart"
|
||||||
|
|
||||||
|
run_podman exec $ctrname sh -c "ulimit -u"
|
||||||
|
assert "$output" == "512" "nproc ulimit persists after restart"
|
||||||
|
|
||||||
|
# Error cases
|
||||||
|
run_podman 125 update --ulimit nofile:1024:1024 $ctrname
|
||||||
|
assert "$output" =~ "error" "Invalid ulimit syntax should fail"
|
||||||
|
|
||||||
|
run_podman 125 update --ulimit nofile=2048:1024 $ctrname
|
||||||
|
assert "$output" =~ "error" "Invalid ulimit values should fail"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
# vim: filetype=sh
|
# vim: filetype=sh
|
||||||
|
|
Loading…
Reference in New Issue