cmd/podman: add --latest option to update #26380

Allow users to target the most recently created container with
`podman update --latest` (short `-l`). The same option already exists
on many other commands, so this brings update in line with the rest of
the CLI and saves users from typing or looking up the newest container.

Fixes: #26380

Signed-off-by: Hayato Kihara <kai.21banana@gmail.com>
This commit is contained in:
Hayato Kihara 2025-06-24 22:06:26 +09:00
parent 7808625785
commit f21dacc4fb
6 changed files with 68 additions and 13 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/containers/podman/v5/cmd/podman/common" "github.com/containers/podman/v5/cmd/podman/common"
"github.com/containers/podman/v5/cmd/podman/registry" "github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/validate"
"github.com/containers/podman/v5/libpod/define" "github.com/containers/podman/v5/libpod/define"
"github.com/containers/podman/v5/pkg/domain/entities" "github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/specgen" "github.com/containers/podman/v5/pkg/specgen"
@ -24,7 +25,7 @@ var (
Short: "Update an existing container", Short: "Update an existing container",
Long: updateDescription, Long: updateDescription,
RunE: update, RunE: update,
Args: cobra.ExactArgs(1), Args: validate.IDOrLatestArgs,
ValidArgsFunction: common.AutocompleteContainers, ValidArgsFunction: common.AutocompleteContainers,
Example: `podman update --cpus=5 foobar_container`, Example: `podman update --cpus=5 foobar_container`,
} }
@ -39,13 +40,17 @@ var (
Example: `podman container update --cpus=5 foobar_container`, Example: `podman container update --cpus=5 foobar_container`,
} }
) )
var (
updateOpts entities.ContainerCreateOptions type ContainerUpdateOptions struct {
) entities.ContainerCreateOptions
Latest bool
}
var updateOptions ContainerUpdateOptions
func updateFlags(cmd *cobra.Command) { func updateFlags(cmd *cobra.Command) {
common.DefineCreateDefaults(&updateOpts) common.DefineCreateDefaults(&updateOptions.ContainerCreateOptions)
common.DefineCreateFlags(cmd, &updateOpts, entities.UpdateMode) common.DefineCreateFlags(cmd, &updateOptions.ContainerCreateOptions, entities.UpdateMode)
} }
func init() { func init() {
@ -53,12 +58,14 @@ func init() {
Command: updateCommand, Command: updateCommand,
}) })
updateFlags(updateCommand) updateFlags(updateCommand)
validate.AddLatestFlag(updateCommand, &updateOptions.Latest)
registry.Commands = append(registry.Commands, registry.CliCommand{ registry.Commands = append(registry.Commands, registry.CliCommand{
Command: containerUpdateCommand, Command: containerUpdateCommand,
Parent: containerCmd, Parent: containerCmd,
}) })
updateFlags(containerUpdateCommand) updateFlags(containerUpdateCommand)
validate.AddLatestFlag(containerUpdateCommand, &updateOptions.Latest)
} }
func GetChangedHealthCheckConfiguration(cmd *cobra.Command, vals *entities.ContainerCreateOptions) define.UpdateHealthCheckConfig { func GetChangedHealthCheckConfiguration(cmd *cobra.Command, vals *entities.ContainerCreateOptions) define.UpdateHealthCheckConfig {
@ -129,12 +136,12 @@ func update(cmd *cobra.Command, args []string) error {
s := &specgen.SpecGenerator{} s := &specgen.SpecGenerator{}
s.ResourceLimits = &specs.LinuxResources{} s.ResourceLimits = &specs.LinuxResources{}
err = createOrUpdateFlags(cmd, &updateOpts) err = createOrUpdateFlags(cmd, &updateOptions.ContainerCreateOptions)
if err != nil { if err != nil {
return err return err
} }
s.ResourceLimits, err = specgenutil.GetResources(s, &updateOpts) s.ResourceLimits, err = specgenutil.GetResources(s, &updateOptions.ContainerCreateOptions)
if err != nil { if err != nil {
return err return err
} }
@ -143,17 +150,21 @@ func update(cmd *cobra.Command, args []string) error {
s.ResourceLimits = &specs.LinuxResources{} s.ResourceLimits = &specs.LinuxResources{}
} }
healthCheckConfig := GetChangedHealthCheckConfiguration(cmd, &updateOpts) healthCheckConfig := GetChangedHealthCheckConfiguration(cmd, &updateOptions.ContainerCreateOptions)
opts := &entities.ContainerUpdateOptions{ opts := &entities.ContainerUpdateOptions{
NameOrID: strings.TrimPrefix(args[0], "/"),
Resources: s.ResourceLimits, Resources: s.ResourceLimits,
ChangedHealthCheckConfiguration: &healthCheckConfig, ChangedHealthCheckConfiguration: &healthCheckConfig,
DevicesLimits: GetChangedDeviceLimits(s), DevicesLimits: GetChangedDeviceLimits(s),
Latest: updateOptions.Latest,
}
if !updateOptions.Latest {
opts.NameOrID = strings.TrimPrefix(args[0], "/")
} }
if cmd.Flags().Changed("restart") { if cmd.Flags().Changed("restart") {
policy, retries, err := util.ParseRestartPolicy(updateOpts.Restart) policy, retries, err := util.ParseRestartPolicy(updateOptions.Restart)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,5 +1,5 @@
####> This option file is used in: ####> This option file is used in:
####> podman attach, container diff, container inspect, diff, exec, init, inspect, kill, logs, mount, network reload, pause, pod inspect, pod kill, pod logs, pod rm, pod start, pod stats, pod stop, pod top, port, restart, rm, start, stats, stop, top, unmount, unpause, wait ####> podman attach, container diff, container inspect, diff, exec, init, inspect, kill, logs, mount, network reload, pause, pod inspect, pod kill, pod logs, pod rm, pod start, pod stats, pod stop, pod top, port, restart, rm, start, stats, stop, top, unmount, unpause, update, wait
####> 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.
#### **--latest**, **-l** #### **--latest**, **-l**

View File

@ -78,6 +78,8 @@ Changing this setting resets the timer, depending on the state of the container.
@@option health-timeout @@option health-timeout
@@option latest
@@option memory @@option memory
@@option memory-reservation @@option memory-reservation
@ -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 the latest container with a new cpu value:
```
podman update --latest --cpus=1
```
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 \\
@ -116,4 +123,5 @@ podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 \\
**[podman(1)](podman.1.md)**, **[podman-create(1)](podman-create.1.md)**, **[podman-run(1)](podman-run.1.md)** **[podman(1)](podman.1.md)**, **[podman-create(1)](podman-create.1.md)**, **[podman-run(1)](podman-run.1.md)**
## HISTORY ## HISTORY
June 2025, Latest option added by Hayato Kihara <kai.21banana@gmail.com>
August 2022, Originally written by Charlie Doern <cdoern@redhat.com> August 2022, Originally written by Charlie Doern <cdoern@redhat.com>

View File

@ -61,6 +61,7 @@ type ContainerUpdateOptions struct {
RestartRetries *uint RestartRetries *uint
Env []string Env []string
UnsetEnv []string UnsetEnv []string
Latest bool
} }
func (u *ContainerUpdateOptions) ProcessSpecgen() { func (u *ContainerUpdateOptions) ProcessSpecgen() {

View File

@ -1804,7 +1804,7 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti
// ContainerUpdate finds and updates the given container's cgroup config with the specified options // 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) { func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *entities.ContainerUpdateOptions) (string, error) {
updateOptions.ProcessSpecgen() updateOptions.ProcessSpecgen()
containers, err := getContainers(ic.Libpod, getContainersOptions{names: []string{updateOptions.NameOrID}}) containers, err := getContainers(ic.Libpod, getContainersOptions{latest: updateOptions.Latest, names: []string{updateOptions.NameOrID}})
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -306,4 +306,39 @@ 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 the latest container", func() {
SkipIfRemote("--latest is local-only")
restartPolicyName := ".HostConfig.RestartPolicy.Name"
restartPolicyRetries := ".HostConfig.RestartPolicy.MaximumRetryCount"
// Arrange an old container
oldContainerName := "old-container"
oldContainer := podmanTest.Podman([]string{"run", "-d", "--name", oldContainerName, ALPINE, "top"})
oldContainer.WaitWithDefaultTimeout()
Expect(oldContainer).Should(ExitCleanly())
podmanTest.CheckContainerSingleField(oldContainerName, restartPolicyName, "no")
podmanTest.CheckContainerSingleField(oldContainerName, restartPolicyRetries, "0")
// Arrange a new container
newContainerName := "new-container"
newContainer := podmanTest.Podman([]string{"run", "-d", "--name", newContainerName, ALPINE, "top"})
newContainer.WaitWithDefaultTimeout()
Expect(newContainer).Should(ExitCleanly())
podmanTest.CheckContainerSingleField(newContainerName, restartPolicyName, "no")
podmanTest.CheckContainerSingleField(newContainerName, restartPolicyRetries, "0")
// Test the latest is updated
updatedContainer := podmanTest.Podman([]string{"update", "--restart", "on-failure:5", "--latest"})
updatedContainer.WaitWithDefaultTimeout()
Expect(updatedContainer).Should(ExitCleanly())
podmanTest.CheckContainerSingleField(oldContainerName, restartPolicyName, "no")
podmanTest.CheckContainerSingleField(oldContainerName, restartPolicyRetries, "0")
podmanTest.CheckContainerSingleField(newContainerName, restartPolicyName, "on-failure")
podmanTest.CheckContainerSingleField(newContainerName, restartPolicyRetries, "5")
})
}) })