Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0460f8d

Browse files
committedMar 11, 2025
Fix WSL installation check on Windows
Fixes containers#25234 Signed-off-by: Mario Loriedo <[email protected]>
1 parent 763e7f4 commit 0460f8d

File tree

4 files changed

+218
-30
lines changed

4 files changed

+218
-30
lines changed
 

‎Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ localunit: test/goecho/goecho test/version/version
652652
UNIT=1 $(GINKGO) \
653653
-r \
654654
$(TESTFLAGS) \
655-
--skip-package test/e2e,pkg/bindings,hack,pkg/machine/e2e \
655+
--skip-package test/e2e,pkg/bindings,hack,pkg/machine/e2e,pkg/machine/wsl \
656656
--cover \
657657
--covermode atomic \
658658
--coverprofile coverprofile \

‎pkg/machine/wsl/machine.go

+4-8
Original file line numberDiff line numberDiff line change
@@ -317,12 +317,12 @@ func checkAndInstallWSL(reExec bool) (bool, error) {
317317

318318
admin := HasAdminRights()
319319

320-
if !IsWSLFeatureEnabled() {
320+
if !wutil.IsWSLFeatureEnabled() {
321321
return false, attemptFeatureInstall(reExec, admin)
322322
}
323323

324324
skip := false
325-
if reExec && !admin {
325+
if !reExec && !admin {
326326
fmt.Println("Launching WSL Kernel Install...")
327327
if err := launchElevate(wslInstallKernel); err != nil {
328328
return false, err
@@ -363,11 +363,11 @@ func attemptFeatureInstall(reExec, admin bool) error {
363363
message += "NOTE: A system reboot will be required as part of this process. " +
364364
"If you prefer, you may abort now, and perform a manual installation using the \"wsl --install\" command."
365365

366-
if reExec && MessageBox(message, "Podman Machine", false) != 1 {
366+
if !reExec && MessageBox(message, "Podman Machine", false) != 1 {
367367
return errors.New("the WSL installation aborted")
368368
}
369369

370-
if reExec && !admin {
370+
if !reExec && !admin {
371371
return launchElevate("install the Windows WSL Features")
372372
}
373373

@@ -622,10 +622,6 @@ func obtainGlobalConfigLock() (*fileLock, error) {
622622
return lockFile(filepath.Join(lockDir, "podman-config.lck"))
623623
}
624624

625-
func IsWSLFeatureEnabled() bool {
626-
return wutil.SilentExec(wutil.FindWSL(), "--set-default-version", "2") == nil
627-
}
628-
629625
func isWSLRunning(dist string) (bool, error) {
630626
return wslCheckExists(dist, true)
631627
}

‎pkg/machine/wsl/wutil/wutil.go

+68-21
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,26 @@ import (
1919
)
2020

2121
var (
22-
once sync.Once
23-
wslPath string
22+
onceFind, onceStatus sync.Once
23+
wslPath string
24+
status wslStatus
25+
wslNotInstalledMessages = []string{"kernel file is not found", "The Windows Subsystem for Linux is not installed"}
26+
vmpDisabledMessages = []string{"enable the Virtual Machine Platform Windows feature", "Enable \"Virtual Machine Platform\""}
27+
wslDisabledMessages = []string{"enable the \"Windows Subsystem for Linux\" optional component"}
2428
)
2529

30+
type wslStatus struct {
31+
installed bool
32+
vmpFeatureEnabled bool
33+
wslFeatureEnabled bool
34+
}
35+
2636
func FindWSL() string {
2737
// At the time of this writing, a defect appeared in the OS preinstalled WSL executable
2838
// where it no longer reliably locates the preferred Windows App Store variant.
2939
//
3040
// Manually discover (and cache) the wsl.exe location to bypass the problem
31-
once.Do(func() {
41+
onceFind.Do(func() {
3242
var locs []string
3343

3444
// Prefer Windows App Store version
@@ -87,24 +97,44 @@ func SilentExecCmd(command string, args ...string) *exec.Cmd {
8797
return cmd
8898
}
8999

90-
func IsWSLInstalled() bool {
91-
cmd := SilentExecCmd(FindWSL(), "--status")
92-
out, err := cmd.StdoutPipe()
93-
cmd.Stderr = nil
94-
if err != nil {
95-
return false
96-
}
97-
if err = cmd.Start(); err != nil {
98-
return false
99-
}
100+
func parseWSLStatus() wslStatus {
101+
onceStatus.Do(func() {
102+
status = wslStatus{
103+
installed: false,
104+
vmpFeatureEnabled: false,
105+
wslFeatureEnabled: false,
106+
}
107+
cmd := SilentExecCmd(FindWSL(), "--status")
108+
out, err := cmd.StdoutPipe()
109+
cmd.Stderr = nil
110+
if err != nil {
111+
return
112+
}
113+
if err = cmd.Start(); err != nil {
114+
return
115+
}
116+
117+
status = matchOutputLine(out)
118+
119+
if err := cmd.Wait(); err != nil {
120+
return
121+
}
122+
})
100123

101-
kernelNotFound := matchOutputLine(out, "kernel file is not found")
124+
return status
125+
}
102126

103-
if err := cmd.Wait(); err != nil {
127+
func IsWSLInstalled() bool {
128+
status := parseWSLStatus()
129+
return status.installed && status.vmpFeatureEnabled
130+
}
131+
132+
func IsWSLFeatureEnabled() bool {
133+
if SilentExec(FindWSL(), "--set-default-version", "2") != nil {
104134
return false
105135
}
106-
107-
return !kernelNotFound
136+
status := parseWSLStatus()
137+
return status.vmpFeatureEnabled
108138
}
109139

110140
func IsWSLStoreVersionInstalled() bool {
@@ -118,13 +148,30 @@ func IsWSLStoreVersionInstalled() bool {
118148
return true
119149
}
120150

121-
func matchOutputLine(output io.ReadCloser, match string) bool {
151+
func matchOutputLine(output io.ReadCloser) wslStatus {
152+
status := wslStatus{
153+
installed: true,
154+
vmpFeatureEnabled: true,
155+
wslFeatureEnabled: true,
156+
}
122157
scanner := bufio.NewScanner(transform.NewReader(output, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
123158
for scanner.Scan() {
124159
line := scanner.Text()
125-
if strings.Contains(line, match) {
126-
return true
160+
for _, match := range wslNotInstalledMessages {
161+
if strings.Contains(line, match) {
162+
status.installed = false
163+
}
164+
}
165+
for _, match := range vmpDisabledMessages {
166+
if strings.Contains(line, match) {
167+
status.vmpFeatureEnabled = false
168+
}
169+
}
170+
for _, match := range wslDisabledMessages {
171+
if strings.Contains(line, match) {
172+
status.wslFeatureEnabled = false
173+
}
127174
}
128175
}
129-
return false
176+
return status
130177
}

‎pkg/machine/wsl/wutil/wutil_test.go

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//go:build windows
2+
3+
package wutil
4+
5+
import (
6+
"github.com/stretchr/testify/assert"
7+
"golang.org/x/text/encoding/unicode"
8+
"io"
9+
"strings"
10+
"testing"
11+
)
12+
13+
const (
14+
WSL1InstalledWithWSLAndVMPEnabled = `Default Version: 1`
15+
WSL2InstalledWithWSLAndVMPEnabled = `Default Version: 2`
16+
WSL1NotInstalled = `Default Version: 1
17+
18+
The Windows Subsystem for Linux kernel can be manually updated with 'wsl --update', but automatic updates cannot occur due to your system settings.
19+
To receive automatic kernel updates, please enable the Windows Update setting: 'Receive updates for other Microsoft products when you update Windows'.
20+
For more information please visit https://aka.ms/wsl2kernel.
21+
22+
The WSL 2 kernel file is not found. To update or restore the kernel please run 'wsl --update'.`
23+
WSL2NotInstalled = `The Windows Subsystem for Linux is not installed. You can install by running 'wsl.exe --install'.
24+
For more information please visit https://aka.ms/wslinstall`
25+
WSL2InstalledWithWSLDisabled = `Default Version: 2
26+
WSL1 is not supported with your current machine configuration.
27+
Please enable the "Windows Subsystem for Linux" optional component to use WSL1.`
28+
WSL2InstalledWithVMPDisabled = `Default Version: 2
29+
WSL2 is not supported with your current machine configuration.
30+
Please enable the "Virtual Machine Platform" optional component and ensure virtualization is enabled in the BIOS.
31+
Enable "Virtual Machine Platform" by running: wsl.exe --install --no-distribution
32+
For information please visit https://aka.ms/enablevirtualization`
33+
WSL2InstalledWithWSLAndVMPDisabled = `Default Version: 2
34+
WSL1 is not supported with your current machine configuration.
35+
Please enable the "Windows Subsystem for Linux" optional component to use WSL1.
36+
WSL2 is not supported with your current machine configuration.
37+
Please enable the "Virtual Machine Platform" optional component and ensure virtualization is enabled in the BIOS.
38+
Enable "Virtual Machine Platform" by running: wsl.exe --install --no-distribution
39+
For information please visit https://aka.ms/enablevirtualization`
40+
WSL1InstalledWithVMPDisabled = `Default Version: 1
41+
Please enable the Virtual Machine Platform Windows feature and ensure virtualization is enabled in the BIOS.
42+
For information please visit https://aka.ms/enablevirtualization`
43+
WSL1InstalledWithWSLDisabled = `Default Version: 1
44+
WSL1 is not supported with your current machine configuration.
45+
Please enable the "Windows Subsystem for Linux" optional component to use WSL1.`
46+
)
47+
48+
func TestMatchOutputLine(t *testing.T) {
49+
tests := []struct {
50+
winVariant string
51+
statusOutput string
52+
want wslStatus
53+
}{
54+
{
55+
"WSL1 configured and both Virtual Machine Platform enabled and Windows Subsystem for Linux are enabled",
56+
WSL1InstalledWithWSLAndVMPEnabled,
57+
wslStatus{
58+
installed: true,
59+
vmpFeatureEnabled: true,
60+
wslFeatureEnabled: true,
61+
},
62+
},
63+
{
64+
"WSL2 configured and both Virtual Machine Platform enabled and Windows Subsystem for Linux enabled",
65+
WSL2InstalledWithWSLAndVMPEnabled,
66+
wslStatus{
67+
installed: true,
68+
vmpFeatureEnabled: true,
69+
wslFeatureEnabled: true,
70+
},
71+
},
72+
{
73+
"WSL not installed (was previously configured as version 1)",
74+
WSL1NotInstalled,
75+
wslStatus{
76+
installed: false,
77+
vmpFeatureEnabled: true,
78+
wslFeatureEnabled: true,
79+
},
80+
},
81+
{
82+
"WSL not installed (was previously configured as version 2)",
83+
WSL2NotInstalled,
84+
wslStatus{
85+
installed: false,
86+
vmpFeatureEnabled: true,
87+
wslFeatureEnabled: true,
88+
},
89+
},
90+
{
91+
"WSL2 configured and Virtual Machine Platform is enabled but Windows Subsystem for Linux is disabled",
92+
WSL2InstalledWithWSLDisabled,
93+
wslStatus{
94+
installed: true,
95+
vmpFeatureEnabled: true,
96+
wslFeatureEnabled: false,
97+
},
98+
},
99+
{
100+
"WSL2 configured and Virtual Machine Platform is disabled but Windows Subsystem for Linux is enabled",
101+
WSL2InstalledWithVMPDisabled,
102+
wslStatus{
103+
installed: true,
104+
vmpFeatureEnabled: false,
105+
wslFeatureEnabled: true,
106+
},
107+
},
108+
{
109+
"WSL2 configured and both Virtual Machine Platform and Windows Subsystem for Linux are disabled",
110+
WSL2InstalledWithWSLAndVMPDisabled,
111+
wslStatus{
112+
installed: true,
113+
vmpFeatureEnabled: false,
114+
wslFeatureEnabled: false,
115+
},
116+
},
117+
{
118+
"WSL1 configured and Virtual Machine Platform is disabled but Windows Subsystem for Linux is enabled",
119+
WSL1InstalledWithVMPDisabled,
120+
wslStatus{
121+
installed: true,
122+
vmpFeatureEnabled: false,
123+
wslFeatureEnabled: true,
124+
},
125+
},
126+
{
127+
"WSL1 configured and Virtual Machine Platform is enabled but Windows Subsystem for Linux is disabled",
128+
WSL1InstalledWithWSLDisabled,
129+
wslStatus{
130+
installed: true,
131+
vmpFeatureEnabled: true,
132+
wslFeatureEnabled: false,
133+
},
134+
},
135+
}
136+
for _, tt := range tests {
137+
t.Run(tt.winVariant, func(t *testing.T) {
138+
encoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewEncoder()
139+
encodedOutput, err := encoder.String(tt.statusOutput)
140+
assert.Nil(t, err)
141+
reader := io.NopCloser(strings.NewReader(encodedOutput))
142+
assert.Equal(t, tt.want, matchOutputLine(reader))
143+
})
144+
}
145+
}

0 commit comments

Comments
 (0)