Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/podman/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
)
createFlags.String(
"restart", "",
"Restart is not supported. Please use a systemd unit file for restart",
"Restart policy to apply when a container exits",
)
createFlags.Bool(
"rm", false,
Expand Down
37 changes: 19 additions & 18 deletions cmd/podman/shared/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
span, _ := opentracing.StartSpanFromContext(ctx, "createContainer")
defer span.Finish()
}
if c.Bool("rm") && c.String("restart") != "" && c.String("restart") != "no" {
return nil, nil, errors.Errorf("the --rm option conflicts with --restart")
}

rtc, err := runtime.GetConfig()
if err != nil {
Expand Down Expand Up @@ -279,9 +282,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
blkioWeight uint16
namespaces map[string]string
)
if c.IsSet("restart") {
return nil, errors.Errorf("--restart option is not supported.\nUse systemd unit files for restarting containers")
}

idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
if err != nil {
Expand Down Expand Up @@ -676,21 +676,22 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
PidsLimit: c.Int64("pids-limit"),
Ulimit: c.StringSlice("ulimit"),
},
Rm: c.Bool("rm"),
StopSignal: stopSignal,
StopTimeout: c.Uint("stop-timeout"),
Sysctl: sysctl,
Systemd: systemd,
Tmpfs: c.StringSlice("tmpfs"),
Tty: tty,
User: user,
UsernsMode: usernsMode,
MountsFlag: c.StringArray("mount"),
Volumes: c.StringArray("volume"),
WorkDir: workDir,
Rootfs: rootfs,
VolumesFrom: c.StringSlice("volumes-from"),
Syslog: c.Bool("syslog"),
RestartPolicy: c.String("restart"),
Rm: c.Bool("rm"),
StopSignal: stopSignal,
StopTimeout: c.Uint("stop-timeout"),
Sysctl: sysctl,
Systemd: systemd,
Tmpfs: c.StringSlice("tmpfs"),
Tty: tty,
User: user,
UsernsMode: usernsMode,
MountsFlag: c.StringArray("mount"),
Volumes: c.StringArray("volume"),
WorkDir: workDir,
Rootfs: rootfs,
VolumesFrom: c.StringSlice("volumes-from"),
Syslog: c.Bool("syslog"),
}

