224 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
package generate
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
 | 
						|
	"github.com/containers/common/pkg/completion"
 | 
						|
	"github.com/containers/common/pkg/report"
 | 
						|
	"github.com/containers/podman/v4/cmd/podman/common"
 | 
						|
	"github.com/containers/podman/v4/cmd/podman/registry"
 | 
						|
	"github.com/containers/podman/v4/cmd/podman/utils"
 | 
						|
	"github.com/containers/podman/v4/pkg/domain/entities"
 | 
						|
	envLib "github.com/containers/podman/v4/pkg/env"
 | 
						|
	systemDefine "github.com/containers/podman/v4/pkg/systemd/define"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
	"github.com/spf13/cobra"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	startTimeoutFlagName      = "start-timeout"
 | 
						|
	stopTimeoutFlagName       = "stop-timeout"
 | 
						|
	stopTimeoutCompatFlagName = "time"
 | 
						|
	restartPolicyFlagName     = "restart-policy"
 | 
						|
	restartSecFlagName        = "restart-sec"
 | 
						|
	newFlagName               = "new"
 | 
						|
	wantsFlagName             = "wants"
 | 
						|
	afterFlagName             = "after"
 | 
						|
	requiresFlagName          = "requires"
 | 
						|
	envFlagName               = "env"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	envInput           []string
 | 
						|
	files              bool
 | 
						|
	format             string
 | 
						|
	systemdRestart     string
 | 
						|
	systemdRestartSec  uint
 | 
						|
	startTimeout       uint
 | 
						|
	stopTimeout        uint
 | 
						|
	systemdOptions     = entities.GenerateSystemdOptions{}
 | 
						|
	systemdDescription = `Generate systemd units for a pod or container.
 | 
						|
  The generated units can later be controlled via systemctl(1).`
 | 
						|
 | 
						|
	systemdCmd = &cobra.Command{
 | 
						|
		Use:               "systemd [options] {CONTAINER|POD}",
 | 
						|
		Short:             "Generate systemd units.",
 | 
						|
		Long:              systemdDescription,
 | 
						|
		RunE:              systemd,
 | 
						|
		Args:              cobra.ExactArgs(1),
 | 
						|
		ValidArgsFunction: common.AutocompleteContainersAndPods,
 | 
						|
		Example: `podman generate systemd CTR
 | 
						|
  podman generate systemd --new --time 10 CTR
 | 
						|
  podman generate systemd --files --name POD`,
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	registry.Commands = append(registry.Commands, registry.CliCommand{
 | 
						|
		Command: systemdCmd,
 | 
						|
		Parent:  GenerateCmd,
 | 
						|
	})
 | 
						|
	flags := systemdCmd.Flags()
 | 
						|
	flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs")
 | 
						|
	flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout")
 | 
						|
	flags.BoolVar(&systemdOptions.TemplateUnitFile, "template", false, "Make it a template file and use %i and %I specifiers. Working only for containers")
 | 
						|
 | 
						|
	flags.UintVarP(&startTimeout, startTimeoutFlagName, "", 0, "Start timeout override")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(startTimeoutFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	// NOTE: initially, there was only a --time/-t flag which mapped to
 | 
						|
	// stop-timeout. To remain backwards compatible create a hidden flag
 | 
						|
	// that maps to StopTimeout.
 | 
						|
	flags.UintVarP(&stopTimeout, stopTimeoutFlagName, "", containerConfig.Engine.StopTimeout, "Stop timeout override")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone)
 | 
						|
	flags.UintVarP(&stopTimeout, stopTimeoutCompatFlagName, "t", containerConfig.Engine.StopTimeout, "Backwards alias for --stop-timeout")
 | 
						|
	_ = flags.MarkHidden("time")
 | 
						|
 | 
						|
	flags.BoolVar(&systemdOptions.New, newFlagName, false, "Create a new container or pod instead of starting an existing one")
 | 
						|
	flags.BoolVarP(&systemdOptions.NoHeader, "no-header", "", false, "Skip header generation")
 | 
						|
 | 
						|
	containerPrefixFlagName := "container-prefix"
 | 
						|
	flags.StringVar(&systemdOptions.ContainerPrefix, containerPrefixFlagName, "container", "Systemd unit name prefix for containers")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(containerPrefixFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	podPrefixFlagName := "pod-prefix"
 | 
						|
	flags.StringVar(&systemdOptions.PodPrefix, podPrefixFlagName, "pod", "Systemd unit name prefix for pods")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(podPrefixFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	separatorFlagName := "separator"
 | 
						|
	flags.StringVar(&systemdOptions.Separator, separatorFlagName, "-", "Systemd unit name separator between name/id and prefix")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(separatorFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	flags.StringVar(&systemdRestart, restartPolicyFlagName, systemDefine.DefaultRestartPolicy, "Systemd restart-policy")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(restartPolicyFlagName, common.AutocompleteSystemdRestartOptions)
 | 
						|
 | 
						|
	flags.UintVarP(&systemdRestartSec, restartSecFlagName, "", 0, "Systemd restart-sec")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(restartSecFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	formatFlagName := "format"
 | 
						|
	flags.StringVar(&format, formatFlagName, "", "Print the created units in specified format (json)")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil))
 | 
						|
 | 
						|
	flags.StringArrayVar(&systemdOptions.Wants, wantsFlagName, nil, "Add (weak) requirement dependencies to the generated unit file")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(wantsFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	flags.StringArrayVar(&systemdOptions.After, afterFlagName, nil, "Add dependencies order to the generated unit file")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(afterFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	flags.StringArrayVar(&systemdOptions.Requires, requiresFlagName, nil, "Similar to wants, but declares stronger requirement dependencies")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(requiresFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	flags.StringArrayVarP(&envInput, envFlagName, "e", nil, "Set environment variables to the systemd unit files")
 | 
						|
	_ = systemdCmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone)
 | 
						|
 | 
						|
	flags.SetNormalizeFunc(utils.TimeoutAliasFlags)
 | 
						|
}
 | 
						|
 | 
						|
func systemd(cmd *cobra.Command, args []string) error {
 | 
						|
	if cmd.Flags().Changed(restartPolicyFlagName) {
 | 
						|
		systemdOptions.RestartPolicy = &systemdRestart
 | 
						|
	}
 | 
						|
 | 
						|
	if registry.IsRemote() {
 | 
						|
		logrus.Warnln("The generated units should be placed on your remote system")
 | 
						|
	}
 | 
						|
 | 
						|
	if cmd.Flags().Changed(newFlagName) && !systemdOptions.New && systemdOptions.TemplateUnitFile {
 | 
						|
		return errors.New("--template cannot be set with --new=false")
 | 
						|
	}
 | 
						|
	if !systemdOptions.New && systemdOptions.TemplateUnitFile {
 | 
						|
		systemdOptions.New = true
 | 
						|
	}
 | 
						|
 | 
						|
	if cmd.Flags().Changed(restartSecFlagName) {
 | 
						|
		systemdOptions.RestartSec = &systemdRestartSec
 | 
						|
	}
 | 
						|
	if cmd.Flags().Changed(startTimeoutFlagName) {
 | 
						|
		systemdOptions.StartTimeout = &startTimeout
 | 
						|
	}
 | 
						|
	setStopTimeout := 0
 | 
						|
	if cmd.Flags().Changed(stopTimeoutFlagName) {
 | 
						|
		setStopTimeout++
 | 
						|
	}
 | 
						|
	if cmd.Flags().Changed(stopTimeoutCompatFlagName) {
 | 
						|
		setStopTimeout++
 | 
						|
	}
 | 
						|
	if cmd.Flags().Changed(envFlagName) {
 | 
						|
		cliEnv, err := envLib.ParseSlice(envInput)
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("parsing environment variables: %w", err)
 | 
						|
		}
 | 
						|
		systemdOptions.AdditionalEnvVariables = envLib.Slice(cliEnv)
 | 
						|
	}
 | 
						|
	switch setStopTimeout {
 | 
						|
	case 1:
 | 
						|
		systemdOptions.StopTimeout = &stopTimeout
 | 
						|
	case 2:
 | 
						|
		return fmt.Errorf("%s and %s are redundant and cannot be used together", stopTimeoutFlagName, stopTimeoutCompatFlagName)
 | 
						|
	}
 | 
						|
 | 
						|
	reports, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if files {
 | 
						|
		cwd, err := os.Getwd()
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("getting current working directory: %w", err)
 | 
						|
		}
 | 
						|
		for name, content := range reports.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
 | 
						|
			reports.Units[name] = path
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	switch {
 | 
						|
	case report.IsJSON(format):
 | 
						|
		return printJSON(reports.Units)
 | 
						|
	case format == "":
 | 
						|
		return printDefault(reports.Units)
 | 
						|
	default:
 | 
						|
		return fmt.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
 | 
						|
}
 |