cmd/tailscale, ipn/localapi: get daemon version from localapi status
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/1546/head
parent
d0dffe33c0
commit
439d70dce2
|
@ -103,7 +103,16 @@ func Goroutines(ctx context.Context) ([]byte, error) {
|
||||||
|
|
||||||
// Status returns the Tailscale daemon's status.
|
// Status returns the Tailscale daemon's status.
|
||||||
func Status(ctx context.Context) (*ipnstate.Status, error) {
|
func Status(ctx context.Context) (*ipnstate.Status, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "http://local-tailscaled.sock/localapi/v0/status", nil)
|
return status(ctx, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusWithPeers returns the Tailscale daemon's status, without the peer info.
|
||||||
|
func StatusWithoutPeers(ctx context.Context) (*ipnstate.Status, error) {
|
||||||
|
return status(ctx, "?peers=false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func status(ctx context.Context, queryString string) (*ipnstate.Status, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://local-tailscaled.sock/localapi/v0/status"+queryString, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/peterbourgon/ff/v2/ffcli"
|
"github.com/peterbourgon/ff/v2/ffcli"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/client/tailscale"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,29 +42,10 @@ func runVersion(ctx context.Context, args []string) error {
|
||||||
|
|
||||||
fmt.Printf("Client: %s\n", version.String())
|
fmt.Printf("Client: %s\n", version.String())
|
||||||
|
|
||||||
c, bc, ctx, cancel := connect(ctx)
|
st, err := tailscale.StatusWithoutPeers(ctx)
|
||||||
defer cancel()
|
if err != nil {
|
||||||
|
return err
|
||||||
bc.AllowVersionSkew = true
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
|
||||||
|
|
||||||
bc.SetNotifyCallback(func(n ipn.Notify) {
|
|
||||||
if n.ErrMessage != nil {
|
|
||||||
log.Fatal(*n.ErrMessage)
|
|
||||||
}
|
}
|
||||||
if n.Engine != nil {
|
fmt.Printf("Daemon: %s\n", st.Version)
|
||||||
fmt.Printf("Daemon: %s\n", n.Version)
|
|
||||||
close(done)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
go pump(ctx, bc, c)
|
|
||||||
|
|
||||||
bc.RequestEngineStatus()
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return nil
|
return nil
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,20 +218,45 @@ func (b *LocalBackend) Status() *ipnstate.Status {
|
||||||
return sb.Status()
|
return sb.Status()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatusWithoutPeers is like Status but omits any details
|
||||||
|
// of peers.
|
||||||
|
func (b *LocalBackend) StatusWithoutPeers() *ipnstate.Status {
|
||||||
|
sb := new(ipnstate.StatusBuilder)
|
||||||
|
b.updateStatus(sb, nil)
|
||||||
|
return sb.Status()
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateStatus implements ipnstate.StatusUpdater.
|
// UpdateStatus implements ipnstate.StatusUpdater.
|
||||||
func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
||||||
b.e.UpdateStatus(sb)
|
b.e.UpdateStatus(sb)
|
||||||
|
b.updateStatus(sb, b.populatePeerStatusLocked)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateStatus populates sb with status.
|
||||||
|
//
|
||||||
|
// extraLocked, if non-nil, is called while b.mu is still held.
|
||||||
|
func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func(*ipnstate.StatusBuilder)) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
sb.SetVersion(version.Long)
|
||||||
sb.SetBackendState(b.state.String())
|
sb.SetBackendState(b.state.String())
|
||||||
sb.SetAuthURL(b.authURL)
|
sb.SetAuthURL(b.authURL)
|
||||||
|
|
||||||
// TODO: hostinfo, and its networkinfo
|
// TODO: hostinfo, and its networkinfo
|
||||||
// TODO: EngineStatus copy (and deprecate it?)
|
// TODO: EngineStatus copy (and deprecate it?)
|
||||||
|
|
||||||
if b.netMap != nil {
|
if b.netMap != nil {
|
||||||
sb.SetMagicDNSSuffix(b.netMap.MagicDNSSuffix())
|
sb.SetMagicDNSSuffix(b.netMap.MagicDNSSuffix())
|
||||||
|
}
|
||||||
|
|
||||||
|
if extraLocked != nil {
|
||||||
|
extraLocked(sb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
|
||||||
|
if b.netMap == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
for id, up := range b.netMap.UserProfiles {
|
for id, up := range b.netMap.UserProfiles {
|
||||||
sb.AddUser(id, up)
|
sb.AddUser(id, up)
|
||||||
}
|
}
|
||||||
|
@ -264,7 +289,6 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
||||||
ExitNode: p.StableID != "" && p.StableID == b.prefs.ExitNodeID,
|
ExitNode: p.StableID != "" && p.StableID == b.prefs.ExitNodeID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WhoIs reports the node and user who owns the node with the given IP:port.
|
// WhoIs reports the node and user who owns the node with the given IP:port.
|
||||||
|
|
|
@ -26,7 +26,14 @@ import (
|
||||||
|
|
||||||
// Status represents the entire state of the IPN network.
|
// Status represents the entire state of the IPN network.
|
||||||
type Status struct {
|
type Status struct {
|
||||||
|
// Version is the daemon's long version (see version.Long).
|
||||||
|
Version string
|
||||||
|
|
||||||
|
// BackendState is an ipn.State string value:
|
||||||
|
// "NoState", "NeedsLogin", "NeedsMachineAuth", "Stopped",
|
||||||
|
// "Starting", "Running".
|
||||||
BackendState string
|
BackendState string
|
||||||
|
|
||||||
AuthURL string // current URL provided by control to authorize client
|
AuthURL string // current URL provided by control to authorize client
|
||||||
TailscaleIPs []netaddr.IP // Tailscale IP(s) assigned to this node
|
TailscaleIPs []netaddr.IP // Tailscale IP(s) assigned to this node
|
||||||
Self *PeerStatus
|
Self *PeerStatus
|
||||||
|
@ -105,6 +112,12 @@ type StatusBuilder struct {
|
||||||
st Status
|
st Status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sb *StatusBuilder) SetVersion(v string) {
|
||||||
|
sb.mu.Lock()
|
||||||
|
defer sb.mu.Unlock()
|
||||||
|
sb.st.Version = v
|
||||||
|
}
|
||||||
|
|
||||||
func (sb *StatusBuilder) SetBackendState(v string) {
|
func (sb *StatusBuilder) SetBackendState(v string) {
|
||||||
sb.mu.Lock()
|
sb.mu.Lock()
|
||||||
defer sb.mu.Unlock()
|
defer sb.mu.Unlock()
|
||||||
|
|
|
@ -10,9 +10,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/ipn/ipnlocal"
|
"tailscale.com/ipn/ipnlocal"
|
||||||
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,7 +120,24 @@ func (h *Handler) serveStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
var st *ipnstate.Status
|
||||||
|
if defBool(r.FormValue("peers"), true) {
|
||||||
|
st = h.b.Status()
|
||||||
|
} else {
|
||||||
|
st = h.b.StatusWithoutPeers()
|
||||||
|
}
|
||||||
e := json.NewEncoder(w)
|
e := json.NewEncoder(w)
|
||||||
e.SetIndent("", "\t")
|
e.SetIndent("", "\t")
|
||||||
e.Encode(h.b.Status())
|
e.Encode(st)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defBool(a string, def bool) bool {
|
||||||
|
if a == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
v, err := strconv.ParseBool(a)
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue