mirror of
https://github.com/containers/podman.git
synced 2025-12-23 22:28:30 -05:00
Fix unless-stopped restart policy to match Docker behavior
- Update documentation: Differentiate `unless-stopped` from `always` - containers stopped by the user before a reboot will not restart. - Add `should-start-on-boot` filter: Identify containers that require a restart after a system reboot. - Update command documentation: Add `restart-policy` and `label!` filters to the documentation for container commands (rm, ps, start, stop, pause, unpause, restart). - Add `restart-policy` and `shoud-start-on-boot` to completions. - Update service: Update `podman-restart.service` to use the `needs-restart=true` filter. - Preserve state: Preserve the `StoppedByUser` state across reboots. - Update API: Add a `ShouldStartOnBoot()` method to the Container API. - Update documentation: Add descriptions for the `should-start-on-boot` filter. Fixes: https://issues.redhat.com/browse/RHEL-129405 Fixes: https://github.com/containers/podman/issues/20418 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
This commit is contained in:
@@ -1808,7 +1808,18 @@ func AutocompletePsFilters(cmd *cobra.Command, _ []string, toComplete string) ([
|
||||
"name=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeNames) },
|
||||
"network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeDefault) },
|
||||
"pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) },
|
||||
"since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) },
|
||||
"restart-policy=": func(_ string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{
|
||||
define.RestartPolicyAlways,
|
||||
define.RestartPolicyNo,
|
||||
define.RestartPolicyOnFailure,
|
||||
define.RestartPolicyUnlessStopped,
|
||||
}, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
"should-start-on-boot=": func(_ string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
"since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) },
|
||||
"status=": func(_ string) ([]string, cobra.ShellCompDirective) {
|
||||
return containerStatuses, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ After=network-online.target
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
Environment=LOGGING="--log-level=info"
|
||||
ExecStart=@@PODMAN@@ $LOGGING start --all --filter restart-policy=always
|
||||
ExecStop=@@PODMAN@@ $LOGGING stop --all --filter restart-policy=always
|
||||
ExecStart=@@PODMAN@@ $LOGGING start --all --filter should-start-on-boot=true
|
||||
ExecStop=@@PODMAN@@ $LOGGING stop --all --filter should-start-on-boot=true
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
@@ -13,7 +13,7 @@ Valid _policy_ values are:
|
||||
- `never` : Synonym for **no**; do not restart containers on exit
|
||||
- `on-failure[:max_retries]` : Restart containers when they exit with a non-zero exit code, retrying indefinitely or until the optional *max_retries* count is hit
|
||||
- `always` : Restart containers when they exit, regardless of status, retrying indefinitely
|
||||
- `unless-stopped` : Identical to **always**
|
||||
- `unless-stopped` : Restart containers when they exit, unless the container was explicitly stopped by the user. After a system reboot, containers with this policy will be restarted by podman-restart.service only if they were not explicitly stopped by the user before the reboot. This differs from **always**, which restarts containers after a system reboot regardless of whether they were user-stopped
|
||||
|
||||
Podman provides a systemd unit file, podman-restart.service, which restarts containers after a system reboot.
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ Valid filters are listed below:
|
||||
| id | [ID] Container's ID (CID prefix match by default; accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| label! | [Key] or [Key=Value] Label NOT assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
| status | [Status] Container's status: 'created', 'initialized', 'exited', 'paused', 'running', 'unknown' |
|
||||
| ancestor | [ImageName] Image or descendant used to create container |
|
||||
@@ -42,8 +43,10 @@ Valid filters are listed below:
|
||||
| health | [Status] healthy or unhealthy |
|
||||
| pod | [Pod] name or full or partial ID of pod |
|
||||
| network | [Network] name or full ID of network |
|
||||
| restart-policy | [Policy] Container's restart policy (e.g., 'no', 'on-failure', 'always', 'unless-stopped') |
|
||||
| until | [DateTime] Containers created before the given duration or time. |
|
||||
| command | [Command] the command the container is executing, only argv[0] is taken |
|
||||
| should-start-on-boot | [Bool] Containers that need to be restarted after system reboot. True for containers with restart policy 'always', or 'unless-stopped' that were not explicitly stopped by the user |
|
||||
|
||||
@@option latest
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ Valid filters are listed below:
|
||||
| network | [Network] name or full ID of network |
|
||||
| until | [DateTime] container created before the given duration or time. |
|
||||
| command | [Command] the command the container is executing, only argv[0] is taken |
|
||||
| restart-policy | [Policy] Container's restart policy (e.g., 'no', 'on-failure', 'always', 'unless-stopped') |
|
||||
| should-start-on-boot | [Bool] Containers that need to be restarted after system reboot. True for containers with restart policy 'always', or 'unless-stopped' that were not explicitly stopped by the user |
|
||||
|
||||
#### **--format**=*format*
|
||||
|
||||
@@ -288,6 +290,14 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS
|
||||
5e3694604817 quay.io/centos/centos:latest sleep 300 3 minutes ago Up 3 minutes centos-test
|
||||
```
|
||||
|
||||
Filter containers that need to be restarted after system reboot.
|
||||
```
|
||||
$ podman ps -a --filter should-start-on-boot=true
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ff660efda598 docker.io/library/nginx:latest nginx -g daemon o... 3 minutes ago Up 3 minutes 0.0.0.0:8080->80/tcp webserver
|
||||
5693e934f4c6 docker.io/library/redis:latest redis-server 3 minutes ago Exited (0) 3 minutes ago 6379/tcp cache
|
||||
```
|
||||
|
||||
Use custom format to show container and pod information.
|
||||
```
|
||||
$ podman ps --format "{{.Names}} is in pod {{.PodName}} ({{.Pod}})"
|
||||
|
||||
@@ -36,6 +36,7 @@ Valid filters are listed below:
|
||||
| id | [ID] Container's ID (CID prefix match by default; accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| label! | [Key] or [Key=Value] Label NOT assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
| status | [Status] Container's status: 'created', 'initialized', 'exited', 'paused', 'running', 'unknown' |
|
||||
| ancestor | [ImageName] Image or descendant used to create container |
|
||||
@@ -45,8 +46,10 @@ Valid filters are listed below:
|
||||
| health | [Status] healthy or unhealthy |
|
||||
| pod | [Pod] name or full or partial ID of pod |
|
||||
| network | [Network] name or full ID of network |
|
||||
| restart-policy | [Policy] Container's restart policy (e.g., 'no', 'on-failure', 'always', 'unless-stopped') |
|
||||
| until | [DateTime] Containers created before the given duration or time. |
|
||||
| command | [Command] the command the container is executing, only argv[0] is taken |
|
||||
| should-start-on-boot | [Bool] Containers that need to be restarted after system reboot. True for containers with restart policy 'always', or 'unless-stopped' that were not explicitly stopped by the user |
|
||||
|
||||
@@option latest
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ Valid filters are listed below:
|
||||
| id | [ID] Container's ID (CID prefix match by default; accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| label! | [Key] or [Key=Value] Label NOT assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
| status | [Status] Container's status: 'created', 'initialized', 'exited', 'paused', 'running', 'unknown' |
|
||||
| ancestor | [ImageName] Image or descendant used to create container |
|
||||
@@ -49,8 +50,10 @@ Valid filters are listed below:
|
||||
| health | [Status] healthy or unhealthy |
|
||||
| pod | [Pod] name or full or partial ID of pod |
|
||||
| network | [Network] name or full ID of network |
|
||||
| restart-policy | [Policy] Container's restart policy (e.g., 'no', 'on-failure', 'always', 'unless-stopped') |
|
||||
| until | [DateTime] Containers created before the given duration or time. |
|
||||
| command | [Command] the command the container is executing, only argv[0] is taken |
|
||||
| should-start-on-boot | [Bool] Containers that need to be restarted after system reboot. True for containers with restart policy 'always', or 'unless-stopped' that were not explicitly stopped by the user |
|
||||
|
||||
#### **--force**, **-f**
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ starting multiple containers.
|
||||
|
||||
@@option detach-keys
|
||||
|
||||
#### **--filter**, **-f**
|
||||
#### **--filter**, **-f**=*filter*
|
||||
|
||||
Filter what containers are going to be started from the given arguments.
|
||||
Multiple filters can be given with multiple uses of the --filter flag.
|
||||
@@ -41,6 +41,7 @@ Valid filters are listed below:
|
||||
| id | [ID] Container's ID (CID prefix match by default; accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| label! | [Key] or [Key=Value] Label NOT assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
| status | [Status] Container's status: 'created', 'initialized', 'exited', 'paused', 'running', 'unknown' |
|
||||
| ancestor | [ImageName] Image or descendant used to create container |
|
||||
@@ -50,8 +51,10 @@ Valid filters are listed below:
|
||||
| health | [Status] healthy or unhealthy |
|
||||
| pod | [Pod] name or full or partial ID of pod |
|
||||
| network | [Network] name or full ID of network |
|
||||
| restart-policy | [Policy] Container's restart policy (e.g., 'no', 'on-failure', 'always', 'unless-stopped') |
|
||||
| until | [DateTime] Containers created before the given duration or time. |
|
||||
| command | [Command] the command the container is executing, only argv[0] is taken |
|
||||
| should-start-on-boot | [Bool] Containers that need to be restarted after system reboot. True for containers with restart policy 'always', or 'unless-stopped' that were not explicitly stopped by the user |
|
||||
|
||||
@@option interactive
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ Valid filters are listed below:
|
||||
| id | [ID] Container's ID (CID prefix match by default; accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| label! | [Key] or [Key=Value] Label NOT assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
| status | [Status] Container's status: 'created', 'initialized', 'exited', 'paused', 'running', 'unknown' |
|
||||
| ancestor | [ImageName] Image or descendant used to create container |
|
||||
@@ -48,8 +49,10 @@ Valid filters are listed below:
|
||||
| health | [Status] healthy or unhealthy |
|
||||
| pod | [Pod] name or full or partial ID of pod |
|
||||
| network | [Network] name or full ID of network |
|
||||
| restart-policy | [Policy] Container's restart policy (e.g., 'no', 'on-failure', 'always', 'unless-stopped') |
|
||||
| until | [DateTime] Containers created before the given duration or time. |
|
||||
| command | [Command] the command the container is executing, only argv[0] is taken |
|
||||
| should-start-on-boot | [Bool] Containers that need to be restarted after system reboot. True for containers with restart policy 'always', or 'unless-stopped' that were not explicitly stopped by the user |
|
||||
|
||||
@@option ignore
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ Valid filters are listed below:
|
||||
| id | [ID] Container's ID (CID prefix match by default; accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| label! | [Key] or [Key=Value] Label NOT assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
| status | [Status] Container's status: 'created', 'initialized', 'exited', 'paused', 'running', 'unknown' |
|
||||
| ancestor | [ImageName] Image or descendant used to create container |
|
||||
@@ -42,8 +43,10 @@ Valid filters are listed below:
|
||||
| health | [Status] healthy or unhealthy |
|
||||
| pod | [Pod] name or full or partial ID of pod |
|
||||
| network | [Network] name or full ID of network |
|
||||
| restart-policy | [Policy] Container's restart policy (e.g., 'no', 'on-failure', 'always', 'unless-stopped') |
|
||||
| until | [DateTime] Containers created before the given duration or time. |
|
||||
| command | [Command] the command the container is executing, only argv[0] is taken |
|
||||
| should-start-on-boot | [Bool] Containers that need to be restarted after system reboot. True for containers with restart policy 'always', or 'unless-stopped' that were not explicitly stopped by the user |
|
||||
|
||||
@@option latest
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ type ContainerState struct {
|
||||
BindMounts map[string]string `json:"bindMounts,omitempty"`
|
||||
// StoppedByUser indicates whether the container was stopped by an
|
||||
// explicit call to the Stop() API.
|
||||
// Warning: This field does persist across system reboots.
|
||||
StoppedByUser bool `json:"stoppedByUser,omitempty"`
|
||||
// RestartPolicyMatch indicates whether the conditions for restart
|
||||
// policy have been met.
|
||||
|
||||
@@ -1093,6 +1093,28 @@ func (c *Container) ShouldRestart(_ context.Context) bool {
|
||||
return c.shouldRestart()
|
||||
}
|
||||
|
||||
// Indicate whether or not the container will should start after a reboot of system
|
||||
func (c *Container) ShouldStartOnBoot() bool {
|
||||
if !c.batched {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if err := c.syncContainer(); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated) {
|
||||
return false
|
||||
}
|
||||
|
||||
configuredRestartPolicy := c.RestartPolicy()
|
||||
isAlways := configuredRestartPolicy == define.RestartPolicyAlways
|
||||
isUnlessStopped := configuredRestartPolicy == define.RestartPolicyUnlessStopped && !c.state.StoppedByUser
|
||||
|
||||
return isAlways || isUnlessStopped
|
||||
}
|
||||
|
||||
// CopyFromArchive copies the contents from the specified tarStream to path
|
||||
// *inside* the container.
|
||||
func (c *Container) CopyFromArchive(_ context.Context, containerPath string, chown, noOverwriteDirNonDir bool, rename map[string]string, tarStream io.Reader) (func() error, error) {
|
||||
|
||||
@@ -635,7 +635,6 @@ func resetContainerState(state *ContainerState) {
|
||||
state.ExecSessions = make(map[string]*ExecSession)
|
||||
state.LegacyExecSessions = nil
|
||||
state.BindMounts = make(map[string]string)
|
||||
state.StoppedByUser = false
|
||||
state.RestartPolicyMatch = false
|
||||
state.RestartCount = 0
|
||||
state.Checkpointed = false
|
||||
|
||||
@@ -287,6 +287,18 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
|
||||
return func(c *libpod.Container) bool {
|
||||
return util.StringMatchRegexSlice(c.Command()[0], filterValues)
|
||||
}, nil
|
||||
case "should-start-on-boot":
|
||||
wantRestart := false
|
||||
var err error
|
||||
for _, fv := range filterValues {
|
||||
wantRestart, err = strconv.ParseBool(fv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return func(c *libpod.Container) bool {
|
||||
return c.ShouldStartOnBoot() == wantRestart
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s is an invalid filter", filter)
|
||||
}
|
||||
|
||||
@@ -1051,4 +1051,70 @@ var _ = Describe("Podman ps", func() {
|
||||
Expect(output).To(HaveLen(1))
|
||||
Expect(output).Should(ContainElement(ContainSubstring("late")))
|
||||
})
|
||||
|
||||
It("podman ps filter should-start-on-boot", func() {
|
||||
commands := [][]string{
|
||||
{"create", "--restart", "unless-stopped", "--name", "test-unless-stopped-user-stop", ALPINE, "top"},
|
||||
{"create", "--restart", "always", "--name", "test-always-user-stop", ALPINE, "top"},
|
||||
{"create", "--restart", "no", "--name", "test-no-restart-user-stop", ALPINE, "top"},
|
||||
{"create", "--restart", "on-failure", "--name", "test-onfailure-user-stop", ALPINE, "top"},
|
||||
{"create", "--restart", "unless-stopped", "--name", "test-unless-stopped-exit-not-started", ALPINE, "false"},
|
||||
{"create", "--restart", "always", "--name", "test-always-exit-not-started", ALPINE, "false"},
|
||||
{"create", "--restart", "on-failure", "--name", "test-onfailure-exit-not-started", ALPINE, "false"},
|
||||
{"start", "test-unless-stopped-user-stop"},
|
||||
{"stop", "test-unless-stopped-user-stop"},
|
||||
{"start", "test-always-user-stop"},
|
||||
{"stop", "test-always-user-stop"},
|
||||
{"start", "test-no-restart-user-stop"},
|
||||
{"stop", "test-no-restart-user-stop"},
|
||||
{"start", "test-onfailure-user-stop"},
|
||||
{"stop", "test-onfailure-user-stop"},
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
podmanTest.PodmanExitCleanly(cmd...)
|
||||
}
|
||||
|
||||
commandsExit := [][]string{
|
||||
{"run", "--name", "test-unless-stopped-exit-bad", "--restart", "unless-stopped", ALPINE, "false"},
|
||||
{"run", "--name", "test-always-exit-bad", "--restart", "always", ALPINE, "false"},
|
||||
{"run", "--name", "test-onfailure-exit-bad", "--restart", "on-failure", ALPINE, "false"},
|
||||
}
|
||||
|
||||
for _, cmd := range commandsExit {
|
||||
session := podmanTest.Podman(cmd)
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(1))
|
||||
}
|
||||
|
||||
session := podmanTest.PodmanExitCleanly("ps", "-a", "--filter", "should-start-on-boot=true", "--format", "{{.Names}}")
|
||||
output := session.OutputToString()
|
||||
|
||||
Expect(output).To(ContainSubstring("test-unless-stopped-exit-bad"))
|
||||
Expect(output).To(ContainSubstring("test-always-exit-bad"))
|
||||
Expect(output).To(ContainSubstring("test-always-user-stop"))
|
||||
|
||||
Expect(output).ToNot(ContainSubstring("test-unless-stopped-exit-not-started"))
|
||||
Expect(output).ToNot(ContainSubstring("test-always-exit-not-started"))
|
||||
Expect(output).ToNot(ContainSubstring("test-unless-stopped-user-stop"))
|
||||
Expect(output).ToNot(ContainSubstring("test-no-restart-user-stop"))
|
||||
Expect(output).ToNot(ContainSubstring("test-onfailure-user-stop"))
|
||||
Expect(output).ToNot(ContainSubstring("test-onfailure-exit-not-started"))
|
||||
Expect(output).ToNot(ContainSubstring("test-onfailure-exit-bad"))
|
||||
|
||||
session = podmanTest.PodmanExitCleanly("ps", "-a", "--filter", "should-start-on-boot=false", "--format", "{{.Names}}")
|
||||
output = session.OutputToString()
|
||||
|
||||
Expect(output).To(ContainSubstring("test-unless-stopped-user-stop"))
|
||||
Expect(output).To(ContainSubstring("test-no-restart-user-stop"))
|
||||
Expect(output).To(ContainSubstring("test-unless-stopped-exit-not-started"))
|
||||
Expect(output).To(ContainSubstring("test-always-exit-not-started"))
|
||||
Expect(output).To(ContainSubstring("test-onfailure-user-stop"))
|
||||
Expect(output).To(ContainSubstring("test-onfailure-exit-not-started"))
|
||||
Expect(output).To(ContainSubstring("test-onfailure-exit-bad"))
|
||||
|
||||
Expect(output).ToNot(ContainSubstring("test-always-user-stop"))
|
||||
Expect(output).ToNot(ContainSubstring("test-unless-stopped-exit-bad"))
|
||||
Expect(output).ToNot(ContainSubstring("test-always-exit-bad"))
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user