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

View File

@ -1,5 +1,5 @@
####> 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
####> are applicable to all of those.
#### **--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 latest
@@option memory
@@option memory-reservation
@ -102,6 +104,11 @@ Update a container with a new cpu quota and period:
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:
```
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)**
## HISTORY
June 2025, Latest option added by Hayato Kihara <kai.21banana@gmail.com>
August 2022, Originally written by Charlie Doern <cdoern@redhat.com>

View File

@ -61,6 +61,7 @@ type ContainerUpdateOptions struct {
RestartRetries *uint
Env []string
UnsetEnv []string
Latest bool
}
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
func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *entities.ContainerUpdateOptions) (string, error) {
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 {
return "", err
}

View File

@ -306,4 +306,39 @@ var _ = Describe("Podman update", func() {
Expect(env).ToNot(ContainSubstring("FOO"))
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")
})
})