cmd/tailscale/cli: add tailscale exit-node suggest --force-probe

Add a new `--force-probe` flag to `tailscale exit-node suggest` that
waits for a routecheck.Refresh to finish before suggesting an exit
node.

This flag is currently hidden from the help text, but this flag is a
hint to the user that exit-node suggestions are based on routecheck
reachability reports.

Updates #17366
Updates tailscale/corp#33033

Signed-off-by: Simon Law <sfllaw@tailscale.com>
This commit is contained in:
Simon Law
2026-07-02 20:26:27 -07:00
committed by Simon Law
parent 932260511e
commit ca91eafce5
4 changed files with 99 additions and 3 deletions
+16
View File
@@ -4,12 +4,15 @@
package routecheck
import (
"context"
"errors"
"net/http"
"time"
jsonv2 "github.com/go-json-experiment/json"
jsonv1 "github.com/go-json-experiment/json/v1"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/localapi"
"tailscale.com/net/routecheck"
"tailscale.com/util/def"
@@ -18,6 +21,7 @@ import (
func init() {
localapi.Register("routecheck", serveRouteCheck)
localapi.HookRouteCheckRefresh.Set(routeCheckRefresh)
}
// ServeRouteCheck handles the API endpoint that serves the routecheck Report.
@@ -66,3 +70,15 @@ func clampRouteCheckTimeout(timeout time.Duration) time.Duration {
}
return min(max(0, timeout), 60*time.Second) // clamp to [0s, 60s]
}
// routeCheckRefresh is a localapi hook for refreshing the [routecheck.Client.Report].
// If the timeout is 0, all probes will timeout immediately.
// If the timeout is negative, then all probes will use the [DefaultTimeout].
func routeCheckRefresh(b *ipnlocal.LocalBackend, ctx context.Context, timeout time.Duration) error {
rc := ClientFor(b)
if rc == nil {
return errors.New("routecheck is not enabled")
}
_, err := rc.Refresh(ctx, clampRouteCheckTimeout(timeout))
return err
}