Skip to content

Commit 590297a

Browse files
authored
Merge pull request #3988 from afbjorklund/wsl2-ssh
Restore DynamicSSHAddress functionality for WSL2
2 parents 964fb30 + 646fde0 commit 590297a

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

pkg/driver/wsl2/vm_windows.go

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
_ "embed"
99
"fmt"
10+
"net"
1011
"os"
1112
"os/exec"
1213
"path/filepath"
@@ -134,7 +135,7 @@ func provisionVM(ctx context.Context, instanceDir, instanceName, distroName stri
134135
for {
135136
<-ctx.Done()
136137
logrus.Info("Context closed, stopping vm")
137-
if status, err := getWslStatus(instanceName); err == nil &&
138+
if status, err := getWslStatus(ctx, instanceName); err == nil &&
138139
status == limatype.StatusRunning {
139140
_ = stopVM(ctx, distroName)
140141
}
@@ -179,13 +180,40 @@ func unregisterVM(ctx context.Context, distroName string) error {
179180
return nil
180181
}
181182

182-
func getWslStatus(instName string) (string, error) {
183+
// GetWslStatus runs `wsl --list --verbose` and parses its output.
184+
// There are several possible outputs, all listed with their whitespace preserved output below.
185+
//
186+
// (1) Expected output if at least one distro is installed:
187+
// PS > wsl --list --verbose
188+
//
189+
// NAME STATE VERSION
190+
//
191+
// * Ubuntu Stopped 2
192+
//
193+
// (2) Expected output when no distros are installed, but WSL is configured properly:
194+
// PS > wsl --list --verbose
195+
// Windows Subsystem for Linux has no installed distributions.
196+
//
197+
// Use 'wsl.exe --list --online' to list available distributions
198+
// and 'wsl.exe --install <Distro>' to install.
199+
//
200+
// Distributions can also be installed by visiting the Microsoft Store:
201+
// https://aka.ms/wslstore
202+
// Error code: Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND
203+
//
204+
// (3) Expected output when no distros are installed, and WSL2 has no kernel installed:
205+
//
206+
// PS > wsl --list --verbose
207+
// Windows Subsystem for Linux has no installed distributions.
208+
// Distributions can be installed by visiting the Microsoft Store:
209+
// https://aka.ms/wslstore
210+
func getWslStatus(ctx context.Context, instName string) (string, error) {
183211
distroName := "lima-" + instName
184212
out, err := executil.RunUTF16leCommand([]string{
185213
"wsl.exe",
186214
"--list",
187215
"--verbose",
188-
})
216+
}, executil.WithContext(ctx))
189217
if err != nil {
190218
return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, out)
191219
}
@@ -229,3 +257,37 @@ func getWslStatus(instName string) (string, error) {
229257

230258
return instState, nil
231259
}
260+
261+
// GetSSHAddress runs a hostname command to get the IP from inside of a wsl2 VM.
262+
//
263+
// Expected output (whitespace preserved, [] for optional):
264+
// PS > wsl -d <distroName> bash -c hostname -I | cut -d' ' -f1
265+
// 168.1.1.1 [10.0.0.1]
266+
// But busybox hostname does not implement --all-ip-addresses:
267+
// hostname: unrecognized option: I
268+
func getSSHAddress(ctx context.Context, instName string) (string, error) {
269+
distroName := "lima-" + instName
270+
// Ubuntu
271+
cmd := exec.CommandContext(ctx, "wsl.exe", "-d", distroName, "bash", "-c", `hostname -I | cut -d ' ' -f1`)
272+
out, err := cmd.CombinedOutput()
273+
if err == nil {
274+
return strings.TrimSpace(string(out)), nil
275+
}
276+
// Alpine
277+
cmd = exec.CommandContext(ctx, "wsl.exe", "-d", distroName, "sh", "-c", `ip route get 1 | awk '{gsub("^.*src ",""); print $1; exit}'`)
278+
out, err = cmd.CombinedOutput()
279+
if err == nil {
280+
return strings.TrimSpace(string(out)), nil
281+
}
282+
// fallback
283+
cmd = exec.CommandContext(ctx, "wsl.exe", "-d", distroName, "hostname", "-i")
284+
out, err = cmd.CombinedOutput()
285+
if err == nil {
286+
ip := net.ParseIP(strings.TrimSpace(string(out)))
287+
// some distributions use "127.0.1.1" as the host IP, but we want something that we can route to here
288+
if ip != nil && !ip.IsLoopback() {
289+
return strings.TrimSpace(string(out)), nil
290+
}
291+
}
292+
return "", fmt.Errorf("failed to get hostname for instance %q, err: %w (out=%q)", instName, err, string(out))
293+
}

pkg/driver/wsl2/wsl_driver_windows.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func (l *LimaWslDriver) BootScripts() (map[string][]byte, error) {
182182
}
183183

184184
func (l *LimaWslDriver) InspectStatus(ctx context.Context, inst *limatype.Instance) string {
185-
status, err := getWslStatus(inst.Name)
185+
status, err := getWslStatus(ctx, inst.Name)
186186
if err != nil {
187187
inst.Status = limatype.StatusBroken
188188
inst.Errors = append(inst.Errors, err)
@@ -193,7 +193,7 @@ func (l *LimaWslDriver) InspectStatus(ctx context.Context, inst *limatype.Instan
193193
inst.SSHLocalPort = 22
194194

195195
if inst.Status == limatype.StatusRunning {
196-
sshAddr, err := l.SSHAddress(ctx)
196+
sshAddr, err := getSSHAddress(ctx, inst.Name)
197197
if err == nil {
198198
inst.SSHAddress = sshAddr
199199
} else {
@@ -206,7 +206,7 @@ func (l *LimaWslDriver) InspectStatus(ctx context.Context, inst *limatype.Instan
206206

207207
func (l *LimaWslDriver) Delete(ctx context.Context) error {
208208
distroName := "lima-" + l.Instance.Name
209-
status, err := getWslStatus(l.Instance.Name)
209+
status, err := getWslStatus(ctx, l.Instance.Name)
210210
if err != nil {
211211
return err
212212
}
@@ -221,7 +221,7 @@ func (l *LimaWslDriver) Delete(ctx context.Context) error {
221221

222222
func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) {
223223
logrus.Infof("Starting WSL VM")
224-
status, err := getWslStatus(l.Instance.Name)
224+
status, err := getWslStatus(ctx, l.Instance.Name)
225225
if err != nil {
226226
return nil, err
227227
}

0 commit comments

Comments
 (0)