ipn/{ipnext,ipnlocal}: add a SafeBackend interface

Updates #12614

Change-Id: I197e673666e86ea74c19e3935ed71aec269b6c94
Co-authored-by: Nick Khyl <nickk@tailscale.com>
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-04-24 13:55:39 -07:00
committed by Brad Fitzpatrick
parent 25c4dc5fd7
commit 3d8533b5d0
8 changed files with 76 additions and 32 deletions
+27 -9
View File
@@ -20,7 +20,6 @@ import (
"tailscale.com/ipn/ipnauth"
"tailscale.com/ipn/ipnext"
"tailscale.com/tailcfg"
"tailscale.com/tsd"
"tailscale.com/types/logger"
"tailscale.com/util/execqueue"
"tailscale.com/util/testenv"
@@ -131,15 +130,32 @@ type Backend interface {
// SwitchToBestProfile switches to the best profile for the current state of the system.
// The reason indicates why the profile is being switched.
SwitchToBestProfile(reason string)
SendNotify(ipn.Notify)
ipnext.SafeBackend
}
// NewExtensionHost returns a new [ExtensionHost] which manages registered extensions for the given backend.
// The extensions are instantiated, but are not initialized until [ExtensionHost.Init] is called.
// It returns an error if instantiating any extension fails.
func NewExtensionHost(logf logger.Logf, b Backend) (*ExtensionHost, error) {
return newExtensionHost(logf, b)
}
func NewExtensionHostForTest(logf logger.Logf, b Backend, overrideExts ...*ipnext.Definition) (*ExtensionHost, error) {
if !testenv.InTest() {
panic("use outside of test")
}
return newExtensionHost(logf, b, overrideExts...)
}
// newExtensionHost is the shared implementation of [NewExtensionHost] and
// [NewExtensionHostForTest].
//
// If overrideExts is non-nil, the registered extensions are ignored and the provided extensions are used instead.
// Overriding extensions is primarily used for testing.
func NewExtensionHost(logf logger.Logf, sys *tsd.System, b Backend, overrideExts ...*ipnext.Definition) (_ *ExtensionHost, err error) {
// If overrideExts is non-nil, the registered extensions are ignored and the
// provided extensions are used instead. Overriding extensions is primarily used
// for testing.
func newExtensionHost(logf logger.Logf, b Backend, overrideExts ...*ipnext.Definition) (_ *ExtensionHost, err error) {
host := &ExtensionHost{
b: b,
logf: logger.WithPrefix(logf, "ipnext: "),
@@ -172,7 +188,7 @@ func NewExtensionHost(logf logger.Logf, sys *tsd.System, b Backend, overrideExts
host.allExtensions = make([]ipnext.Extension, 0, numExts)
for _, d := range exts {
ext, err := d.MakeExtension(logf, sys)
ext, err := d.MakeExtension(logf, b)
if errors.Is(err, ipnext.SkipExtension) {
// The extension wants to be skipped.
host.logf("%q: %v", d.Name(), err)
@@ -334,12 +350,14 @@ func (h *ExtensionHost) SwitchToBestProfileAsync(reason string) {
})
}
// Backend returns the [Backend] used by the extension host.
func (h *ExtensionHost) Backend() Backend {
// SendNotifyAsync implements [ipnext.Host].
func (h *ExtensionHost) SendNotifyAsync(n ipn.Notify) {
if h == nil {
return nil
return
}
return h.b
h.enqueueBackendOperation(func(b Backend) {
b.SendNotify(n)
})
}
// addFuncHook appends non-nil fn to hooks.