mirror of
https://github.com/openshift/openshift-mcp-server.git
synced 2025-10-17 14:27:48 +03:00
test: pods_exec tests executed from mcp client
This commit is contained in:
@@ -36,7 +36,6 @@ type Kubernetes struct {
|
||||
CloseWatchKubeConfig CloseWatchKubeConfig
|
||||
scheme *runtime.Scheme
|
||||
parameterCodec runtime.ParameterCodec
|
||||
restClient rest.Interface
|
||||
clientSet kubernetes.Interface
|
||||
discoveryClient *discovery.DiscoveryClient
|
||||
deferredDiscoveryRESTMapper *restmapper.DeferredDiscoveryRESTMapper
|
||||
@@ -48,11 +47,7 @@ func NewKubernetes() (*Kubernetes, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restClient, err := rest.HTTPClientFor(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientSet, err := kubernetes.NewForConfigAndClient(cfg, restClient)
|
||||
clientSet, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func (k *Kubernetes) PodsExec(ctx context.Context, namespace, name, container st
|
||||
func (k *Kubernetes) createExecutor(namespace, name string, podExecOptions *v1.PodExecOptions) (remotecommand.Executor, error) {
|
||||
// Compute URL
|
||||
// https://github.com/kubernetes/kubectl/blob/5366de04e168bcbc11f5e340d131a9ca8b7d0df4/pkg/cmd/exec/exec.go#L382-L397
|
||||
req := k.restClient.
|
||||
req := k.clientSet.CoreV1().RESTClient().
|
||||
Post().
|
||||
Resource("pods").
|
||||
Namespace(namespace).
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPodsExec(t *testing.T) {
|
||||
mockServer := NewMockServer()
|
||||
defer mockServer.Close()
|
||||
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path != "/api/v1/namespaces/default/pods/pod-to-exec/exec" {
|
||||
return
|
||||
}
|
||||
var stdin, stdout bytes.Buffer
|
||||
ctx, err := createHTTPStreams(w, req, &StreamOptions{
|
||||
Stdin: &stdin,
|
||||
Stdout: &stdout,
|
||||
})
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
defer ctx.conn.Close()
|
||||
_, _ = io.WriteString(ctx.stdoutStream, "total 0\n")
|
||||
}))
|
||||
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path != "/api/v1/namespaces/default/pods/pod-to-exec" {
|
||||
return
|
||||
}
|
||||
writeObject(w, &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "pod-to-exec",
|
||||
},
|
||||
Spec: v1.PodSpec{Containers: []v1.Container{{Name: "container-to-exec"}}},
|
||||
})
|
||||
}))
|
||||
k8s := mockServer.NewKubernetes()
|
||||
out, err := k8s.PodsExec(context.Background(), "default", "pod-to-exec", "", []string{"ls", "-l"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if out != "total 0\n" {
|
||||
t.Fatalf("unexpected output: %s", out)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package kubernetes
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -10,26 +10,21 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||
"k8s.io/apimachinery/pkg/util/httpstream/spdy"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
type MockServer struct {
|
||||
server *httptest.Server
|
||||
config *rest.Config
|
||||
restClient *rest.RESTClient
|
||||
restHandlers []http.HandlerFunc
|
||||
clientSet kubernetes.Interface
|
||||
parameterCodec runtime.ParameterCodec
|
||||
server *httptest.Server
|
||||
config *rest.Config
|
||||
restHandlers []http.HandlerFunc
|
||||
}
|
||||
|
||||
func NewMockServer() *MockServer {
|
||||
ms := &MockServer{}
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
ms.parameterCodec = runtime.NewParameterCodec(scheme)
|
||||
ms.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
for _, handler := range ms.restHandlers {
|
||||
handler(w, req)
|
||||
@@ -44,9 +39,7 @@ func NewMockServer() *MockServer {
|
||||
GroupVersion: &v1.SchemeGroupVersion,
|
||||
},
|
||||
}
|
||||
ms.restClient, _ = rest.RESTClientFor(ms.config)
|
||||
ms.restHandlers = make([]http.HandlerFunc, 0)
|
||||
ms.clientSet = kubernetes.NewForConfigOrDie(ms.config)
|
||||
return ms
|
||||
}
|
||||
|
||||
@@ -58,15 +51,6 @@ func (m *MockServer) Handle(handler http.Handler) {
|
||||
m.restHandlers = append(m.restHandlers, handler.ServeHTTP)
|
||||
}
|
||||
|
||||
func (m *MockServer) NewKubernetes() *Kubernetes {
|
||||
return &Kubernetes{
|
||||
cfg: m.config,
|
||||
restClient: m.restClient,
|
||||
clientSet: m.clientSet,
|
||||
parameterCodec: m.parameterCodec,
|
||||
}
|
||||
}
|
||||
|
||||
func writeObject(w http.ResponseWriter, obj runtime.Object) {
|
||||
w.Header().Set("Content-Type", runtime.ContentTypeJSON)
|
||||
if err := json.NewEncoder(w).Encode(obj); err != nil {
|
||||
67
pkg/mcp/pods_exec_test.go
Normal file
67
pkg/mcp/pods_exec_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"io"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPodsExec(t *testing.T) {
|
||||
testCase(t, func(c *mcpContext) {
|
||||
mockServer := NewMockServer()
|
||||
defer mockServer.Close()
|
||||
c.withKubeConfig(mockServer.config)
|
||||
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path != "/api/v1/namespaces/default/pods/pod-to-exec/exec" {
|
||||
return
|
||||
}
|
||||
var stdin, stdout bytes.Buffer
|
||||
ctx, err := createHTTPStreams(w, req, &StreamOptions{
|
||||
Stdin: &stdin,
|
||||
Stdout: &stdout,
|
||||
})
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
defer ctx.conn.Close()
|
||||
_, _ = io.WriteString(ctx.stdoutStream, strings.Join(req.URL.Query()["command"], " "))
|
||||
_, _ = io.WriteString(ctx.stdoutStream, "\ntotal 0\n")
|
||||
}))
|
||||
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path != "/api/v1/namespaces/default/pods/pod-to-exec" {
|
||||
return
|
||||
}
|
||||
writeObject(w, &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "pod-to-exec",
|
||||
},
|
||||
Spec: v1.PodSpec{Containers: []v1.Container{{Name: "container-to-exec"}}},
|
||||
})
|
||||
}))
|
||||
toolResult, err := c.callTool("pods_exec", map[string]interface{}{
|
||||
"namespace": "default",
|
||||
"name": "pod-to-exec",
|
||||
"command": []interface{}{"ls", "-l"},
|
||||
})
|
||||
t.Run("pods_exec returns command output", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
if toolResult.Content[0].(mcp.TextContent).Text != "ls -l\ntotal 0\n" {
|
||||
t.Errorf("unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user