if config.Privileged {
Expand Down
29 changes: 10 additions & 19 deletions docs/podman-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -567,11 +567,17 @@ If container is running in --read-only mode, then mount a read-write tmpfs on /r

**--restart=""**

Not implemented.
Restart policy to follow when containers exit.
Restart policy will not take effect if a container is stopped via the `podman kill` or `podman stop` commands.
Valid values are:

Restart should be handled via a systemd unit files. Please add your podman
commands to a unit file and allow systemd or your init system to handle the
restarting of the container processes. See example below.
- `no` : Do not restart containers on exit
- `on-failure[:max_retries]` : Restart containers when they exit with a non-0 exit code, retrying indefinitely or until the optional max_retries count is hit
- `always` : Restart containers when they exit, regardless of status, retrying indefinitely

Please note that restart will not restart containers after a system reboot.
If this functionality is required in your environment, you can invoke Podman from a systemd unit file, or create an init script for whichever init system is in use.
To generate systemd unit files, please see *podman generate systemd*

**--rm**=*true*|*false*

Expand Down Expand Up @@ -859,21 +865,6 @@ the uids and gids from the host.
$ podman create --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello
```

### Running a podman container to restart inside of a systemd unit file


```
[Unit]
Description=My App
[Service]
Restart=always
ExecStart=/usr/bin/podman start -a my_app
ExecStop=/usr/bin/podman stop -t 10 my_app
KillMode=process
[Install]
WantedBy=multi-user.target
```

### Rootless Containers

Podman runs as a non root user on most systems. This feature requires that a new enough version of shadow-utils
Expand Down
1 change: 1 addition & 0 deletions docs/podman-events.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The *container* event type will report the follow statuses:
* pause
* prune
* remove
* restart
* restore
* start
* stop
Expand Down
14 changes: 10 additions & 4 deletions docs/podman-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -589,11 +589,17 @@ If container is running in --read-only mode, then mount a read-write tmpfs on /r

**--restart=""**

Not implemented.
Restart policy to follow when containers exit.
Restart policy will not take effect if a container is stopped via the `podman kill` or `podman stop` commands.
Valid values are:

Restart should be handled via a systemd unit files. Please add your podman
commands to a unit file and allow systemd or your init system to handle the
restarting of the container processes. See *podman generate systemd*.
- `no` : Do not restart containers on exit
- `on-failure[:max_retries]` : Restart containers when they exit with a non-0 exit code, retrying indefinitely or until the optional max_retries count is hit
- `always` : Restart containers when they exit, regardless of status, retrying indefinitely

Please note that restart will not restart containers after a system reboot.
If this functionality is required in your environment, you can invoke Podman from a systemd unit file, or create an init script for whichever init system is in use.
To generate systemd unit files, please see *podman generate systemd*

**--rm**=*true*|*false*

Expand Down
61 changes: 61 additions & 0 deletions libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ func (ns LinuxNS) String() string {
}
}

// Valid restart policy types.
const (
// RestartPolicyNone indicates that no restart policy has been requested
// by a container.
RestartPolicyNone = ""
// RestartPolicyNo is identical in function to RestartPolicyNone.
RestartPolicyNo = "no"
// RestartPolicyAlways unconditionally restarts the container.
RestartPolicyAlways = "always"
// RestartPolicyOnFailure restarts the container on non-0 exit code,
// with an optional maximum number of retries.
RestartPolicyOnFailure = "on-failure"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docker also supports unless-stopped

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    -   **RestartPolicy** – The behavior to apply when the container exits.  The
            value is an object with a `Name` property of either `"always"` to
            always restart, `"unless-stopped"` to restart always except when
            user has manually stopped the container or `"on-failure"` to restart only when the container
            exit code is non-zero.  If `on-failure` is used, `MaximumRetryCount`
            controls the number of times to retry before giving up.
            The default is not to restart. (optional)
            An ever increasing delay (double the previous delay, starting at 100mS)
            is added before each restart to prevent flooding the server.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless-stopped is very much a "restarted the daemon" thing, so I just throw an error about it in pkg/spec if they try to pass it.

We still want to point people to systemd for cases like that, where the container will be restarted after system reboot, for example. The manpages make this clear, but it might be good to add the warning to Podman itself too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reading of unless-stopped, seems to be exactly what you are designing. IE If a user stops a container then it will not restart, otherwise if the container fails, it will be restarted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the interpretation depends on where we look. The man page is referring to the daemon start only while the docs state:

Similar to always, except that when the container is stopped (manually or otherwise), it is not restarted even after Docker daemon restarts

This makes me believe that we can support it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From that description, it's identical to always - restart policy never triggers after the container is stopped via an API call.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Minus the daemon restart aspect)

)

// Container is a single OCI container.
// All operations on a Container that access state must begin with a call to
// syncContainer().
Expand Down Expand Up @@ -179,6 +193,16 @@ type ContainerState struct {
// This maps the path the file will be mounted to in the container to
// the path of the file on disk outside the container
BindMounts map[string]string `json:"bindMounts,omitempty"`
// StoppedByUser indicates whether the container was stopped by an
// explicit call to the Stop() API.
StoppedByUser bool `json:"stoppedByUser,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what we could use for unless-stopped.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per Docker, this is the default for all restart policies - we never initiate a container restart if explicitly stopped by API call

// RestartPolicyMatch indicates whether the conditions for restart
// policy have been met.
RestartPolicyMatch bool `json:"restartPolicyMatch,omitempty"`
// RestartCount is how many times the container was restarted by its
// restart policy. This is NOT incremented by normal container restarts
// (only by restart policy).
RestartCount uint `json:"restartCount,omitempty"`

// ExtensionStageHooks holds hooks which will be executed by libpod
// and not delegated to the OCI runtime.
Expand Down Expand Up @@ -346,6 +370,17 @@ type ContainerConfig struct {
LogPath string `json:"logPath"`
// File containing the conmon PID
ConmonPidFile string `json:"conmonPidFile,omitempty"`
// RestartPolicy indicates what action the container will take upon
// exiting naturally.
// Allowed options are "no" (take no action), "on-failure" (restart on
// non-zero exit code, up an a maximum of RestartRetries times),
// and "always" (always restart the container on any exit code).
// The empty string is treated as the default ("no")
RestartPolicy string `json:"restart_policy,omitempty"`
// RestartRetries indicates the number of attempts that will be made to
// restart the container. Used only if RestartPolicy is set to
// "on-failure".
RestartRetries uint `json:"restart_retries,omitempty"`
// TODO log options for log drivers

PostConfigureNetNS bool `json:"postConfigureNetNS"`
Expand Down Expand Up @@ -729,6 +764,17 @@ func (c *Container) LogPath() string {
return c.config.LogPath
}

// RestartPolicy returns the container's restart policy.
func (c *Container) RestartPolicy() string {
return c.config.RestartPolicy
}

// RestartRetries returns the number of retries that will be attempted when
// using the "on-failure" restart policy
func (c *Container) RestartRetries() uint {
return c.config.RestartRetries
}

// RuntimeName returns the name of the runtime
func (c *Container) RuntimeName() string {
return c.runtime.ociRuntime.name
Expand Down Expand Up @@ -1003,6 +1049,21 @@ func (c *Container) BindMounts() (map[string]string, error) {
return newMap, nil
}

// StoppedByUser returns whether the container was last stopped by an explicit
// call to the Stop() API, or whether it exited naturally.
func (c *Container) StoppedByUser() (bool, error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()

if err := c.syncContainer(); err != nil {
return false, err
}
}

return c.state.StoppedByUser, nil
}

// Misc Accessors
// Most will require locking

Expand Down
29 changes: 25 additions & 4 deletions libpod/container_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ func (c *Container) Init(ctx context.Context) (err error) {

if c.state.State == ContainerStateStopped {
// Reinitialize the container
return c.reinit(ctx)
return c.reinit(ctx, false)
}

// Initialize the container for the first time
return c.init(ctx)
return c.init(ctx, false)
}

// Start starts a container.
Expand Down Expand Up @@ -199,8 +199,15 @@ func (c *Container) Kill(signal uint) error {
if c.state.State != ContainerStateRunning {
return errors.Wrapf(ErrCtrStateInvalid, "can only kill running containers")
}

defer c.newContainerEvent(events.Kill)
return c.runtime.ociRuntime.killContainer(c, signal)
if err := c.runtime.ociRuntime.killContainer(c, signal); err != nil {
return err
}

c.state.StoppedByUser = true

return c.save()
}

// Exec starts a new process inside the container
Expand Down Expand Up @@ -583,6 +590,7 @@ func (c *Container) Cleanup(ctx context.Context) error {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()

if err := c.syncContainer(); err != nil {
return err
}
Expand All @@ -593,6 +601,19 @@ func (c *Container) Cleanup(ctx context.Context) error {
return errors.Wrapf(ErrCtrStateInvalid, "container %s is running or paused, refusing to clean up", c.ID())
}

// Handle restart policy.
// Returns a bool indicating whether we actually restarted.
// If we did, don't proceed to cleanup - just exit.
didRestart, err := c.handleRestartPolicy(ctx)
if err != nil {
return err
}
if didRestart {
return nil
}

// If we didn't restart, we perform a normal cleanup

// Check if we have active exec sessions
if len(c.state.ExecSessions) != 0 {
return errors.Wrapf(ErrCtrStateInvalid, "container %s has active exec sessions, refusing to clean up", c.ID())
Expand Down Expand Up @@ -754,7 +775,7 @@ func (c *Container) Refresh(ctx context.Context) error {
if err := c.prepare(); err != nil {
return err
}
if err := c.init(ctx); err != nil {
if err := c.init(ctx, false); err != nil {
return err
}
}
Expand Down
1 change: 1 addition & 0 deletions libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data)
LogPath: config.LogPath,
ConmonPidFile: config.ConmonPidFile,
Name: config.Name,
RestartCount: int32(runtimeInfo.RestartCount),
Driver: driverData.Name,
MountLabel: config.MountLabel,
ProcessLabel: config.ProcessLabel,
Expand Down
Loading