Merge pull request #9445 from jmguzik/no-header-info-for-systemd-generation

No header info for systemd generation
This commit is contained in:
OpenShift Merge Robot
2021-02-22 13:44:43 -05:00
committed by GitHub
14 changed files with 175 additions and 21 deletions

View File

@ -52,6 +52,7 @@ func init() {
flags.UintVarP(&systemdTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Stop timeout override") flags.UintVarP(&systemdTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Stop timeout override")
_ = systemdCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) _ = systemdCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one") flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one")
flags.BoolVarP(&systemdOptions.NoHeader, "no-header", "", false, "Skip header generation")
containerPrefixFlagName := "container-prefix" containerPrefixFlagName := "container-prefix"
flags.StringVar(&systemdOptions.ContainerPrefix, containerPrefixFlagName, "container", "Systemd unit name prefix for containers") flags.StringVar(&systemdOptions.ContainerPrefix, containerPrefixFlagName, "container", "Systemd unit name prefix for containers")

View File

@ -32,6 +32,10 @@ Use the name of the container for the start, stop, and description in the unit f
Using this flag will yield unit files that do not expect containers and pods to exist. Instead, new containers and pods are created based on their configuration files. The unit files are created best effort and may need to be further edited; please review the generated files carefully before using them in production. Using this flag will yield unit files that do not expect containers and pods to exist. Instead, new containers and pods are created based on their configuration files. The unit files are created best effort and may need to be further edited; please review the generated files carefully before using them in production.
#### **--no-header**
Do not generate the header including meta data such as the Podman version and the timestamp.
#### **--time**, **-t**=*value* #### **--time**, **-t**=*value*
Override the default stop timeout for the container with the given value. Override the default stop timeout for the container with the given value.

View File

