Move all the FooForTest methods on LocalBackend to instead be methods on a new unexported forTest type which is then given out to callers in other packages via an exported ForTest method (panicking in non-test contexts) that returns that unexported type. This is unusual style (exported returning unexported) but declutters godoc and makes call sites both more explicit and easier to read without the "ForTest" suffix polluting the symbols. Now FooForTest() changes into ForTest().Foo(). This was motivated by a pending change moving a bunch of code out of LocalBackend into other packages that required adding more ForTest methods to LocalBackend to keep the tests (now in other packages) working. Instead, do this refactor now so the future change is prettier. Updates #12614 Updates #cleanup Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: Ib25e6d76d48dc8622ac3a955e0b1220d582e63a8
122 lines
3.8 KiB
Go
122 lines
3.8 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package ipnlocal
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"tailscale.com/control/controlclient"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/ipn/ipnauth"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/key"
|
|
"tailscale.com/types/netmap"
|
|
"tailscale.com/util/testenv"
|
|
"tailscale.com/wgengine/filter"
|
|
)
|
|
|
|
// forTest is an unexported type to hide all the test-only
|
|
// methods on [LocalBackend] from godoc.
|
|
type forTest struct{ b *LocalBackend }
|
|
|
|
// ForTest returns a handle to test-only methods on b.
|
|
// The resulting type is unexported to make it very obvious
|
|
// in godoc that this is not stable API. This method panics
|
|
// if called outside of tests, which also centralizes all
|
|
// must-be-in-tests validation.
|
|
func (b *LocalBackend) ForTest() forTest {
|
|
testenv.AssertInTest()
|
|
return forTest{b}
|
|
}
|
|
|
|
// HandleC2N calls [LocalBackend.handleC2N], for use by feature/ packages that
|
|
// register C2N handlers and want to test them.
|
|
func (f forTest) HandleC2N(w http.ResponseWriter, r *http.Request) {
|
|
f.b.handleC2N(w, r)
|
|
}
|
|
|
|
// SetIPServiceMappings overwrites the LocalBackend's IP-to-service mappings
|
|
// and propagates them to the netstack subsystem if registered.
|
|
func (f forTest) SetIPServiceMappings(m netmap.IPServiceMappings) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.ipVIPServiceMap = m
|
|
if ns, ok := b.sys.Netstack.GetOK(); ok {
|
|
ns.UpdateIPServiceMappings(m)
|
|
}
|
|
}
|
|
|
|
// GetFilter returns the current packet filter.
|
|
func (f forTest) GetFilter() *filter.Filter {
|
|
b := f.b
|
|
// Take b.mu so the read serializes with [LocalBackend.setControlClientStatusLocked],
|
|
// which installs the netmap and the filter at separate sub-steps. Without
|
|
// this, a test thread that observes the new netmap (via [LocalBackend.NetMapWithPeers])
|
|
// can race ahead of the filter store and read the previous filter.
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.currentNode().filterAtomic.Load()
|
|
}
|
|
|
|
// SetControlClientGetter sets the func that creates a control plane
|
|
// client. It can be called at most once, before Start.
|
|
func (f forTest) SetControlClientGetter(newControlClient func(controlclient.Options) (controlclient.Client, error)) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if b.ccGen != nil {
|
|
panic("invalid use of forTest.SetControlClientGetter after Start")
|
|
}
|
|
b.ccGen = newControlClient
|
|
}
|
|
|
|
// Peers returns all the current peers, sorted by Node.ID, for integration
|
|
// tests in another repo.
|
|
func (f forTest) Peers() []tailcfg.NodeView {
|
|
return f.b.currentNode().PeersForTest()
|
|
}
|
|
|
|
// AwaitNodeKey returns a channel that is closed once a peer with the given
|
|
// node key first appears in the current netmap. If the peer is already
|
|
// present, the returned channel is already closed. See
|
|
// [nodeBackend.AwaitNodeKeyForTest].
|
|
func (f forTest) AwaitNodeKey(k key.NodePublic) <-chan struct{} {
|
|
return f.b.currentNode().AwaitNodeKeyForTest(k)
|
|
}
|
|
|
|
// CurrentUser returns the current user and the associated WindowsUserID.
|
|
// It will be removed along with the rest of the "current user" functionality
|
|
// as we progress on the multi-user improvements (tailscale/corp#18342).
|
|
func (f forTest) CurrentUser() (ipn.WindowsUserID, ipnauth.Actor) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.pm.CurrentUserID(), b.currentUser
|
|
}
|
|
|
|
// ConfigureCerts sets a certificate retrieval function to be used by this
|
|
// local backend, skipping the usual ACME certificate registration.
|
|
func (f forTest) ConfigureCerts(getCert func(hostname string) (*TLSCertKeyPair, error)) {
|
|
b := f.b
|
|
cs := b.certState()
|
|
if cs == nil {
|
|
panic("forTest.ConfigureCerts called without cert extension registered")
|
|
}
|
|
b.mu.Lock()
|
|
cs.getCertForTest = getCert
|
|
b.mu.Unlock()
|
|
}
|
|
|
|
// SetPrefs replaces the current prefs with newp.
|
|
func (f forTest) SetPrefs(newp *ipn.Prefs) {
|
|
if newp == nil {
|
|
panic("forTest.SetPrefs got nil prefs")
|
|
}
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.setPrefsLocked(newp)
|
|
}
|