automation-tests/cmd/podman/machine/list.go

224 lines
5.8 KiB
Go

//go:build amd64 || arm64
// +build amd64 arm64
package machine
import (
"fmt"
"os"
"sort"
"strconv"
"time"
"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/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/machine"
"github.com/docker/go-units"
"github.com/spf13/cobra"
)
var (
lsCmd = &cobra.Command{
Use: "list [options]",
Aliases: []string{"ls"},
Short: "List machines",
Long: "List managed virtual machines.",
PersistentPreRunE: machinePreRunE,
RunE: list,
Args: validate.NoArgs,
ValidArgsFunction: completion.AutocompleteNone,
Example: `podman machine list,
podman machine list --format json
podman machine ls`,
}
listFlag = listFlagType{}
)
type listFlagType struct {
format string
noHeading bool
quiet bool
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: lsCmd,
Parent: machineCmd,
})
flags := lsCmd.Flags()
formatFlagName := "format"
flags.StringVar(&listFlag.format, formatFlagName, "{{range .}}{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n{{end -}}", "Format volume output using JSON or a Go template")
_ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.ListReporter{}))
flags.BoolVarP(&listFlag.noHeading, "noheading", "n", false, "Do not print headers")
flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Show only machine names")
}
func list(cmd *cobra.Command, args []string) error {
var (
opts machine.ListOptions
listResponse []*machine.ListResponse
err error
)
listResponse, err = provider.List(opts)
if err != nil {
return fmt.Errorf("listing vms: %w", err)
}
// Sort by last run
sort.Slice(listResponse, func(i, j int) bool {
return listResponse[i].LastUp.After(listResponse[j].LastUp)
})
// Bring currently running machines to top
sort.Slice(listResponse, func(i, j int) bool {
return listResponse[i].Running
})
if report.IsJSON(listFlag.format) {
machineReporter, err := toMachineFormat(listResponse)
if err != nil {
return err
}
b, err := json.MarshalIndent(machineReporter, "", " ")
if err != nil {
return err
}
os.Stdout.Write(b)
return nil
}
machineReporter, err := toHumanFormat(listResponse)
if err != nil {
return err
}
return outputTemplate(cmd, machineReporter)
}
func outputTemplate(cmd *cobra.Command, responses []*entities.ListReporter) error {
headers := report.Headers(entities.ListReporter{}, map[string]string{
"LastUp": "LAST UP",
"VmType": "VM TYPE",
"CPUs": "CPUS",
"Memory": "MEMORY",
"DiskSize": "DISK SIZE",
})
rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()
var err error
switch {
case cmd.Flag("format").Changed:
rpt, err = rpt.Parse(report.OriginUser, listFlag.format)
case listFlag.quiet:
rpt, err = rpt.Parse(report.OriginUser, "{{.Name}}\n")
default:
rpt, err = rpt.Parse(report.OriginPodman, listFlag.format)
}
if err != nil {
return err
}
if rpt.RenderHeaders && !listFlag.noHeading {
if err := rpt.Execute(headers); err != nil {
return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return rpt.Execute(responses)
}
func strTime(t time.Time) string {
iso, err := t.MarshalText()
if err != nil {
return ""
}
return string(iso)
}
func strUint(u uint64) string {
return strconv.FormatUint(u, 10)
}
func streamName(imageStream string) string {
if imageStream == "" {
return "default"
}
return imageStream
}
func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
machineResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
response := new(entities.ListReporter)
response.Default = vm.Name == cfg.Engine.ActiveService
response.Name = vm.Name
response.Running = vm.Running
response.LastUp = strTime(vm.LastUp)
response.Created = strTime(vm.CreatedAt)
response.Stream = streamName(vm.Stream)
response.VMType = vm.VMType
response.CPUs = vm.CPUs
response.Memory = strUint(vm.Memory)
response.DiskSize = strUint(vm.DiskSize)
response.Port = vm.Port
response.RemoteUsername = vm.RemoteUsername
response.IdentityPath = vm.IdentityPath
response.Starting = vm.Starting
response.UserModeNetworking = vm.UserModeNetworking
machineResponses = append(machineResponses, response)
}
return machineResponses, nil
}
func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
humanResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
response := new(entities.ListReporter)
if vm.Name == cfg.Engine.ActiveService {
response.Name = vm.Name + "*"
response.Default = true
} else {
response.Name = vm.Name
}
switch {
case vm.Starting:
response.LastUp = "Currently starting"
response.Starting = true
case vm.Running:
response.LastUp = "Currently running"
response.Running = true
case vm.LastUp.IsZero():
response.LastUp = "Never"
default:
response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago"
}
response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago"
response.VMType = vm.VMType
response.CPUs = vm.CPUs
response.Memory = units.BytesSize(float64(vm.Memory))
response.DiskSize = units.BytesSize(float64(vm.DiskSize))
humanResponses = append(humanResponses, response)
}
return humanResponses, nil
}