Compare commits

...

1 Commits

Author SHA1 Message Date
Claire Wang
6ae633b689 tstime: replace time.NewTimer with tstime.Clock timer
Updates #8587

Signed-off-by: Claire Wang <claire@tailscale.com>
2023-07-12 13:53:32 -04:00
23 changed files with 113 additions and 69 deletions

View File

@@ -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...)

View File

@@ -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 {

View File

@@ -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: ")

View File

@@ -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:

View File

@@ -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,

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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:
}
}

View File

@@ -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")
}
})

View File

@@ -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:
}
}

View File

@@ -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

View File

@@ -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():

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -673,6 +673,7 @@ func NewConn(opts Options) (*Conn, error) {
SkipExternalNetwork: inTest(),
PortMapper: c.portMapper,
UseDNSCache: true,
Clock: &tstime.StdClock{},
}
c.ignoreSTUNPackets()

View File

@@ -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())