Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Podman machine info #14762

Merged
merged 1 commit into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions cmd/podman/machine/info.go
Original file line number Diff line number Diff line change
@@ -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
}
20 changes: 13 additions & 7 deletions cmd/podman/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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)

Expand Down
36 changes: 36 additions & 0 deletions docs/source/markdown/podman-machine-info.1.md
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be nice to have a simple go template example too.

$ 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 <[email protected]>
3 changes: 2 additions & 1 deletion docs/source/markdown/podman-machine.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -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 <[email protected]>
1 change: 1 addition & 0 deletions pkg/machine/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Provider interface {
IsValidVMName(name string) (bool, error)
CheckExclusiveActiveVM() (bool, string, error)
RemoveAndCleanMachines() error
VMType() string
}

type RemoteConnectionType string
Expand Down
20 changes: 20 additions & 0 deletions pkg/machine/e2e/config_info.go
Original file line number Diff line number Diff line change
@@ -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
}
58 changes: 58 additions & 0 deletions pkg/machine/e2e/info_test.go
Original file line number Diff line number Diff line change
@@ -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())
})
})
5 changes: 4 additions & 1 deletion pkg/machine/qemu/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 4 additions & 0 deletions pkg/machine/wsl/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1553,3 +1553,7 @@ func (p *Provider) RemoveAndCleanMachines() error {
}
return prevErr
}

func (p *Provider) VMType() string {
return vmtype
}