all: use iterators in more places instead of Range funcs

And misc cleanup along the way.

Updates #12912

Change-Id: I0cab148b49efc668c6f5cdf09c740b84a713e388
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2025-01-04 10:14:23 -08:00 committed by Brad Fitzpatrick
parent ad8d8e37de
commit 47bd0723a0
7 changed files with 33 additions and 50 deletions

View File

@ -663,21 +663,23 @@ func peerChangeDiff(was tailcfg.NodeView, n *tailcfg.Node) (_ *tailcfg.PeerChang
} }
case "CapMap": case "CapMap":
if len(n.CapMap) != was.CapMap().Len() { if len(n.CapMap) != was.CapMap().Len() {
// If they have different lengths, they're different.
if n.CapMap == nil { if n.CapMap == nil {
pc().CapMap = make(tailcfg.NodeCapMap) pc().CapMap = make(tailcfg.NodeCapMap)
} else { } else {
pc().CapMap = maps.Clone(n.CapMap) pc().CapMap = maps.Clone(n.CapMap)
} }
break } else {
} // If they have the same length, check that all their keys
was.CapMap().Range(func(k tailcfg.NodeCapability, v views.Slice[tailcfg.RawMessage]) bool { // have the same values.
for k, v := range was.CapMap().All() {
nv, ok := n.CapMap[k] nv, ok := n.CapMap[k]
if !ok || !views.SliceEqual(v, views.SliceOf(nv)) { if !ok || !views.SliceEqual(v, views.SliceOf(nv)) {
pc().CapMap = maps.Clone(n.CapMap) pc().CapMap = maps.Clone(n.CapMap)
return false break
}
}
} }
return true
})
case "Tags": case "Tags":
if !views.SliceEqual(was.Tags(), views.SliceOf(n.Tags)) { if !views.SliceEqual(was.Tags(), views.SliceOf(n.Tags)) {
return nil, false return nil, false

View File

@ -11,7 +11,6 @@ import (
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/views"
) )
// TODO(andrew-d): should we have a package-global registry of logknobs? It // TODO(andrew-d): should we have a package-global registry of logknobs? It
@ -59,7 +58,7 @@ func (lk *LogKnob) Set(v bool) {
// about; we use this rather than a concrete type to avoid a circular // about; we use this rather than a concrete type to avoid a circular
// dependency. // dependency.
type NetMap interface { type NetMap interface {
SelfCapabilities() views.Slice[tailcfg.NodeCapability] HasSelfCapability(tailcfg.NodeCapability) bool
} }
// UpdateFromNetMap will enable logging if the SelfNode in the provided NetMap // UpdateFromNetMap will enable logging if the SelfNode in the provided NetMap
@ -68,8 +67,7 @@ func (lk *LogKnob) UpdateFromNetMap(nm NetMap) {
if lk.capName == "" { if lk.capName == "" {
return return
} }
lk.cap.Store(nm.HasSelfCapability(lk.capName))
lk.cap.Store(views.SliceContains(nm.SelfCapabilities(), lk.capName))
} }
// Do will call log with the provided format and arguments if any of the // Do will call log with the provided format and arguments if any of the

View File

@ -11,6 +11,7 @@ import (
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/util/set"
) )
var testKnob = NewLogKnob( var testKnob = NewLogKnob(
@ -63,11 +64,7 @@ func TestLogKnob(t *testing.T) {
} }
testKnob.UpdateFromNetMap(&netmap.NetworkMap{ testKnob.UpdateFromNetMap(&netmap.NetworkMap{
SelfNode: (&tailcfg.Node{ AllCaps: set.Of(tailcfg.NodeCapability("https://tailscale.com/cap/testing")),
Capabilities: []tailcfg.NodeCapability{
"https://tailscale.com/cap/testing",
},
}).View(),
}) })
if !testKnob.shouldLog() { if !testKnob.shouldLog() {
t.Errorf("expected shouldLog()=true") t.Errorf("expected shouldLog()=true")

View File

@ -1126,11 +1126,10 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
ss.Capabilities = make([]tailcfg.NodeCapability, 1, cm.Len()+1) ss.Capabilities = make([]tailcfg.NodeCapability, 1, cm.Len()+1)
ss.Capabilities[0] = "HTTPS://TAILSCALE.COM/s/DEPRECATED-NODE-CAPS#see-https://github.com/tailscale/tailscale/issues/11508" ss.Capabilities[0] = "HTTPS://TAILSCALE.COM/s/DEPRECATED-NODE-CAPS#see-https://github.com/tailscale/tailscale/issues/11508"
ss.CapMap = make(tailcfg.NodeCapMap, sn.CapMap().Len()) ss.CapMap = make(tailcfg.NodeCapMap, sn.CapMap().Len())
cm.Range(func(k tailcfg.NodeCapability, v views.Slice[tailcfg.RawMessage]) bool { for k, v := range cm.All() {
ss.CapMap[k] = v.AsSlice() ss.CapMap[k] = v.AsSlice()
ss.Capabilities = append(ss.Capabilities, k) ss.Capabilities = append(ss.Capabilities, k)
return true }
})
slices.Sort(ss.Capabilities[1:]) slices.Sort(ss.Capabilities[1:])
} }
} }
@ -1192,10 +1191,9 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
} }
if cm := p.CapMap(); cm.Len() > 0 { if cm := p.CapMap(); cm.Len() > 0 {
ps.CapMap = make(tailcfg.NodeCapMap, cm.Len()) ps.CapMap = make(tailcfg.NodeCapMap, cm.Len())
cm.Range(func(k tailcfg.NodeCapability, v views.Slice[tailcfg.RawMessage]) bool { for k, v := range cm.All() {
ps.CapMap[k] = v.AsSlice() ps.CapMap[k] = v.AsSlice()
return true }
})
} }
peerStatusFromNode(ps, p) peerStatusFromNode(ps, p)
@ -5918,15 +5916,15 @@ func (b *LocalBackend) setServeProxyHandlersLocked() {
} }
var backends map[string]bool var backends map[string]bool
b.serveConfig.RangeOverWebs(func(_ ipn.HostPort, conf ipn.WebServerConfigView) (cont bool) { b.serveConfig.RangeOverWebs(func(_ ipn.HostPort, conf ipn.WebServerConfigView) (cont bool) {
conf.Handlers().Range(func(_ string, h ipn.HTTPHandlerView) (cont bool) { for _, h := range conf.Handlers().All() {
backend := h.Proxy() backend := h.Proxy()
if backend == "" { if backend == "" {
// Only create proxy handlers for servers with a proxy backend. // Only create proxy handlers for servers with a proxy backend.
return true continue
} }
mak.Set(&backends, backend, true) mak.Set(&backends, backend, true)
if _, ok := b.serveProxyHandlers.Load(backend); ok { if _, ok := b.serveProxyHandlers.Load(backend); ok {
return true continue
} }
b.logf("serve: creating a new proxy handler for %s", backend) b.logf("serve: creating a new proxy handler for %s", backend)
@ -5935,11 +5933,10 @@ func (b *LocalBackend) setServeProxyHandlersLocked() {
// The backend endpoint (h.Proxy) should have been validated by expandProxyTarget // The backend endpoint (h.Proxy) should have been validated by expandProxyTarget
// in the CLI, so just log the error here. // in the CLI, so just log the error here.
b.logf("[unexpected] could not create proxy for %v: %s", backend, err) b.logf("[unexpected] could not create proxy for %v: %s", backend, err)
return true continue
} }
b.serveProxyHandlers.Store(backend, p) b.serveProxyHandlers.Store(backend, p)
return true }
})
return true return true
}) })

