mirror of https://github.com/containers/podman.git
149 lines
4.4 KiB
Go
149 lines
4.4 KiB
Go
package systemdgen
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/containers/libpod/version"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ContainerInfo contains data required for generating a container's systemd
|
|
// unit file.
|
|
type ContainerInfo struct {
|
|
// ServiceName of the systemd service.
|
|
ServiceName string
|
|
// Name or ID of the container.
|
|
ContainerName string
|
|
// InfraContainer of the pod.
|
|
InfraContainer string
|
|
// StopTimeout sets the timeout Podman waits before killing the container
|
|
// during service stop.
|
|
StopTimeout int
|
|
// RestartPolicy of the systemd unit (e.g., no, on-failure, always).
|
|
RestartPolicy string
|
|
// PIDFile of the service. Required for forking services. Must point to the
|
|
// PID of the associated conmon process.
|
|
PIDFile string
|
|
// GenerateTimestamp, if set the generated unit file has a time stamp.
|
|
GenerateTimestamp bool
|
|
// BoundToServices are the services this service binds to. Note that this
|
|
// service runs after them.
|
|
BoundToServices []string
|
|
// RequiredServices are services this service requires. Note that this
|
|
// service runs before them.
|
|
RequiredServices []string
|
|
// PodmanVersion for the header. Will be set internally. Will be auto-filled
|
|
// if left empty.
|
|
PodmanVersion string
|
|
// Executable is the path to the podman executable. Will be auto-filled if
|
|
// left empty.
|
|
Executable string
|
|
// TimeStamp at the time of creating the unit file. Will be set internally.
|
|
TimeStamp string
|
|
}
|
|
|
|
var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"}
|
|
|
|
// validateRestartPolicy checks that the user-provided policy is valid.
|
|
func validateRestartPolicy(restart string) error {
|
|
for _, i := range restartPolicies {
|
|
if i == restart {
|
|
return nil
|
|
}
|
|
}
|
|
return errors.Errorf("%s is not a valid restart policy", restart)
|
|
}
|
|
|
|
const containerTemplate = `# {{.ServiceName}}.service
|
|
# autogenerated by Podman {{.PodmanVersion}}
|
|
{{- if .TimeStamp}}
|
|
# {{.TimeStamp}}
|
|
{{- end}}
|
|
|
|
[Unit]
|
|
Description=Podman {{.ServiceName}}.service
|
|
Documentation=man:podman-generate-systemd(1)
|
|
{{- if .BoundToServices}}
|
|
RefuseManualStart=yes
|
|
RefuseManualStop=yes
|
|
BindsTo={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
|
|
After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
|
|
{{- end}}
|
|
{{- if .RequiredServices}}
|
|
Requires={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
|
|
Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
|
|
{{- end}}
|
|
|
|
[Service]
|
|
Restart={{.RestartPolicy}}
|
|
ExecStart={{.Executable}} start {{.ContainerName}}
|
|
ExecStop={{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerName}}
|
|
KillMode=none
|
|
Type=forking
|
|
PIDFile={{.PIDFile}}
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target`
|
|
|
|
// CreateContainerSystemdUnit creates a systemd unit file for a container.
|
|
func CreateContainerSystemdUnit(info *ContainerInfo, generateFiles bool) (string, error) {
|
|
if err := validateRestartPolicy(info.RestartPolicy); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Make sure the executable is set.
|
|
if info.Executable == "" {
|
|
executable, err := os.Executable()
|
|
if err != nil {
|
|
executable = "/usr/bin/podman"
|
|
logrus.Warnf("Could not obtain podman executable location, using default %s", executable)
|
|
}
|
|
info.Executable = executable
|
|
}
|
|
|
|
if info.PodmanVersion == "" {
|
|
info.PodmanVersion = version.Version
|
|
}
|
|
if info.GenerateTimestamp {
|
|
info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate))
|
|
}
|
|
|
|
// Sort the slices to assure a deterministic output.
|
|
sort.Strings(info.RequiredServices)
|
|
sort.Strings(info.BoundToServices)
|
|
|
|
// Generate the template and compile it.
|
|
templ, err := template.New("systemd_service_file").Parse(containerTemplate)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error parsing systemd service template")
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := templ.Execute(&buf, info); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !generateFiles {
|
|
return buf.String(), nil
|
|
}
|
|
|
|
buf.WriteByte('\n')
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error getting current working directory")
|
|
}
|
|
path := filepath.Join(cwd, fmt.Sprintf("%s.service", info.ServiceName))
|
|
if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil {
|
|
return "", errors.Wrap(err, "error generating systemd unit")
|
|
}
|
|
return path, nil
|
|
}
|