-
Notifications
You must be signed in to change notification settings - Fork 25
Improve WSL 2 experience #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve WSL 2 experience #57
Conversation
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
interactive/interactive.go
Outdated
| func WSL2() (bool, error) { | ||
| if runtime.GOOS != "linux" { | ||
| return false, nil | ||
| } | ||
| out, err := exec.Command("cat", "/proc/version").CombinedOutput() | ||
| if err != nil { | ||
| return false, errors.Wrapf(err, "detecting WSL2: %s", string(out)) | ||
| } | ||
| return bytes.Contains(out, []byte("WSL2")), nil | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would love to move this WSL2 function to https://github.com/efficientgo/core. What do you think? cc @matej-g @bwplotka
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also considering adding context support there to enable quick work cancellation. Not sure whether it would be valuable.
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
interactive/interactive.go
Outdated
| if runtime.GOOS != "linux" { | ||
| return false, nil | ||
| } | ||
| out, err := exec.Command("cat", "/proc/version").CombinedOutput() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just do grep here instead of reading the entire contents and doing bytes.Contains?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am afraid grep might not be thoroughly available in Linux distros by default, especially the more slim ones. /proc/version contains very little text, so I don't think the performance is a problem. It has this "shape": Linux version 2.6.31.12-0.2-default (geeko@buildhost) (gcc version 4.4.1 [gcc-4_4-branch revision 150839] (SUSE Linux) ) #1 SMP 2010-03-16 21:25:39 +0100.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I even thought about not using cat and directly reading /proc/version. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually now I consider simply reading the file better. 100% agnostic of whatever is installed in the OS and hides the error from callers. If the file doesn't exist it means not WSL 2.
interactive/interactive.go
Outdated
| if err != nil { | ||
| return false, errors.Wrapf(err, "detecting WSL2: %s", string(out)) | ||
| } | ||
| return bytes.Contains(out, []byte("WSL2")), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to check if it contains WSL2 or Microsoft? Referriing to similar issue microsoft/WSL#4071
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm aware of that issue. I believe it is better to check for WSL2 because:
- I don't know and didn't test if this works with WSL 1. Likely it doesn't as networking is very different between WSL 1 and 2.
- We are future proof in case Microsoft releases WSL 3 and it requires a different workaround (😂 😭 )
- No false positives if one day a wild Microsoft Linux distribution appears.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just imagining someone having to come back to this code in 5 years like 'wth is a WSL?'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documented what is WSL at ad6f833
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
env.go
Outdated
| // Close shutdowns isolated environment and cleans its resources. | ||
| Close() | ||
| // WSL2 returns true if the environment is WSL2. | ||
| WSL2() bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure i understand why we need this. If WSL2 is not compatible with cadvisor we should either:
- Remove cadvisor (to remove a dependency on linux for e2e)
- Keep cadvisor, and encourage people who use linux subsystem on linux to disable cadvisor.
Adding a field to Environment configuration for this very specific edge case stinks of 'leaky abstraction'.
Conversely you can invert this and have a 'useCadvisor' env variable that will determine if it should be enabled or not so there isn't this conceptual hop from:
' Why is it relevant if i'm using WSL2?'
greps WSL2
'Oh, WSL doesn't support cadvisor.'
'I'll just disable cadvisor'
Just my thoughts, not willing to die on this hill though. IMHO i'd rather avoid embedding OS checks in configuration for very specific feature compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got rid of the WSL() function in the Environment interface, as indeed I agree it leaks details from the DockerEnvironment type. Instead now I just import e2einteractive and use it from there. Note that as I mentioned in another comment, I would like to move the function to efficientgo/core if maintainers agree. Changes are in 4330eb9.
Keep cadvisor, and encourage people who use linux subsystem on linux to disable cadvisor.
I talked with @bwplotka about this and our conclusion is that we should "panic" and stop execution in the scenario where we're running in WSL 2 and the option to run cAdvisor is set to true.
env_docker.go
Outdated
| d.hostAddr = dockerMacOSGatewayAddr | ||
| d.hostAddr = dockerGatewayAddr | ||
| default: | ||
| if d.WSL2() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does runtime.GOOS return if running on WSL? If it isn't linux what is it?
This switch statement is also redundant, you really shouldn't have to branch conditionals inside of a switch. This should either be in a case or should not be set here.
I also know it was like this before so 🤷🏽
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does runtime.GOOS return if running on WSL? If it isn't linux what is it?
It does return linux, that's why the implementation checks for the kernel version by reading from /proc/version. Also it uses runtime.GOOS != linux as a condition to fail quickly returning false.
| // via IPv4 to confirm Prometheus can scrape the local endpoint. | ||
| // Explicitly asking for an IPv4 listener works. | ||
| if env.WSL2() { | ||
| networkType = "tcp4" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is tcp4 the more specific option (specifies you want an ipv4 connection)? If so we can just make that the default and get rid of the branching conditionals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do this. Only downside would be that Docker one day might drop support for ipv4, but I bet that's likely going to happen only after our lifetimes. Will change!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, this might also not work on servers/machines with only ipv6 stack (might some cloud/on-prem CI runners?). 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think, @moadz?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use IPv6 then for network port later on? If the latter step requires IPv4, we might as well put tcp4 here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only affecting the listener that is used for a quick test of the host-container networking. It won't affect anything after this point.
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
interactive/interactive.go
Outdated
| // HostOSPlatform returns the host's OS platform akin to `runtime.GOOS`, with | ||
| // added awareness of Windows Subsystem for Linux (WSL) 2 environments. | ||
| // The possible values are the same as `runtime.GOOS`, plus "WSL2". | ||
| func HostOSPlatform() string { | ||
| if wsl2() { | ||
| return "WSL2" | ||
| } | ||
| return runtime.GOOS | ||
| } | ||
|
|
||
| func wsl2() bool { | ||
| if runtime.GOOS != "linux" { | ||
| return false | ||
| } | ||
| version, err := os.ReadFile("/proc/version") | ||
| if err != nil { | ||
| return false | ||
| } | ||
| return bytes.Contains(version, []byte("WSL2")) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Repeating old comment here for visibility after some updates: I would love to move these two functions into https://github.com/efficientgo/core. If maintainers agree, I'll open a PR there and update this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... We could consider that. Feel free to add TODO with this, but I ideally we test it's usability (how useful it is for others) in e2e first.
bwplotka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts, otherwise LGTM. Thanks!
interactive/interactive.go
Outdated
| // HostOSPlatform returns the host's OS platform akin to `runtime.GOOS`, with | ||
| // added awareness of Windows Subsystem for Linux (WSL) 2 environments. | ||
| // The possible values are the same as `runtime.GOOS`, plus "WSL2". | ||
| func HostOSPlatform() string { | ||
| if wsl2() { | ||
| return "WSL2" | ||
| } | ||
| return runtime.GOOS | ||
| } | ||
|
|
||
| func wsl2() bool { | ||
| if runtime.GOOS != "linux" { | ||
| return false | ||
| } | ||
| version, err := os.ReadFile("/proc/version") | ||
| if err != nil { | ||
| return false | ||
| } | ||
| return bytes.Contains(version, []byte("WSL2")) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... We could consider that. Feel free to add TODO with this, but I ideally we test it's usability (how useful it is for others) in e2e first.
| // via IPv4 to confirm Prometheus can scrape the local endpoint. | ||
| // Explicitly asking for an IPv4 listener works. | ||
| if env.WSL2() { | ||
| networkType = "tcp4" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use IPv6 then for network port later on? If the latter step requires IPv4, we might as well put tcp4 here.
monitoring/monitoring.go
Outdated
| env.AddListener(l) | ||
|
|
||
| if opt.useCadvisor { | ||
| if e2einteractive.HostOSPlatform() == "WSL2" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this is ideally in separate package or e2e.
bwplotka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing, thanks! 💪🏽
| return false | ||
| } | ||
| version, err := os.ReadFile("/proc/version") | ||
| if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we could be more strict on error cases, but we can improve later (:
* Detect WSL2 and open in browser from Windows Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Fix hostAddr in WSL2 Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Fix host-local endpoint test in WSL2 Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Fail fast to start in WSL2 with cadvisor Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Inline some conditionals Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Detect WSL 2 without relying on OS binaries/tools Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Fix var name Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Drop environment.WSL2() and use func from package Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Improve how OS platform is checked Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Document what is WSL Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Fix WSL2 check for network test Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Fix networkType deletion Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Separate OS platform function into its own package * Reorder imports * Add TODO to new `host` package * Please the copyright linter Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com>
With proper detection of WSL 2 we can give it the special treatment it needs.
Changes
On detection of WSL 2, the following happens:
tcp4(IPv4). I tested withtcp(should be IPv4 and v6) but it ended up only opening atcp6port and the check happens via IPv4, which fails.hostAddris set tohost.docker.local, as Windows/WSL 2 also use it.OpenInBrowserwill open the URLs in the Windows browser.I tested this out in my Windows box and it works flawlessly.