Handlers for generate systemd with custom dependencies

This commit includes:
* Handlers for generate systemd unit
  with manually defined dependencies such as:
  Wants=, After= and Requires=

* The new unit and e2e tests for checking generated systemd units
  for container and pod with custom dependencies

* Documented descriptions for custom dependencies options

Signed-off-by: Eugene (Evgenii) Shubin <esendjer@gmail.com>
This commit is contained in:
esendjer
2022-01-19 14:56:37 +05:00
parent 094b11cbcb
commit b9a2d8698a
13 changed files with 660 additions and 12 deletions

View File

@ -25,6 +25,9 @@ const (
restartPolicyFlagName = "restart-policy" restartPolicyFlagName = "restart-policy"
restartSecFlagName = "restart-sec" restartSecFlagName = "restart-sec"
newFlagName = "new" newFlagName = "new"
wantsFlagName = "wants"
afterFlagName = "after"
requiresFlagName = "requires"
) )
var ( var (
@ -97,6 +100,15 @@ func init() {
flags.StringVar(&format, formatFlagName, "", "Print the created units in specified format (json)") flags.StringVar(&format, formatFlagName, "", "Print the created units in specified format (json)")
_ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) _ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil))
flags.StringArrayVar(&systemdOptions.Wants, wantsFlagName, nil, "Add (weak) requirement dependencies to the generated unit file")
_ = systemdCmd.RegisterFlagCompletionFunc(wantsFlagName, completion.AutocompleteNone)
flags.StringArrayVar(&systemdOptions.After, afterFlagName, nil, "Add dependencies order to the generated unit file")
_ = systemdCmd.RegisterFlagCompletionFunc(afterFlagName, completion.AutocompleteNone)
flags.StringArrayVar(&systemdOptions.Requires, requiresFlagName, nil, "Similar to wants, but declares stronger requirement dependencies")
_ = systemdCmd.RegisterFlagCompletionFunc(requiresFlagName, completion.AutocompleteNone)
flags.SetNormalizeFunc(utils.TimeoutAliasFlags) flags.SetNormalizeFunc(utils.TimeoutAliasFlags)
} }

View File

@ -68,6 +68,22 @@ Set the systemd unit name prefix for pods. The default is *pod*.
Set the systemd unit name separator between the name/id of a container/pod and the prefix. The default is *-*. Set the systemd unit name separator between the name/id of a container/pod and the prefix. The default is *-*.
#### **--wants**=*dependency_name*
Add the systemd unit wants (`Wants=`) option, that this service is (weak) dependent on. This option may be specified more than once. This option does not influence the order in which services are started or stopped.
User-defined dependencies will be appended to the generated unit file, but any existing options such as needed or defined by default (e.g. `online.target`) will **not** be removed or overridden.
#### **--after**=*dependency_name*
Add the systemd unit after (`After=`) option, that ordering dependencies between the list of dependencies and this service. This option may be specified more than once.
User-defined dependencies will be appended to the generated unit file, but any existing options such as needed or defined by default (e.g. `online.target`) will **not** be removed or overridden.
#### **--requires**=*dependency_name*
Set the systemd unit requires (`Requires=`) option. Similar to wants, but declares a stronger requirement dependency.
#### **--template** #### **--template**
Add template specifiers to run multiple services from the systemd unit file. Add template specifiers to run multiple services from the systemd unit file.

View File

@ -17,17 +17,20 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct { query := struct {
Name bool `schema:"useName"` Name bool `schema:"useName"`
New bool `schema:"new"` New bool `schema:"new"`
NoHeader bool `schema:"noHeader"` NoHeader bool `schema:"noHeader"`
TemplateUnitFile bool `schema:"templateUnitFile"` TemplateUnitFile bool `schema:"templateUnitFile"`
RestartPolicy *string `schema:"restartPolicy"` RestartPolicy *string `schema:"restartPolicy"`
RestartSec uint `schema:"restartSec"` RestartSec uint `schema:"restartSec"`
StopTimeout uint `schema:"stopTimeout"` StopTimeout uint `schema:"stopTimeout"`
StartTimeout uint `schema:"startTimeout"` 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"`
Wants []string `schema:"wants"`
After []string `schema:"after"`
Requires []string `schema:"requires"`
}{ }{
StartTimeout: 0, StartTimeout: 0,
StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout, StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout,
@ -55,6 +58,9 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
PodPrefix: query.PodPrefix, PodPrefix: query.PodPrefix,
Separator: query.Separator, Separator: query.Separator,
RestartSec: &query.RestartSec, RestartSec: &query.RestartSec,
Wants: query.Wants,
After: query.After,
Requires: query.Requires,
} }
report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options) report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options)

