diff --git a/e2e/vm/vm_darwin_test.go b/e2e/vm/vm_darwin_test.go index 4652cd96e..a928c5bec 100644 --- a/e2e/vm/vm_darwin_test.go +++ b/e2e/vm/vm_darwin_test.go @@ -96,3 +96,8 @@ var resetDisks = func(_ *option.Option, installed bool) { } gomega.Expect(os.RemoveAll(dataDiskDir)).ShouldNot(gomega.HaveOccurred()) } + +var shutdownWSL = func() error { + // no-op on darwin + return nil +} diff --git a/e2e/vm/vm_test.go b/e2e/vm/vm_test.go index 90d57b174..c7bf30ff3 100644 --- a/e2e/vm/vm_test.go +++ b/e2e/vm/vm_test.go @@ -4,7 +4,6 @@ package vm import ( - "os/exec" "runtime" "time" @@ -29,7 +28,7 @@ var resetVM = func(o *option.Option) { // clean up iptables //nolint:lll // link to explanation // https://docs.rancherdesktop.io/troubleshooting-tips/#q-how-do-i-fix-fata0005-subnet-1040024-overlaps-with-other-one-on-this-address-space-when-running-a-container-using-nerdctl-run - gomega.Expect(exec.Command("wsl", "--shutdown").Run()).Should(gomega.BeNil()) + gomega.Expect(shutdownWSL()).Should(gomega.BeNil()) } ginkgo.DeferCleanup(func() { @@ -38,7 +37,7 @@ var resetVM = func(o *option.Option) { time.Sleep(1 * time.Second) command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(10).Run() if runtime.GOOS == "windows" { - gomega.Expect(exec.Command("wsl", "--shutdown").Run()).Should(gomega.BeNil()) + gomega.Expect(shutdownWSL()).Should(gomega.BeNil()) } time.Sleep(1 * time.Second) command.New(o, virtualMachineRootCmd, "init").WithoutCheckingExitCode().WithTimeoutInSeconds(160).Run() diff --git a/e2e/vm/vm_windows_test.go b/e2e/vm/vm_windows_test.go index 2098483d5..edbc43055 100644 --- a/e2e/vm/vm_windows_test.go +++ b/e2e/vm/vm_windows_test.go @@ -7,9 +7,13 @@ package vm import ( + "context" + "errors" "os" + "os/exec" "path/filepath" "testing" + "time" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -60,3 +64,31 @@ var resetDisks = func(_ *option.Option, _ bool) { dataDiskDir := filepath.Join(finchRootDir, ".finch", ".disks") gomega.Expect(os.RemoveAll(dataDiskDir)).ShouldNot(gomega.HaveOccurred()) } + +// shutdownWSL is a wrapper function for "wsl --shutdown". +// +// This is a workaround for https://github.com/microsoft/WSL/issues/8529 +// +// If WSL is suspected of hanging for longer than 10 seconds, then +// kill the WSL service and retry the shutdown command. +var shutdownWSL = func() error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := exec.CommandContext(ctx, "wsl", "--shutdown").Run(); err != nil && errors.Is(err, context.DeadlineExceeded) { + // wsl is hung, kill the wsl service and try again. + // https://github.com/microsoft/WSL/issues/8529 + killCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + exec.CommandContext(killCtx, "taskkill", "/f", "/im", "wslservice.exe").Run() + + retryCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + return exec.CommandContext(retryCtx, "wsl", "--shutdown").Run() + } else if err != nil { + return err + } + + return nil +}