|
| 1 | +package start |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + |
| 6 | + "github.com/containers/kubernetes-mcp-server/pkg/api" |
| 7 | + "github.com/containers/kubernetes-mcp-server/pkg/output" |
| 8 | + "github.com/google/jsonschema-go/jsonschema" |
| 9 | + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 10 | + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" |
| 11 | + "k8s.io/apimachinery/pkg/runtime/schema" |
| 12 | + "k8s.io/client-go/dynamic" |
| 13 | + "k8s.io/utils/ptr" |
| 14 | +) |
| 15 | + |
| 16 | +func Tools() []api.ServerTool { |
| 17 | + return []api.ServerTool{ |
| 18 | + { |
| 19 | + Tool: api.Tool{ |
| 20 | + Name: "vm_start", |
| 21 | + Description: "Start a halted or stopped VirtualMachine by changing its runStrategy to Always", |
| 22 | + InputSchema: &jsonschema.Schema{ |
| 23 | + Type: "object", |
| 24 | + Properties: map[string]*jsonschema.Schema{ |
| 25 | + "namespace": { |
| 26 | + Type: "string", |
| 27 | + Description: "The namespace of the virtual machine", |
| 28 | + }, |
| 29 | + "name": { |
| 30 | + Type: "string", |
| 31 | + Description: "The name of the virtual machine to start", |
| 32 | + }, |
| 33 | + }, |
| 34 | + Required: []string{"namespace", "name"}, |
| 35 | + }, |
| 36 | + Annotations: api.ToolAnnotations{ |
| 37 | + Title: "Virtual Machine: Start", |
| 38 | + ReadOnlyHint: ptr.To(false), |
| 39 | + DestructiveHint: ptr.To(false), |
| 40 | + IdempotentHint: ptr.To(true), |
| 41 | + OpenWorldHint: ptr.To(false), |
| 42 | + }, |
| 43 | + }, |
| 44 | + Handler: start, |
| 45 | + }, |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +func start(params api.ToolHandlerParams) (*api.ToolCallResult, error) { |
| 50 | + // Parse required parameters |
| 51 | + namespace, err := getRequiredString(params, "namespace") |
| 52 | + if err != nil { |
| 53 | + return api.NewToolCallResult("", err), nil |
| 54 | + } |
| 55 | + |
| 56 | + name, err := getRequiredString(params, "name") |
| 57 | + if err != nil { |
| 58 | + return api.NewToolCallResult("", err), nil |
| 59 | + } |
| 60 | + |
| 61 | + // Get dynamic client |
| 62 | + restConfig := params.RESTConfig() |
| 63 | + if restConfig == nil { |
| 64 | + return api.NewToolCallResult("", fmt.Errorf("failed to get REST config")), nil |
| 65 | + } |
| 66 | + |
| 67 | + dynamicClient, err := dynamic.NewForConfig(restConfig) |
| 68 | + if err != nil { |
| 69 | + return api.NewToolCallResult("", fmt.Errorf("failed to create dynamic client: %w", err)), nil |
| 70 | + } |
| 71 | + |
| 72 | + // Get the current VM |
| 73 | + gvr := schema.GroupVersionResource{ |
| 74 | + Group: "kubevirt.io", |
| 75 | + Version: "v1", |
| 76 | + Resource: "virtualmachines", |
| 77 | + } |
| 78 | + |
| 79 | + vm, err := dynamicClient.Resource(gvr).Namespace(namespace).Get( |
| 80 | + params.Context, |
| 81 | + name, |
| 82 | + metav1.GetOptions{}, |
| 83 | + ) |
| 84 | + if err != nil { |
| 85 | + return api.NewToolCallResult("", fmt.Errorf("failed to get VirtualMachine: %w", err)), nil |
| 86 | + } |
| 87 | + |
| 88 | + // Update runStrategy to Always |
| 89 | + if err := unstructured.SetNestedField(vm.Object, "Always", "spec", "runStrategy"); err != nil { |
| 90 | + return api.NewToolCallResult("", fmt.Errorf("failed to set runStrategy: %w", err)), nil |
| 91 | + } |
| 92 | + |
| 93 | + // Update the VM |
| 94 | + updatedVM, err := dynamicClient.Resource(gvr).Namespace(namespace).Update( |
| 95 | + params.Context, |
| 96 | + vm, |
| 97 | + metav1.UpdateOptions{}, |
| 98 | + ) |
| 99 | + if err != nil { |
| 100 | + return api.NewToolCallResult("", fmt.Errorf("failed to update VirtualMachine: %w", err)), nil |
| 101 | + } |
| 102 | + |
| 103 | + // Format the output |
| 104 | + marshalledYaml, err := output.MarshalYaml(updatedVM) |
| 105 | + if err != nil { |
| 106 | + return api.NewToolCallResult("", fmt.Errorf("failed to marshal VirtualMachine: %w", err)), nil |
| 107 | + } |
| 108 | + |
| 109 | + return api.NewToolCallResult("# VirtualMachine started successfully\n"+marshalledYaml, nil), nil |
| 110 | +} |
| 111 | + |
| 112 | +func getRequiredString(params api.ToolHandlerParams, key string) (string, error) { |
| 113 | + args := params.GetArguments() |
| 114 | + val, ok := args[key] |
| 115 | + if !ok { |
| 116 | + return "", fmt.Errorf("%s parameter required", key) |
| 117 | + } |
| 118 | + str, ok := val.(string) |
| 119 | + if !ok { |
| 120 | + return "", fmt.Errorf("%s parameter must be a string", key) |
| 121 | + } |
| 122 | + return str, nil |
| 123 | +} |
0 commit comments