libnetwork: move resolver tests that were skipped on Windows

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2023-08-10 12:35:52 +02:00
parent 1e4e9161c5
commit de4ba13400
No known key found for this signature in database
GPG key ID: 76698F39D527CE8C
2 changed files with 178 additions and 171 deletions

View file

@ -5,7 +5,6 @@ import (
"encoding/hex"
"errors"
"net"
"runtime"
"syscall"
"testing"
"time"
@ -16,7 +15,6 @@ import (
"github.com/sirupsen/logrus"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
// a simple/null address type that will be used to fake a local address for unit testing
@ -95,102 +93,6 @@ func checkDNSRRType(t *testing.T, actual, expected uint16) {
}
}
func TestDNSIPQuery(t *testing.T) {
skip.If(t, runtime.GOOS == "windows", "test only works on linux")
defer netnsutils.SetupTestOSContext(t)()
c, err := New()
if err != nil {
t.Fatal(err)
}
defer c.Stop()
n, err := c.NewNetwork("bridge", "dtnet1", "", nil)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := n.Delete(); err != nil {
t.Fatal(err)
}
}()
ep, err := n.CreateEndpoint("testep")
if err != nil {
t.Fatal(err)
}
sb, err := c.NewSandbox("c1")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := sb.Delete(); err != nil {
t.Fatal(err)
}
}()
// we need the endpoint only to populate ep_list for the sandbox as part of resolve_name
// it is not set as a target for name resolution and does not serve any other purpose
err = ep.Join(sb)
if err != nil {
t.Fatal(err)
}
// add service records which are used to resolve names. These are the real targets for the DNS querries
n.addSvcRecords("ep1", "name1", "svc1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
w := new(tstwriter)
// the unit tests right now will focus on non-proxyed DNS requests
r := NewResolver(resolverIPSandbox, false, sb)
// test name1's IP is resolved correctly with the default A type query
// Also make sure DNS lookups are case insensitive
names := []string{"name1", "NaMe1"}
for _, name := range names {
q := new(dns.Msg)
q.SetQuestion(name, dns.TypeA)
r.serveDNS(w, q)
resp := w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeSuccess)
checkDNSAnswersCount(t, resp, 1)
checkDNSRRType(t, resp.Answer[0].Header().Rrtype, dns.TypeA)
if answer, ok := resp.Answer[0].(*dns.A); ok {
if !answer.A.Equal(net.ParseIP("192.168.0.1")) {
t.Fatalf("IP response in Answer %v does not match 192.168.0.1", answer.A)
}
} else {
t.Fatal("Answer of type A not found")
}
w.ClearResponse()
}
// test MX query with name1 results in Success response with 0 answer records
q := new(dns.Msg)
q.SetQuestion("name1", dns.TypeMX)
r.serveDNS(w, q)
resp := w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeSuccess)
checkDNSAnswersCount(t, resp, 0)
w.ClearResponse()
// test MX query with non existent name results in ServFail response with 0 answer records
// since this is a unit test env, we disable proxying DNS above which results in ServFail rather than NXDOMAIN
q = new(dns.Msg)
q.SetQuestion("nonexistent", dns.TypeMX)
r.serveDNS(w, q)
resp = w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeServerFailure)
w.ClearResponse()
}
func newDNSHandlerServFailOnce(requests *int) func(w dns.ResponseWriter, r *dns.Msg) {
return func(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
@ -234,79 +136,6 @@ func waitForLocalDNSServer(t *testing.T) {
}
}
func TestDNSProxyServFail(t *testing.T) {
skip.If(t, runtime.GOOS == "windows", "test only works on linux")
osctx := netnsutils.SetupTestOSContextEx(t)
defer osctx.Cleanup(t)
c, err := New()
if err != nil {
t.Fatal(err)
}
defer c.Stop()
n, err := c.NewNetwork("bridge", "dtnet2", "", nil)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := n.Delete(); err != nil {
t.Fatal(err)
}
}()
sb, err := c.NewSandbox("c1")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := sb.Delete(); err != nil {
t.Fatal(err)
}
}()
var nRequests int
// initialize a local DNS server and configure it to fail the first query
dns.HandleFunc(".", newDNSHandlerServFailOnce(&nRequests))
// use TCP for predictable results. Connection tests (to figure out DNS server initialization) don't work with UDP
server := &dns.Server{Addr: "127.0.0.1:53", Net: "tcp"}
srvErrCh := make(chan error, 1)
osctx.Go(t, func() {
srvErrCh <- server.ListenAndServe()
})
defer func() {
server.Shutdown() //nolint:errcheck
if err := <-srvErrCh; err != nil {
t.Error(err)
}
}()
waitForLocalDNSServer(t)
t.Log("DNS Server can be reached")
w := new(tstwriter)
r := NewResolver(resolverIPSandbox, true, sb)
q := new(dns.Msg)
q.SetQuestion("name1.", dns.TypeA)
var localDNSEntries []extDNSEntry
extTestDNSEntry := extDNSEntry{IPStr: "127.0.0.1", HostLoopback: true}
// configure two external DNS entries and point both to local DNS server thread
localDNSEntries = append(localDNSEntries, extTestDNSEntry)
localDNSEntries = append(localDNSEntries, extTestDNSEntry)
// this should generate two requests: the first will fail leading to a retry
r.SetExtServers(localDNSEntries)
r.serveDNS(w, q)
if nRequests != 2 {
t.Fatalf("Expected 2 DNS querries. Found: %d", nRequests)
}
t.Logf("Expected number of DNS requests generated")
}
// Packet 24 extracted from
// https://gist.github.com/vojtad/3bac63b8c91b1ec50e8d8b36047317fa/raw/7d75eb3d3448381bf252ae55ea5123a132c46658/host.pcap
// (https://github.com/moby/moby/issues/44575)

