mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-29 23:33:45 +00:00
ipn/ipnlocal: make GetExt work earlier, before extension init
Taildrop wasn't working on iOS since #15971 because GetExt didn't work until after init, but that PR moved Init until after Start. This makes GetExt work before LocalBackend.Start (ExtensionHost.Init). Updates #15812 Change-Id: I6e87257cd97a20f86083a746d39df223e5b6791b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
54970054a6
commit
30a89ad378
@ -114,7 +114,7 @@ func RegisterExtension(name string, newExt NewExtensionFn) {
|
|||||||
panic(fmt.Sprintf("ipnext: newExt is nil: %q", name))
|
panic(fmt.Sprintf("ipnext: newExt is nil: %q", name))
|
||||||
}
|
}
|
||||||
if extensions.Contains(name) {
|
if extensions.Contains(name) {
|
||||||
panic(fmt.Sprintf("ipnext: duplicate extensions: %q", name))
|
panic(fmt.Sprintf("ipnext: duplicate extension name %q", name))
|
||||||
}
|
}
|
||||||
extensions.Set(name, &Definition{name, newExt})
|
extensions.Set(name, &Definition{name, newExt})
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/execqueue"
|
"tailscale.com/util/execqueue"
|
||||||
|
"tailscale.com/util/mak"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -97,7 +98,8 @@ type ExtensionHost struct {
|
|||||||
initialized atomic.Bool
|
initialized atomic.Bool
|
||||||
// activeExtensions is a subset of allExtensions that have been initialized and are ready to use.
|
// activeExtensions is a subset of allExtensions that have been initialized and are ready to use.
|
||||||
activeExtensions []ipnext.Extension
|
activeExtensions []ipnext.Extension
|
||||||
// extensionsByName are the activeExtensions indexed by their names.
|
// extensionsByName are the extensions indexed by their names.
|
||||||
|
// They are not necessarily initialized (in activeExtensions) yet.
|
||||||
extensionsByName map[string]ipnext.Extension
|
extensionsByName map[string]ipnext.Extension
|
||||||
// postInitWorkQueue is a queue of functions to be executed
|
// postInitWorkQueue is a queue of functions to be executed
|
||||||
// by the workQueue after all extensions have been initialized.
|
// by the workQueue after all extensions have been initialized.
|
||||||
@ -184,6 +186,24 @@ func newExtensionHost(logf logger.Logf, b Backend, overrideExts ...*ipnext.Defin
|
|||||||
return nil, fmt.Errorf("failed to create %q extension: %v", d.Name(), err)
|
return nil, fmt.Errorf("failed to create %q extension: %v", d.Name(), err)
|
||||||
}
|
}
|
||||||
host.allExtensions = append(host.allExtensions, ext)
|
host.allExtensions = append(host.allExtensions, ext)
|
||||||
|
|
||||||
|
if d.Name() != ext.Name() {
|
||||||
|
return nil, fmt.Errorf("extension name %q does not match the registered name %q", ext.Name(), d.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := host.extensionsByName[ext.Name()]; ok {
|
||||||
|
return nil, fmt.Errorf("duplicate extension name %q", ext.Name())
|
||||||
|
} else {
|
||||||
|
mak.Set(&host.extensionsByName, ext.Name(), ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(ext)
|
||||||
|
if _, ok := host.extByType.Load(typ); ok {
|
||||||
|
if _, ok := ext.(interface{ PermitDoubleRegister() }); !ok {
|
||||||
|
return nil, fmt.Errorf("duplicate extension type %T", ext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
host.extByType.Store(typ, ext)
|
||||||
}
|
}
|
||||||
return host, nil
|
return host, nil
|
||||||
}
|
}
|
||||||
@ -215,10 +235,6 @@ func (h *ExtensionHost) init() {
|
|||||||
defer h.initDone.Store(true)
|
defer h.initDone.Store(true)
|
||||||
|
|
||||||
// Initialize the extensions in the order they were registered.
|
// Initialize the extensions in the order they were registered.
|
||||||
h.mu.Lock()
|
|
||||||
h.activeExtensions = make([]ipnext.Extension, 0, len(h.allExtensions))
|
|
||||||
h.extensionsByName = make(map[string]ipnext.Extension, len(h.allExtensions))
|
|
||||||
h.mu.Unlock()
|
|
||||||
for _, ext := range h.allExtensions {
|
for _, ext := range h.allExtensions {
|
||||||
// Do not hold the lock while calling [ipnext.Extension.Init].
|
// Do not hold the lock while calling [ipnext.Extension.Init].
|
||||||
// Extensions call back into the host to register their callbacks,
|
// Extensions call back into the host to register their callbacks,
|
||||||
@ -240,8 +256,6 @@ func (h *ExtensionHost) init() {
|
|||||||
// We'd like to make them visible to other extensions that are initialized later.
|
// We'd like to make them visible to other extensions that are initialized later.
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
h.activeExtensions = append(h.activeExtensions, ext)
|
h.activeExtensions = append(h.activeExtensions, ext)
|
||||||
h.extensionsByName[ext.Name()] = ext
|
|
||||||
h.extByType.Store(reflect.TypeOf(ext), ext)
|
|
||||||
h.mu.Unlock()
|
h.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"tailscale.com/tstime"
|
"tailscale.com/tstime"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/lazy"
|
"tailscale.com/types/lazy"
|
||||||
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/persist"
|
"tailscale.com/types/persist"
|
||||||
"tailscale.com/util/must"
|
"tailscale.com/util/must"
|
||||||
)
|
)
|
||||||
@ -1042,6 +1043,38 @@ func TestNilExtensionHostMethodCall(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extBeforeStartExtension is a test extension used by TestGetExtBeforeStart.
|
||||||
|
// It is registered with the [ipnext.RegisterExtension].
|
||||||
|
type extBeforeStartExtension struct{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ipnext.RegisterExtension("ext-before-start", mkExtBeforeStartExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkExtBeforeStartExtension(logger.Logf, ipnext.SafeBackend) (ipnext.Extension, error) {
|
||||||
|
return extBeforeStartExtension{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extBeforeStartExtension) Name() string { return "ext-before-start" }
|
||||||
|
func (extBeforeStartExtension) Init(ipnext.Host) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (extBeforeStartExtension) Shutdown() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetExtBeforeStart verifies that an extension registered via
|
||||||
|
// RegisterExtension can be retrieved with GetExt before the host is started
|
||||||
|
// (via LocalBackend.Start)
|
||||||
|
func TestGetExtBeforeStart(t *testing.T) {
|
||||||
|
lb := newTestBackend(t)
|
||||||
|
// Now call GetExt without calling Start on the LocalBackend.
|
||||||
|
_, ok := GetExt[extBeforeStartExtension](lb)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("didn't find extension")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// checkMethodCallWithZeroArgs calls the method m on the receiver r
|
// checkMethodCallWithZeroArgs calls the method m on the receiver r
|
||||||
// with zero values for all its arguments, except the receiver itself.
|
// with zero values for all its arguments, except the receiver itself.
|
||||||
// It returns the result of the method call, or fails the test if the call panics.
|
// It returns the result of the method call, or fails the test if the call panics.
|
||||||
@ -1151,6 +1184,10 @@ type testExtension struct {
|
|||||||
|
|
||||||
var _ ipnext.Extension = (*testExtension)(nil)
|
var _ ipnext.Extension = (*testExtension)(nil)
|
||||||
|
|
||||||
|
// PermitDoubleRegister is a sentinel method whose existence tells the
|
||||||
|
// ExtensionHost to permit it to be registered multiple times.
|
||||||
|
func (*testExtension) PermitDoubleRegister() {}
|
||||||
|
|
||||||
func (e *testExtension) setT(t *testing.T) {
|
func (e *testExtension) setT(t *testing.T) {
|
||||||
e.t = t
|
e.t = t
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user