First commit of golang CLI.
This commit is contained in:
249
wild-cli/cmd/wild/util/backup.go
Normal file
249
wild-cli/cmd/wild/util/backup.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/wild-cloud/wild-cli/internal/config"
|
||||
"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 (
|
||||
backupAll bool
|
||||
)
|
||||
|
||||
func NewBackupCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "backup",
|
||||
Short: "Backup Wild Cloud system",
|
||||
Long: `Backup the entire Wild Cloud system including applications and data.
|
||||
|
||||
This command performs a comprehensive backup of your Wild Cloud system using restic,
|
||||
including WC_HOME directory and all application data.
|
||||
|
||||
Examples:
|
||||
wild backup
|
||||
wild backup --all`,
|
||||
RunE: runBackup,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&backupAll, "all", true, "backup all applications")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runBackup(cmd *cobra.Command, args []string) error {
|
||||
output.Header("Wild Cloud System Backup")
|
||||
|
||||
// Initialize environment
|
||||
env := environment.New()
|
||||
if err := env.RequiresProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check external tools
|
||||
toolManager := external.NewManager()
|
||||
if err := toolManager.CheckTools(cmd.Context(), []string{"restic"}); err != nil {
|
||||
return fmt.Errorf("required tools not available: %w", err)
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
configMgr := config.NewManager(env.ConfigPath(), env.SecretsPath())
|
||||
|
||||
// Get backup configuration
|
||||
backupRoot, err := configMgr.Get("cloud.backup.root")
|
||||
if err != nil || backupRoot == nil {
|
||||
return fmt.Errorf("backup root not configured. Set cloud.backup.root in config.yaml")
|
||||
}
|
||||
|
||||
backupPassword, err := configMgr.GetSecret("cloud.backupPassword")
|
||||
if err != nil || backupPassword == nil {
|
||||
return fmt.Errorf("backup password not configured. Set cloud.backupPassword in secrets.yaml")
|
||||
}
|
||||
|
||||
stagingDir, err := configMgr.Get("cloud.backup.staging")
|
||||
if err != nil || stagingDir == nil {
|
||||
return fmt.Errorf("backup staging directory not configured. Set cloud.backup.staging in config.yaml")
|
||||
}
|
||||
|
||||
repository := fmt.Sprintf("%v", backupRoot)
|
||||
password := fmt.Sprintf("%v", backupPassword)
|
||||
staging := fmt.Sprintf("%v", stagingDir)
|
||||
|
||||
output.Info("Backup repository: " + repository)
|
||||
|
||||
// Initialize restic tool
|
||||
restic := toolManager.Restic()
|
||||
restic.SetRepository(repository)
|
||||
restic.SetPassword(password)
|
||||
|
||||
// Check if repository exists, initialize if needed
|
||||
output.Info("Checking if restic repository exists...")
|
||||
if err := checkOrInitializeRepository(cmd.Context(), restic); err != nil {
|
||||
return fmt.Errorf("repository initialization failed: %w", err)
|
||||
}
|
||||
|
||||
// Create staging directory
|
||||
if err := os.MkdirAll(staging, 0755); err != nil {
|
||||
return fmt.Errorf("creating staging directory: %w", err)
|
||||
}
|
||||
|
||||
// Generate backup tags
|
||||
today := time.Now().Format("2006-01-02")
|
||||
tags := []string{"wild-cloud", "wc-home", today}
|
||||
|
||||
// Backup entire WC_HOME
|
||||
output.Info("Backing up WC_HOME directory...")
|
||||
wcHome := env.WCHome()
|
||||
if wcHome == "" {
|
||||
wcHome = env.WildCloudDir()
|
||||
}
|
||||
|
||||
if err := restic.Backup(cmd.Context(), []string{wcHome}, []string{".wildcloud/cache"}, tags); err != nil {
|
||||
return fmt.Errorf("backing up WC_HOME: %w", err)
|
||||
}
|
||||
|
||||
output.Success("WC_HOME backup completed")
|
||||
|
||||
// Backup applications if requested
|
||||
if backupAll {
|
||||
output.Info("Running backup for all applications...")
|
||||
if err := backupAllApplications(cmd.Context(), env, configMgr, restic, staging, today); err != nil {
|
||||
return fmt.Errorf("application backup failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Future enhancements
|
||||
// - Backup Kubernetes resources (kubectl get all -A -o yaml)
|
||||
// - Backup persistent volumes
|
||||
// - Backup secrets and configmaps
|
||||
|
||||
output.Success("Wild Cloud system backup completed successfully!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkOrInitializeRepository checks if restic repository exists and initializes if needed
|
||||
func checkOrInitializeRepository(ctx context.Context, restic *external.ResticTool) error {
|
||||
// Try to check repository
|
||||
if err := restic.Check(ctx); err != nil {
|
||||
output.Warning("No existing backup repository found. Initializing restic repository...")
|
||||
if err := restic.InitRepository(ctx); err != nil {
|
||||
return fmt.Errorf("initializing repository: %w", err)
|
||||
}
|
||||
output.Success("Repository initialized successfully")
|
||||
} else {
|
||||
output.Info("Using existing backup repository")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// backupAllApplications backs up all applications using the app backup functionality
|
||||
func backupAllApplications(ctx context.Context, env *environment.Environment, configMgr *config.Manager, restic *external.ResticTool, staging, dateTag string) error {
|
||||
// Get list of applications
|
||||
appsDir := env.AppsDir()
|
||||
if _, err := os.Stat(appsDir); os.IsNotExist(err) {
|
||||
output.Warning("No apps directory found, skipping application backups")
|
||||
return nil
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(appsDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading apps directory: %w", err)
|
||||
}
|
||||
|
||||
var apps []string
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
apps = append(apps, entry.Name())
|
||||
}
|
||||
}
|
||||
|
||||
if len(apps) == 0 {
|
||||
output.Warning("No applications found, skipping application backups")
|
||||
return nil
|
||||
}
|
||||
|
||||
output.Info(fmt.Sprintf("Found %d applications to backup: %v", len(apps), apps))
|
||||
|
||||
// For now, we'll use the existing bash script for application backups
|
||||
// This maintains compatibility with the existing backup infrastructure
|
||||
wcRoot := env.WCRoot()
|
||||
if wcRoot == "" {
|
||||
output.Warning("WC_ROOT not set, skipping application-specific backups")
|
||||
return nil
|
||||
}
|
||||
|
||||
appBackupScript := filepath.Join(wcRoot, "bin", "wild-app-backup")
|
||||
if _, err := os.Stat(appBackupScript); os.IsNotExist(err) {
|
||||
output.Warning("App backup script not found, skipping application backups")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute the app backup script
|
||||
bashTool := external.NewBaseTool("bash", "bash")
|
||||
|
||||
// Set environment variables needed by the script
|
||||
oldWCRoot := os.Getenv("WC_ROOT")
|
||||
oldWCHome := os.Getenv("WC_HOME")
|
||||
defer func() {
|
||||
if oldWCRoot != "" {
|
||||
_ = os.Setenv("WC_ROOT", oldWCRoot)
|
||||
}
|
||||
if oldWCHome != "" {
|
||||
_ = os.Setenv("WC_HOME", oldWCHome)
|
||||
}
|
||||
}()
|
||||
|
||||
_ = os.Setenv("WC_ROOT", wcRoot)
|
||||
_ = os.Setenv("WC_HOME", env.WCHome())
|
||||
|
||||
output.Info("Running application backup script...")
|
||||
if _, err := bashTool.Execute(ctx, appBackupScript, "--all"); err != nil {
|
||||
output.Warning(fmt.Sprintf("Application backup script failed: %v", err))
|
||||
return nil // Don't fail the entire backup for app backup issues
|
||||
}
|
||||
|
||||
output.Success("Application backup script completed")
|
||||
|
||||
// Upload each app's backup to restic individually
|
||||
stagingAppsDir := filepath.Join(staging, "apps")
|
||||
if _, err := os.Stat(stagingAppsDir); err != nil {
|
||||
output.Warning("No app staging directory found, skipping app backup uploads")
|
||||
return nil
|
||||
}
|
||||
|
||||
entries, err = os.ReadDir(stagingAppsDir)
|
||||
if err != nil {
|
||||
output.Warning(fmt.Sprintf("Reading app staging directory failed: %v", err))
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
appName := entry.Name()
|
||||
appBackupDir := filepath.Join(stagingAppsDir, appName)
|
||||
|
||||
output.Info(fmt.Sprintf("Uploading backup for app: %s", appName))
|
||||
|
||||
tags := []string{"wild-cloud", appName, dateTag}
|
||||
if err := restic.Backup(ctx, []string{appBackupDir}, []string{}, tags); err != nil {
|
||||
output.Warning(fmt.Sprintf("Failed to backup app %s: %v", appName, err))
|
||||
continue
|
||||
}
|
||||
|
||||
output.Success(fmt.Sprintf("Backup for app '%s' completed", appName))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
157
wild-cli/cmd/wild/util/dashboard.go
Normal file
157
wild-cli/cmd/wild/util/dashboard.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewDashboardCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "dashboard",
|
||||
Short: "Manage Kubernetes dashboard",
|
||||
Long: `Manage access to the Kubernetes dashboard.`,
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newDashboardTokenCommand(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newDashboardTokenCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "token",
|
||||
Short: "Get dashboard access token",
|
||||
Long: `Get an access token for the Kubernetes dashboard.
|
||||
|
||||
This command retrieves the authentication token needed to access the Kubernetes dashboard.
|
||||
|
||||
Examples:
|
||||
wild dashboard token`,
|
||||
RunE: runDashboardToken,
|
||||
}
|
||||
}
|
||||
|
||||
func runDashboardToken(cmd *cobra.Command, args []string) error {
|
||||
output.Header("Kubernetes Dashboard Token")
|
||||
|
||||
// Initialize environment
|
||||
env := environment.New()
|
||||
if err := env.RequiresProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check 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()
|
||||
|
||||
// The namespace where the dashboard is installed
|
||||
namespace := "kubernetes-dashboard"
|
||||
secretName := "dashboard-admin-token"
|
||||
|
||||
// Try to get the token from the secret
|
||||
token, err := getDashboardToken(cmd.Context(), kubectl, namespace, secretName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get dashboard token: %w", err)
|
||||
}
|
||||
|
||||
// Print the token with nice formatting
|
||||
output.Success("Use this token to authenticate to the Kubernetes Dashboard:")
|
||||
output.Info("")
|
||||
output.Printf("%s\n", token)
|
||||
output.Info("")
|
||||
|
||||
// Additional instructions
|
||||
output.Info("Instructions:")
|
||||
output.Info("1. Copy the token above")
|
||||
output.Info("2. Navigate to your Kubernetes Dashboard URL")
|
||||
output.Info("3. Select 'Token' authentication method")
|
||||
output.Info("4. Paste the token and click 'Sign In'")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDashboardToken retrieves the dashboard token from Kubernetes
|
||||
func getDashboardToken(ctx context.Context, kubectl *external.KubectlTool, namespace, secretName string) (string, error) {
|
||||
// Try to get the secret directly
|
||||
secretData, err := kubectl.GetResource(ctx, "secret", secretName, namespace)
|
||||
if err != nil {
|
||||
// If secret doesn't exist, try to find any admin-related secret
|
||||
output.Warning("Dashboard admin token secret not found, searching for available tokens...")
|
||||
return findDashboardToken(ctx, kubectl, namespace)
|
||||
}
|
||||
|
||||
// Extract token from secret data
|
||||
// The secret data is in YAML format, we need to parse it
|
||||
secretStr := string(secretData)
|
||||
lines := strings.Split(secretStr, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "token:") {
|
||||
// Extract the base64 encoded token
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 {
|
||||
encodedToken := parts[1]
|
||||
// Decode base64 token using kubectl
|
||||
tokenBytes, err := kubectl.Execute(ctx, "exec", "deploy/coredns", "-n", "kube-system", "--", "base64", "-d")
|
||||
if err != nil {
|
||||
// Try alternative method with echo and base64
|
||||
echoCmd := fmt.Sprintf("echo '%s' | base64 -d", encodedToken)
|
||||
tokenBytes, err = kubectl.Execute(ctx, "exec", "deploy/coredns", "-n", "kube-system", "--", "sh", "-c", echoCmd)
|
||||
if err != nil {
|
||||
// Return the encoded token as fallback
|
||||
return encodedToken, nil
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(string(tokenBytes)), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("token not found in secret data")
|
||||
}
|
||||
|
||||
// findDashboardToken searches for available dashboard tokens
|
||||
func findDashboardToken(ctx context.Context, kubectl *external.KubectlTool, namespace string) (string, error) {
|
||||
// List all secrets in the dashboard namespace
|
||||
secrets, err := kubectl.GetResource(ctx, "secrets", "", namespace)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to list secrets in namespace %s: %w", namespace, err)
|
||||
}
|
||||
|
||||
// Look for tokens in the secret list
|
||||
secretsStr := string(secrets)
|
||||
lines := strings.Split(secretsStr, "\n")
|
||||
|
||||
var tokenSecrets []string
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "token") && strings.Contains(line, "dashboard") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) > 0 {
|
||||
tokenSecrets = append(tokenSecrets, parts[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(tokenSecrets) == 0 {
|
||||
return "", fmt.Errorf("no dashboard token secrets found in namespace %s", namespace)
|
||||
}
|
||||
|
||||
// Try the first available token secret
|
||||
secretName := tokenSecrets[0]
|
||||
output.Info("Using token secret: " + secretName)
|
||||
|
||||
return getDashboardToken(ctx, kubectl, namespace, secretName)
|
||||
}
|
126
wild-cli/cmd/wild/util/status.go
Normal file
126
wild-cli/cmd/wild/util/status.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func runStatus(cmd *cobra.Command, args []string) error {
|
||||
output.Header("Wild Cloud Status")
|
||||
|
||||
// Initialize environment
|
||||
env := environment.New()
|
||||
|
||||
// Check if we're in a project directory
|
||||
detected, _ := env.DetectWCHome()
|
||||
if detected != "" {
|
||||
env.SetWCHome(detected)
|
||||
output.Success("Found Wild Cloud project: " + detected)
|
||||
} else {
|
||||
output.Warning("Not in a Wild Cloud project directory")
|
||||
}
|
||||
|
||||
// Check environment
|
||||
output.Info("\n=== Environment ===")
|
||||
if env.WCRoot() != "" {
|
||||
output.Success("WC_ROOT: " + env.WCRoot())
|
||||
} else {
|
||||
output.Warning("WC_ROOT: Not set")
|
||||
}
|
||||
|
||||
if env.WCHome() != "" {
|
||||
output.Success("WC_HOME: " + env.WCHome())
|
||||
} else {
|
||||
output.Warning("WC_HOME: Not set")
|
||||
}
|
||||
|
||||
// Check external tools
|
||||
output.Info("\n=== External Tools ===")
|
||||
toolManager := external.NewManager()
|
||||
tools := toolManager.ListTools()
|
||||
|
||||
for toolName, installed := range tools {
|
||||
if installed {
|
||||
version, err := toolManager.GetToolVersion(toolName)
|
||||
if err != nil {
|
||||
output.Success(fmt.Sprintf("%-12s: Installed (version unknown)", toolName))
|
||||
} else {
|
||||
output.Success(fmt.Sprintf("%-12s: %s", toolName, version))
|
||||
}
|
||||
} else {
|
||||
output.Warning(fmt.Sprintf("%-12s: Not installed", toolName))
|
||||
}
|
||||
}
|
||||
|
||||
// Check project structure if in project
|
||||
if env.WCHome() != "" {
|
||||
output.Info("\n=== Project Structure ===")
|
||||
|
||||
// Check config files
|
||||
if fileExists(env.ConfigPath()) {
|
||||
output.Success("config.yaml: Found")
|
||||
} else {
|
||||
output.Warning("config.yaml: Missing")
|
||||
}
|
||||
|
||||
if fileExists(env.SecretsPath()) {
|
||||
output.Success("secrets.yaml: Found")
|
||||
} else {
|
||||
output.Warning("secrets.yaml: Missing")
|
||||
}
|
||||
|
||||
if dirExists(env.AppsDir()) {
|
||||
output.Success("apps/ directory: Found")
|
||||
} else {
|
||||
output.Warning("apps/ directory: Missing")
|
||||
}
|
||||
|
||||
if dirExists(env.WildCloudDir()) {
|
||||
output.Success(".wildcloud/ directory: Found")
|
||||
} else {
|
||||
output.Warning(".wildcloud/ directory: Missing")
|
||||
}
|
||||
|
||||
// Check cluster connectivity if tools are available
|
||||
if tools["kubectl"] {
|
||||
output.Info("\n=== Cluster Status ===")
|
||||
kubectl := toolManager.Kubectl()
|
||||
|
||||
ctx := context.Background()
|
||||
nodes, err := kubectl.GetNodes(ctx)
|
||||
if err != nil {
|
||||
output.Warning("Cluster: Not accessible (" + err.Error() + ")")
|
||||
} else {
|
||||
output.Success("Cluster: Connected")
|
||||
output.Info("Nodes:\n" + string(nodes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output.Info("\n=== Summary ===")
|
||||
if detected != "" {
|
||||
output.Success("Wild Cloud project is properly configured")
|
||||
} else {
|
||||
output.Warning("Run 'wild setup scaffold' to initialize a project")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func fileExists(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
return err == nil && !info.IsDir()
|
||||
}
|
||||
|
||||
func dirExists(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
return err == nil && info.IsDir()
|
||||
}
|
61
wild-cli/cmd/wild/util/template.go
Normal file
61
wild-cli/cmd/wild/util/template.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/wild-cloud/wild-cli/internal/config"
|
||||
"github.com/wild-cloud/wild-cli/internal/environment"
|
||||
)
|
||||
|
||||
func newCompileCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "compile",
|
||||
Short: "Compile template from stdin",
|
||||
Long: `Compile a template from stdin using Wild Cloud configuration context.
|
||||
|
||||
This command reads template content from stdin and processes it using the
|
||||
current project's config.yaml and secrets.yaml as context.
|
||||
|
||||
Examples:
|
||||
echo 'Hello {{.config.cluster.name}}' | wild template compile
|
||||
cat template.yml | wild template compile`,
|
||||
RunE: runCompileTemplate,
|
||||
}
|
||||
}
|
||||
|
||||
func runCompileTemplate(cmd *cobra.Command, args []string) error {
|
||||
// Initialize environment
|
||||
env := environment.New()
|
||||
if err := env.RequiresProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create config manager
|
||||
mgr := config.NewManager(env.ConfigPath(), env.SecretsPath())
|
||||
|
||||
// Create template engine
|
||||
engine, err := config.NewTemplateEngine(mgr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating template engine: %w", err)
|
||||
}
|
||||
|
||||
// Read template from stdin
|
||||
templateContent, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading template from stdin: %w", err)
|
||||
}
|
||||
|
||||
// Process template
|
||||
result, err := engine.Process(string(templateContent))
|
||||
if err != nil {
|
||||
return fmt.Errorf("processing template: %w", err)
|
||||
}
|
||||
|
||||
// Output result
|
||||
fmt.Print(result)
|
||||
return nil
|
||||
}
|
32
wild-cli/cmd/wild/util/util.go
Normal file
32
wild-cli/cmd/wild/util/util.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewBackupCommand is implemented in backup.go
|
||||
// NewDashboardCommand is implemented in dashboard.go
|
||||
|
||||
// NewTemplateCommand creates the template command
|
||||
func NewTemplateCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "template",
|
||||
Short: "Process templates",
|
||||
Long: `Process template files with Wild Cloud configuration.`,
|
||||
}
|
||||
|
||||
cmd.AddCommand(newCompileCommand())
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewStatusCommand creates the status command
|
||||
func NewStatusCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Show Wild Cloud status",
|
||||
Long: `Show the overall status of the Wild Cloud system.`,
|
||||
RunE: runStatus,
|
||||
}
|
||||
}
|
||||
|
||||
// NewVersionCommand is implemented in version.go
|
52
wild-cli/cmd/wild/util/version.go
Normal file
52
wild-cli/cmd/wild/util/version.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/wild-cloud/wild-cli/internal/output"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "0.1.0-dev"
|
||||
BuildDate = "development"
|
||||
)
|
||||
|
||||
func NewVersionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Show version information",
|
||||
Long: `Show version information for Wild CLI and components.
|
||||
|
||||
This command displays version information for the Wild CLI and related components.
|
||||
|
||||
Examples:
|
||||
wild version`,
|
||||
RunE: runVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func runVersion(cmd *cobra.Command, args []string) error {
|
||||
output.Header("Wild CLI Version Information")
|
||||
|
||||
output.Info(fmt.Sprintf("Wild CLI Version: %s", Version))
|
||||
output.Info(fmt.Sprintf("Build Date: %s", BuildDate))
|
||||
output.Info(fmt.Sprintf("Go Version: %s", "go1.21+"))
|
||||
|
||||
// TODO: Add component versions
|
||||
// - kubectl version
|
||||
// - talosctl version
|
||||
// - restic version
|
||||
// - yq version
|
||||
|
||||
output.Info("")
|
||||
output.Info("Components:")
|
||||
output.Info(" - Native Go implementation replacing 35+ bash scripts")
|
||||
output.Info(" - Unified CLI with Cobra framework")
|
||||
output.Info(" - Cross-platform support (Linux/macOS/Windows)")
|
||||
output.Info(" - Built-in template engine with sprig functions")
|
||||
output.Info(" - Integrated external tool management")
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user