Files
wild-cloud/wild-cli/internal/external/kubectl.go

227 lines
5.4 KiB
Go

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
}