package external import ( "context" "fmt" "strings" ) // KubectlTool wraps kubectl operations type KubectlTool struct { *BaseTool kubeconfig string } // NewKubectlTool creates a new kubectl tool wrapper func NewKubectlTool() *KubectlTool { return &KubectlTool{ BaseTool: NewBaseTool("kubectl", "kubectl"), } } // SetKubeconfig sets the kubeconfig file path func (k *KubectlTool) SetKubeconfig(path string) { k.kubeconfig = path } // Apply applies Kubernetes manifests func (k *KubectlTool) Apply(ctx context.Context, manifests []string, namespace string, dryRun bool) error { for _, manifest := range manifests { args := []string{"apply", "-f", "-"} if namespace != "" { args = append(args, "--namespace", namespace) } if dryRun { args = append(args, "--dry-run=client") } if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } _, err := k.ExecuteWithInput(ctx, manifest, args...) if err != nil { return fmt.Errorf("applying manifest: %w", err) } } return nil } // ApplyKustomize applies using kustomize func (k *KubectlTool) ApplyKustomize(ctx context.Context, path string, namespace string, dryRun bool) error { args := []string{"apply", "-k", path} if namespace != "" { args = append(args, "--namespace", namespace) } if dryRun { args = append(args, "--dry-run=client") } if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } _, err := k.Execute(ctx, args...) if err != nil { return fmt.Errorf("applying kustomize: %w", err) } return nil } // Delete deletes Kubernetes resources func (k *KubectlTool) Delete(ctx context.Context, resource, name, namespace string, ignoreNotFound bool) error { args := []string{"delete", resource, name} if namespace != "" { args = append(args, "--namespace", namespace) } if ignoreNotFound { args = append(args, "--ignore-not-found=true") } if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } _, err := k.Execute(ctx, args...) if err != nil { return fmt.Errorf("deleting resource: %w", err) } return nil } // CreateSecret creates a Kubernetes secret func (k *KubectlTool) CreateSecret(ctx context.Context, name, namespace string, data map[string]string) error { // First try to delete existing secret _ = k.Delete(ctx, "secret", name, namespace, true) args := []string{"create", "secret", "generic", name} for key, value := range data { args = append(args, "--from-literal="+key+"="+value) } if namespace != "" { args = append(args, "--namespace", namespace) } if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } _, err := k.Execute(ctx, args...) if err != nil { return fmt.Errorf("creating secret: %w", err) } return nil } // GetResource gets a Kubernetes resource func (k *KubectlTool) GetResource(ctx context.Context, resource, name, namespace string) ([]byte, error) { args := []string{"get", resource} if name != "" { args = append(args, name) } if namespace != "" { args = append(args, "--namespace", namespace) } args = append(args, "-o", "yaml") if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } output, err := k.Execute(ctx, args...) if err != nil { return nil, fmt.Errorf("getting resource: %w", err) } return output, nil } // WaitForDeletion waits for a resource to be deleted func (k *KubectlTool) WaitForDeletion(ctx context.Context, resource, name, namespace string, timeout string) error { args := []string{"wait", "--for=delete", resource + "/" + name} if namespace != "" { args = append(args, "--namespace", namespace) } if timeout != "" { args = append(args, "--timeout="+timeout) } if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } _, err := k.Execute(ctx, args...) if err != nil { return fmt.Errorf("waiting for deletion: %w", err) } return nil } // GetNodes returns information about cluster nodes func (k *KubectlTool) GetNodes(ctx context.Context) ([]byte, error) { args := []string{"get", "nodes", "-o", "wide"} if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } output, err := k.Execute(ctx, args...) if err != nil { return nil, fmt.Errorf("getting nodes: %w", err) } return output, nil } // GetServiceAccount gets a service account token func (k *KubectlTool) GetServiceAccountToken(ctx context.Context, serviceAccount, namespace string) (string, error) { // Get the service account args := []string{"get", "serviceaccount", serviceAccount, "--namespace", namespace, "-o", "jsonpath={.secrets[0].name}"} if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } secretName, err := k.Execute(ctx, args...) if err != nil { return "", fmt.Errorf("getting service account secret: %w", err) } secretNameStr := strings.TrimSpace(string(secretName)) if secretNameStr == "" { return "", fmt.Errorf("no secret found for service account %s", serviceAccount) } // Get the token from the secret args = []string{"get", "secret", secretNameStr, "--namespace", namespace, "-o", "jsonpath={.data.token}"} if k.kubeconfig != "" { args = append([]string{"--kubeconfig", k.kubeconfig}, args...) } tokenBytes, err := k.Execute(ctx, args...) if err != nil { return "", fmt.Errorf("getting token from secret: %w", err) } return string(tokenBytes), nil }