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

machine: Add -all-providers flag to machine list #23726

Merged
merged 1 commit into from
Aug 30, 2024
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
60 changes: 41 additions & 19 deletions cmd/podman/machine/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import (
"os"
"sort"
"strconv"
"strings"
"time"

"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/report"
"github.com/containers/podman/v5/cmd/podman/common"
"github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/validate"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/machine"
provider2 "github.com/containers/podman/v5/pkg/machine/provider"
"github.com/containers/podman/v5/pkg/machine/shim"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/docker/go-units"
Expand All @@ -24,11 +27,12 @@ import (

var (
lsCmd = &cobra.Command{
Use: "list [options]",
Aliases: []string{"ls"},
Short: "List machines",
Long: "List managed virtual machines.",
PersistentPreRunE: machinePreRunE,
Use: "list [options]",
Aliases: []string{"ls"},
Short: "List machines",
Long: "List managed virtual machines.",
// do not use machinePreRunE, as that pre-sets the provider
PersistentPreRunE: rootlessOnly,
ashley-cui marked this conversation as resolved.
Show resolved Hide resolved
RunE: list,
Args: validate.NoArgs,
ValidArgsFunction: completion.AutocompleteNone,
Expand All @@ -40,9 +44,10 @@ var (
)

type listFlagType struct {
format string
noHeading bool
quiet bool
format string
noHeading bool
quiet bool
allProviders bool
}

func init() {
Expand All @@ -57,15 +62,26 @@ func init() {
_ = 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")
flags.BoolVar(&listFlag.allProviders, "all-providers", false, "Show machines from all providers")
ashley-cui marked this conversation as resolved.
Show resolved Hide resolved
}

func list(cmd *cobra.Command, args []string) error {
var (
opts machine.ListOptions
err error
)
var providers []vmconfigs.VMProvider
if listFlag.allProviders {
providers = provider2.GetAll()
} else {
provider, err = provider2.Get()
if err != nil {
return err
}
providers = []vmconfigs.VMProvider{provider}
}

listResponse, err := shim.List([]vmconfigs.VMProvider{provider}, opts)
listResponse, err := shim.List(providers, opts)
if err != nil {
return err
}
Expand All @@ -79,12 +95,8 @@ func list(cmd *cobra.Command, args []string) error {
return listResponse[i].Running
})

defaultCon := ""
con, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true)
if err == nil {
// ignore the error here we only want to know if we have a default connection to show it in list
defaultCon = con.Name
}
// ignore the error here we only want to know if we have a default connection to show it in list
defaultCon, _ := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true)
ashley-cui marked this conversation as resolved.
Show resolved Hide resolved

if report.IsJSON(listFlag.format) {
machineReporter := toMachineFormat(listResponse, defaultCon)
Expand Down Expand Up @@ -152,11 +164,16 @@ func streamName(imageStream string) string {
return imageStream
}

func toMachineFormat(vms []*machine.ListResponse, defaultCon string) []*entities.ListReporter {
func toMachineFormat(vms []*machine.ListResponse, defaultCon *config.Connection) []*entities.ListReporter {
machineResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
isDefault := false
// check port, in case we somehow have machines with the same name in different providers
if defaultCon != nil {
isDefault = vm.Name == defaultCon.Name && strings.Contains(defaultCon.URI, strconv.Itoa((vm.Port)))
}
response := new(entities.ListReporter)
response.Default = vm.Name == defaultCon
response.Default = isDefault
response.Name = vm.Name
response.Running = vm.Running
response.LastUp = strTime(vm.LastUp)
Expand All @@ -177,11 +194,16 @@ func toMachineFormat(vms []*machine.ListResponse, defaultCon string) []*entities
return machineResponses
}

func toHumanFormat(vms []*machine.ListResponse, defaultCon string) []*entities.ListReporter {
func toHumanFormat(vms []*machine.ListResponse, defaultCon *config.Connection) []*entities.ListReporter {
humanResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
response := new(entities.ListReporter)
if vm.Name == defaultCon {
isDefault := false
// check port, in case we somehow have machines with the same name in different providers
if defaultCon != nil {
isDefault = vm.Name == defaultCon.Name && strings.Contains(defaultCon.URI, strconv.Itoa((vm.Port)))
}
if isDefault {
response.Name = vm.Name + "*"
response.Default = true
} else {
Expand Down
11 changes: 10 additions & 1 deletion cmd/podman/machine/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/containers/podman/v5/pkg/machine"
provider2 "github.com/containers/podman/v5/pkg/machine/provider"
"github.com/containers/podman/v5/pkg/machine/shim"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -50,10 +51,18 @@ func reset(_ *cobra.Command, _ []string) error {
err error
)

providers, err := provider2.GetAll(resetOptions.Force)
providers := provider2.GetAll()
if err != nil {
return err
}
for _, p := range providers {
hasPerms := provider2.HasPermsForProvider(p.VMType())
isInstalled, err := provider2.IsInstalled(p.VMType())
if !hasPerms && (isInstalled || err != nil) && !resetOptions.Force {
logrus.Warnf("Managing %s machines require admin authority.", p.VMType().String())
logrus.Warnf("Continuing to reset may cause Podman to be unaware of remaining VMs in the VM manager.")
}
}

if !resetOptions.Force {
listResponse, err := shim.List(providers, machine.ListOptions{})
Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-machine-list.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ environment variable while the machines are running can lead to unexpected behav

## OPTIONS

#### **--all-providers**

Show machines from all providers

#### **--format**=*format*

Change the default output format. This can be of a supported type like 'json'
Expand Down
11 changes: 11 additions & 0 deletions pkg/machine/e2e/config_darwin_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
package e2e_test

import "github.com/containers/podman/v5/pkg/machine/define"

const podmanBinary = "../../../bin/darwin/podman"

func getOtherProvider() string {
if isVmtype(define.AppleHvVirt) {
return "libkrun"
} else if isVmtype(define.LibKrun) {
return "applehv"
}
return ""
}
4 changes: 4 additions & 0 deletions pkg/machine/e2e/config_linux_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
package e2e_test

const podmanBinary = "../../../bin/podman-remote"

func getOtherProvider() string {
return ""
}
16 changes: 13 additions & 3 deletions pkg/machine/e2e/config_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ type listMachine struct {
-q, --quiet Show only machine names
*/

format string
noHeading bool
quiet bool
format string
noHeading bool
quiet bool
allProviders bool

cmd []string
}
Expand All @@ -25,6 +26,10 @@ func (i *listMachine) buildCmd(m *machineTestBuilder) []string {
if i.quiet {
cmd = append(cmd, "--quiet")
}
if i.allProviders {
cmd = append(cmd, "--all-providers")
}

i.cmd = cmd
return cmd
}
Expand All @@ -43,3 +48,8 @@ func (i *listMachine) withFormat(format string) *listMachine {
i.format = format
return i
}

func (i *listMachine) withAllProviders() *listMachine {
i.allProviders = true
return i
}
11 changes: 11 additions & 0 deletions pkg/machine/e2e/config_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"os/exec"
"strings"

"github.com/containers/podman/v5/pkg/machine/define"
)

const podmanBinary = "../../../bin/windows/podman.exe"
Expand All @@ -23,3 +25,12 @@ func pgrep(n string) (string, error) {
}
return strOut, nil
}

func getOtherProvider() string {
if isVmtype(define.WSLVirt) {
return "hyperv"
} else if isVmtype(define.HyperVVirt) {
return "wsl"
}
return ""
}
43 changes: 43 additions & 0 deletions pkg/machine/e2e/list_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package e2e_test

import (
"os"
"slices"
"strconv"
"strings"
"time"

"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/machine/define"
jsoniter "github.com/json-iterator/go"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -182,6 +184,47 @@ var _ = Describe("podman machine list", func() {
Expect(listSession).To(Exit(0))
Expect(listSession.outputToString()).To(Equal("2GiB 11GiB"))
})
It("list machine from all providers", func() {
skipIfVmtype(define.QemuVirt, "linux only has one provider")

// create machine on other provider
currprovider := os.Getenv("CONTAINERS_MACHINE_PROVIDER")
os.Setenv("CONTAINERS_MACHINE_PROVIDER", getOtherProvider())
defer os.Setenv("CONTAINERS_MACHINE_PROVIDER", currprovider)

// this may take a long time - we're not pre-fetching this image
othermach := new(initMachine)
session, err := mb.setName("otherprovider").setCmd(othermach).run()
// make sure to remove machine from other provider later
defer func() {
os.Setenv("CONTAINERS_MACHINE_PROVIDER", getOtherProvider())
defer os.Setenv("CONTAINERS_MACHINE_PROVIDER", currprovider)
rm := new(rmMachine)
removed, err := mb.setName("otherprovider").setCmd(rm.withForce()).run()
Expect(err).ToNot(HaveOccurred())
Expect(removed).To(Exit(0))
}()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))

// change back to current provider
os.Setenv("CONTAINERS_MACHINE_PROVIDER", currprovider)
name := randomString()
i := new(initMachine)
session, err = mb.setName(name).setCmd(i.withImage(mb.imagePath)).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))

list := new(listMachine)
listSession, err := mb.setCmd(list.withAllProviders().withFormat("{{.Name}}")).run()
Expect(err).NotTo(HaveOccurred())
Expect(listSession).To(Exit(0))
listNames := listSession.outputToStringSlice()
stripAsterisk(listNames)
Expect(listNames).To(HaveLen(2))
Expect(slices.Contains(listNames, "otherprovider")).To(BeTrue())
Expect(slices.Contains(listNames, name)).To(BeTrue())
})
})

func stripAsterisk(sl []string) {
Expand Down
73 changes: 11 additions & 62 deletions pkg/machine/provider/platform.go
Original file line number Diff line number Diff line change
@@ -1,71 +1,20 @@
//go:build !windows && !darwin

package provider

import (
"errors"
"fmt"
"io/fs"
"os"

"github.com/containers/common/pkg/config"
"github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/qemu"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/sirupsen/logrus"
)

func Get() (vmconfigs.VMProvider, error) {
cfg, err := config.Default()
if err != nil {
return nil, err
}
provider := cfg.Machine.Provider
if providerOverride, found := os.LookupEnv("CONTAINERS_MACHINE_PROVIDER"); found {
provider = providerOverride
}
resolvedVMType, err := define.ParseVMType(provider, define.QemuVirt)
if err != nil {
return nil, err
}

logrus.Debugf("Using Podman machine with `%s` virtualization provider", resolvedVMType.String())
switch resolvedVMType {
case define.QemuVirt:
return qemu.NewStubber()
default:
return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String())
}
}

func GetAll(_ bool) ([]vmconfigs.VMProvider, error) {
return []vmconfigs.VMProvider{new(qemu.QEMUStubber)}, nil
}

// SupportedProviders returns the providers that are supported on the host operating system
func SupportedProviders() []define.VMType {
return []define.VMType{define.QemuVirt}
}

// InstalledProviders returns the supported providers that are installed on the host
func InstalledProviders() ([]define.VMType, error) {
cfg, err := config.Default()
if err != nil {
return nil, err
}
_, err = cfg.FindHelperBinary(qemu.QemuCommand, true)
if errors.Is(err, fs.ErrNotExist) {
return []define.VMType{}, nil
installedTypes := []define.VMType{}
providers := GetAll()
for _, p := range providers {
installed, err := IsInstalled(p.VMType())
if err != nil {
return nil, err
}
if installed {
installedTypes = append(installedTypes, p.VMType())
}
}
if err != nil {
return nil, err
}

return []define.VMType{define.QemuVirt}, nil
}

// HasPermsForProvider returns whether the host operating system has the proper permissions to use the given provider
func HasPermsForProvider(provider define.VMType) bool {
// there are no permissions required for QEMU
return provider == define.QemuVirt
return installedTypes, nil
}
Loading