diff options
| author | Brad Fitzpatrick <bradfitz@tailscale.com> | 2026-02-11 04:45:45 +0000 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@tailscale.com> | 2026-02-11 09:08:30 -0800 |
| commit | 8527cb1ffd79026a0db82cb04adc8290d5033344 (patch) | |
| tree | bedb90c2ec3852f3325c33f168785d13601447f1 /ipn/ipnlocal/peerapi_test.go | |
| parent | 6cbfc2f3babe5e6e55ddc589dee413801f663797 (diff) | |
| download | tailscale-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.go | 251 |
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() -} |
