@@ -2,16 +2,22 @@ package mcp
22
33import (
44 "context"
5+ "encoding/json"
6+ "fmt"
57 "github.com/mark3labs/mcp-go/client"
68 "github.com/mark3labs/mcp-go/mcp"
79 "github.com/mark3labs/mcp-go/server"
810 "github.com/spf13/afero"
911 corev1 "k8s.io/api/core/v1"
12+ apiextensionsv1spec "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
13+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
1014 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+ "k8s.io/apimachinery/pkg/watch"
1116 "k8s.io/client-go/kubernetes"
1217 "k8s.io/client-go/rest"
1318 "k8s.io/client-go/tools/clientcmd"
1419 "k8s.io/client-go/tools/clientcmd/api"
20+ toolswatch "k8s.io/client-go/tools/watch"
1521 "net/http/httptest"
1622 "os"
1723 "path/filepath"
@@ -142,14 +148,74 @@ func (c *mcpContext) withEnvTest() {
142148 c .withKubeConfig (envTestRestConfig )
143149}
144150
151+ // inOpenShift sets up the kubernetes environment to seem to be running OpenShift
152+ func (c * mcpContext ) inOpenShift () func () {
153+ c .withKubeConfig (envTestRestConfig )
154+ return c .crdApply (`
155+ {
156+ "apiVersion": "apiextensions.k8s.io/v1",
157+ "kind": "CustomResourceDefinition",
158+ "metadata": {"name": "routes.route.openshift.io"},
159+ "spec": {
160+ "group": "route.openshift.io",
161+ "versions": [{
162+ "name": "v1","served": true,"storage": true,
163+ "schema": {"openAPIV3Schema": {"type": "object","x-kubernetes-preserve-unknown-fields": true}}
164+ }],
165+ "scope": "Namespaced",
166+ "names": {"plural": "routes","singular": "route","kind": "Route"}
167+ }
168+ }` )
169+ }
170+
145171// newKubernetesClient creates a new Kubernetes client with the current kubeconfig
146172func (c * mcpContext ) newKubernetesClient () * kubernetes.Clientset {
147173 c .withEnvTest ()
148- pathOptions := clientcmd .NewDefaultPathOptions ()
149- cfg , _ := clientcmd .BuildConfigFromFlags ("" , pathOptions .GetDefaultFilename ())
174+ cfg , _ := clientcmd .BuildConfigFromFlags ("" , clientcmd .NewDefaultPathOptions ().GetDefaultFilename ())
150175 return kubernetes .NewForConfigOrDie (cfg )
151176}
152177
178+ // newApiExtensionsClient creates a new ApiExtensions client with the envTest kubeconfig
179+ func (c * mcpContext ) newApiExtensionsClient () * apiextensionsv1.ApiextensionsV1Client {
180+ return apiextensionsv1 .NewForConfigOrDie (envTestRestConfig )
181+ }
182+
183+ // crdApply creates a CRD from the provided resource string and waits for it to be established, returns a cleanup function
184+ func (c * mcpContext ) crdApply (resource string ) func () {
185+ apiExtensionsV1Client := c .newApiExtensionsClient ()
186+ var crd = & apiextensionsv1spec.CustomResourceDefinition {}
187+ err := json .Unmarshal ([]byte (resource ), crd )
188+ _ , err = apiExtensionsV1Client .CustomResourceDefinitions ().Create (c .ctx , crd , metav1.CreateOptions {})
189+ if err != nil {
190+ panic (fmt .Errorf ("failed to create CRD %v" , err ))
191+ }
192+ c .crdWaitUntilReady (crd .Name )
193+ return func () {
194+ err = apiExtensionsV1Client .CustomResourceDefinitions ().Delete (c .ctx , "routes.route.openshift.io" , metav1.DeleteOptions {})
195+ if err != nil {
196+ panic (fmt .Errorf ("failed to delete CRD %v" , err ))
197+ }
198+ }
199+ }
200+
201+ // crdWaitUntilReady waits for a CRD to be established
202+ func (c * mcpContext ) crdWaitUntilReady (name string ) {
203+ watcher , err := c .newApiExtensionsClient ().CustomResourceDefinitions ().Watch (c .ctx , metav1.ListOptions {
204+ FieldSelector : "metadata.name=" + name ,
205+ })
206+ _ , err = toolswatch .UntilWithoutRetry (c .ctx , watcher , func (event watch.Event ) (bool , error ) {
207+ for _ , c := range event .Object .(* apiextensionsv1spec.CustomResourceDefinition ).Status .Conditions {
208+ if c .Type == apiextensionsv1spec .Established && c .Status == apiextensionsv1spec .ConditionTrue {
209+ return true , nil
210+ }
211+ }
212+ return false , nil
213+ })
214+ if err != nil {
215+ panic (fmt .Errorf ("failed to wait for CRD %v" , err ))
216+ }
217+ }
218+
153219// callTool helper function to call a tool by name with arguments
154220func (c * mcpContext ) callTool (name string , args map [string ]interface {}) (* mcp.CallToolResult , error ) {
155221 callToolRequest := mcp.CallToolRequest {}
0 commit comments