ipn/ipn{ext,local}: allow extension lookup by name or type

In this PR, we add two methods to facilitate extension lookup by both extensions,
and non-extensions (e.g., PeerAPI or LocalAPI handlers):
 - FindExtensionByName returns an extension with the specified name.
   It can then be type asserted to a given type.
 - FindMatchingExtension is like errors.As, but for extensions.
   It returns the first extension that matches the target type (either a specific extension
   or an interface).

Updates tailscale/corp#27645
Updates tailscale/corp#27502

Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
Nick Khyl
2025-04-11 10:09:03 -05:00
committed by Nick Khyl
parent 1e290867bd
commit f28c8d0ec0
4 changed files with 184 additions and 0 deletions

View File

@@ -174,6 +174,9 @@ func DefinitionWithErrForTest(name string, err error) *Definition {
//
// A host must be safe for concurrent use.
type Host interface {
// Extensions returns the host's [ExtensionServices].
Extensions() ExtensionServices
// Profiles returns the host's [ProfileServices].
Profiles() ProfileServices
@@ -197,6 +200,22 @@ type Host interface {
RegisterControlClientCallback(NewControlClientCallback) (unregister func())
}
// ExtensionServices provides access to the [Host]'s extension management services,
// such as fetching active extensions.
type ExtensionServices interface {
// FindExtensionByName returns an active extension with the given name,
// or nil if no such extension exists.
FindExtensionByName(name string) any
// FindMatchingExtension finds the first active extension that matches target,
// and if one is found, sets target to that extension and returns true.
// Otherwise, it returns false.
//
// It panics if target is not a non-nil pointer to either a type
// that implements [ipnext.Extension], or to any interface type.
FindMatchingExtension(target any) bool
}
// ProfileServices provides access to the [Host]'s profile management services,
// such as switching profiles and registering profile change callbacks.
type ProfileServices interface {