mirror of https://github.com/tailscale/tailscale/
cmd/tailscale/cli,client,ipn: add appc-routes cli command
Allow the user to access information about routes an app connector has learned, such as how many routes for each domain. Fixes tailscale/corp#32624 Signed-off-by: Fran Bull <fran@tailscale.com>pull/17340/head
parent
976389c0f7
commit
65d6c80695
@ -0,0 +1,153 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
"tailscale.com/appc"
|
||||
)
|
||||
|
||||
var appcRoutesArgs struct {
|
||||
all bool
|
||||
domainMap bool
|
||||
n bool
|
||||
}
|
||||
|
||||
var appcRoutesCmd = &ffcli.Command{
|
||||
Name: "appc-routes",
|
||||
ShortUsage: "tailscale appc-routes",
|
||||
Exec: runAppcRoutesInfo,
|
||||
ShortHelp: "Print the current app connector routes",
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("appc-routes")
|
||||
fs.BoolVar(&appcRoutesArgs.all, "all", false, "Print learned domains and routes and extra policy configured routes.")
|
||||
fs.BoolVar(&appcRoutesArgs.domainMap, "map", false, "Print the map of learned domains: [routes].")
|
||||
fs.BoolVar(&appcRoutesArgs.n, "n", false, "Print the total number of routes this node advertises.")
|
||||
return fs
|
||||
})(),
|
||||
LongHelp: strings.TrimSpace(`
|
||||
The 'tailscale appc-routes' command prints the current App Connector route status.
|
||||
|
||||
By default this command prints the domains configured in the app connector configuration and how many routes have been
|
||||
learned for each domain.
|
||||
|
||||
--all prints the routes learned from the domains configured in the app connector configuration; and any extra routes provided
|
||||
in the the policy app connector 'routes' field.
|
||||
|
||||
--map prints the routes learned from the domains configured in the app connector configuration.
|
||||
|
||||
-n prints the total number of routes advertised by this device, whether learned, set in the policy, or set locally.
|
||||
|
||||
For more information about App Connectors, refer to
|
||||
https://tailscale.com/kb/1281/app-connectors
|
||||
`),
|
||||
}
|
||||
|
||||
func getAllOutput(ri *appc.RouteInfo) (string, error) {
|
||||
domains, err := json.MarshalIndent(ri.Domains, " ", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
control, err := json.MarshalIndent(ri.Control, " ", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s := fmt.Sprintf(`Learned Routes
|
||||
==============
|
||||
%s
|
||||
|
||||
Routes from Policy
|
||||
==================
|
||||
%s
|
||||
`, domains, control)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type domainCount struct {
|
||||
domain string
|
||||
count int
|
||||
}
|
||||
|
||||
func getSummarizeLearnedOutput(ri *appc.RouteInfo) string {
|
||||
x := make([]domainCount, len(ri.Domains))
|
||||
i := 0
|
||||
maxDomainWidth := 0
|
||||
for k, v := range ri.Domains {
|
||||
if len(k) > maxDomainWidth {
|
||||
maxDomainWidth = len(k)
|
||||
}
|
||||
x[i] = domainCount{domain: k, count: len(v)}
|
||||
i++
|
||||
}
|
||||
slices.SortFunc(x, func(i, j domainCount) int {
|
||||
if i.count > j.count {
|
||||
return -1
|
||||
}
|
||||
if i.count < j.count {
|
||||
return 1
|
||||
}
|
||||
if i.domain > j.domain {
|
||||
return 1
|
||||
}
|
||||
if i.domain < j.domain {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
s := ""
|
||||
fmtString := fmt.Sprintf("%%-%ds %%d\n", maxDomainWidth) // eg "%-10s %d\n"
|
||||
for _, dc := range x {
|
||||
s += fmt.Sprintf(fmtString, dc.domain, dc.count)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func runAppcRoutesInfo(ctx context.Context, args []string) error {
|
||||
prefs, err := localClient.GetPrefs(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !prefs.AppConnector.Advertise {
|
||||
fmt.Println("not a connector")
|
||||
return nil
|
||||
}
|
||||
|
||||
if appcRoutesArgs.n {
|
||||
fmt.Println(len(prefs.AdvertiseRoutes))
|
||||
return nil
|
||||
}
|
||||
|
||||
routeInfo, err := localClient.GetAppConnectorRouteInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if appcRoutesArgs.domainMap {
|
||||
domains, err := json.Marshal(routeInfo.Domains)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(domains))
|
||||
return nil
|
||||
}
|
||||
|
||||
if appcRoutesArgs.all {
|
||||
s, err := getAllOutput(&routeInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Print(getSummarizeLearnedOutput(&routeInfo))
|
||||
return nil
|
||||
}
|
||||
Loading…
Reference in New Issue