Skip to content
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

Fix IPv6 support #1562

Closed
wants to merge 3 commits into from
Closed

Fix IPv6 support #1562

wants to merge 3 commits into from

Conversation

imiric
Copy link
Contributor

@imiric imiric commented Jul 17, 2020

Closes #1537

This was straightforward to fix once I realized I could test this with Docker 😄 It's trivial to setup, so we could have a few integration/E2E tests in CI. Other than that, I'm not sure how else to test this without substantially refactoring the Dialer to make it testable. Any other ideas?

@imiric imiric requested review from mstoykov and na-- July 17, 2020 17:46
@codecov-commenter
Copy link

codecov-commenter commented Jul 17, 2020

Codecov Report

Merging #1562 into master will decrease coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1562      +/-   ##
==========================================
- Coverage   77.11%   77.10%   -0.02%     
==========================================
  Files         162      162              
  Lines       13192    13199       +7     
==========================================
+ Hits        10173    10177       +4     
- Misses       2499     2503       +4     
+ Partials      520      519       -1     
Impacted Files Coverage Δ
lib/netext/dialer.go 100.00% <100.00%> (+2.56%) ⬆️
lib/testutils/minirunner/minirunner.go 78.57% <0.00%> (-3.58%) ⬇️
lib/executor/vu_handle.go 93.69% <0.00%> (-2.71%) ⬇️
lib/netext/httpext/request.go 96.72% <0.00%> (-0.55%) ⬇️
lib/executor/constant_arrival_rate.go 96.75% <0.00%> (-0.55%) ⬇️
stats/cloud/collector.go 79.04% <0.00%> (+0.59%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 73d5ee7...d6c6ecc. Read the comment docs.

Comment on lines 75 to 108
ip := net.ParseIP(host)
if ip == nil {
// It's not an IP address, so lookup the hostname in the Hosts
// option before trying to resolve DNS.
var ok bool
ip, ok = d.Hosts[host]
if !ok {
var dnsErr error
ip, dnsErr = d.Resolver.FetchOne(host)
if dnsErr != nil {
return nil, dnsErr
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably good for a function and can be tested on it's own

Copy link
Member

@na-- na-- Jul 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, the IP:port splitting and DNS resolving logic can be a separate function(s), so you can test it with unit tests with valid and invalid IPv4, IPv6, and domains. I also think we can also test the actual IPv6 connection logic as well, at least if we have IPv6 on our systems or CI. And we can reliably skip it if we don't.

We should be able to use net.Listen with tcp6 for network and port 0. If it returns an error, then we t.Skip() the test. If not, we can spin up a test httptest.Server, or even httpmultibin there.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to use net.Listen with tcp6 for network and port 0. If it returns an error, then we t.Skip() the test. If not, we can spin up a test httptest.Server, or even httpmultibin there.

I suppose this could be done, I am not sure it will test everything nicely as we can probably test it on the loopback interface, but not between machines .... 🤷 But we can at least try that :D

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loopback interface should still be employing most of the networking stack, I think - from the standpoint of Go and k6, everything should be pretty much the same, only the OS cares... That said, besides running the tests in the IPv6-enabled Docker container, like @imiric showed, might be the only way to run that test, for now, given that GitHub Actions doesn't support it: actions/runner-images#668 (comment) 😞

Copy link
Contributor Author

@imiric imiric Jul 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if 437d91f is enough.

Ideally resolveHost() should work on a Resolver interface so that we can mock out the net.LookupIP() call. Right now this does an actual DNS lookup, which might fail if the DNS server doesn't support IPv6. Unlikely, but we should avoid environment issues like this, and there's no need to test the DNS. :)

Since we don't do much mocking elsewhere, it was easier to not do this, but let me know.

I'll look into the higher-level connection tests with httpmultibin.

EDIT: Ah, spoke too soon. Maybe it should be mocked after all.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@imiric I vote for t.Skip on windows and unskipping it once on github actions for windows

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-/ I'm leaning towards the interface approach, as it's not an issue with Windows, but with the environment. This test could fail just as easily on a dev machine using a non-IPv6 DNS, so removing the environment out of the equation is the safest approach for these unit tests. For integration tests there should be no mocking, but we can mark them as such and run them in an integration environment eventually.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at d6c6ecc.

I decided to extract more of DialContext into that function, so now it's awkwardly named checkAndResolveAddress, but I think having unit tests for this crucial part of k6 makes up for it.

@@ -66,31 +65,42 @@ func (b BlackListedIPError) Error() string {
return fmt.Sprintf("IP (%s) is in a blacklisted range (%s)", b.ip, b.net)
}

func resolveHost(host string, hosts map[string]net.IP, resolver *dnscache.Resolver) (net.IP, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A minor nitpick, but if this was a method of the Dialer, you can skip the first 2 arguments while still having it very testable. And mocking it can easily be done by just defining a simple new interface with a FetchOne() method yourself, for now (there will probably be further methods once we fix the DNS issues) and using that in the Dialer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I did that in d6c6ecc, but only the hosts argument can be removed, we need the other 2 :)

@imiric
Copy link
Contributor Author

imiric commented Jul 21, 2020

As discussed, closing this in favor of #1489, which also fixes this.

@imiric imiric closed this Jul 21, 2020
imiric pushed a commit to imiric/k6 that referenced this pull request Aug 5, 2020
imiric pushed a commit to imiric/k6 that referenced this pull request Aug 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IPv6 support
4 participants