View File

@ -326,7 +326,7 @@ func (b *LocalBackend) setServeConfigLocked(config *ipn.ServeConfig, etag string
if b.serveConfig.Valid() { if b.serveConfig.Valid() {
has = b.serveConfig.Foreground().Contains has = b.serveConfig.Foreground().Contains
} }
prevConfig.Foreground().Range(func(k string, v ipn.ServeConfigView) (cont bool) { for k := range prevConfig.Foreground().All() {
if !has(k) { if !has(k) {
for _, sess := range b.notifyWatchers { for _, sess := range b.notifyWatchers {
if sess.sessionID == k { if sess.sessionID == k {
@ -334,8 +334,7 @@ func (b *LocalBackend) setServeConfigLocked(config *ipn.ServeConfig, etag string
} }
} }
} }
return true }
})
} }
return nil return nil

View File

@ -1457,7 +1457,7 @@ const (
// NodeCapMap is a map of capabilities to their optional values. It is valid for // NodeCapMap is a map of capabilities to their optional values. It is valid for
// a capability to have no values (nil slice); such capabilities can be tested // a capability to have no values (nil slice); such capabilities can be tested
// for by using the Contains method. // for by using the [NodeCapMap.Contains] method.
// //
// See [NodeCapability] for more information on keys. // See [NodeCapability] for more information on keys.
type NodeCapMap map[NodeCapability][]RawMessage type NodeCapMap map[NodeCapability][]RawMessage
@ -1873,7 +1873,7 @@ type MapResponse struct {
// PeersChangedPatch, if non-nil, means that node(s) have changed. // PeersChangedPatch, if non-nil, means that node(s) have changed.
// This is a lighter version of the older PeersChanged support that // This is a lighter version of the older PeersChanged support that
// only supports certain types of updates // only supports certain types of updates.
// //
// These are applied after Peers* above, but in practice the // These are applied after Peers* above, but in practice the
// control server should only send these on their own, without // control server should only send these on their own, without

View File

@ -197,21 +197,11 @@ func (nm *NetworkMap) DomainName() string {
return nm.Domain return nm.Domain
} }
// SelfCapabilities returns SelfNode.Capabilities if nm and nm.SelfNode are // HasSelfCapability reports whether nm.SelfNode contains capability c.
// non-nil. This is a method so we can use it in envknob/logknob without a //
// circular dependency. // It exists to satisify an unused (as of 2025-01-04) interface in the logknob package.
func (nm *NetworkMap) SelfCapabilities() views.Slice[tailcfg.NodeCapability] { func (nm *NetworkMap) HasSelfCapability(c tailcfg.NodeCapability) bool {
var zero views.Slice[tailcfg.NodeCapability] return nm.AllCaps.Contains(c)
if nm == nil || !nm.SelfNode.Valid() {
return zero
}
out := nm.SelfNode.Capabilities().AsSlice()
nm.SelfNode.CapMap().Range(func(k tailcfg.NodeCapability, _ views.Slice[tailcfg.RawMessage]) (cont bool) {
out = append(out, k)
return true
})
return views.SliceOf(out)
} }
func (nm *NetworkMap) String() string { func (nm *NetworkMap) String() string {