139 lines
4.2 KiB
Go
139 lines
4.2 KiB
Go
package app
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/wild-cloud/wild-cli/internal/environment"
|
|
"github.com/wild-cloud/wild-cli/internal/external"
|
|
"github.com/wild-cloud/wild-cli/internal/output"
|
|
)
|
|
|
|
var (
|
|
deleteForce bool
|
|
deleteDryRun bool
|
|
)
|
|
|
|
func newDeleteCommand() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "delete <name>",
|
|
Short: "Delete an application from the cluster",
|
|
Long: `Delete a Wild Cloud app and all its resources.
|
|
|
|
This will delete:
|
|
- App deployment, services, and other Kubernetes resources
|
|
- App secrets from the app's namespace
|
|
- App namespace (if empty after resource deletion)
|
|
- Local app configuration files from apps/<app_name>
|
|
|
|
Examples:
|
|
wild app delete nextcloud
|
|
wild app delete nextcloud --force
|
|
wild app delete nextcloud --dry-run`,
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: runAppDelete,
|
|
}
|
|
|
|
cmd.Flags().BoolVar(&deleteForce, "force", false, "skip confirmation prompts")
|
|
cmd.Flags().BoolVar(&deleteDryRun, "dry-run", false, "show what would be deleted without actually deleting")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func runAppDelete(cmd *cobra.Command, args []string) error {
|
|
appName := args[0]
|
|
|
|
// Initialize environment
|
|
env := environment.New()
|
|
if err := env.RequiresProject(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check if app exists
|
|
appDir := filepath.Join(env.AppsDir(), appName)
|
|
if _, err := os.Stat(appDir); os.IsNotExist(err) {
|
|
return fmt.Errorf("app directory 'apps/%s' not found", appName)
|
|
}
|
|
|
|
// Initialize external tools
|
|
toolManager := external.NewManager()
|
|
if err := toolManager.CheckTools(cmd.Context(), []string{"kubectl"}); err != nil {
|
|
return fmt.Errorf("required tools not available: %w", err)
|
|
}
|
|
|
|
kubectl := toolManager.Kubectl()
|
|
|
|
// Confirmation prompt (unless --force or --dry-run)
|
|
if !deleteForce && !deleteDryRun {
|
|
output.Warning(fmt.Sprintf("This will delete all resources for app '%s'", appName))
|
|
output.Info("This includes:")
|
|
output.Info(" - Kubernetes deployments, services, secrets, and other resources")
|
|
output.Info(" - App namespace (if empty after deletion)")
|
|
output.Info(" - Local configuration files in apps/" + appName + "/")
|
|
output.Info("")
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
output.Printf("Are you sure you want to delete app '%s'? (y/N): ", appName)
|
|
response, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read confirmation: %w", err)
|
|
}
|
|
response = strings.TrimSpace(strings.ToLower(response))
|
|
if response != "y" && response != "yes" {
|
|
output.Info("Deletion cancelled")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if deleteDryRun {
|
|
output.Header("DRY RUN: Deleting app '" + appName + "'")
|
|
} else {
|
|
output.Header("Deleting app '" + appName + "'")
|
|
}
|
|
|
|
// Step 1: Delete namespace (this will delete ALL resources)
|
|
output.Info("Deleting namespace and all remaining resources...")
|
|
|
|
var kubectlArgs []string
|
|
if deleteDryRun {
|
|
kubectlArgs = []string{"delete", "namespace", appName, "--dry-run=client", "--ignore-not-found=true"}
|
|
} else {
|
|
kubectlArgs = []string{"delete", "namespace", appName, "--ignore-not-found=true"}
|
|
}
|
|
|
|
if _, err := kubectl.Execute(cmd.Context(), kubectlArgs...); err != nil {
|
|
return fmt.Errorf("failed to delete namespace: %w", err)
|
|
}
|
|
|
|
// Wait for namespace deletion to complete (only if not dry-run)
|
|
if !deleteDryRun {
|
|
output.Info("Waiting for namespace deletion to complete...")
|
|
waitArgs := []string{"wait", "--for=delete", "namespace", appName, "--timeout=60s"}
|
|
// Ignore error as namespace might already be deleted
|
|
_, _ = kubectl.Execute(cmd.Context(), waitArgs...)
|
|
}
|
|
|
|
// Step 2: Delete local app configuration files
|
|
output.Info("Deleting local app configuration...")
|
|
if deleteDryRun {
|
|
output.Info(fmt.Sprintf("DRY RUN: Would delete directory 'apps/%s/'", appName))
|
|
} else {
|
|
if err := os.RemoveAll(appDir); err != nil {
|
|
return fmt.Errorf("failed to delete local configuration directory: %w", err)
|
|
}
|
|
output.Info(fmt.Sprintf("Deleted local configuration directory: apps/%s/", appName))
|
|
}
|
|
|
|
output.Success(fmt.Sprintf("App '%s' deletion complete!", appName))
|
|
output.Info("")
|
|
output.Info("Note: Dependency apps (if any) were not deleted.")
|
|
output.Info("If you want to delete dependencies, run wild app delete for each dependency separately.")
|
|
|
|
return nil
|
|
}
|