Compare commits
1 Commits
irbekrm/fi
...
clairew/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ae633b689 |
@@ -23,11 +23,13 @@ import (
|
||||
"tailscale.com/client/tailscale"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/paths"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
|
||||
var Stderr io.Writer = os.Stderr
|
||||
var Stdout io.Writer = os.Stdout
|
||||
var clock tstime.Clock = &tstime.StdClock{} // global tstime.clock variable for tailscale cli package.
|
||||
|
||||
func errf(format string, a ...any) {
|
||||
fmt.Fprintf(Stderr, format, a...)
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"tailscale.com/paths"
|
||||
"tailscale.com/safesocket"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/must"
|
||||
@@ -678,6 +679,7 @@ func runTS2021(ctx context.Context, args []string) error {
|
||||
ProtocolVersion: uint16(ts2021Args.version),
|
||||
Dialer: dialFunc,
|
||||
Logf: logf,
|
||||
Clock: &tstime.StdClock{},
|
||||
}).Dial(ctx)
|
||||
log.Printf("controlhttp.Dial = %p, %v", conn, err)
|
||||
if err != nil {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/portmapper"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
@@ -54,7 +55,8 @@ func runNetcheck(ctx context.Context, args []string) error {
|
||||
c := &netcheck.Client{
|
||||
UDPBindAddr: envknob.String("TS_DEBUG_NETCHECK_UDP_BIND"),
|
||||
PortMapper: portmapper.NewClient(logf, netMon, nil, nil),
|
||||
UseDNSCache: false, // always resolve, don't cache
|
||||
UseDNSCache: false, // always resolve, don't cache,
|
||||
Clock: &tstime.StdClock{},
|
||||
}
|
||||
if netcheckArgs.verbose {
|
||||
c.Logf = logger.WithPrefix(log.Printf, "netcheck: ")
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/safesocket"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/preftype"
|
||||
"tailscale.com/util/dnsname"
|
||||
@@ -692,9 +693,9 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
|
||||
// shuts down. (Issue 2333)
|
||||
var timeoutCh <-chan time.Time
|
||||
if upArgs.timeout > 0 {
|
||||
timeoutTimer := time.NewTimer(upArgs.timeout)
|
||||
var timeoutTimer tstime.TimerController
|
||||
timeoutTimer, timeoutCh = clock.NewTimer(upArgs.timeout)
|
||||
defer timeoutTimer.Stop()
|
||||
timeoutCh = timeoutTimer.C
|
||||
}
|
||||
select {
|
||||
case <-running:
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"tailscale.com/logtail/backoff"
|
||||
"tailscale.com/net/sockstats"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/empty"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
@@ -48,7 +49,7 @@ var _ Client = (*Auto)(nil)
|
||||
// It's a concrete implementation of the Client interface.
|
||||
type Auto struct {
|
||||
direct *Direct // our interface to the server APIs
|
||||
timeNow func() time.Time
|
||||
clock tstime.Clock
|
||||
logf logger.Logf
|
||||
expiry *time.Time
|
||||
closed bool
|
||||
@@ -107,12 +108,12 @@ func NewNoStart(opts Options) (_ *Auto, err error) {
|
||||
if opts.Logf == nil {
|
||||
opts.Logf = func(fmt string, args ...any) {}
|
||||
}
|
||||
if opts.TimeNow == nil {
|
||||
opts.TimeNow = time.Now
|
||||
if opts.Clock == nil {
|
||||
opts.Clock = &tstime.StdClock{}
|
||||
}
|
||||
c := &Auto{
|
||||
direct: direct,
|
||||
timeNow: opts.TimeNow,
|
||||
clock: opts.Clock,
|
||||
logf: opts.Logf,
|
||||
newMapCh: make(chan struct{}, 1),
|
||||
quit: make(chan struct{}),
|
||||
@@ -702,14 +703,14 @@ func (c *Auto) Logout(ctx context.Context) error {
|
||||
c.mu.Unlock()
|
||||
c.cancelAuth()
|
||||
|
||||
timer := time.NewTimer(10 * time.Second)
|
||||
timer, timerChannel := c.clock.NewTimer(10 * time.Second)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case err := <-errc:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
case <-timerChannel:
|
||||
return context.DeadlineExceeded
|
||||
}
|
||||
}
|
||||
@@ -770,7 +771,7 @@ func (c *Auto) TestOnlySetAuthKey(authkey string) {
|
||||
}
|
||||
|
||||
func (c *Auto) TestOnlyTimeNow() time.Time {
|
||||
return c.timeNow()
|
||||
return c.clock.Now()
|
||||
}
|
||||
|
||||
// SetDNS sends the SetDNSRequest request to the control plane server,
|
||||
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tka"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/netmap"
|
||||
@@ -63,7 +64,7 @@ type Direct struct {
|
||||
dialer *tsdial.Dialer
|
||||
dnsCache *dnscache.Resolver
|
||||
serverURL string // URL of the tailcontrol server
|
||||
timeNow func() time.Time
|
||||
clock tstime.Clock
|
||||
lastPrintMap time.Time
|
||||
newDecompressor func() (Decompressor, error)
|
||||
keepAlive bool
|
||||
@@ -105,8 +106,8 @@ type Options struct {
|
||||
GetMachinePrivateKey func() (key.MachinePrivate, error) // returns the machine key to use
|
||||
ServerURL string // URL of the tailcontrol server
|
||||
AuthKey string // optional node auth key for auto registration
|
||||
TimeNow func() time.Time // time.Now implementation used by Client
|
||||
Hostinfo *tailcfg.Hostinfo // non-nil passes ownership, nil means to use default using os.Hostname, etc
|
||||
Clock tstime.Clock
|
||||
Hostinfo *tailcfg.Hostinfo // non-nil passes ownership, nil means to use default using os.Hostname, etc
|
||||
DiscoPublicKey key.DiscoPublic
|
||||
NewDecompressor func() (Decompressor, error)
|
||||
KeepAlive bool
|
||||
@@ -191,8 +192,8 @@ func NewDirect(opts Options) (*Direct, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.TimeNow == nil {
|
||||
opts.TimeNow = time.Now
|
||||
if opts.Clock == nil {
|
||||
opts.Clock = &tstime.StdClock{}
|
||||
}
|
||||
if opts.Logf == nil {
|
||||
// TODO(apenwarr): remove this default and fail instead.
|
||||
@@ -235,7 +236,7 @@ func NewDirect(opts Options) (*Direct, error) {
|
||||
httpc: httpc,
|
||||
getMachinePrivKey: opts.GetMachinePrivateKey,
|
||||
serverURL: opts.ServerURL,
|
||||
timeNow: opts.TimeNow,
|
||||
clock: opts.Clock,
|
||||
logf: opts.Logf,
|
||||
newDecompressor: opts.NewDecompressor,
|
||||
keepAlive: opts.KeepAlive,
|
||||
@@ -432,7 +433,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
||||
authKey, isWrapped, wrappedSig, wrappedKey := decodeWrappedAuthkey(c.authKey, c.logf)
|
||||
hi := c.hostInfoLocked()
|
||||
backendLogID := hi.BackendLogID
|
||||
expired := c.expiry != nil && !c.expiry.IsZero() && c.expiry.Before(c.timeNow())
|
||||
expired := c.expiry != nil && !c.expiry.IsZero() && c.expiry.Before(c.clock.Now())
|
||||
c.mu.Unlock()
|
||||
|
||||
machinePrivKey, err := c.getMachinePrivKey()
|
||||
@@ -947,7 +948,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
|
||||
return nil
|
||||
}
|
||||
|
||||
timeout := time.NewTimer(pollTimeout)
|
||||
timeout, timeoutChannel := c.clock.NewTimer(pollTimeout)
|
||||
timeoutReset := make(chan struct{})
|
||||
pollDone := make(chan struct{})
|
||||
defer close(pollDone)
|
||||
@@ -957,14 +958,14 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
|
||||
case <-pollDone:
|
||||
vlogf("netmap: ending timeout goroutine")
|
||||
return
|
||||
case <-timeout.C:
|
||||
case <-timeoutChannel:
|
||||
c.logf("map response long-poll timed out!")
|
||||
cancel()
|
||||
return
|
||||
case <-timeoutReset:
|
||||
if !timeout.Stop() {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
case <-timeoutChannel:
|
||||
case <-pollDone:
|
||||
vlogf("netmap: ending timeout goroutine")
|
||||
return
|
||||
@@ -1089,7 +1090,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
|
||||
go dumpGoroutinesToURL(c.httpc, resp.Debug.GoroutineDumpURL)
|
||||
}
|
||||
if sleep := time.Duration(resp.Debug.SleepSeconds * float64(time.Second)); sleep > 0 {
|
||||
if err := sleepAsRequested(ctx, c.logf, timeoutReset, sleep); err != nil {
|
||||
if err := sleepAsRequested(ctx, c.logf, timeoutReset, sleep, c.clock); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1119,7 +1120,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
|
||||
// This is handy for debugging, and our logs processing
|
||||
// pipeline depends on it. (TODO: Remove this dependency.)
|
||||
// Code elsewhere prints netmap diffs every time they are received.
|
||||
now := c.timeNow()
|
||||
now := c.clock.Now()
|
||||
if now.Sub(c.lastPrintMap) >= 5*time.Minute {
|
||||
c.lastPrintMap = now
|
||||
c.logf("[v1] new network map[%d]:\n%s", i, nm.VeryConcise())
|
||||
@@ -1459,7 +1460,7 @@ func answerC2NPing(logf logger.Logf, c2nHandler http.Handler, c *http.Client, pr
|
||||
}
|
||||
}
|
||||
|
||||
func sleepAsRequested(ctx context.Context, logf logger.Logf, timeoutReset chan<- struct{}, d time.Duration) error {
|
||||
func sleepAsRequested(ctx context.Context, logf logger.Logf, timeoutReset chan<- struct{}, d time.Duration, clock tstime.Clock) error {
|
||||
const maxSleep = 5 * time.Minute
|
||||
if d > maxSleep {
|
||||
logf("sleeping for %v, capped from server-requested %v ...", maxSleep, d)
|
||||
@@ -1468,20 +1469,20 @@ func sleepAsRequested(ctx context.Context, logf logger.Logf, timeoutReset chan<-
|
||||
logf("sleeping for server-requested %v ...", d)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(pollTimeout / 2)
|
||||
ticker, tickerChannel := clock.NewTicker(pollTimeout / 2)
|
||||
defer ticker.Stop()
|
||||
timer := time.NewTimer(d)
|
||||
timer, timerChannel := clock.NewTimer(d)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
case <-timerChannel:
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
case <-tickerChannel:
|
||||
select {
|
||||
case timeoutReset <- struct{}{}:
|
||||
case <-timer.C:
|
||||
case <-timerChannel:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/mak"
|
||||
@@ -450,6 +451,7 @@ func (nc *NoiseClient) dial(ctx context.Context) (*noiseConn, error) {
|
||||
DialPlan: dialPlan,
|
||||
Logf: nc.logf,
|
||||
NetMon: nc.netMon,
|
||||
Clock: &tstime.StdClock{},
|
||||
}).Dial(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -147,13 +147,13 @@ func (a *Dialer) dial(ctx context.Context) (*ClientConn, error) {
|
||||
// before we do anything.
|
||||
if c.DialStartDelaySec > 0 {
|
||||
a.logf("[v2] controlhttp: waiting %.2f seconds before dialing %q @ %v", c.DialStartDelaySec, a.Hostname, c.IP)
|
||||
tmr := time.NewTimer(time.Duration(c.DialStartDelaySec * float64(time.Second)))
|
||||
tmr, tmrChannel := a.Clock.NewTimer(time.Duration(c.DialStartDelaySec * float64(time.Second)))
|
||||
defer tmr.Stop()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
case <-tmr.C:
|
||||
case <-tmrChannel:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ func (a *Dialer) dialHost(ctx context.Context, addr netip.Addr) (*ClientConn, er
|
||||
|
||||
// In case outbound port 80 blocked or MITM'ed poorly, start a backup timer
|
||||
// to dial port 443 if port 80 doesn't either succeed or fail quickly.
|
||||
try443Timer := time.AfterFunc(a.httpsFallbackDelay(), func() { try(u443) })
|
||||
try443Timer := a.Clock.AfterFunc(a.httpsFallbackDelay(), func() { try(u443) })
|
||||
defer try443Timer.Stop()
|
||||
|
||||
var err80, err443 error
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"tailscale.com/net/dnscache"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
@@ -89,6 +90,8 @@ type Dialer struct {
|
||||
drainFinished chan struct{}
|
||||
omitCertErrorLogging bool
|
||||
testFallbackDelay time.Duration
|
||||
|
||||
Clock tstime.Clock
|
||||
}
|
||||
|
||||
func strDef(v1, v2 string) string {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"tailscale.com/net/socks5"
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
@@ -204,6 +205,7 @@ func testControlHTTP(t *testing.T, param httpTestParam) {
|
||||
Logf: t.Logf,
|
||||
omitCertErrorLogging: true,
|
||||
testFallbackDelay: 50 * time.Millisecond,
|
||||
Clock: &tstest.Clock{},
|
||||
}
|
||||
|
||||
if proxy != nil {
|
||||
@@ -660,6 +662,7 @@ func TestDialPlan(t *testing.T) {
|
||||
drainFinished: drained,
|
||||
omitCertErrorLogging: true,
|
||||
testFallbackDelay: 50 * time.Millisecond,
|
||||
Clock: &tstest.Clock{},
|
||||
}
|
||||
|
||||
conn, err := a.dial(ctx)
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"golang.org/x/time/rate"
|
||||
"tailscale.com/disco"
|
||||
"tailscale.com/net/memnet"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
@@ -267,6 +268,7 @@ func TestSendRecv(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSendFreeze(t *testing.T) {
|
||||
clock := &tstest.Clock{}
|
||||
serverPrivateKey := key.NewNode()
|
||||
s := NewServer(serverPrivateKey, t.Logf)
|
||||
defer s.Close()
|
||||
@@ -398,14 +400,14 @@ func TestSendFreeze(t *testing.T) {
|
||||
}
|
||||
drain := func(t *testing.T, name string) bool {
|
||||
t.Helper()
|
||||
timer := time.NewTimer(1 * time.Second)
|
||||
timer, timerChannel := clock.NewTimer(1 * time.Second)
|
||||
defer timer.Stop()
|
||||
|
||||
// Ensure ch has at least one element.
|
||||
ch := chs(name)
|
||||
select {
|
||||
case <-ch:
|
||||
case <-timer.C:
|
||||
case <-timerChannel:
|
||||
t.Errorf("no packet received by %s", name)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"tailscale.com/net/tshttpproxy"
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/cmpx"
|
||||
@@ -83,6 +84,7 @@ type Client struct {
|
||||
serverPubKey key.NodePublic
|
||||
tlsState *tls.ConnectionState
|
||||
pingOut map[derp.PingMessage]chan<- bool // chan to send to on pong
|
||||
clock tstime.Clock
|
||||
}
|
||||
|
||||
func (c *Client) String() string {
|
||||
@@ -101,6 +103,7 @@ func NewRegionClient(privateKey key.NodePrivate, logf logger.Logf, netMon *netmo
|
||||
getRegion: getRegion,
|
||||
ctx: ctx,
|
||||
cancelCtx: cancel,
|
||||
clock: &tstime.StdClock{},
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -108,7 +111,7 @@ func NewRegionClient(privateKey key.NodePrivate, logf logger.Logf, netMon *netmo
|
||||
// NewNetcheckClient returns a Client that's only able to have its DialRegionTLS method called.
|
||||
// It's used by the netcheck package.
|
||||
func NewNetcheckClient(logf logger.Logf) *Client {
|
||||
return &Client{logf: logf}
|
||||
return &Client{logf: logf, clock: &tstime.StdClock{}}
|
||||
}
|
||||
|
||||
// NewClient returns a new DERP-over-HTTP client. It connects lazily.
|
||||
@@ -129,6 +132,7 @@ func NewClient(privateKey key.NodePrivate, serverURL string, logf logger.Logf) (
|
||||
url: u,
|
||||
ctx: ctx,
|
||||
cancelCtx: cancel,
|
||||
clock: &tstime.StdClock{},
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
@@ -644,14 +648,14 @@ func (c *Client) dialNode(ctx context.Context, n *tailcfg.DERPNode) (net.Conn, e
|
||||
nwait++
|
||||
go func() {
|
||||
if proto == "tcp4" && c.preferIPv6() {
|
||||
t := time.NewTimer(200 * time.Millisecond)
|
||||
t, tChannel := c.clock.NewTimer(200 * time.Millisecond)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Either user canceled original context,
|
||||
// it timed out, or the v6 dial succeeded.
|
||||
t.Stop()
|
||||
return
|
||||
case <-t.C:
|
||||
case <-tChannel:
|
||||
// Start v4 dial
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,11 +91,11 @@ func (c *Client) RunWatchConnectionLoop(ctx context.Context, ignoreServerKey key
|
||||
}
|
||||
|
||||
sleep := func(d time.Duration) {
|
||||
t := time.NewTimer(d)
|
||||
t, tChannel := c.clock.NewTimer(d)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
case <-t.C:
|
||||
case <-tChannel:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ import (
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tka"
|
||||
"tailscale.com/tsd"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/empty"
|
||||
"tailscale.com/types/key"
|
||||
@@ -259,6 +260,7 @@ type LocalBackend struct {
|
||||
// tkaSyncLock MUST be taken before mu (or inversely, mu must not be held
|
||||
// at the moment that tkaSyncLock is taken).
|
||||
tkaSyncLock sync.Mutex
|
||||
clock tstime.Clock
|
||||
}
|
||||
|
||||
// clientGen is a func that creates a control plane client.
|
||||
@@ -311,6 +313,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
||||
em: newExpiryManager(logf),
|
||||
gotPortPollRes: make(chan struct{}),
|
||||
loginFlags: loginFlags,
|
||||
clock: &tstime.StdClock{},
|
||||
}
|
||||
|
||||
netMon := sys.NetMon.Get()
|
||||
@@ -1380,12 +1383,12 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||
// HTTP request (via doSetHostinfoFilterServices >
|
||||
// cli.SetHostinfo). In practice this is very quick.
|
||||
t0 := time.Now()
|
||||
timer := time.NewTimer(time.Second)
|
||||
timer, timerChannel := b.clock.NewTimer(time.Second)
|
||||
select {
|
||||
case <-b.gotPortPollRes:
|
||||
b.logf("[v1] got initial portlist info in %v", time.Since(t0).Round(time.Millisecond))
|
||||
timer.Stop()
|
||||
case <-timer.C:
|
||||
case <-timerChannel:
|
||||
b.logf("timeout waiting for initial portlist")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
@@ -23,10 +24,7 @@ type Backoff struct {
|
||||
// logf is the function used for log messages when backing off.
|
||||
logf logger.Logf
|
||||
|
||||
// NewTimer is the function that acts like time.NewTimer.
|
||||
// It's for use in unit tests.
|
||||
NewTimer func(time.Duration) *time.Timer
|
||||
|
||||
Clock tstime.Clock // use tstime.Clock for NewTimer
|
||||
// LogLongerThan sets the minimum time of a single backoff interval
|
||||
// before we mention it in the log.
|
||||
LogLongerThan time.Duration
|
||||
@@ -40,7 +38,7 @@ func NewBackoff(name string, logf logger.Logf, maxBackoff time.Duration) *Backof
|
||||
name: name,
|
||||
logf: logf,
|
||||
maxBackoff: maxBackoff,
|
||||
NewTimer: time.NewTimer,
|
||||
Clock: &tstime.StdClock{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,10 +70,10 @@ func (b *Backoff) BackOff(ctx context.Context, err error) {
|
||||
if d >= b.LogLongerThan {
|
||||
b.logf("%s: [v1] backoff: %d msec", b.name, d.Milliseconds())
|
||||
}
|
||||
t := b.NewTimer(d)
|
||||
t, tChannel := b.Clock.NewTimer(d)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
case <-t.C:
|
||||
case <-tChannel:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/net/sockstats"
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/nettype"
|
||||
@@ -200,6 +201,7 @@ type forwarder struct {
|
||||
// /etc/resolv.conf is missing/corrupt, and the peerapi ExitDNS stub
|
||||
// resolver lookup.
|
||||
cloudHostFallback []resolverAndDelay
|
||||
clock tstime.Clock
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -212,6 +214,7 @@ func newForwarder(logf logger.Logf, netMon *netmon.Monitor, linkSel ForwardLinkS
|
||||
netMon: netMon,
|
||||
linkSel: linkSel,
|
||||
dialer: dialer,
|
||||
clock: &tstime.StdClock{},
|
||||
}
|
||||
f.ctx, f.ctxCancel = context.WithCancel(context.Background())
|
||||
return f
|
||||
@@ -695,9 +698,9 @@ func (f *forwarder) forwardWithDestChan(ctx context.Context, query packet, respo
|
||||
for i := range resolvers {
|
||||
go func(rr *resolverAndDelay) {
|
||||
if rr.startDelay > 0 {
|
||||
timer := time.NewTimer(rr.startDelay)
|
||||
timer, timerChannel := f.clock.NewTimer(rr.startDelay)
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-timerChannel:
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/cloudenv"
|
||||
"tailscale.com/util/singleflight"
|
||||
@@ -397,6 +398,7 @@ func (d *dialer) DialContext(ctx context.Context, network, address string) (retC
|
||||
address: address,
|
||||
host: host,
|
||||
port: port,
|
||||
clock: &tstime.StdClock{},
|
||||
}
|
||||
defer func() {
|
||||
// On failure, consider that our DNS might be wrong and ask the DNS fallback mechanism for
|
||||
@@ -471,6 +473,7 @@ type dialCall struct {
|
||||
|
||||
mu sync.Mutex // lock ordering: dialer.mu, then dialCall.mu
|
||||
fails map[netip.Addr]error // set of IPs that failed to dial thus far
|
||||
clock tstime.Clock
|
||||
}
|
||||
|
||||
// dnsWasTrustworthy reports whether we think the IP address(es) we
|
||||
@@ -585,9 +588,9 @@ func (dc *dialCall) raceDial(ctx context.Context, ips []netip.Addr) (net.Conn, e
|
||||
go func() {
|
||||
for i, ip := range ips {
|
||||
if i != 0 {
|
||||
timer := time.NewTimer(fallbackDelay)
|
||||
timer, timerChannel := dc.clock.NewTimer(fallbackDelay)
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-timerChannel:
|
||||
case <-failBoost:
|
||||
timer.Stop()
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"tailscale.com/net/stun"
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/nettype"
|
||||
"tailscale.com/types/opt"
|
||||
@@ -166,8 +167,7 @@ type Client struct {
|
||||
// If nil, the interface will be looked up dynamically.
|
||||
NetMon *netmon.Monitor
|
||||
|
||||
// TimeNow, if non-nil, is used instead of time.Now.
|
||||
TimeNow func() time.Time
|
||||
Clock tstime.Clock // use tstime.Clock.Now() instead of time.Now
|
||||
|
||||
// GetSTUNConn4 optionally provides a func to return the
|
||||
// connection to use for sending & receiving IPv4 packets. If
|
||||
@@ -1013,11 +1013,11 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
|
||||
}(probeSet)
|
||||
}
|
||||
|
||||
stunTimer := time.NewTimer(stunProbeTimeout)
|
||||
stunTimer, stunTimerChannel := c.Clock.NewTimer(stunProbeTimeout)
|
||||
defer stunTimer.Stop()
|
||||
|
||||
select {
|
||||
case <-stunTimer.C:
|
||||
case <-stunTimerChannel:
|
||||
case <-ctx.Done():
|
||||
case <-wg.DoneChan():
|
||||
// All of our probes finished, so if we have >0 responses, we
|
||||
@@ -1436,8 +1436,8 @@ func (c *Client) logConciseReport(r *Report, dm *tailcfg.DERPMap) {
|
||||
}
|
||||
|
||||
func (c *Client) timeNow() time.Time {
|
||||
if c.TimeNow != nil {
|
||||
return c.TimeNow()
|
||||
if c.Clock != nil {
|
||||
return c.Clock.Now()
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
@@ -1531,9 +1531,9 @@ func (rs *reportState) runProbe(ctx context.Context, dm *tailcfg.DERPMap, probe
|
||||
}
|
||||
|
||||
if probe.delay > 0 {
|
||||
delayTimer := time.NewTimer(probe.delay)
|
||||
delayTimer, delayTimerChannel := c.Clock.NewTimer(probe.delay)
|
||||
select {
|
||||
case <-delayTimer.C:
|
||||
case <-delayTimerChannel:
|
||||
case <-ctx.Done():
|
||||
delayTimer.Stop()
|
||||
return
|
||||
|
||||
@@ -161,6 +161,7 @@ func TestBasic(t *testing.T) {
|
||||
c := &Client{
|
||||
Logf: t.Logf,
|
||||
UDPBindAddr: "127.0.0.1:0",
|
||||
Clock: &tstest.Clock{},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
@@ -200,7 +201,8 @@ func TestWorksWhenUDPBlocked(t *testing.T) {
|
||||
dm.Regions[1].Nodes[0].STUNOnly = true
|
||||
|
||||
c := &Client{
|
||||
Logf: t.Logf,
|
||||
Logf: t.Logf,
|
||||
Clock: &tstest.Clock{},
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond)
|
||||
defer cancel()
|
||||
@@ -340,10 +342,10 @@ func TestAddReportHistoryAndSetPreferredDERP(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fakeTime := time.Unix(123, 0)
|
||||
c := &Client{
|
||||
TimeNow: func() time.Time { return fakeTime },
|
||||
Clock: tstest.NewClock(tstest.ClockOpts{Start: fakeTime}),
|
||||
}
|
||||
for _, s := range tt.steps {
|
||||
fakeTime = fakeTime.Add(s.after)
|
||||
c.Clock.(*tstest.Clock).Advance(s.after)
|
||||
c.addReportHistoryAndSetPreferredDERP(s.r)
|
||||
}
|
||||
lastReport := tt.steps[len(tt.steps)-1].r
|
||||
@@ -800,6 +802,7 @@ func TestNoCaptivePortalWhenUDP(t *testing.T) {
|
||||
// Set the delay long enough that we have time to cancel it
|
||||
// when our STUN probe succeeds.
|
||||
testCaptivePortalDelay: 10 * time.Second,
|
||||
Clock: &tstest.Clock{},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
|
||||
@@ -49,6 +49,7 @@ import (
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/tsd"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/logid"
|
||||
"tailscale.com/types/nettype"
|
||||
@@ -1029,7 +1030,8 @@ func (s *Server) listen(network, addr string, lnOn listenOn) (net.Listener, erro
|
||||
keys: keys,
|
||||
addr: addr,
|
||||
|
||||
conn: make(chan net.Conn),
|
||||
conn: make(chan net.Conn),
|
||||
clock: &tstime.StdClock{},
|
||||
}
|
||||
s.mu.Lock()
|
||||
for _, key := range keys {
|
||||
@@ -1061,6 +1063,7 @@ type listener struct {
|
||||
addr string
|
||||
conn chan net.Conn
|
||||
closed bool // guarded by s.mu
|
||||
clock tstime.Clock
|
||||
}
|
||||
|
||||
func (ln *listener) Accept() (net.Conn, error) {
|
||||
@@ -1096,11 +1099,11 @@ func (ln *listener) closeLocked() error {
|
||||
}
|
||||
|
||||
func (ln *listener) handle(c net.Conn) {
|
||||
t := time.NewTimer(time.Second)
|
||||
t, tChannel := ln.clock.NewTimer(time.Second)
|
||||
defer t.Stop()
|
||||
select {
|
||||
case ln.conn <- c:
|
||||
case <-t.C:
|
||||
case <-tChannel:
|
||||
// TODO(bradfitz): this isn't ideal. Think about how
|
||||
// we how we want to do pushback.
|
||||
c.Close()
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"context"
|
||||
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/tstime"
|
||||
)
|
||||
|
||||
// Logf is the basic Tailscale logger type: a printf-like func.
|
||||
@@ -47,6 +48,8 @@ var jencPool = &sync.Pool{New: func() any {
|
||||
return je
|
||||
}}
|
||||
|
||||
var clock tstime.Clock = &tstime.StdClock{}
|
||||
|
||||
// JSON marshals v as JSON and writes it to logf formatted with the annotation to make logtail
|
||||
// treat it as a structured log.
|
||||
//
|
||||
@@ -259,10 +262,10 @@ func SlowLoggerWithClock(ctx context.Context, logf Logf, f time.Duration, burst
|
||||
|
||||
// Otherwise, sleep for 2x the duration so that we don't
|
||||
// immediately sleep again on the next call.
|
||||
tmr := time.NewTimer(2 * f)
|
||||
tmr, tmrChannel := clock.NewTimer(2 * f)
|
||||
defer tmr.Stop()
|
||||
select {
|
||||
case curr := <-tmr.C:
|
||||
case curr := <-tmrChannel:
|
||||
tb.AdvanceTo(curr)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
@@ -673,6 +673,7 @@ func NewConn(opts Options) (*Conn, error) {
|
||||
SkipExternalNetwork: inTest(),
|
||||
PortMapper: c.portMapper,
|
||||
UseDNSCache: true,
|
||||
Clock: &tstime.StdClock{},
|
||||
}
|
||||
|
||||
c.ignoreSTUNPackets()
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/net/dns"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/wgengine/capture"
|
||||
@@ -40,6 +41,7 @@ func NewWatchdog(e Engine) Engine {
|
||||
fatalf: log.Fatalf,
|
||||
maxWait: 45 * time.Second,
|
||||
inFlight: make(map[inFlightKey]time.Time),
|
||||
clock: &tstime.StdClock{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +60,8 @@ type watchdogEngine struct {
|
||||
inFlightMu sync.Mutex
|
||||
inFlight map[inFlightKey]time.Time
|
||||
inFlightCtr uint64
|
||||
|
||||
clock tstime.Clock
|
||||
}
|
||||
|
||||
func (e *watchdogEngine) watchdogErr(name string, fn func() error) error {
|
||||
@@ -82,12 +86,12 @@ func (e *watchdogEngine) watchdogErr(name string, fn func() error) error {
|
||||
go func() {
|
||||
errCh <- fn()
|
||||
}()
|
||||
t := time.NewTimer(e.maxWait)
|
||||
t, tChannel := e.clock.NewTimer(e.maxWait)
|
||||
select {
|
||||
case err := <-errCh:
|
||||
t.Stop()
|
||||
return err
|
||||
case <-t.C:
|
||||
case <-tChannel:
|
||||
buf := new(strings.Builder)
|
||||
pprof.Lookup("goroutine").WriteTo(buf, 1)
|
||||
e.logf("wgengine watchdog stacks:\n%s", buf.String())
|
||||
|
||||
Reference in New Issue
Block a user