View File

@ -72,6 +72,27 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
// type: integer // type: integer
// default: 0 // default: 0
// description: Configures the time to sleep before restarting a service. // description: Configures the time to sleep before restarting a service.
// - in: query
// name: wants
// type: array
// items:
// type: string
// default: []
// description: Systemd Wants list for the container or pods.
// - in: query
// name: after
// type: array
// items:
// type: string
// default: []
// description: Systemd After list for the container or pods.
// - in: query
// name: requires
// type: array
// items:
// type: string
// default: []
// description: Systemd Requires list for the container or pods.
// produces: // produces:
// - application/json // - application/json
// responses: // responses:

View File

@ -32,4 +32,10 @@ type SystemdOptions struct {
PodPrefix *string PodPrefix *string
// Separator - systemd unit name separator between name/id and prefix // Separator - systemd unit name separator between name/id and prefix
Separator *string Separator *string
// Wants - systemd wants list for the container or pods
Wants *[]string
// After - systemd after list for the container or pods
After *[]string
// Requires - systemd requires list for the container or pods
Requires *[]string
} }

View File

@ -181,3 +181,48 @@ func (o *SystemdOptions) GetSeparator() string {
} }
return *o.Separator return *o.Separator
} }
// WithWants set field Wants to given value
func (o *SystemdOptions) WithWants(value []string) *SystemdOptions {
o.Wants = &value
return o
}
// GetWants returns value of field Wants
func (o *SystemdOptions) GetWants() []string {
if o.Wants == nil {
var z []string
return z
}
return *o.Wants
}
// WithAfter set field After to given value
func (o *SystemdOptions) WithAfter(value []string) *SystemdOptions {
o.After = &value
return o
}
// GetAfter returns value of field After
func (o *SystemdOptions) GetAfter() []string {
if o.After == nil {
var z []string
return z
}
return *o.After
}
// WithRequires set field Requires to given value
func (o *SystemdOptions) WithRequires(value []string) *SystemdOptions {
o.Requires = &value
return o
}
// GetRequires returns value of field Requires
func (o *SystemdOptions) GetRequires() []string {
if o.Requires == nil {
var z []string
return z
}
return *o.Requires
}

View File

@ -26,6 +26,12 @@ type GenerateSystemdOptions struct {
NoHeader bool NoHeader bool
// TemplateUnitFile - make use of %i and %I to differentiate between the different instances of the unit // TemplateUnitFile - make use of %i and %I to differentiate between the different instances of the unit
TemplateUnitFile bool TemplateUnitFile bool
// Wants - systemd wants list for the container or pods
Wants []string
// After - systemd after list for the container or pods
After []string
// Requires - systemd requires list for the container or pods
Requires []string
} }
// GenerateSystemdReport // GenerateSystemdReport

View File

