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)
|
||||
|
||||
ulimitFlagName := "ulimit"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.Ulimit,
|
||||
ulimitFlagName, cf.Ulimit,
|
||||
"Ulimit options",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone)
|
||||
|
||||
userFlagName := "user"
|
||||
createFlags.StringVarP(
|
||||
&cf.User,
|
||||
|
@ -559,6 +551,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(unsetenvFlagName, completion.AutocompleteNone)
|
||||
|
||||
ulimitFlagName := "ulimit"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.Ulimit,
|
||||
ulimitFlagName, cf.Ulimit,
|
||||
"Ulimit options",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthCmdFlagName := "health-cmd"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthCmd,
|
||||
|
|
|
@ -179,6 +179,18 @@ func update(cmd *cobra.Command, args []string) error {
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--ulimit**=*option*
|
||||
|
|
|
@ -92,6 +92,8 @@ Changing this setting resets the timer, depending on the state of the container.
|
|||
|
||||
@@option restart
|
||||
|
||||
@@option ulimit
|
||||
|
||||
@@option unsetenv.update
|
||||
|
||||
|
||||
|
@ -102,6 +104,11 @@ Update a container with a new cpu quota and period:
|
|||
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:
|
||||
```
|
||||
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
|
||||
oldRetries := c.config.RestartRetries
|
||||
oldRlimits := c.config.Spec.Process.Rlimits
|
||||
|
||||
if updateOptions.RestartPolicy != 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)
|
||||
}
|
||||
|
||||
if updateOptions.Rlimits != nil {
|
||||
c.config.Spec.Process.Rlimits = updateOptions.Rlimits
|
||||
}
|
||||
|
||||
if err := c.runtime.state.SafeRewriteContainerConfig(c, "", "", c.config); err != nil {
|
||||
// Assume DB write failed, revert to old resources block
|
||||
c.config.Spec.Linux.Resources = oldResources
|
||||
c.config.RestartPolicy = oldRestart
|
||||
c.config.RestartRetries = oldRetries
|
||||
c.config.Spec.Process.Rlimits = oldRlimits
|
||||
return err
|
||||
}
|
||||
|
||||
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.
|
||||
// To keep inspect accurate we need to update the on-disk OCI spec.
|
||||
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 {
|
||||
onDiskSpec.Process.Env = c.config.Spec.Process.Env
|
||||
}
|
||||
if updateOptions.Rlimits != nil {
|
||||
onDiskSpec.Process.Rlimits = updateOptions.Rlimits
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -822,11 +822,25 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
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{
|
||||
Resources: resources,
|
||||
ChangedHealthCheckConfiguration: &define.UpdateHealthCheckConfig{},
|
||||
RestartPolicy: restartPolicy,
|
||||
RestartRetries: restartRetries,
|
||||
Rlimits: rlimits,
|
||||
}
|
||||
|
||||
if err := ctr.Update(updateOptions); err != nil {
|
||||
|
|
|
@ -462,6 +462,7 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
RestartRetries: restartRetries,
|
||||
Env: options.Env,
|
||||
UnsetEnv: options.UnsetEnv,
|
||||
Rlimits: options.Rlimits,
|
||||
}
|
||||
|
||||
err = ctr.Update(updateOptions)
|
||||
|
|
|
@ -81,6 +81,7 @@ type UpdateEntities struct {
|
|||
define.UpdateContainerDevicesLimits
|
||||
Env []string
|
||||
UnsetEnv []string
|
||||
Rlimits []specs.POSIXRlimit
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
|
|
|
@ -40,6 +40,9 @@ func Update(ctx context.Context, options *types.ContainerUpdateOptions) (string,
|
|||
if options.DevicesLimits != nil {
|
||||
updateEntities.UpdateContainerDevicesLimits = *options.DevicesLimits
|
||||
}
|
||||
if options.Rlimits != nil {
|
||||
updateEntities.Rlimits = options.Rlimits
|
||||
}
|
||||
|
||||
requestData, err := jsoniter.MarshalToString(updateEntities)
|
||||
if err != nil {
|
||||
|
|
|
@ -53,6 +53,7 @@ type ContainerUpdateOptions struct {
|
|||
// - RestartRetries to change restart retries
|
||||
// - Env to change the environment variables.
|
||||
// - UntsetEnv to unset the environment variables.
|
||||
// - Rlimits to change POSIX resource limits.
|
||||
Specgen *specgen.SpecGenerator
|
||||
Resources *specs.LinuxResources
|
||||
DevicesLimits *define.UpdateContainerDevicesLimits
|
||||
|
@ -61,6 +62,7 @@ type ContainerUpdateOptions struct {
|
|||
RestartRetries *uint
|
||||
Env []string
|
||||
UnsetEnv []string
|
||||
Rlimits []specs.POSIXRlimit
|
||||
}
|
||||
|
||||
func (u *ContainerUpdateOptions) ProcessSpecgen() {
|
||||
|
@ -87,4 +89,8 @@ func (u *ContainerUpdateOptions) ProcessSpecgen() {
|
|||
if u.RestartRetries == nil {
|
||||
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 {
|
||||
return "", fmt.Errorf("container not found")
|
||||
}
|
||||
container := containers[0].Container
|
||||
ctr := containers[0]
|
||||
|
||||
updateOptions.Resources, err = specgenutil.UpdateMajorAndMinorNumbers(updateOptions.Resources, updateOptions.DevicesLimits)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = container.Update(updateOptions); err != nil {
|
||||
if err = ctr.Container.Update(updateOptions); err != nil {
|
||||
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).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"
|
||||
}
|
||||
|
||||
# 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
|
||||
|
|
Loading…
Reference in New Issue