mirror of
https://github.com/containers/podman.git
synced 2025-06-09 09:04:08 +08:00
Merge pull request #3800 from vrothberg/generate-pod
generate systemd pod
This commit is contained in:
7
API.md
7
API.md
@ -49,8 +49,6 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
|||||||
|
|
||||||
[func GenerateKube(name: string, service: bool) KubePodService](#GenerateKube)
|
[func GenerateKube(name: string, service: bool) KubePodService](#GenerateKube)
|
||||||
|
|
||||||
[func GenerateSystemd(name: string, restart: string, timeout: int, useName: bool) string](#GenerateSystemd)
|
|
||||||
|
|
||||||
[func GetAttachSockets(name: string) Sockets](#GetAttachSockets)
|
[func GetAttachSockets(name: string) Sockets](#GetAttachSockets)
|
||||||
|
|
||||||
[func GetContainer(id: string) Container](#GetContainer)
|
[func GetContainer(id: string) Container](#GetContainer)
|
||||||
@ -482,11 +480,6 @@ error will be returned. See also [ImportImage](ImportImage).
|
|||||||
method GenerateKube(name: [string](https://godoc.org/builtin#string), service: [bool](https://godoc.org/builtin#bool)) [KubePodService](#KubePodService)</div>
|
method GenerateKube(name: [string](https://godoc.org/builtin#string), service: [bool](https://godoc.org/builtin#bool)) [KubePodService](#KubePodService)</div>
|
||||||
GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
|
GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
|
||||||
and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
|
and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
|
||||||
### <a name="GenerateSystemd"></a>func GenerateSystemd
|
|
||||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
|
||||||
|
|
||||||
method GenerateSystemd(name: [string](https://godoc.org/builtin#string), restart: [string](https://godoc.org/builtin#string), timeout: [int](https://godoc.org/builtin#int), useName: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
|
|
||||||
|
|
||||||
### <a name="GetAttachSockets"></a>func GetAttachSockets
|
### <a name="GetAttachSockets"></a>func GetAttachSockets
|
||||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||||
|
|
||||||
|
@ -156,6 +156,7 @@ type GenerateKubeValues struct {
|
|||||||
type GenerateSystemdValues struct {
|
type GenerateSystemdValues struct {
|
||||||
PodmanCommand
|
PodmanCommand
|
||||||
Name bool
|
Name bool
|
||||||
|
Files bool
|
||||||
RestartPolicy string
|
RestartPolicy string
|
||||||
StopTimeout int
|
StopTimeout int
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,14 @@ var (
|
|||||||
// Commands that are universally implemented
|
// Commands that are universally implemented
|
||||||
generateCommands = []*cobra.Command{
|
generateCommands = []*cobra.Command{
|
||||||
_containerKubeCommand,
|
_containerKubeCommand,
|
||||||
_containerSystemdCommand,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
// Systemd-service generation is not supported for remote-clients.
|
||||||
|
if !remoteclient {
|
||||||
|
generateCommands = append(generateCommands, _containerSystemdCommand)
|
||||||
|
}
|
||||||
generateCommand.Command = _generateCommand
|
generateCommand.Command = _generateCommand
|
||||||
generateCommand.AddCommand(generateCommands...)
|
generateCommand.AddCommand(generateCommands...)
|
||||||
generateCommand.SetUsageTemplate(UsageTemplate())
|
generateCommand.SetUsageTemplate(UsageTemplate())
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
"github.com/containers/libpod/pkg/adapter"
|
"github.com/containers/libpod/pkg/adapter"
|
||||||
"github.com/containers/libpod/pkg/systemdgen"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -40,7 +39,10 @@ func init() {
|
|||||||
containerSystemdCommand.SetHelpTemplate(HelpTemplate())
|
containerSystemdCommand.SetHelpTemplate(HelpTemplate())
|
||||||
containerSystemdCommand.SetUsageTemplate(UsageTemplate())
|
containerSystemdCommand.SetUsageTemplate(UsageTemplate())
|
||||||
flags := containerSystemdCommand.Flags()
|
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.IntVarP(&containerSystemdCommand.StopTimeout, "timeout", "t", -1, "stop timeout override")
|
||||||
flags.StringVar(&containerSystemdCommand.RestartPolicy, "restart-policy", "on-failure", "applicable systemd restart-policy")
|
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 {
|
if c.Flag("timeout").Changed && c.StopTimeout < 0 {
|
||||||
return errors.New("timeout value must be 0 or greater")
|
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)
|
unit, err := runtime.GenerateSystemd(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1241,8 +1241,6 @@ method GetLayersMapWithImageInfo() -> (layerMap: string)
|
|||||||
# BuildImageHierarchyMap is for the development of Podman and should not be used.
|
# BuildImageHierarchyMap is for the development of Podman and should not be used.
|
||||||
method BuildImageHierarchyMap(name: string) -> (imageInfo: string)
|
method BuildImageHierarchyMap(name: string) -> (imageInfo: string)
|
||||||
|
|
||||||
method GenerateSystemd(name: string, restart: string, timeout: int, useName: bool) -> (unit: string)
|
|
||||||
|
|
||||||
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
|
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
|
||||||
error ImageNotFound (id: string, reason: string)
|
error ImageNotFound (id: string, reason: string)
|
||||||
|
|
||||||
|
@ -4,16 +4,20 @@
|
|||||||
podman-generate-systemd- Generate Systemd Unit file
|
podman-generate-systemd- Generate Systemd Unit file
|
||||||
|
|
||||||
## SYNOPSIS
|
## SYNOPSIS
|
||||||
**podman generate systemd** [*options*] *container*
|
**podman generate systemd** [*options*] *container|pod*
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
**podman generate systemd** will create a Systemd unit file that can be used to control a container. The
|
**podman generate systemd** will create a systemd unit file that can be used to control a container or pod.
|
||||||
command will dynamically create the unit file and output it to stdout where it can be piped by the user
|
By default, the command will print the content of the unit files to stdout.
|
||||||
to a file. The options can be used to influence the results of the output as well.
|
|
||||||
|
|
||||||
|
Note that this command is not supported for the remote client.
|
||||||
|
|
||||||
## OPTIONS:
|
## 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**
|
**--name**, **-n**
|
||||||
|
|
||||||
Use the name of the container for the start, stop, and description in the unit file
|
Use the name of the container for the start, stop, and description in the unit file
|
||||||
@ -27,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*.
|
"on-watchdog", "on-abort", or "always". The default policy is *on-failure*.
|
||||||
|
|
||||||
## Examples
|
## 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]
|
[Unit]
|
||||||
Description=c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc Podman Container
|
Description=Podman container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service
|
||||||
|
Documentation=man:podman-generate-systemd(1)
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Restart=on-failure
|
Restart=always
|
||||||
ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
ExecStart=/usr/bin/podman start de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6
|
||||||
ExecStop=/usr/bin/podman stop -t 10 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
ExecStop=/usr/bin/podman stop -t 1 de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6
|
||||||
KillMode=none
|
KillMode=none
|
||||||
Type=forking
|
Type=forking
|
||||||
PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid
|
PIDFile=/run/user/1000/overlay-containers/de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6/userdata/conmon.pid
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
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]
|
[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]
|
[Service]
|
||||||
Restart=always
|
Restart=on-failure
|
||||||
ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
ExecStart=/usr/bin/podman start 77a818221650-infra
|
||||||
ExecStop=/usr/bin/podman stop -t 1 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
|
ExecStop=/usr/bin/podman stop -t 10 77a818221650-infra
|
||||||
KillMode=none
|
KillMode=none
|
||||||
Type=forking
|
Type=forking
|
||||||
PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid
|
PIDFile=/run/user/1000/overlay-containers/ccfd5c71a088768774ca7bd05888d55cc287698dde06f475c8b02f696a25adcd/userdata/conmon.pid
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
```
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
podman(1), podman-container(1)
|
podman(1), podman-container(1), systemctl(1), systemd.unit(5), systemd.service(5)
|
||||||
|
|
||||||
## HISTORY
|
## 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)
|
April 2019, Originally compiled by Brent Baude (bbaude at redhat dot com)
|
||||||
|
@ -12,9 +12,10 @@ The generate command will create structured output (like YAML) based on a contai
|
|||||||
## COMMANDS
|
## COMMANDS
|
||||||
|
|
||||||
| Command | Man Page | Description |
|
| Command | Man Page | Description |
|
||||||
| ------- | --------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|---------|------------------------------------------------------------|-------------------------------------------------------------------------------------|
|
||||||
| kube | [podman-generate-kube(1)](podman-generate-kube.1.md)| Generate Kubernetes YAML based on a pod or container. |
|
| 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. |
|
| 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
|
## SEE ALSO
|
||||||
podman, podman-pod, podman-container
|
podman, podman-pod, podman-container
|
||||||
|
@ -16,14 +16,30 @@ type containerNode struct {
|
|||||||
dependedOn []*containerNode
|
dependedOn []*containerNode
|
||||||
}
|
}
|
||||||
|
|
||||||
type containerGraph struct {
|
// ContainerGraph is a dependency graph based on a set of containers.
|
||||||
|
type ContainerGraph struct {
|
||||||
nodes map[string]*containerNode
|
nodes map[string]*containerNode
|
||||||
noDepNodes []*containerNode
|
noDepNodes []*containerNode
|
||||||
notDependedOnNodes map[string]*containerNode
|
notDependedOnNodes map[string]*containerNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildContainerGraph(ctrs []*Container) (*containerGraph, error) {
|
// DependencyMap returns the dependency graph as map with the key being a
|
||||||
graph := new(containerGraph)
|
// 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.nodes = make(map[string]*containerNode)
|
||||||
graph.notDependedOnNodes = 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
|
// Detect cycles in a container graph using Tarjan's strongly connected
|
||||||
// components algorithm
|
// components algorithm
|
||||||
// Return true if a cycle is found, false otherwise
|
// Return true if a cycle is found, false otherwise
|
||||||
func detectCycles(graph *containerGraph) (bool, error) {
|
func detectCycles(graph *ContainerGraph) (bool, error) {
|
||||||
type nodeInfo struct {
|
type nodeInfo struct {
|
||||||
index int
|
index int
|
||||||
lowLink int
|
lowLink int
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestBuildContainerGraphNoCtrsIsEmpty(t *testing.T) {
|
func TestBuildContainerGraphNoCtrsIsEmpty(t *testing.T) {
|
||||||
graph, err := buildContainerGraph([]*Container{})
|
graph, err := BuildContainerGraph([]*Container{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 0, len(graph.nodes))
|
assert.Equal(t, 0, len(graph.nodes))
|
||||||
assert.Equal(t, 0, len(graph.noDepNodes))
|
assert.Equal(t, 0, len(graph.noDepNodes))
|
||||||
@ -24,7 +24,7 @@ func TestBuildContainerGraphOneCtr(t *testing.T) {
|
|||||||
ctr1, err := getTestCtr1(manager)
|
ctr1, err := getTestCtr1(manager)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
graph, err := buildContainerGraph([]*Container{ctr1})
|
graph, err := BuildContainerGraph([]*Container{ctr1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 1, len(graph.nodes))
|
assert.Equal(t, 1, len(graph.nodes))
|
||||||
assert.Equal(t, 1, len(graph.noDepNodes))
|
assert.Equal(t, 1, len(graph.noDepNodes))
|
||||||
@ -49,7 +49,7 @@ func TestBuildContainerGraphTwoCtrNoEdge(t *testing.T) {
|
|||||||
ctr2, err := getTestCtr2(manager)
|
ctr2, err := getTestCtr2(manager)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2})
|
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 2, len(graph.nodes))
|
assert.Equal(t, 2, len(graph.nodes))
|
||||||
assert.Equal(t, 2, len(graph.noDepNodes))
|
assert.Equal(t, 2, len(graph.noDepNodes))
|
||||||
@ -76,7 +76,7 @@ func TestBuildContainerGraphTwoCtrOneEdge(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
ctr2.config.UserNsCtr = ctr1.config.ID
|
ctr2.config.UserNsCtr = ctr1.config.ID
|
||||||
|
|
||||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2})
|
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 2, len(graph.nodes))
|
assert.Equal(t, 2, len(graph.nodes))
|
||||||
assert.Equal(t, 1, len(graph.noDepNodes))
|
assert.Equal(t, 1, len(graph.noDepNodes))
|
||||||
@ -99,7 +99,7 @@ func TestBuildContainerGraphTwoCtrCycle(t *testing.T) {
|
|||||||
ctr2.config.UserNsCtr = ctr1.config.ID
|
ctr2.config.UserNsCtr = ctr1.config.ID
|
||||||
ctr1.config.NetNsCtr = ctr2.config.ID
|
ctr1.config.NetNsCtr = ctr2.config.ID
|
||||||
|
|
||||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2})
|
_, err = BuildContainerGraph([]*Container{ctr1, ctr2})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ func TestBuildContainerGraphThreeCtrNoEdges(t *testing.T) {
|
|||||||
ctr3, err := getTestCtrN("3", manager)
|
ctr3, err := getTestCtrN("3", manager)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 3, len(graph.nodes))
|
assert.Equal(t, 3, len(graph.nodes))
|
||||||
assert.Equal(t, 3, len(graph.noDepNodes))
|
assert.Equal(t, 3, len(graph.noDepNodes))
|
||||||
@ -150,7 +150,7 @@ func TestBuildContainerGraphThreeContainersTwoInCycle(t *testing.T) {
|
|||||||
ctr1.config.UserNsCtr = ctr2.config.ID
|
ctr1.config.UserNsCtr = ctr2.config.ID
|
||||||
ctr2.config.IPCNsCtr = ctr1.config.ID
|
ctr2.config.IPCNsCtr = ctr1.config.ID
|
||||||
|
|
||||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ func TestBuildContainerGraphThreeContainersCycle(t *testing.T) {
|
|||||||
ctr2.config.IPCNsCtr = ctr3.config.ID
|
ctr2.config.IPCNsCtr = ctr3.config.ID
|
||||||
ctr3.config.NetNsCtr = ctr1.config.ID
|
ctr3.config.NetNsCtr = ctr1.config.ID
|
||||||
|
|
||||||
_, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ func TestBuildContainerGraphThreeContainersNoCycle(t *testing.T) {
|
|||||||
ctr1.config.NetNsCtr = ctr3.config.ID
|
ctr1.config.NetNsCtr = ctr3.config.ID
|
||||||
ctr2.config.IPCNsCtr = 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.NoError(t, err)
|
||||||
assert.Equal(t, 3, len(graph.nodes))
|
assert.Equal(t, 3, len(graph.nodes))
|
||||||
assert.Equal(t, 1, len(graph.noDepNodes))
|
assert.Equal(t, 1, len(graph.noDepNodes))
|
||||||
@ -215,7 +215,7 @@ func TestBuildContainerGraphFourContainersNoEdges(t *testing.T) {
|
|||||||
ctr4, err := getTestCtrN("4", manager)
|
ctr4, err := getTestCtrN("4", manager)
|
||||||
assert.NoError(t, err)
|
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.NoError(t, err)
|
||||||
assert.Equal(t, 4, len(graph.nodes))
|
assert.Equal(t, 4, len(graph.nodes))
|
||||||
assert.Equal(t, 4, len(graph.noDepNodes))
|
assert.Equal(t, 4, len(graph.noDepNodes))
|
||||||
@ -256,7 +256,7 @@ func TestBuildContainerGraphFourContainersTwoInCycle(t *testing.T) {
|
|||||||
ctr1.config.IPCNsCtr = ctr2.config.ID
|
ctr1.config.IPCNsCtr = ctr2.config.ID
|
||||||
ctr2.config.UserNsCtr = ctr1.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)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +280,7 @@ func TestBuildContainerGraphFourContainersAllInCycle(t *testing.T) {
|
|||||||
ctr3.config.NetNsCtr = ctr4.config.ID
|
ctr3.config.NetNsCtr = ctr4.config.ID
|
||||||
ctr4.config.UTSNsCtr = ctr1.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)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +303,7 @@ func TestBuildContainerGraphFourContainersNoneInCycle(t *testing.T) {
|
|||||||
ctr1.config.NetNsCtr = ctr3.config.ID
|
ctr1.config.NetNsCtr = ctr3.config.ID
|
||||||
ctr2.config.UserNsCtr = 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.NoError(t, err)
|
||||||
assert.Equal(t, 4, len(graph.nodes))
|
assert.Equal(t, 4, len(graph.nodes))
|
||||||
assert.Equal(t, 2, len(graph.noDepNodes))
|
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
|
// Build a dependency graph of containers
|
||||||
graph, err := buildContainerGraph(depCtrs)
|
graph, err := BuildContainerGraph(depCtrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error generating dependency graph for container %s", c.ID())
|
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
|
// Build a dependency graph of containers in the pod
|
||||||
graph, err := buildContainerGraph(allCtrs)
|
graph, err := BuildContainerGraph(allCtrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID())
|
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
|
// Build a dependency graph of containers in the pod
|
||||||
graph, err := buildContainerGraph(allCtrs)
|
graph, err := BuildContainerGraph(allCtrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID())
|
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
|
return portContainers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateSystemd creates a unit file for a container
|
// generateServiceName generates the container name and the service name for systemd service.
|
||||||
func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) {
|
func generateServiceName(c *cliconfig.GenerateSystemdValues, ctr *libpod.Container, pod *libpod.Pod) (string, string) {
|
||||||
ctr, err := r.Runtime.LookupContainer(c.InputArgs[0])
|
var kind, name, ctrName string
|
||||||
if err != nil {
|
if pod == nil {
|
||||||
return "", err
|
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())
|
timeout := int(ctr.StopTimeout())
|
||||||
if c.StopTimeout >= 0 {
|
if c.StopTimeout >= 0 {
|
||||||
timeout = c.StopTimeout
|
timeout = c.StopTimeout
|
||||||
}
|
}
|
||||||
name := ctr.ID()
|
|
||||||
if c.Name {
|
|
||||||
name = ctr.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
config := ctr.Config()
|
config := ctr.Config()
|
||||||
conmonPidFile := config.ConmonPidFile
|
conmonPidFile := config.ConmonPidFile
|
||||||
if 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
|
// GetNamespaces returns namespace information about a container for PS
|
||||||
|
@ -951,7 +951,7 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) {
|
|||||||
|
|
||||||
// GenerateSystemd creates a systemd until for a container
|
// GenerateSystemd creates a systemd until for a container
|
||||||
func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) {
|
func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) {
|
||||||
return iopodman.GenerateSystemd().Call(r.Conn, c.InputArgs[0], c.RestartPolicy, int64(c.StopTimeout), c.Name)
|
return "", errors.New("systemd generation not supported for remote clients")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNamespaces returns namespace information about a container for PS
|
// GetNamespaces returns namespace information about a container for PS
|
||||||
|
@ -1,29 +1,59 @@
|
|||||||
package systemdgen
|
package systemdgen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/version"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var template = `[Unit]
|
// ContainerInfo contains data required for generating a container's systemd
|
||||||
Description=%s Podman Container
|
// unit file.
|
||||||
[Service]
|
type ContainerInfo struct {
|
||||||
Restart=%s
|
// ServiceName of the systemd service.
|
||||||
ExecStart=%s start %s
|
ServiceName string
|
||||||
ExecStop=%s stop -t %d %s
|
// Name or ID of the container.
|
||||||
KillMode=none
|
ContainerName string
|
||||||
Type=forking
|
// InfraContainer of the pod.
|
||||||
PIDFile=%s
|
InfraContainer string
|
||||||
[Install]
|
// StopTimeout sets the timeout Podman waits before killing the container
|
||||||
WantedBy=multi-user.target`
|
// 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"}
|
var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"}
|
||||||
|
|
||||||
// ValidateRestartPolicy checks that the user-provided policy is valid
|
// validateRestartPolicy checks that the user-provided policy is valid.
|
||||||
func ValidateRestartPolicy(restart string) error {
|
func validateRestartPolicy(restart string) error {
|
||||||
for _, i := range restartPolicies {
|
for _, i := range restartPolicies {
|
||||||
if i == restart {
|
if i == restart {
|
||||||
return nil
|
return nil
|
||||||
@ -32,28 +62,87 @@ func ValidateRestartPolicy(restart string) error {
|
|||||||
return errors.Errorf("%s is not a valid restart policy", restart)
|
return errors.Errorf("%s is not a valid restart policy", restart)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateSystemdUnitAsString takes variables to create a systemd unit file used to control
|
const containerTemplate = `# {{.ServiceName}}.service
|
||||||
// a libpod container
|
# autogenerated by Podman {{.PodmanVersion}}
|
||||||
func CreateSystemdUnitAsString(name, cid, restart, pidFile string, stopTimeout int) (string, error) {
|
{{- if .TimeStamp}}
|
||||||
podmanExe := getPodmanExecutable()
|
# {{.TimeStamp}}
|
||||||
return createSystemdUnitAsString(podmanExe, name, cid, restart, pidFile, stopTimeout)
|
{{- end}}
|
||||||
}
|
|
||||||
|
|
||||||
func createSystemdUnitAsString(exe, name, cid, restart, pidFile string, stopTimeout int) (string, error) {
|
[Unit]
|
||||||
if err := ValidateRestartPolicy(restart); err != nil {
|
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
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
unit := fmt.Sprintf(template, name, restart, exe, name, exe, stopTimeout, name, pidFile)
|
// Make sure the executable is set.
|
||||||
return unit, nil
|
if info.Executable == "" {
|
||||||
}
|
executable, err := os.Executable()
|
||||||
|
|
||||||
func getPodmanExecutable() string {
|
|
||||||
podmanExe, err := os.Executable()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
podmanExe = "/usr/bin/podman"
|
executable = "/usr/bin/podman"
|
||||||
logrus.Warnf("Could not obtain podman executable location, using default %s", podmanExe)
|
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) {
|
func TestValidateRestartPolicy(t *testing.T) {
|
||||||
type args struct {
|
type ContainerInfo struct {
|
||||||
restart string
|
restart string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
ContainerInfo ContainerInfo
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"good-on", args{restart: "no"}, false},
|
{"good-on", ContainerInfo{restart: "no"}, false},
|
||||||
{"good-on-success", args{restart: "on-success"}, false},
|
{"good-on-success", ContainerInfo{restart: "on-success"}, false},
|
||||||
{"good-on-failure", args{restart: "on-failure"}, false},
|
{"good-on-failure", ContainerInfo{restart: "on-failure"}, false},
|
||||||
{"good-on-abnormal", args{restart: "on-abnormal"}, false},
|
{"good-on-abnormal", ContainerInfo{restart: "on-abnormal"}, false},
|
||||||
{"good-on-watchdog", args{restart: "on-watchdog"}, false},
|
{"good-on-watchdog", ContainerInfo{restart: "on-watchdog"}, false},
|
||||||
{"good-on-abort", args{restart: "on-abort"}, false},
|
{"good-on-abort", ContainerInfo{restart: "on-abort"}, false},
|
||||||
{"good-always", args{restart: "always"}, false},
|
{"good-always", ContainerInfo{restart: "always"}, false},
|
||||||
{"fail", args{restart: "foobar"}, true},
|
{"fail", ContainerInfo{restart: "foobar"}, true},
|
||||||
{"failblank", args{restart: ""}, true},
|
{"failblank", ContainerInfo{restart: ""}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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)
|
t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateSystemdUnitAsString(t *testing.T) {
|
func TestCreateContainerSystemdUnit(t *testing.T) {
|
||||||
goodID := `[Unit]
|
goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||||
Description=639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 Podman Container
|
# autogenerated by Podman CI
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||||
|
Documentation=man:podman-generate-systemd(1)
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
|
ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
|
||||||
@ -42,11 +47,17 @@ ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6
|
|||||||
KillMode=none
|
KillMode=none
|
||||||
Type=forking
|
Type=forking
|
||||||
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
|
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target`
|
WantedBy=multi-user.target`
|
||||||
|
|
||||||
goodName := `[Unit]
|
goodName := `# container-foobar.service
|
||||||
Description=foobar Podman Container
|
# autogenerated by Podman CI
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Podman container-foobar.service
|
||||||
|
Documentation=man:podman-generate-systemd(1)
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
ExecStart=/usr/bin/podman start foobar
|
ExecStart=/usr/bin/podman start foobar
|
||||||
@ -54,56 +65,121 @@ ExecStop=/usr/bin/podman stop -t 10 foobar
|
|||||||
KillMode=none
|
KillMode=none
|
||||||
Type=forking
|
Type=forking
|
||||||
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
|
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]
|
[Install]
|
||||||
WantedBy=multi-user.target`
|
WantedBy=multi-user.target`
|
||||||
|
|
||||||
type args struct {
|
|
||||||
exe string
|
|
||||||
name string
|
|
||||||
cid string
|
|
||||||
restart string
|
|
||||||
pidFile string
|
|
||||||
stopTimeout int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
info ContainerInfo
|
||||||
want string
|
want string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
|
|
||||||
{"good with id",
|
{"good with id",
|
||||||
args{
|
ContainerInfo{
|
||||||
"/usr/bin/podman",
|
Executable: "/usr/bin/podman",
|
||||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
ContainerName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||||
"always",
|
RestartPolicy: "always",
|
||||||
"/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||||
10,
|
StopTimeout: 10,
|
||||||
|
PodmanVersion: "CI",
|
||||||
},
|
},
|
||||||
goodID,
|
goodID,
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{"good with name",
|
{"good with name",
|
||||||
args{
|
ContainerInfo{
|
||||||
"/usr/bin/podman",
|
Executable: "/usr/bin/podman",
|
||||||
"foobar",
|
ServiceName: "container-foobar",
|
||||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
ContainerName: "foobar",
|
||||||
"always",
|
RestartPolicy: "always",
|
||||||
"/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||||
10,
|
StopTimeout: 10,
|
||||||
|
PodmanVersion: "CI",
|
||||||
},
|
},
|
||||||
goodName,
|
goodName,
|
||||||
false,
|
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",
|
{"bad restart policy",
|
||||||
args{
|
ContainerInfo{
|
||||||
"/usr/bin/podman",
|
Executable: "/usr/bin/podman",
|
||||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
||||||
"639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
|
RestartPolicy: "never",
|
||||||
"never",
|
PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
||||||
"/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
|
StopTimeout: 10,
|
||||||
10,
|
PodmanVersion: "CI",
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
true,
|
true,
|
||||||
@ -111,13 +187,13 @@ WantedBy=multi-user.target`
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
if got != tt.want {
|
if got != tt.want {
|
||||||
t.Errorf("CreateSystemdUnitAsString() = %v, want %v", got, tt.want)
|
t.Errorf("CreateContainerSystemdUnit() = \n%v, want \n%v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ package varlinkapi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/shared"
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
||||||
"github.com/containers/libpod/pkg/systemdgen"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateKube ...
|
// GenerateKube ...
|
||||||
@ -29,24 +29,3 @@ func (i *LibpodAPI) GenerateKube(call iopodman.VarlinkCall, name string, service
|
|||||||
Service: string(servB),
|
Service: string(servB),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateSystemd ...
|
|
||||||
func (i *LibpodAPI) GenerateSystemd(call iopodman.VarlinkCall, nameOrID, restart string, stopTimeout int64, useName bool) error {
|
|
||||||
ctr, err := i.Runtime.LookupContainer(nameOrID)
|
|
||||||
if err != nil {
|
|
||||||
return call.ReplyErrorOccurred(err.Error())
|
|
||||||
}
|
|
||||||
timeout := int(ctr.StopTimeout())
|
|
||||||
if stopTimeout >= 0 {
|
|
||||||
timeout = int(stopTimeout)
|
|
||||||
}
|
|
||||||
name := ctr.ID()
|
|
||||||
if useName {
|
|
||||||
name = ctr.Name()
|
|
||||||
}
|
|
||||||
unit, err := systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), restart, ctr.Config().StaticDir, timeout)
|
|
||||||
if err != nil {
|
|
||||||
return call.ReplyErrorOccurred(err.Error())
|
|
||||||
}
|
|
||||||
return call.ReplyGenerateSystemd(unit)
|
|
||||||
}
|
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
. "github.com/containers/libpod/test/utils"
|
. "github.com/containers/libpod/test/utils"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Podman generate systemd", func() {
|
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 := podmanTest.Podman([]string{"generate", "systemd", "foobar"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Not(Equal(0)))
|
Expect(session.ExitCode()).To(Not(Equal(0)))
|
||||||
@ -51,6 +52,19 @@ var _ = Describe("Podman generate systemd", func() {
|
|||||||
Expect(session.ExitCode()).To(Not(Equal(0)))
|
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() {
|
It("podman generate systemd", func() {
|
||||||
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
|
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
|
||||||
n.WaitWithDefaultTimeout()
|
n.WaitWithDefaultTimeout()
|
||||||
@ -61,6 +75,23 @@ var _ = Describe("Podman generate systemd", func() {
|
|||||||
Expect(session.ExitCode()).To(Equal(0))
|
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() {
|
It("podman generate systemd with timeout", func() {
|
||||||
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
|
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
|
||||||
n.WaitWithDefaultTimeout()
|
n.WaitWithDefaultTimeout()
|
||||||
@ -69,6 +100,81 @@ var _ = Describe("Podman generate systemd", func() {
|
|||||||
session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "5", "nginx"})
|
session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "5", "nginx"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
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