@ -18,6 +18,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
query := struct { query := struct {
Name bool `schema:"useName"` Name bool `schema:"useName"`
New bool `schema:"new"` New bool `schema:"new"`
NoHeader bool `schema:"noHeader"`
RestartPolicy string `schema:"restartPolicy"` RestartPolicy string `schema:"restartPolicy"`
StopTimeout uint `schema:"stopTimeout"` StopTimeout uint `schema:"stopTimeout"`
ContainerPrefix string `schema:"containerPrefix"` ContainerPrefix string `schema:"containerPrefix"`
@ -41,6 +42,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
options := entities.GenerateSystemdOptions{ options := entities.GenerateSystemdOptions{
Name: query.Name, Name: query.Name,
New: query.New, New: query.New,
NoHeader: query.NoHeader,
RestartPolicy: query.RestartPolicy, RestartPolicy: query.RestartPolicy,
StopTimeout: &query.StopTimeout, StopTimeout: &query.StopTimeout,
ContainerPrefix: query.ContainerPrefix, ContainerPrefix: query.ContainerPrefix,

View File

@ -32,6 +32,11 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
// default: false // default: false
// description: Create a new container instead of starting an existing one. // description: Create a new container instead of starting an existing one.
// - in: query // - in: query
// name: noHeader
// type: boolean
// default: false
// description: Do not generate the header including the Podman version and the timestamp.
// - in: query
// name: time // name: time
// type: integer // type: integer
// default: 10 // default: 10

View File

@ -14,6 +14,8 @@ type SystemdOptions struct {
UseName *bool UseName *bool
// New - create a new container instead of starting a new one. // New - create a new container instead of starting a new one.
New *bool New *bool
// NoHeader - Removes autogenerated by Podman and timestamp if set to true
NoHeader *bool
// RestartPolicy - systemd restart policy. // RestartPolicy - systemd restart policy.
RestartPolicy *string RestartPolicy *string
// StopTimeout - time when stopping the container. // StopTimeout - time when stopping the container.

View File

@ -52,6 +52,22 @@ func (o *SystemdOptions) GetNew() bool {
return *o.New return *o.New
} }
// WithNoHeader
func (o *SystemdOptions) WithNoHeader(value bool) *SystemdOptions {
v := &value
o.NoHeader = v
return o
}
// GetNoHeader
func (o *SystemdOptions) GetNoHeader() bool {
var noHeader bool
if o.NoHeader == nil {
return noHeader
}
return *o.NoHeader
}
// WithRestartPolicy // WithRestartPolicy
func (o *SystemdOptions) WithRestartPolicy(value string) *SystemdOptions { func (o *SystemdOptions) WithRestartPolicy(value string) *SystemdOptions {
v := &value v := &value

View File

@ -18,6 +18,8 @@ type GenerateSystemdOptions 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
// NoHeader - skip header generation
NoHeader bool
} }
// GenerateSystemdReport // GenerateSystemdReport

View File

@ -8,7 +8,7 @@ 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) options := new(generate.SystemdOptions).WithUseName(opts.Name).WithContainerPrefix(opts.ContainerPrefix).WithNew(opts.New).WithNoHeader(opts.NoHeader)
options.WithPodPrefix(opts.PodPrefix).WithRestartPolicy(opts.RestartPolicy).WithSeparator(opts.Separator) options.WithPodPrefix(opts.PodPrefix).WithRestartPolicy(opts.RestartPolicy).WithSeparator(opts.Separator)
if to := opts.StopTimeout; to != nil { if to := opts.StopTimeout; to != nil {
options.WithStopTimeout(*opts.StopTimeout) options.WithStopTimeout(*opts.StopTimeout)

View File

@ -31,10 +31,12 @@ func validateRestartPolicy(restart string) error {
} }
const headerTemplate = `# {{{{.ServiceName}}}}.service const headerTemplate = `# {{{{.ServiceName}}}}.service
{{{{- if (eq .GenerateNoHeader false) }}}}
# autogenerated by Podman {{{{.PodmanVersion}}}} # autogenerated by Podman {{{{.PodmanVersion}}}}
{{{{- if .TimeStamp}}}} {{{{- if .TimeStamp}}}}
# {{{{.TimeStamp}}}} # {{{{.TimeStamp}}}}
{{{{- end}}}} {{{{- end}}}}
{{{{- end}}}}
[Unit] [Unit]
Description=Podman {{{{.ServiceName}}}}.service Description=Podman {{{{.ServiceName}}}}.service

View File

@ -65,7 +65,8 @@ type containerInfo struct {
ExecStop string ExecStop string
// ExecStopPost of the unit. // ExecStopPost of the unit.
ExecStopPost string ExecStopPost string
// Removes autogenerated by Podman and timestamp if set to true
GenerateNoHeader bool
// If not nil, the container is part of the pod. We can use the // If not nil, the container is part of the pod. We can use the
// podInfo to extract the relevant data. // podInfo to extract the relevant data.
Pod *podInfo Pod *podInfo
@ -292,10 +293,15 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
if info.PodmanVersion == "" { if info.PodmanVersion == "" {
info.PodmanVersion = version.Version.String() info.PodmanVersion = version.Version.String()
} }
if options.NoHeader {
info.GenerateNoHeader = true
info.GenerateTimestamp = false
}
if info.GenerateTimestamp { if info.GenerateTimestamp {
info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate)) info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate))
} }
// Sort the slices to assure a deterministic output. // Sort the slices to assure a deterministic output.
sort.Strings(info.BoundToServices) sort.Strings(info.BoundToServices)

View File

@ -37,9 +37,11 @@ func TestValidateRestartPolicyContainer(t *testing.T) {
} }
func TestCreateContainerSystemdUnit(t *testing.T) { func TestCreateContainerSystemdUnit(t *testing.T) {
goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service serviceInfo := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
# autogenerated by Podman CI `
headerInfo := `# autogenerated by Podman CI
`
goodIDContent := `
[Unit] [Unit]
Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
Documentation=man:podman-generate-systemd(1) Documentation=man:podman-generate-systemd(1)
@ -59,6 +61,8 @@ Type=forking
[Install] [Install]
WantedBy=multi-user.target default.target WantedBy=multi-user.target default.target
` `
goodID := serviceInfo + headerInfo + goodIDContent
goodIDNoHeaderInfo := serviceInfo + goodIDContent
goodName := `# container-foobar.service goodName := `# container-foobar.service
# autogenerated by Podman CI # autogenerated by Podman CI
@ -381,6 +385,7 @@ WantedBy=multi-user.target default.target
info containerInfo info containerInfo
want string want string
new bool new bool
noHeader bool
wantErr bool wantErr bool
}{ }{
@ -398,6 +403,23 @@ WantedBy=multi-user.target default.target
goodID, goodID,
false, false,
false, false,
false,
},
{"good with noHeader",
containerInfo{
Executable: "/usr/bin/podman",
ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
RestartPolicy: "always",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 22,
PodmanVersion: "CI",
EnvVariable: EnvVariable,
},
goodIDNoHeaderInfo,
false,
true,
false,
}, },
{"good with name", {"good with name",
containerInfo{ containerInfo{
@ -413,6 +435,7 @@ WantedBy=multi-user.target default.target
goodName, goodName,
false, false,
false, false,
false,
}, },
{"good with name and bound to", {"good with name and bound to",
containerInfo{ containerInfo{
@ -429,6 +452,7 @@ WantedBy=multi-user.target default.target
goodNameBoundTo, goodNameBoundTo,
false, false,
false, false,
false,
}, },
{"bad restart policy", {"bad restart policy",
containerInfo{ containerInfo{
@ -442,6 +466,7 @@ WantedBy=multi-user.target default.target
}, },
"", "",
false, false,
false,
true, true,
}, },
{"good with name and generic", {"good with name and generic",
@ -459,6 +484,7 @@ WantedBy=multi-user.target default.target
goodWithNameAndGeneric, goodWithNameAndGeneric,
true, true,
false, false,
false,
}, },
{"good with explicit short detach param", {"good with explicit short detach param",
containerInfo{ containerInfo{
@ -475,6 +501,7 @@ WantedBy=multi-user.target default.target
goodWithExplicitShortDetachParam, goodWithExplicitShortDetachParam,
true, true,
false, false,
false,
}, },
{"good with explicit short detach param and podInfo", {"good with explicit short detach param and podInfo",
containerInfo{ containerInfo{
@ -494,6 +521,7 @@ WantedBy=multi-user.target default.target
goodNameNewWithPodFile, goodNameNewWithPodFile,
true, true,
false, false,
false,
}, },
{"good with explicit full detach param", {"good with explicit full detach param",
containerInfo{ containerInfo{
@ -510,6 +538,7 @@ WantedBy=multi-user.target default.target
goodNameNewDetach, goodNameNewDetach,
true, true,
false, false,
false,
}, },
{"good with id and no param", {"good with id and no param",
containerInfo{ containerInfo{
@ -526,6 +555,7 @@ WantedBy=multi-user.target default.target
goodIDNew, goodIDNew,
true, true,
false, false,
false,
}, },
{"good with explicit detach=true param", {"good with explicit detach=true param",
containerInfo{ containerInfo{
@ -542,6 +572,7 @@ WantedBy=multi-user.target default.target
genGoodNewDetach("--detach=true"), genGoodNewDetach("--detach=true"),
true, true,
false, false,
false,
}, },
{"good with explicit detach=false param", {"good with explicit detach=false param",
containerInfo{ containerInfo{
@ -558,6 +589,7 @@ WantedBy=multi-user.target default.target
genGoodNewDetach("-d"), genGoodNewDetach("-d"),
true, true,
false, false,
false,
}, },
{"good with explicit detach=false param", {"good with explicit detach=false param",
containerInfo{ containerInfo{
@ -574,6 +606,7 @@ WantedBy=multi-user.target default.target
goodNameNewDetachFalseWithCmd, goodNameNewDetachFalseWithCmd,
true, true,
false, false,
false,
}, },
{"good with multiple detach=false params", {"good with multiple detach=false params",
containerInfo{ containerInfo{
@ -590,6 +623,7 @@ WantedBy=multi-user.target default.target
goodNameNewDetachFalseWithCmd, goodNameNewDetachFalseWithCmd,
true, true,
false, false,
false,
}, },
{"good with multiple shorthand params detach first", {"good with multiple shorthand params detach first",
containerInfo{ containerInfo{
@ -606,6 +640,7 @@ WantedBy=multi-user.target default.target
genGoodNewDetach("-dti"), genGoodNewDetach("-dti"),
true, true,
false, false,
false,
}, },
{"good with multiple shorthand params detach last", {"good with multiple shorthand params detach last",
containerInfo{ containerInfo{
@ -622,6 +657,7 @@ WantedBy=multi-user.target default.target
genGoodNewDetach("-tid"), genGoodNewDetach("-tid"),
true, true,
false, false,
false,
}, },
{"good with root flags", {"good with root flags",
containerInfo{ containerInfo{
@ -638,6 +674,7 @@ WantedBy=multi-user.target default.target
goodNewRootFlags, goodNewRootFlags,
true, true,
false, false,
false,
}, },
{"good with container create", {"good with container create",
containerInfo{ containerInfo{
@ -654,6 +691,7 @@ WantedBy=multi-user.target default.target
goodContainerCreate, goodContainerCreate,
true, true,
false, false,
false,
}, },
{"good with journald log tag (see #9034)", {"good with journald log tag (see #9034)",
containerInfo{ containerInfo{
@ -670,6 +708,7 @@ WantedBy=multi-user.target default.target
goodNewWithJournaldTag, goodNewWithJournaldTag,
true, true,
false, false,
false,
}, },
{"good with special chars", {"good with special chars",
containerInfo{ containerInfo{
@ -686,6 +725,7 @@ WantedBy=multi-user.target default.target
goodNewWithSpecialChars, goodNewWithSpecialChars,
true, true,
false, false,
false,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -693,6 +733,7 @@ WantedBy=multi-user.target default.target
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
opts := entities.GenerateSystemdOptions{ opts := entities.GenerateSystemdOptions{
New: test.new, New: test.new,
NoHeader: test.noHeader,
} }
got, err := executeContainerTemplate(&test.info, opts) got, err := executeContainerTemplate(&test.info, opts)
if (err != nil) != test.wantErr { if (err != nil) != test.wantErr {

View File

@ -70,6 +70,8 @@ type podInfo struct {
ExecStop string ExecStop string
// ExecStopPost of the unit. // ExecStopPost of the unit.
ExecStopPost string ExecStopPost string
// Removes autogenerated by Podman and timestamp if set to true
GenerateNoHeader bool
} }
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}}}}
@ -319,6 +321,12 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
if info.PodmanVersion == "" { if info.PodmanVersion == "" {
info.PodmanVersion = version.Version.String() info.PodmanVersion = version.Version.String()
} }
if options.NoHeader {
info.GenerateNoHeader = true
info.GenerateTimestamp = false
}
if info.GenerateTimestamp { if info.GenerateTimestamp {
info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate)) info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate))
} }

View File

@ -37,9 +37,11 @@ func TestValidateRestartPolicyPod(t *testing.T) {
} }
func TestCreatePodSystemdUnit(t *testing.T) { func TestCreatePodSystemdUnit(t *testing.T) {
podGood := `# pod-123abc.service serviceInfo := `# pod-123abc.service
# autogenerated by Podman CI `
headerInfo := `# autogenerated by Podman CI
`
podContent := `
[Unit] [Unit]
Description=Podman pod-123abc.service Description=Podman pod-123abc.service
Documentation=man:podman-generate-systemd(1) Documentation=man:podman-generate-systemd(1)
@ -61,6 +63,8 @@ Type=forking
[Install] [Install]
WantedBy=multi-user.target default.target WantedBy=multi-user.target default.target
` `
podGood := serviceInfo + headerInfo + podContent
podGoodNoHeaderInfo := serviceInfo + podContent
podGoodNamedNew := `# pod-123abc.service podGoodNamedNew := `# pod-123abc.service
# autogenerated by Podman CI # autogenerated by Podman CI
@ -175,6 +179,7 @@ WantedBy=multi-user.target default.target
info podInfo info podInfo
want string want string
new bool new bool
noHeader bool
wantErr bool wantErr bool
}{ }{
{"pod", {"pod",
@ -192,6 +197,24 @@ WantedBy=multi-user.target default.target
podGood, podGood,
false, false,
false, false,
false,
},
{"pod noHeader",
podInfo{
Executable: "/usr/bin/podman",
ServiceName: "pod-123abc",
InfraNameOrID: "jadda-jadda-infra",
RestartPolicy: "always",
PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
StopTimeout: 42,
PodmanVersion: "CI",
RequiredServices: []string{"container-1", "container-2"},
CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "bar=arg with space"},
},
podGoodNoHeaderInfo,
false,
true,
false,
}, },
{"pod with root args", {"pod with root args",
podInfo{ podInfo{
@ -208,6 +231,7 @@ WantedBy=multi-user.target default.target
podGood, podGood,
false, false,
false, false,
false,
}, },
{"pod --new", {"pod --new",
podInfo{ podInfo{
@ -224,6 +248,7 @@ WantedBy=multi-user.target default.target
podGoodNamedNew, podGoodNamedNew,
true, true,
false, false,
false,
}, },
{"pod --new with root args", {"pod --new with root args",
podInfo{ podInfo{
@ -240,6 +265,7 @@ WantedBy=multi-user.target default.target
podGoodNamedNewWithRootArgs, podGoodNamedNewWithRootArgs,
true, true,
false, false,
false,
}, },
{"pod --new with --replace=false", {"pod --new with --replace=false",
podInfo{ podInfo{
@ -256,6 +282,7 @@ WantedBy=multi-user.target default.target
podGoodNamedNewWithReplaceFalse, podGoodNamedNewWithReplaceFalse,
true, true,
false, false,
false,
}, },
{"pod --new with double curly braces", {"pod --new with double curly braces",
podInfo{ podInfo{
@ -272,6 +299,7 @@ WantedBy=multi-user.target default.target
podNewLabelWithCurlyBraces, podNewLabelWithCurlyBraces,
true, true,
false, false,
false,
}, },
} }
@ -280,6 +308,7 @@ WantedBy=multi-user.target default.target
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
opts := entities.GenerateSystemdOptions{ opts := entities.GenerateSystemdOptions{
New: test.new, New: test.new,
NoHeader: test.noHeader,
} }
got, err := executePodTemplate(&test.info, opts) got, err := executePodTemplate(&test.info, opts)
if (err != nil) != test.wantErr { if (err != nil) != test.wantErr {

View File

@ -62,6 +62,42 @@ var _ = Describe("Podman generate systemd", func() {
Expect(session.ErrorToString()).To(ContainSubstring("bogus is not a valid restart policy")) Expect(session.ErrorToString()).To(ContainSubstring("bogus is not a valid restart policy"))
}) })
It("podman generate systemd with --no-header=true", func() {
session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"generate", "systemd", "foobar", "--no-header=true"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).NotTo(ContainSubstring("autogenerated by"))
})
It("podman generate systemd with --no-header", func() {
session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"generate", "systemd", "foobar", "--no-header"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).NotTo(ContainSubstring("autogenerated by"))
})
It("podman generate systemd with --no-header=false", func() {
session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"generate", "systemd", "foobar", "--no-header=false"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("autogenerated by"))
})
It("podman generate systemd good timeout value", func() { It("podman generate systemd good timeout value", func() {
session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"}) session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()