Merge pull request #2985 from baude/generatesystemd

Generate Systemd
This commit is contained in:
OpenShift Merge Robot
2019-05-02 22:33:09 +02:00
committed by GitHub
14 changed files with 342 additions and 17 deletions

View File

@ -136,12 +136,18 @@ type ExportValues struct {
PodmanCommand
Output string
}
type GenerateKubeValues struct {
PodmanCommand
Service bool
}
type GenerateSystemdValues struct {
PodmanCommand
Name bool
RestartPolicy string
StopTimeout int
}
type HistoryValues struct {
PodmanCommand
Human bool

View File

@ -18,6 +18,7 @@ var (
// Commands that are universally implemented
generateCommands = []*cobra.Command{
_containerKubeCommand,
_containerSystemdCommand,
}
)

View File

@ -0,0 +1,70 @@
package main
import (
"fmt"
"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"
)
var (
containerSystemdCommand cliconfig.GenerateSystemdValues
containerSystemdDescription = `Command generates a systemd unit file for a Podman container
`
_containerSystemdCommand = &cobra.Command{
Use: "systemd [flags] CONTAINER | POD",
Short: "Generate a systemd unit file for a Podman container",
Long: containerSystemdDescription,
RunE: func(cmd *cobra.Command, args []string) error {
containerSystemdCommand.InputArgs = args
containerSystemdCommand.GlobalFlags = MainGlobalOpts
containerSystemdCommand.Remote = remoteclient
return generateSystemdCmd(&containerSystemdCommand)
},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 1 || len(args) < 1 {
return errors.New("provide only one container name or ID")
}
return nil
},
Example: `podman generate kube ctrID
`,
}
)
func init() {
containerSystemdCommand.Command = _containerSystemdCommand
containerSystemdCommand.SetHelpTemplate(HelpTemplate())
containerSystemdCommand.SetUsageTemplate(UsageTemplate())
flags := containerSystemdCommand.Flags()
flags.BoolVarP(&containerSystemdCommand.Name, "name", "n", false, "use the container name instead of ID")
flags.IntVarP(&containerSystemdCommand.StopTimeout, "timeout", "t", -1, "stop timeout override")
flags.StringVar(&containerSystemdCommand.RestartPolicy, "restart-policy", "on-failure", "applicable systemd restart-policy")
}
func generateSystemdCmd(c *cliconfig.GenerateSystemdValues) error {
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
// User input stop timeout must be 0 or greater
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 {
return err
}
fmt.Println(unit)
return nil
}

View File

@ -1218,6 +1218,8 @@ method GetLayersMapWithImageInfo() -> (layerMap: string)
# BuildImageHierarchyMap is for the development of Podman and should not be used.
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.
error ImageNotFound (id: string, reason: string)

View File

