summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@tailscale.com>2021-04-21 14:17:21 -0700
committerBrad Fitzpatrick <bradfitz@tailscale.com>2021-04-21 14:40:05 -0700
commit664f490bf1fcb55417b9e0b1eca8d46fb6272cd5 (patch)
treee5ad0ecf2bbb4c72bdff9010bbe69d7747c30352
parent2f17a34242bd73036a3c55a02caf9ad07f60fb45 (diff)
downloadtailscale-bradfitz/sleep.tar.xz
tailscale-bradfitz/sleep.zip
control/controlclient, tailcfg: add Debug.SleepSeconds (mapver 19)bradfitz/sleep
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
-rw-r--r--control/controlclient/direct.go39
-rw-r--r--tailcfg/tailcfg.go9
2 files changed, 47 insertions, 1 deletions
diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go
index 96bd268b4..726d86fd7 100644
--- a/control/controlclient/direct.go
+++ b/control/controlclient/direct.go
@@ -795,6 +795,45 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
}
setControlAtomic(&controlUseDERPRoute, resp.Debug.DERPRoute)
setControlAtomic(&controlTrimWGConfig, resp.Debug.TrimWGConfig)
+ if sleep := time.Duration(resp.Debug.SleepSeconds * float64(time.Second)); sleep > 0 {
+ const maxSleep = 5 * time.Minute
+ // SleepFor sleeps for d, but keeps the poll timeout from
+ // firing while we're sleeeping.
+ sleepFor := func(d time.Duration) {
+ ticker := time.NewTicker(pollTimeout / 2)
+ defer ticker.Stop()
+ timer := time.NewTimer(d)
+ defer timer.Stop()
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-timer.C:
+ return
+ case <-ticker.C:
+ select {
+ case timeoutReset <- struct{}{}:
+ case <-timer.C:
+ return
+ case <-ctx.Done():
+ return
+ }
+ }
+ }
+ }
+ if sleep > maxSleep {
+ c.logf("sleeping for %v, capped from server-requested %v ...", maxSleep, sleep)
+ sleepFor(maxSleep)
+ } else {
+ c.logf("sleeping for server-requested %v ...", sleep)
+ sleepFor(sleep)
+ }
+ select {
+ default:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
}
nm := sess.netmapForResponse(&resp)
diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go
index 81419040a..1c49a19dd 100644
--- a/tailcfg/tailcfg.go
+++ b/tailcfg/tailcfg.go
@@ -41,7 +41,8 @@ import (
// 16: 2021-04-15: client understands Node.Online, MapResponse.OnlineChange
// 17: 2021-04-18: MapResponse.Domain empty means unchanged
// 18: 2021-04-19: MapResponse.Node nil means unchanged (all fields now omitempty)
-const CurrentMapRequestVersion = 18
+// 19: 2021-04-21: MapResponse.Debug.SleepSeconds
+const CurrentMapRequestVersion = 19
type StableID string
@@ -1012,6 +1013,12 @@ type Debug struct {
// GoroutineDumpURL, if non-empty, requests that the client do
// a one-time dump of its active goroutines to the given URL.
GoroutineDumpURL string `json:",omitempty"`
+
+ // SleepSeconds requests that the client sleep for the
+ // provided number of seconds.
+ // The client can (and should) limit the value (such as 5
+ // minutes).
+ SleepSeconds float64 `json:",omitempty"`
}
func (k MachineKey) String() string { return fmt.Sprintf("mkey:%x", k[:]) }