Skip to content

Commit

Permalink
Add podman run --timeout option
Browse files Browse the repository at this point in the history
This option allows users to specify the maximum amount of time to run
before conmon sends the kill signal to the container.

Fixes: #6412

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan committed Apr 23, 2021
1 parent ba60821 commit 3538815
Show file tree
Hide file tree
Showing 17 changed files with 92 additions and 6 deletions.
10 changes: 9 additions & 1 deletion cmd/podman/common/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
createFlags.UintVar(
&cf.StopTimeout,
stopTimeoutFlagName, containerConfig.Engine.StopTimeout,
"Timeout (in seconds) to stop a container. Default is 10",
"Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.",
)
_ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone)

Expand Down Expand Up @@ -697,6 +697,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
)
_ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag)

timeoutFlagName := "timeout"
createFlags.UintVar(
&cf.Timeout,
timeoutFlagName, 0,
"Maximum length of time a container is allowed to run. The container will be killed automatically after the time expires.",
)
_ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone)

tmpfsFlagName := "tmpfs"
createFlags.StringArrayVar(
&cf.TmpFS,
Expand Down
1 change: 1 addition & 0 deletions cmd/podman/common/create_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type ContainerCLIOpts struct {
SubGIDName string
Sysctl []string
Systemd string
Timeout uint
TmpFS []string
TTY bool
Timezone string
Expand Down
1 change: 1 addition & 0 deletions cmd/podman/common/specgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
}
s.Remove = c.Rm
s.StopTimeout = &c.StopTimeout
s.Timeout = c.Timeout
s.Timezone = c.Timezone
s.Umask = c.Umask
s.Secrets = c.Secrets
Expand Down
3 changes: 2 additions & 1 deletion cmd/podman/containers/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ func stopFlags(cmd *cobra.Command) {
_ = flags.MarkHidden("cidfile")
_ = flags.MarkHidden("ignore")
}
flags.SetNormalizeFunc(utils.AliasFlags)

flags.SetNormalizeFunc(utils.TimeoutAliasFlags)
}

func init() {
Expand Down
2 changes: 1 addition & 1 deletion cmd/podman/generate/systemd.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func init() {
flags.StringVar(&format, formatFlagName, "", "Print the created units in specified format (json)")
_ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil))

flags.SetNormalizeFunc(utils.AliasFlags)
flags.SetNormalizeFunc(utils.TimeoutAliasFlags)
}

func systemd(cmd *cobra.Command, args []string) error {
Expand Down
11 changes: 9 additions & 2 deletions cmd/podman/utils/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
name = "health-timeout"
case "net":
name = "network"
case "timeout":
name = "time"
case "namespace":
name = "ns"
case "storage":
Expand All @@ -34,3 +32,12 @@ func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
}
return pflag.NormalizedName(name)
}

// TimeoutAliasFlags is a function to handle backwards compatibility with old timeout flags
func TimeoutAliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
switch name {
case "timeout":
name = "time"
}
return pflag.NormalizedName(name)
}
6 changes: 6 additions & 0 deletions docs/source/markdown/podman-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,12 @@ The `container_manage_cgroup` boolean must be enabled for this to be allowed on

`setsebool -P container_manage_cgroup true`

#### **\-\-timeout**=*seconds*

Maximimum time a container is allowed to run before conmon sends it the kill
signal. By default containers will run until they exit or are stopped by
`podman stop`.

#### **\-\-tmpfs**=*fs*

