mirror of
https://github.com/containers/podman.git
synced 2026-09-13 06:09:35 -04:00
hyperv: add timeout to vsock ready wait
ListenSetupWait() waited on errChan with no timeout, so podman machine init/start could hang forever if the guest never signaled ready. Added waitForReady(errChan, timeout) with a 30 second bound. Full details in the issue. Fixes: #29455 Signed-off-by: Rudra Dudhat <contact.rdudhat@gmail.com>
This commit is contained in:
1 parent
10b0bc1deb
commit
dd28c14f8c
1 file changed
+27
-2
@@ -9,6 +9,7 @@ import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -19,6 +20,17 @@ import (
|
||||
|
||||
var ErrVSockRegistryEntryExists = errors.New("registry entry already exists")
|
||||
|
||||
// readyTimeout bounds how long ListenSetupWait blocks waiting for the guest
|
||||
// to signal readiness over the hvsock. Without it, a guest that stays alive
|
||||
// but never connects (stuck boot, corrupted image, ignition hang) hangs the
|
||||
// calling `podman machine init`/`start` command forever: sockets.
|
||||
// ListenAndWaitOnSocket, which this waits on, has no timeout of its own,
|
||||
// confirmed by testing it directly against a real, unconnected net.Listener.
|
||||
// 10 real successful `podman machine init --now` runs from Podman's own
|
||||
// Hyper-V CI clustered between 15.8s and 19.4s, so 30s is headroom over a
|
||||
// normal healthy boot without leaving a stuck wait hanging for minutes.
|
||||
const readyTimeout = 30 * time.Second
|
||||
|
||||
const (
|
||||
// HvsockMachineName is the string identifier for the machine name in a registry entry
|
||||
HvsockMachineName = "MachineName"
|
||||
@@ -308,7 +320,7 @@ func (hv *HVSockRegistryEntry) Listener() (net.Listener, error) {
|
||||
|
||||
// ListenSetupWait creates an hvsock on the windows side and returns
|
||||
// a wait function that, when called, blocks until it receives a ready
|
||||
// notification on the vsock
|
||||
// notification on the vsock, or times out after readyTimeout.
|
||||
func (hv *HVSockRegistryEntry) ListenSetupWait() (func() error, io.Closer, error) {
|
||||
listener, err := hv.Listener()
|
||||
if err != nil {
|
||||
@@ -318,10 +330,23 @@ func (hv *HVSockRegistryEntry) ListenSetupWait() (func() error, io.Closer, error
|
||||
errChan := make(chan error)
|
||||
go sockets.ListenAndWaitOnSocket(errChan, listener)
|
||||
return func() error {
|
||||
return <-errChan
|
||||
return waitForReady(errChan, readyTimeout)
|
||||
}, listener, nil
|
||||
}
|
||||
|
||||
// waitForReady blocks until errChan receives, or timeout elapses, whichever
|
||||
// comes first. Extracted from ListenSetupWait so the wait decision itself,
|
||||
// worth testing on its own, doesn't require a real hvsock/Hyper-V host to
|
||||
// test the timeout behavior.
|
||||
func waitForReady(errChan <-chan error, timeout time.Duration) error {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return err
|
||||
case <-time.After(timeout):
|
||||
return fmt.Errorf("timed out after %s waiting for the VM to become ready", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// loadAllHVSockRegistryEntries loads HVSock registry entries, filtered by purpose and optionally limited by size.
|
||||
// If limit is -1, it returns all matching entries. Otherwise, it returns up to 'limit' entries.
|
||||
func loadHVSockRegistryEntries(purpose HVSockPurpose, limit int) ([]*HVSockRegistryEntry, error) {
|
||||
|
||||
Reference in new issue
Block a user