generate systemd: add --start-timeout flag

Add a new flag to set the start timeout for a generated systemd unit.
To make naming consistent, add a new --stop-timeout flag as well and let
the previous --time map to it.

Fixes: #11618
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg 2021-11-22 11:05:59 +01:00
parent 1bfbb28b03
commit 566b78dd02
12 changed files with 106 additions and 25 deletions

View File

@ -19,16 +19,19 @@ import (
) )
const ( const (
restartPolicyFlagName = "restart-policy" startTimeoutFlagName = "start-timeout"
timeFlagName = "time" stopTimeoutFlagName = "stop-timeout"
newFlagName = "new" stopTimeoutCompatFlagName = "time"
restartPolicyFlagName = "restart-policy"
newFlagName = "new"
) )
var ( var (
files bool files bool
format string format string
systemdTimeout uint
systemdRestart string systemdRestart string
startTimeout uint
stopTimeout uint
systemdOptions = entities.GenerateSystemdOptions{} systemdOptions = entities.GenerateSystemdOptions{}
systemdDescription = `Generate systemd units for a pod or container. systemdDescription = `Generate systemd units for a pod or container.
The generated units can later be controlled via systemctl(1).` The generated units can later be controlled via systemctl(1).`
@ -56,8 +59,17 @@ func init() {
flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout") flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout")
flags.BoolVar(&systemdOptions.TemplateUnitFile, "template", false, "Make it a template file and use %i and %I specifiers. Working only for containers") flags.BoolVar(&systemdOptions.TemplateUnitFile, "template", false, "Make it a template file and use %i and %I specifiers. Working only for containers")
flags.UintVarP(&systemdTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Stop timeout override") flags.UintVarP(&startTimeout, startTimeoutFlagName, "", 0, "Start timeout override")
_ = systemdCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) _ = systemdCmd.RegisterFlagCompletionFunc(startTimeoutFlagName, completion.AutocompleteNone)
// NOTE: initially, there was only a --time/-t flag which mapped to
// stop-timeout. To remain backwards compatible create a hidden flag
// that maps to StopTimeout.
flags.UintVarP(&stopTimeout, stopTimeoutFlagName, "", containerConfig.Engine.StopTimeout, "Stop timeout override")
_ = systemdCmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone)
flags.UintVarP(&stopTimeout, stopTimeoutCompatFlagName, "t", containerConfig.Engine.StopTimeout, "Backwards alias for --stop-timeout")
_ = flags.MarkHidden("time")
flags.BoolVar(&systemdOptions.New, newFlagName, false, "Create a new container or pod instead of starting an existing one") flags.BoolVar(&systemdOptions.New, newFlagName, false, "Create a new container or pod instead of starting an existing one")
flags.BoolVarP(&systemdOptions.NoHeader, "no-header", "", false, "Skip header generation") flags.BoolVarP(&systemdOptions.NoHeader, "no-header", "", false, "Skip header generation")
@ -84,9 +96,6 @@ func init() {
} }
func systemd(cmd *cobra.Command, args []string) error { func systemd(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed(timeFlagName) {
systemdOptions.StopTimeout = &systemdTimeout
}
if cmd.Flags().Changed(restartPolicyFlagName) { if cmd.Flags().Changed(restartPolicyFlagName) {
systemdOptions.RestartPolicy = &systemdRestart systemdOptions.RestartPolicy = &systemdRestart
} }
@ -102,6 +111,23 @@ func systemd(cmd *cobra.Command, args []string) error {
systemdOptions.New = true systemdOptions.New = true
} }
if cmd.Flags().Changed(startTimeoutFlagName) {
systemdOptions.StartTimeout = &startTimeout
}
setStopTimeout := 0
if cmd.Flags().Changed(stopTimeoutFlagName) {
setStopTimeout++
}
if cmd.Flags().Changed(stopTimeoutCompatFlagName) {
setStopTimeout++
}
switch setStopTimeout {
case 1:
systemdOptions.StopTimeout = &stopTimeout
case 2:
return fmt.Errorf("%s and %s are redundant and cannot be used together", stopTimeoutFlagName, stopTimeoutCompatFlagName)
}
reports, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions) reports, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions)
if err != nil { if err != nil {
return err return err

View File

@ -38,9 +38,13 @@ Note that `--new` only works on containers and pods created directly via Podman
Do not generate the header including meta data such as the Podman version and the timestamp. Do not generate the header including meta data such as the Podman version and the timestamp.
#### **--time**, **-t**=*value* #### **--start-timeout** =*value*
Override the default stop timeout for the container with the given value. Override the default start timeout for the container with the given value in seconds.
#### **--stop-timeout** =*value*
Override the default stop timeout for the container with the given value in seconds.
#### **--restart-policy**=*policy* #### **--restart-policy**=*policy*

View File

@ -23,10 +23,12 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
TemplateUnitFile bool `schema:"templateUnitFile"` TemplateUnitFile bool `schema:"templateUnitFile"`
RestartPolicy *string `schema:"restartPolicy"` RestartPolicy *string `schema:"restartPolicy"`
StopTimeout uint `schema:"stopTimeout"` StopTimeout uint `schema:"stopTimeout"`
StartTimeout uint `schema:"startTimeout"`
ContainerPrefix string `schema:"containerPrefix"` ContainerPrefix string `schema:"containerPrefix"`
PodPrefix string `schema:"podPrefix"` PodPrefix string `schema:"podPrefix"`
Separator string `schema:"separator"` Separator string `schema:"separator"`
}{ }{
StartTimeout: 0,
StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout, StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout,
ContainerPrefix: "container", ContainerPrefix: "container",
PodPrefix: "pod", PodPrefix: "pod",
@ -46,6 +48,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
NoHeader: query.NoHeader, NoHeader: query.NoHeader,
TemplateUnitFile: query.TemplateUnitFile, TemplateUnitFile: query.TemplateUnitFile,
RestartPolicy: query.RestartPolicy, RestartPolicy: query.RestartPolicy,
StartTimeout: &query.StartTimeout,
StopTimeout: &query.StopTimeout, StopTimeout: &query.StopTimeout,
ContainerPrefix: query.ContainerPrefix, ContainerPrefix: query.ContainerPrefix,
PodPrefix: query.PodPrefix, PodPrefix: query.PodPrefix,

View File

@ -37,10 +37,15 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
// default: false // default: false
// description: Do not generate the header including the Podman version and the timestamp. // description: Do not generate the header including the Podman version and the timestamp.
// - in: query // - in: query
// name: time // name: startTimeout
// type: integer
// default: 0
// description: Start timeout in seconds.
// - in: query
// name: stopTimeout
// type: integer // type: integer
// default: 10 // default: 10
// description: Stop timeout override. // description: Stop timeout in seconds.
// - in: query // - in: query
// name: restartPolicy // name: restartPolicy
// default: on-failure // default: on-failure

View File

@ -20,6 +20,8 @@ type SystemdOptions struct {
TemplateUnitFile *bool TemplateUnitFile *bool
// RestartPolicy - systemd restart policy. // RestartPolicy - systemd restart policy.
RestartPolicy *string RestartPolicy *string
// StartTimeout - time when starting the container.
StartTimeout *uint
// StopTimeout - time when stopping the container. // StopTimeout - time when stopping the container.
StopTimeout *uint StopTimeout *uint
// ContainerPrefix - systemd unit name prefix for containers // ContainerPrefix - systemd unit name prefix for containers

View File

@ -92,6 +92,21 @@ func (o *SystemdOptions) GetRestartPolicy() string {
return *o.RestartPolicy return *o.RestartPolicy
} }
// WithStartTimeout set field StartTimeout to given value
func (o *SystemdOptions) WithStartTimeout(value uint) *SystemdOptions {
o.StartTimeout = &value
return o
}
// GetStartTimeout returns value of field StartTimeout
func (o *SystemdOptions) GetStartTimeout() uint {
if o.StartTimeout == nil {
var z uint
return z
}
return *o.StartTimeout
}
// WithStopTimeout set field StopTimeout to given value // WithStopTimeout set field StopTimeout to given value
func (o *SystemdOptions) WithStopTimeout(value uint) *SystemdOptions { func (o *SystemdOptions) WithStopTimeout(value uint) *SystemdOptions {
o.StopTimeout = &value o.StopTimeout = &value

View File

@ -10,6 +10,8 @@ type GenerateSystemdOptions struct {
New bool New bool
// RestartPolicy - systemd restart policy. // RestartPolicy - systemd restart policy.
RestartPolicy *string RestartPolicy *string
// StartTimeout - time when starting the container.
StartTimeout *uint
// StopTimeout - time when stopping the container. // StopTimeout - time when stopping the container.
StopTimeout *uint StopTimeout *uint
// ContainerPrefix - systemd unit name prefix for containers // ContainerPrefix - systemd unit name prefix for containers

View File

@ -8,14 +8,18 @@ import (
) )
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, opts entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, opts entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
options := new(generate.SystemdOptions).WithUseName(opts.Name).WithContainerPrefix(opts.ContainerPrefix).WithNew(opts.New).WithNoHeader(opts.NoHeader).WithTemplateUnitFile(opts.TemplateUnitFile) options := new(generate.SystemdOptions).WithUseName(opts.Name).WithContainerPrefix(opts.ContainerPrefix).WithNew(opts.New).WithNoHeader(opts.NoHeader).WithTemplateUnitFile(opts.TemplateUnitFile).WithPodPrefix(opts.PodPrefix).WithSeparator(opts.Separator)
options.WithPodPrefix(opts.PodPrefix).WithSeparator(opts.Separator)
if opts.StartTimeout != nil {
options.WithStartTimeout(*opts.StartTimeout)
}
if opts.StopTimeout != nil {
options.WithStopTimeout(*opts.StopTimeout)
}
if opts.RestartPolicy != nil { if opts.RestartPolicy != nil {
options.WithRestartPolicy(*opts.RestartPolicy) options.WithRestartPolicy(*opts.RestartPolicy)
} }
if to := opts.StopTimeout; to != nil {
options.WithStopTimeout(*opts.StopTimeout)
}
return generate.Systemd(ic.ClientCtx, nameOrID, options) return generate.Systemd(ic.ClientCtx, nameOrID, options)
} }

View File

@ -73,6 +73,8 @@ type containerInfo struct {
ExecStartPre string ExecStartPre string
// ExecStart of the unit. // ExecStart of the unit.
ExecStart string ExecStart string
// TimeoutStartSec of the unit.
TimeoutStartSec uint
// TimeoutStopSec of the unit. // TimeoutStopSec of the unit.
TimeoutStopSec uint TimeoutStopSec uint
// ExecStop of the unit. // ExecStop of the unit.
@ -109,6 +111,9 @@ Restart={{{{.RestartPolicy}}}}
{{{{- if .StartLimitBurst}}}} {{{{- if .StartLimitBurst}}}}
StartLimitBurst={{{{.StartLimitBurst}}}} StartLimitBurst={{{{.StartLimitBurst}}}}
{{{{- end}}}} {{{{- end}}}}
{{{{- if ne .TimeoutStartSec 0}}}}
TimeoutStartSec={{{{.TimeoutStartSec}}}}
{{{{- end}}}}
TimeoutStopSec={{{{.TimeoutStopSec}}}} TimeoutStopSec={{{{.TimeoutStopSec}}}}
{{{{- if .ExecStartPre}}}} {{{{- if .ExecStartPre}}}}
ExecStartPre={{{{.ExecStartPre}}}} ExecStartPre={{{{.ExecStartPre}}}}
@ -148,9 +153,14 @@ func ContainerUnit(ctr *libpod.Container, options entities.GenerateSystemdOption
} }
func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSystemdOptions) (*containerInfo, error) { func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSystemdOptions) (*containerInfo, error) {
timeout := ctr.StopTimeout() stopTimeout := ctr.StopTimeout()
if options.StopTimeout != nil { if options.StopTimeout != nil {
timeout = *options.StopTimeout stopTimeout = *options.StopTimeout
}
startTimeout := uint(0)
if options.StartTimeout != nil {
startTimeout = *options.StartTimeout
} }
config := ctr.Config() config := ctr.Config()
@ -185,7 +195,8 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste
ContainerNameOrID: nameOrID, ContainerNameOrID: nameOrID,
RestartPolicy: define.DefaultRestartPolicy, RestartPolicy: define.DefaultRestartPolicy,
PIDFile: conmonPidFile, PIDFile: conmonPidFile,
StopTimeout: timeout, TimeoutStartSec: startTimeout,
StopTimeout: stopTimeout,
GenerateTimestamp: true, GenerateTimestamp: true,
CreateCommand: createCommand, CreateCommand: createCommand,
RunRoot: runRoot, RunRoot: runRoot,

View File

@ -195,9 +195,9 @@ func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) (
return nil, errors.Wrap(err, "could not find infra container") return nil, errors.Wrap(err, "could not find infra container")
} }
timeout := infraCtr.StopTimeout() stopTimeout := infraCtr.StopTimeout()
if options.StopTimeout != nil { if options.StopTimeout != nil {
timeout = *options.StopTimeout stopTimeout = *options.StopTimeout
} }
config := infraCtr.Config() config := infraCtr.Config()
@ -223,7 +223,7 @@ func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) (
ServiceName: serviceName, ServiceName: serviceName,
InfraNameOrID: ctrNameOrID, InfraNameOrID: ctrNameOrID,
PIDFile: conmonPidFile, PIDFile: conmonPidFile,
StopTimeout: timeout, StopTimeout: stopTimeout,
GenerateTimestamp: true, GenerateTimestamp: true,
CreateCommand: createCommand, CreateCommand: createCommand,
} }

View File

@ -147,6 +147,15 @@ var _ = Describe("Podman generate systemd", func() {
session := podmanTest.Podman([]string{"generate", "systemd", "--time", "5", "nginx"}) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "5", "nginx"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0)) Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=65"))
Expect(session.OutputToString()).ToNot(ContainSubstring("TimeoutStartSec="))
Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5"))
session = podmanTest.Podman([]string{"generate", "systemd", "--stop-timeout", "5", "--start-timeout", "123", "nginx"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("TimeoutStartSec=123"))
Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=65"))
Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5")) Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5"))
}) })

View File

@ -138,7 +138,7 @@ function service_cleanup() {
} }
# Regression test for #11438 # Regression test for #11438
@test "podman generate systemd - restart policy" { @test "podman generate systemd - restart policy & timeouts" {
cname=$(random_string) cname=$(random_string)
run_podman create --restart=always --name $cname $IMAGE run_podman create --restart=always --name $cname $IMAGE
run_podman generate systemd --new $cname run_podman generate systemd --new $cname