diff --git a/go.mod b/go.mod index 1ac418b4..1008e7dd 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( k8s.io/cli-runtime v0.34.1 k8s.io/client-go v0.34.1 k8s.io/klog/v2 v2.130.1 - k8s.io/kubectl v0.33.4 + k8s.io/kubectl v0.34.1 k8s.io/metrics v0.34.1 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 sigs.k8s.io/controller-runtime v0.22.1 diff --git a/go.sum b/go.sum index 7259f54a..8ce50c22 100644 --- a/go.sum +++ b/go.sum @@ -445,8 +445,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/kubectl v0.33.4 h1:nXEI6Vi+oB9hXxoAHyHisXolm/l1qutK3oZQMak4N98= -k8s.io/kubectl v0.33.4/go.mod h1:Xe7P9X4DfILvKmlBsVqUtzktkI56lEj22SJW7cFy6nE= +k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI= +k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A= k8s.io/metrics v0.34.1 h1:374Rexmp1xxgRt64Bi0TsjAM8cA/Y8skwCoPdjtIslE= k8s.io/metrics v0.34.1/go.mod h1:Drf5kPfk2NJrlpcNdSiAAHn/7Y9KqxpRNagByM7Ei80= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= diff --git a/pkg/mcp/pods_top_test.go b/pkg/mcp/pods_top_test.go index bd8c8dc6..9fd218bb 100644 --- a/pkg/mcp/pods_top_test.go +++ b/pkg/mcp/pods_top_test.go @@ -71,12 +71,12 @@ func TestPodsTopMetricsAvailable(t *testing.T) { if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/pods" { if req.URL.Query().Get("labelSelector") == "app=pod-ns-5-42" { _, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` + - `{"metadata":{"name":"pod-ns-5-42","namespace":"ns-5"},"containers":[{"name":"container-1","usage":{"cpu":"42m","memory":"42Mi"}}]}` + + `{"metadata":{"name":"pod-ns-5-42","namespace":"ns-5"},"containers":[{"name":"container-1","usage":{"cpu":"42m","memory":"42Mi","swap":"42Mi"}}]}` + `]}`)) } else { _, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` + - `{"metadata":{"name":"pod-1","namespace":"default"},"containers":[{"name":"container-1","usage":{"cpu":"100m","memory":"200Mi"}},{"name":"container-2","usage":{"cpu":"200m","memory":"300Mi"}}]},` + - `{"metadata":{"name":"pod-2","namespace":"ns-1"},"containers":[{"name":"container-1-ns-1","usage":{"cpu":"300m","memory":"400Mi"}}]}` + + `{"metadata":{"name":"pod-1","namespace":"default"},"containers":[{"name":"container-1","usage":{"cpu":"100m","memory":"200Mi","swap":"13Mi"}},{"name":"container-2","usage":{"cpu":"200m","memory":"300Mi","swap":"37Mi"}}]},` + + `{"metadata":{"name":"pod-2","namespace":"ns-1"},"containers":[{"name":"container-1-ns-1","usage":{"cpu":"300m","memory":"400Mi","swap":"42Mi"}}]}` + `]}`)) } @@ -85,14 +85,14 @@ func TestPodsTopMetricsAvailable(t *testing.T) { // Pod Metrics from configured namespace if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods" { _, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` + - `{"metadata":{"name":"pod-1","namespace":"default"},"containers":[{"name":"container-1","usage":{"cpu":"10m","memory":"20Mi"}},{"name":"container-2","usage":{"cpu":"30m","memory":"40Mi"}}]}` + + `{"metadata":{"name":"pod-1","namespace":"default"},"containers":[{"name":"container-1","usage":{"cpu":"10m","memory":"20Mi","swap":"13Mi"}},{"name":"container-2","usage":{"cpu":"30m","memory":"40Mi","swap":"37Mi"}}]}` + `]}`)) return } // Pod Metrics from ns-5 namespace if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/namespaces/ns-5/pods" { _, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` + - `{"metadata":{"name":"pod-ns-5-1","namespace":"ns-5"},"containers":[{"name":"container-1","usage":{"cpu":"10m","memory":"20Mi"}}]}` + + `{"metadata":{"name":"pod-ns-5-1","namespace":"ns-5"},"containers":[{"name":"container-1","usage":{"cpu":"10m","memory":"20Mi","swap":"42Mi"}}]}` + `]}`)) return } @@ -100,7 +100,7 @@ func TestPodsTopMetricsAvailable(t *testing.T) { if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/namespaces/ns-5/pods/pod-ns-5-5" { _, _ = w.Write([]byte(`{"kind":"PodMetrics","apiVersion":"metrics.k8s.io/v1beta1",` + `"metadata":{"name":"pod-ns-5-5","namespace":"ns-5"},` + - `"containers":[{"name":"container-1","usage":{"cpu":"13m","memory":"37Mi"}}]` + + `"containers":[{"name":"container-1","usage":{"cpu":"13m","memory":"37Mi","swap":"42Mi"}}]` + `}`)) } })) @@ -113,21 +113,21 @@ func TestPodsTopMetricsAvailable(t *testing.T) { if podsTopDefaults.IsError { t.Fatalf("call tool failed %s", textContent) } - expectedHeaders := regexp.MustCompile(`(?m)^\s*NAMESPACE\s+POD\s+NAME\s+CPU\(cores\)\s+MEMORY\(bytes\)\s*$`) + expectedHeaders := regexp.MustCompile(`(?m)^\s*NAMESPACE\s+POD\s+NAME\s+CPU\(cores\)\s+MEMORY\(bytes\)\s+SWAP\(bytes\)\s*$`) if !expectedHeaders.MatchString(textContent) { t.Errorf("Expected headers '%s' not found in output:\n%s", expectedHeaders.String(), textContent) } expectedRows := []string{ - "default\\s+pod-1\\s+container-1\\s+100m\\s+200Mi", - "default\\s+pod-1\\s+container-2\\s+200m\\s+300Mi", - "ns-1\\s+pod-2\\s+container-1-ns-1\\s+300m\\s+400Mi", + "default\\s+pod-1\\s+container-1\\s+100m\\s+200Mi\\s+13Mi", + "default\\s+pod-1\\s+container-2\\s+200m\\s+300Mi\\s+37Mi", + "ns-1\\s+pod-2\\s+container-1-ns-1\\s+300m\\s+400Mi\\s+42Mi", } for _, row := range expectedRows { if !regexp.MustCompile(row).MatchString(textContent) { t.Errorf("Expected row '%s' not found in output:\n%s", row, textContent) } } - expectedTotal := regexp.MustCompile(`(?m)^\s+600m\s+900Mi\s*$`) + expectedTotal := regexp.MustCompile(`(?m)^\s+600m\s+900Mi\s+92Mi\s*$`) if !expectedTotal.MatchString(textContent) { t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent) } @@ -141,15 +141,15 @@ func TestPodsTopMetricsAvailable(t *testing.T) { } textContent := podsTopConfiguredNamespace.Content[0].(mcp.TextContent).Text expectedRows := []string{ - "default\\s+pod-1\\s+container-1\\s+10m\\s+20Mi", - "default\\s+pod-1\\s+container-2\\s+30m\\s+40Mi", + "default\\s+pod-1\\s+container-1\\s+10m\\s+20Mi\\s+13Mi", + "default\\s+pod-1\\s+container-2\\s+30m\\s+40Mi\\s+37Mi", } for _, row := range expectedRows { if !regexp.MustCompile(row).MatchString(textContent) { t.Errorf("Expected row '%s' not found in output:\n%s", row, textContent) } } - expectedTotal := regexp.MustCompile(`(?m)^\s+40m\s+60Mi\s*$`) + expectedTotal := regexp.MustCompile(`(?m)^\s+40m\s+60Mi\s+50Mi\s*$`) if !expectedTotal.MatchString(textContent) { t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent) } @@ -162,11 +162,11 @@ func TestPodsTopMetricsAvailable(t *testing.T) { t.Fatalf("call tool failed %v", err) } textContent := podsTopNamespace.Content[0].(mcp.TextContent).Text - expectedRow := regexp.MustCompile(`ns-5\s+pod-ns-5-1\s+container-1\s+10m\s+20Mi`) + expectedRow := regexp.MustCompile(`ns-5\s+pod-ns-5-1\s+container-1\s+10m\s+20Mi\s+42Mi`) if !expectedRow.MatchString(textContent) { t.Errorf("Expected row '%s' not found in output:\n%s", expectedRow.String(), textContent) } - expectedTotal := regexp.MustCompile(`(?m)^\s+10m\s+20Mi\s*$`) + expectedTotal := regexp.MustCompile(`(?m)^\s+10m\s+20Mi\s+42Mi\s*$`) if !expectedTotal.MatchString(textContent) { t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent) } @@ -180,11 +180,11 @@ func TestPodsTopMetricsAvailable(t *testing.T) { t.Fatalf("call tool failed %v", err) } textContent := podsTopNamespaceName.Content[0].(mcp.TextContent).Text - expectedRow := regexp.MustCompile(`ns-5\s+pod-ns-5-5\s+container-1\s+13m\s+37Mi`) + expectedRow := regexp.MustCompile(`ns-5\s+pod-ns-5-5\s+container-1\s+13m\s+37Mi\s+42Mi`) if !expectedRow.MatchString(textContent) { t.Errorf("Expected row '%s' not found in output:\n%s", expectedRow.String(), textContent) } - expectedTotal := regexp.MustCompile(`(?m)^\s+13m\s+37Mi\s*$`) + expectedTotal := regexp.MustCompile(`(?m)^\s+13m\s+37Mi\s+42Mi\s*$`) if !expectedTotal.MatchString(textContent) { t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent) } @@ -201,7 +201,7 @@ func TestPodsTopMetricsAvailable(t *testing.T) { if !expectedRow.MatchString(textContent) { t.Errorf("Expected row '%s' not found in output:\n%s", expectedRow.String(), textContent) } - expectedTotal := regexp.MustCompile(`(?m)^\s+42m\s+42Mi\s*$`) + expectedTotal := regexp.MustCompile(`(?m)^\s+42m\s+42Mi\s+42Mi\s*$`) if !expectedTotal.MatchString(textContent) { t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent) } diff --git a/pkg/toolsets/core/pods.go b/pkg/toolsets/core/pods.go index ce879089..5b6645ee 100644 --- a/pkg/toolsets/core/pods.go +++ b/pkg/toolsets/core/pods.go @@ -337,7 +337,7 @@ func podsTop(params api.ToolHandlerParams) (*api.ToolCallResult, error) { return api.NewToolCallResult("", fmt.Errorf("failed to get pods top: %v", err)), nil } buf := new(bytes.Buffer) - printer := metricsutil.NewTopCmdPrinter(buf) + printer := metricsutil.NewTopCmdPrinter(buf, true) err = printer.PrintPodMetrics(ret.Items, true, true, false, "", true) if err != nil { return api.NewToolCallResult("", fmt.Errorf("failed to get pods top: %v", err)), nil