@ -8,7 +8,17 @@ 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).WithPodPrefix(opts.PodPrefix).WithSeparator(opts.Separator) 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).
WithWants(opts.Wants).
WithAfter(opts.After).
WithRequires(opts.Requires)
if opts.StartTimeout != nil { if opts.StartTimeout != nil {
options.WithStartTimeout(*opts.StartTimeout) options.WithStartTimeout(*opts.StartTimeout)

View File

@ -94,6 +94,13 @@ type containerInfo struct {
RunRoot string RunRoot string
// Add %i and %I to description and execute parts // Add %i and %I to description and execute parts
IdentifySpecifier bool IdentifySpecifier bool
// Wants are the list of services that this service is (weak) dependent on. This
// option does not influence the order in which services are started or stopped.
Wants []string
// After ordering dependencies between the list of services and this service.
After []string
// Similar to Wants, but declares a stronger requirement dependency.
Requires []string
} }
const containerTemplate = headerTemplate + ` const containerTemplate = headerTemplate + `
@ -101,6 +108,19 @@ const containerTemplate = headerTemplate + `
BindsTo={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} BindsTo={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
After={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} After={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
{{{{- end}}}} {{{{- end}}}}
{{{{- if or .Wants .After .Requires }}}}
# User-defined dependencies
{{{{- end}}}}
{{{{- if .Wants}}}}
Wants={{{{- range $index, $value := .Wants }}}}{{{{ if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}}
{{{{- end}}}}
{{{{- if .After}}}}
After={{{{- range $index, $value := .After }}}}{{{{ if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}}
{{{{- end}}}}
{{{{- if .Requires}}}}
Requires={{{{- range $index, $value := .Requires }}}}{{{{ if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}}
{{{{- end}}}}
[Service] [Service]
Environment={{{{.EnvVariable}}}}=%n{{{{- if (eq .IdentifySpecifier true) }}}}-%i{{{{- end}}}} Environment={{{{.EnvVariable}}}}=%n{{{{- if (eq .IdentifySpecifier true) }}}}-%i{{{{- end}}}}
@ -201,6 +221,9 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste
CreateCommand: createCommand, CreateCommand: createCommand,
RunRoot: runRoot, RunRoot: runRoot,
containerEnv: envs, containerEnv: envs,
Wants: options.Wants,
After: options.After,
Requires: options.Requires,
} }
return &info, nil return &info, nil

View File

@ -87,6 +87,116 @@ ExecStopPost=/usr/bin/podman stop -t 10 foobar
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking Type=forking
[Install]
WantedBy=default.target
`
goodNameCustomWants := `# container-foobar.service
# autogenerated by Podman CI
[Unit]
Description=Podman container-foobar.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
# User-defined dependencies
Wants=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
ExecStopPost=/usr/bin/podman stop -t 10 foobar
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
`
goodNameCustomAfter := `# container-foobar.service
# autogenerated by Podman CI
[Unit]
Description=Podman container-foobar.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
# User-defined dependencies
After=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
ExecStopPost=/usr/bin/podman stop -t 10 foobar
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
`
goodNameCustomRequires := `# container-foobar.service
# autogenerated by Podman CI
[Unit]
Description=Podman container-foobar.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
# User-defined dependencies
Requires=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
ExecStopPost=/usr/bin/podman stop -t 10 foobar
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
`
goodNameCustomDependencies := `# container-foobar.service
# autogenerated by Podman CI
[Unit]
Description=Podman container-foobar.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
# User-defined dependencies
Wants=a.service b.service c.target
After=a.service b.service c.target
Requires=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
ExecStopPost=/usr/bin/podman stop -t 10 foobar
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install] [Install]
WantedBy=default.target WantedBy=default.target
` `
@ -613,6 +723,84 @@ WantedBy=default.target
false, false,
false, false,
}, },
{"good with name and wants",
containerInfo{
Executable: "/usr/bin/podman",
ServiceName: "container-foobar",
ContainerNameOrID: "foobar",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 10,
PodmanVersion: "CI",
Wants: []string{"a.service", "b.service", "c.target"},
EnvVariable: define.EnvVariable,
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
},
goodNameCustomWants,
false,
false,
false,
false,
},
{"good with name and after",
containerInfo{
Executable: "/usr/bin/podman",
ServiceName: "container-foobar",
ContainerNameOrID: "foobar",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 10,
PodmanVersion: "CI",
After: []string{"a.service", "b.service", "c.target"},
EnvVariable: define.EnvVariable,
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
},
goodNameCustomAfter,
false,
false,
false,
false,
},
{"good with name and requires",
containerInfo{
Executable: "/usr/bin/podman",
ServiceName: "container-foobar",
ContainerNameOrID: "foobar",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 10,
PodmanVersion: "CI",
Requires: []string{"a.service", "b.service", "c.target"},
EnvVariable: define.EnvVariable,
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
},
goodNameCustomRequires,
false,
false,
false,
false,
},
{"good with name and dependencies",
containerInfo{
Executable: "/usr/bin/podman",
ServiceName: "container-foobar",
ContainerNameOrID: "foobar",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 10,
PodmanVersion: "CI",
Wants: []string{"a.service", "b.service", "c.target"},
After: []string{"a.service", "b.service", "c.target"},
Requires: []string{"a.service", "b.service", "c.target"},
EnvVariable: define.EnvVariable,
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
},
goodNameCustomDependencies,
false,
false,
false,
false,
},
{"good with name and bound to", {"good with name and bound to",
containerInfo{ containerInfo{
Executable: "/usr/bin/podman", Executable: "/usr/bin/podman",

View File

@ -83,10 +83,30 @@ type podInfo struct {
RunRoot string RunRoot string
// Add %i and %I to description and execute parts - this should not be used // Add %i and %I to description and execute parts - this should not be used
IdentifySpecifier bool IdentifySpecifier bool
// Wants are the list of services that this service is (weak) dependent on. This
// option does not influence the order in which services are started or stopped.
Wants []string
// After ordering dependencies between the list of services and this service.
After []string
// Similar to Wants, but declares a stronger requirement dependency.
Requires []string
} }
const podTemplate = headerTemplate + `Requires={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} const podTemplate = headerTemplate + `Requires={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
Before={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} Before={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
{{{{- if or .Wants .After .Requires }}}}
# User-defined dependencies
{{{{- end}}}}
{{{{- if .Wants}}}}
Wants={{{{- range $index, $value := .Wants }}}}{{{{ if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}}
{{{{- end}}}}
{{{{- if .After}}}}
After={{{{- range $index, $value := .After }}}}{{{{ if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}}
{{{{- end}}}}
{{{{- if .Requires}}}}
Requires={{{{- range $index, $value := .Requires }}}}{{{{ if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}}
{{{{- end}}}}
[Service] [Service]
Environment={{{{.EnvVariable}}}}=%n Environment={{{{.EnvVariable}}}}=%n

View File

@ -67,6 +67,121 @@ WantedBy=default.target
podGood := serviceInfo + headerInfo + podContent podGood := serviceInfo + headerInfo + podContent
podGoodNoHeaderInfo := serviceInfo + podContent podGoodNoHeaderInfo := serviceInfo + podContent
podGoodCustomWants := `# pod-123abc.service
# autogenerated by Podman CI
[Unit]
Description=Podman pod-123abc.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
Requires=container-1.service container-2.service
Before=container-1.service container-2.service
# User-defined dependencies
Wants=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=102
ExecStart=/usr/bin/podman start jadda-jadda-infra
ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra
ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
`
podGoodCustomAfter := `# pod-123abc.service
# autogenerated by Podman CI
[Unit]
Description=Podman pod-123abc.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
Requires=container-1.service container-2.service
Before=container-1.service container-2.service
# User-defined dependencies
After=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=102
ExecStart=/usr/bin/podman start jadda-jadda-infra
ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra
ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
`
podGoodCustomRequires := `# pod-123abc.service
# autogenerated by Podman CI
[Unit]
Description=Podman pod-123abc.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
Requires=container-1.service container-2.service
Before=container-1.service container-2.service
# User-defined dependencies
Requires=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=102
ExecStart=/usr/bin/podman start jadda-jadda-infra
ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra
ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
`
podGoodCustomDependencies := `# pod-123abc.service
# autogenerated by Podman CI
[Unit]
Description=Podman pod-123abc.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/containers/storage
Requires=container-1.service container-2.service
Before=container-1.service container-2.service
# User-defined dependencies
Wants=a.service b.service c.target
After=a.service b.service c.target
Requires=a.service b.service c.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=102
ExecStart=/usr/bin/podman start jadda-jadda-infra
ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra
ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra
PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
`
podGoodRestartSec := `# pod-123abc.service podGoodRestartSec := `# pod-123abc.service
# autogenerated by Podman CI # autogenerated by Podman CI
@ -232,6 +347,94 @@ WantedBy=default.target
false, false,
false, false,
}, },
{"pod",
podInfo{
Executable: "/usr/bin/podman",
ServiceName: "pod-123abc",
InfraNameOrID: "jadda-jadda-infra",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 42,
PodmanVersion: "CI",
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
RequiredServices: []string{"container-1", "container-2"},
Wants: []string{"a.service", "b.service", "c.target"},
CreateCommand: []string{
"podman", "pod", "create", "--name", "foo", "--wants", "a.service",
"--wants", "b.service", "--wants", "c.target", "bar=arg with space"},
},
podGoodCustomWants,
false,
false,
false,
},
{"pod",
podInfo{
Executable: "/usr/bin/podman",
ServiceName: "pod-123abc",
InfraNameOrID: "jadda-jadda-infra",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 42,
PodmanVersion: "CI",
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
RequiredServices: []string{"container-1", "container-2"},
After: []string{"a.service", "b.service", "c.target"},
CreateCommand: []string{
"podman", "pod", "create", "--name", "foo", "--after", "a.service",
"--after", "b.service", "--after", "c.target", "bar=arg with space"},
},
podGoodCustomAfter,
false,
false,
false,
},
{"pod",
podInfo{
Executable: "/usr/bin/podman",
ServiceName: "pod-123abc",
InfraNameOrID: "jadda-jadda-infra",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 42,
PodmanVersion: "CI",
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
RequiredServices: []string{"container-1", "container-2"},
Requires: []string{"a.service", "b.service", "c.target"},
CreateCommand: []string{
"podman", "pod", "create", "--name", "foo", "--requires", "a.service",
"--requires", "b.service", "--requires", "c.target", "bar=arg with space"},
},
podGoodCustomRequires,
false,
false,
false,
},
{"pod",
podInfo{
Executable: "/usr/bin/podman",
ServiceName: "pod-123abc",
InfraNameOrID: "jadda-jadda-infra",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 42,
PodmanVersion: "CI",
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
RequiredServices: []string{"container-1", "container-2"},
Wants: []string{"a.service", "b.service", "c.target"},
After: []string{"a.service", "b.service", "c.target"},
Requires: []string{"a.service", "b.service", "c.target"},
CreateCommand: []string{
"podman", "pod", "create", "--name", "foo", "--wants", "a.service",
"--wants", "b.service", "--wants", "c.target", "--after", "a.service",
"--after", "b.service", "--after", "c.target", "--requires", "a.service",
"--requires", "b.service", "--requires", "c.target", "bar=arg with space"},
},
podGoodCustomDependencies,
false,
false,
false,
},
{"pod restartSec", {"pod restartSec",
podInfo{ podInfo{
Executable: "/usr/bin/podman", Executable: "/usr/bin/podman",

View File

@ -159,6 +159,50 @@ var _ = Describe("Podman generate systemd", func() {
Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5")) Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5"))
}) })
It("podman generate systemd with user-defined dependencies", func() {
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
n.WaitWithDefaultTimeout()
Expect(n).Should(Exit(0))
session := podmanTest.Podman([]string{"generate", "systemd", "--wants", "foobar.service", "nginx"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined Wants option
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("Wants=foobar.service"))
session = podmanTest.Podman([]string{"generate", "systemd", "--after", "foobar.service", "nginx"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined After option
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("After=foobar.service"))
session = podmanTest.Podman([]string{"generate", "systemd", "--requires", "foobar.service", "nginx"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined Requires option
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("Requires=foobar.service"))
session = podmanTest.Podman([]string{
"generate", "systemd",
"--wants", "foobar.service", "--wants", "barfoo.service",
"--after", "foobar.service", "--after", "barfoo.service",
"--requires", "foobar.service", "--requires", "barfoo.service", "nginx"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined Want, After, Requires options
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("Wants=foobar.service barfoo.service"))
Expect(session.OutputToString()).To(ContainSubstring("After=foobar.service barfoo.service"))
Expect(session.OutputToString()).To(ContainSubstring("Requires=foobar.service barfoo.service"))
})
It("podman generate systemd pod --name", func() { It("podman generate systemd pod --name", func() {
n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"})
n.WaitWithDefaultTimeout() n.WaitWithDefaultTimeout()
@ -213,6 +257,54 @@ var _ = Describe("Podman generate systemd", func() {
Expect(session.OutputToString()).To(ContainSubstring("/container-foo-1.service")) Expect(session.OutputToString()).To(ContainSubstring("/container-foo-1.service"))
}) })
It("podman generate systemd pod with user-defined dependencies", func() {
n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"})
n.WaitWithDefaultTimeout()
Expect(n).Should(Exit(0))
n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"})
n.WaitWithDefaultTimeout()
Expect(n).Should(Exit(0))
session := podmanTest.Podman([]string{"generate", "systemd", "--name", "--wants", "foobar.service", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined Wants option
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("Wants=foobar.service"))
session = podmanTest.Podman([]string{"generate", "systemd", "--name", "--after", "foobar.service", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined After option
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("After=foobar.service"))
session = podmanTest.Podman([]string{"generate", "systemd", "--name", "--requires", "foobar.service", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined Requires option
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("Requires=foobar.service"))
session = podmanTest.Podman([]string{
"generate", "systemd", "--name",
"--wants", "foobar.service", "--wants", "barfoo.service",
"--after", "foobar.service", "--after", "barfoo.service",
"--requires", "foobar.service", "--requires", "barfoo.service", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// The generated systemd unit should contain the User-defined Want, After, Requires options
Expect(session.OutputToString()).To(ContainSubstring("# User-defined dependencies"))
Expect(session.OutputToString()).To(ContainSubstring("Wants=foobar.service barfoo.service"))
Expect(session.OutputToString()).To(ContainSubstring("After=foobar.service barfoo.service"))
Expect(session.OutputToString()).To(ContainSubstring("Requires=foobar.service barfoo.service"))
})
It("podman generate systemd --new --name foo", func() { It("podman generate systemd --new --name foo", func() {
n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"})
n.WaitWithDefaultTimeout() n.WaitWithDefaultTimeout()