Create a tmpfs mount
Expand Down
6 changes: 6 additions & 0 deletions docs/source/markdown/podman-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,12 @@ The **container_manage_cgroup** boolean must be enabled for this to be allowed o
setsebool -P container_manage_cgroup true
```

#### **\-\-timeout**=*seconds*

Maximimum time a container is allowed to run before conmon sends it the kill
signal. By default containers will run until they exit or are stopped by
`podman stop`.

#### **\-\-tmpfs**=*fs*

Create a tmpfs mount.
Expand Down
2 changes: 2 additions & 0 deletions libpod/container_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ type ContainerMiscConfig struct {
StopSignal uint `json:"stopSignal,omitempty"`
// StopTimeout is the signal that will be used to stop the container
StopTimeout uint `json:"stopTimeout,omitempty"`
// Timeout is maximimum time a container will run before getting the kill signal
Timeout uint `json:"timeout,omitempty"`
// Time container was created
CreatedTime time.Time `json:"createdTime"`
// CgroupManager is the cgroup manager used to create this container.
Expand Down
2 changes: 2 additions & 0 deletions libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.WorkingDir = spec.Process.Cwd
}

ctrConfig.StopTimeout = c.config.StopTimeout
ctrConfig.Timeout = c.config.Timeout
ctrConfig.OpenStdin = c.config.Stdin
ctrConfig.Image = c.config.RootfsImageName
ctrConfig.SystemdMode = c.config.Systemd
Expand Down
4 changes: 4 additions & 0 deletions libpod/define/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ type InspectContainerConfig struct {
Umask string `json:"Umask,omitempty"`
// Secrets are the secrets mounted in the container
Secrets []*InspectSecret `json:"Secrets,omitempty"`
// Timeout is time before container is killed by conmon
Timeout uint `json:"Timeout"`
// StopTimeout is time before container is stoped when calling stop
StopTimeout uint `json:"StopTimeout"`
}

// InspectRestartPolicy holds information about the container's restart policy.
Expand Down
4 changes: 4 additions & 0 deletions libpod/oci_conmon_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,10 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
args = append(args, "-i")
}

if ctr.config.Timeout > 0 {
args = append(args, fmt.Sprintf("--timeout=%d", ctr.config.Timeout))
}

if !r.enableKeyring {
args = append(args, "--no-new-keyring")
}
Expand Down
13 changes: 13 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,19 @@ func WithStopTimeout(timeout uint) CtrCreateOption {
}
}

// WithTimeout sets the maximum time a container is allowed to run"
func WithTimeout(timeout uint) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}

ctr.config.Timeout = timeout

return nil
}
}

// WithIDMappings sets the idmappings for the container
func WithIDMappings(idmappings storage.IDMappingOptions) CtrCreateOption {
return func(ctr *Container) error {
Expand Down
3 changes: 3 additions & 0 deletions pkg/specgen/generate/container_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.StopTimeout != nil {
options = append(options, libpod.WithStopTimeout(*s.StopTimeout))
}
if s.Timeout != 0 {
options = append(options, libpod.WithTimeout(s.Timeout))
}
if s.LogConfiguration != nil {
if len(s.LogConfiguration.Path) > 0 {
options = append(options, libpod.WithLogPath(s.LogConfiguration.Path))
Expand Down
5 changes: 5 additions & 0 deletions pkg/specgen/specgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ type ContainerBasicConfig struct {
// instead.
// Optional.
StopTimeout *uint `json:"stop_timeout,omitempty"`
// Timeout is a maximum time in seconds the container will run before
// main process is sent SIGKILL.
// If 0 is used, signal will not be sent. Container can run indefinitely
// Optional.
Timeout uint `json:"timeout,omitempty"`
// LogConfiguration describes the logging for a container including
// driver, path, and options.
// Optional
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/generate_systemd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ var _ = Describe("Podman generate systemd", func() {
n.WaitWithDefaultTimeout()
Expect(n.ExitCode()).To(Equal(0))

session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "42", "--name", "--new", "foo"})
session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "--new", "foo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

Expand Down
23 changes: 23 additions & 0 deletions test/system/030-run.bats
Original file line number Diff line number Diff line change
Expand Up @@ -668,4 +668,27 @@ json-file | f
is "$output" ".*HOME=/.*"
}

@test "podman run --timeout - basic test" {
cid=timeouttest
t0=$SECONDS
run_podman 255 run --name $cid --timeout 10 $IMAGE sleep 60
t1=$SECONDS
# Confirm that container is stopped. Podman-remote unfortunately
# cannot tell the difference between "stopped" and "exited", and
# spits them out interchangeably, so we need to recognize either.
run_podman inspect --format '{{.State.Status}} {{.State.ExitCode}}' $cid
is "$output" "\\(stopped\|exited\\) \-1" \
"Status and exit code of stopped container"

# This operation should take
# exactly 10 seconds. Give it some leeway.
delta_t=$(( $t1 - $t0 ))
[ $delta_t -gt 8 ] ||\
die "podman stop: ran too quickly! ($delta_t seconds; expected >= 10)"
[ $delta_t -le 14 ] ||\
die "podman stop: took too long ($delta_t seconds; expected ~10)"

run_podman rm $cid
}

# vim: filetype=sh

0 comments on commit 3538815

Please sign in to comment.