ipn/ipnlocal: add c2n handler to flush logtail for support debugging

Updates tailscale/corp#8564

Change-Id: I0c619d4007069f90cffd319fba66bd034d63e84d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/6938/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent 673b3d8dbd
commit 69c0b7e712

@ -526,6 +526,9 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logid string) (_ *ip
return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err) return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err)
} }
lb.SetVarRoot(opts.VarRoot) lb.SetVarRoot(opts.VarRoot)
if logPol != nil {
lb.SetLogFlusher(logPol.Logtail.StartFlush)
}
if root := lb.TailscaleVarRoot(); root != "" { if root := lb.TailscaleVarRoot(); root != "" {
dnsfallback.SetCachePath(filepath.Join(root, "derpmap.cached.json")) dnsfallback.SetCachePath(filepath.Join(root, "derpmap.cached.json"))
} }

@ -26,6 +26,16 @@ func (b *LocalBackend) handleC2N(w http.ResponseWriter, r *http.Request) {
// Test handler. // Test handler.
body, _ := io.ReadAll(r.Body) body, _ := io.ReadAll(r.Body)
w.Write(body) w.Write(body)
case "/logtail/flush":
if r.Method != "POST" {
http.Error(w, "bad method", http.StatusMethodNotAllowed)
return
}
if b.TryFlushLogs() {
w.WriteHeader(http.StatusNoContent)
} else {
http.Error(w, "no log flusher wired up", http.StatusInternalServerError)
}
case "/debug/goroutines": case "/debug/goroutines":
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
w.Write(goroutines.ScrubbedGoroutineDump()) w.Write(goroutines.ScrubbedGoroutineDump())

@ -140,6 +140,7 @@ type LocalBackend struct {
gotPortPollRes chan struct{} // closed upon first readPoller result gotPortPollRes chan struct{} // closed upon first readPoller result
newDecompressor func() (controlclient.Decompressor, error) newDecompressor func() (controlclient.Decompressor, error)
varRoot string // or empty if SetVarRoot never called varRoot string // or empty if SetVarRoot never called
logFlushFunc func() // or nil if SetLogFlusher wasn't called
sshAtomicBool atomic.Bool sshAtomicBool atomic.Bool
shutdownCalled bool // if Shutdown has been called shutdownCalled bool // if Shutdown has been called
@ -3015,6 +3016,25 @@ func (b *LocalBackend) SetVarRoot(dir string) {
b.varRoot = dir b.varRoot = dir
} }
// SetLogFlusher sets a func to be called to flush log uploads.
//
// It should only be called before the LocalBackend is used.
func (b *LocalBackend) SetLogFlusher(flushFunc func()) {
b.logFlushFunc = flushFunc
}
// TryFlushLogs calls the log flush function. It returns false if a log flush
// function was never initialized with SetLogFlusher.
//
// TryFlushLogs should not block.
func (b *LocalBackend) TryFlushLogs() bool {
if b.logFlushFunc == nil {
return false
}
b.logFlushFunc()
return true
}
// TailscaleVarRoot returns the root directory of Tailscale's writable // TailscaleVarRoot returns the root directory of Tailscale's writable
// storage area. (e.g. "/var/lib/tailscale") // storage area. (e.g. "/var/lib/tailscale")
// //

@ -292,6 +292,7 @@ func (h *Handler) serveBugReport(w http.ResponseWriter, r *http.Request) {
http.Error(w, "only POST allowed", http.StatusMethodNotAllowed) http.Error(w, "only POST allowed", http.StatusMethodNotAllowed)
return return
} }
defer h.b.TryFlushLogs() // kick off upload after bugreport's done logging
logMarker := func() string { logMarker := func() string {
return fmt.Sprintf("BUG-%v-%v-%v", h.backendLogID, time.Now().UTC().Format("20060102150405Z"), randHex(8)) return fmt.Sprintf("BUG-%v-%v-%v", h.backendLogID, time.Now().UTC().Format("20060102150405Z"), randHex(8))

@ -461,12 +461,24 @@ func (l *Logger) upload(ctx context.Context, body []byte, origlen int) (uploaded
return true, nil return true, nil
} }
// Flush uploads all logs to the server. // Flush uploads all logs to the server. It blocks until complete or there is an
// It blocks until complete or there is an unrecoverable error. // unrecoverable error.
//
// TODO(bradfitz): this apparently just returns nil, as of tailscale/corp@9c2ec35.
// Finish cleaning this up.
func (l *Logger) Flush() error { func (l *Logger) Flush() error {
return nil return nil
} }
// StartFlush starts a log upload, if anything is pending.
//
// If l is nil, StartFlush is a no-op.
func (l *Logger) StartFlush() {
if l != nil {
l.tryDrainWake()
}
}
// logtailDisabled is whether logtail uploads to logcatcher are disabled. // logtailDisabled is whether logtail uploads to logcatcher are disabled.
var logtailDisabled atomic.Bool var logtailDisabled atomic.Bool

@ -88,7 +88,8 @@ type CapabilityVersion int
// - 49: 2022-11-03: Client understands EarlyNoise // - 49: 2022-11-03: Client understands EarlyNoise
// - 50: 2022-11-14: Client understands CapabilityIngress // - 50: 2022-11-14: Client understands CapabilityIngress
// - 51: 2022-11-30: Client understands CapabilityTailnetLockAlpha // - 51: 2022-11-30: Client understands CapabilityTailnetLockAlpha
const CurrentCapabilityVersion CapabilityVersion = 51 // - 52: 2023-01-05: client can handle c2n POST /logtail/flush
const CurrentCapabilityVersion CapabilityVersion = 52
type StableID string type StableID string

Loading…
Cancel
Save