ipn/localapi: sort localapi handler map keys

Updates #cleanup

Change-Id: I750ed8d033954f1f8786fb35dd16895bb1c5af8e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/11490/merge
Brad Fitzpatrick 8 months ago committed by Brad Fitzpatrick
parent 21b32b467e
commit 38377c37b5

@ -83,63 +83,63 @@ var handler = map[string]localAPIHandler{
// without a trailing slash: // without a trailing slash:
"bugreport": (*Handler).serveBugReport, "bugreport": (*Handler).serveBugReport,
"check-ip-forwarding": (*Handler).serveCheckIPForwarding, "check-ip-forwarding": (*Handler).serveCheckIPForwarding,
"check-udp-gro-forwarding": (*Handler).serveCheckUDPGROForwarding,
"check-prefs": (*Handler).serveCheckPrefs, "check-prefs": (*Handler).serveCheckPrefs,
"check-udp-gro-forwarding": (*Handler).serveCheckUDPGROForwarding,
"component-debug-logging": (*Handler).serveComponentDebugLogging, "component-debug-logging": (*Handler).serveComponentDebugLogging,
"debug": (*Handler).serveDebug, "debug": (*Handler).serveDebug,
"debug-capture": (*Handler).serveDebugCapture,
"debug-derp-region": (*Handler).serveDebugDERPRegion, "debug-derp-region": (*Handler).serveDebugDERPRegion,
"debug-dial-types": (*Handler).serveDebugDialTypes, "debug-dial-types": (*Handler).serveDebugDialTypes,
"debug-log": (*Handler).serveDebugLog,
"debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches, "debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches,
"debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules, "debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules,
"debug-portmap": (*Handler).serveDebugPortmap,
"debug-peer-endpoint-changes": (*Handler).serveDebugPeerEndpointChanges, "debug-peer-endpoint-changes": (*Handler).serveDebugPeerEndpointChanges,
"debug-capture": (*Handler).serveDebugCapture, "debug-portmap": (*Handler).serveDebugPortmap,
"debug-log": (*Handler).serveDebugLog,
"derpmap": (*Handler).serveDERPMap, "derpmap": (*Handler).serveDERPMap,
"dev-set-state-store": (*Handler).serveDevSetStateStore, "dev-set-state-store": (*Handler).serveDevSetStateStore,
"set-push-device-token": (*Handler).serveSetPushDeviceToken,
"handle-push-message": (*Handler).serveHandlePushMessage,
"dial": (*Handler).serveDial, "dial": (*Handler).serveDial,
"drive/fileserver-address": (*Handler).serveDriveServerAddr,
"drive/shares": (*Handler).serveShares,
"file-targets": (*Handler).serveFileTargets, "file-targets": (*Handler).serveFileTargets,
"goroutines": (*Handler).serveGoroutines, "goroutines": (*Handler).serveGoroutines,
"handle-push-message": (*Handler).serveHandlePushMessage,
"id-token": (*Handler).serveIDToken, "id-token": (*Handler).serveIDToken,
"login-interactive": (*Handler).serveLoginInteractive, "login-interactive": (*Handler).serveLoginInteractive,
"logout": (*Handler).serveLogout, "logout": (*Handler).serveLogout,
"logtap": (*Handler).serveLogTap, "logtap": (*Handler).serveLogTap,
"metrics": (*Handler).serveMetrics, "metrics": (*Handler).serveMetrics,
"ping": (*Handler).servePing, "ping": (*Handler).servePing,
"prefs": (*Handler).servePrefs,
"pprof": (*Handler).servePprof, "pprof": (*Handler).servePprof,
"prefs": (*Handler).servePrefs,
"query-feature": (*Handler).serveQueryFeature,
"reload-config": (*Handler).reloadConfig, "reload-config": (*Handler).reloadConfig,
"reset-auth": (*Handler).serveResetAuth, "reset-auth": (*Handler).serveResetAuth,
"serve-config": (*Handler).serveServeConfig, "serve-config": (*Handler).serveServeConfig,
"set-dns": (*Handler).serveSetDNS, "set-dns": (*Handler).serveSetDNS,
"set-expiry-sooner": (*Handler).serveSetExpirySooner, "set-expiry-sooner": (*Handler).serveSetExpirySooner,
"set-gui-visible": (*Handler).serveSetGUIVisible, "set-gui-visible": (*Handler).serveSetGUIVisible,
"drive/fileserver-address": (*Handler).serveDriveServerAddr, "set-push-device-token": (*Handler).serveSetPushDeviceToken,
"drive/shares": (*Handler).serveShares,
"start": (*Handler).serveStart, "start": (*Handler).serveStart,
"status": (*Handler).serveStatus, "status": (*Handler).serveStatus,
"tka/affected-sigs": (*Handler).serveTKAAffectedSigs,
"tka/cosign-recovery-aum": (*Handler).serveTKACosignRecoveryAUM,
"tka/disable": (*Handler).serveTKADisable,
"tka/force-local-disable": (*Handler).serveTKALocalDisable,
"tka/generate-recovery-aum": (*Handler).serveTKAGenerateRecoveryAUM,
"tka/init": (*Handler).serveTKAInit, "tka/init": (*Handler).serveTKAInit,
"tka/log": (*Handler).serveTKALog, "tka/log": (*Handler).serveTKALog,
"tka/modify": (*Handler).serveTKAModify, "tka/modify": (*Handler).serveTKAModify,
"tka/sign": (*Handler).serveTKASign, "tka/sign": (*Handler).serveTKASign,
"tka/status": (*Handler).serveTKAStatus, "tka/status": (*Handler).serveTKAStatus,
"tka/disable": (*Handler).serveTKADisable,
"tka/force-local-disable": (*Handler).serveTKALocalDisable,
"tka/affected-sigs": (*Handler).serveTKAAffectedSigs,
"tka/wrap-preauth-key": (*Handler).serveTKAWrapPreauthKey,
"tka/verify-deeplink": (*Handler).serveTKAVerifySigningDeeplink,
"tka/generate-recovery-aum": (*Handler).serveTKAGenerateRecoveryAUM,
"tka/cosign-recovery-aum": (*Handler).serveTKACosignRecoveryAUM,
"tka/submit-recovery-aum": (*Handler).serveTKASubmitRecoveryAUM, "tka/submit-recovery-aum": (*Handler).serveTKASubmitRecoveryAUM,
"upload-client-metrics": (*Handler).serveUploadClientMetrics, "tka/verify-deeplink": (*Handler).serveTKAVerifySigningDeeplink,
"watch-ipn-bus": (*Handler).serveWatchIPNBus, "tka/wrap-preauth-key": (*Handler).serveTKAWrapPreauthKey,
"whois": (*Handler).serveWhoIs,
"query-feature": (*Handler).serveQueryFeature,
"update/check": (*Handler).serveUpdateCheck, "update/check": (*Handler).serveUpdateCheck,
"update/install": (*Handler).serveUpdateInstall, "update/install": (*Handler).serveUpdateInstall,
"update/progress": (*Handler).serveUpdateProgress, "update/progress": (*Handler).serveUpdateProgress,
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
"whois": (*Handler).serveWhoIs,
} }
var ( var (

@ -9,11 +9,18 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"go/ast"
"go/parser"
"go/token"
"io" "io"
"log"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/netip" "net/netip"
"net/url" "net/url"
"os"
"slices"
"strconv"
"strings" "strings"
"testing" "testing"
@ -26,6 +33,7 @@ import (
"tailscale.com/tstest" "tailscale.com/tstest"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/logid" "tailscale.com/types/logid"
"tailscale.com/util/slicesx"
"tailscale.com/wgengine" "tailscale.com/wgengine"
) )
@ -318,3 +326,67 @@ func newTestLocalBackend(t testing.TB) *ipnlocal.LocalBackend {
} }
return lb return lb
} }
func TestKeepItSorted(t *testing.T) {
// Parse the localapi.go file into an AST.
fset := token.NewFileSet() // positions are relative to fset
src, err := os.ReadFile("localapi.go")
if err != nil {
log.Fatal(err)
}
f, err := parser.ParseFile(fset, "localapi.go", src, 0)
if err != nil {
log.Fatal(err)
}
getHandler := func() *ast.ValueSpec {
for _, d := range f.Decls {
if g, ok := d.(*ast.GenDecl); ok && g.Tok == token.VAR {
for _, s := range g.Specs {
if vs, ok := s.(*ast.ValueSpec); ok {
if len(vs.Names) == 1 && vs.Names[0].Name == "handler" {
return vs
}
}
}
}
}
return nil
}
keys := func() (ret []string) {
h := getHandler()
if h == nil {
t.Fatal("no handler var found")
}
cl, ok := h.Values[0].(*ast.CompositeLit)
if !ok {
t.Fatalf("handler[0] is %T, want *ast.CompositeLit", h.Values[0])
}
for _, e := range cl.Elts {
kv := e.(*ast.KeyValueExpr)
strLt := kv.Key.(*ast.BasicLit)
if strLt.Kind != token.STRING {
t.Fatalf("got: %T, %q", kv.Key, kv.Key)
}
k, err := strconv.Unquote(strLt.Value)
if err != nil {
t.Fatalf("unquote: %v", err)
}
ret = append(ret, k)
}
return
}
gotKeys := keys()
endSlash, noSlash := slicesx.Partition(keys(), func(s string) bool { return strings.HasSuffix(s, "/") })
if !slices.IsSorted(endSlash) {
t.Errorf("the items ending in a slash aren't sorted")
}
if !slices.IsSorted(noSlash) {
t.Errorf("the items ending in a slash aren't sorted")
}
if !t.Failed() {
want := append(endSlash, noSlash...)
if !slices.Equal(gotKeys, want) {
t.Errorf("items with trailing slashes should precede those without")
}
}
}

Loading…
Cancel
Save