summaryrefslogtreecommitdiffhomepage
path: root/ipn/ipnlocal/node_backend_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'ipn/ipnlocal/node_backend_test.go')
-rw-r--r--ipn/ipnlocal/node_backend_test.go185
1 files changed, 168 insertions, 17 deletions
diff --git a/ipn/ipnlocal/node_backend_test.go b/ipn/ipnlocal/node_backend_test.go
index f6698bd4b..bd41cd70a 100644
--- a/ipn/ipnlocal/node_backend_test.go
+++ b/ipn/ipnlocal/node_backend_test.go
@@ -6,9 +6,12 @@ package ipnlocal
import (
"context"
"errors"
+ "fmt"
+ "net/netip"
"testing"
"time"
+ "tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
"tailscale.com/tstest"
"tailscale.com/types/netmap"
@@ -17,7 +20,7 @@ import (
)
func TestNodeBackendReadiness(t *testing.T) {
- nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
+ nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New(), nil)
// The node backend is not ready until [nodeBackend.ready] is called,
// and [nodeBackend.Wait] should fail with [context.DeadlineExceeded].
@@ -48,7 +51,7 @@ func TestNodeBackendReadiness(t *testing.T) {
}
func TestNodeBackendShutdown(t *testing.T) {
- nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
+ nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New(), nil)
shutdownCause := errors.New("test shutdown")
@@ -86,7 +89,7 @@ func TestNodeBackendShutdown(t *testing.T) {
}
func TestNodeBackendReadyAfterShutdown(t *testing.T) {
- nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
+ nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New(), nil)
shutdownCause := errors.New("test shutdown")
nb.shutdown(shutdownCause)
@@ -98,7 +101,7 @@ func TestNodeBackendReadyAfterShutdown(t *testing.T) {
func TestNodeBackendParentContextCancellation(t *testing.T) {
ctx, cancelCtx := context.WithCancel(context.Background())
- nb := newNodeBackend(ctx, tstest.WhileTestRunningLogger(t), eventbus.New())
+ nb := newNodeBackend(ctx, tstest.WhileTestRunningLogger(t), eventbus.New(), nil)
cancelCtx()
@@ -115,7 +118,7 @@ func TestNodeBackendParentContextCancellation(t *testing.T) {
}
func TestNodeBackendConcurrentReadyAndShutdown(t *testing.T) {
- nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
+ nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New(), nil)
// Calling [nodeBackend.ready] and [nodeBackend.shutdown] concurrently
// should not cause issues, and [nodeBackend.Wait] should unblock,
@@ -127,6 +130,17 @@ func TestNodeBackendConcurrentReadyAndShutdown(t *testing.T) {
}
func TestNodeBackendReachability(t *testing.T) {
+ addrs := []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")}
+ defaults := func(n tailcfg.Node) tailcfg.Node {
+ if n.ID == 0 {
+ n.ID = 1234
+ }
+ if n.Name == "" {
+ n.Name = "exit-node.example.ts.net"
+ }
+ return n
+ }
+
for _, tc := range []struct {
name string
@@ -139,54 +153,191 @@ func TestNodeBackendReachability(t *testing.T) {
// peer node.
cap bool
+ // Peer defines the peer node.
peer tailcfg.Node
+
+ // Ping sets how the peer node responds to pings:
+ // pingTimedOut: peer is unreachable
+ // pingSuccess: peer responds to pings
+ // pingLocalhost: peer is the same as the self node
+ ping mockPinger
+
want bool
}{
{
+ name: "disabled/nil",
+ cap: false,
+ peer: defaults(tailcfg.Node{
+ Online: nil,
+ }),
+ want: false,
+ },
+ {
name: "disabled/offline",
cap: false,
- peer: tailcfg.Node{
+ peer: defaults(tailcfg.Node{
Online: ptr.To(false),
- },
+ }),
want: false,
},
{
name: "disabled/online",
cap: false,
- peer: tailcfg.Node{
+ peer: defaults(tailcfg.Node{
Online: ptr.To(true),
- },
+ }),
want: true,
},
{
+ name: "enabled/no_ip",
+ cap: true,
+ ping: pingTimedOut,
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(false),
+ Addresses: nil,
+ }),
+ want: false,
+ },
+ {
name: "enabled/offline",
cap: true,
- peer: tailcfg.Node{
- Online: ptr.To(false),
- },
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(false),
+ Addresses: addrs,
+ }),
+ ping: pingTimedOut,
+ want: false,
+ },
+ {
+ name: "enabled/offline_but_pingable",
+ cap: true,
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(false),
+ Addresses: addrs,
+ }),
+ ping: pingSuccess,
want: true,
},
{
name: "enabled/online",
cap: true,
- peer: tailcfg.Node{
- Online: ptr.To(true),
- },
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(true),
+ Addresses: addrs,
+ }),
+ ping: pingSuccess,
want: true,
},
+ {
+ name: "enabled/online_but_unpingable",
+ cap: true,
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(true),
+ Addresses: addrs,
+ }),
+ ping: pingTimedOut,
+ want: false,
+ },
+ {
+ name: "enabled/offline_localhost",
+ cap: true,
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(false),
+ Addresses: addrs,
+ }),
+ ping: pingLocalhost,
+ want: true,
+ },
+ {
+ name: "enabled/online_localhost",
+ cap: true,
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(true),
+ Addresses: addrs,
+ }),
+ ping: pingLocalhost,
+ want: true,
+ },
+ {
+ name: "enabled/offline_but_cancelled",
+ cap: true,
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(false),
+ Addresses: addrs,
+ }),
+ ping: pingCancelled,
+ want: false,
+ },
+ {
+ name: "enabled/online_but_cancelled",
+ cap: true,
+ peer: defaults(tailcfg.Node{
+ Online: ptr.To(true),
+ Addresses: addrs,
+ }),
+ ping: pingCancelled,
+ want: false,
+ },
} {
+
t.Run(tc.name, func(t *testing.T) {
- nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
+ ctx := t.Context()
+
+ nb := newNodeBackend(ctx, tstest.WhileTestRunningLogger(t), eventbus.New(), mockPinger(tc.ping))
nb.netMap = &netmap.NetworkMap{}
if tc.cap {
nb.netMap.AllCaps.Make()
nb.netMap.AllCaps.Add(tailcfg.NodeAttrClientSideReachability)
}
- got := nb.PeerIsReachable(t.Context(), tc.peer.View())
+ if tc.ping == pingCancelled {
+ c, cancel := context.WithCancelCause(ctx)
+ ctx = c
+ cancel(fmt.Errorf("subtest: %q", tc.name))
+ }
+
+ got := nb.PeerIsReachable(ctx, tc.peer.View())
if got != tc.want {
t.Errorf("got %v, want %v", got, tc.want)
}
})
}
}
+
+type mockPinger int
+
+const (
+ pingTimedOut mockPinger = iota
+ pingSuccess
+ pingLocalhost
+ pingCancelled
+)
+
+func (p mockPinger) Ping(ctx context.Context, ip netip.Addr, pingType tailcfg.PingType, size int) (*ipnstate.PingResult, error) {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ default:
+ }
+
+ res := &ipnstate.PingResult{
+ IP: ip.String(),
+ NodeIP: ip.String(),
+ }
+ switch p {
+ case pingTimedOut:
+ ctx, cancel := context.WithTimeout(ctx, 0)
+ defer cancel()
+ <-ctx.Done()
+ res.Err = ctx.Err().Error()
+ return res, nil
+ case pingLocalhost:
+ res.Err = fmt.Sprintf("%v is local Tailscale IP", ip)
+ res.IsLocalIP = true
+ case pingSuccess:
+ res.LatencySeconds = 1
+ default:
+ panic(fmt.Sprintf("unknown mockPinger %v", p))
+ }
+ return res, nil
+}