cmd/tailscale: provide a better error message when tailscaled isn't running

Fixes #2797

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2021-09-13 17:35:55 -07:00 committed by Brad Fitzpatrick
parent 3e2a7de2e9
commit a83f08c54b

View File

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