mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-08 09:07:44 +00:00
ipn: move e2e_test back to corp repo.
It depends on corp things, so can't run here anyway. Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
557b310e67
commit
2b74236567
309
ipn/e2e_test.go
309
ipn/e2e_test.go
@ -1,309 +0,0 @@
|
|||||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build depends_on_currently_unreleased
|
|
||||||
|
|
||||||
package ipn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/http/cookiejar"
|
|
||||||
"net/http/httptest"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/tailscale/wireguard-go/tun/tuntest"
|
|
||||||
"github.com/tailscale/wireguard-go/wgcfg"
|
|
||||||
"tailscale.com/control/controlclient"
|
|
||||||
"tailscale.com/tailcfg"
|
|
||||||
"tailscale.com/tstest"
|
|
||||||
"tailscale.com/types/logger"
|
|
||||||
"tailscale.com/wgengine"
|
|
||||||
"tailscale.com/wgengine/magicsock"
|
|
||||||
"tailscale.com/wgengine/router"
|
|
||||||
"tailscale.com/wgengine/tstun"
|
|
||||||
"tailscale.io/control" // not yet released
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Hacky way to signal to magicsock for now not to bind on the
|
|
||||||
// unspecified address. TODO(bradfitz): clean up wgengine's
|
|
||||||
// constructors.
|
|
||||||
os.Setenv("IN_TS_TEST", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPN(t *testing.T) {
|
|
||||||
tstest.PanicOnLog()
|
|
||||||
rc := tstest.NewResourceCheck()
|
|
||||||
defer rc.Assert(t)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
// This gets reassigned inside every test, so that the connections
|
|
||||||
// all log using the "current" t.Logf function. Sigh.
|
|
||||||
current_t := t
|
|
||||||
logf := func(s string, args ...interface{}) {
|
|
||||||
current_t.Helper()
|
|
||||||
current_t.Logf(s, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn off STUN for the test to make it hermetic.
|
|
||||||
// TODO(crawshaw): add a test that runs against a local STUN server.
|
|
||||||
magicsock.DisableSTUNForTesting = true
|
|
||||||
defer func() { magicsock.DisableSTUNForTesting = false }()
|
|
||||||
|
|
||||||
// TODO(apenwarr): Make resource checks actually pass.
|
|
||||||
// They don't right now, because (at least) wgengine doesn't fully
|
|
||||||
// shut down.
|
|
||||||
// rc := tstest.NewResourceCheck()
|
|
||||||
// defer rc.Assert(t)
|
|
||||||
|
|
||||||
var ctl *control.Server
|
|
||||||
|
|
||||||
ctlHandler := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctl.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
https := httptest.NewServer(http.HandlerFunc(ctlHandler))
|
|
||||||
https.Config.ErrorLog = logger.StdLogger(logf)
|
|
||||||
serverURL := https.URL
|
|
||||||
|
|
||||||
tmpdir, err := ioutil.TempDir("", "ipntest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("create tempdir: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctl, err = control.New(tmpdir, tmpdir, tmpdir, serverURL, true, logf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("create control server: %v\n", ctl)
|
|
||||||
}
|
|
||||||
defer ctl.Shutdown()
|
|
||||||
defer https.Close()
|
|
||||||
defer https.CloseClientConnections()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if _, err := ctl.DB().FindOrCreateUser("google", "test1@example.com", "", ""); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n1 := newNode(t, ctx, logf, "n1", https, false)
|
|
||||||
defer n1.Backend.Shutdown()
|
|
||||||
n1.Backend.StartLoginInteractive()
|
|
||||||
|
|
||||||
n2 := newNode(t, ctx, logf, "n2", https, true)
|
|
||||||
defer n2.Backend.Shutdown()
|
|
||||||
n2.Backend.StartLoginInteractive()
|
|
||||||
|
|
||||||
t.Run("login", func(t *testing.T) {
|
|
||||||
current_t = t
|
|
||||||
|
|
||||||
var s1, s2 State
|
|
||||||
for {
|
|
||||||
logf("\n\nn1.state=%v n2.state=%v\n\n", s1, s2)
|
|
||||||
|
|
||||||
// TODO(crawshaw): switch from || to &&. To do this we need to
|
|
||||||
// transmit some data so that the handshake completes on both
|
|
||||||
// sides. (Because handshakes are 1RTT, it is the data
|
|
||||||
// transmission that completes the handshake.)
|
|
||||||
if s1 == Running || s2 == Running {
|
|
||||||
// TODO(apenwarr): ensure state sequence.
|
|
||||||
// Right now we'll just exit as soon as
|
|
||||||
// state==Running, even if the backend is lying or
|
|
||||||
// something. Not a great test.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case n := <-n1.NotifyCh:
|
|
||||||
logf("n1n: %v\n", n)
|
|
||||||
if n.State != nil {
|
|
||||||
s1 = *n.State
|
|
||||||
if s1 == NeedsMachineAuth {
|
|
||||||
authNode(t, ctl, n1.Backend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case n := <-n2.NotifyCh:
|
|
||||||
logf("n2n: %v\n", n)
|
|
||||||
if n.State != nil {
|
|
||||||
s2 = *n.State
|
|
||||||
if s2 == NeedsMachineAuth {
|
|
||||||
authNode(t, ctl, n2.Backend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
t.Fatalf("\n\n\nFATAL: timed out waiting for notifications.\n\n\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
current_t = t
|
|
||||||
|
|
||||||
n1addr := n1.Backend.NetMap().Addresses[0].IP
|
|
||||||
n2addr := n2.Backend.NetMap().Addresses[0].IP
|
|
||||||
|
|
||||||
t.Run("ping n2", func(t *testing.T) {
|
|
||||||
current_t = t
|
|
||||||
t.Skip("TODO(crawshaw): skipping ping test, it is flaky")
|
|
||||||
msg := tuntest.Ping(n2addr.IP(), n1addr.IP())
|
|
||||||
n1.ChannelTUN.Outbound <- msg
|
|
||||||
select {
|
|
||||||
case msgRecv := <-n2.ChannelTUN.Inbound:
|
|
||||||
if !bytes.Equal(msg, msgRecv) {
|
|
||||||
t.Error("bad ping")
|
|
||||||
}
|
|
||||||
case <-time.After(1 * time.Second):
|
|
||||||
t.Error("no ping seen")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
current_t = t
|
|
||||||
|
|
||||||
t.Run("ping n1", func(t *testing.T) {
|
|
||||||
current_t = t
|
|
||||||
t.Skip("TODO(crawshaw): skipping ping test, it is flaky")
|
|
||||||
msg := tuntest.Ping(n1addr.IP(), n2addr.IP())
|
|
||||||
n2.ChannelTUN.Outbound <- msg
|
|
||||||
select {
|
|
||||||
case msgRecv := <-n1.ChannelTUN.Inbound:
|
|
||||||
if !bytes.Equal(msg, msgRecv) {
|
|
||||||
t.Error("bad ping")
|
|
||||||
}
|
|
||||||
case <-time.After(1 * time.Second):
|
|
||||||
t.Error("no ping seen")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
current_t = t
|
|
||||||
|
|
||||||
drain:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-n1.NotifyCh:
|
|
||||||
case <-n2.NotifyCh:
|
|
||||||
default:
|
|
||||||
break drain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n1.Backend.Logout()
|
|
||||||
|
|
||||||
t.Run("logout", func(t *testing.T) {
|
|
||||||
current_t = t
|
|
||||||
|
|
||||||
var s State
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case n := <-n1.NotifyCh:
|
|
||||||
if n.State == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s = *n.State
|
|
||||||
logf("n.State=%v", s)
|
|
||||||
if s == NeedsLogin {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-time.After(3 * time.Second):
|
|
||||||
t.Fatalf("timeout waiting for logout State=NeedsLogin, got State=%v", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
current_t = t
|
|
||||||
}
|
|
||||||
|
|
||||||
type testNode struct {
|
|
||||||
Backend *LocalBackend
|
|
||||||
ChannelTUN *tuntest.ChannelTUN
|
|
||||||
NotifyCh <-chan Notify
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new IPN node.
|
|
||||||
func newNode(t *testing.T, ctx context.Context, logfx logger.Logf, prefix string, https *httptest.Server, weirdPrefs bool) testNode {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
logfe := logger.WithPrefix(logfx, prefix+"e: ")
|
|
||||||
logf := logger.WithPrefix(logfx, prefix+": ")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
httpc := https.Client()
|
|
||||||
httpc.Jar, err = cookiejar.New(nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tun := tuntest.NewChannelTUN()
|
|
||||||
tundev := tstun.WrapTUN(logfe, tun.TUN())
|
|
||||||
e1, err := wgengine.NewUserspaceEngineAdvanced(logfe, tundev, router.NewFake, 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("NewFakeEngine: %v\n", err)
|
|
||||||
}
|
|
||||||
n, err := NewLocalBackend(logf, prefix, &MemoryStore{}, e1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("NewLocalBackend: %v\n", err)
|
|
||||||
}
|
|
||||||
nch := make(chan Notify, 1000)
|
|
||||||
c := controlclient.Persist{
|
|
||||||
Provider: "google",
|
|
||||||
LoginName: "test1@example.com",
|
|
||||||
}
|
|
||||||
prefs := NewPrefs()
|
|
||||||
prefs.ControlURL = https.URL
|
|
||||||
prefs.Persist = &c
|
|
||||||
|
|
||||||
if weirdPrefs {
|
|
||||||
// Let's test some nonempty extra prefs fields to make sure
|
|
||||||
// the server can handle them.
|
|
||||||
prefs.AdvertiseTags = []string{"tag:abc"}
|
|
||||||
cidr, err := wgcfg.ParseCIDR("1.2.3.4/24")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ParseCIDR: %v", err)
|
|
||||||
}
|
|
||||||
prefs.AdvertiseRoutes = []wgcfg.CIDR{cidr}
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Start(Options{
|
|
||||||
HTTPTestClient: httpc,
|
|
||||||
FrontendLogID: prefix + "-f",
|
|
||||||
Prefs: prefs,
|
|
||||||
Notify: func(n Notify) {
|
|
||||||
// Automatically visit auth URLs
|
|
||||||
if n.BrowseToURL != nil {
|
|
||||||
logf("BrowseToURL: %v", *n.BrowseToURL)
|
|
||||||
|
|
||||||
authURL := *n.BrowseToURL
|
|
||||||
i := strings.Index(authURL, "/a/")
|
|
||||||
if i == -1 {
|
|
||||||
panic("bad authURL: " + authURL)
|
|
||||||
}
|
|
||||||
authURL = authURL[:i] + "/login?refresh=true&next_url=" + url.PathEscape(authURL[i:])
|
|
||||||
|
|
||||||
form := url.Values{"user": []string{c.LoginName}}
|
|
||||||
req, err := http.NewRequest("POST", authURL, strings.NewReader(form.Encode()))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
|
|
||||||
if _, err := httpc.Do(req.WithContext(ctx)); err != nil {
|
|
||||||
logf("BrowseToURL: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nch <- n
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return testNode{
|
|
||||||
Backend: n,
|
|
||||||
ChannelTUN: tun,
|
|
||||||
NotifyCh: nch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the control server to authorize the given node.
|
|
||||||
func authNode(t *testing.T, ctl *control.Server, n *LocalBackend) {
|
|
||||||
mk := n.prefs.Persist.PrivateMachineKey.Public()
|
|
||||||
nk := n.prefs.Persist.PrivateNodeKey.Public()
|
|
||||||
ctl.AuthorizeMachine(tailcfg.MachineKey(mk), tailcfg.NodeKey(nk))
|
|
||||||
}
|
|
17
ipn/local.go
17
ipn/local.go
@ -1026,3 +1026,20 @@ func (b *LocalBackend) setNetInfo(ni *tailcfg.NetInfo) {
|
|||||||
}
|
}
|
||||||
c.SetNetInfo(ni)
|
c.SetNetInfo(ni)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestOnlyPublicKeys returns the current machine and node public
|
||||||
|
// keys. Used in tests only to facilitate automated node authorization
|
||||||
|
// in the test harness.
|
||||||
|
func (b *LocalBackend) TestOnlyPublicKeys() (machineKey tailcfg.MachineKey, nodeKey tailcfg.NodeKey) {
|
||||||
|
b.mu.Lock()
|
||||||
|
prefs := b.prefs
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if prefs == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mk := prefs.Persist.PrivateMachineKey.Public()
|
||||||
|
nk := prefs.Persist.PrivateNodeKey.Public()
|
||||||
|
return tailcfg.MachineKey(mk), tailcfg.NodeKey(nk)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user