Skip to content

Commit

Permalink
fix: use ip route show default to get WSL2 addr of windows (#6094) [s…
Browse files Browse the repository at this point in the history
…kip ci]

Co-authored-by: Stanislav Zhuk <stasadev@gmail.com>
  • Loading branch information
rfay and stasadev committed Apr 15, 2024
1 parent 45ba4e9 commit 8bfa3e5
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 31 deletions.
8 changes: 3 additions & 5 deletions docs/content/users/debugging-profiling/step-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ Xdebug is a server-side tool, and it’s installed automatically in the containe

All IDEs basically work the same, listening on a port and reacting when they’re contacted there. IDEs other than those listed here work fine, if they listen on Xdebug’s default port 9003.

!!!tip
This was port 9000 through DDEV v1.18, changed in v1.19+ to port 9003.

**Key facts:**

* Enable Xdebug by running [`ddev xdebug`](../usage/commands.md#xdebug) or `ddev xdebug on` from your project directory.
Expand Down Expand Up @@ -143,9 +140,10 @@ Here are basic steps to take to sort out any difficulty:
WSL2 is a complicated environment for Xdebug, especially if you're running your IDE on the Windows side, as most people do.

* With PhpStorm, consider using the "Remote Development" feature to connect to WSL. That runs an actual PhpStorm instance on WSL2 to reduce networking complexity.
* When using an IDE inside WSL2—like you would running PhpStorm or Visual Studio Code inside WSL2, or using PhpStorm's "Remote Development" feature—you may need to use the [`xdebug_ide_location`](../configuration/config.md#xdebugidelocation) setting to tell Xdebug to expect the IDE under WSL2. You can do this by running `ddev config global --xdebug-ide-location=wsl2`.
* When using an IDE inside WSL2—like you would when running PhpStorm or Visual Studio Code inside WSL2, or using PhpStorm's "Remote Development" feature—you may need to use the [`xdebug_ide_location`](../configuration/config.md#xdebugidelocation) setting to tell Xdebug to expect the IDE under WSL2. You can do this by running `ddev config global --xdebug-ide-location=wsl2`.
* `export DDEV_DEBUG=true && ddev start` will show you how DDEV is calculating the `host.docker.internal` IP address to be used when contacting the IDE, which may give a hint about problems you might discover in the general troubleshooting discussed above, when trying to connect to the listening IDE.
* If you're using the IDE on the Windows side, WSL2’s `/etc/resolv.conf` file is the authoritative way to figure out where the IDE is in the Windows networking scheme. That value should be the same as `host.docker.internal`, so running `ddev exec ping -c 1 host.docker.internal` will show you what's actually being used. If your IDE is actually at a different address, you can tell DDEV to override the discovered value for `host.docker.internal` by running `ddev config global --xdebug-ide-location=<some_ip_address>`.
* If you're using docker-ce and have the IDE on the Windows side, `ip -4 route show default` in the WSL2 distro is the best known way to figure out where the IDE is in the Windows networking scheme, so DDEV uses that to determine `host.docker.internal`. That value should be the same as `host.docker.internal`, so running `ddev exec ping -c 1 host.docker.internal` will show you what's actually being used. If your IDE is actually at a different address, you can tell DDEV to override the discovered value for `host.docker.internal` by running `ddev config global --xdebug-ide-location=<some_ip_address>`.
* If you’re using PhpStorm inside WSL2 (or perhaps other Linux configurations), go to *Help**Edit Custom VM Options* and add an additional line: `-Djava.net.preferIPv4Stack=true` This makes PhpStorm listen for Xdebug using IPv4; the Linux version of PhpStorm seems to default to using only IPv6.
* If you’re on WSL2 using Docker Desktop, make sure that the `docker` command is the one provided by Docker Desktop. `ls -l $(which docker)` should show a link to `/mnt/wsl/docker-desktop...`. If you’re on WSL2 using Docker installed inside WSL2, make sure that `ls -l $(which docker)` is *not* a link to `/mnt/wsl`.
* You can run `export DDEV_DEBUG=true` and `ddev start` to get information about how `host.docker.internal` is figured out, which can help in some situations especially with WSL2. (`host.docker.internal` inside the web container is where Xdebug thinks it should connect to your IDE. You can see what it is set to by running `ddev exec ping host.docker.internal`.)
* On some WSL2 docker-ce systems you may have to work hard to find out the correct IP address for the Windows side. DDEV tries to figure this out for you, but it may not be able to do so. The IP address shown as `nameserver` in `/etc/resolv.conf` may be the correct one, and this used to be the recommended technique. If it's the address you need you can change the address DDEV will use for `host.docker.internal` using `ddev config global --xdebug-ide-location=<some-ip-address>`.
81 changes: 55 additions & 26 deletions pkg/dockerutil/dockerutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -1232,9 +1232,11 @@ func GetHostDockerInternalIP() (string, error) {
break

case nodeps.IsWSL2() && !IsDockerDesktop():
// Microsoft instructions for finding Windows IP address at
// https://learn.microsoft.com/en-us/windows/wsl/networking#accessing-windows-networking-apps-from-linux-host-ip
// If IDE is on Windows, we have to parse /etc/resolv.conf
hostDockerInternal = wsl2ResolvConfNameserver()
util.Debug("host.docker.internal='%s' because IsWSL2 and !IsDockerDesktop; received from resolv.conf", hostDockerInternal)
hostDockerInternal = wsl2GetWindowsHostIP()
util.Debug("host.docker.internal='%s' because IsWSL2 and !IsDockerDesktop; received from ip -4 route show default", hostDockerInternal)

// Docker on Linux doesn't define host.docker.internal
// so we need to go get the bridge IP address
Expand Down Expand Up @@ -1277,9 +1279,8 @@ func GetNFSServerAddr() (string, error) {
break

case nodeps.IsWSL2() && !IsDockerDesktop():
// If IDE is on Windows, we have to parse /etc/resolv.conf
// Else it will be fine, we can fallthrough to the Linux version
nfsAddr = wsl2ResolvConfNameserver()

nfsAddr = wsl2GetWindowsHostIP()

// Docker on Linux doesn't define host.docker.internal
// so we need to go get the bridge IP address
Expand All @@ -1305,31 +1306,59 @@ func GetNFSServerAddr() (string, error) {
return nfsAddr, nil
}

// 2024-04-13: The approach in wsl2ResolveConfNameserver no longer seems to be valid

// wsl2ResolvConfNameserver parses /etc/resolv.conf to get the nameserver,
// which is the only documented way to know how to connect to the host
// to connect to PhpStorm or other IDE listening there. Or for other apps.
func wsl2ResolvConfNameserver() string {
if nodeps.IsWSL2() {
isAuto, err := fileutil.FgrepStringInFile("/etc/resolv.conf", "automatically generated by WSL")
if err != nil || !isAuto {
util.Warning("unable to determine WSL2 host.docker.internal because /etc/resolv.conf is not available or not auto-generated")
return ""
}
// We grepped it so no need to check error
etcResolv, _ := fileutil.ReadFileIntoString("/etc/resolv.conf")
util.Debug("resolv.conf=%s", etcResolv)

nameserverRegex := regexp.MustCompile(`nameserver *([0-9\.]*)`)
// nameserverRegex.ReplaceAllFunc([]byte(etcResolv), []byte(`$1`))
res := nameserverRegex.FindStringSubmatch(etcResolv)
if res == nil || len(res) != 2 {
util.Warning("unable to determine host.docker.internal from /etc/resolv.conf")
return ""
}
return res[1]
//func wsl2ResolvConfNameserver() string {
// if nodeps.IsWSL2() {
// isAuto, err := fileutil.FgrepStringInFile("/etc/resolv.conf", "automatically generated by WSL")
// if err != nil || !isAuto {
// util.Warning("unable to determine WSL2 host.docker.internal because /etc/resolv.conf is not available or not auto-generated")
// return ""
// }
// // We grepped it so no need to check error
// etcResolv, _ := fileutil.ReadFileIntoString("/etc/resolv.conf")
// util.Debug("resolv.conf=%s", etcResolv)
//
// nameserverRegex := regexp.MustCompile(`nameserver *([0-9\.]*)`)
// // nameserverRegex.ReplaceAllFunc([]byte(etcResolv), []byte(`$1`))
// res := nameserverRegex.FindStringSubmatch(etcResolv)
// if res == nil || len(res) != 2 {
// util.Warning("unable to determine host.docker.internal from /etc/resolv.conf")
// return ""
// }
// return res[1]
// }
// util.Warning("inappropriately using wsl2ResolvConfNameserver() but not on WSL2")
// return ""
//}

// wsl2GetWindowsHostIP() uses ip -4 route show default to get the Windows IP address
// for use in determining host.docker.internal
func wsl2GetWindowsHostIP() string {
// Get default route from WSL2
out, err := ddevexec.RunHostCommand("ip", "-4", "route", "show", "default")

if err != nil {
util.Warning("Unable to run 'ip -4 route show default' to get Windows IP address")
return ""
}
util.Warning("inappropriately using wsl2ResolvConfNameserver() but not on WSL2")
return ""
parts := strings.Split(out, " ")
if len(parts) < 3 {
util.Warning("Unable to parse output of 'ip -4 route show default', result was %v", parts)
return ""
}

ip := parts[2]

if parsedIP := net.ParseIP(ip); parsedIP == nil {
util.Warning("Unable to validate IP address '%s' from 'ip -4 route show default'", ip)
return ""
}

return ip
}

// RemoveImage removes an image with force
Expand Down

0 comments on commit 8bfa3e5

Please sign in to comment.