mirror of
https://github.com/containers/podman.git
synced 2025-06-19 16:33:24 +08:00
--no-header flag implementation for generate systemd
Signed-off-by: Jakub Guzik <jakubmguzik@gmail.com>
This commit is contained in:
@ -52,6 +52,7 @@ func init() {
|
||||
flags.UintVarP(&systemdTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Stop timeout override")
|
||||
_ = systemdCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
|
||||
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"
|
||||
flags.StringVar(&systemdOptions.ContainerPrefix, containerPrefixFlagName, "container", "Systemd unit name prefix for containers")
|
||||
|
@ -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.
|
||||
|
||||
#### **--no-header**
|
||||
|
||||
Do not generate the header including meta data such as the Podman version and the timestamp.
|
||||
|
||||
#### **--time**, **-t**=*value*
|
||||
|
||||
Override the default stop timeout for the container with the given value.
|
||||
|
@ -18,6 +18,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
|
||||
query := struct {
|
||||
Name bool `schema:"useName"`
|
||||
New bool `schema:"new"`
|
||||
NoHeader bool `schema:"noHeader"`
|
||||
RestartPolicy string `schema:"restartPolicy"`
|
||||
StopTimeout uint `schema:"stopTimeout"`
|
||||
ContainerPrefix string `schema:"containerPrefix"`
|
||||
@ -41,6 +42,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
|
||||
options := entities.GenerateSystemdOptions{
|
||||
Name: query.Name,
|
||||
New: query.New,
|
||||
NoHeader: query.NoHeader,
|
||||
RestartPolicy: query.RestartPolicy,
|
||||
StopTimeout: &query.StopTimeout,
|
||||
ContainerPrefix: query.ContainerPrefix,
|
||||
|
@ -32,6 +32,11 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
|
||||
// default: false
|
||||
// description: Create a new container instead of starting an existing one.
|
||||
// - 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
|
||||
// type: integer
|
||||
// default: 10
|
||||
|
@ -14,6 +14,8 @@ type SystemdOptions struct {
|
||||
UseName *bool
|
||||
// New - create a new container instead of starting a new one.
|
||||
New *bool
|
||||
// NoHeader - Removes autogenerated by Podman and timestamp if set to true
|
||||
NoHeader *bool
|
||||
// RestartPolicy - systemd restart policy.
|
||||
RestartPolicy *string
|
||||
// StopTimeout - time when stopping the container.
|
||||
|
@ -52,6 +52,22 @@ func (o *SystemdOptions) GetNew() bool {
|
||||
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
|
||||
func (o *SystemdOptions) WithRestartPolicy(value string) *SystemdOptions {
|
||||
v := &value
|
||||
|
@ -18,6 +18,8 @@ type GenerateSystemdOptions struct {
|
||||
PodPrefix string
|
||||
// Separator - systemd unit name separator between name/id and prefix
|
||||
Separator string
|
||||
// NoHeader - skip header generation
|
||||
NoHeader bool
|
||||
}
|
||||
|
||||
// GenerateSystemdReport
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
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)
|
||||
if to := opts.StopTimeout; to != nil {
|
||||
options.WithStopTimeout(*opts.StopTimeout)
|
||||
|
@ -31,10 +31,12 @@ func validateRestartPolicy(restart string) error {
|
||||
}
|
||||
|
||||
const headerTemplate = `# {{{{.ServiceName}}}}.service
|
||||
{{{{- if (eq .GenerateNoHeader false) }}}}
|
||||
# autogenerated by Podman {{{{.PodmanVersion}}}}
|
||||
{{{{- if .TimeStamp}}}}
|
||||
# {{{{.TimeStamp}}}}
|
||||
{{{{- end}}}}
|
||||
{{{{- end}}}}
|
||||
|
||||
[Unit]
|
||||
Description=Podman {{{{.ServiceName}}}}.service
|
||||
|
@ -65,7 +65,8 @@ type containerInfo struct {
|
||||
ExecStop string
|
||||
// ExecStopPost of the unit.
|
||||
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
|
||||
// podInfo to extract the relevant data.
|
||||
Pod *podInfo
|
||||
@ -292,10 +293,15 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
|
||||
if info.PodmanVersion == "" {
|
||||
info.PodmanVersion = version.Version.String()
|
||||
}
|
||||
|
||||
if options.NoHeader {
|
||||
info.GenerateNoHeader = true
|
||||
info.GenerateTimestamp = false
|
||||
}
|
||||
|
||||
if info.GenerateTimestamp {
|
||||
info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate))
|
||||
}
|
||||
|
||||
// Sort the slices to assure a deterministic output.
|
||||
sort.Strings(info.BoundToServices)
|
||||
|
||||
|
@ -37,9 +37,11 @@ func TestValidateRestartPolicyContainer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateContainerSystemdUnit(t *testing.T) {
|
||||
goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||
# autogenerated by Podman CI
|
||||
|
||||
serviceInfo := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||
`
|
||||
headerInfo := `# autogenerated by Podman CI
|
||||
`
|
||||
goodIDContent := `
|
||||
[Unit]
|
||||
Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
@ -59,6 +61,8 @@ Type=forking
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
goodID := serviceInfo + headerInfo + goodIDContent
|
||||
goodIDNoHeaderInfo := serviceInfo + goodIDContent
|
||||
|
||||
goodName := `# container-foobar.service
|
||||
# autogenerated by Podman CI
|
||||
@ -377,11 +381,12 @@ Type=forking
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
tests := []struct {
|
||||
name string
|
||||
info containerInfo
|
||||
want string
|
||||
new bool
|
||||
wantErr bool
|
||||
name string
|
||||
info containerInfo
|
||||
want string
|
||||
new bool
|
||||
noHeader bool
|
||||
wantErr bool
|
||||
}{
|
||||
|
||||
{"good with id",
|
||||
@ -398,6 +403,23 @@ WantedBy=multi-user.target default.target
|
||||
goodID,
|
||||
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",
|
||||
containerInfo{
|
||||
@ -413,6 +435,7 @@ WantedBy=multi-user.target default.target
|
||||
goodName,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with name and bound to",
|
||||
containerInfo{
|
||||
@ -429,6 +452,7 @@ WantedBy=multi-user.target default.target
|
||||
goodNameBoundTo,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"bad restart policy",
|
||||
containerInfo{
|
||||
@ -442,6 +466,7 @@ WantedBy=multi-user.target default.target
|
||||
},
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{"good with name and generic",
|
||||
@ -459,6 +484,7 @@ WantedBy=multi-user.target default.target
|
||||
goodWithNameAndGeneric,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with explicit short detach param",
|
||||
containerInfo{
|
||||
@ -475,6 +501,7 @@ WantedBy=multi-user.target default.target
|
||||
goodWithExplicitShortDetachParam,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with explicit short detach param and podInfo",
|
||||
containerInfo{
|
||||
@ -494,6 +521,7 @@ WantedBy=multi-user.target default.target
|
||||
goodNameNewWithPodFile,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with explicit full detach param",
|
||||
containerInfo{
|
||||
@ -510,6 +538,7 @@ WantedBy=multi-user.target default.target
|
||||
goodNameNewDetach,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with id and no param",
|
||||
containerInfo{
|
||||
@ -526,6 +555,7 @@ WantedBy=multi-user.target default.target
|
||||
goodIDNew,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with explicit detach=true param",
|
||||
containerInfo{
|
||||
@ -542,6 +572,7 @@ WantedBy=multi-user.target default.target
|
||||
genGoodNewDetach("--detach=true"),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with explicit detach=false param",
|
||||
containerInfo{
|
||||
@ -558,6 +589,7 @@ WantedBy=multi-user.target default.target
|
||||
genGoodNewDetach("-d"),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with explicit detach=false param",
|
||||
containerInfo{
|
||||
@ -574,6 +606,7 @@ WantedBy=multi-user.target default.target
|
||||
goodNameNewDetachFalseWithCmd,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with multiple detach=false params",
|
||||
containerInfo{
|
||||
@ -590,6 +623,7 @@ WantedBy=multi-user.target default.target
|
||||
goodNameNewDetachFalseWithCmd,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with multiple shorthand params detach first",
|
||||
containerInfo{
|
||||
@ -606,6 +640,7 @@ WantedBy=multi-user.target default.target
|
||||
genGoodNewDetach("-dti"),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with multiple shorthand params detach last",
|
||||
containerInfo{
|
||||
@ -622,6 +657,7 @@ WantedBy=multi-user.target default.target
|
||||
genGoodNewDetach("-tid"),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with root flags",
|
||||
containerInfo{
|
||||
@ -638,6 +674,7 @@ WantedBy=multi-user.target default.target
|
||||
goodNewRootFlags,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with container create",
|
||||
containerInfo{
|
||||
@ -654,6 +691,7 @@ WantedBy=multi-user.target default.target
|
||||
goodContainerCreate,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with journald log tag (see #9034)",
|
||||
containerInfo{
|
||||
@ -670,6 +708,7 @@ WantedBy=multi-user.target default.target
|
||||
goodNewWithJournaldTag,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"good with special chars",
|
||||
containerInfo{
|
||||
@ -686,13 +725,15 @@ WantedBy=multi-user.target default.target
|
||||
goodNewWithSpecialChars,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
test := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
opts := entities.GenerateSystemdOptions{
|
||||
New: test.new,
|
||||
New: test.new,
|
||||
NoHeader: test.noHeader,
|
||||
}
|
||||
got, err := executeContainerTemplate(&test.info, opts)
|
||||
if (err != nil) != test.wantErr {
|
||||
|
@ -70,6 +70,8 @@ type podInfo struct {
|
||||
ExecStop string
|
||||
// ExecStopPost of the unit.
|
||||
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}}}}
|
||||
@ -319,6 +321,12 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
|
||||
if info.PodmanVersion == "" {
|
||||
info.PodmanVersion = version.Version.String()
|
||||
}
|
||||
|
||||
if options.NoHeader {
|
||||
info.GenerateNoHeader = true
|
||||
info.GenerateTimestamp = false
|
||||
}
|
||||
|
||||
if info.GenerateTimestamp {
|
||||
info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate))
|
||||
}
|
||||
|
@ -37,9 +37,11 @@ func TestValidateRestartPolicyPod(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePodSystemdUnit(t *testing.T) {
|
||||
podGood := `# pod-123abc.service
|
||||
# autogenerated by Podman CI
|
||||
|
||||
serviceInfo := `# pod-123abc.service
|
||||
`
|
||||
headerInfo := `# autogenerated by Podman CI
|
||||
`
|
||||
podContent := `
|
||||
[Unit]
|
||||
Description=Podman pod-123abc.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
@ -61,6 +63,8 @@ Type=forking
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
podGood := serviceInfo + headerInfo + podContent
|
||||
podGoodNoHeaderInfo := serviceInfo + podContent
|
||||
|
||||
podGoodNamedNew := `# pod-123abc.service
|
||||
# autogenerated by Podman CI
|
||||
@ -171,11 +175,12 @@ WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
info podInfo
|
||||
want string
|
||||
new bool
|
||||
wantErr bool
|
||||
name string
|
||||
info podInfo
|
||||
want string
|
||||
new bool
|
||||
noHeader bool
|
||||
wantErr bool
|
||||
}{
|
||||
{"pod",
|
||||
podInfo{
|
||||
@ -192,6 +197,24 @@ WantedBy=multi-user.target default.target
|
||||
podGood,
|
||||
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",
|
||||
podInfo{
|
||||
@ -208,6 +231,7 @@ WantedBy=multi-user.target default.target
|
||||
podGood,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"pod --new",
|
||||
podInfo{
|
||||
@ -224,6 +248,7 @@ WantedBy=multi-user.target default.target
|
||||
podGoodNamedNew,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"pod --new with root args",
|
||||
podInfo{
|
||||
@ -240,6 +265,7 @@ WantedBy=multi-user.target default.target
|
||||
podGoodNamedNewWithRootArgs,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"pod --new with --replace=false",
|
||||
podInfo{
|
||||
@ -256,6 +282,7 @@ WantedBy=multi-user.target default.target
|
||||
podGoodNamedNewWithReplaceFalse,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"pod --new with double curly braces",
|
||||
podInfo{
|
||||
@ -272,6 +299,7 @@ WantedBy=multi-user.target default.target
|
||||
podNewLabelWithCurlyBraces,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
@ -279,7 +307,8 @@ WantedBy=multi-user.target default.target
|
||||
test := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
opts := entities.GenerateSystemdOptions{
|
||||
New: test.new,
|
||||
New: test.new,
|
||||
NoHeader: test.noHeader,
|
||||
}
|
||||
got, err := executePodTemplate(&test.info, opts)
|
||||
if (err != nil) != test.wantErr {
|
||||
|
@ -62,6 +62,42 @@ var _ = Describe("Podman generate systemd", func() {
|
||||
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() {
|
||||
session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
|
Reference in New Issue
Block a user