mirror of
https://github.com/containers/podman.git
synced 2025-06-23 18:59:30 +08:00
APIv2 add generate systemd endpoint
Add support for generating systemd units via the api and podman-remote. Change the GenerateSystemdReport type to return the units as map[string]string with the unit name as key. Add `--format` flag to `podman generate systemd` to allow the output to be formatted as json. Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
This commit is contained in:
@ -1,15 +1,22 @@
|
|||||||
package pods
|
package pods
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containers/podman/v2/cmd/podman/registry"
|
"github.com/containers/podman/v2/cmd/podman/registry"
|
||||||
"github.com/containers/podman/v2/cmd/podman/utils"
|
"github.com/containers/podman/v2/cmd/podman/utils"
|
||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
files bool
|
||||||
|
format string
|
||||||
systemdTimeout uint
|
systemdTimeout uint
|
||||||
systemdOptions = entities.GenerateSystemdOptions{}
|
systemdOptions = entities.GenerateSystemdOptions{}
|
||||||
systemdDescription = `Generate systemd units for a pod or container.
|
systemdDescription = `Generate systemd units for a pod or container.
|
||||||
@ -29,19 +36,20 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
Mode: []entities.EngineMode{entities.ABIMode},
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
Command: systemdCmd,
|
Command: systemdCmd,
|
||||||
Parent: generateCmd,
|
Parent: generateCmd,
|
||||||
})
|
})
|
||||||
flags := systemdCmd.Flags()
|
flags := systemdCmd.Flags()
|
||||||
flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs")
|
flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs")
|
||||||
flags.BoolVarP(&systemdOptions.Files, "files", "f", false, "Generate .service files instead of printing to stdout")
|
flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout")
|
||||||
flags.UintVarP(&systemdTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Stop timeout override")
|
flags.UintVarP(&systemdTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Stop timeout override")
|
||||||
flags.StringVar(&systemdOptions.RestartPolicy, "restart-policy", "on-failure", "Systemd restart-policy")
|
flags.StringVar(&systemdOptions.RestartPolicy, "restart-policy", "on-failure", "Systemd restart-policy")
|
||||||
flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one")
|
flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one")
|
||||||
flags.StringVar(&systemdOptions.ContainerPrefix, "container-prefix", "container", "Systemd unit name prefix for containers")
|
flags.StringVar(&systemdOptions.ContainerPrefix, "container-prefix", "container", "Systemd unit name prefix for containers")
|
||||||
flags.StringVar(&systemdOptions.PodPrefix, "pod-prefix", "pod", "Systemd unit name prefix for pods")
|
flags.StringVar(&systemdOptions.PodPrefix, "pod-prefix", "pod", "Systemd unit name prefix for pods")
|
||||||
flags.StringVar(&systemdOptions.Separator, "separator", "-", "Systemd unit name separator between name/id and prefix")
|
flags.StringVar(&systemdOptions.Separator, "separator", "-", "Systemd unit name separator between name/id and prefix")
|
||||||
|
flags.StringVar(&format, "format", "", "Print the created units in specified format (json)")
|
||||||
flags.SetNormalizeFunc(utils.AliasFlags)
|
flags.SetNormalizeFunc(utils.AliasFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,11 +58,68 @@ func systemd(cmd *cobra.Command, args []string) error {
|
|||||||
systemdOptions.StopTimeout = &systemdTimeout
|
systemdOptions.StopTimeout = &systemdTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if registry.IsRemote() {
|
||||||
|
logrus.Warnln("The generated units should be placed on your remote system")
|
||||||
|
}
|
||||||
|
|
||||||
report, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions)
|
report, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(report.Output)
|
if files {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error getting current working directory")
|
||||||
|
}
|
||||||
|
for name, content := range report.Units {
|
||||||
|
path := filepath.Join(cwd, fmt.Sprintf("%s.service", name))
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.WriteString(content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add newline if default format is given
|
||||||
|
if format == "" {
|
||||||
|
path += "\n"
|
||||||
|
}
|
||||||
|
// modify in place so we can print the
|
||||||
|
// paths when --files is set
|
||||||
|
report.Units[name] = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case "json":
|
||||||
|
return printJSON(report.Units)
|
||||||
|
case "":
|
||||||
|
return printDefault(report.Units)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("unknown --format argument: %s", format)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func printDefault(units map[string]string) error {
|
||||||
|
for _, content := range units {
|
||||||
|
fmt.Print(content)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printJSON(units map[string]string) error {
|
||||||
|
b, err := json.MarshalIndent(units, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ podman\-generate\-systemd - Generate systemd unit file(s) for a container or pod
|
|||||||
**podman generate systemd** will create a systemd unit file that can be used to control a container or pod.
|
**podman generate systemd** will create a systemd unit file that can be used to control a container or pod.
|
||||||
By default, the command will print the content of the unit files to stdout.
|
By default, the command will print the content of the unit files to stdout.
|
||||||
|
|
||||||
Note that this command is not supported for the remote client.
|
_Note: If you use this command with the remote client, you would still have to place the generated units on the remote system._
|
||||||
|
|
||||||
## OPTIONS:
|
## OPTIONS:
|
||||||
|
|
||||||
@ -20,6 +20,10 @@ Generate files instead of printing to stdout. The generated files are named {co
|
|||||||
|
|
||||||
Note: On a system with SELinux enabled, the generated files will inherit contexts from the current working directory. Depending on the SELinux setup, changes to the generated files using `restorecon`, `chcon`, or `semanage` may be required to allow systemd to access these files. Alternatively, use the `-Z` option when running `mv` or `cp`.
|
Note: On a system with SELinux enabled, the generated files will inherit contexts from the current working directory. Depending on the SELinux setup, changes to the generated files using `restorecon`, `chcon`, or `semanage` may be required to allow systemd to access these files. Alternatively, use the `-Z` option when running `mv` or `cp`.
|
||||||
|
|
||||||
|
**--format**=*format*
|
||||||
|
|
||||||
|
Print the created units in specified format (json). If `--files` is specified the paths to the created files will be printed instead of the unit content.
|
||||||
|
|
||||||
**--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
|
||||||
|
@ -7,10 +7,55 @@ import (
|
|||||||
"github.com/containers/podman/v2/pkg/api/handlers/utils"
|
"github.com/containers/podman/v2/pkg/api/handlers/utils"
|
||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||||
"github.com/containers/podman/v2/pkg/domain/infra/abi"
|
"github.com/containers/podman/v2/pkg/domain/infra/abi"
|
||||||
|
"github.com/containers/podman/v2/pkg/util"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
|
||||||
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
|
query := struct {
|
||||||
|
Name bool `schema:"useName"`
|
||||||
|
New bool `schema:"new"`
|
||||||
|
RestartPolicy string `schema:"restartPolicy"`
|
||||||
|
StopTimeout uint `schema:"stopTimeout"`
|
||||||
|
ContainerPrefix string `schema:"containerPrefix"`
|
||||||
|
PodPrefix string `schema:"podPrefix"`
|
||||||
|
Separator string `schema:"separator"`
|
||||||
|
}{
|
||||||
|
RestartPolicy: "on-failure",
|
||||||
|
StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout,
|
||||||
|
ContainerPrefix: "container",
|
||||||
|
PodPrefix: "pod",
|
||||||
|
Separator: "-",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
|
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
|
||||||
|
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
containerEngine := abi.ContainerEngine{Libpod: runtime}
|
||||||
|
options := entities.GenerateSystemdOptions{
|
||||||
|
Name: query.Name,
|
||||||
|
New: query.New,
|
||||||
|
RestartPolicy: query.RestartPolicy,
|
||||||
|
StopTimeout: &query.StopTimeout,
|
||||||
|
ContainerPrefix: query.ContainerPrefix,
|
||||||
|
PodPrefix: query.PodPrefix,
|
||||||
|
Separator: query.Separator,
|
||||||
|
}
|
||||||
|
report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options)
|
||||||
|
if err != nil {
|
||||||
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error generating systemd units"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.WriteResponse(w, http.StatusOK, report.Units)
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateKube(w http.ResponseWriter, r *http.Request) {
|
func GenerateKube(w http.ResponseWriter, r *http.Request) {
|
||||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
|
@ -8,6 +8,68 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
|
func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
|
||||||
|
// swagger:operation GET /libpod/generate/{name:.*}/systemd libpod libpodGenerateSystemd
|
||||||
|
// ---
|
||||||
|
// tags:
|
||||||
|
// - containers
|
||||||
|
// - pods
|
||||||
|
// summary: Generate Systemd Units
|
||||||
|
// description: Generate Systemd Units based on a pod or container.
|
||||||
|
// parameters:
|
||||||
|
// - in: path
|
||||||
|
// name: name:.*
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// description: Name or ID of the container or pod.
|
||||||
|
// - in: query
|
||||||
|
// name: useName
|
||||||
|
// type: boolean
|
||||||
|
// default: false
|
||||||
|
// description: Use container/pod names instead of IDs.
|
||||||
|
// - in: query
|
||||||
|
// name: new
|
||||||
|
// type: boolean
|
||||||
|
// default: false
|
||||||
|
// description: Create a new container instead of starting an existing one.
|
||||||
|
// - in: query
|
||||||
|
// name: time
|
||||||
|
// type: integer
|
||||||
|
// default: 10
|
||||||
|
// description: Stop timeout override.
|
||||||
|
// - in: query
|
||||||
|
// name: restartPolicy
|
||||||
|
// default: on-failure
|
||||||
|
// type: string
|
||||||
|
// enum: ["no", on-success, on-failure, on-abnormal, on-watchdog, on-abort, always]
|
||||||
|
// description: Systemd restart-policy.
|
||||||
|
// - in: query
|
||||||
|
// name: containerPrefix
|
||||||
|
// type: string
|
||||||
|
// default: container
|
||||||
|
// description: Systemd unit name prefix for containers.
|
||||||
|
// - in: query
|
||||||
|
// name: podPrefix
|
||||||
|
// type: string
|
||||||
|
// default: pod
|
||||||
|
// description: Systemd unit name prefix for pods.
|
||||||
|
// - in: query
|
||||||
|
// name: separator
|
||||||
|
// type: string
|
||||||
|
// default: "-"
|
||||||
|
// description: Systemd unit name separator between name/id and prefix.
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// responses:
|
||||||
|
// 200:
|
||||||
|
// description: no error
|
||||||
|
// schema:
|
||||||
|
// type: object
|
||||||
|
// additionalProperties:
|
||||||
|
// type: string
|
||||||
|
// 500:
|
||||||
|
// $ref: "#/responses/InternalError"
|
||||||
|
r.HandleFunc(VersionedPath("/libpod/generate/{name:.*}/systemd"), s.APIHandler(libpod.GenerateSystemd)).Methods(http.MethodGet)
|
||||||
|
|
||||||
// swagger:operation GET /libpod/generate/{name:.*}/kube libpod libpodGenerateKube
|
// swagger:operation GET /libpod/generate/{name:.*}/kube libpod libpodGenerateKube
|
||||||
// ---
|
// ---
|
||||||
// tags:
|
// tags:
|
||||||
|
@ -10,6 +10,33 @@ import (
|
|||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Systemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
|
||||||
|
conn, err := bindings.GetClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
params := url.Values{}
|
||||||
|
|
||||||
|
params.Set("useName", strconv.FormatBool(options.Name))
|
||||||
|
params.Set("new", strconv.FormatBool(options.New))
|
||||||
|
if options.RestartPolicy != "" {
|
||||||
|
params.Set("restartPolicy", options.RestartPolicy)
|
||||||
|
}
|
||||||
|
if options.StopTimeout != nil {
|
||||||
|
params.Set("stopTimeout", strconv.FormatUint(uint64(*options.StopTimeout), 10))
|
||||||
|
}
|
||||||
|
params.Set("containerPrefix", options.ContainerPrefix)
|
||||||
|
params.Set("podPrefix", options.PodPrefix)
|
||||||
|
params.Set("separator", options.Separator)
|
||||||
|
|
||||||
|
response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/systemd", params, nil, nameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
report := &entities.GenerateSystemdReport{}
|
||||||
|
return report, response.Process(&report.Units)
|
||||||
|
}
|
||||||
|
|
||||||
func Kube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
func Kube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
||||||
conn, err := bindings.GetClient(ctx)
|
conn, err := bindings.GetClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -4,8 +4,6 @@ import "io"
|
|||||||
|
|
||||||
// GenerateSystemdOptions control the generation of systemd unit files.
|
// GenerateSystemdOptions control the generation of systemd unit files.
|
||||||
type GenerateSystemdOptions struct {
|
type GenerateSystemdOptions struct {
|
||||||
// Files - generate files instead of printing to stdout.
|
|
||||||
Files bool
|
|
||||||
// Name - use container/pod name instead of its ID.
|
// Name - use container/pod name instead of its ID.
|
||||||
Name bool
|
Name bool
|
||||||
// New - create a new container instead of starting a new one.
|
// New - create a new container instead of starting a new one.
|
||||||
@ -24,9 +22,8 @@ type GenerateSystemdOptions struct {
|
|||||||
|
|
||||||
// GenerateSystemdReport
|
// GenerateSystemdReport
|
||||||
type GenerateSystemdReport struct {
|
type GenerateSystemdReport struct {
|
||||||
// Output of the generate process. Either the generated files or their
|
// Units of the generate process. key = unit name -> value = unit content
|
||||||
// entire content.
|
Units map[string]string
|
||||||
Output string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateKubeOptions control the generation of Kubernetes YAML files.
|
// GenerateKubeOptions control the generation of Kubernetes YAML files.
|
||||||
|
@ -19,11 +19,11 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
|
|||||||
ctr, ctrErr := ic.Libpod.LookupContainer(nameOrID)
|
ctr, ctrErr := ic.Libpod.LookupContainer(nameOrID)
|
||||||
if ctrErr == nil {
|
if ctrErr == nil {
|
||||||
// Generate the unit for the container.
|
// Generate the unit for the container.
|
||||||
s, err := generate.ContainerUnit(ctr, options)
|
name, content, err := generate.ContainerUnit(ctr, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &entities.GenerateSystemdReport{Output: s}, nil
|
return &entities.GenerateSystemdReport{Units: map[string]string{name: content}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's not a container, we either have a pod or garbage.
|
// If it's not a container, we either have a pod or garbage.
|
||||||
@ -34,11 +34,11 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate the units for the pod and all its containers.
|
// Generate the units for the pod and all its containers.
|
||||||
s, err := generate.PodUnits(pod, options)
|
units, err := generate.PodUnits(pod, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &entities.GenerateSystemdReport{Output: s}, nil
|
return &entities.GenerateSystemdReport{Units: units}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
||||||
|
@ -5,11 +5,10 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/podman/v2/pkg/bindings/generate"
|
"github.com/containers/podman/v2/pkg/bindings/generate"
|
||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
|
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
|
||||||
return nil, errors.New("not implemented for tunnel")
|
return generate.Systemd(ic.ClientCxt, nameOrID, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
||||||
|
@ -3,9 +3,7 @@ package generate
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
@ -87,17 +85,22 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
// ContainerUnit generates a systemd unit for the specified container. Based
|
// ContainerUnit generates a systemd unit for the specified container. Based
|
||||||
// on the options, the return value might be the entire unit or a file it has
|
// on the options, the return value might be the entire unit or a file it has
|
||||||
// been written to.
|
// been written to.
|
||||||
func ContainerUnit(ctr *libpod.Container, options entities.GenerateSystemdOptions) (string, error) {
|
func ContainerUnit(ctr *libpod.Container, options entities.GenerateSystemdOptions) (string, string, error) {
|
||||||
info, err := generateContainerInfo(ctr, options)
|
info, err := generateContainerInfo(ctr, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return executeContainerTemplate(info, options)
|
content, err := executeContainerTemplate(info, options)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return info.ServiceName, content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSystemdOptions) (*containerInfo, error) {
|
func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSystemdOptions) (*containerInfo, error) {
|
||||||
@ -288,18 +291,5 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.Files {
|
|
||||||
return buf.String(), nil
|
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
|
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
goodName := `# container-foobar.service
|
goodName := `# container-foobar.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -78,7 +79,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
goodNameBoundTo := `# container-foobar.service
|
goodNameBoundTo := `# container-foobar.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -102,7 +104,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
goodWithNameAndGeneric := `# jadda-jadda.service
|
goodWithNameAndGeneric := `# jadda-jadda.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -125,7 +128,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
goodWithExplicitShortDetachParam := `# jadda-jadda.service
|
goodWithExplicitShortDetachParam := `# jadda-jadda.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -148,7 +152,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
goodNameNewWithPodFile := `# jadda-jadda.service
|
goodNameNewWithPodFile := `# jadda-jadda.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -171,7 +176,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
goodNameNewDetach := `# jadda-jadda.service
|
goodNameNewDetach := `# jadda-jadda.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -194,7 +200,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -217,7 +224,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -375,7 +383,6 @@ WantedBy=multi-user.target default.target`
|
|||||||
test := tt
|
test := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
opts := entities.GenerateSystemdOptions{
|
opts := entities.GenerateSystemdOptions{
|
||||||
Files: false,
|
|
||||||
New: test.new,
|
New: test.new,
|
||||||
}
|
}
|
||||||
got, err := executeContainerTemplate(&test.info, opts)
|
got, err := executeContainerTemplate(&test.info, opts)
|
||||||
|
@ -3,9 +3,7 @@ package generate
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
@ -88,39 +86,40 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
// PodUnits generates systemd units for the specified pod and its containers.
|
// PodUnits generates systemd units for the specified pod and its containers.
|
||||||
// Based on the options, the return value might be the content of all units or
|
// Based on the options, the return value might be the content of all units or
|
||||||
// the files they been written to.
|
// the files they been written to.
|
||||||
func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (string, error) {
|
func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (map[string]string, error) {
|
||||||
// Error out if the pod has no infra container, which we require to be the
|
// Error out if the pod has no infra container, which we require to be the
|
||||||
// main service.
|
// main service.
|
||||||
if !pod.HasInfraContainer() {
|
if !pod.HasInfraContainer() {
|
||||||
return "", errors.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name())
|
return nil, errors.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
podInfo, err := generatePodInfo(pod, options)
|
podInfo, err := generatePodInfo(pod, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
infraID, err := pod.InfraContainerID()
|
infraID, err := pod.InfraContainerID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the container-dependency graph for the Pod.
|
// Compute the container-dependency graph for the Pod.
|
||||||
containers, err := pod.AllContainers()
|
containers, err := pod.AllContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(containers) == 0 {
|
if len(containers) == 0 {
|
||||||
return "", errors.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name())
|
return nil, errors.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name())
|
||||||
}
|
}
|
||||||
graph, err := libpod.BuildContainerGraph(containers)
|
graph, err := libpod.BuildContainerGraph(containers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traverse the dependency graph and create systemdgen.containerInfo's for
|
// Traverse the dependency graph and create systemdgen.containerInfo's for
|
||||||
@ -133,7 +132,7 @@ func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (string,
|
|||||||
}
|
}
|
||||||
ctrInfo, err := generateContainerInfo(ctr, options)
|
ctrInfo, err := generateContainerInfo(ctr, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Now add the container's dependencies and at the container as a
|
// Now add the container's dependencies and at the container as a
|
||||||
// required service of the infra container.
|
// required service of the infra container.
|
||||||
@ -149,24 +148,23 @@ func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (string,
|
|||||||
containerInfos = append(containerInfos, ctrInfo)
|
containerInfos = append(containerInfos, ctrInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
units := map[string]string{}
|
||||||
// Now generate the systemd service for all containers.
|
// Now generate the systemd service for all containers.
|
||||||
builder := strings.Builder{}
|
|
||||||
out, err := executePodTemplate(podInfo, options)
|
out, err := executePodTemplate(podInfo, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
builder.WriteString(out)
|
units[podInfo.ServiceName] = out
|
||||||
for _, info := range containerInfos {
|
for _, info := range containerInfos {
|
||||||
info.pod = podInfo
|
info.pod = podInfo
|
||||||
builder.WriteByte('\n')
|
|
||||||
out, err := executeContainerTemplate(info, options)
|
out, err := executeContainerTemplate(info, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
builder.WriteString(out)
|
units[info.ServiceName] = out
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.String(), nil
|
return units, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) (*podInfo, error) {
|
func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) (*podInfo, error) {
|
||||||
@ -339,18 +337,5 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.Files {
|
|
||||||
return buf.String(), nil
|
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
|
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
podGoodNamedNew := `# pod-123abc.service
|
podGoodNamedNew := `# pod-123abc.service
|
||||||
# autogenerated by Podman CI
|
# autogenerated by Podman CI
|
||||||
@ -84,7 +85,8 @@ KillMode=none
|
|||||||
Type=forking
|
Type=forking
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target default.target`
|
WantedBy=multi-user.target default.target
|
||||||
|
`
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -130,7 +132,6 @@ WantedBy=multi-user.target default.target`
|
|||||||
test := tt
|
test := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
opts := entities.GenerateSystemdOptions{
|
opts := entities.GenerateSystemdOptions{
|
||||||
Files: false,
|
|
||||||
New: test.new,
|
New: test.new,
|
||||||
}
|
}
|
||||||
got, err := executePodTemplate(&test.info, opts)
|
got, err := executePodTemplate(&test.info, opts)
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build !remote
|
|
||||||
|
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -61,7 +59,7 @@ var _ = Describe("Podman generate systemd", func() {
|
|||||||
session = podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "bogus", "foobar"})
|
session = podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "bogus", "foobar"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session).To(ExitWithError())
|
Expect(session).To(ExitWithError())
|
||||||
found, _ := session.ErrorGrepString("Error: bogus is not a valid restart policy")
|
found, _ := session.ErrorGrepString("bogus is not a valid restart policy")
|
||||||
Expect(found).Should(BeTrue())
|
Expect(found).Should(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -383,4 +381,15 @@ var _ = Describe("Podman generate systemd", func() {
|
|||||||
found, _ = session.GrepString("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id")
|
found, _ = session.GrepString("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id")
|
||||||
Expect(found).To(BeTrue())
|
Expect(found).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman generate systemd --format json", func() {
|
||||||
|
n := podmanTest.Podman([]string{"create", "--name", "foo", ALPINE})
|
||||||
|
n.WaitWithDefaultTimeout()
|
||||||
|
Expect(n.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session := podmanTest.Podman([]string{"generate", "systemd", "--format", "json", "foo"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(session.IsJSONOutputValid()).To(BeTrue())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user