Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move snapshots interface to localhost #10302

Merged
merged 14 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/v1.18.0-beta34/localhostdebug.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changelog:
- type: NON_USER_FACING
description: Move snapshot debug endpoints localhost:9095. can be reverted by setting env ADMIN_HANDLERS_WITH_STATS to 'true'
4 changes: 2 additions & 2 deletions pkg/utils/glooadminutils/admincli/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
"github.com/rotisserie/eris"
"github.com/solo-io/gloo/pkg/utils/cmdutils"
"github.com/solo-io/gloo/pkg/utils/requestutils/curl"
"github.com/solo-io/gloo/projects/gloo/pkg/servers/admin"
"github.com/solo-io/go-utils/threadsafe"
)

const (
InputSnapshotPath = "/snapshots/input"
DefaultAdminPort = 9091
)

// Client is a utility for executing requests against the Gloo Admin API
Expand All @@ -33,7 +33,7 @@ func NewClient() *Client {
curlOptions: []curl.Option{
curl.WithScheme("http"),
curl.WithHost("127.0.0.1"),
curl.WithPort(DefaultAdminPort),
curl.WithPort(admin.AdminPort),
// 3 retries, exponential back-off, 10 second max
curl.WithRetries(3, 0, 10),
},
Expand Down
79 changes: 79 additions & 0 deletions projects/gloo/pkg/servers/admin/server.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package admin

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"sort"

"github.com/solo-io/gloo/projects/gloo/pkg/servers/iosnapshot"
"github.com/solo-io/go-utils/contextutils"
"istio.io/istio/pkg/kube/krt"
)

const (
AdminPort = 9095
)

// ServerHandlers 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 ServerHandlers(ctx context.Context, history iosnapshot.History, dbg *krt.DebugHandler) func(mux *http.ServeMux, profiles map[string]string) {
Expand All @@ -20,6 +27,7 @@ func ServerHandlers(ctx context.Context, history iosnapshot.History, dbg *krt.De
response := history.GetInputSnapshot(ctx)
respondJson(w, response)
})
profiles["/snapshots/input"] = "Input Snapshot"

// The Edge Snapshot is intended to return a representation of the ApiSnapshot object that the Control Plane
// manages internally. This is not intended to be consumed by users, but instead be a mechanism to feed this
Expand All @@ -28,6 +36,7 @@ func ServerHandlers(ctx context.Context, history iosnapshot.History, dbg *krt.De
response := history.GetEdgeApiSnapshot(ctx)
respondJson(w, response)
})
profiles["/snapshots/edge"] = "Edge Snapshot"

// The Proxy Snapshot is intended to return a representation of the Proxies within the ApiSnapshot object.
// Proxies may either be persisted in etcD or in-memory, so this Api provides a single mechansim to access
Expand All @@ -36,16 +45,20 @@ func ServerHandlers(ctx context.Context, history iosnapshot.History, dbg *krt.De
response := history.GetProxySnapshot(ctx)
respondJson(w, response)
})
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 := history.GetXdsSnapshot(ctx)
respondJson(w, response)
})
profiles["/snapshots/xds"] = "XDS Snapshot"

m.HandleFunc("/snapshots/krt", func(w http.ResponseWriter, r *http.Request) {
writeJSON(w, dbg, r)
})
profiles["/snapshots/krt"] = "KRT Snapshot"
}
}

Expand Down Expand Up @@ -92,3 +105,69 @@ func writeJSON(w http.ResponseWriter, obj any, req *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}
}

func StartHandlers(ctx context.Context, addHandlers ...func(mux *http.ServeMux, profiles map[string]string)) error {
mux := new(http.ServeMux)
profileDescriptions := map[string]string{}
for _, addHandler := range addHandlers {
addHandler(mux, profileDescriptions)
}
idx := index(profileDescriptions)
mux.HandleFunc("/", idx)
mux.HandleFunc("/snapshots/", idx)
server := &http.Server{
Addr: fmt.Sprintf("localhost:%d", AdminPort),
Handler: mux,
}
contextutils.LoggerFrom(ctx).Infof("Admin server starting at %s", server.Addr)
go func() {
err := server.ListenAndServe()
if err == http.ErrServerClosed {
contextutils.LoggerFrom(ctx).Infof("Admin server closed")
} else {
contextutils.LoggerFrom(ctx).Warnf("Admin server closed with unexpected error: %v", err)
}
}()
go func() {
<-ctx.Done()
if server != nil {
err := server.Close()
if err != nil {
contextutils.LoggerFrom(ctx).Warnf("Admin server shutdown returned error: %v", err)
}
}
}()
return nil
}

