From 87b05b6a6fbb5c407db8fddc6e89ae2bc28dda19 Mon Sep 17 00:00:00 2001 From: Shane Smith Date: Thu, 2 Jun 2022 16:31:21 -0400 Subject: Prevent simultaneous machine starts Running `podman machine start` twice at the same time in different terminals, for example, will make the second invocation fail and the first one hang. [NO NEW TESTS NEEDED] Signed-off-by: Shane Smith --- pkg/machine/qemu/machine.go | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'pkg/machine') diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index b9f23662e..071da94a6 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -480,6 +480,10 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { wait = time.Millisecond * 500 ) + if v.Starting { + return fmt.Errorf("machine %q is already in the process of being started", v.Name) + } + v.Starting = true if err := v.writeConfig(); err != nil { return fmt.Errorf("writing JSON file: %w", err) -- cgit v1.2.3-54-g00ecf From 81153ffa21c5d29abb8ebadffaa19b4e02e4bc60 Mon Sep 17 00:00:00 2001 From: Shane Smith Date: Tue, 7 Jun 2022 12:57:54 -0400 Subject: Introduce 'Starting' status for machines - The State() function now returns machine.Starting status instead of an empty string if the VM is in the process of starting. - The `CheckExclusiveActiveVM()` function returns `true` to prevent starting a VM while another is in the process of starting. - `podman machine ls` displays "Currently starting" under "Last Up" for the starting VM - `podman machine ls` supports `{{.Starting}}` boolean field in the format - `podman machine inspect` displays "starting" in the "State" field for the starting VM Signed-off-by: Shane Smith --- cmd/podman/machine/list.go | 9 +++++++-- cmd/podman/machine/start.go | 2 +- pkg/machine/config.go | 7 +++++-- pkg/machine/qemu/machine.go | 13 ++++++------- pkg/machine/wsl/machine.go | 1 + 5 files changed, 20 insertions(+), 12 deletions(-) (limited to 'pkg/machine') diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index 5254d50cf..bb14d4a67 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -48,6 +48,7 @@ type ListReporter struct { Default bool Created string Running bool + Starting bool LastUp string Stream string VMType string @@ -224,10 +225,14 @@ func toHumanFormat(vms []*machine.ListResponse) ([]*ListReporter, error) { } else { response.Name = vm.Name } - if vm.Running { + switch { + case vm.Running: response.LastUp = "Currently running" response.Running = true - } else { + case vm.Starting: + response.LastUp = "Currently starting" + response.Starting = true + default: response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago" } response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago" diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go index c9b99e63b..3bd7f4a25 100644 --- a/cmd/podman/machine/start.go +++ b/cmd/podman/machine/start.go @@ -56,7 +56,7 @@ func start(_ *cobra.Command, args []string) error { if vmName == activeName { return errors.Wrapf(machine.ErrVMAlreadyRunning, "cannot start VM %s", vmName) } - return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running", vmName, activeName) + return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running or starting", vmName, activeName) } fmt.Printf("Starting machine %q\n", vmName) if err := vm.Start(vmName, machine.StartOptions{}); err != nil { diff --git a/pkg/machine/config.go b/pkg/machine/config.go index abbebc9f9..fcc129338 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -42,7 +42,9 @@ const ( // Running indicates the qemu vm is running. Running Status = "running" // Stopped indicates the vm has stopped. - Stopped Status = "stopped" + Stopped Status = "stopped" + // Starting indicated the vm is in the process of starting + Starting Status = "starting" DefaultMachineName string = "podman-machine-default" ) @@ -62,7 +64,7 @@ var ( DefaultIgnitionUserName = "core" ErrNoSuchVM = errors.New("VM does not exist") ErrVMAlreadyExists = errors.New("VM already exists") - ErrVMAlreadyRunning = errors.New("VM already running") + ErrVMAlreadyRunning = errors.New("VM already running or starting") ErrMultipleActiveVM = errors.New("only one VM can be active at a time") ForwarderBinaryName = "gvproxy" ) @@ -88,6 +90,7 @@ type ListResponse struct { CreatedAt time.Time LastUp time.Time Running bool + Starting bool Stream string VMType string CPUs uint64 diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 071da94a6..4ae04b77f 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -480,10 +480,6 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { wait = time.Millisecond * 500 ) - if v.Starting { - return fmt.Errorf("machine %q is already in the process of being started", v.Name) - } - v.Starting = true if err := v.writeConfig(); err != nil { return fmt.Errorf("writing JSON file: %w", err) @@ -908,7 +904,7 @@ func (v *MachineVM) State(bypass bool) (machine.Status, error) { } // Check if we can dial it if v.Starting && !bypass { - return "", nil + return machine.Starting, nil } monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout) if err != nil { @@ -1079,8 +1075,11 @@ func getVMInfos() ([]*machine.ListResponse, error) { return err } } - if state == machine.Running { + switch state { + case machine.Running: listEntry.Running = true + case machine.Starting: + listEntry.Starting = true } listed = append(listed, listEntry) @@ -1113,7 +1112,7 @@ func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) { return false, "", errors.Wrap(err, "error checking VM active") } for _, vm := range vms { - if vm.Running { + if vm.Running || vm.Starting { return true, vm.Name, nil } } diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 06020aded..075f42cb2 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -1312,6 +1312,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) { listEntry.RemoteUsername = vm.RemoteUsername listEntry.Port = vm.Port listEntry.IdentityPath = vm.IdentityPath + listEntry.Starting = false running := vm.isRunning() listEntry.CreatedAt, listEntry.LastUp, _ = vm.updateTimeStamps(running) -- cgit v1.2.3-54-g00ecf