mirror of
https://github.com/containers/podman.git
synced 2025-05-17 23:26:08 +08:00
generate systemd: support pods and geneartig files
Support generating systemd unit files for a pod. Podman generates one unit file for the pod including the PID file for the infra container's conmon process and one unit file for each container (excluding the infra container). Note that this change implies refactorings in the `pkg/systemdgen` API. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
@ -156,6 +156,7 @@ type GenerateKubeValues struct {
|
||||
type GenerateSystemdValues struct {
|
||||
PodmanCommand
|
||||
Name bool
|
||||
Files bool
|
||||
RestartPolicy string
|
||||
StopTimeout int
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||
"github.com/containers/libpod/pkg/adapter"
|
||||
"github.com/containers/libpod/pkg/systemdgen"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -40,7 +39,10 @@ func init() {
|
||||
containerSystemdCommand.SetHelpTemplate(HelpTemplate())
|
||||
containerSystemdCommand.SetUsageTemplate(UsageTemplate())
|
||||
flags := containerSystemdCommand.Flags()
|
||||
flags.BoolVarP(&containerSystemdCommand.Name, "name", "n", false, "use the container name instead of ID")
|
||||
flags.BoolVarP(&containerSystemdCommand.Name, "name", "n", false, "use the container/pod name instead of ID")
|
||||
if !remoteclient {
|
||||
flags.BoolVarP(&containerSystemdCommand.Files, "files", "f", false, "generate files instead of printing to stdout")
|
||||
}
|
||||
flags.IntVarP(&containerSystemdCommand.StopTimeout, "timeout", "t", -1, "stop timeout override")
|
||||
flags.StringVar(&containerSystemdCommand.RestartPolicy, "restart-policy", "on-failure", "applicable systemd restart-policy")
|
||||
}
|
||||
@ -56,10 +58,6 @@ func generateSystemdCmd(c *cliconfig.GenerateSystemdValues) error {
|
||||
if c.Flag("timeout").Changed && c.StopTimeout < 0 {
|
||||
return errors.New("timeout value must be 0 or greater")
|
||||
}
|
||||
// Make sure the input restart policy is valid
|
||||
if err := systemdgen.ValidateRestartPolicy(c.RestartPolicy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unit, err := runtime.GenerateSystemd(c)
|
||||
if err != nil {
|
||||
|
@ -4,17 +4,20 @@
|
||||
podman-generate-systemd- Generate Systemd Unit file
|
||||
|
||||
## SYNOPSIS
|
||||
**podman generate systemd** [*options*] *container*
|
||||
**podman generate systemd** [*options*] *container|pod*
|
||||
|
||||
## DESCRIPTION
|
||||
**podman generate systemd** will create a Systemd unit file that can be used to control a container. The
|
||||
command will dynamically create the unit file and output it to stdout where it can be piped by the user
|
||||
to a file. The options can be used to influence the results of the output as well.
|
||||
**podman generate systemd** will create a systemd unit file that can be used to control a container or pod.
|
||||
By default, the command will print the content of the unit files to stdout.
|
||||
|
||||
Note that this command is not supported for the remote client.
|
||||
|
||||
## OPTIONS:
|
||||
|
||||
**--files**, **-f**
|
||||
|
||||
Generate files instead of printing to stdout. The generated files are named {container,pod}-{ID,name}.service and will be placed in the current working directory.
|
||||
|
||||
**--name**, **-n**
|
||||
|
||||
Use the name of the container for the start, stop, and description in the unit file
|
||||
@ -28,41 +31,66 @@ Set the systemd restart policy. The restart-policy must be one of: "no", "on-su
|
||||
"on-watchdog", "on-abort", or "always". The default policy is *on-failure*.
|
||||
|
||||
## Examples
|
||||
Create a systemd unit file for a container running nginx:
|
||||
|
||||
Create and print a systemd unit file for a container running nginx with an *always* restart policy and 1-second timeout to stdout.
|
||||
```
|
||||
$ sudo podman generate systemd nginx
|
||||
$ podman create --name nginx nginx:latest
|
||||
$ podman generate systemd --restart-policy=always -t 1 nginx
|
||||
# container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service
|
||||
# autogenerated by Podman 1.5.2
|
||||
# Wed Aug 21 09:46:45 CEST 2019
|
||||
|
||||
[Unit]
|
||||
Description=c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc Podman Container
|
||||
Description=Podman container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
||||
ExecStop=/usr/bin/podman stop -t 10 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/podman start de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6
|
||||
ExecStop=/usr/bin/podman stop -t 1 de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid
|
||||
PIDFile=/run/user/1000/overlay-containers/de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6/userdata/conmon.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Create a systemd unit file for a container running nginx with an *always* restart policy and 1-second timeout.
|
||||
Create systemd unit files for a pod with two simple alpine containers. Note that these container services cannot be started or stopped individually via `systemctl`; they are managed by the pod service. You can still use `systemctl status` or journalctl to examine them.
|
||||
```
|
||||
$ sudo podman generate systemd --restart-policy=always -t 1 nginx
|
||||
$ podman pod create --name systemd-pod
|
||||
$ podman create --pod systemd-pod alpine top
|
||||
$ podman create --pod systemd-pod alpine top
|
||||
$ podman generate systemd --files --name systemd-pod
|
||||
/home/user/pod-systemd-pod.service
|
||||
/home/user/container-amazing_chandrasekhar.service
|
||||
/home/user/container-jolly_shtern.service
|
||||
$ cat pod-systemd-pod.service
|
||||
# pod-systemd-pod.service
|
||||
# autogenerated by Podman 1.5.2
|
||||
# Wed Aug 21 09:52:37 CEST 2019
|
||||
|
||||
[Unit]
|
||||
Description=c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc Podman Container
|
||||
Description=Podman pod-systemd-pod.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
Requires=container-amazing_chandrasekhar.service container-jolly_shtern.service
|
||||
Before=container-amazing_chandrasekhar.service container-jolly_shtern.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
||||
ExecStop=/usr/bin/podman stop -t 1 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
||||
Restart=on-failure
|
||||
ExecStart=/usr/bin/podman start 77a818221650-infra
|
||||
ExecStop=/usr/bin/podman stop -t 10 77a818221650-infra
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid
|
||||
PIDFile=/run/user/1000/overlay-containers/ccfd5c71a088768774ca7bd05888d55cc287698dde06f475c8b02f696a25adcd/userdata/conmon.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-container(1)
|
||||
podman(1), podman-container(1), systemctl(1), systemd.unit(5), systemd.service(5)
|
||||
|
||||
## HISTORY
|
||||
August 2019, Updated with pod support by Valentin Rothberg (rothberg at redhat dot com)
|
||||
April 2019, Originally compiled by Brent Baude (bbaude at redhat dot com)
|
||||
|
@ -11,10 +11,10 @@ The generate command will create structured output (like YAML) based on a contai
|
||||
|
||||
## COMMANDS
|
||||
|
||||
| Command | Man Page | Description |
|
||||
|---------|------------------------------------------------------------|------------------------------------------------------------------------------------|
|
||||
| kube | [podman-generate-kube(1)](podman-generate-kube.1.md) | Generate Kubernetes YAML based on a pod or container. |
|
||||
| systemd | [podman-generate-systemd(1)](podman-generate-systemd.1.md) | Generate a systemd unit file for a container. Not supported for the remote client. |
|
||||
| Command | Man Page | Description |
|
||||
|---------|------------------------------------------------------------|-------------------------------------------------------------------------------------|
|
||||
| kube | [podman-generate-kube(1)](podman-generate-kube.1.md) | Generate Kubernetes YAML based on a pod or container. |
|
||||
| systemd | [podman-generate-systemd(1)](podman-generate-systemd.1.md) | Generate systemd unit file(s) for a container. Not supported for the remote client. |
|
||||
|
||||
|
||||
## SEE ALSO
|
||||
|
@ -16,14 +16,30 @@ type containerNode struct {
|
||||
dependedOn []*containerNode
|
||||
}
|
||||
|
||||
type containerGraph struct {
|
||||
// ContainerGraph is a dependency graph based on a set of containers.
|
||||
type ContainerGraph struct {
|
||||
nodes map[string]*containerNode
|
||||
noDepNodes []*containerNode
|
||||
notDependedOnNodes map[string]*containerNode
|
||||
}
|
||||
|
||||
func buildContainerGraph(ctrs []*Container) (*containerGraph, error) {
|
||||
graph := new(containerGraph)
|
||||
// DependencyMap returns the dependency graph as map with the key being a
|
||||
// container and the value being the containers the key depends on.
|
||||
func (cg *ContainerGraph) DependencyMap() (dependencies map[*Container][]*Container) {
|
||||
dependencies = make(map[*Container][]*Container)
|
||||
for _, node := range cg.nodes {
|
||||
dependsOn := make([]*Container, len(node.dependsOn))
|
||||
for i, d := range node.dependsOn {
|
||||
dependsOn[i] = d.container
|
||||
}
|
||||
dependencies[node.container] = dependsOn
|
||||
}
|
||||
return dependencies
|
||||
}
|
||||
|
||||
// BuildContainerGraph builds a dependency graph based on the container slice.
|
||||
func BuildContainerGraph(ctrs []*Container) (*ContainerGraph, error) {
|
||||
graph := new(ContainerGraph)
|
||||
graph.nodes = make(map[string]*containerNode)
|
||||
graph.notDependedOnNodes = make(map[string]*containerNode)
|
||||
|
||||
@ -78,7 +94,7 @@ func buildContainerGraph(ctrs []*Container) (*containerGraph, error) {
|
||||
// Detect cycles in a container graph using Tarjan's strongly connected
|
||||
// components algorithm
|
||||
// Return true if a cycle is found, false otherwise
|
||||
func detectCycles(graph *containerGraph) (bool, error) {
|
||||
func detectCycles(graph *ContainerGraph) (bool, error) {
|
||||
type nodeInfo struct {
|
||||
index int
|
||||
lowLink int
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func TestBuildContainerGraphNoCtrsIsEmpty(t *testing.T) {
|
||||
graph, err := buildContainerGraph([]*Container{})
|
||||
graph, err := BuildContainerGraph([]*Container{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(graph.nodes))
|
||||
assert.Equal(t, 0, len(graph.noDepNodes))
|
||||
@ -24,7 +24,7 @@ func TestBuildContainerGraphOneCtr(t *testing.T) {
|
||||
ctr1, err := getTestCtr1(manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
graph, err := buildContainerGraph([]*Container{ctr1})
|
||||
graph, err := BuildContainerGraph([]*Container{ctr1})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(graph.nodes))
|
||||
assert.Equal(t, 1, len(graph.noDepNodes))
|
||||
@ -49,7 +49,7 @@ func TestBuildContainerGraphTwoCtrNoEdge(t *testing.T) {
|
||||
ctr2, err := getTestCtr2(manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2})
|
||||
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(graph.nodes))
|
||||
assert.Equal(t, 2, len(graph.noDepNodes))
|
||||
@ -76,7 +76,7 @@ func TestBuildContainerGraphTwoCtrOneEdge(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
ctr2.config.UserNsCtr = ctr1.config.ID
|
||||
|
||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2})
|
||||
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(graph.nodes))
|
||||
assert.Equal(t, 1, len(graph.noDepNodes))
|
||||
@ -99,7 +99,7 @@ func TestBuildContainerGraphTwoCtrCycle(t *testing.T) {
|
||||
ctr2.config.UserNsCtr = ctr1.config.ID
|
||||
ctr1.config.NetNsCtr = ctr2.config.ID
|
||||
|
||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2})
|
||||
_, err = BuildContainerGraph([]*Container{ctr1, ctr2})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ func TestBuildContainerGraphThreeCtrNoEdges(t *testing.T) {
|
||||
ctr3, err := getTestCtrN("3", manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(graph.nodes))
|
||||
assert.Equal(t, 3, len(graph.noDepNodes))
|
||||
@ -150,7 +150,7 @@ func TestBuildContainerGraphThreeContainersTwoInCycle(t *testing.T) {
|
||||
ctr1.config.UserNsCtr = ctr2.config.ID
|
||||
ctr2.config.IPCNsCtr = ctr1.config.ID
|
||||
|
||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ func TestBuildContainerGraphThreeContainersCycle(t *testing.T) {
|
||||
ctr2.config.IPCNsCtr = ctr3.config.ID
|
||||
ctr3.config.NetNsCtr = ctr1.config.ID
|
||||
|
||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@ -190,7 +190,7 @@ func TestBuildContainerGraphThreeContainersNoCycle(t *testing.T) {
|
||||
ctr1.config.NetNsCtr = ctr3.config.ID
|
||||
ctr2.config.IPCNsCtr = ctr3.config.ID
|
||||
|
||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(graph.nodes))
|
||||
assert.Equal(t, 1, len(graph.noDepNodes))
|
||||
@ -215,7 +215,7 @@ func TestBuildContainerGraphFourContainersNoEdges(t *testing.T) {
|
||||
ctr4, err := getTestCtrN("4", manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(graph.nodes))
|
||||
assert.Equal(t, 4, len(graph.noDepNodes))
|
||||
@ -256,7 +256,7 @@ func TestBuildContainerGraphFourContainersTwoInCycle(t *testing.T) {
|
||||
ctr1.config.IPCNsCtr = ctr2.config.ID
|
||||
ctr2.config.UserNsCtr = ctr1.config.ID
|
||||
|
||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ func TestBuildContainerGraphFourContainersAllInCycle(t *testing.T) {
|
||||
ctr3.config.NetNsCtr = ctr4.config.ID
|
||||
ctr4.config.UTSNsCtr = ctr1.config.ID
|
||||
|
||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ func TestBuildContainerGraphFourContainersNoneInCycle(t *testing.T) {
|
||||
ctr1.config.NetNsCtr = ctr3.config.ID
|
||||
ctr2.config.UserNsCtr = ctr3.config.ID
|
||||
|
||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(graph.nodes))
|
||||
assert.Equal(t, 2, len(graph.noDepNodes))
|
||||
|
@ -788,7 +788,7 @@ func (c *Container) startDependencies(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Build a dependency graph of containers
|
||||
graph, err := buildContainerGraph(depCtrs)
|
||||
graph, err := BuildContainerGraph(depCtrs)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error generating dependency graph for container %s", c.ID())
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) {
|
||||
}
|
||||
|
||||
// Build a dependency graph of containers in the pod
|
||||
graph, err := buildContainerGraph(allCtrs)
|
||||
graph, err := BuildContainerGraph(allCtrs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID())
|
||||
}
|
||||
@ -289,7 +289,7 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) {
|
||||
}
|
||||
|
||||
// Build a dependency graph of containers in the pod
|
||||
graph, err := buildContainerGraph(allCtrs)
|
||||
graph, err := BuildContainerGraph(allCtrs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID())
|
||||
}
|
||||
|
@ -1094,28 +1094,145 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) {
|
||||
return portContainers, nil
|
||||
}
|
||||
|
||||
// GenerateSystemd creates a unit file for a container
|
||||
func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) {
|
||||
ctr, err := r.Runtime.LookupContainer(c.InputArgs[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
// generateServiceName generates the container name and the service name for systemd service.
|
||||
func generateServiceName(c *cliconfig.GenerateSystemdValues, ctr *libpod.Container, pod *libpod.Pod) (string, string) {
|
||||
var kind, name, ctrName string
|
||||
if pod == nil {
|
||||
kind = "container"
|
||||
name = ctr.ID()
|
||||
if c.Name {
|
||||
name = ctr.Name()
|
||||
}
|
||||
ctrName = name
|
||||
} else {
|
||||
kind = "pod"
|
||||
name = pod.ID()
|
||||
ctrName = ctr.ID()
|
||||
if c.Name {
|
||||
name = pod.Name()
|
||||
ctrName = ctr.Name()
|
||||
}
|
||||
}
|
||||
return ctrName, fmt.Sprintf("%s-%s", kind, name)
|
||||
}
|
||||
|
||||
// generateSystemdgenContainerInfo is a helper to generate a
|
||||
// systemdgen.ContainerInfo for `GenerateSystemd`.
|
||||
func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSystemdValues, nameOrID string, pod *libpod.Pod) (*systemdgen.ContainerInfo, bool, error) {
|
||||
ctr, err := r.Runtime.LookupContainer(nameOrID)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
timeout := int(ctr.StopTimeout())
|
||||
if c.StopTimeout >= 0 {
|
||||
timeout = c.StopTimeout
|
||||
}
|
||||
name := ctr.ID()
|
||||
if c.Name {
|
||||
name = ctr.Name()
|
||||
}
|
||||
|
||||
config := ctr.Config()
|
||||
conmonPidFile := config.ConmonPidFile
|
||||
if conmonPidFile == "" {
|
||||
return "", errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag")
|
||||
return nil, true, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag")
|
||||
}
|
||||
|
||||
return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, conmonPidFile, timeout)
|
||||
name, serviceName := generateServiceName(c, ctr, pod)
|
||||
info := &systemdgen.ContainerInfo{
|
||||
ServiceName: serviceName,
|
||||
ContainerName: name,
|
||||
RestartPolicy: c.RestartPolicy,
|
||||
PIDFile: conmonPidFile,
|
||||
StopTimeout: timeout,
|
||||
GenerateTimestamp: true,
|
||||
}
|
||||
|
||||
return info, true, nil
|
||||
}
|
||||
|
||||
// GenerateSystemd creates a unit file for a container or pod.
|
||||
func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) {
|
||||
// First assume it's a container.
|
||||
if info, found, err := r.generateSystemdgenContainerInfo(c, c.InputArgs[0], nil); found && err != nil {
|
||||
return "", err
|
||||
} else if found && err == nil {
|
||||
return systemdgen.CreateContainerSystemdUnit(info, c.Files)
|
||||
}
|
||||
|
||||
// We're either having a pod or garbage.
|
||||
pod, err := r.Runtime.LookupPod(c.InputArgs[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Error out if the pod has no infra container, which we require to be the
|
||||
// main service.
|
||||
if !pod.HasInfraContainer() {
|
||||
return "", fmt.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name())
|
||||
}
|
||||
|
||||
// Generate a systemdgen.ContainerInfo for the infra container. This
|
||||
// ContainerInfo acts as the main service of the pod.
|
||||
infraID, err := pod.InfraContainerID()
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
podInfo, _, err := r.generateSystemdgenContainerInfo(c, infraID, pod)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Compute the container-dependency graph for the Pod.
|
||||
containers, err := pod.AllContainers()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(containers) == 0 {
|
||||
return "", fmt.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name())
|
||||
}
|
||||
graph, err := libpod.BuildContainerGraph(containers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Traverse the dependency graph and create systemdgen.ContainerInfo's for
|
||||
// each container.
|
||||
containerInfos := []*systemdgen.ContainerInfo{podInfo}
|
||||
for ctr, dependencies := range graph.DependencyMap() {
|
||||
// Skip the infra container as we already generated it.
|
||||
if ctr.ID() == infraID {
|
||||
continue
|
||||
}
|
||||
ctrInfo, _, err := r.generateSystemdgenContainerInfo(c, ctr.ID(), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Now add the container's dependencies and at the container as a
|
||||
// required service of the infra container.
|
||||
for _, dep := range dependencies {
|
||||
if dep.ID() == infraID {
|
||||
ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, podInfo.ServiceName)
|
||||
} else {
|
||||
_, serviceName := generateServiceName(c, dep, nil)
|
||||
ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, serviceName)
|
||||
}
|
||||
}
|
||||
podInfo.RequiredServices = append(podInfo.RequiredServices, ctrInfo.ServiceName)
|
||||
containerInfos = append(containerInfos, ctrInfo)
|
||||
}
|
||||
|
||||
// Now generate the systemd service for all containers.
|
||||
builder := strings.Builder{}
|
||||
for i, info := range containerInfos {
|
||||
if i > 0 {
|
||||
builder.WriteByte('\n')
|
||||
}
|
||||
out, err := systemdgen.CreateContainerSystemdUnit(info, c.Files)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
builder.WriteString(out)
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// GetNamespaces returns namespace information about a container for PS
|
||||
|
@ -1,29 +1,59 @@
|
||||
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"
|
||||
)
|
||||
|
||||
var template = `[Unit]
|
||||
Description=%s Podman Container
|
||||
[Service]
|
||||
Restart=%s
|
||||
ExecStart=%s start %s
|
||||
ExecStop=%s stop -t %d %s
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=%s
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
// 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 {
|
||||
// validateRestartPolicy checks that the user-provided policy is valid.
|
||||
func validateRestartPolicy(restart string) error {
|
||||
for _, i := range restartPolicies {
|
||||
if i == restart {
|
||||
return nil
|
||||
@ -32,28 +62,87 @@ func ValidateRestartPolicy(restart string) error {
|
||||
return errors.Errorf("%s is not a valid restart policy", restart)
|
||||
}
|
||||
|
||||
// CreateSystemdUnitAsString takes variables to create a systemd unit file used to control
|
||||
// a libpod container
|
||||
func CreateSystemdUnitAsString(name, cid, restart, pidFile string, stopTimeout int) (string, error) {
|
||||
podmanExe := getPodmanExecutable()
|
||||
return createSystemdUnitAsString(podmanExe, name, cid, restart, pidFile, stopTimeout)
|
||||
}
|
||||
const containerTemplate = `# {{.ServiceName}}.service
|
||||
# autogenerated by Podman {{.PodmanVersion}}
|
||||
{{- if .TimeStamp}}
|
||||
# {{.TimeStamp}}
|
||||
{{- end}}
|
||||
|
||||
func createSystemdUnitAsString(exe, name, cid, restart, pidFile string, stopTimeout int) (string, error) {
|
||||
if err := ValidateRestartPolicy(restart); err != nil {
|
||||
[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
|
||||
}
|
||||
|
||||
unit := fmt.Sprintf(template, name, restart, exe, name, exe, stopTimeout, name, pidFile)
|
||||
return unit, nil
|
||||
}
|
||||
|
||||
func getPodmanExecutable() string {
|
||||
podmanExe, err := os.Executable()
|
||||
if err != nil {
|
||||
podmanExe = "/usr/bin/podman"
|
||||
logrus.Warnf("Could not obtain podman executable location, using default %s", podmanExe)
|
||||
// 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
|
||||
}
|
||||
|
||||
return podmanExe
|
||||
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
|
||||
}
|
||||
|
@ -5,36 +5,41 @@ import (
|
||||
)
|
||||
|
||||
func TestValidateRestartPolicy(t *testing.T) {
|
||||
type args struct {
|
||||
type ContainerInfo struct {
|
||||
restart string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
name string
|
||||
ContainerInfo ContainerInfo
|
||||
wantErr bool
|
||||
}{
|
||||
{"good-on", args{restart: "no"}, false},
|
||||
{"good-on-success", args{restart: "on-success"}, false},
|
||||
{"good-on-failure", args{restart: "on-failure"}, false},
|
||||
{"good-on-abnormal", args{restart: "on-abnormal"}, false},
|
||||
{"good-on-watchdog", args{restart: "on-watchdog"}, false},
|
||||
{"good-on-abort", args{restart: "on-abort"}, false},
|
||||
{"good-always", args{restart: "always"}, false},
|
||||
{"fail", args{restart: "foobar"}, true},
|
||||
{"failblank", args{restart: ""}, true},
|
||||
{"good-on", ContainerInfo{restart: "no"}, false},
|
||||
{"good-on-success", ContainerInfo{restart: "on-success"}, false},
|
||||
{"good-on-failure", ContainerInfo{restart: "on-failure"}, false},
|
||||
{"good-on-abnormal", ContainerInfo{restart: "on-abnormal"}, false},
|
||||
{"good-on-watchdog", ContainerInfo{restart: "on-watchdog"}, false},
|
||||
{"good-on-abort", ContainerInfo{restart: "on-abort"}, false},
|
||||
{"good-always", ContainerInfo{restart: "always"}, false},
|
||||
{"fail", ContainerInfo{restart: "foobar"}, true},
|
||||
{"failblank", ContainerInfo{restart: ""}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := ValidateRestartPolicy(tt.args.restart); (err != nil) != tt.wantErr {
|
||||
if err := validateRestartPolicy(tt.ContainerInfo.restart); (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSystemdUnitAsString(t *testing.T) {
|
||||
goodID := `[Unit]
|
||||
Description=639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 Podman Container
|
||||
func TestCreateContainerSystemdUnit(t *testing.T) {
|
||||
goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||
# autogenerated by Podman CI
|
||||
|
||||
[Unit]
|
||||
Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
|
||||
@ -42,11 +47,17 @@ ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
|
||||
goodName := `[Unit]
|
||||
Description=foobar Podman Container
|
||||
goodName := `# container-foobar.service
|
||||
# autogenerated by Podman CI
|
||||
|
||||
[Unit]
|
||||
Description=Podman container-foobar.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/podman start foobar
|
||||
@ -54,56 +65,121 @@ ExecStop=/usr/bin/podman stop -t 10 foobar
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
|
||||
goodNameBoundTo := `# container-foobar.service
|
||||
# autogenerated by Podman CI
|
||||
|
||||
[Unit]
|
||||
Description=Podman container-foobar.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
RefuseManualStart=yes
|
||||
RefuseManualStop=yes
|
||||
BindsTo=a.service b.service c.service pod.service
|
||||
After=a.service b.service c.service pod.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/podman start foobar
|
||||
ExecStop=/usr/bin/podman stop -t 10 foobar
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
|
||||
podGoodName := `# pod-123abc.service
|
||||
# autogenerated by Podman CI
|
||||
|
||||
[Unit]
|
||||
Description=Podman pod-123abc.service
|
||||
Documentation=man:podman-generate-systemd(1)
|
||||
Requires=container-1.service container-2.service
|
||||
Before=container-1.service container-2.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/podman start jadda-jadda-infra
|
||||
ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
|
||||
type args struct {
|
||||
exe string
|
||||
name string
|
||||
cid string
|
||||
restart string
|
||||
pidFile string
|
||||
stopTimeout int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
info ContainerInfo
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
|
||||
{"good with id",
|
||||
args{
|
||||
"/usr/bin/podman",
|
||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
"always",
|
||||
"/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
10,
|
||||
ContainerInfo{
|
||||
Executable: "/usr/bin/podman",
|
||||
ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
ContainerName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
RestartPolicy: "always",
|
||||
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
StopTimeout: 10,
|
||||
PodmanVersion: "CI",
|
||||
},
|
||||
goodID,
|
||||
false,
|
||||
},
|
||||
{"good with name",
|
||||
args{
|
||||
"/usr/bin/podman",
|
||||
"foobar",
|
||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
"always",
|
||||
"/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
10,
|
||||
ContainerInfo{
|
||||
Executable: "/usr/bin/podman",
|
||||
ServiceName: "container-foobar",
|
||||
ContainerName: "foobar",
|
||||
RestartPolicy: "always",
|
||||
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
StopTimeout: 10,
|
||||
PodmanVersion: "CI",
|
||||
},
|
||||
goodName,
|
||||
false,
|
||||
},
|
||||
{"good with name and bound to",
|
||||
ContainerInfo{
|
||||
Executable: "/usr/bin/podman",
|
||||
ServiceName: "container-foobar",
|
||||
ContainerName: "foobar",
|
||||
RestartPolicy: "always",
|
||||
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
StopTimeout: 10,
|
||||
PodmanVersion: "CI",
|
||||
BoundToServices: []string{"pod", "a", "b", "c"},
|
||||
},
|
||||
goodNameBoundTo,
|
||||
false,
|
||||
},
|
||||
{"pod",
|
||||
ContainerInfo{
|
||||
Executable: "/usr/bin/podman",
|
||||
ServiceName: "pod-123abc",
|
||||
ContainerName: "jadda-jadda-infra",
|
||||
RestartPolicy: "always",
|
||||
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
StopTimeout: 10,
|
||||
PodmanVersion: "CI",
|
||||
RequiredServices: []string{"container-1", "container-2"},
|
||||
},
|
||||
podGoodName,
|
||||
false,
|
||||
},
|
||||
{"bad restart policy",
|
||||
args{
|
||||
"/usr/bin/podman",
|
||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
"never",
|
||||
"/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
10,
|
||||
ContainerInfo{
|
||||
Executable: "/usr/bin/podman",
|
||||
ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||
RestartPolicy: "never",
|
||||
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||
StopTimeout: 10,
|
||||
PodmanVersion: "CI",
|
||||
},
|
||||
"",
|
||||
true,
|
||||
@ -111,13 +187,13 @@ WantedBy=multi-user.target`
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := createSystemdUnitAsString(tt.args.exe, tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidFile, tt.args.stopTimeout)
|
||||
got, err := CreateContainerSystemdUnit(&tt.info, false)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CreateSystemdUnitAsString() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("CreateSystemdUnitAsString() = %v, want %v", got, tt.want)
|
||||
t.Errorf("CreateContainerSystemdUnit() = \n%v, want \n%v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -3,10 +3,11 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
. "github.com/containers/libpod/test/utils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"os"
|
||||
)
|
||||
|
||||
var _ = Describe("Podman generate systemd", func() {
|
||||
@ -33,7 +34,7 @@ var _ = Describe("Podman generate systemd", func() {
|
||||
|
||||
})
|
||||
|
||||
It("podman generate systemd on bogus container", func() {
|
||||
It("podman generate systemd on bogus container/pod", func() {
|
||||
session := podmanTest.Podman([]string{"generate", "systemd", "foobar"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Not(Equal(0)))
|
||||
@ -51,6 +52,19 @@ var _ = Describe("Podman generate systemd", func() {
|
||||
Expect(session.ExitCode()).To(Not(Equal(0)))
|
||||
})
|
||||
|
||||
It("podman generate systemd good timeout value", func() {
|
||||
session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"generate", "systemd", "--timeout", "1234", "foobar"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
found, _ := session.GrepString(" stop -t 1234 ")
|
||||
Expect(found).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman generate systemd", func() {
|
||||
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
|
||||
n.WaitWithDefaultTimeout()
|
||||
@ -61,6 +75,23 @@ var _ = Describe("Podman generate systemd", func() {
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman generate systemd --files --name", func() {
|
||||
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
|
||||
n.WaitWithDefaultTimeout()
|
||||
Expect(n.ExitCode()).To(Equal(0))
|
||||
|
||||
session := podmanTest.Podman([]string{"generate", "systemd", "--files", "--name", "nginx"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
for _, file := range session.OutputToStringArray() {
|
||||
os.Remove(file)
|
||||
}
|
||||
|
||||
found, _ := session.GrepString("/container-nginx.service")
|
||||
Expect(found).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman generate systemd with timeout", func() {
|
||||
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
|
||||
n.WaitWithDefaultTimeout()
|
||||
@ -69,6 +100,81 @@ var _ = Describe("Podman generate systemd", func() {
|
||||
session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "5", "nginx"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
found, _ := session.GrepString("podman stop -t 5")
|
||||
Expect(found).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman generate systemd pod --name", func() {
|
||||
n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"})
|
||||
n.WaitWithDefaultTimeout()
|
||||
Expect(n.ExitCode()).To(Equal(0))
|
||||
|
||||
n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"})
|
||||
n.WaitWithDefaultTimeout()
|
||||
Expect(n.ExitCode()).To(Equal(0))
|
||||
|
||||
n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"})
|
||||
n.WaitWithDefaultTimeout()
|
||||
Expect(n.ExitCode()).To(Equal(0))
|
||||
|
||||
session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "42", "--name", "foo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
// Grepping the output (in addition to unit tests)
|
||||
found, _ := session.GrepString("# pod-foo.service")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("# container-foo-1.service")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString(" start foo-1")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("-infra") // infra container
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("# container-foo-2.service")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString(" stop -t 42 foo-2")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("BindsTo=pod-foo.service")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("PIDFile=")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("/userdata/conmon.pid")
|
||||
Expect(found).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman generate systemd pod --name --files", func() {
|
||||
n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"})
|
||||
n.WaitWithDefaultTimeout()
|
||||
Expect(n.ExitCode()).To(Equal(0))
|
||||
|
||||
n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"})
|
||||
n.WaitWithDefaultTimeout()
|
||||
Expect(n.ExitCode()).To(Equal(0))
|
||||
|
||||
session := podmanTest.Podman([]string{"generate", "systemd", "--name", "--files", "foo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
for _, file := range session.OutputToStringArray() {
|
||||
os.Remove(file)
|
||||
}
|
||||
|
||||
found, _ := session.GrepString("/pod-foo.service")
|
||||
Expect(found).To(BeTrue())
|
||||
|
||||
found, _ = session.GrepString("/container-foo-1.service")
|
||||
Expect(found).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user