diff --git a/projects/gateway2/admin/krt_snapshot.go b/projects/gateway2/admin/krt_snapshot.go new file mode 100644 index 00000000000..d459dfd8669 --- /dev/null +++ b/projects/gateway2/admin/krt_snapshot.go @@ -0,0 +1,14 @@ +package admin + +import ( + "net/http" + + "istio.io/istio/pkg/kube/krt" +) + +func addKrtSnapshotHandler(path string, mux *http.ServeMux, profiles map[string]dynamicProfileDescription, dbg *krt.DebugHandler) { + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, dbg, r) + }) + profiles[path] = func() string { return "KRT Snapshot" } +} diff --git a/projects/gateway2/admin/logging.go b/projects/gateway2/admin/logging.go new file mode 100644 index 00000000000..c4de9287a50 --- /dev/null +++ b/projects/gateway2/admin/logging.go @@ -0,0 +1,61 @@ +package admin + +import ( + "fmt" + "net/http" + + "github.com/solo-io/go-utils/contextutils" +) + +// The logging handler is an AtomicLevel that supports dynamically changing the log level at runtime. +func addLoggingHandler(path string, mux *http.ServeMux, profiles map[string]dynamicProfileDescription) { + mux.Handle(path, contextutils.GetLogHandler()) + profiles[path] = getLoggingDescription +} + +// Gets a string representation of the current log level. +func getLogLevel() string { + return contextutils.GetLogLevel().String() +} + +// Gets the html/js to display in the UI for the logging endpoint. +func getLoggingDescription() string { + currentLogLevel := getLogLevel() + + // build the options selector, with the current log level selected by default + selectorText := `` + + return `View or change the log level of the program. Note: does not persist across pod restarts.
+ +Log level: +` + selectorText + ` + + + + + ` +} diff --git a/projects/gateway2/admin/pprof.go b/projects/gateway2/admin/pprof.go new file mode 100644 index 00000000000..612ec78e670 --- /dev/null +++ b/projects/gateway2/admin/pprof.go @@ -0,0 +1,20 @@ +package admin + +import ( + "net/http" + "net/http/pprof" +) + +func addPprofHandler(path string, mux *http.ServeMux, profiles map[string]dynamicProfileDescription) { + mux.HandleFunc(path, pprof.Index) + mux.HandleFunc(path+"cmdline", pprof.Cmdline) + mux.HandleFunc(path+"profile", pprof.Profile) + mux.HandleFunc(path+"symbol", pprof.Symbol) + mux.HandleFunc(path+"trace", pprof.Trace) + + profiles[path] = func() string { + return `PProf related things:
+ full goroutine stack dump + ` + } +} diff --git a/projects/gateway2/admin/server.go b/projects/gateway2/admin/server.go index 63591385cd2..f7e66a3a5f1 100644 --- a/projects/gateway2/admin/server.go +++ b/projects/gateway2/admin/server.go @@ -6,38 +6,41 @@ import ( "encoding/json" "fmt" "net/http" + "os" "sort" - "github.com/envoyproxy/go-control-plane/pkg/cache/v3" envoycache "github.com/envoyproxy/go-control-plane/pkg/cache/v3" - "github.com/rotisserie/eris" "github.com/solo-io/gloo/projects/gateway2/controller" "github.com/solo-io/go-utils/contextutils" - "github.com/solo-io/go-utils/stats" "istio.io/istio/pkg/kube/krt" ) const ( - AdminPort = 9095 + AdminPort = 9091 ) func RunAdminServer(ctx context.Context, setupOpts *controller.SetupOpts) error { // serverHandlers defines the custom handlers that the Admin Server will support serverHandlers := getServerHandlers(ctx, setupOpts.KrtDebugger, setupOpts.Cache) - stats.StartCancellableStatsServerWithPort(ctx, stats.DefaultStartupOptions(), func(mux *http.ServeMux, profiles map[string]string) { - // let people know these moved - profiles[fmt.Sprintf("http://localhost:%d/snapshots/", AdminPort)] = fmt.Sprintf("To see snapshots, port forward to port %d", AdminPort) - }) + // initialize the atomic log level + if envLogLevel := os.Getenv(contextutils.LogLevelEnvName); envLogLevel != "" { + contextutils.SetLogLevelFromString(envLogLevel) + } + startHandlers(ctx, serverHandlers) return nil } +// use a function for the profile descriptions so that every time the admin page is displayed, it can show +// up-to-date info in the description (e.g. the current log level) +type dynamicProfileDescription func() string + // getServerHandlers returns the custom handlers for the Admin Server, which will be bound to the http.ServeMux -// These endpoints serve as the basis for an Admin Interface for the Control Plane (https://github.com/solo-io/gloo/issues/6494) -func getServerHandlers(ctx context.Context, dbg *krt.DebugHandler, cache envoycache.SnapshotCache) func(mux *http.ServeMux, profiles map[string]string) { - return func(m *http.ServeMux, profiles map[string]string) { +// These endpoints serve as the basis for an Admin Interface for the Control Plane (https://github.com/kgateway-dev/kgateway/issues/6494) +func getServerHandlers(ctx context.Context, dbg *krt.DebugHandler, cache envoycache.SnapshotCache) func(mux *http.ServeMux, profiles map[string]dynamicProfileDescription) { + return func(m *http.ServeMux, profiles map[string]dynamicProfileDescription) { /* // The Input Snapshot is intended to return a list of resources that are persisted in the Kubernetes DB, etcD @@ -57,18 +60,13 @@ func getServerHandlers(ctx context.Context, dbg *krt.DebugHandler, cache envoyca profiles["/snapshots/proxies"] = "Proxy Snapshot" */ - // The xDS Snapshot is intended to return the full in-memory xDS cache that the Control Plane manages - // and serves up to running proxies. - m.HandleFunc("/snapshots/xds", func(w http.ResponseWriter, r *http.Request) { - response := getXdsSnapshotDataFromCache(cache) - writeJSON(w, response, r) - }) - profiles["/snapshots/xds"] = "XDS Snapshot" + addXdsSnapshotHandler("/snapshots/xds", m, profiles, cache) - m.HandleFunc("/snapshots/krt", func(w http.ResponseWriter, r *http.Request) { - writeJSON(w, dbg, r) - }) - profiles["/snapshots/krt"] = "KRT Snapshot" + addKrtSnapshotHandler("/snapshots/krt", m, profiles, dbg) + + addLoggingHandler("/logging", m, profiles) + + addPprofHandler("/debug/pprof/", m, profiles) } } @@ -116,9 +114,9 @@ func writeJSON(w http.ResponseWriter, obj any, req *http.Request) { } } -func startHandlers(ctx context.Context, addHandlers ...func(mux *http.ServeMux, profiles map[string]string)) error { +func startHandlers(ctx context.Context, addHandlers ...func(mux *http.ServeMux, profiles map[string]dynamicProfileDescription)) error { mux := new(http.ServeMux) - profileDescriptions := map[string]string{} + profileDescriptions := map[string]dynamicProfileDescription{} for _, addHandler := range addHandlers { addHandler(mux, profileDescriptions) } @@ -150,7 +148,7 @@ func startHandlers(ctx context.Context, addHandlers ...func(mux *http.ServeMux, return nil } -func index(profileDescriptions map[string]string) func(w http.ResponseWriter, r *http.Request) { +func index(profileDescriptions map[string]dynamicProfileDescription) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { type profile struct { @@ -159,11 +157,11 @@ func index(profileDescriptions map[string]string) func(w http.ResponseWriter, r Desc string } var profiles []profile - for href, desc := range profileDescriptions { + for href, descFunc := range profileDescriptions { profiles = append(profiles, profile{ Name: href, Href: href, - Desc: desc, + Desc: descFunc(), }) } @@ -181,28 +179,3 @@ func index(profileDescriptions map[string]string) func(w http.ResponseWriter, r w.Write(buf.Bytes()) } } - -func getXdsSnapshotDataFromCache(xdsCache cache.SnapshotCache) SnapshotResponseData { - cacheKeys := xdsCache.GetStatusKeys() - cacheEntries := make(map[string]interface{}, len(cacheKeys)) - - for _, k := range cacheKeys { - xdsSnapshot, err := getXdsSnapshot(xdsCache, k) - if err != nil { - cacheEntries[k] = err.Error() - } else { - cacheEntries[k] = xdsSnapshot - } - } - - return completeSnapshotResponse(cacheEntries) -} - -func getXdsSnapshot(xdsCache cache.SnapshotCache, k string) (cache cache.ResourceSnapshot, err error) { - defer func() { - if r := recover(); r != nil { - err = eris.New(fmt.Sprintf("panic occurred while getting xds snapshot: %v", r)) - } - }() - return xdsCache.GetSnapshot(k) -} diff --git a/projects/gateway2/admin/xds_snapshot.go b/projects/gateway2/admin/xds_snapshot.go new file mode 100644 index 00000000000..a05c4a7948b --- /dev/null +++ b/projects/gateway2/admin/xds_snapshot.go @@ -0,0 +1,45 @@ +package admin + +import ( + "fmt" + "net/http" + + "github.com/envoyproxy/go-control-plane/pkg/cache/v3" + envoycache "github.com/envoyproxy/go-control-plane/pkg/cache/v3" + "github.com/rotisserie/eris" +) + +// The xDS Snapshot is intended to return the full in-memory xDS cache that the Control Plane manages +// and serves up to running proxies. +func addXdsSnapshotHandler(path string, mux *http.ServeMux, profiles map[string]dynamicProfileDescription, cache envoycache.SnapshotCache) { + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + response := getXdsSnapshotDataFromCache(cache) + writeJSON(w, response, r) + }) + profiles[path] = func() string { return "XDS Snapshot" } +} + +func getXdsSnapshotDataFromCache(xdsCache cache.SnapshotCache) SnapshotResponseData { + cacheKeys := xdsCache.GetStatusKeys() + cacheEntries := make(map[string]interface{}, len(cacheKeys)) + + for _, k := range cacheKeys { + xdsSnapshot, err := getXdsSnapshot(xdsCache, k) + if err != nil { + cacheEntries[k] = err.Error() + } else { + cacheEntries[k] = xdsSnapshot + } + } + + return completeSnapshotResponse(cacheEntries) +} + +func getXdsSnapshot(xdsCache cache.SnapshotCache, k string) (cache cache.ResourceSnapshot, err error) { + defer func() { + if r := recover(); r != nil { + err = eris.New(fmt.Sprintf("panic occurred while getting xds snapshot: %v", r)) + } + }() + return xdsCache.GetSnapshot(k) +} diff --git a/test/ginkgo/parallel/ports.go b/test/ginkgo/parallel/ports.go index 9f13611c203..a3c64012852 100644 --- a/test/ginkgo/parallel/ports.go +++ b/test/ginkgo/parallel/ports.go @@ -100,9 +100,9 @@ func portInUseListen(proposedPort uint32) error { } var denyListPorts = map[uint32]struct{}{ - // See gloo/pkg/servers/admin/server.go - // See https://github.com/solo-io/solo-projects/issues/7307 for more details - 9095: {}, + // See projects/gateway2/admin/server.go + // This port is reserved for the admin server + 9091: {}, } func portInDenyList(proposedPort uint32) error {