diff options
-rw-r--r-- | cmd/podman/machine/info.go | 182 | ||||
-rw-r--r-- | cmd/podman/machine/machine.go | 20 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine-info.1.md | 36 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine.1.md | 3 | ||||
-rw-r--r-- | pkg/machine/config.go | 1 | ||||
-rw-r--r-- | pkg/machine/e2e/config_info.go | 20 | ||||
-rw-r--r-- | pkg/machine/e2e/info_test.go | 58 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 5 | ||||
-rw-r--r-- | pkg/machine/wsl/machine.go | 4 |
9 files changed, 320 insertions, 9 deletions
diff --git a/cmd/podman/machine/info.go b/cmd/podman/machine/info.go new file mode 100644 index 000000000..9932027d8 --- /dev/null +++ b/cmd/podman/machine/info.go @@ -0,0 +1,182 @@ +//go:build amd64 || arm64 +// +build amd64 arm64 + +package machine + +import ( + "fmt" + "html/template" + "os" + "runtime" + + "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/config" + "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/validate" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/machine" + "github.com/ghodss/yaml" + "github.com/spf13/cobra" +) + +var infoDescription = `Display information pertaining to the machine host.` + +var ( + infoCmd = &cobra.Command{ + Use: "info [options]", + Short: "Display machine host info", + Long: infoDescription, + PersistentPreRunE: rootlessOnly, + RunE: info, + Args: validate.NoArgs, + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman machine info`, + } +) + +var ( + inFormat string +) + +// Info contains info on the machine host and version info +type Info struct { + Host *HostInfo `json:"Host"` + Version define.Version `json:"Version"` +} + +// HostInfo contains info on the machine host +type HostInfo struct { + Arch string `json:"Arch"` + CurrentMachine string `json:"CurrentMachine"` + DefaultMachine string `json:"DefaultMachine"` + EventsDir string `json:"EventsDir"` + MachineConfigDir string `json:"MachineConfigDir"` + MachineImageDir string `json:"MachineImageDir"` + MachineState string `json:"MachineState"` + NumberOfMachines int `json:"NumberOfMachines"` + OS string `json:"OS"` + VMType string `json:"VMType"` +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: infoCmd, + Parent: machineCmd, + }) + + flags := infoCmd.Flags() + formatFlagName := "format" + flags.StringVarP(&inFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template") + _ = infoCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&define.Info{})) +} + +func info(cmd *cobra.Command, args []string) error { + info := Info{} + version, err := define.GetVersion() + if err != nil { + return fmt.Errorf("error getting version info %w", err) + } + info.Version = version + + host, err := hostInfo() + if err != nil { + return err + } + info.Host = host + + switch { + case report.IsJSON(inFormat): + b, err := json.MarshalIndent(info, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + case cmd.Flags().Changed("format"): + tmpl := template.New(cmd.Name()).Funcs(template.FuncMap(report.DefaultFuncs)) + inFormat = report.NormalizeFormat(inFormat) + tmpl, err := tmpl.Parse(inFormat) + if err != nil { + return err + } + return tmpl.Execute(os.Stdout, info) + default: + b, err := yaml.Marshal(info) + if err != nil { + return err + } + fmt.Println(string(b)) + } + + return nil +} + +func hostInfo() (*HostInfo, error) { + host := HostInfo{} + + host.Arch = runtime.GOARCH + host.OS = runtime.GOOS + + provider := GetSystemDefaultProvider() + var listOpts machine.ListOptions + listResponse, err := provider.List(listOpts) + if err != nil { + return nil, fmt.Errorf("failed to get machines %w", err) + } + + host.NumberOfMachines = len(listResponse) + + cfg, err := config.ReadCustomConfig() + if err != nil { + return nil, err + } + + // Default state of machine is stopped + host.MachineState = "Stopped" + for _, vm := range listResponse { + // Set default machine if found + if vm.Name == cfg.Engine.ActiveService { + host.DefaultMachine = vm.Name + } + // If machine is running or starting, it is automatically the current machine + if vm.Running { + host.CurrentMachine = vm.Name + host.MachineState = "Running" + } else if vm.Starting { + host.CurrentMachine = vm.Name + host.MachineState = "Starting" + } + } + // If no machines are starting or running, set current machine to default machine + // If no default machines are found, do not report a default machine or a state + if host.CurrentMachine == "" { + if host.DefaultMachine == "" { + host.MachineState = "" + } else { + host.CurrentMachine = host.DefaultMachine + } + } + + host.VMType = provider.VMType() + + dataDir, err := machine.GetDataDir(host.VMType) + if err != nil { + return nil, fmt.Errorf("failed to get machine image dir") + } + host.MachineImageDir = dataDir + + confDir, err := machine.GetConfDir(host.VMType) + if err != nil { + return nil, fmt.Errorf("failed to get machine config dir %w", err) + } + host.MachineConfigDir = confDir + + eventsDir, err := eventSockDir() + if err != nil { + return nil, fmt.Errorf("failed to get events dir: %w", err) + } + host.EventsDir = eventsDir + + return &host, nil +} diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go index 5a8a06b9d..0618337cc 100644 --- a/cmd/podman/machine/machine.go +++ b/cmd/podman/machine/machine.go @@ -101,12 +101,6 @@ func resolveEventSock() ([]string, error) { return []string{sock}, nil } - xdg, err := util.GetRuntimeDir() - if err != nil { - logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err) - return nil, nil - } - re := regexp.MustCompile(`machine_events.*\.sock`) sockPaths := make([]string, 0) fn := func(path string, info os.DirEntry, err error) error { @@ -125,8 +119,12 @@ func resolveEventSock() ([]string, error) { sockPaths = append(sockPaths, path) return nil } + sockDir, err := eventSockDir() + if err != nil { + logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err) + } - if err := filepath.WalkDir(filepath.Join(xdg, "podman"), fn); err != nil { + if err := filepath.WalkDir(sockDir, fn); err != nil { if errors.Is(err, os.ErrNotExist) { return nil, nil } @@ -135,6 +133,14 @@ func resolveEventSock() ([]string, error) { return sockPaths, nil } +func eventSockDir() (string, error) { + xdg, err := util.GetRuntimeDir() + if err != nil { + return "", err + } + return filepath.Join(xdg, "podman"), nil +} + func newMachineEvent(status events.Status, event events.Event) { openEventSock.Do(initMachineEvents) diff --git a/docs/source/markdown/podman-machine-info.1.md b/docs/source/markdown/podman-machine-info.1.md new file mode 100644 index 000000000..33c207d32 --- /dev/null +++ b/docs/source/markdown/podman-machine-info.1.md @@ -0,0 +1,36 @@ +% podman-machine-info(1) + +## NAME +podman\-machine\-info - Display machine host info + +## SYNOPSIS +**podman machine info** + +## DESCRIPTION + +Display information pertaining to the machine host. +Rootless only, as all `podman machine` commands can be only be used with rootless Podman. + +## OPTIONS + +#### **--format**=*format*, **-f** + +Change output format to "json" or a Go template. + +#### **--help** + +Print usage statement. + +## EXAMPLES + +``` +$ podman machine info +$ podman machine info --format json +$ podman machine info --format {{.Host.Arch}} +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)** + +## HISTORY +June 2022, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md index c55226e02..6197b8d4e 100644 --- a/docs/source/markdown/podman-machine.1.md +++ b/docs/source/markdown/podman-machine.1.md @@ -20,6 +20,7 @@ All `podman machine` commands are rootless only. | Command | Man Page | Description | |---------|------------------------------------------------------|-----------------------------------| +| info | [podman-machine-info(1)](podman-machine-info.1.md) | Display machine host info | | init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine | | inspect | [podman-machine-inspect(1)](podman-machine-inspect.1.md) | Inspect one or more virtual machines | | list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines | @@ -30,7 +31,7 @@ All `podman machine` commands are rootless only. | stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)** +**[podman(1)](podman.1.md)**, **[podman-machine-info(1)](podman-machine-info.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)** ## HISTORY March 2021, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/pkg/machine/config.go b/pkg/machine/config.go index fcc129338..66fa6ab91 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -55,6 +55,7 @@ type Provider interface { IsValidVMName(name string) (bool, error) CheckExclusiveActiveVM() (bool, string, error) RemoveAndCleanMachines() error + VMType() string } type RemoteConnectionType string diff --git a/pkg/machine/e2e/config_info.go b/pkg/machine/e2e/config_info.go new file mode 100644 index 000000000..410c7e518 --- /dev/null +++ b/pkg/machine/e2e/config_info.go @@ -0,0 +1,20 @@ +package e2e + +type infoMachine struct { + format string + cmd []string +} + +func (i *infoMachine) buildCmd(m *machineTestBuilder) []string { + cmd := []string{"machine", "info"} + if len(i.format) > 0 { + cmd = append(cmd, "--format", i.format) + } + i.cmd = cmd + return cmd +} + +func (i *infoMachine) withFormat(format string) *infoMachine { + i.format = format + return i +} diff --git a/pkg/machine/e2e/info_test.go b/pkg/machine/e2e/info_test.go new file mode 100644 index 000000000..eeabb78af --- /dev/null +++ b/pkg/machine/e2e/info_test.go @@ -0,0 +1,58 @@ +package e2e + +import ( + "github.com/containers/podman/v4/cmd/podman/machine" + jsoniter "github.com/json-iterator/go" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("podman machine info", func() { + var ( + mb *machineTestBuilder + testDir string + ) + + BeforeEach(func() { + testDir, mb = setup() + }) + AfterEach(func() { + teardown(originalHomeDir, testDir, mb) + }) + + It("machine info", func() { + info := new(infoMachine) + infoSession, err := mb.setCmd(info).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + + // Verify go template works and check for no running machines + info = new(infoMachine) + infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + Expect(infoSession.outputToString()).To(Equal("0")) + + // Create a machine and check if info has been updated + i := new(initMachine) + initSession, err := mb.setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).To(BeNil()) + Expect(initSession).To(Exit(0)) + + info = new(infoMachine) + infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + Expect(infoSession.outputToString()).To(Equal("1")) + + // Check if json is in correct format + infoSession, err = mb.setCmd(info.withFormat("json")).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + + infoReport := &machine.Info{} + err = jsoniter.Unmarshal(infoSession.Bytes(), infoReport) + Expect(err).To(BeNil()) + }) +}) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 2fe0230cf..616d573eb 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -1698,6 +1698,9 @@ func isProcessAlive(pid int) bool { if err == nil || err == unix.EPERM { return true } - return false } + +func (p *Provider) VMType() string { + return vmtype +} diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 075f42cb2..6553fb6a5 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -1553,3 +1553,7 @@ func (p *Provider) RemoveAndCleanMachines() error { } return prevErr } + +func (p *Provider) VMType() string { + return vmtype +} |