diff --git a/arthas/README.md b/arthas/README.md index 077b281..e61ed70 100644 --- a/arthas/README.md +++ b/arthas/README.md @@ -1,6 +1,6 @@ # Arthas Mission Control Plugin -The Arthas plugin adds JVM diagnostics to Mission Control for Kubernetes workloads. It attaches [Arthas](https://arthas.aliyun.com/) to a Java process running in a selected pod and exposes the Arthas web console, HTTP API, and diagnostic operations from the Mission Control UI. +The Arthas plugin adds JVM diagnostics to Mission Control for Kubernetes workloads. It attaches [Arthas](https://arthas.aliyun.com/) to a Java process running in a selected pod and exposes supported diagnostic operations from the Mission Control UI. ## What it does @@ -15,8 +15,8 @@ The Arthas plugin adds JVM diagnostics to Mission Control for Kubernetes workloa - Resolves the selected workload to a running pod and container. - Copies/installs `arthas-boot.jar` into the target pod when needed. - Attaches Arthas to the JVM in the target container. -- Opens Kubernetes port-forwards to the Arthas HTTP console and optional MCP endpoint. -- Proxies the Arthas UI/API through Mission Control. +- Opens a Kubernetes port-forward to the Arthas HTTP API for plugin-side operation handlers. +- Executes supported Arthas commands through declared Mission Control plugin operations. - Tracks active sessions inside the plugin process. ## Operations diff --git a/arthas/http.go b/arthas/http.go index 2081edc..6180303 100644 --- a/arthas/http.go +++ b/arthas/http.go @@ -1,16 +1,10 @@ package main import ( - "bytes" "context" "encoding/json" - "fmt" "io" - "log" "net/http" - "net/http/httputil" - "net/url" - "os" "strings" "github.com/flanksource/incident-commander/plugin/sdk" @@ -43,206 +37,3 @@ func (p *ArthasPlugin) httpInvoke(operation string, handler func(context.Context } }) } - -func (p *ArthasPlugin) HTTPHandler() http.Handler { - mux := http.NewServeMux() - mux.HandleFunc("/proxy/", p.httpProxyConsole) - mux.HandleFunc("/mcp/", p.httpProxyMCP) - mux.Handle("/version", sdk.VersionHandler(sdk.BuildInfo{ - Name: "arthas", - Version: Version, - BuildDate: BuildDate, - UIChecksum: uiChecksum, - })) - return mux -} - -func (p *ArthasPlugin) httpProxyConsole(w http.ResponseWriter, r *http.Request) { - p.proxyTo(w, r, "/proxy/", func(s sessionPorts) int { return s.HTTP }, true) -} - -func (p *ArthasPlugin) httpProxyMCP(w http.ResponseWriter, r *http.Request) { - p.proxyTo(w, r, "/mcp/", func(s sessionPorts) int { return s.MCP }, false) -} - -type sessionPorts struct { - HTTP int - MCP int -} - -func (p *ArthasPlugin) proxyTo(w http.ResponseWriter, r *http.Request, prefix string, portOf func(sessionPorts) int, rewriteHTML bool) { - rest := strings.TrimPrefix(r.URL.Path, prefix) - id, tail, _ := strings.Cut(rest, "/") - if id == "" { - http.Error(w, "missing session id", http.StatusBadRequest) - return - } - sess, ok := p.sessions.Get(id) - if !ok { - http.Error(w, "session not found", http.StatusNotFound) - return - } - port := portOf(sessionPorts{HTTP: sess.HTTPLocalPort, MCP: sess.MCPLocalPort}) - if port == 0 { - http.Error(w, "session endpoint is not enabled", http.StatusBadRequest) - return - } - target, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", port)) - proxy := httputil.NewSingleHostReverseProxy(target) - proxy.ErrorLog = log.New(os.Stderr, "[WARN] arthas console proxy: ", 0) - if rewriteHTML { - // The browser sees this iframe behind the host's UI proxy, so absolute - // URLs we inject must include the X-Forwarded-Prefix the host set. - hostPrefix := strings.TrimRight(r.Header.Get("X-Forwarded-Prefix"), "/") - basePrefix := fmt.Sprintf("%s%s%s/", hostPrefix, prefix, id) - proxy.ModifyResponse = func(resp *http.Response) error { - return rewriteArthasResponse(resp, basePrefix) - } - } - r.URL.Path = "/" + tail - r.URL.RawPath = "" - proxy.ServeHTTP(w, r) -} - -func rewriteArthasResponse(resp *http.Response, basePrefix string) error { - rewriteLocation(resp, basePrefix) - - ctype := resp.Header.Get("Content-Type") - isHTML := strings.Contains(ctype, "text/html") - isJS := strings.Contains(ctype, "javascript") - isCSS := strings.Contains(ctype, "text/css") - if !isHTML && !isJS && !isCSS { - return nil - } - body, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - _ = resp.Body.Close() - - proxyRoot := strings.TrimSuffix(basePrefix, "/") - if isHTML { - body = rewriteHTMLRootPaths(body, basePrefix) - wsShim := fmt.Appendf(nil, ``, basePrefix, proxyRoot) - idx := bytes.Index(body, []byte("
")) - if idx < 0 { - idx = bytes.Index(bytes.ToLower(body), []byte("")) - } - if idx >= 0 { - insertAt := idx + len("") - base := fmt.Appendf(nil, `