diff --git a/pkg/machine/define/errors.go b/pkg/machine/define/errors.go index 1a32bc5165..c1f45853c7 100644 --- a/pkg/machine/define/errors.go +++ b/pkg/machine/define/errors.go @@ -8,12 +8,9 @@ import ( ) var ( - ErrNoSuchVM = errors.New("VM does not exist") - ErrWrongState = errors.New("VM in wrong state to perform action") - ErrVMAlreadyExists = errors.New("VM already exists") - ErrVMAlreadyRunning = errors.New("VM already running or starting") - ErrMultipleActiveVM = errors.New("only one VM can be active at a time") - ErrNotImplemented = errors.New("functionality not implemented") + ErrWrongState = errors.New("VM in wrong state to perform action") + ErrVMAlreadyExists = errors.New("VM already exists") + ErrNotImplemented = errors.New("functionality not implemented") ) type ErrVMRunningCannotDestroyed struct { @@ -49,3 +46,24 @@ type ErrIncompatibleMachineConfig struct { func (err *ErrIncompatibleMachineConfig) Error() string { return fmt.Sprintf("incompatible machine config %q (%s) for this version of Podman", err.Path, err.Name) } + +type ErrVMAlreadyRunning struct { + Name string +} + +func (err *ErrVMAlreadyRunning) Error() string { + return fmt.Sprintf("%s already starting or running", err.Name) +} + +type ErrMultipleActiveVM struct { + Name string + Provider *string +} + +func (err *ErrMultipleActiveVM) Error() string { + errMsg := fmt.Sprintf("a machine %q is already running", err.Name) + if err.Provider == nil { + return errMsg + } + return fmt.Sprintf("%s on the %q provider", errMsg, *err.Provider) +} diff --git a/pkg/machine/shim/host.go b/pkg/machine/shim/host.go index b0daa19ba9..a7126757ce 100644 --- a/pkg/machine/shim/host.go +++ b/pkg/machine/shim/host.go @@ -265,7 +265,7 @@ func VMExists(name string, vmstubbers []vmconfigs.VMProvider) (*vmconfigs.Machin return nil, false, err } if mc, found := mcs[name]; found { - return mc, true, nil + return mc.MachineConfig, true, nil } // Check with the provider hypervisor for _, vmstubber := range vmstubbers { @@ -281,31 +281,46 @@ func VMExists(name string, vmstubbers []vmconfigs.VMProvider) (*vmconfigs.Machin } // checkExclusiveActiveVM checks if any of the machines are already running -func checkExclusiveActiveVM(provider vmconfigs.VMProvider, mc *vmconfigs.MachineConfig) error { +func checkExclusiveActiveVM(currentProvider vmconfigs.VMProvider, mc *vmconfigs.MachineConfig) error { + providers := provider.GetAll() // Check if any other machines are running; if so, we error - localMachines, err := getMCsOverProviders([]vmconfigs.VMProvider{provider}) + localMachines, err := getMCsOverProviders(providers) if err != nil { return err } + for name, localMachine := range localMachines { - state, err := provider.State(localMachine, false) + state, err := localMachine.Provider.State(localMachine.MachineConfig, false) if err != nil { return err } if state == machineDefine.Running || state == machineDefine.Starting { if mc.Name == name { - return fmt.Errorf("unable to start %q: machine %s: %w", mc.Name, name, machineDefine.ErrVMAlreadyRunning) + return fmt.Errorf("unable to start %q: already running", mc.Name) + } + + // A machine is running in the current provider + if currentProvider == localMachine.Provider { + fail := machineDefine.ErrVMAlreadyRunning{Name: mc.Name} + return fmt.Errorf("unable to start %q: %w", mc.Name, &fail) } - return fmt.Errorf("unable to start %q: machine %s is already running: %w", mc.Name, name, machineDefine.ErrMultipleActiveVM) + // A machine is running in an alternate provider + providerRunningMachine := localMachine.Provider.VMType().String() + return &machineDefine.ErrMultipleActiveVM{Name: name, Provider: &providerRunningMachine} } } return nil } +type knownMachineConfig struct { + Provider vmconfigs.VMProvider + MachineConfig *vmconfigs.MachineConfig +} + // getMCsOverProviders loads machineconfigs from a config dir derived from the "provider". it returns only what is known on // disk so things like status may be incomplete or inaccurate -func getMCsOverProviders(vmstubbers []vmconfigs.VMProvider) (map[string]*vmconfigs.MachineConfig, error) { - mcs := make(map[string]*vmconfigs.MachineConfig) +func getMCsOverProviders(vmstubbers []vmconfigs.VMProvider) (map[string]knownMachineConfig, error) { + mcs := make(map[string]knownMachineConfig) for _, stubber := range vmstubbers { dirs, err := env.GetMachineDirs(stubber.VMType()) if err != nil { @@ -320,7 +335,10 @@ func getMCsOverProviders(vmstubbers []vmconfigs.VMProvider) (map[string]*vmconfi // iterate known mcs and add the stubbers for mcName, mc := range stubberMCs { if _, ok := mcs[mcName]; !ok { - mcs[mcName] = mc + mcs[mcName] = knownMachineConfig{ + Provider: stubber, + MachineConfig: mc, + } } } } @@ -414,7 +432,7 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDe } if state == machineDefine.Running || state == machineDefine.Starting { - return fmt.Errorf("machine %s: %w", mc.Name, machineDefine.ErrVMAlreadyRunning) + return fmt.Errorf("unable to start %q: already running", mc.Name) } }