From 0f22c1c772cf2a685fe8fc28c3cac35e3c9ab67d Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Wed, 1 Oct 2025 14:27:29 -0500 Subject: [PATCH] Provider obfuscation in command line For Podman 6, we still have providers and will continue to have a default provider for each platform. But where a platform has multiple providers, we want users to be able to cross provider boudnaries imposed in Podman 4/5. The key change is to look up virtual machines by name, as before, but to then also iterate all possible providers. As of this PR, init will still only create with the default provider, but a subsequent PR will introdouce an provider override. I also removed the "--all-providers" command line option on `podman machine ls` because it no longer makes sense. And I marked the all provider list test to be skipped. Signed-off-by: Brent Baude --- cmd/podman/machine/cp.go | 25 ++-- cmd/podman/machine/init.go | 15 ++- cmd/podman/machine/inspect.go | 21 +-- cmd/podman/machine/list.go | 21 +-- cmd/podman/machine/machine.go | 1 + cmd/podman/machine/os/apply.go | 7 +- cmd/podman/machine/os/manager.go | 52 +++----- cmd/podman/machine/rm.go | 11 +- cmd/podman/machine/set.go | 11 +- cmd/podman/machine/ssh.go | 36 ++--- cmd/podman/machine/start.go | 10 +- cmd/podman/machine/stop.go | 10 +- cmd/podman/system/reset_machine.go | 2 +- .../markdown/podman-machine-list.1.md.in | 4 - pkg/machine/define/errors.go | 9 +- pkg/machine/e2e/list_test.go | 2 +- pkg/machine/os/machine_os.go | 10 +- pkg/machine/shim/host.go | 123 ++++++++++-------- pkg/machine/vmconfigs/machine.go | 2 +- 19 files changed, 159 insertions(+), 213 deletions(-) diff --git a/cmd/podman/machine/cp.go b/cmd/podman/machine/cp.go index 7d730ad13b..a143557027 100644 --- a/cmd/podman/machine/cp.go +++ b/cmd/podman/machine/cp.go @@ -14,7 +14,7 @@ import ( "github.com/containers/podman/v6/pkg/copy" "github.com/containers/podman/v6/pkg/machine" "github.com/containers/podman/v6/pkg/machine/define" - "github.com/containers/podman/v6/pkg/machine/env" + "github.com/containers/podman/v6/pkg/machine/shim" "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/containers/podman/v6/pkg/specgen" "github.com/spf13/cobra" @@ -78,12 +78,16 @@ func cp(_ *cobra.Command, args []string) error { destPath = args[1] } - mc, err := resolveMachine(srcMachine, destMachine) + vmName, err := resolveMachineName(srcMachine, destMachine) + if err != nil { + return err + } + mc, vmProvider, err := shim.VMExists(vmName) if err != nil { return err } - state, err := provider.State(mc, false) + state, err := vmProvider.State(mc, false) if err != nil { return err } @@ -135,25 +139,18 @@ func localhostSSHCopy(opts *cpOptions) error { return cmd.Run() } -func resolveMachine(srcMachine, destMachine string) (*vmconfigs.MachineConfig, error) { +func resolveMachineName(srcMachine, destMachine string) (string, error) { if len(srcMachine) > 0 && len(destMachine) > 0 { - return nil, errors.New("copying between two machines is unsupported") + return "", errors.New("copying between two machines is unsupported") } if len(srcMachine) == 0 && len(destMachine) == 0 { - return nil, errors.New("a machine name must prefix either the source path or destination path") + return "", errors.New("a machine name must prefix either the source path or destination path") } - - dirs, err := env.GetMachineDirs(provider.VMType()) - if err != nil { - return nil, err - } - name := destMachine if len(srcMachine) > 0 { cpOpts.IsSrc = true name = srcMachine } - - return vmconfigs.LoadMachineByName(name, dirs) + return name, nil } diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index fd0f980494..6bf03d9eaf 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -12,7 +12,6 @@ import ( "github.com/containers/podman/v6/libpod/events" "github.com/containers/podman/v6/pkg/machine/define" "github.com/containers/podman/v6/pkg/machine/shim" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/shirou/gopsutil/v4/mem" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -184,14 +183,20 @@ func initMachine(cmd *cobra.Command, args []string) error { } // Check if machine already exists - _, exists, err := shim.VMExists(initOpts.Name, []vmconfigs.VMProvider{provider}) - if err != nil { + var errNotExists *define.ErrVMDoesNotExist + _, _, err := shim.VMExists(initOpts.Name) + // errors.As checks for nil so safe to use. + if !errors.As(err, &errNotExists) { return err } - // machine exists, return error + // Check if something on the hypervisor exists with the same name + exists, err := shim.VMExistsOnHyperVisor(initOpts.Name) + if err != nil { + return err + } if exists { - return fmt.Errorf("%s: %w", initOpts.Name, define.ErrVMAlreadyExists) + return fmt.Errorf("%s already exists on hypervisor", initOpts.Name) } // check if a system connection already exists diff --git a/cmd/podman/machine/inspect.go b/cmd/podman/machine/inspect.go index 403ec17384..b83b26ba27 100644 --- a/cmd/podman/machine/inspect.go +++ b/cmd/podman/machine/inspect.go @@ -10,7 +10,7 @@ import ( "github.com/containers/podman/v6/cmd/podman/utils" "github.com/containers/podman/v6/pkg/machine" "github.com/containers/podman/v6/pkg/machine/env" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" + "github.com/containers/podman/v6/pkg/machine/shim" "github.com/spf13/cobra" "go.podman.io/common/pkg/report" ) @@ -48,33 +48,34 @@ func inspect(cmd *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) - dirs, err := env.GetMachineDirs(provider.VMType()) - if err != nil { - return err - } if len(args) < 1 { args = append(args, defaultMachineName) } vms := make([]machine.InspectInfo, 0, len(args)) for _, name := range args { - mc, err := vmconfigs.LoadMachineByName(name, dirs) + mc, vmProvider, err := shim.VMExists(name) if err != nil { errs = append(errs, err) continue } - state, err := provider.State(mc, false) + dirs, err := env.GetMachineDirs(provider.VMType()) if err != nil { return err } - podmanSocket, podmanPipe, err := mc.ConnectionInfo(provider.VMType()) + state, err := vmProvider.State(mc, false) if err != nil { return err } - rosetta, err := provider.GetRosetta(mc) + podmanSocket, podmanPipe, err := mc.ConnectionInfo(vmProvider.VMType()) + if err != nil { + return err + } + + rosetta, err := vmProvider.GetRosetta(mc) if err != nil { return err } @@ -91,7 +92,7 @@ func inspect(cmd *cobra.Command, args []string) error { Resources: mc.Resources, SSHConfig: mc.SSH, State: state, - UserModeNetworking: provider.UserModeNetworkEnabled(mc), + UserModeNetworking: vmProvider.UserModeNetworkEnabled(mc), Rootful: mc.HostUser.Rootful, Rosetta: rosetta, } diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index f33bf942fc..b25ff0530b 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -17,7 +17,6 @@ import ( "github.com/containers/podman/v6/pkg/machine" provider2 "github.com/containers/podman/v6/pkg/machine/provider" "github.com/containers/podman/v6/pkg/machine/shim" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/docker/go-units" "github.com/spf13/cobra" "go.podman.io/common/pkg/completion" @@ -44,10 +43,9 @@ var ( ) type listFlagType struct { - format string - noHeading bool - quiet bool - allProviders bool + format string + noHeading bool + quiet bool } func init() { @@ -62,7 +60,6 @@ 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") } func list(cmd *cobra.Command, _ []string) error { @@ -70,17 +67,7 @@ func list(cmd *cobra.Command, _ []string) error { 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} - } - + providers := provider2.GetAll() listResponse, err := shim.List(providers, opts) if err != nil { return err diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go index 48ed1ede87..bf96602d70 100644 --- a/cmd/podman/machine/machine.go +++ b/cmd/podman/machine/machine.go @@ -42,6 +42,7 @@ var ( ) var ( + // TODO This needs to be deleted! provider vmconfigs.VMProvider ) diff --git a/cmd/podman/machine/os/apply.go b/cmd/podman/machine/os/apply.go index 0e23c2b83f..80111fa3c9 100644 --- a/cmd/podman/machine/os/apply.go +++ b/cmd/podman/machine/os/apply.go @@ -8,7 +8,6 @@ import ( "github.com/containers/podman/v6/cmd/podman/registry" "github.com/containers/podman/v6/cmd/podman/validate" "github.com/containers/podman/v6/pkg/machine/os" - provider2 "github.com/containers/podman/v6/pkg/machine/provider" "github.com/spf13/cobra" ) @@ -49,11 +48,7 @@ func apply(_ *cobra.Command, args []string) error { Restart: restart, } - provider, err := provider2.Get() - if err != nil { - return err - } - osManager, err := NewOSManager(managerOpts, provider) + osManager, err := NewOSManager(managerOpts) if err != nil { return err } diff --git a/cmd/podman/machine/os/manager.go b/cmd/podman/machine/os/manager.go index 9f6ad57fed..1fa47a9476 100644 --- a/cmd/podman/machine/os/manager.go +++ b/cmd/podman/machine/os/manager.go @@ -9,10 +9,8 @@ import ( "strings" "github.com/containers/podman/v6/pkg/machine/define" - "github.com/containers/podman/v6/pkg/machine/env" pkgOS "github.com/containers/podman/v6/pkg/machine/os" - "github.com/containers/podman/v6/pkg/machine/provider" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" + "github.com/containers/podman/v6/pkg/machine/shim" machineconfig "go.podman.io/common/pkg/machine" ) @@ -23,13 +21,30 @@ type ManagerOpts struct { } // NewOSManager creates a new OSManager depending on the mode of the call -func NewOSManager(opts ManagerOpts, p vmconfigs.VMProvider) (pkgOS.Manager, error) { +func NewOSManager(opts ManagerOpts) (pkgOS.Manager, error) { // If a VM name is specified, then we know that we are not inside a // Podman VM, but rather outside of it. if machineconfig.IsPodmanMachine() && opts.VMName == "" { return guestOSManager() } - return machineOSManager(opts, p) + + // Set to the default name if no VM was provided + if opts.VMName == "" { + opts.VMName = define.DefaultMachineName + } + + mc, vmProvider, err := shim.VMExists(opts.VMName) + if err != nil { + return nil, err + } + + return &pkgOS.MachineOS{ + VM: mc, + Provider: vmProvider, + Args: opts.CLIArgs, + VMName: opts.VMName, + Restart: opts.Restart, + }, nil } // guestOSManager returns an OSmanager for inside-VM operations @@ -43,33 +58,6 @@ func guestOSManager() (pkgOS.Manager, error) { } } -// machineOSManager returns an os manager that manages outside the VM. -func machineOSManager(opts ManagerOpts, _ vmconfigs.VMProvider) (pkgOS.Manager, error) { - vmName := opts.VMName - if opts.VMName == "" { - vmName = define.DefaultMachineName - } - p, err := provider.Get() - if err != nil { - return nil, err - } - dirs, err := env.GetMachineDirs(p.VMType()) - if err != nil { - return nil, err - } - mc, err := vmconfigs.LoadMachineByName(vmName, dirs) - if err != nil { - return nil, err - } - return &pkgOS.MachineOS{ - VM: mc, - Provider: p, - Args: opts.CLIArgs, - VMName: vmName, - Restart: opts.Restart, - }, nil -} - type Distribution struct { Name string Variant string diff --git a/cmd/podman/machine/rm.go b/cmd/podman/machine/rm.go index a243da534b..c6ced6fcf7 100644 --- a/cmd/podman/machine/rm.go +++ b/cmd/podman/machine/rm.go @@ -6,9 +6,7 @@ import ( "github.com/containers/podman/v6/cmd/podman/registry" "github.com/containers/podman/v6/libpod/events" "github.com/containers/podman/v6/pkg/machine" - "github.com/containers/podman/v6/pkg/machine/env" "github.com/containers/podman/v6/pkg/machine/shim" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/spf13/cobra" ) @@ -55,17 +53,12 @@ func rm(_ *cobra.Command, args []string) error { vmName = args[0] } - dirs, err := env.GetMachineDirs(provider.VMType()) + mc, vmProvider, err := shim.VMExists(vmName) if err != nil { return err } - mc, err := vmconfigs.LoadMachineByName(vmName, dirs) - if err != nil { - return err - } - - if err := shim.Remove(mc, provider, dirs, destroyOptions); err != nil { + if err := shim.Remove(mc, vmProvider, destroyOptions); err != nil { return err } newMachineEvent(events.Remove, events.Event{Name: vmName}) diff --git a/cmd/podman/machine/set.go b/cmd/podman/machine/set.go index 8211a343a5..ecfae810ae 100644 --- a/cmd/podman/machine/set.go +++ b/cmd/podman/machine/set.go @@ -5,9 +5,7 @@ package machine import ( "github.com/containers/podman/v6/cmd/podman/registry" "github.com/containers/podman/v6/pkg/machine/define" - "github.com/containers/podman/v6/pkg/machine/env" "github.com/containers/podman/v6/pkg/machine/shim" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/spf13/cobra" "go.podman.io/common/pkg/completion" "go.podman.io/common/pkg/strongunits" @@ -93,12 +91,7 @@ func setMachine(cmd *cobra.Command, args []string) error { vmName = args[0] } - dirs, err := env.GetMachineDirs(provider.VMType()) - if err != nil { - return err - } - - mc, err := vmconfigs.LoadMachineByName(vmName, dirs) + mc, vmProvider, err := shim.VMExists(vmName) if err != nil { return err } @@ -129,5 +122,5 @@ func setMachine(cmd *cobra.Command, args []string) error { // At this point, we have the known changed information, etc // Walk through changes to the providers if they need them - return shim.Set(mc, provider, setOpts) + return shim.Set(mc, vmProvider, setOpts) } diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go index 909d39aa54..66beaeb072 100644 --- a/cmd/podman/machine/ssh.go +++ b/cmd/podman/machine/ssh.go @@ -3,14 +3,14 @@ package machine import ( + "errors" "fmt" - "github.com/containers/podman/v6/pkg/machine/define" - "github.com/containers/podman/v6/pkg/machine/env" - "github.com/containers/podman/v6/cmd/podman/registry" "github.com/containers/podman/v6/cmd/podman/utils" "github.com/containers/podman/v6/pkg/machine" + "github.com/containers/podman/v6/pkg/machine/define" + "github.com/containers/podman/v6/pkg/machine/shim" "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/spf13/cobra" "go.podman.io/common/pkg/completion" @@ -45,20 +45,14 @@ func init() { _ = sshCmd.RegisterFlagCompletionFunc(usernameFlagName, completion.AutocompleteNone) } -// TODO Remember that this changed upstream and needs to updated as such! - func ssh(_ *cobra.Command, args []string) error { var ( - err error - mc *vmconfigs.MachineConfig - validVM bool + err error + exists bool + mc *vmconfigs.MachineConfig + vmProvider vmconfigs.VMProvider ) - dirs, err := env.GetMachineDirs(provider.VMType()) - if err != nil { - return err - } - // Set the VM to default vmName := defaultMachineName // If len is greater than 0, it means we may have been @@ -68,23 +62,22 @@ func ssh(_ *cobra.Command, args []string) error { // note: previous incantations of this up by a specific name // and errors were ignored. this error is not ignored because // it implies podman cannot read its machine files, which is bad - machines, err := vmconfigs.LoadMachinesInDir(dirs) + mc, vmProvider, err = shim.VMExists(args[0]) if err != nil { return err } - - mc, validVM = machines[args[0]] - if validVM { + if errors.Is(err, &define.ErrVMDoesNotExist{}) { vmName = args[0] } else { sshOpts.Args = append(sshOpts.Args, args[0]) } + exists = true } // If len is greater than 1, it means we might have been // given a vmname and args or just args if len(args) > 1 { - if validVM { + if exists { sshOpts.Args = args[1:] } else { sshOpts.Args = args @@ -93,13 +86,12 @@ func ssh(_ *cobra.Command, args []string) error { // If the machine config was not loaded earlier, we load it now if mc == nil { - mc, err = vmconfigs.LoadMachineByName(vmName, dirs) + mc, vmProvider, err = shim.VMExists(vmName) if err != nil { - return fmt.Errorf("vm %s not found: %w", vmName, err) + return err } } - - state, err := provider.State(mc, false) + state, err := vmProvider.State(mc, false) if err != nil { return err } diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go index f7c88dc880..824acbff69 100644 --- a/cmd/podman/machine/start.go +++ b/cmd/podman/machine/start.go @@ -8,9 +8,7 @@ import ( "github.com/containers/podman/v6/cmd/podman/registry" "github.com/containers/podman/v6/libpod/events" "github.com/containers/podman/v6/pkg/machine" - "github.com/containers/podman/v6/pkg/machine/env" "github.com/containers/podman/v6/pkg/machine/shim" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/spf13/cobra" ) @@ -54,11 +52,7 @@ func start(_ *cobra.Command, args []string) error { vmName = args[0] } - dirs, err := env.GetMachineDirs(provider.VMType()) - if err != nil { - return err - } - mc, err := vmconfigs.LoadMachineByName(vmName, dirs) + mc, vmProvider, err := shim.VMExists(vmName) if err != nil { return err } @@ -67,7 +61,7 @@ func start(_ *cobra.Command, args []string) error { fmt.Printf("Starting machine %q\n", vmName) } - if err := shim.Start(mc, provider, dirs, startOpts); err != nil { + if err := shim.Start(mc, vmProvider, startOpts); err != nil { return err } fmt.Printf("Machine %q started successfully\n", vmName) diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go index 87db79df8c..144f5e9cfe 100644 --- a/cmd/podman/machine/stop.go +++ b/cmd/podman/machine/stop.go @@ -7,9 +7,7 @@ import ( "github.com/containers/podman/v6/cmd/podman/registry" "github.com/containers/podman/v6/libpod/events" - "github.com/containers/podman/v6/pkg/machine/env" "github.com/containers/podman/v6/pkg/machine/shim" - "github.com/containers/podman/v6/pkg/machine/vmconfigs" "github.com/spf13/cobra" ) @@ -44,16 +42,12 @@ func stop(_ *cobra.Command, args []string) error { vmName = args[0] } - dirs, err := env.GetMachineDirs(provider.VMType()) - if err != nil { - return err - } - mc, err := vmconfigs.LoadMachineByName(vmName, dirs) + mc, vmProvider, err := shim.VMExists(vmName) if err != nil { return err } - if err := shim.Stop(mc, provider, dirs, false); err != nil { + if err := shim.Stop(mc, vmProvider, false); err != nil { return err } diff --git a/cmd/podman/system/reset_machine.go b/cmd/podman/system/reset_machine.go index 6d2c313fe1..6eed7fa337 100644 --- a/cmd/podman/system/reset_machine.go +++ b/cmd/podman/system/reset_machine.go @@ -42,7 +42,7 @@ func resetMachine() error { } if state == define.Running { - if err := shim.Stop(mc, provider, dirs, true); err != nil { + if err := shim.Stop(mc, provider, true); err != nil { logrus.Errorf("unable to stop running machine %s: %q", mc.Name, err) } } diff --git a/docs/source/markdown/podman-machine-list.1.md.in b/docs/source/markdown/podman-machine-list.1.md.in index a850666cd7..e243927063 100644 --- a/docs/source/markdown/podman-machine-list.1.md.in +++ b/docs/source/markdown/podman-machine-list.1.md.in @@ -26,10 +26,6 @@ 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' diff --git a/pkg/machine/define/errors.go b/pkg/machine/define/errors.go index e570359186..f42a92fa8f 100644 --- a/pkg/machine/define/errors.go +++ b/pkg/machine/define/errors.go @@ -7,12 +7,19 @@ import ( var ( ErrWrongState = errors.New("VM in wrong state to perform action") - ErrVMAlreadyExists = errors.New("VM already exists") ErrNotImplemented = errors.New("functionality not implemented") ErrInitRelaunchAttempt = errors.New("stopping execution: 'init' relaunched with --reexec flag to reinitialize the VM") ErrRebootInitiated = errors.New("system reboot initiated") ) +type ErrVMAlreadyExists struct { + Name string +} + +func (err *ErrVMAlreadyExists) Error() string { + return fmt.Sprintf("machine %q already exists", err.Name) +} + type ErrVMRunningCannotDestroyed struct { Name string } diff --git a/pkg/machine/e2e/list_test.go b/pkg/machine/e2e/list_test.go index 6551c6157e..c967d9fa4f 100644 --- a/pkg/machine/e2e/list_test.go +++ b/pkg/machine/e2e/list_test.go @@ -185,7 +185,7 @@ var _ = Describe("podman machine list", func() { Expect(listSession.outputToString()).To(Equal("2GiB 11GiB")) }) It("list machine from all providers", func() { - skipIfVmtype(define.QemuVirt, "linux only has one provider") + Skip("This test will be changed for when --provider is added to init") // create machine on other provider currprovider := os.Getenv("CONTAINERS_MACHINE_PROVIDER") diff --git a/pkg/machine/os/machine_os.go b/pkg/machine/os/machine_os.go index 13a44ebebe..0f7b717742 100644 --- a/pkg/machine/os/machine_os.go +++ b/pkg/machine/os/machine_os.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/containers/podman/v6/pkg/machine" - "github.com/containers/podman/v6/pkg/machine/env" "github.com/containers/podman/v6/pkg/machine/shim" "github.com/containers/podman/v6/pkg/machine/vmconfigs" ) @@ -28,16 +27,11 @@ func (m *MachineOS) Apply(image string, _ ApplyOptions) error { return err } - dirs, err := env.GetMachineDirs(m.Provider.VMType()) - if err != nil { - return err - } - if m.Restart { - if err := shim.Stop(m.VM, m.Provider, dirs, false); err != nil { + if err := shim.Stop(m.VM, m.Provider, false); err != nil { return err } - if err := shim.Start(m.VM, m.Provider, dirs, machine.StartOptions{NoInfo: true}); err != nil { + if err := shim.Start(m.VM, m.Provider, machine.StartOptions{NoInfo: true}); err != nil { return err } fmt.Printf("Machine %q restarted successfully\n", m.VMName) diff --git a/pkg/machine/shim/host.go b/pkg/machine/shim/host.go index 089fdb19c1..3d399e52fd 100644 --- a/pkg/machine/shim/host.go +++ b/pkg/machine/shim/host.go @@ -32,42 +32,35 @@ import ( // List is done at the host level to allow for a *possible* future where // more than one provider is used func List(vmstubbers []vmconfigs.VMProvider, _ machine.ListOptions) ([]*machine.ListResponse, error) { - var lrs []*machine.ListResponse - - for _, s := range vmstubbers { - dirs, err := env.GetMachineDirs(s.VMType()) - if err != nil { - return nil, err - } - mcs, err := vmconfigs.LoadMachinesInDir(dirs) - if err != nil { - return nil, err - } - for name, mc := range mcs { - state, err := s.State(mc, false) - if err != nil { - return nil, err - } - lr := machine.ListResponse{ - Name: name, - CreatedAt: mc.Created, - LastUp: mc.LastUp, - Running: state == machineDefine.Running, - Starting: mc.Starting, - VMType: s.VMType().String(), - CPUs: mc.Resources.CPUs, - Memory: mc.Resources.Memory, - Swap: mc.Swap, - DiskSize: mc.Resources.DiskSize, - Port: mc.SSH.Port, - RemoteUsername: mc.SSH.RemoteUsername, - IdentityPath: mc.SSH.IdentityPath, - UserModeNetworking: s.UserModeNetworkEnabled(mc), - } - lrs = append(lrs, &lr) - } + lrs := make([]*machine.ListResponse, 0) + mcs, err := getMCsOverProviders(vmstubbers) + if err != nil { + return nil, err } + for name, mc := range mcs { + state, err := mc.Provider.State(mc.MachineConfig, false) + if err != nil { + return nil, err + } + lr := machine.ListResponse{ + Name: name, + CreatedAt: mc.Created, + LastUp: mc.LastUp, + Running: state == machineDefine.Running, + Starting: mc.Starting, + VMType: mc.Provider.VMType().String(), + CPUs: mc.Resources.CPUs, + Memory: mc.Resources.Memory, + Swap: mc.Swap, + DiskSize: mc.Resources.DiskSize, + Port: mc.SSH.Port, + RemoteUsername: mc.SSH.RemoteUsername, + IdentityPath: mc.SSH.IdentityPath, + UserModeNetworking: mc.Provider.UserModeNetworkEnabled(mc.MachineConfig), + } + lrs = append(lrs, &lr) + } return lrs, nil } @@ -300,27 +293,37 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) error { return mc.Write() } +type MultiProvider []vmconfigs.VMProvider + // VMExists looks across given providers for a machine's existence. returns the actual config and found bool -func VMExists(name string, vmstubbers []vmconfigs.VMProvider) (*vmconfigs.MachineConfig, bool, error) { +func VMExists(name string) (*vmconfigs.MachineConfig, vmconfigs.VMProvider, error) { // Look on disk first - mcs, err := getMCsOverProviders(vmstubbers) + mcs, err := getMCsOverProviders(provider.GetAll()) if err != nil { - return nil, false, err + return nil, nil, err } if mc, found := mcs[name]; found { - return mc.MachineConfig, true, nil + vmType := mc.Provider + return mc.MachineConfig, vmType, nil } - // Check with the provider hypervisor - for _, vmstubber := range vmstubbers { - exists, err := vmstubber.Exists(name) + + return nil, nil, &machineDefine.ErrVMDoesNotExist{Name: name} +} + +// VMExistsOnHyperVisor actually checks the managing hypervisor (like WSL, HyperV) +// to make sure a VM with the same name does not happen to exist. +func VMExistsOnHyperVisor(name string) (bool, error) { + providers := provider.GetAll() + for _, p := range providers { + exists, err := p.Exists(name) if err != nil { - return nil, false, err + return false, err } if exists { - return nil, true, fmt.Errorf("vm %q already exists on hypervisor", name) + return true, nil } } - return nil, false, nil + return false, nil } // checkExclusiveActiveVM checks if any of the machines are already running @@ -356,8 +359,8 @@ func checkExclusiveActiveVM(currentProvider vmconfigs.VMProvider, mc *vmconfigs. } type knownMachineConfig struct { - Provider vmconfigs.VMProvider - MachineConfig *vmconfigs.MachineConfig + Provider vmconfigs.VMProvider + *vmconfigs.MachineConfig } // getMCsOverProviders loads machineconfigs from a config dir derived from the "provider". it returns only what is known on @@ -373,15 +376,9 @@ func getMCsOverProviders(vmstubbers []vmconfigs.VMProvider) (map[string]knownMac if err != nil { return nil, err } - // TODO When we get to golang-1.20+ we can replace the following with maps.Copy - // maps.Copy(mcs, stubberMCs) - // iterate known mcs and add the stubbers for mcName, mc := range stubberMCs { if _, ok := mcs[mcName]; !ok { - mcs[mcName] = knownMachineConfig{ - Provider: stubber, - MachineConfig: mc, - } + mcs[mcName] = knownMachineConfig{Provider: stubber, MachineConfig: mc} } } } @@ -389,7 +386,11 @@ func getMCsOverProviders(vmstubbers []vmconfigs.VMProvider) (map[string]knownMac } // Stop stops the machine as well as supporting binaries/processes -func Stop(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDefine.MachineDirs, hardStop bool) error { +func Stop(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, hardStop bool) error { + dirs, err := env.GetMachineDirs(mp.VMType()) + if err != nil { + return err + } // state is checked here instead of earlier because stopping a stopped vm is not considered // an error. so putting in one place instead of sprinkling all over. mc.Lock() @@ -445,11 +446,15 @@ func stopLocked(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *mach return mc.Write() } -func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDefine.MachineDirs, opts machine.StartOptions) error { +func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, opts machine.StartOptions) error { defaultBackoff := 500 * time.Millisecond maxBackoffs := 6 signalChanClosed := false + dirs, err := env.GetMachineDirs(mp.VMType()) + if err != nil { + return err + } mc.Lock() defer mc.Unlock() if err := mc.Refresh(); err != nil { @@ -672,7 +677,11 @@ func Set(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, opts machineDefin return mc.Write() } -func Remove(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDefine.MachineDirs, opts machine.RemoveOptions) error { +func Remove(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, opts machine.RemoveOptions) error { + dirs, err := env.GetMachineDirs(mp.VMType()) + if err != nil { + return err + } mc.Lock() defer mc.Unlock() if err := mc.Refresh(); err != nil { @@ -776,7 +785,7 @@ func Reset(mps []vmconfigs.VMProvider, _ machine.ResetOptions) error { } for _, mc := range mcs { - err := Stop(mc, p, d, true) + err := Stop(mc, p, true) if err != nil { resetErrors = multierror.Append(resetErrors, err) } diff --git a/pkg/machine/vmconfigs/machine.go b/pkg/machine/vmconfigs/machine.go index 41d66c9f2d..d80291d1c9 100644 --- a/pkg/machine/vmconfigs/machine.go +++ b/pkg/machine/vmconfigs/machine.go @@ -59,7 +59,7 @@ func NewMachineConfig(opts define.InitOptions, dirs *define.MachineDirs, sshIden // Given that we are locked now and check again that the config file does not exists, // if it does it means the VM was already created and we should error. if err := fileutils.Exists(cf.Path); err == nil { - return nil, fmt.Errorf("%s: %w", opts.Name, define.ErrVMAlreadyExists) + return nil, &define.ErrVMAlreadyExists{Name: opts.Name} } if vmtype != define.QemuVirt && len(opts.USBs) > 0 {