pkg/tools/kubectl_tool.go (73 lines of code) (raw):

// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tools import ( "context" "os" "os/exec" "strings" "github.com/GoogleCloudPlatform/kubectl-ai/gollm" ) func init() { RegisterTool(&Kubectl{}) } type Kubectl struct{} func (t *Kubectl) Name() string { return "kubectl" } func (t *Kubectl) Description() string { return "Executes a kubectl command against the user's Kubernetes cluster. Use this tool only when you need to query or modify the state of the user's Kubernetes cluster." } func (t *Kubectl) FunctionDefinition() *gollm.FunctionDefinition { return &gollm.FunctionDefinition{ Name: t.Name(), Description: t.Description(), Parameters: &gollm.Schema{ Type: gollm.TypeObject, Properties: map[string]*gollm.Schema{ "command": { Type: gollm.TypeString, Description: `The complete kubectl command to execute. Prefer to use heredoc syntax for multi-line commands. Please include the kubectl prefix as well. Example: user: what pods are running in the cluster? assistant: kubectl get pods user: what is the status of the pod my-pod? assistant: kubectl get pod my-pod -o jsonpath='{.status.phase}' `, }, "modifies_resource": { Type: gollm.TypeString, Description: `Whether the command modifies a kubernetes resource. Possible values: - "yes" if the command modifies a resource - "no" if the command does not modify a resource - "unknown" if the command's effect on the resource is unknown `, }, }, }, } } func (t *Kubectl) Run(ctx context.Context, args map[string]any) (any, error) { kubeconfig := ctx.Value("kubeconfig").(string) workDir := ctx.Value("work_dir").(string) command := args["command"].(string) return runKubectlCommand(ctx, command, workDir, kubeconfig) } func runKubectlCommand(ctx context.Context, command, workDir, kubeconfig string) (*ExecResult, error) { if strings.Contains(command, "kubectl edit") { return &ExecResult{Error: "interactive mode not supported for kubectl, please use non-interactive commands"}, nil } if strings.Contains(command, "kubectl port-forward") { return &ExecResult{Error: "port-forwarding is not allowed because assistant is running in an unattended mode, please try some other alternative"}, nil } cmd := exec.CommandContext(ctx, bashBin, "-c", command) cmd.Env = os.Environ() cmd.Dir = workDir if kubeconfig != "" { kubeconfig, err := expandShellVar(kubeconfig) if err != nil { return nil, err } cmd.Env = append(cmd.Env, "KUBECONFIG="+kubeconfig) } return executeCommand(cmd) }