summaryrefslogtreecommitdiffhomepage
path: root/ipn/ipnlocal/peerapi_test.go
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@tailscale.com>2026-02-11 04:45:45 +0000
committerBrad Fitzpatrick <bradfitz@tailscale.com>2026-02-11 09:08:30 -0800
commit8527cb1ffd79026a0db82cb04adc8290d5033344 (patch)
treebedb90c2ec3852f3325c33f168785d13601447f1 /ipn/ipnlocal/peerapi_test.go
parent6cbfc2f3babe5e6e55ddc589dee413801f663797 (diff)
downloadtailscale-bradfitz/feature_appconnectors.tar.xz
tailscale-bradfitz/feature_appconnectors.zip
ipn/ipnlocal, feature/appconnectors: move app connector code out of LocalBackedbradfitz/feature_appconnectors
This is Claude Code's attempt at moving App Connector code out of LocalBackend, with plenty of tips and guidance. This is probably too big of a single commit (and untested, and not sufficiently reviewed) but shared for discussion purposes, so we can start thinking about what hooks we might actually want, and how we can break something like this up into smaller chunks that are reviewable. Updates #12614 Change-Id: I4c79abbef687bfb7bc81f94c393c08b7636fd3c6 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Diffstat (limited to 'ipn/ipnlocal/peerapi_test.go')
-rw-r--r--ipn/ipnlocal/peerapi_test.go251
1 files changed, 0 insertions, 251 deletions
diff --git a/ipn/ipnlocal/peerapi_test.go b/ipn/ipnlocal/peerapi_test.go
index 63abf089c..5a4523216 100644
--- a/ipn/ipnlocal/peerapi_test.go
+++ b/ipn/ipnlocal/peerapi_test.go
@@ -4,26 +4,19 @@
package ipnlocal
import (
- "context"
- "encoding/json"
"net/http"
"net/http/httptest"
"net/netip"
- "slices"
"strings"
"testing"
"go4.org/netipx"
- "golang.org/x/net/dns/dnsmessage"
- "tailscale.com/appc"
- "tailscale.com/appc/appctest"
"tailscale.com/health"
"tailscale.com/ipn"
"tailscale.com/ipn/store/mem"
"tailscale.com/tailcfg"
"tailscale.com/tsd"
"tailscale.com/tstest"
- "tailscale.com/types/appctype"
"tailscale.com/types/logger"
"tailscale.com/types/netmap"
"tailscale.com/util/eventbus/eventbustest"
@@ -245,247 +238,3 @@ func TestPeerAPIReplyToDNSQueries(t *testing.T) {
}
}
-func TestPeerAPIPrettyReplyCNAME(t *testing.T) {
- for _, shouldStore := range []bool{false, true} {
- var h peerAPIHandler
- h.remoteAddr = netip.MustParseAddrPort("100.150.151.152:12345")
-
- sys := tsd.NewSystemWithBus(eventbustest.NewBus(t))
-
- ht := health.NewTracker(sys.Bus.Get())
- reg := new(usermetric.Registry)
- eng, _ := wgengine.NewFakeUserspaceEngine(logger.Discard, 0, ht, reg, sys.Bus.Get(), sys.Set)
- pm := must.Get(newProfileManager(new(mem.Store), t.Logf, ht))
- a := appc.NewAppConnector(appc.Config{
- Logf: t.Logf,
- EventBus: sys.Bus.Get(),
- HasStoredRoutes: shouldStore,
- })
- t.Cleanup(a.Close)
- sys.Set(pm.Store())
- sys.Set(eng)
-
- b := newTestLocalBackendWithSys(t, sys)
- b.pm = pm
- b.appConnector = a // configure as an app connector just to enable the API.
-
- h.ps = &peerAPIServer{b: b}
- h.ps.resolver = &fakeResolver{build: func(b *dnsmessage.Builder) {
- b.CNAMEResource(
- dnsmessage.ResourceHeader{
- Name: dnsmessage.MustNewName("www.example.com."),
- Type: dnsmessage.TypeCNAME,
- Class: dnsmessage.ClassINET,
- TTL: 0,
- },
- dnsmessage.CNAMEResource{
- CNAME: dnsmessage.MustNewName("example.com."),
- },
- )
- b.AResource(
- dnsmessage.ResourceHeader{
- Name: dnsmessage.MustNewName("example.com."),
- Type: dnsmessage.TypeA,
- Class: dnsmessage.ClassINET,
- TTL: 0,
- },
- dnsmessage.AResource{
- A: [4]byte{192, 0, 0, 8},
- },
- )
- }}
- f := filter.NewAllowAllForTest(logger.Discard)
- h.ps.b.setFilter(f)
-
- if !h.replyToDNSQueries() {
- t.Errorf("unexpectedly deny; wanted to be a DNS server")
- }
-
- w := httptest.NewRecorder()
- h.handleDNSQuery(w, httptest.NewRequest("GET", "/dns-query?q=www.example.com.", nil))
- if w.Code != http.StatusOK {
- t.Errorf("unexpected status code: %v", w.Code)
- }
- var addrs []string
- json.NewDecoder(w.Body).Decode(&addrs)
- if len(addrs) == 0 {
- t.Fatalf("no addresses returned")
- }
- for _, addr := range addrs {
- netip.MustParseAddr(addr)
- }
- }
-}
-
-func TestPeerAPIReplyToDNSQueriesAreObserved(t *testing.T) {
- for _, shouldStore := range []bool{false, true} {
- var h peerAPIHandler
- h.remoteAddr = netip.MustParseAddrPort("100.150.151.152:12345")
-
- sys := tsd.NewSystemWithBus(eventbustest.NewBus(t))
- bw := eventbustest.NewWatcher(t, sys.Bus.Get())
-
- rc := &appctest.RouteCollector{}
- ht := health.NewTracker(sys.Bus.Get())
- pm := must.Get(newProfileManager(new(mem.Store), t.Logf, ht))
-
- reg := new(usermetric.Registry)
- eng, _ := wgengine.NewFakeUserspaceEngine(logger.Discard, 0, ht, reg, sys.Bus.Get(), sys.Set)
- a := appc.NewAppConnector(appc.Config{
- Logf: t.Logf,
- EventBus: sys.Bus.Get(),
- RouteAdvertiser: rc,
- HasStoredRoutes: shouldStore,
- })
- t.Cleanup(a.Close)
- sys.Set(pm.Store())
- sys.Set(eng)
-
- b := newTestLocalBackendWithSys(t, sys)
- b.pm = pm
- b.appConnector = a
-
- h.ps = &peerAPIServer{b: b}
- h.ps.b.appConnector.UpdateDomains([]string{"example.com"})
- a.Wait(t.Context())
-
- h.ps.resolver = &fakeResolver{build: func(b *dnsmessage.Builder) {
- b.AResource(
- dnsmessage.ResourceHeader{
- Name: dnsmessage.MustNewName("example.com."),
- Type: dnsmessage.TypeA,
- Class: dnsmessage.ClassINET,
- TTL: 0,
- },
- dnsmessage.AResource{
- A: [4]byte{192, 0, 0, 8},
- },
- )
- }}
- f := filter.NewAllowAllForTest(logger.Discard)
- h.ps.b.setFilter(f)
-
- if !h.ps.b.OfferingAppConnector() {
- t.Fatal("expecting to be offering app connector")
- }
- if !h.replyToDNSQueries() {
- t.Errorf("unexpectedly deny; wanted to be a DNS server")
- }
-
- w := httptest.NewRecorder()
- h.handleDNSQuery(w, httptest.NewRequest("GET", "/dns-query?q=example.com.", nil))
- if w.Code != http.StatusOK {
- t.Errorf("unexpected status code: %v", w.Code)
- }
- a.Wait(t.Context())
-
- wantRoutes := []netip.Prefix{netip.MustParsePrefix("192.0.0.8/32")}
- if !slices.Equal(rc.Routes(), wantRoutes) {
- t.Errorf("got %v; want %v", rc.Routes(), wantRoutes)
- }
-
- if err := eventbustest.Expect(bw,
- eqUpdate(appctype.RouteUpdate{Advertise: mustPrefix("192.0.0.8/32")}),
- ); err != nil {
- t.Error(err)
- }
- }
-}
-
-func TestPeerAPIReplyToDNSQueriesAreObservedWithCNAMEFlattening(t *testing.T) {
- for _, shouldStore := range []bool{false, true} {
- ctx := context.Background()
- var h peerAPIHandler
- h.remoteAddr = netip.MustParseAddrPort("100.150.151.152:12345")
-
- sys := tsd.NewSystemWithBus(eventbustest.NewBus(t))
- bw := eventbustest.NewWatcher(t, sys.Bus.Get())
-
- ht := health.NewTracker(sys.Bus.Get())
- reg := new(usermetric.Registry)
- rc := &appctest.RouteCollector{}
- eng, _ := wgengine.NewFakeUserspaceEngine(logger.Discard, 0, ht, reg, sys.Bus.Get(), sys.Set)
- pm := must.Get(newProfileManager(new(mem.Store), t.Logf, ht))
- a := appc.NewAppConnector(appc.Config{
- Logf: t.Logf,
- EventBus: sys.Bus.Get(),
- RouteAdvertiser: rc,
- HasStoredRoutes: shouldStore,
- })
- t.Cleanup(a.Close)
- sys.Set(pm.Store())
- sys.Set(eng)
-
- b := newTestLocalBackendWithSys(t, sys)
- b.pm = pm
- b.appConnector = a
-
- h.ps = &peerAPIServer{b: b}
- h.ps.b.appConnector.UpdateDomains([]string{"www.example.com"})
- a.Wait(ctx)
-
- h.ps.resolver = &fakeResolver{build: func(b *dnsmessage.Builder) {
- b.CNAMEResource(
- dnsmessage.ResourceHeader{
- Name: dnsmessage.MustNewName("www.example.com."),
- Type: dnsmessage.TypeCNAME,
- Class: dnsmessage.ClassINET,
- TTL: 0,
- },
- dnsmessage.CNAMEResource{
- CNAME: dnsmessage.MustNewName("example.com."),
- },
- )
- b.AResource(
- dnsmessage.ResourceHeader{
- Name: dnsmessage.MustNewName("example.com."),
- Type: dnsmessage.TypeA,
- Class: dnsmessage.ClassINET,
- TTL: 0,
- },
- dnsmessage.AResource{
- A: [4]byte{192, 0, 0, 8},
- },
- )
- }}
- f := filter.NewAllowAllForTest(logger.Discard)
- h.ps.b.setFilter(f)
-
- if !h.ps.b.OfferingAppConnector() {
- t.Fatal("expecting to be offering app connector")
- }
- if !h.replyToDNSQueries() {
- t.Errorf("unexpectedly deny; wanted to be a DNS server")
- }
-
- w := httptest.NewRecorder()
- h.handleDNSQuery(w, httptest.NewRequest("GET", "/dns-query?q=www.example.com.", nil))
- if w.Code != http.StatusOK {
- t.Errorf("unexpected status code: %v", w.Code)
- }
- a.Wait(ctx)
-
- wantRoutes := []netip.Prefix{netip.MustParsePrefix("192.0.0.8/32")}
- if !slices.Equal(rc.Routes(), wantRoutes) {
- t.Errorf("got %v; want %v", rc.Routes(), wantRoutes)
- }
-
- if err := eventbustest.Expect(bw,
- eqUpdate(appctype.RouteUpdate{Advertise: mustPrefix("192.0.0.8/32")}),
- ); err != nil {
- t.Error(err)
- }
- }
-}
-
-type fakeResolver struct {
- build func(*dnsmessage.Builder)
-}
-
-func (f *fakeResolver) HandlePeerDNSQuery(ctx context.Context, q []byte, from netip.AddrPort, allowName func(name string) bool) (res []byte, err error) {
- b := dnsmessage.NewBuilder(nil, dnsmessage.Header{})
- b.EnableCompression()
- b.StartAnswers()
- f.build(&b)
- return b.Finish()
-}