func index(profileDescriptions map[string]string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {

type profile struct {
Name string
Href string
Desc string
}
var profiles []profile
for href, desc := range profileDescriptions {
profiles = append(profiles, profile{
Name: href,
Href: href,
Desc: desc,
})
}

sort.Slice(profiles, func(i, j int) bool {
return profiles[i].Name < profiles[j].Name
})

// Adding other profiles exposed from within this package
var buf bytes.Buffer
fmt.Fprintf(&buf, "<h1>Admin Server</h1>\n")
for _, p := range profiles {
fmt.Fprintf(&buf, "<h2><a href=\"%s\"}>%s</a></h2><p>%s</p>\n", p.Name, p.Name, p.Desc)

}
w.Write(buf.Bytes())
}
}
17 changes: 16 additions & 1 deletion projects/gloo/pkg/syncer/setup/start_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package setup

import (
"context"
"fmt"
"net/http"

"github.com/solo-io/gloo/pkg/utils/envutils"
"github.com/solo-io/gloo/projects/gloo/pkg/servers/admin"
"github.com/solo-io/gloo/projects/gloo/pkg/servers/iosnapshot"
"github.com/solo-io/go-utils/stats"
Expand Down Expand Up @@ -63,8 +66,20 @@ func AdminServerStartFunc(history iosnapshot.History, dbg *krt.DebugHandler) Sta
// NOTE: There is a slight difference in how we run this server -vs- how we used to run it
// In the past, we would start the server once, at the beginning of the running container
// Now, we start a new server each time we invoke a StartFunc.
stats.StartCancellableStatsServerWithPort(ctx, stats.DefaultStartupOptions(), serverHandlers)
if serverAdminHandlersWithStats() {
stats.StartCancellableStatsServerWithPort(ctx, stats.DefaultStartupOptions(), serverHandlers)
} else {
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/", admin.AdminPort)] = fmt.Sprintf("To see snapshots, port forward to port %d", admin.AdminPort)
})
admin.StartHandlers(ctx, serverHandlers)
}

return nil
}
}

func serverAdminHandlersWithStats() bool {
sam-heilbron marked this conversation as resolved.
Show resolved Hide resolved
return envutils.IsEnvTruthy("ADMIN_HANDLERS_WITH_STATS")
}
5 changes: 3 additions & 2 deletions test/kubernetes/testutils/assertions/gloo.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/solo-io/gloo/pkg/utils/glooadminutils/admincli"
"github.com/solo-io/gloo/pkg/utils/kubeutils/portforward"
"github.com/solo-io/gloo/pkg/utils/requestutils/curl"
"github.com/solo-io/gloo/projects/gloo/pkg/servers/admin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
Expand All @@ -26,7 +27,7 @@ func (p *Provider) AssertGlooAdminApi(

portForwarder, err := p.clusterContext.Cli.StartPortForward(ctx,
portforward.WithDeployment(glooDeployment.GetName(), glooDeployment.GetNamespace()),
portforward.WithPorts(int(admincli.DefaultAdminPort), int(admincli.DefaultAdminPort)),
portforward.WithPorts(int(admin.AdminPort), int(admin.AdminPort)),
)
p.Require.NoError(err, "can open port-forward")
defer func() {
Expand All @@ -49,7 +50,7 @@ func (p *Provider) AssertGlooAdminApi(
WithReceiver(io.Discard). // adminAssertion can overwrite this
WithCurlOptions(
curl.WithRetries(3, 0, 10),
curl.WithPort(int(admincli.DefaultAdminPort)),
curl.WithPort(int(admin.AdminPort)),
)

for _, adminAssertion := range adminAssertions {
Expand Down
Loading