Skip to content

Commit c2cadfb

Browse files
Merge pull request containers#22322 from mheon/update_the_config
Make `podman update` changes persistent
2 parents 3c15aa3 + 482ef7b commit c2cadfb

File tree

25 files changed

+515
-103
lines changed

25 files changed

+515
-103
lines changed

cmd/podman/common/completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,7 @@ func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete strin
14231423
events.PullError.String(), events.Push.String(), events.Refresh.String(), events.Remove.String(),
14241424
events.Rename.String(), events.Renumber.String(), events.Restart.String(), events.Restore.String(),
14251425
events.Save.String(), events.Start.String(), events.Stop.String(), events.Sync.String(), events.Tag.String(),
1426-
events.Unmount.String(), events.Unpause.String(), events.Untag.String(),
1426+
events.Unmount.String(), events.Unpause.String(), events.Untag.String(), events.Update.String(),
14271427
}, cobra.ShellCompDirectiveNoFileComp
14281428
}
14291429
eventTypes := func(_ string) ([]string, cobra.ShellCompDirective) {

cmd/podman/common/create.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,15 +643,17 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
643643
`If a container with the same name exists, replace it`,
644644
)
645645
}
646-
if mode == entities.InfraMode || (mode == entities.CreateMode) { // infra container flags, create should also pick these up
646+
// Restart is allowed for created, updated, and infra ctr
647+
if mode == entities.InfraMode || mode == entities.CreateMode || mode == entities.UpdateMode {
647648
restartFlagName := "restart"
648649
createFlags.StringVar(
649650
&cf.Restart,
650651
restartFlagName, "",
651652
`Restart policy to apply when a container exits ("always"|"no"|"never"|"on-failure"|"unless-stopped")`,
652653
)
653654
_ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption)
654-
655+
}
656+
if mode == entities.InfraMode || (mode == entities.CreateMode) { // infra container flags, create should also pick these up
655657
shmSizeFlagName := "shm-size"
656658
createFlags.String(
657659
shmSizeFlagName, shmSize(),

cmd/podman/containers/update.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import (
77

88
"github.com/containers/podman/v5/cmd/podman/common"
99
"github.com/containers/podman/v5/cmd/podman/registry"
10+
"github.com/containers/podman/v5/libpod/define"
1011
"github.com/containers/podman/v5/pkg/domain/entities"
1112
"github.com/containers/podman/v5/pkg/specgen"
1213
"github.com/containers/podman/v5/pkg/specgenutil"
14+
"github.com/containers/podman/v5/pkg/util"
1315
"github.com/opencontainers/runtime-spec/specs-go"
1416
"github.com/spf13/cobra"
1517
)
@@ -70,6 +72,17 @@ func update(cmd *cobra.Command, args []string) error {
7072
return err
7173
}
7274

75+
if updateOpts.Restart != "" {
76+
policy, retries, err := util.ParseRestartPolicy(updateOpts.Restart)
77+
if err != nil {
78+
return err
79+
}
80+
s.RestartPolicy = policy
81+
if policy == define.RestartPolicyOnFailure {
82+
s.RestartRetries = &retries
83+
}
84+
}
85+
7386
// we need to pass the whole specgen since throttle devices are parsed later due to cross compat.
7487
s.ResourceLimits, err = specgenutil.GetResources(s, &updateOpts)
7588
if err != nil {

docs/source/markdown/options/restart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
####> This option file is used in:
2-
####> podman create, pod clone, pod create, run
2+
####> podman create, pod clone, pod create, run, update
33
####> If file is edited, make sure the changes
44
####> are applicable to all of those.
55
#### **--restart**=*policy*

docs/source/markdown/podman-events.1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ The *container* event type reports the follow statuses:
4747
* sync
4848
* unmount
4949
* unpause
50+
* update
5051

5152
The *pod* event type reports the follow statuses:
5253
* create

docs/source/markdown/podman-update.1.md.in

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
% podman-update 1
22

33
## NAME
4-
podman\-update - Update the cgroup configuration of a given container
4+
podman\-update - Update the configuration of a given container
55

66
## SYNOPSIS
77
**podman update** [*options*] *container*
@@ -10,10 +10,8 @@ podman\-update - Update the cgroup configuration of a given container
1010

1111
## DESCRIPTION
1212

13-
Updates the cgroup configuration of an already existing container. The currently supported options are a subset of the
14-
podman create/run resource limits options. These new options are non-persistent and only last for the current execution of the container; the configuration is honored on its next run.
15-
This means that this command can only be executed on an already running container and the changes made is erased the next time the container is stopped and restarted, this is to ensure immutability.
16-
This command takes one argument, a container name or ID, alongside the resource flags to modify the cgroup.
13+
Updates the configuration of an already existing container, allowing different resource limits to be set.
14+
The currently supported options are a subset of the podman create/run resource limit options.
1715

1816
## OPTIONS
1917

@@ -55,6 +53,8 @@ This command takes one argument, a container name or ID, alongside the resource
5553

5654
@@option pids-limit
5755

56+
@@option restart
57+
5858

5959
## EXAMPLEs
6060

docs/source/markdown/podman.1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ the exit codes follow the `chroot` standard, see below:
384384
| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
385385
| [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. |
386386
| [podman-untag(1)](podman-untag.1.md) | Remove one or more names from a locally-stored image. |
387-
| [podman-update(1)](podman-update.1.md) | Update the cgroup configuration of a given container. |
387+
| [podman-update(1)](podman-update.1.md) | Update the configuration of a given container. |
388388
| [podman-version(1)](podman-version.1.md) | Display the Podman version information. |
389389
| [podman-volume(1)](podman-volume.1.md) | Simple management tool for volumes. |
390390
| [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |

libpod/container_api.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,24 @@ func (c *Container) Start(ctx context.Context, recursive bool) (finalErr error)
119119
}
120120

121121
// Update updates the given container.
122-
// only the cgroup config can be updated and therefore only a linux resource spec is passed.
123-
func (c *Container) Update(res *spec.LinuxResources) error {
124-
if err := c.syncContainer(); err != nil {
125-
return err
122+
// Either resource limits or restart policy can be updated.
123+
// Either resourcs or restartPolicy must not be nil.
124+
// If restartRetries is not nil, restartPolicy must be set and must be "on-failure".
125+
func (c *Container) Update(resources *spec.LinuxResources, restartPolicy *string, restartRetries *uint) error {
126+
if !c.batched {
127+
c.lock.Lock()
128+
defer c.lock.Unlock()
129+
130+
if err := c.syncContainer(); err != nil {
131+
return err
132+
}
126133
}
127-
return c.update(res)
134+
135+
if c.ensureState(define.ContainerStateRemoving) {
136+
return fmt.Errorf("container %s is being removed, cannot update: %w", c.ID(), define.ErrCtrStateInvalid)
137+
}
138+
139+
return c.update(resources, restartPolicy, restartRetries)
128140
}
129141

130142
// StartAndAttach starts a container and attaches to it.

libpod/container_inspect.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,9 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
467467

468468
restartPolicy := new(define.InspectRestartPolicy)
469469
restartPolicy.Name = c.config.RestartPolicy
470+
if restartPolicy.Name == "" {
471+
restartPolicy.Name = define.RestartPolicyNo
472+
}
470473
restartPolicy.MaximumRetryCount = c.config.RestartRetries
471474
hostConfig.RestartPolicy = restartPolicy
472475
if c.config.NoCgroups {

libpod/container_internal.go

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2514,11 +2514,77 @@ func (c *Container) extractSecretToCtrStorage(secr *ContainerSecret) error {
25142514
return nil
25152515
}
25162516

2517-
// update calls the ociRuntime update function to modify a cgroup config after container creation
2518-
func (c *Container) update(resources *spec.LinuxResources) error {
2519-
if err := c.ociRuntime.UpdateContainer(c, resources); err != nil {
2517+
// Update a container's resources or restart policy after creation.
2518+
// At least one of resources or restartPolicy must not be nil.
2519+
func (c *Container) update(resources *spec.LinuxResources, restartPolicy *string, restartRetries *uint) error {
2520+
if resources == nil && restartPolicy == nil {
2521+
return fmt.Errorf("must provide at least one of resources and restartPolicy to update a container: %w", define.ErrInvalidArg)
2522+
}
2523+
if restartRetries != nil && restartPolicy == nil {
2524+
return fmt.Errorf("must provide restart policy if updating restart retries: %w", define.ErrInvalidArg)
2525+
}
2526+
2527+
oldResources := c.config.Spec.Linux.Resources
2528+
oldRestart := c.config.RestartPolicy
2529+
oldRetries := c.config.RestartRetries
2530+
2531+
if restartPolicy != nil {
2532+
if err := define.ValidateRestartPolicy(*restartPolicy); err != nil {
2533+
return err
2534+
}
2535+
2536+
if restartRetries != nil {
2537+
if *restartPolicy != define.RestartPolicyOnFailure {
2538+
return fmt.Errorf("cannot set restart policy retries unless policy is on-failure: %w", define.ErrInvalidArg)
2539+
}
2540+
}
2541+
2542+
c.config.RestartPolicy = *restartPolicy
2543+
if restartRetries != nil {
2544+
c.config.RestartRetries = *restartRetries
2545+
} else {
2546+
c.config.RestartRetries = 0
2547+
}
2548+
}
2549+
2550+
if resources != nil {
2551+
if c.config.Spec.Linux == nil {
2552+
c.config.Spec.Linux = new(spec.Linux)
2553+
}
2554+
c.config.Spec.Linux.Resources = resources
2555+
}
2556+
2557+
if err := c.runtime.state.SafeRewriteContainerConfig(c, "", "", c.config); err != nil {
2558+
// Assume DB write failed, revert to old resources block
2559+
c.config.Spec.Linux.Resources = oldResources
2560+
c.config.RestartPolicy = oldRestart
2561+
c.config.RestartRetries = oldRetries
25202562
return err
25212563
}
2564+
2565+
if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused) && resources != nil {
2566+
// So `podman inspect` on running containers sources its OCI spec from disk.
2567+
// To keep inspect accurate we need to update the on-disk OCI spec.
2568+
onDiskSpec, err := c.specFromState()
2569+
if err != nil {
2570+
return fmt.Errorf("retrieving on-disk OCI spec to update: %w", err)
2571+
}
2572+
if onDiskSpec.Linux == nil {
2573+
onDiskSpec.Linux = new(spec.Linux)
2574+
}
2575+
onDiskSpec.Linux.Resources = resources
2576+
if err := c.saveSpec(onDiskSpec); err != nil {
2577+
logrus.Errorf("Unable to update container %s OCI spec - `podman inspect` may not be accurate until container is restarted: %v", c.ID(), err)
2578+
}
2579+
2580+
if err := c.ociRuntime.UpdateContainer(c, resources); err != nil {
2581+
return err
2582+
}
2583+
}
2584+
25222585
logrus.Debugf("updated container %s", c.ID())
2586+
2587+
c.newContainerEvent(events.Update)
2588+
25232589
return nil
25242590
}

libpod/define/container.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package define
22

3+
import (
4+
"fmt"
5+
)
6+
37
// Valid restart policy types.
48
const (
59
// RestartPolicyNone indicates that no restart policy has been requested
@@ -27,6 +31,16 @@ var RestartPolicyMap = map[string]string{
2731
RestartPolicyUnlessStopped: RestartPolicyUnlessStopped,
2832
}
2933

34+
// Validate that the given string is a valid restart policy.
35+
func ValidateRestartPolicy(policy string) error {
36+
switch policy {
37+
case RestartPolicyNone, RestartPolicyNo, RestartPolicyOnFailure, RestartPolicyAlways, RestartPolicyUnlessStopped:
38+
return nil
39+
default:
40+
return fmt.Errorf("%q is not a valid restart policy: %w", policy, ErrInvalidArg)
41+
}
42+
}
43+
3044
// InitContainerTypes
3145
const (
3246
// AlwaysInitContainer is an init container that runs on each

libpod/events/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ const (
208208
Unpause Status = "unpause"
209209
// Untag ...
210210
Untag Status = "untag"
211+
// Update indicates that a container's configuration has been modified.
212+
Update Status = "update"
211213
)
212214

213215
// EventFilter for filtering events

libpod/events/events.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ func StringToStatus(name string) (Status, error) {
231231
return Unpause, nil
232232
case Untag.String():
233233
return Untag, nil
234+
case Update.String():
235+
return Update, nil
234236
}
235237
return "", fmt.Errorf("unknown event status %q", name)
236238
}

libpod/options.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,13 +1392,12 @@ func WithRestartPolicy(policy string) CtrCreateOption {
13921392
return define.ErrCtrFinalized
13931393
}
13941394

1395-
switch policy {
1396-
case define.RestartPolicyNone, define.RestartPolicyNo, define.RestartPolicyOnFailure, define.RestartPolicyAlways, define.RestartPolicyUnlessStopped:
1397-
ctr.config.RestartPolicy = policy
1398-
default:
1399-
return fmt.Errorf("%q is not a valid restart policy: %w", policy, define.ErrInvalidArg)
1395+
if err := define.ValidateRestartPolicy(policy); err != nil {
1396+
return err
14001397
}
14011398

1399+
ctr.config.RestartPolicy = policy
1400+
14021401
return nil
14031402
}
14041403
}

0 commit comments

Comments
 (0)