From a83f08c54b9b0a12d2e78c130d8acab39d71ae0a Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 13 Sep 2021 17:35:55 -0700 Subject: [PATCH] cmd/tailscale: provide a better error message when tailscaled isn't running Fixes #2797 Signed-off-by: Brad Fitzpatrick --- client/tailscale/tailscale.go | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/client/tailscale/tailscale.go b/client/tailscale/tailscale.go index e74c8b3fe..d8b1a20fa 100644 --- a/client/tailscale/tailscale.go +++ b/client/tailscale/tailscale.go @@ -17,6 +17,8 @@ "net" "net/http" "net/url" + "os/exec" + "runtime" "strconv" "strings" "time" @@ -101,6 +103,15 @@ func send(ctx context.Context, method, path string, wantStatus int, body io.Read } res, err := DoLocalRequest(req) if err != nil { + if ue, ok := err.(*url.Error); ok { + if oe, ok := ue.Err.(*net.OpError); ok && oe.Op == "dial" { + pathPrefix := path + if i := strings.Index(path, "?"); i != -1 { + pathPrefix = path[:i] + } + return nil, fmt.Errorf("Failed to connect to local Tailscale daemon for %s; %s Error: %w", pathPrefix, tailscaledConnectHint(), oe) + } + } return nil, err } defer res.Body.Close() @@ -375,3 +386,33 @@ func ExpandSNIName(ctx context.Context, name string) (fqdn string, ok bool) { } return "", false } + +// tailscaledConnectHint gives a little thing about why tailscaled (or +// platform equivalent) is not answering localapi connections. +// +// It ends in a punctuation. See caller. +func tailscaledConnectHint() string { + if runtime.GOOS != "linux" { + // TODO(bradfitz): flesh this out + return "not running?" + } + out, err := exec.Command("systemctl", "show", "tailscaled.service", "--no-page", "--property", "LoadState,ActiveState,SubState").Output() + if err != nil { + return "not running?" + } + // Parse: + // LoadState=loaded + // ActiveState=inactive + // SubState=dead + st := map[string]string{} + for _, line := range strings.Split(string(out), "\n") { + if i := strings.Index(line, "="); i != -1 { + st[line[:i]] = strings.TrimSpace(line[i+1:]) + } + } + if st["LoadState"] == "loaded" && + (st["SubState"] != "running" || st["ActiveState"] != "active") { + return "systemd tailscaled.service not running." + } + return "not running?" +}