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 PodmanCommand
Output string Output string
} }
type GenerateKubeValues struct { type GenerateKubeValues struct {
PodmanCommand PodmanCommand
Service bool Service bool
} }
type GenerateSystemdValues struct {
PodmanCommand
Name bool
RestartPolicy string
StopTimeout int
}
type HistoryValues struct { type HistoryValues struct {
PodmanCommand PodmanCommand
Human bool Human bool

View File

@ -18,6 +18,7 @@ var (
// Commands that are universally implemented // Commands that are universally implemented
generateCommands = []*cobra.Command{ generateCommands = []*cobra.Command{
_containerKubeCommand, _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. # 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)

View File

@ -25,6 +25,8 @@ Command | Descr
[podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container | [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-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(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-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(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 | [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=" subcommands="
kube kube
systemd
" "
__podman_subcommands "$subcommands $aliases" && return __podman_subcommands "$subcommands $aliases" && return
@ -2460,6 +2461,32 @@ _podman_generate_kube() {
esac 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() { _podman_play_kube() {
local options_with_args=" local options_with_args="
--authfile --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 | | 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. |
## SEE ALSO ## SEE ALSO
podman, podman-pod, podman-container 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 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 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* **--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 $ 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 ### Configuring Storage Options from the command line
Podman allows for the configuration of storage by changing the values 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/cmd/podman/shared"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/adapter/shortcuts" "github.com/containers/libpod/pkg/adapter/shortcuts"
"github.com/containers/libpod/pkg/systemdgen"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -940,3 +941,20 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) {
} }
return portContainers, nil 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 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" "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 ...
@ -28,3 +29,24 @@ 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)
}

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))
})
})