@ -25,6 +25,8 @@ Command | Descr
[podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container |
[podman-export(1)](/docs/podman-export.1.md) | Export container's filesystem contents as a tar archive |
[podman-generate(1)](/docs/podman-generate.1.md) | Generate structured output based on Podman containers and pods |
[podman-generate-kube(1)](/docs/podman-generate-kube.1.md) | Generate Kubernetes YAML based on a container or Pod |
[podman-generate-systemd(1)](/docs/podman-generate-systemd.1.md) | Generate a Systemd unit file for a container |
[podman-history(1)](/docs/podman-history.1.md) | Shows the history of an image |
[podman-image(1)](/docs/podman-image.1.md) | Manage Images |
[podman-image-exists(1)](/docs/podman-image-exists.1.md) | Check if an image exists in local storage |

View File

@ -919,6 +919,7 @@ _podman_generate() {
"
subcommands="
kube
systemd
"
__podman_subcommands "$subcommands $aliases" && return
@ -2460,6 +2461,32 @@ _podman_generate_kube() {
esac
}
_podman_generate_systemd() {
local options_with_args="
--restart-policy
-t
--timeout"
local boolean_options="
-h
--help
-n
--name
"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
;;
*)
COMPREPLY=( $( compgen -W "
$(__podman_containers --all)
" -- "$cur" ) )
__ltrim_colon_completions "$cur"
;;
esac
}
_podman_play_kube() {
local options_with_args="
--authfile

View File

@ -0,0 +1,69 @@
% podman-generate Podman Man Pages
% Brent Baude
% April 2019
# NAME
podman-generate-systemd- Generate Systemd Unit file
# SYNOPSIS
**podman generate systemd** [*-n*|*--name*] [*-t*|*--timeout*] [*--restart-policy*] *container*
# 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.
# OPTIONS:
**--name** **-n**
Use the name of the container for the start, stop, and description in the unit file
**--timeout** **-t**
Override the default stop timeout for the container with the given value.
**--restart-policy**
Set the SystemD restart policy. The restart-policy must be one of: "no", "on-success", "on-failure", "on-abnormal",
"on-watchdog", "on-abort", or "always". The default policy is *on-failure*.
## Examples ##
Create a systemd unit file for a container running nginx:
```
$ sudo podman generate systemd nginx
[Unit]
Description=c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc Podman Container
[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
ExecStop=/usr/bin/podman stop -t 10 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
KillMode=none
Type=forking
PIDFile=/var/lib/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc.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.
```
$ sudo podman generate systemd --restart-policy=always -t 1 nginx
[Unit]
Description=c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc Podman Container
[Service]
Restart=always
ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
ExecStop=/usr/bin/podman stop -t 1 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc
KillMode=none
Type=forking
PIDFile=/var/lib/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc.pid
[Install]
WantedBy=multi-user.target
```
## SEE ALSO
podman(1), podman-container(1)
# HISTORY
April 2019, Originally compiled by Brent Baude (bbaude at redhat dot com)

View File

@ -14,6 +14,7 @@ The generate command will create structured output (like YAML) based on a contai
| 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. |
## SEE ALSO
podman, podman-pod, podman-container

View File

@ -593,7 +593,7 @@ Not implemented.
Restart should be handled via a systemd unit files. Please add your podman
commands to a unit file and allow systemd or your init system to handle the
restarting of the container processes. See example below.
restarting of the container processes. See *podman generate systemd*.
**--rm**=*true*|*false*
@ -1151,21 +1151,6 @@ the uids and gids from the host.
$ podman run --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello
```
### Running a podman container to restart inside of a systemd unit file
```
[Unit]
Description=My App
[Service]
Restart=always
ExecStart=/usr/bin/podman start -a my_app
ExecStop=/usr/bin/podman stop -t 10 my_app
KillMode=process
[Install]
WantedBy=multi-user.target
```
### Configuring Storage Options from the command line
Podman allows for the configuration of storage by changing the values

View File

@ -18,6 +18,7 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/adapter/shortcuts"
"github.com/containers/libpod/pkg/systemdgen"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -940,3 +941,20 @@ 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
}
timeout := int(ctr.StopTimeout())
if c.StopTimeout >= 0 {
timeout = int(c.StopTimeout)
}
name := ctr.ID()
if c.Name {
name = ctr.Name()
}
return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, ctr.Config().StaticDir, timeout)
}

View File

@ -956,3 +956,8 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) {
}
return containers, nil
}
// GenerateSystemd creates a systemd until for a container
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)
}

View File

@ -0,0 +1,43 @@
package systemdgen
import (
"fmt"
"path/filepath"
"github.com/pkg/errors"
)
var template = `[Unit]
Description=%s Podman Container
[Service]
Restart=%s
ExecStart=/usr/bin/podman start %s
ExecStop=/usr/bin/podman stop -t %d %s
KillMode=none
Type=forking
PIDFile=%s
[Install]
WantedBy=multi-user.target`
var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"}
// ValidateRestartPolicy checks that the user-provided policy is valid
func ValidateRestartPolicy(restart string) error {
for _, i := range restartPolicies {
if i == restart {
return nil
}
}
return errors.Errorf("%s is not a valid restart policy", restart)
}
// CreateSystemdUnitAsString takes variables to create a systemd unit file used to control
// a libpod container
func CreateSystemdUnitAsString(name, cid, restart, pidPath string, stopTimeout int) (string, error) {
if err := ValidateRestartPolicy(restart); err != nil {
return "", err
}
pidFile := filepath.Join(pidPath, fmt.Sprintf("%s.pid", cid))
unit := fmt.Sprintf(template, name, restart, name, stopTimeout, name, pidFile)
return unit, nil
}

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"github.com/containers/libpod/cmd/podman/shared"
iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/pkg/systemdgen"
)
// GenerateKube ...
@ -28,3 +29,24 @@ func (i *LibpodAPI) GenerateKube(call iopodman.VarlinkCall, name string, service
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)
}

View File

@ -0,0 +1,74 @@
// +build !remoteclient
package integration
import (
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"os"
)
var _ = Describe("Podman generate systemd", func() {
var (
tempdir string
err error
podmanTest *PodmanTestIntegration
)
BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
podmanTest = PodmanTestCreate(tempdir)
podmanTest.Setup()
podmanTest.RestoreAllArtifacts()
})
AfterEach(func() {
podmanTest.Cleanup()
f := CurrentGinkgoTestDescription()
processTestResult(f)
})
It("podman generate systemd on bogus container", func() {
session := podmanTest.Podman([]string{"generate", "systemd", "foobar"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
It("podman generate systemd bad restart policy", func() {
session := podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "never", "foobar"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
It("podman generate systemd bad timeout value", func() {
session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "-1", "foobar"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
It("podman generate systemd", func() {
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
n.WaitWithDefaultTimeout()
Expect(n.ExitCode()).To(Equal(0))
session := podmanTest.Podman([]string{"generate", "systemd", "nginx"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
})
It("podman generate systemd with timeout", func() {
n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx})
n.WaitWithDefaultTimeout()
Expect(n.ExitCode()).To(Equal(0))
session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "5", "nginx"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
})
})