cmd/tailscale/cli: make push get peerapi base via localapi, not TSMP ping

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/1696/head
Brad Fitzpatrick 2021-04-13 08:50:16 -07:00
parent 1dc2cf4835
commit a08d978476
1 changed files with 27 additions and 46 deletions

View File

@ -13,7 +13,6 @@ import (
"io" "io"
"log" "log"
"mime" "mime"
"net"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -23,9 +22,8 @@ import (
"github.com/peterbourgon/ff/v2/ffcli" "github.com/peterbourgon/ff/v2/ffcli"
"golang.org/x/time/rate" "golang.org/x/time/rate"
"inet.af/netaddr"
"tailscale.com/client/tailscale" "tailscale.com/client/tailscale"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
) )
var pushCmd = &ffcli.Command{ var pushCmd = &ffcli.Command{
@ -63,11 +61,15 @@ func runPush(ctx context.Context, args []string) error {
return err return err
} }
peerAPIPort, err := discoverPeerAPIPort(ctx, ip) peerAPIBase, lastSeen, err := discoverPeerAPIBase(ctx, ip)
if err != nil { if err != nil {
return err return err
} }
if !lastSeen.IsZero() && time.Since(lastSeen) > lastSeenOld {
fmt.Fprintf(os.Stderr, "# warning: %s last seen %v ago\n", hostOrIP, time.Since(lastSeen).Round(time.Minute))
}
var fileContents io.Reader var fileContents io.Reader
var name = pushArgs.name var name = pushArgs.name
var contentLength int64 = -1 var contentLength int64 = -1
@ -103,7 +105,7 @@ func runPush(ctx context.Context, args []string) error {
} }
} }
dstURL := "http://" + net.JoinHostPort(ip, fmt.Sprint(peerAPIPort)) + "/v0/put/" + url.PathEscape(name) dstURL := peerAPIBase + "/v0/put/" + url.PathEscape(name)
req, err := http.NewRequestWithContext(ctx, "PUT", dstURL, fileContents) req, err := http.NewRequestWithContext(ctx, "PUT", dstURL, fileContents)
if err != nil { if err != nil {
return err return err
@ -124,51 +126,28 @@ func runPush(ctx context.Context, args []string) error {
return errors.New(res.Status) return errors.New(res.Status)
} }
func discoverPeerAPIPort(ctx context.Context, ip string) (port uint16, err error) { func discoverPeerAPIBase(ctx context.Context, ipStr string) (base string, lastSeen time.Time, err error) {
c, bc, ctx, cancel := connect(ctx) ip, err := netaddr.ParseIP(ipStr)
defer cancel() if err != nil {
return "", time.Time{}, err
prc := make(chan *ipnstate.PingResult, 2)
bc.SetNotifyCallback(func(n ipn.Notify) {
if n.ErrMessage != nil {
log.Fatal(*n.ErrMessage)
} }
if pr := n.PingResult; pr != nil && pr.IP == ip { fts, err := tailscale.FileTargets(ctx)
prc <- pr if err != nil {
return "", time.Time{}, err
} }
}) for _, ft := range fts {
go pump(ctx, bc, c) n := ft.Node
for _, a := range n.Addresses {
ticker := time.NewTicker(time.Second) if a.IP != ip {
defer ticker.Stop() continue
discoPings := 0
timer := time.NewTimer(10 * time.Second)
defer timer.Stop()
sendPings := func() {
bc.Ping(ip, false)
bc.Ping(ip, true)
} }
sendPings() if n.LastSeen != nil {
for { lastSeen = *n.LastSeen
select {
case <-ticker.C:
sendPings()
case <-timer.C:
return 0, fmt.Errorf("timeout contacting %v; it offline?", ip)
case pr := <-prc:
if p := pr.PeerAPIPort; p != 0 {
return p, nil
} }
discoPings++ return ft.PeerAPIURL, lastSeen, nil
if discoPings == 3 {
return 0, fmt.Errorf("%v is online, but seems to be running an old Tailscale version", ip)
}
case <-ctx.Done():
return 0, ctx.Err()
} }
} }
return "", time.Time{}, errors.New("target seems to be running an old Tailscale version")
} }
const maxSniff = 4 << 20 const maxSniff = 4 << 20
@ -213,6 +192,8 @@ func (r *slowReader) Read(p []byte) (n int, err error) {
return return
} }
const lastSeenOld = 20 * time.Minute
func runPushTargets(ctx context.Context, args []string) error { func runPushTargets(ctx context.Context, args []string) error {
if len(args) > 0 { if len(args) > 0 {
return errors.New("invalid arguments with --targets") return errors.New("invalid arguments with --targets")
@ -227,7 +208,7 @@ func runPushTargets(ctx context.Context, args []string) error {
if n.LastSeen == nil { if n.LastSeen == nil {
ago = "\tnode never seen" ago = "\tnode never seen"
} else { } else {
if d := time.Since(*n.LastSeen); d > 20*time.Minute { if d := time.Since(*n.LastSeen); d > lastSeenOld {
ago = fmt.Sprintf("\tlast seen %v ago", d.Round(time.Minute)) ago = fmt.Sprintf("\tlast seen %v ago", d.Round(time.Minute))
} }
} }