77 lines
2.0 KiB
Go
77 lines
2.0 KiB
Go
package node
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/netip"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
)
|
|
|
|
type TailscaleStatus struct {
|
|
Installed bool `json:"installed"`
|
|
State string `json:"state"`
|
|
Online bool `json:"online"`
|
|
Addresses []string `json:"addresses"`
|
|
}
|
|
|
|
type boundedProviderOutput struct{ bytes.Buffer }
|
|
|
|
func (b *boundedProviderOutput) Write(data []byte) (int, error) {
|
|
if b.Len()+len(data) > 1024*1024 {
|
|
return 0, errors.New("provider status too large")
|
|
}
|
|
return b.Buffer.Write(data)
|
|
}
|
|
|
|
func ReadTailscale() TailscaleStatus {
|
|
value := TailscaleStatus{State: "not_installed", Addresses: []string{}}
|
|
if _, err := os.Stat("/usr/bin/tailscale"); err != nil {
|
|
return value
|
|
}
|
|
value.Installed = true
|
|
value.State = "unavailable"
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
cmd := exec.CommandContext(ctx, "/usr/bin/tailscale", "status", "--json", "--peers=false")
|
|
// Do not request peer inventory or expose auth URLs, user identities, keys,
|
|
// provider diagnostics or profile objects in the product API.
|
|
var output boundedProviderOutput
|
|
cmd.Stdout = &output
|
|
if err := cmd.Run(); err != nil {
|
|
return value
|
|
}
|
|
return parseTailscale(output.Bytes())
|
|
}
|
|
|
|
func parseTailscale(data []byte) TailscaleStatus {
|
|
value := TailscaleStatus{Installed: true, State: "unavailable", Addresses: []string{}}
|
|
if len(data) > 1024*1024 {
|
|
return value
|
|
}
|
|
var raw struct {
|
|
BackendState string
|
|
TailscaleIPs []string
|
|
Self *struct{ Online bool }
|
|
}
|
|
if json.Unmarshal(data, &raw) != nil {
|
|
return value
|
|
}
|
|
switch raw.BackendState {
|
|
case "Running", "Stopped", "NeedsLogin", "NeedsMachineAuth", "Starting", "NoState":
|
|
value.State = raw.BackendState
|
|
default:
|
|
return value
|
|
}
|
|
value.Online = raw.BackendState == "Running" && raw.Self != nil && raw.Self.Online
|
|
for _, address := range raw.TailscaleIPs {
|
|
if ip, err := netip.ParseAddr(address); err == nil {
|
|
value.Addresses = append(value.Addresses, ip.String())
|
|
}
|
|
}
|
|
return value
|
|
}
|