all: apply godoc [Name] link conventions across comments

Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.

Pattern applied across the tree:
  In-package         [Foo], [Foo.Bar]
  Cross-package      [pkg.Foo], [pkg.Foo.Bar]
  Stdlib             [netip.Prefix], [errors.Is], [context.Context]
  Tailscale          [tailcfg.MapResponse], [tailcfg.Node.CapMap],
                     [tailcfg.NodeAttrSuggestExitNode]

Skip rules:
  - File:line refs left as plain text
  - HuJSON wire keys inside backtick raw strings untouched
  - ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
    symbols, left as plain text
  - JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
    link tags, decorative dividers — all left as-is
This commit is contained in:
Kristoffer Dalby 2026-05-18 18:35:53 +00:00
parent 17236fd284
commit 4cca63155d
124 changed files with 1037 additions and 1011 deletions

View file

@ -150,7 +150,7 @@ func (s *State) DebugOverview() string {
return sb.String()
}
// DebugNodeStore returns debug information about the NodeStore.
// DebugNodeStore returns debug information about the [NodeStore].
func (s *State) DebugNodeStore() string {
return s.nodeStore.DebugString()
}
@ -257,7 +257,7 @@ func (s *State) DebugFilter() ([]tailcfg.FilterRule, error) {
}
// DebugRoutes returns the current primary routes information as a
// structured object built from the NodeStore snapshot.
// structured object built from the [NodeStore] snapshot.
func (s *State) DebugRoutes() types.DebugRoutes {
debug := types.DebugRoutes{
AvailableRoutes: make(map[types.NodeID][]netip.Prefix),
@ -421,7 +421,7 @@ func (s *State) DebugDERPJSON() DebugDERPInfo {
return info
}
// DebugNodeStoreJSON returns the actual nodes map from the current NodeStore snapshot.
// DebugNodeStoreJSON returns the actual nodes map from the current [NodeStore] snapshot.
func (s *State) DebugNodeStoreJSON() map[types.NodeID]types.Node {
snapshot := s.nodeStore.data.Load()
return snapshot.nodesByID

View file

@ -29,7 +29,7 @@ type HAHealthProber struct {
lastStableSession *xsync.Map[types.NodeID, uint64]
}
// NewHAHealthProber creates a prober that uses the given State for
// NewHAHealthProber creates a prober that uses the given [State] for
// ping tracking and primary route management.
// isConnected should return true if a node has an active map session.
func NewHAHealthProber(

View file

@ -1,5 +1,5 @@
// Package state provides pure functions for processing MapRequest data.
// These functions are extracted from UpdateNodeFromMapRequest to improve
// Package state provides pure functions for processing [tailcfg.MapRequest] data.
// These functions are extracted from [State.UpdateNodeFromMapRequest] to improve
// testability and maintainability.
package state
@ -10,19 +10,19 @@ import (
"tailscale.com/tailcfg"
)
// netInfoFromMapRequest determines the correct NetInfo to use.
// Returns the NetInfo that should be used for this request.
// netInfoFromMapRequest determines the correct [tailcfg.NetInfo] to use.
// Returns the [tailcfg.NetInfo] that should be used for this request.
func netInfoFromMapRequest(
nodeID types.NodeID,
currentHostinfo *tailcfg.Hostinfo,
reqHostinfo *tailcfg.Hostinfo,
) *tailcfg.NetInfo {
// If request has NetInfo, use it
// If request has [tailcfg.NetInfo], use it
if reqHostinfo != nil && reqHostinfo.NetInfo != nil {
return reqHostinfo.NetInfo
}
// Otherwise, use current NetInfo if available
// Otherwise, use current [tailcfg.NetInfo] if available
if currentHostinfo != nil && currentHostinfo.NetInfo != nil {
log.Debug().
Caller().
@ -33,7 +33,7 @@ func netInfoFromMapRequest(
return currentHostinfo.NetInfo
}
// No NetInfo available anywhere - log for debugging
// No [tailcfg.NetInfo] available anywhere - log for debugging
var hostname string
if reqHostinfo != nil {
hostname = reqHostinfo.Hostname

View file

@ -75,7 +75,7 @@ func TestNetInfoPreservationInRegistrationFlow(t *testing.T) {
nodeID := types.NodeID(1)
// This test reproduces the bug in registration flows where NetInfo was lost
// because we used the wrong hostinfo reference when calling NetInfoFromMapRequest
// because we used the wrong hostinfo reference when calling [netInfoFromMapRequest]
t.Run("registration_flow_bug_reproduction", func(t *testing.T) {
// Simulate existing node with NetInfo (before re-registration)
existingNodeHostinfo := &tailcfg.Hostinfo{

View file

@ -21,12 +21,12 @@ import (
)
// fallbackGivenName is the DNS label used when a node is written with
// an empty GivenName. Matches Tailscale SaaS behaviour for empty
// sanitised labels.
// an empty [types.Node.GivenName]. Matches Tailscale SaaS behaviour
// for empty sanitised labels.
const fallbackGivenName = "node"
// Errors returned by SetGivenName. ErrNodeNotFound is defined in
// state.go and reused here.
// Errors returned by [NodeStore.SetGivenName]. [ErrNodeNotFound] is defined
// in state.go and reused here.
var (
ErrGivenNameTaken = errors.New("given name already in use by another node")
ErrGivenNameInvalid = errors.New("given name is not a valid DNS label")
@ -132,10 +132,10 @@ func NewNodeStore(allNodes types.Nodes, peersFunc PeersFunc, batchSize int, batc
return store
}
// Snapshot is the representation of the current state of the NodeStore.
// Snapshot is the representation of the current state of the [NodeStore].
// It contains all nodes and their relationships.
// It is a copy-on-write structure, meaning that when a write occurs,
// a new Snapshot is created with the updated state,
// a new [Snapshot] is created with the updated state,
// and replaces the old one atomically.
type Snapshot struct {
// nodesByID is the main source of truth for nodes.
@ -161,7 +161,7 @@ type Snapshot struct {
// based on the current policy.
type PeersFunc func(nodes []types.NodeView) map[types.NodeID][]types.NodeView
// work represents a single operation to be performed on the NodeStore.
// work represents a single operation to be performed on the [NodeStore].
type work struct {
op int
nodeID types.NodeID
@ -211,18 +211,19 @@ func (s *NodeStore) PutNode(n types.Node) types.NodeView {
return resultNode
}
// UpdateNodeFunc is a function type that takes a pointer to a Node and modifies it.
// UpdateNodeFunc is a function type that takes a pointer to a [types.Node] and modifies it.
type UpdateNodeFunc func(n *types.Node)
// UpdateNode applies a function to modify a specific node in the
// store. Single-node convenience wrapper around [NodeStore.UpdateNodes]
// — the writer goroutine signals completion only after the post-batch
// snapshot has been stored, so the follow-up GetNode read sees the
// applied update. Returns the resulting node and whether it exists.
// snapshot has been stored, so the follow-up [NodeStore.GetNode] read
// sees the applied update. Returns the resulting node and whether it
// exists.
//
// Callers that need to change several nodes atomically should call
// UpdateNodes directly; collecting changes into one batch keeps the
// election from running on a half-applied snapshot.
// [NodeStore.UpdateNodes] directly; collecting changes into one batch
// keeps the election from running on a half-applied snapshot.
func (s *NodeStore) UpdateNode(nodeID types.NodeID, updateFn UpdateNodeFunc) (types.NodeView, bool) {
timer := prometheus.NewTimer(nodeStoreOperationDuration.WithLabelValues("update"))
defer timer.ObserveDuration()
@ -287,19 +288,20 @@ func (s *NodeStore) DeleteNode(id types.NodeID) {
nodeStoreOperations.WithLabelValues("delete").Inc()
}
// SetGivenName sets node.GivenName on the node identified by id,
// SetGivenName sets [types.Node.GivenName] on the node identified by id,
// rejecting the write if the name is already held by another node.
// Intended for the admin rename path, where auto-bumping a
// user-supplied name would be surprising.
//
// Returns:
// - the stored NodeView and nil on success
// - ErrGivenNameInvalid if name is not a valid DNS label
// - ErrGivenNameTaken if another node already holds name
// - ErrNodeNotFound if no node with id exists
// - the stored [types.NodeView] and nil on success
// - [ErrGivenNameInvalid] if name is not a valid DNS label
// - [ErrGivenNameTaken] if another node already holds name
// - [ErrNodeNotFound] if no node with id exists
//
// Runs as a single writer-goroutine op, so the uniqueness check and
// the write are atomic with respect to concurrent PutNode/UpdateNode.
// Runs as a single writer-goroutine op, so the uniqueness check and the
// write are atomic with respect to concurrent
// [NodeStore.PutNode]/[NodeStore.UpdateNode].
func (s *NodeStore) SetGivenName(id types.NodeID, name string) (types.NodeView, error) {
timer := prometheus.NewTimer(nodeStoreOperationDuration.WithLabelValues("set_name"))
defer timer.ObserveDuration()
@ -330,13 +332,13 @@ func (s *NodeStore) SetGivenName(id types.NodeID, name string) (types.NodeView,
return <-w.nodeResult, nil
}
// Start initializes the NodeStore and starts processing the write queue.
// Start initializes the [NodeStore] and starts processing the write queue.
func (s *NodeStore) Start() {
s.writeQueue = make(chan work)
go s.processWrite()
}
// Stop stops the NodeStore.
// Stop stops the [NodeStore].
func (s *NodeStore) Stop() {
close(s.writeQueue)
}
@ -536,14 +538,14 @@ func (s *NodeStore) applyBatch(batch []work) {
// resolveGivenName returns a unique DNS label for the node identified
// by self, based on the caller-supplied base label. If base is empty
// it falls back to fallbackGivenName ("node"). The label's own holder
// it falls back to [fallbackGivenName] ("node"). The label's own holder
// (self) is excluded from the collision scan so an idempotent write
// keeps the current label.
//
// On collision the label is bumped as base, base-1, base-2, …, first
// unused wins. Must be called from the NodeStore writer goroutine
// (inside applyBatch) so the nodes map reflects all earlier ops in
// the batch and no other writer can interleave.
// unused wins. Must be called from the [NodeStore] writer goroutine
// (inside [NodeStore.applyBatch]) so the nodes map reflects all earlier
// ops in the batch and no other writer can interleave.
func resolveGivenName(nodes map[types.NodeID]types.Node, self types.NodeID, base string) string {
if base == "" {
base = fallbackGivenName
@ -569,7 +571,7 @@ func resolveGivenName(nodes map[types.NodeID]types.Node, self types.NodeID, base
}
// snapshotFromNodes builds the index maps and primary-route table for
// a new Snapshot. prevRoutes carries forward the previous primary
// a new [Snapshot]. prevRoutes carries forward the previous primary
// assignment so a still-valid choice survives unrelated batches.
func snapshotFromNodes(
nodes map[types.NodeID]types.Node,
@ -719,8 +721,8 @@ func electPrimaryRoutes(
// GetNode retrieves a node by its ID.
// The bool indicates if the node exists or is available (like "err not found").
// The NodeView might be invalid, so it must be checked with .Valid(), which must be used to ensure
// it isn't an invalid node (this is more of a node error or node is broken).
// The [types.NodeView] might be invalid, so it must be checked with .Valid(), which must
// be used to ensure it isn't an invalid node (this is more of a node error or node is broken).
func (s *NodeStore) GetNode(id types.NodeID) (types.NodeView, bool) {
timer := prometheus.NewTimer(nodeStoreOperationDuration.WithLabelValues("get"))
defer timer.ObserveDuration()
@ -735,10 +737,10 @@ func (s *NodeStore) GetNode(id types.NodeID) (types.NodeView, bool) {
return n.View(), true
}
// GetNodeByNodeKey retrieves a node by its NodeKey.
// GetNodeByNodeKey retrieves a node by its [key.NodePublic].
// The bool indicates if the node exists or is available (like "err not found").
// The NodeView might be invalid, so it must be checked with .Valid(), which must be used to ensure
// it isn't an invalid node (this is more of a node error or node is broken).
// The [types.NodeView] might be invalid, so it must be checked with .Valid(), which must
// be used to ensure it isn't an invalid node (this is more of a node error or node is broken).
func (s *NodeStore) GetNodeByNodeKey(nodeKey key.NodePublic) (types.NodeView, bool) {
timer := prometheus.NewTimer(nodeStoreOperationDuration.WithLabelValues("get_by_key"))
defer timer.ObserveDuration()
@ -790,7 +792,7 @@ func (s *NodeStore) GetNodeByMachineKeyAnyUser(machineKey key.MachinePublic) (ty
return types.NodeView{}, false
}
// DebugString returns debug information about the NodeStore.
// DebugString returns debug information about the [NodeStore].
func (s *NodeStore) DebugString() string {
snapshot := s.data.Load()
@ -971,8 +973,8 @@ func (s *NodeStore) PrimaryRoutesString() string {
return b.String()
}
// RebuildPeerMaps rebuilds the peer relationship map using the current peersFunc.
// This must be called after policy changes because peersFunc uses PolicyManager's
// RebuildPeerMaps rebuilds the peer relationship map using the current [PeersFunc].
// This must be called after policy changes because [PeersFunc] uses [policy.PolicyManager]'s
// filters to determine which nodes can see each other. Without rebuilding, the
// peer map would use stale filter data until the next node add/delete.
func (s *NodeStore) RebuildPeerMaps() {

View file

@ -10,10 +10,10 @@ import (
const pingIDLength = 16
// pingTracker correlates outgoing PingRequests with incoming HEAD
// pingTracker correlates outgoing [tailcfg.PingRequest]s with incoming HEAD
// callbacks. Entries have no server-side TTL: callers are responsible
// for cleaning up via CancelPing or by reading from the response channel
// within their own timeout.
// for cleaning up via [pingTracker.cancel] or by reading from the response
// channel within their own timeout.
type pingTracker struct {
mu sync.Mutex
pending map[string]*pendingPing
@ -80,7 +80,7 @@ func (pt *pingTracker) cancel(pingID string) {
}
// drain closes every outstanding response channel and clears the map.
// Called from State.Close to unblock any caller still waiting on a
// Called from [State.Close] to unblock any caller still waiting on a
// channel that will never receive.
func (pt *pingTracker) drain() {
pt.mu.Lock()
@ -93,7 +93,7 @@ func (pt *pingTracker) drain() {
}
// RegisterPing tracks a pending ping and returns its ID and a channel
// for the latency. Callers must defer CancelPing or read the channel
// for the latency. Callers must defer [State.CancelPing] or read the channel
// within their own timeout; there is no server-side TTL.
func (s *State) RegisterPing(nodeID types.NodeID) (string, <-chan time.Duration) {
return s.pings.register(nodeID)

View file

@ -20,12 +20,13 @@ var (
ErrRequestedTagsInvalidOrNotPermitted = errors.New("requested tags")
)
// ErrTaggedNodeHasUser is returned when a tagged node has a UserID set.
// ErrTaggedNodeHasUser is returned when a tagged node has a [types.Node.UserID] set.
var ErrTaggedNodeHasUser = errors.New("tagged node must not have user_id set")
// validateNodeOwnership ensures proper node ownership model.
// A node must be either user-owned or tagged, and these are mutually exclusive:
// tagged nodes must not have a UserID, and user-owned nodes must not have tags.
// tagged nodes must not have a [types.Node.UserID], and user-owned nodes must
// not have tags.
func validateNodeOwnership(node *types.Node) error {
if node.IsTagged() {
if len(node.Tags) == 0 {

View file

@ -4,7 +4,7 @@ import (
"time"
)
// Test configuration for NodeStore batching.
// Test configuration for [NodeStore] batching.
// These values are optimized for test speed rather than production use.
const (
TestBatchSize = 5