tstest/integration/vms: add in-process DERP server (#2108)

Previously this test would reach out to the public DERP servers in order
to help machines connect with eachother. This is not ideal given our
plans to run these tests completely disconnected from the internet. This
patch introduces an in-process DERP server running on its own randomly
assigned HTTP port.

Updates #1988

Signed-off-by: Christine Dodrill <xe@tailscale.com>
This commit is contained in:
Christine Dodrill 2021-06-25 12:59:45 -07:00 committed by GitHub
parent 6b234323a0
commit 194d5b8412
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 59 deletions

View File

@ -9,6 +9,11 @@
package integration package integration
import ( import (
"crypto/rand"
"crypto/tls"
"net"
"net/http"
"net/http/httptest"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@ -19,6 +24,13 @@
"testing" "testing"
"time" "time"
"tailscale.com/derp"
"tailscale.com/derp/derphttp"
"tailscale.com/net/stun/stuntest"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/types/nettype"
"tailscale.com/version" "tailscale.com/version"
) )
@ -98,3 +110,53 @@ func exe() string {
} }
return "" return ""
} }
// RunDERPAndSTUN runs a local DERP and STUN server for tests, returning the derpMap
// that clients should use. This creates resources that must be cleaned up with the
// returned cleanup function.
func RunDERPAndSTUN(t testing.TB, logf logger.Logf, ipAddress string) (derpMap *tailcfg.DERPMap) {
t.Helper()
var serverPrivateKey key.Private
if _, err := rand.Read(serverPrivateKey[:]); err != nil {
t.Fatal(err)
}
d := derp.NewServer(serverPrivateKey, logf)
httpsrv := httptest.NewUnstartedServer(derphttp.Handler(d))
httpsrv.Config.ErrorLog = logger.StdLogger(logf)
httpsrv.Config.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
httpsrv.StartTLS()
stunAddr, stunCleanup := stuntest.ServeWithPacketListener(t, nettype.Std{})
m := &tailcfg.DERPMap{
Regions: map[int]*tailcfg.DERPRegion{
1: {
RegionID: 1,
RegionCode: "test",
Nodes: []*tailcfg.DERPNode{
{
Name: "t1",
RegionID: 1,
HostName: ipAddress,
IPv4: ipAddress,
IPv6: "none",
STUNPort: stunAddr.Port,
DERPTestPort: httpsrv.Listener.Addr().(*net.TCPAddr).Port,
STUNTestIP: stunAddr.IP.String(),
},
},
},
},
}
t.Cleanup(func() {
httpsrv.CloseClientConnections()
httpsrv.Close()
d.Close()
stunCleanup()
})
return m
}

View File

