cmd/tailscale: add tailnet info to whoami output

Updates #14375

Change-Id: Ia101a4a3005adb9118051b3416f5a64a4a45987d
Signed-off-by: Will Norris <will@tailscale.com>
This commit is contained in:
Will Norris
2026-07-24 11:39:53 -07:00
committed by Will Norris
parent aec6f8bf13
commit cd34d441be
2 changed files with 32 additions and 4 deletions
+1 -1
View File
@@ -48,5 +48,5 @@ func runWhoami(ctx context.Context, args []string) error {
if err != nil { if err != nil {
return err return err
} }
return printWhoIs(who, whoamiArgs.json) return printWhoIs(who, st.CurrentTailnet, whoamiArgs.json)
} }
+31 -3
View File
@@ -4,6 +4,7 @@
package cli package cli
import ( import (
"cmp"
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
@@ -14,6 +15,8 @@ import (
"github.com/peterbourgon/ff/v3/ffcli" "github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/client/tailscale/apitype" "tailscale.com/client/tailscale/apitype"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
) )
var whoisCmd = &ffcli.Command{ var whoisCmd = &ffcli.Command{
@@ -47,16 +50,27 @@ func runWhoIs(ctx context.Context, args []string) error {
if err != nil { if err != nil {
return err return err
} }
return printWhoIs(who, whoIsArgs.json) return printWhoIs(who, nil, whoIsArgs.json)
}
// whoisAndTailnet combines a WhoIsResponse and TailnetStatus for display.
type whoisAndTailnet struct {
*apitype.WhoIsResponse
CurrentTailnet *ipnstate.TailnetStatus `json:",omitzero"`
} }
// printWhoIs prints the WhoIsResponse to Stdout, either as JSON (if asJSON is // printWhoIs prints the WhoIsResponse to Stdout, either as JSON (if asJSON is
// true) or in a human-readable form. // true) or in a human-readable form.
func printWhoIs(who *apitype.WhoIsResponse, asJSON bool) error { // If tailnet is non-nil, tailnet information is included in the output.
func printWhoIs(who *apitype.WhoIsResponse, tailnet *ipnstate.TailnetStatus, asJSON bool) error {
wat := whoisAndTailnet{
WhoIsResponse: who,
CurrentTailnet: tailnet,
}
if asJSON { if asJSON {
ec := json.NewEncoder(Stdout) ec := json.NewEncoder(Stdout)
ec.SetIndent("", " ") ec.SetIndent("", " ")
ec.Encode(who) ec.Encode(wat)
return nil return nil
} }
@@ -75,6 +89,20 @@ func printWhoIs(who *apitype.WhoIsResponse, asJSON bool) error {
fmt.Fprintf(w, " Name:\t%s\n", who.UserProfile.LoginName) fmt.Fprintf(w, " Name:\t%s\n", who.UserProfile.LoginName)
fmt.Fprintf(w, " ID:\t%d\n", who.UserProfile.ID) fmt.Fprintf(w, " ID:\t%d\n", who.UserProfile.ID)
} }
if tailnet != nil {
// use the tailnet display name, if present
var displayName string
if who.Node.HasCap(tailcfg.NodeAttrTailnetDisplayName) {
v, err := tailcfg.UnmarshalNodeCapJSON[string](who.Node.CapMap, tailcfg.NodeAttrTailnetDisplayName)
if err == nil && len(v) > 0 {
displayName = v[0]
}
}
fmt.Fprintln(w, "Tailnet:")
// TODO(will@): plumb through StableTailnetID so we can show that here as well
fmt.Fprintf(w, " Name:\t%s\n", cmp.Or(displayName, tailnet.Name))
fmt.Fprintf(w, " MagicDNS Suffix:\t%s\n", tailnet.MagicDNSSuffix)
}
w.Flush() w.Flush()
w = nil // avoid accidental use w = nil // avoid accidental use