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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/podman/v2/cmd/podman/registry"
|
||||
"github.com/containers/podman/v2/cmd/podman/utils"
|
||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
files bool
|
||||
format string
|
||||
systemdTimeout uint
|
||||
systemdOptions = entities.GenerateSystemdOptions{}
|
||||
systemdDescription = `Generate systemd units for a pod or container.
|
||||
@ -29,19 +36,20 @@ var (
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode},
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: systemdCmd,
|
||||
Parent: generateCmd,
|
||||
})
|
||||
flags := systemdCmd.Flags()
|
||||
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.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.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.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)
|
||||
}
|
||||
|
||||
@ -50,11 +58,68 @@ func systemd(cmd *cobra.Command, args []string) error {
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
@ -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.
|
||||
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:
|
||||
|
||||
@ -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`.
|
||||
|
||||
**--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**
|
||||
|
||||
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/domain/entities"
|
||||
"github.com/containers/podman/v2/pkg/domain/infra/abi"
|
||||
"github.com/containers/podman/v2/pkg/util"
|
||||
"github.com/gorilla/schema"
|
||||
"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) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
|
@ -8,6 +8,68 @@ import (
|
||||
)
|
||||
|
||||
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
|
||||
// ---
|
||||
// tags:
|
||||
|
@ -10,6 +10,33 @@ import (
|
||||
"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) {
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
|
@ -4,8 +4,6 @@ import "io"
|
||||
|
||||
// GenerateSystemdOptions control the generation of systemd unit files.
|
||||
type GenerateSystemdOptions struct {
|
||||
// Files - generate files instead of printing to stdout.
|
||||
Files bool
|
||||
// Name - use container/pod name instead of its ID.
|
||||
Name bool
|
||||
// New - create a new container instead of starting a new one.
|
||||
@ -24,9 +22,8 @@ type GenerateSystemdOptions struct {
|
||||
|
||||
// GenerateSystemdReport
|
||||
type GenerateSystemdReport struct {
|
||||
// Output of the generate process. Either the generated files or their
|
||||
// entire content.
|
||||
Output string
|
||||
// Units of the generate process. key = unit name -> value = unit content
|
||||
Units map[string]string
|
||||
}
|
||||
|
||||
// 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)
|
||||
if ctrErr == nil {
|
||||
// Generate the unit for the container.
|
||||
s, err := generate.ContainerUnit(ctr, options)
|
||||
name, content, err := generate.ContainerUnit(ctr, options)
|
||||
if err != nil {
|
||||
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.
|
||||
@ -34,11 +34,11 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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) {
|
||||
|
@ -5,11 +5,10 @@ import (
|
||||
|
||||
"github.com/containers/podman/v2/pkg/bindings/generate"
|
||||
"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) {
|
||||
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) {
|
||||
|
@ -3,9 +3,7 @@ package generate
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
@ -87,17 +85,22 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
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) {
|
||||
@ -288,18 +291,5 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !options.Files {
|
||||
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
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
@ -56,7 +56,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
goodName := `# container-foobar.service
|
||||
# autogenerated by Podman CI
|
||||
@ -78,7 +79,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
goodNameBoundTo := `# container-foobar.service
|
||||
# autogenerated by Podman CI
|
||||
@ -102,7 +104,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
goodWithNameAndGeneric := `# jadda-jadda.service
|
||||
# autogenerated by Podman CI
|
||||
@ -125,7 +128,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
goodWithExplicitShortDetachParam := `# jadda-jadda.service
|
||||
# autogenerated by Podman CI
|
||||
@ -148,7 +152,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
goodNameNewWithPodFile := `# jadda-jadda.service
|
||||
# autogenerated by Podman CI
|
||||
@ -171,7 +176,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
goodNameNewDetach := `# jadda-jadda.service
|
||||
# autogenerated by Podman CI
|
||||
@ -194,7 +200,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
|
||||
# autogenerated by Podman CI
|
||||
@ -217,7 +224,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -375,8 +383,7 @@ WantedBy=multi-user.target default.target`
|
||||
test := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
opts := entities.GenerateSystemdOptions{
|
||||
Files: false,
|
||||
New: test.new,
|
||||
New: test.new,
|
||||
}
|
||||
got, err := executeContainerTemplate(&test.info, opts)
|
||||
if (err != nil) != test.wantErr {
|
||||
|
@ -3,9 +3,7 @@ package generate
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
@ -88,39 +86,40 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// main service.
|
||||
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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
infraID, err := pod.InfraContainerID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compute the container-dependency graph for the Pod.
|
||||
containers, err := pod.AllContainers()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
// Now add the container's dependencies and at the container as a
|
||||
// required service of the infra container.
|
||||
@ -149,24 +148,23 @@ func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (string,
|
||||
containerInfos = append(containerInfos, ctrInfo)
|
||||
}
|
||||
|
||||
units := map[string]string{}
|
||||
// Now generate the systemd service for all containers.
|
||||
builder := strings.Builder{}
|
||||
out, err := executePodTemplate(podInfo, options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
builder.WriteString(out)
|
||||
units[podInfo.ServiceName] = out
|
||||
for _, info := range containerInfos {
|
||||
info.pod = podInfo
|
||||
builder.WriteByte('\n')
|
||||
out, err := executeContainerTemplate(info, options)
|
||||
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) {
|
||||
@ -339,18 +337,5 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !options.Files {
|
||||
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
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
@ -58,7 +58,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
podGoodNamedNew := `# pod-123abc.service
|
||||
# autogenerated by Podman CI
|
||||
@ -84,7 +85,8 @@ KillMode=none
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target`
|
||||
WantedBy=multi-user.target default.target
|
||||
`
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -130,8 +132,7 @@ WantedBy=multi-user.target default.target`
|
||||
test := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
opts := entities.GenerateSystemdOptions{
|
||||
Files: false,
|
||||
New: test.new,
|
||||
New: test.new,
|
||||
}
|
||||
got, err := executePodTemplate(&test.info, opts)
|
||||
if (err != nil) != test.wantErr {
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build !remote
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
@ -61,7 +59,7 @@ var _ = Describe("Podman generate systemd", func() {
|
||||
session = podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "bogus", "foobar"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
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())
|
||||
})
|
||||
|
||||
@ -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")
|
||||
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