feature/featuretags: add a catch-all "Debug" feature flag

Saves 168 KB.

Updates #12614

Change-Id: Iaab3ae3efc6ddc7da39629ef13e5ec44976952ba
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-09-30 09:53:55 -07:00
committed by Brad Fitzpatrick
parent bbb16e4e72
commit ee034d48fc
15 changed files with 573 additions and 455 deletions
+22
View File
@@ -15,6 +15,8 @@ import (
"time"
"tailscale.com/control/controlclient"
"tailscale.com/feature"
"tailscale.com/feature/buildfeatures"
"tailscale.com/ipn"
"tailscale.com/net/sockstats"
"tailscale.com/tailcfg"
@@ -130,6 +132,10 @@ func handleC2NLogtailFlush(b *LocalBackend, w http.ResponseWriter, r *http.Reque
}
func handleC2NDebugNetMap(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
if !buildfeatures.HasDebug {
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
return
}
ctx := r.Context()
if r.Method != httpm.POST && r.Method != httpm.GET {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
@@ -190,20 +196,36 @@ func handleC2NDebugNetMap(b *LocalBackend, w http.ResponseWriter, r *http.Reques
}
func handleC2NDebugGoroutines(_ *LocalBackend, w http.ResponseWriter, r *http.Request) {
if !buildfeatures.HasDebug {
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
return
}
w.Header().Set("Content-Type", "text/plain")
w.Write(goroutines.ScrubbedGoroutineDump(true))
}
func handleC2NDebugPrefs(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
if !buildfeatures.HasDebug {
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
return
}
writeJSON(w, b.Prefs())
}
func handleC2NDebugMetrics(_ *LocalBackend, w http.ResponseWriter, r *http.Request) {
if !buildfeatures.HasDebug {
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
return
}
w.Header().Set("Content-Type", "text/plain")
clientmetric.WritePrometheusExpositionFormat(w)
}
func handleC2NDebugComponentLogging(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
if !buildfeatures.HasDebug {
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
return
}
component := r.FormValue("component")
secs, _ := strconv.Atoi(r.FormValue("secs"))
if secs == 0 {
+14 -6
View File
@@ -557,12 +557,14 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
b.logf("[unexpected] failed to wire up PeerAPI port for engine %T", e)
}
for _, component := range ipn.DebuggableComponents {
key := componentStateKey(component)
if ut, err := ipn.ReadStoreInt(pm.Store(), key); err == nil {
if until := time.Unix(ut, 0); until.After(b.clock.Now()) {
// conditional to avoid log spam at start when off
b.SetComponentDebugLogging(component, until)
if buildfeatures.HasDebug {
for _, component := range ipn.DebuggableComponents {
key := componentStateKey(component)
if ut, err := ipn.ReadStoreInt(pm.Store(), key); err == nil {
if until := time.Unix(ut, 0); until.After(b.clock.Now()) {
// conditional to avoid log spam at start when off
b.SetComponentDebugLogging(component, until)
}
}
}
}
@@ -666,6 +668,9 @@ func componentStateKey(component string) ipn.StateKey {
// - magicsock
// - sockstats
func (b *LocalBackend) SetComponentDebugLogging(component string, until time.Time) error {
if !buildfeatures.HasDebug {
return feature.ErrUnavailable
}
b.mu.Lock()
defer b.mu.Unlock()
@@ -790,6 +795,9 @@ func (b *LocalBackend) QueryDNS(name string, queryType dnsmessage.Type) (res []b
// enabled until, or the zero time if component's time is not currently
// enabled.
func (b *LocalBackend) GetComponentDebugLogging(component string) time.Time {
if !buildfeatures.HasDebug {
return time.Time{}
}
b.mu.Lock()
defer b.mu.Unlock()