View file

@ -0,0 +1,178 @@
//go:build !windows
package libnetwork
import (
"net"
"testing"
"github.com/docker/docker/internal/testutils/netnsutils"
"github.com/miekg/dns"
)
// test only works on linux
func TestDNSIPQuery(t *testing.T) {
defer netnsutils.SetupTestOSContext(t)()
c, err := New()
if err != nil {
t.Fatal(err)
}
defer c.Stop()
n, err := c.NewNetwork("bridge", "dtnet1", "", nil)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := n.Delete(); err != nil {
t.Fatal(err)
}
}()
ep, err := n.CreateEndpoint("testep")
if err != nil {
t.Fatal(err)
}
sb, err := c.NewSandbox("c1")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := sb.Delete(); err != nil {
t.Fatal(err)
}
}()
// we need the endpoint only to populate ep_list for the sandbox as part of resolve_name
// it is not set as a target for name resolution and does not serve any other purpose
err = ep.Join(sb)
if err != nil {
t.Fatal(err)
}
// add service records which are used to resolve names. These are the real targets for the DNS querries
n.addSvcRecords("ep1", "name1", "svc1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
w := new(tstwriter)
// the unit tests right now will focus on non-proxyed DNS requests
r := NewResolver(resolverIPSandbox, false, sb)
// test name1's IP is resolved correctly with the default A type query
// Also make sure DNS lookups are case insensitive
names := []string{"name1", "NaMe1"}
for _, name := range names {
q := new(dns.Msg)
q.SetQuestion(name, dns.TypeA)
r.serveDNS(w, q)
resp := w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeSuccess)
checkDNSAnswersCount(t, resp, 1)
checkDNSRRType(t, resp.Answer[0].Header().Rrtype, dns.TypeA)
if answer, ok := resp.Answer[0].(*dns.A); ok {
if !answer.A.Equal(net.ParseIP("192.168.0.1")) {
t.Fatalf("IP response in Answer %v does not match 192.168.0.1", answer.A)
}
} else {
t.Fatal("Answer of type A not found")
}
w.ClearResponse()
}
// test MX query with name1 results in Success response with 0 answer records
q := new(dns.Msg)
q.SetQuestion("name1", dns.TypeMX)
r.serveDNS(w, q)
resp := w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeSuccess)
checkDNSAnswersCount(t, resp, 0)
w.ClearResponse()
// test MX query with non existent name results in ServFail response with 0 answer records
// since this is a unit test env, we disable proxying DNS above which results in ServFail rather than NXDOMAIN
q = new(dns.Msg)
q.SetQuestion("nonexistent", dns.TypeMX)
r.serveDNS(w, q)
resp = w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeServerFailure)
w.ClearResponse()
}
// test only works on linux
func TestDNSProxyServFail(t *testing.T) {
osctx := netnsutils.SetupTestOSContextEx(t)
defer osctx.Cleanup(t)
c, err := New()
if err != nil {
t.Fatal(err)
}
defer c.Stop()
n, err := c.NewNetwork("bridge", "dtnet2", "", nil)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := n.Delete(); err != nil {
t.Fatal(err)
}
}()
sb, err := c.NewSandbox("c1")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := sb.Delete(); err != nil {
t.Fatal(err)
}
}()
var nRequests int
// initialize a local DNS server and configure it to fail the first query
dns.HandleFunc(".", newDNSHandlerServFailOnce(&nRequests))
// use TCP for predictable results. Connection tests (to figure out DNS server initialization) don't work with UDP
server := &dns.Server{Addr: "127.0.0.1:53", Net: "tcp"}
srvErrCh := make(chan error, 1)
osctx.Go(t, func() {
srvErrCh <- server.ListenAndServe()
})
defer func() {
server.Shutdown() //nolint:errcheck
if err := <-srvErrCh; err != nil {
t.Error(err)
}
}()
waitForLocalDNSServer(t)
t.Log("DNS Server can be reached")
w := new(tstwriter)
r := NewResolver(resolverIPSandbox, true, sb)
q := new(dns.Msg)
q.SetQuestion("name1.", dns.TypeA)
var localDNSEntries []extDNSEntry
extTestDNSEntry := extDNSEntry{IPStr: "127.0.0.1", HostLoopback: true}
// configure two external DNS entries and point both to local DNS server thread
localDNSEntries = append(localDNSEntries, extTestDNSEntry)
localDNSEntries = append(localDNSEntries, extTestDNSEntry)
// this should generate two requests: the first will fail leading to a retry
r.SetExtServers(localDNSEntries)
r.serveDNS(w, q)
if nRequests != 2 {
t.Fatalf("Expected 2 DNS querries. Found: %d", nRequests)
}
t.Logf("Expected number of DNS requests generated")
}