@ -7,8 +7,6 @@
import ( import (
"bytes" "bytes"
"context" "context"
crand "crypto/rand"
"crypto/tls"
"encoding/json" "encoding/json"
"errors" "errors"
"flag" "flag"
@ -16,7 +14,6 @@
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
@ -31,18 +28,13 @@
"time" "time"
"go4.org/mem" "go4.org/mem"
"tailscale.com/derp"
"tailscale.com/derp/derphttp"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/stun/stuntest"
"tailscale.com/safesocket" "tailscale.com/safesocket"
"tailscale.com/smallzstd" "tailscale.com/smallzstd"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tstest" "tailscale.com/tstest"
"tailscale.com/tstest/integration/testcontrol" "tailscale.com/tstest/integration/testcontrol"
"tailscale.com/types/key"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/nettype"
) )
var ( var (
@ -301,8 +293,6 @@ type testEnv struct {
TrafficTrap *trafficTrap TrafficTrap *trafficTrap
TrafficTrapServer *httptest.Server TrafficTrapServer *httptest.Server
derpShutdown func()
} }
type testEnvOpt interface { type testEnvOpt interface {
@ -323,7 +313,7 @@ func newTestEnv(t testing.TB, bins *Binaries, opts ...testEnvOpt) *testEnv {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
t.Skip("not tested/working on Windows yet") t.Skip("not tested/working on Windows yet")
} }
derpMap, derpShutdown := runDERPAndStun(t, logger.Discard) derpMap := RunDERPAndSTUN(t, logger.Discard, "127.0.0.1")
logc := new(logCatcher) logc := new(logCatcher)
control := &testcontrol.Server{ control := &testcontrol.Server{
DERPMap: derpMap, DERPMap: derpMap,
@ -339,7 +329,6 @@ func newTestEnv(t testing.TB, bins *Binaries, opts ...testEnvOpt) *testEnv {
ControlServer: control.HTTPTestServer, ControlServer: control.HTTPTestServer,
TrafficTrap: trafficTrap, TrafficTrap: trafficTrap,
TrafficTrapServer: httptest.NewServer(trafficTrap), TrafficTrapServer: httptest.NewServer(trafficTrap),
derpShutdown: derpShutdown,
} }
for _, o := range opts { for _, o := range opts {
o.modifyTestEnv(e) o.modifyTestEnv(e)
@ -357,7 +346,6 @@ func (e *testEnv) Close() error {
e.LogCatcherServer.Close() e.LogCatcherServer.Close()
e.TrafficTrapServer.Close() e.TrafficTrapServer.Close()
e.ControlServer.Close() e.ControlServer.Close()
e.derpShutdown()
return nil return nil
} }
@ -620,51 +608,6 @@ func (tt *trafficTrap) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(403) w.WriteHeader(403)
} }
func runDERPAndStun(t testing.TB, logf logger.Logf) (derpMap *tailcfg.DERPMap, cleanup func()) {
var serverPrivateKey key.Private
if _, err := crand.Read(serverPrivateKey[:]); err != nil {
t.Fatal(err)
}
d := derp.NewServer(serverPrivateKey, logf)
httpsrv := httptest.NewUnstartedServer(derphttp.Handler(d))
httpsrv.Config.ErrorLog = logger.StdLogger(logf)
httpsrv.Config.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
httpsrv.StartTLS()
stunAddr, stunCleanup := stuntest.ServeWithPacketListener(t, nettype.Std{})
m := &tailcfg.DERPMap{
Regions: map[int]*tailcfg.DERPRegion{
1: {
RegionID: 1,
RegionCode: "test",
Nodes: []*tailcfg.DERPNode{
{
Name: "t1",
RegionID: 1,
HostName: "127.0.0.1", // to bypass HTTP proxy
IPv4: "127.0.0.1",
IPv6: "none",
STUNPort: stunAddr.Port,
DERPTestPort: httpsrv.Listener.Addr().(*net.TCPAddr).Port,
STUNTestIP: stunAddr.IP.String(),
},
},
},
},
}
cleanup = func() {
httpsrv.CloseClientConnections()
httpsrv.Close()
d.Close()
stunCleanup()
}
return m, cleanup
}
type authURLParserWriter struct { type authURLParserWriter struct {
buf bytes.Buffer buf bytes.Buffer
fn func(urlStr string) error fn func(urlStr string) error

View File

@ -546,7 +546,8 @@ func TestVMIntegrationEndToEnd(t *testing.T) {
rex := distroRex.Unwrap() rex := distroRex.Unwrap()
ln, err := net.Listen("tcp", deriveBindhost(t)+":0") bindHost := deriveBindhost(t)
ln, err := net.Listen("tcp", net.JoinHostPort(bindHost, "0"))
if err != nil { if err != nil {
t.Fatalf("can't make TCP listener: %v", err) t.Fatalf("can't make TCP listener: %v", err)
} }
@ -555,6 +556,9 @@ func TestVMIntegrationEndToEnd(t *testing.T) {
cs := &testcontrol.Server{} cs := &testcontrol.Server{}
derpMap := integration.RunDERPAndSTUN(t, t.Logf, bindHost)
cs.DERPMap = derpMap
var ( var (
ipMu sync.Mutex ipMu sync.Mutex
ipMap = map[string]ipMapping{} ipMap = map[string]ipMapping{}