tka: keep the CompactionDefaults alongside the other limits #6
+20
-10
@@ -16,6 +16,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/tstest/integration/testcontrol"
|
"tailscale.com/tstest/integration/testcontrol"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@@ -31,6 +32,8 @@ func TestMapAgainstTestControl(t *testing.T) {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
ht := new(health.Tracker)
|
||||||
|
|
||||||
serverKey, err := DiscoverServerKey(ctx, baseURL)
|
serverKey, err := DiscoverServerKey(ctx, baseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("DiscoverServerKey: %v", err)
|
t.Fatalf("DiscoverServerKey: %v", err)
|
||||||
@@ -41,8 +44,9 @@ func TestMapAgainstTestControl(t *testing.T) {
|
|||||||
nodeKey = key.NewNode()
|
nodeKey = key.NewNode()
|
||||||
machineKey = key.NewMachine()
|
machineKey = key.NewMachine()
|
||||||
c, err := NewClient(ClientOpts{
|
c, err := NewClient(ClientOpts{
|
||||||
ServerURL: baseURL,
|
ServerURL: baseURL,
|
||||||
MachineKey: machineKey,
|
MachineKey: machineKey,
|
||||||
|
HealthTracker: ht,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewClient %s: %v", hostname, err)
|
t.Fatalf("NewClient %s: %v", hostname, err)
|
||||||
@@ -62,8 +66,9 @@ func TestMapAgainstTestControl(t *testing.T) {
|
|||||||
nodeKeyB, _ := register("b")
|
nodeKeyB, _ := register("b")
|
||||||
|
|
||||||
clientA, err := NewClient(ClientOpts{
|
clientA, err := NewClient(ClientOpts{
|
||||||
ServerURL: baseURL,
|
ServerURL: baseURL,
|
||||||
MachineKey: machineKeyA,
|
MachineKey: machineKeyA,
|
||||||
|
HealthTracker: ht,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewClient A: %v", err)
|
t.Fatalf("NewClient A: %v", err)
|
||||||
@@ -144,6 +149,8 @@ func TestSendMapUpdateAgainstTestControl(t *testing.T) {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
ht := new(health.Tracker)
|
||||||
|
|
||||||
serverKey, err := DiscoverServerKey(ctx, baseURL)
|
serverKey, err := DiscoverServerKey(ctx, baseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("DiscoverServerKey: %v", err)
|
t.Fatalf("DiscoverServerKey: %v", err)
|
||||||
@@ -154,8 +161,9 @@ func TestSendMapUpdateAgainstTestControl(t *testing.T) {
|
|||||||
nodeKey = key.NewNode()
|
nodeKey = key.NewNode()
|
||||||
machineKey = key.NewMachine()
|
machineKey = key.NewMachine()
|
||||||
c, err := NewClient(ClientOpts{
|
c, err := NewClient(ClientOpts{
|
||||||
ServerURL: baseURL,
|
ServerURL: baseURL,
|
||||||
MachineKey: machineKey,
|
MachineKey: machineKey,
|
||||||
|
HealthTracker: ht,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewClient %s: %v", hostname, err)
|
t.Fatalf("NewClient %s: %v", hostname, err)
|
||||||
@@ -176,8 +184,9 @@ func TestSendMapUpdateAgainstTestControl(t *testing.T) {
|
|||||||
|
|
||||||
// B starts a streaming map poll so we can observe updates about peer A.
|
// B starts a streaming map poll so we can observe updates about peer A.
|
||||||
clientB, err := NewClient(ClientOpts{
|
clientB, err := NewClient(ClientOpts{
|
||||||
ServerURL: baseURL,
|
ServerURL: baseURL,
|
||||||
MachineKey: machineKeyB,
|
MachineKey: machineKeyB,
|
||||||
|
HealthTracker: ht,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewClient B: %v", err)
|
t.Fatalf("NewClient B: %v", err)
|
||||||
@@ -228,8 +237,9 @@ func TestSendMapUpdateAgainstTestControl(t *testing.T) {
|
|||||||
|
|
||||||
// A pushes its disco key via SendMapUpdate.
|
// A pushes its disco key via SendMapUpdate.
|
||||||
clientA, err := NewClient(ClientOpts{
|
clientA, err := NewClient(ClientOpts{
|
||||||
ServerURL: baseURL,
|
ServerURL: baseURL,
|
||||||
MachineKey: machineKeyA,
|
MachineKey: machineKeyA,
|
||||||
|
HealthTracker: ht,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewClient A: %v", err)
|
t.Fatalf("NewClient A: %v", err)
|
||||||
|
|||||||
+11
-5
@@ -19,6 +19,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"tailscale.com/control/ts2021"
|
"tailscale.com/control/ts2021"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
@@ -43,6 +44,10 @@ type ClientOpts struct {
|
|||||||
|
|
||||||
// Logf is the log function. If nil, logger.Discard is used.
|
// Logf is the log function. If nil, logger.Discard is used.
|
||||||
Logf logger.Logf
|
Logf logger.Logf
|
||||||
|
|
||||||
|
// HealthTracker, if non-nil, is the health tracker passed through
|
||||||
|
// to the underlying noise client. May be nil.
|
||||||
|
HealthTracker *health.Tracker
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is a Tailscale protocol client that speaks to a coordination
|
// Client is a Tailscale protocol client that speaks to a coordination
|
||||||
@@ -155,11 +160,12 @@ func (c *Client) noiseClient(ctx context.Context) (*ts2021.Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nc, err := ts2021.NewClient(ts2021.ClientOpts{
|
nc, err := ts2021.NewClient(ts2021.ClientOpts{
|
||||||
ServerURL: c.serverURL,
|
ServerURL: c.serverURL,
|
||||||
PrivKey: c.opts.MachineKey,
|
PrivKey: c.opts.MachineKey,
|
||||||
ServerPubKey: c.serverPub,
|
ServerPubKey: c.serverPub,
|
||||||
Dialer: tsdial.NewFromFuncForDebug(c.logf, (&net.Dialer{}).DialContext),
|
Dialer: tsdial.NewFromFuncForDebug(c.logf, (&net.Dialer{}).DialContext),
|
||||||
Logf: c.logf,
|
Logf: c.logf,
|
||||||
|
HealthTracker: c.opts.HealthTracker,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating noise client: %w", err)
|
return nil, fmt.Errorf("creating noise client: %w", err)
|
||||||
|
|||||||
@@ -156,8 +156,8 @@ type Server struct {
|
|||||||
updates map[tailcfg.NodeID]chan updateType
|
updates map[tailcfg.NodeID]chan updateType
|
||||||
authPath map[string]*AuthPath
|
authPath map[string]*AuthPath
|
||||||
nodeKeyAuthed set.Set[key.NodePublic]
|
nodeKeyAuthed set.Set[key.NodePublic]
|
||||||
msgToSend map[key.NodePublic]any // value is *tailcfg.PingRequest or entire *tailcfg.MapResponse
|
msgToSend map[key.NodePublic][]any // FIFO queue per node; values are *tailcfg.PingRequest or *tailcfg.MapResponse
|
||||||
allExpired bool // All nodes will be told their node key is expired.
|
allExpired bool // All nodes will be told their node key is expired.
|
||||||
|
|
||||||
// tkaStorage records the Tailnet Lock state, if any.
|
// tkaStorage records the Tailnet Lock state, if any.
|
||||||
// If nil, Tailnet Lock is not enabled in the Tailnet.
|
// If nil, Tailnet Lock is not enabled in the Tailnet.
|
||||||
@@ -300,14 +300,16 @@ func (s *Server) AddRawMapResponse(nodeKeyDst key.NodePublic, mr *tailcfg.MapRes
|
|||||||
func (s *Server) addDebugMessage(nodeKeyDst key.NodePublic, msg any) bool {
|
func (s *Server) addDebugMessage(nodeKeyDst key.NodePublic, msg any) bool {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.msgToSend == nil {
|
|
||||||
s.msgToSend = map[key.NodePublic]any{}
|
|
||||||
}
|
|
||||||
// Now send the update to the channel
|
|
||||||
node := s.nodeLocked(nodeKeyDst)
|
node := s.nodeLocked(nodeKeyDst)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
updatesCh := s.updates[node.ID]
|
||||||
|
if updatesCh == nil {
|
||||||
|
// No streaming poll is registered, so there's nobody to deliver
|
||||||
|
// the message to.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if _, ok := msg.(*tailcfg.MapResponse); ok {
|
if _, ok := msg.(*tailcfg.MapResponse); ok {
|
||||||
if s.suppressAutoMapResponses == nil {
|
if s.suppressAutoMapResponses == nil {
|
||||||
@@ -316,10 +318,14 @@ func (s *Server) addDebugMessage(nodeKeyDst key.NodePublic, msg any) bool {
|
|||||||
s.suppressAutoMapResponses.Add(nodeKeyDst)
|
s.suppressAutoMapResponses.Add(nodeKeyDst)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.msgToSend[nodeKeyDst] = msg
|
mak.Set(&s.msgToSend, nodeKeyDst, append(s.msgToSend[nodeKeyDst], msg))
|
||||||
nodeID := node.ID
|
// sendUpdate returning false here is fine: the channel is a lossy
|
||||||
oldUpdatesCh := s.updates[nodeID]
|
// wake-up signal whose buffer is single-slot. A full buffer means a
|
||||||
return sendUpdate(oldUpdatesCh, updateDebugInjection)
|
// prior wake-up is still pending, and the streaming poll will check
|
||||||
|
// msgToSend when it processes that wake-up. The queue in msgToSend
|
||||||
|
// is the source of truth.
|
||||||
|
sendUpdate(updatesCh, updateDebugInjection)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark the Node key of every node as expired
|
// Mark the Node key of every node as expired
|
||||||
@@ -1472,15 +1478,29 @@ func (s *Server) MapResponse(req *tailcfg.MapRequest) (res *tailcfg.MapResponse,
|
|||||||
res.Node.PrimaryRoutes = s.nodeSubnetRoutes[nk]
|
res.Node.PrimaryRoutes = s.nodeSubnetRoutes[nk]
|
||||||
res.Node.AllowedIPs = append(res.Node.Addresses, s.nodeSubnetRoutes[nk]...)
|
res.Node.AllowedIPs = append(res.Node.Addresses, s.nodeSubnetRoutes[nk]...)
|
||||||
|
|
||||||
// Consume a PingRequest while protected by mutex if it exists
|
// Consume a PingRequest at the head of the queue, if any.
|
||||||
switch m := s.msgToSend[nk].(type) {
|
if q := s.msgToSend[nk]; len(q) > 0 {
|
||||||
case *tailcfg.PingRequest:
|
if pr, ok := q[0].(*tailcfg.PingRequest); ok {
|
||||||
res.PingRequest = m
|
res.PingRequest = pr
|
||||||
delete(s.msgToSend, nk)
|
s.popMsgToSendLocked(nk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// popMsgToSendLocked pops the head of the per-node message queue.
|
||||||
|
// s.mu must be held.
|
||||||
|
func (s *Server) popMsgToSendLocked(nk key.NodePublic) {
|
||||||
|
q := s.msgToSend[nk]
|
||||||
|
if len(q) <= 1 {
|
||||||
|
delete(s.msgToSend, nk)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Zero the head to allow GC of any large referenced response.
|
||||||
|
q[0] = nil
|
||||||
|
s.msgToSend[nk] = q[1:]
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) canGenerateAutomaticMapResponseFor(nk key.NodePublic) bool {
|
func (s *Server) canGenerateAutomaticMapResponseFor(nk key.NodePublic) bool {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
@@ -1490,22 +1510,21 @@ func (s *Server) canGenerateAutomaticMapResponseFor(nk key.NodePublic) bool {
|
|||||||
func (s *Server) hasPendingRawMapMessage(nk key.NodePublic) bool {
|
func (s *Server) hasPendingRawMapMessage(nk key.NodePublic) bool {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
_, ok := s.msgToSend[nk]
|
return len(s.msgToSend[nk]) > 0
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) takeRawMapMessage(nk key.NodePublic) (mapResJSON []byte, ok bool) {
|
func (s *Server) takeRawMapMessage(nk key.NodePublic) (mapResJSON []byte, ok bool) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
mr, ok := s.msgToSend[nk]
|
q := s.msgToSend[nk]
|
||||||
if !ok {
|
if len(q) == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
delete(s.msgToSend, nk)
|
mr := q[0]
|
||||||
|
s.popMsgToSendLocked(nk)
|
||||||
|
|
||||||
// If it's a bare PingRequest, wrap it in a MapResponse.
|
// If it's a bare PingRequest, wrap it in a MapResponse.
|
||||||
switch pr := mr.(type) {
|
if pr, ok := mr.(*tailcfg.PingRequest); ok {
|
||||||
case *tailcfg.PingRequest:
|
|
||||||
mr = &tailcfg.MapResponse{PingRequest: pr}
|
mr = &tailcfg.MapResponse{PingRequest: pr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user