v3.42.11: Replace all Unicode emojis with ASCII text
All checks were successful
CI/CD / Test (push) Successful in 1m13s
CI/CD / Lint (push) Successful in 1m20s
CI/CD / Build & Release (push) Successful in 3m10s

- Replace all emoji characters with ASCII equivalents throughout codebase
- Replace Unicode box-drawing characters (═║╔╗╚╝━─) with ASCII (+|-=)
- Replace checkmarks (✓✗) with [OK]/[FAIL] markers
- 59 files updated, 741 lines changed
- Improves terminal compatibility and reduces visual noise
This commit is contained in:
2026-01-08 09:42:01 +01:00
parent 5fb88b14ba
commit 3e41d88445
61 changed files with 1040 additions and 739 deletions

View File

@@ -252,8 +252,8 @@ func runCatalogSync(cmd *cobra.Command, args []string) error {
}
defer cat.Close()
fmt.Printf("📁 Syncing backups from: %s\n", absDir)
fmt.Printf("📊 Catalog database: %s\n\n", catalogDBPath)
fmt.Printf("[DIR] Syncing backups from: %s\n", absDir)
fmt.Printf("[STATS] Catalog database: %s\n\n", catalogDBPath)
ctx := context.Background()
result, err := cat.SyncFromDirectory(ctx, absDir)
@@ -265,17 +265,17 @@ func runCatalogSync(cmd *cobra.Command, args []string) error {
cat.SetLastSync(ctx)
// Show results
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
fmt.Printf(" Sync Results\n")
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf(" Added: %d\n", result.Added)
fmt.Printf(" 🔄 Updated: %d\n", result.Updated)
fmt.Printf(" 🗑️ Removed: %d\n", result.Removed)
fmt.Printf("=====================================================\n")
fmt.Printf(" [OK] Added: %d\n", result.Added)
fmt.Printf(" [SYNC] Updated: %d\n", result.Updated)
fmt.Printf(" [DEL] Removed: %d\n", result.Removed)
if result.Errors > 0 {
fmt.Printf(" Errors: %d\n", result.Errors)
fmt.Printf(" [FAIL] Errors: %d\n", result.Errors)
}
fmt.Printf(" ⏱️ Duration: %.2fs\n", result.Duration)
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf(" [TIME] Duration: %.2fs\n", result.Duration)
fmt.Printf("=====================================================\n")
// Show details if verbose
if catalogVerbose && len(result.Details) > 0 {
@@ -323,7 +323,7 @@ func runCatalogList(cmd *cobra.Command, args []string) error {
// Table format
fmt.Printf("%-30s %-12s %-10s %-20s %-10s %s\n",
"DATABASE", "TYPE", "SIZE", "CREATED", "STATUS", "PATH")
fmt.Println(strings.Repeat("", 120))
fmt.Println(strings.Repeat("-", 120))
for _, entry := range entries {
dbName := truncateString(entry.Database, 28)
@@ -331,10 +331,10 @@ func runCatalogList(cmd *cobra.Command, args []string) error {
status := string(entry.Status)
if entry.VerifyValid != nil && *entry.VerifyValid {
status = " verified"
status = "[OK] verified"
}
if entry.DrillSuccess != nil && *entry.DrillSuccess {
status = " tested"
status = "[OK] tested"
}
fmt.Printf("%-30s %-12s %-10s %-20s %-10s %s\n",
@@ -377,20 +377,20 @@ func runCatalogStats(cmd *cobra.Command, args []string) error {
}
// Table format
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
if catalogDatabase != "" {
fmt.Printf(" Catalog Statistics: %s\n", catalogDatabase)
} else {
fmt.Printf(" Catalog Statistics\n")
}
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n")
fmt.Printf("=====================================================\n\n")
fmt.Printf("📊 Total Backups: %d\n", stats.TotalBackups)
fmt.Printf("💾 Total Size: %s\n", stats.TotalSizeHuman)
fmt.Printf("📏 Average Size: %s\n", catalog.FormatSize(stats.AvgSize))
fmt.Printf("⏱️ Average Duration: %.1fs\n", stats.AvgDuration)
fmt.Printf(" Verified: %d\n", stats.VerifiedCount)
fmt.Printf("🧪 Drill Tested: %d\n", stats.DrillTestedCount)
fmt.Printf("[STATS] Total Backups: %d\n", stats.TotalBackups)
fmt.Printf("[SAVE] Total Size: %s\n", stats.TotalSizeHuman)
fmt.Printf("[SIZE] Average Size: %s\n", catalog.FormatSize(stats.AvgSize))
fmt.Printf("[TIME] Average Duration: %.1fs\n", stats.AvgDuration)
fmt.Printf("[OK] Verified: %d\n", stats.VerifiedCount)
fmt.Printf("[TEST] Drill Tested: %d\n", stats.DrillTestedCount)
if stats.OldestBackup != nil {
fmt.Printf("📅 Oldest Backup: %s\n", stats.OldestBackup.Format("2006-01-02 15:04"))
@@ -400,27 +400,27 @@ func runCatalogStats(cmd *cobra.Command, args []string) error {
}
if len(stats.ByDatabase) > 0 && catalogDatabase == "" {
fmt.Printf("\n📁 By Database:\n")
fmt.Printf("\n[DIR] By Database:\n")
for db, count := range stats.ByDatabase {
fmt.Printf(" %-30s %d\n", db, count)
}
}
if len(stats.ByType) > 0 {
fmt.Printf("\n📦 By Type:\n")
fmt.Printf("\n[PKG] By Type:\n")
for t, count := range stats.ByType {
fmt.Printf(" %-15s %d\n", t, count)
}
}
if len(stats.ByStatus) > 0 {
fmt.Printf("\n📋 By Status:\n")
fmt.Printf("\n[LOG] By Status:\n")
for s, count := range stats.ByStatus {
fmt.Printf(" %-15s %d\n", s, count)
}
}
fmt.Printf("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("\n=====================================================\n")
return nil
}
@@ -488,26 +488,26 @@ func runCatalogGaps(cmd *cobra.Command, args []string) error {
}
if len(allGaps) == 0 {
fmt.Printf(" No backup gaps detected (expected interval: %s)\n", interval)
fmt.Printf("[OK] No backup gaps detected (expected interval: %s)\n", interval)
return nil
}
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
fmt.Printf(" Backup Gaps Detected (expected interval: %s)\n", interval)
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n")
fmt.Printf("=====================================================\n\n")
totalGaps := 0
criticalGaps := 0
for database, gaps := range allGaps {
fmt.Printf("📁 %s (%d gaps)\n", database, len(gaps))
fmt.Printf("[DIR] %s (%d gaps)\n", database, len(gaps))
for _, gap := range gaps {
totalGaps++
icon := ""
icon := "[INFO]"
switch gap.Severity {
case catalog.SeverityWarning:
icon = "⚠️"
icon = "[WARN]"
case catalog.SeverityCritical:
icon = "🚨"
criticalGaps++
@@ -523,7 +523,7 @@ func runCatalogGaps(cmd *cobra.Command, args []string) error {
fmt.Println()
}
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
fmt.Printf("Total: %d gaps detected", totalGaps)
if criticalGaps > 0 {
fmt.Printf(" (%d critical)", criticalGaps)
@@ -598,20 +598,20 @@ func runCatalogSearch(cmd *cobra.Command, args []string) error {
fmt.Printf("Found %d matching backups:\n\n", len(entries))
for _, entry := range entries {
fmt.Printf("📁 %s\n", entry.Database)
fmt.Printf("[DIR] %s\n", entry.Database)
fmt.Printf(" Path: %s\n", entry.BackupPath)
fmt.Printf(" Type: %s | Size: %s | Created: %s\n",
entry.DatabaseType,
catalog.FormatSize(entry.SizeBytes),
entry.CreatedAt.Format("2006-01-02 15:04:05"))
if entry.Encrypted {
fmt.Printf(" 🔒 Encrypted\n")
fmt.Printf(" [LOCK] Encrypted\n")
}
if entry.VerifyValid != nil && *entry.VerifyValid {
fmt.Printf(" Verified: %s\n", entry.VerifiedAt.Format("2006-01-02 15:04"))
fmt.Printf(" [OK] Verified: %s\n", entry.VerifiedAt.Format("2006-01-02 15:04"))
}
if entry.DrillSuccess != nil && *entry.DrillSuccess {
fmt.Printf(" 🧪 Drill Tested: %s\n", entry.DrillTestedAt.Format("2006-01-02 15:04"))
fmt.Printf(" [TEST] Drill Tested: %s\n", entry.DrillTestedAt.Format("2006-01-02 15:04"))
}
fmt.Println()
}
@@ -655,64 +655,64 @@ func runCatalogInfo(cmd *cobra.Command, args []string) error {
return nil
}
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
fmt.Printf(" Backup Details\n")
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n")
fmt.Printf("=====================================================\n\n")
fmt.Printf("📁 Database: %s\n", entry.Database)
fmt.Printf("[DIR] Database: %s\n", entry.Database)
fmt.Printf("🔧 Type: %s\n", entry.DatabaseType)
fmt.Printf("🖥️ Host: %s:%d\n", entry.Host, entry.Port)
fmt.Printf("[HOST] Host: %s:%d\n", entry.Host, entry.Port)
fmt.Printf("📂 Path: %s\n", entry.BackupPath)
fmt.Printf("📦 Backup Type: %s\n", entry.BackupType)
fmt.Printf("💾 Size: %s (%d bytes)\n", catalog.FormatSize(entry.SizeBytes), entry.SizeBytes)
fmt.Printf("🔐 SHA256: %s\n", entry.SHA256)
fmt.Printf("[PKG] Backup Type: %s\n", entry.BackupType)
fmt.Printf("[SAVE] Size: %s (%d bytes)\n", catalog.FormatSize(entry.SizeBytes), entry.SizeBytes)
fmt.Printf("[HASH] SHA256: %s\n", entry.SHA256)
fmt.Printf("📅 Created: %s\n", entry.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("⏱️ Duration: %.2fs\n", entry.Duration)
fmt.Printf("📋 Status: %s\n", entry.Status)
fmt.Printf("[TIME] Duration: %.2fs\n", entry.Duration)
fmt.Printf("[LOG] Status: %s\n", entry.Status)
if entry.Compression != "" {
fmt.Printf("📦 Compression: %s\n", entry.Compression)
fmt.Printf("[PKG] Compression: %s\n", entry.Compression)
}
if entry.Encrypted {
fmt.Printf("🔒 Encrypted: yes\n")
fmt.Printf("[LOCK] Encrypted: yes\n")
}
if entry.CloudLocation != "" {
fmt.Printf("☁️ Cloud: %s\n", entry.CloudLocation)
fmt.Printf("[CLOUD] Cloud: %s\n", entry.CloudLocation)
}
if entry.RetentionPolicy != "" {
fmt.Printf("📆 Retention: %s\n", entry.RetentionPolicy)
}
fmt.Printf("\n📊 Verification:\n")
fmt.Printf("\n[STATS] Verification:\n")
if entry.VerifiedAt != nil {
status := " Failed"
status := "[FAIL] Failed"
if entry.VerifyValid != nil && *entry.VerifyValid {
status = " Valid"
status = "[OK] Valid"
}
fmt.Printf(" Status: %s (checked %s)\n", status, entry.VerifiedAt.Format("2006-01-02 15:04"))
} else {
fmt.Printf(" Status: Not verified\n")
fmt.Printf(" Status: [WAIT] Not verified\n")
}
fmt.Printf("\n🧪 DR Drill Test:\n")
fmt.Printf("\n[TEST] DR Drill Test:\n")
if entry.DrillTestedAt != nil {
status := " Failed"
status := "[FAIL] Failed"
if entry.DrillSuccess != nil && *entry.DrillSuccess {
status = " Passed"
status = "[OK] Passed"
}
fmt.Printf(" Status: %s (tested %s)\n", status, entry.DrillTestedAt.Format("2006-01-02 15:04"))
} else {
fmt.Printf(" Status: Not tested\n")
fmt.Printf(" Status: [WAIT] Not tested\n")
}
if len(entry.Metadata) > 0 {
fmt.Printf("\n📝 Additional Metadata:\n")
fmt.Printf("\n[NOTE] Additional Metadata:\n")
for k, v := range entry.Metadata {
fmt.Printf(" %s: %s\n", k, v)
}
}
fmt.Printf("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("\n=====================================================\n")
return nil
}

View File

@@ -115,7 +115,7 @@ func runCleanup(cmd *cobra.Command, args []string) error {
DryRun: dryRun,
}
fmt.Printf("🗑️ Cleanup Policy:\n")
fmt.Printf("[CLEANUP] Cleanup Policy:\n")
fmt.Printf(" Directory: %s\n", backupDir)
fmt.Printf(" Retention: %d days\n", policy.RetentionDays)
fmt.Printf(" Min backups: %d\n", policy.MinBackups)
@@ -142,16 +142,16 @@ func runCleanup(cmd *cobra.Command, args []string) error {
}
// Display results
fmt.Printf("📊 Results:\n")
fmt.Printf("[RESULTS] Results:\n")
fmt.Printf(" Total backups: %d\n", result.TotalBackups)
fmt.Printf(" Eligible for deletion: %d\n", result.EligibleForDeletion)
if len(result.Deleted) > 0 {
fmt.Printf("\n")
if dryRun {
fmt.Printf("🔍 Would delete %d backup(s):\n", len(result.Deleted))
fmt.Printf("[DRY-RUN] Would delete %d backup(s):\n", len(result.Deleted))
} else {
fmt.Printf(" Deleted %d backup(s):\n", len(result.Deleted))
fmt.Printf("[OK] Deleted %d backup(s):\n", len(result.Deleted))
}
for _, file := range result.Deleted {
fmt.Printf(" - %s\n", filepath.Base(file))
@@ -159,33 +159,33 @@ func runCleanup(cmd *cobra.Command, args []string) error {
}
if len(result.Kept) > 0 && len(result.Kept) <= 10 {
fmt.Printf("\n📦 Kept %d backup(s):\n", len(result.Kept))
fmt.Printf("\n[KEPT] Kept %d backup(s):\n", len(result.Kept))
for _, file := range result.Kept {
fmt.Printf(" - %s\n", filepath.Base(file))
}
} else if len(result.Kept) > 10 {
fmt.Printf("\n📦 Kept %d backup(s)\n", len(result.Kept))
fmt.Printf("\n[KEPT] Kept %d backup(s)\n", len(result.Kept))
}
if !dryRun && result.SpaceFreed > 0 {
fmt.Printf("\n💾 Space freed: %s\n", metadata.FormatSize(result.SpaceFreed))
fmt.Printf("\n[FREED] Space freed: %s\n", metadata.FormatSize(result.SpaceFreed))
}
if len(result.Errors) > 0 {
fmt.Printf("\n⚠️ Errors:\n")
fmt.Printf("\n[WARN] Errors:\n")
for _, err := range result.Errors {
fmt.Printf(" - %v\n", err)
}
}
fmt.Println(strings.Repeat("", 50))
fmt.Println(strings.Repeat("-", 50))
if dryRun {
fmt.Println(" Dry run completed (no files were deleted)")
fmt.Println("[OK] Dry run completed (no files were deleted)")
} else if len(result.Deleted) > 0 {
fmt.Println(" Cleanup completed successfully")
fmt.Println("[OK] Cleanup completed successfully")
} else {
fmt.Println(" No backups eligible for deletion")
fmt.Println("[INFO] No backups eligible for deletion")
}
return nil
@@ -212,7 +212,7 @@ func runCloudCleanup(ctx context.Context, uri string) error {
return fmt.Errorf("invalid cloud URI: %w", err)
}
fmt.Printf("☁️ Cloud Cleanup Policy:\n")
fmt.Printf("[CLOUD] Cloud Cleanup Policy:\n")
fmt.Printf(" URI: %s\n", uri)
fmt.Printf(" Provider: %s\n", cloudURI.Provider)
fmt.Printf(" Bucket: %s\n", cloudURI.Bucket)
@@ -295,7 +295,7 @@ func runCloudCleanup(ctx context.Context, uri string) error {
}
// Display results
fmt.Printf("📊 Results:\n")
fmt.Printf("[RESULTS] Results:\n")
fmt.Printf(" Total backups: %d\n", totalBackups)
fmt.Printf(" Eligible for deletion: %d\n", len(toDelete))
fmt.Printf(" Will keep: %d\n", len(toKeep))
@@ -303,9 +303,9 @@ func runCloudCleanup(ctx context.Context, uri string) error {
if len(toDelete) > 0 {
if dryRun {
fmt.Printf("🔍 Would delete %d backup(s):\n", len(toDelete))
fmt.Printf("[DRY-RUN] Would delete %d backup(s):\n", len(toDelete))
} else {
fmt.Printf("🗑️ Deleting %d backup(s):\n", len(toDelete))
fmt.Printf("[DELETE] Deleting %d backup(s):\n", len(toDelete))
}
var totalSize int64
@@ -321,7 +321,7 @@ func runCloudCleanup(ctx context.Context, uri string) error {
if !dryRun {
if err := backend.Delete(ctx, backup.Key); err != nil {
fmt.Printf(" Error: %v\n", err)
fmt.Printf(" [FAIL] Error: %v\n", err)
} else {
deletedCount++
// Also try to delete metadata
@@ -330,12 +330,12 @@ func runCloudCleanup(ctx context.Context, uri string) error {
}
}
fmt.Printf("\n💾 Space %s: %s\n",
fmt.Printf("\n[FREED] Space %s: %s\n",
map[bool]string{true: "would be freed", false: "freed"}[dryRun],
cloud.FormatSize(totalSize))
if !dryRun && deletedCount > 0 {
fmt.Printf(" Successfully deleted %d backup(s)\n", deletedCount)
fmt.Printf("[OK] Successfully deleted %d backup(s)\n", deletedCount)
}
} else {
fmt.Println("No backups eligible for deletion")
@@ -405,7 +405,7 @@ func runGFSCleanup(backupDir string) error {
}
// Display tier breakdown
fmt.Printf("📊 Backup Classification:\n")
fmt.Printf("[STATS] Backup Classification:\n")
fmt.Printf(" Yearly: %d\n", result.YearlyKept)
fmt.Printf(" Monthly: %d\n", result.MonthlyKept)
fmt.Printf(" Weekly: %d\n", result.WeeklyKept)
@@ -416,9 +416,9 @@ func runGFSCleanup(backupDir string) error {
// Display deletions
if len(result.Deleted) > 0 {
if dryRun {
fmt.Printf("🔍 Would delete %d backup(s):\n", len(result.Deleted))
fmt.Printf("[SEARCH] Would delete %d backup(s):\n", len(result.Deleted))
} else {
fmt.Printf(" Deleted %d backup(s):\n", len(result.Deleted))
fmt.Printf("[OK] Deleted %d backup(s):\n", len(result.Deleted))
}
for _, file := range result.Deleted {
fmt.Printf(" - %s\n", filepath.Base(file))
@@ -427,7 +427,7 @@ func runGFSCleanup(backupDir string) error {
// Display kept backups (limited display)
if len(result.Kept) > 0 && len(result.Kept) <= 15 {
fmt.Printf("\n📦 Kept %d backup(s):\n", len(result.Kept))
fmt.Printf("\n[PKG] Kept %d backup(s):\n", len(result.Kept))
for _, file := range result.Kept {
// Show tier classification
info, _ := os.Stat(file)
@@ -440,28 +440,28 @@ func runGFSCleanup(backupDir string) error {
}
}
} else if len(result.Kept) > 15 {
fmt.Printf("\n📦 Kept %d backup(s)\n", len(result.Kept))
fmt.Printf("\n[PKG] Kept %d backup(s)\n", len(result.Kept))
}
if !dryRun && result.SpaceFreed > 0 {
fmt.Printf("\n💾 Space freed: %s\n", metadata.FormatSize(result.SpaceFreed))
fmt.Printf("\n[SAVE] Space freed: %s\n", metadata.FormatSize(result.SpaceFreed))
}
if len(result.Errors) > 0 {
fmt.Printf("\n⚠️ Errors:\n")
fmt.Printf("\n[WARN] Errors:\n")
for _, err := range result.Errors {
fmt.Printf(" - %v\n", err)
}
}
fmt.Println(strings.Repeat("", 50))
fmt.Println(strings.Repeat("-", 50))
if dryRun {
fmt.Println(" GFS dry run completed (no files were deleted)")
fmt.Println("[OK] GFS dry run completed (no files were deleted)")
} else if len(result.Deleted) > 0 {
fmt.Println(" GFS cleanup completed successfully")
fmt.Println("[OK] GFS cleanup completed successfully")
} else {
fmt.Println(" No backups eligible for deletion under GFS policy")
fmt.Println("[INFO] No backups eligible for deletion under GFS policy")
}
return nil

View File

@@ -189,12 +189,12 @@ func runCloudUpload(cmd *cobra.Command, args []string) error {
}
}
fmt.Printf("☁️ Uploading %d file(s) to %s...\n\n", len(files), backend.Name())
fmt.Printf("[CLOUD] Uploading %d file(s) to %s...\n\n", len(files), backend.Name())
successCount := 0
for _, localPath := range files {
filename := filepath.Base(localPath)
fmt.Printf("📤 %s\n", filename)
fmt.Printf("[UPLOAD] %s\n", filename)
// Progress callback
var lastPercent int
@@ -214,21 +214,21 @@ func runCloudUpload(cmd *cobra.Command, args []string) error {
err := backend.Upload(ctx, localPath, filename, progress)
if err != nil {
fmt.Printf(" Failed: %v\n\n", err)
fmt.Printf(" [FAIL] Failed: %v\n\n", err)
continue
}
// Get file size
if info, err := os.Stat(localPath); err == nil {
fmt.Printf(" Uploaded (%s)\n\n", cloud.FormatSize(info.Size()))
fmt.Printf(" [OK] Uploaded (%s)\n\n", cloud.FormatSize(info.Size()))
} else {
fmt.Printf(" Uploaded\n\n")
fmt.Printf(" [OK] Uploaded\n\n")
}
successCount++
}
fmt.Println(strings.Repeat("", 50))
fmt.Printf(" Successfully uploaded %d/%d file(s)\n", successCount, len(files))
fmt.Println(strings.Repeat("-", 50))
fmt.Printf("[OK] Successfully uploaded %d/%d file(s)\n", successCount, len(files))
return nil
}
@@ -248,8 +248,8 @@ func runCloudDownload(cmd *cobra.Command, args []string) error {
localPath = filepath.Join(localPath, filepath.Base(remotePath))
}
fmt.Printf("☁️ Downloading from %s...\n\n", backend.Name())
fmt.Printf("📥 %s %s\n", remotePath, localPath)
fmt.Printf("[CLOUD] Downloading from %s...\n\n", backend.Name())
fmt.Printf("[DOWNLOAD] %s -> %s\n", remotePath, localPath)
// Progress callback
var lastPercent int
@@ -274,9 +274,9 @@ func runCloudDownload(cmd *cobra.Command, args []string) error {
// Get file size
if info, err := os.Stat(localPath); err == nil {
fmt.Printf(" Downloaded (%s)\n", cloud.FormatSize(info.Size()))
fmt.Printf(" [OK] Downloaded (%s)\n", cloud.FormatSize(info.Size()))
} else {
fmt.Printf(" Downloaded\n")
fmt.Printf(" [OK] Downloaded\n")
}
return nil
@@ -294,7 +294,7 @@ func runCloudList(cmd *cobra.Command, args []string) error {
prefix = args[0]
}
fmt.Printf("☁️ Listing backups in %s/%s...\n\n", backend.Name(), cloudBucket)
fmt.Printf("[CLOUD] Listing backups in %s/%s...\n\n", backend.Name(), cloudBucket)
backups, err := backend.List(ctx, prefix)
if err != nil {
@@ -311,7 +311,7 @@ func runCloudList(cmd *cobra.Command, args []string) error {
totalSize += backup.Size
if cloudVerbose {
fmt.Printf("📦 %s\n", backup.Name)
fmt.Printf("[FILE] %s\n", backup.Name)
fmt.Printf(" Size: %s\n", cloud.FormatSize(backup.Size))
fmt.Printf(" Modified: %s\n", backup.LastModified.Format(time.RFC3339))
if backup.StorageClass != "" {
@@ -328,7 +328,7 @@ func runCloudList(cmd *cobra.Command, args []string) error {
}
}
fmt.Println(strings.Repeat("", 50))
fmt.Println(strings.Repeat("-", 50))
fmt.Printf("Total: %d backup(s), %s\n", len(backups), cloud.FormatSize(totalSize))
return nil
@@ -360,7 +360,7 @@ func runCloudDelete(cmd *cobra.Command, args []string) error {
// Confirmation prompt
if !cloudConfirm {
fmt.Printf("⚠️ Delete %s (%s) from cloud storage?\n", remotePath, cloud.FormatSize(size))
fmt.Printf("[WARN] Delete %s (%s) from cloud storage?\n", remotePath, cloud.FormatSize(size))
fmt.Print("Type 'yes' to confirm: ")
var response string
fmt.Scanln(&response)
@@ -370,14 +370,14 @@ func runCloudDelete(cmd *cobra.Command, args []string) error {
}
}
fmt.Printf("🗑️ Deleting %s...\n", remotePath)
fmt.Printf("[DELETE] Deleting %s...\n", remotePath)
err = backend.Delete(ctx, remotePath)
if err != nil {
return fmt.Errorf("delete failed: %w", err)
}
fmt.Printf(" Deleted %s (%s)\n", remotePath, cloud.FormatSize(size))
fmt.Printf("[OK] Deleted %s (%s)\n", remotePath, cloud.FormatSize(size))
return nil
}

View File

@@ -61,10 +61,10 @@ func runCPUInfo(ctx context.Context) error {
// Show current vs optimal
if cfg.AutoDetectCores {
fmt.Println("\n CPU optimization is enabled")
fmt.Println("\n[OK] CPU optimization is enabled")
fmt.Println("Job counts are automatically optimized based on detected hardware")
} else {
fmt.Println("\n⚠️ CPU optimization is disabled")
fmt.Println("\n[WARN] CPU optimization is disabled")
fmt.Println("Consider enabling --auto-detect-cores for better performance")
}

View File

@@ -372,9 +372,9 @@ func runDedupRestore(cmd *cobra.Command, args []string) error {
// Verify hash
if manifest.SHA256 != "" {
if restoredHash == manifest.SHA256 {
fmt.Printf(" Verification: SHA-256 matches\n")
fmt.Printf(" Verification: [OK] SHA-256 matches\n")
} else {
fmt.Printf(" Verification: SHA-256 MISMATCH!\n")
fmt.Printf(" Verification: [FAIL] SHA-256 MISMATCH!\n")
fmt.Printf(" Expected: %s\n", manifest.SHA256)
fmt.Printf(" Got: %s\n", restoredHash)
return fmt.Errorf("integrity verification failed")

View File

@@ -318,7 +318,7 @@ func runDrillList(cmd *cobra.Command, args []string) error {
}
fmt.Printf("%-15s %-40s %-20s %s\n", "ID", "NAME", "IMAGE", "STATUS")
fmt.Println(strings.Repeat("", 100))
fmt.Println(strings.Repeat("-", 100))
for _, c := range containers {
fmt.Printf("%-15s %-40s %-20s %s\n",
@@ -345,7 +345,7 @@ func runDrillCleanup(cmd *cobra.Command, args []string) error {
return err
}
fmt.Println(" Cleanup completed")
fmt.Println("[OK] Cleanup completed")
return nil
}
@@ -369,32 +369,32 @@ func runDrillReport(cmd *cobra.Command, args []string) error {
func printDrillResult(result *drill.DrillResult) {
fmt.Printf("\n")
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
fmt.Printf(" DR Drill Report: %s\n", result.DrillID)
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n")
fmt.Printf("=====================================================\n\n")
status := " PASSED"
status := "[OK] PASSED"
if !result.Success {
status = " FAILED"
status = "[FAIL] FAILED"
} else if result.Status == drill.StatusPartial {
status = "⚠️ PARTIAL"
status = "[WARN] PARTIAL"
}
fmt.Printf("📋 Status: %s\n", status)
fmt.Printf("💾 Backup: %s\n", filepath.Base(result.BackupPath))
fmt.Printf("🗄️ Database: %s (%s)\n", result.DatabaseName, result.DatabaseType)
fmt.Printf("⏱️ Duration: %.2fs\n", result.Duration)
fmt.Printf("[LOG] Status: %s\n", status)
fmt.Printf("[SAVE] Backup: %s\n", filepath.Base(result.BackupPath))
fmt.Printf("[DB] Database: %s (%s)\n", result.DatabaseName, result.DatabaseType)
fmt.Printf("[TIME] Duration: %.2fs\n", result.Duration)
fmt.Printf("📅 Started: %s\n", result.StartTime.Format(time.RFC3339))
fmt.Printf("\n")
// Phases
fmt.Printf("📊 Phases:\n")
fmt.Printf("[STATS] Phases:\n")
for _, phase := range result.Phases {
icon := ""
icon := "[OK]"
if phase.Status == "failed" {
icon = ""
icon = "[FAIL]"
} else if phase.Status == "running" {
icon = "🔄"
icon = "[SYNC]"
}
fmt.Printf(" %s %-20s (%.2fs) %s\n", icon, phase.Name, phase.Duration, phase.Message)
}
@@ -412,10 +412,10 @@ func printDrillResult(result *drill.DrillResult) {
fmt.Printf("\n")
// RTO
fmt.Printf("⏱️ RTO Analysis:\n")
rtoIcon := ""
fmt.Printf("[TIME] RTO Analysis:\n")
rtoIcon := "[OK]"
if !result.RTOMet {
rtoIcon = ""
rtoIcon = "[FAIL]"
}
fmt.Printf(" Actual RTO: %.2fs\n", result.ActualRTO)
fmt.Printf(" Target RTO: %.0fs\n", result.TargetRTO)
@@ -424,11 +424,11 @@ func printDrillResult(result *drill.DrillResult) {
// Validation results
if len(result.ValidationResults) > 0 {
fmt.Printf("🔍 Validation Queries:\n")
fmt.Printf("[SEARCH] Validation Queries:\n")
for _, vr := range result.ValidationResults {
icon := ""
icon := "[OK]"
if !vr.Success {
icon = ""
icon = "[FAIL]"
}
fmt.Printf(" %s %s: %s\n", icon, vr.Name, vr.Result)
if vr.Error != "" {
@@ -440,11 +440,11 @@ func printDrillResult(result *drill.DrillResult) {
// Check results
if len(result.CheckResults) > 0 {
fmt.Printf(" Checks:\n")
fmt.Printf("[OK] Checks:\n")
for _, cr := range result.CheckResults {
icon := ""
icon := "[OK]"
if !cr.Success {
icon = ""
icon = "[FAIL]"
}
fmt.Printf(" %s %s\n", icon, cr.Message)
}
@@ -453,7 +453,7 @@ func printDrillResult(result *drill.DrillResult) {
// Errors and warnings
if len(result.Errors) > 0 {
fmt.Printf(" Errors:\n")
fmt.Printf("[FAIL] Errors:\n")
for _, e := range result.Errors {
fmt.Printf(" • %s\n", e)
}
@@ -461,7 +461,7 @@ func printDrillResult(result *drill.DrillResult) {
}
if len(result.Warnings) > 0 {
fmt.Printf("⚠️ Warnings:\n")
fmt.Printf("[WARN] Warnings:\n")
for _, w := range result.Warnings {
fmt.Printf(" • %s\n", w)
}
@@ -470,14 +470,14 @@ func printDrillResult(result *drill.DrillResult) {
// Container info
if result.ContainerKept {
fmt.Printf("📦 Container kept: %s\n", result.ContainerID[:12])
fmt.Printf("[PKG] Container kept: %s\n", result.ContainerID[:12])
fmt.Printf(" Connect with: docker exec -it %s bash\n", result.ContainerID[:12])
fmt.Printf("\n")
}
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
fmt.Printf(" %s\n", result.Message)
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("=====================================================\n")
}
func updateCatalogWithDrillResult(ctx context.Context, backupPath string, result *drill.DrillResult) {

View File

@@ -63,9 +63,9 @@ func runEngineList(cmd *cobra.Command, args []string) error {
continue
}
status := " Available"
status := "[Y] Available"
if !avail.Available {
status = " Not available"
status = "[N] Not available"
}
fmt.Printf("\n%s (%s)\n", info.Name, info.Description)

View File

@@ -176,12 +176,12 @@ func runInstallStatus(ctx context.Context) error {
}
fmt.Println()
fmt.Println("📦 DBBackup Installation Status")
fmt.Println(strings.Repeat("", 50))
fmt.Println("[STATUS] DBBackup Installation Status")
fmt.Println(strings.Repeat("=", 50))
if clusterStatus.Installed {
fmt.Println()
fmt.Println("🔹 Cluster Backup:")
fmt.Println(" * Cluster Backup:")
fmt.Printf(" Service: %s\n", formatStatus(clusterStatus.Installed, clusterStatus.Active))
fmt.Printf(" Timer: %s\n", formatStatus(clusterStatus.TimerEnabled, clusterStatus.TimerActive))
if clusterStatus.NextRun != "" {
@@ -192,7 +192,7 @@ func runInstallStatus(ctx context.Context) error {
}
} else {
fmt.Println()
fmt.Println(" No systemd services installed")
fmt.Println("[NONE] No systemd services installed")
fmt.Println()
fmt.Println("Run 'sudo dbbackup install' to install as a systemd service")
}
@@ -200,13 +200,13 @@ func runInstallStatus(ctx context.Context) error {
// Check for exporter
if _, err := os.Stat("/etc/systemd/system/dbbackup-exporter.service"); err == nil {
fmt.Println()
fmt.Println("🔹 Metrics Exporter:")
fmt.Println(" * Metrics Exporter:")
// Check if exporter is active using systemctl
cmd := exec.CommandContext(ctx, "systemctl", "is-active", "dbbackup-exporter")
if err := cmd.Run(); err == nil {
fmt.Printf(" Service: active\n")
fmt.Printf(" Service: [OK] active\n")
} else {
fmt.Printf(" Service: inactive\n")
fmt.Printf(" Service: [-] inactive\n")
}
}
@@ -219,9 +219,9 @@ func formatStatus(installed, active bool) string {
return "not installed"
}
if active {
return " active"
return "[OK] active"
}
return " inactive"
return "[-] inactive"
}
func expandSchedule(schedule string) string {

View File

@@ -436,7 +436,7 @@ func runPITREnable(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to enable PITR: %w", err)
}
log.Info(" PITR enabled successfully!")
log.Info("[OK] PITR enabled successfully!")
log.Info("")
log.Info("Next steps:")
log.Info("1. Restart PostgreSQL: sudo systemctl restart postgresql")
@@ -463,7 +463,7 @@ func runPITRDisable(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to disable PITR: %w", err)
}
log.Info(" PITR disabled successfully!")
log.Info("[OK] PITR disabled successfully!")
log.Info("PostgreSQL restart required: sudo systemctl restart postgresql")
return nil
@@ -483,15 +483,15 @@ func runPITRStatus(cmd *cobra.Command, args []string) error {
}
// Display PITR configuration
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("======================================================")
fmt.Println(" Point-in-Time Recovery (PITR) Status")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("======================================================")
fmt.Println()
if config.Enabled {
fmt.Println("Status: ENABLED")
fmt.Println("Status: [OK] ENABLED")
} else {
fmt.Println("Status: DISABLED")
fmt.Println("Status: [FAIL] DISABLED")
}
fmt.Printf("WAL Level: %s\n", config.WALLevel)
@@ -510,7 +510,7 @@ func runPITRStatus(cmd *cobra.Command, args []string) error {
// Extract archive dir from command (simple parsing)
fmt.Println()
fmt.Println("WAL Archive Statistics:")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("======================================================")
// TODO: Parse archive dir and show stats
fmt.Println(" (Use 'dbbackup wal list --archive-dir <dir>' to view archives)")
}
@@ -574,13 +574,13 @@ func runWALList(cmd *cobra.Command, args []string) error {
}
// Display archives
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("======================================================")
fmt.Printf(" WAL Archives (%d files)\n", len(archives))
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("======================================================")
fmt.Println()
fmt.Printf("%-28s %10s %10s %8s %s\n", "WAL Filename", "Timeline", "Segment", "Size", "Archived At")
fmt.Println("────────────────────────────────────────────────────────────────────────────────")
fmt.Println("--------------------------------------------------------------------------------")
for _, archive := range archives {
size := formatWALSize(archive.ArchivedSize)
@@ -644,7 +644,7 @@ func runWALCleanup(cmd *cobra.Command, args []string) error {
return fmt.Errorf("WAL cleanup failed: %w", err)
}
log.Info(" WAL cleanup completed", "deleted", deleted, "retention_days", archiveConfig.RetentionDays)
log.Info("[OK] WAL cleanup completed", "deleted", deleted, "retention_days", archiveConfig.RetentionDays)
return nil
}
@@ -671,7 +671,7 @@ func runWALTimeline(cmd *cobra.Command, args []string) error {
// Display timeline details
if len(history.Timelines) > 0 {
fmt.Println("\nTimeline Details:")
fmt.Println("═════════════════")
fmt.Println("=================")
for _, tl := range history.Timelines {
fmt.Printf("\nTimeline %d:\n", tl.TimelineID)
if tl.ParentTimeline > 0 {
@@ -690,7 +690,7 @@ func runWALTimeline(cmd *cobra.Command, args []string) error {
fmt.Printf(" Created: %s\n", tl.CreatedAt.Format("2006-01-02 15:04:05"))
}
if tl.TimelineID == history.CurrentTimeline {
fmt.Printf(" Status: CURRENT\n")
fmt.Printf(" Status: [CURR] CURRENT\n")
}
}
}
@@ -759,15 +759,15 @@ func runBinlogList(cmd *cobra.Command, args []string) error {
return nil
}
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Printf(" Binary Log Files (%s)\n", bm.ServerType())
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Println()
if len(binlogs) > 0 {
fmt.Println("Source Directory:")
fmt.Printf("%-24s %10s %-19s %-19s %s\n", "Filename", "Size", "Start Time", "End Time", "Format")
fmt.Println("────────────────────────────────────────────────────────────────────────────────")
fmt.Println("--------------------------------------------------------------------------------")
var totalSize int64
for _, b := range binlogs {
@@ -797,7 +797,7 @@ func runBinlogList(cmd *cobra.Command, args []string) error {
fmt.Println()
fmt.Println("Archived Binlogs:")
fmt.Printf("%-24s %10s %-19s %s\n", "Original", "Size", "Archived At", "Flags")
fmt.Println("────────────────────────────────────────────────────────────────────────────────")
fmt.Println("--------------------------------------------------------------------------------")
var totalSize int64
for _, a := range archived {
@@ -914,7 +914,7 @@ func runBinlogArchive(cmd *cobra.Command, args []string) error {
bm.SaveArchiveMetadata(allArchived)
}
log.Info(" Binlog archiving completed", "archived", len(newArchives))
log.Info("[OK] Binlog archiving completed", "archived", len(newArchives))
return nil
}
@@ -1014,15 +1014,15 @@ func runBinlogValidate(cmd *cobra.Command, args []string) error {
return fmt.Errorf("validating binlog chain: %w", err)
}
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Println(" Binlog Chain Validation")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Println()
if validation.Valid {
fmt.Println("Status: VALID - Binlog chain is complete")
fmt.Println("Status: [OK] VALID - Binlog chain is complete")
} else {
fmt.Println("Status: INVALID - Binlog chain has gaps")
fmt.Println("Status: [FAIL] INVALID - Binlog chain has gaps")
}
fmt.Printf("Files: %d binlog files\n", validation.LogCount)
@@ -1055,7 +1055,7 @@ func runBinlogValidate(cmd *cobra.Command, args []string) error {
fmt.Println()
fmt.Println("Errors:")
for _, e := range validation.Errors {
fmt.Printf(" %s\n", e)
fmt.Printf(" [FAIL] %s\n", e)
}
}
@@ -1094,9 +1094,9 @@ func runBinlogPosition(cmd *cobra.Command, args []string) error {
}
defer rows.Close()
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Println(" Current Binary Log Position")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Println()
if rows.Next() {
@@ -1178,24 +1178,24 @@ func runMySQLPITRStatus(cmd *cobra.Command, args []string) error {
return fmt.Errorf("getting PITR status: %w", err)
}
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Printf(" MySQL/MariaDB PITR Status (%s)\n", status.DatabaseType)
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("=============================================================")
fmt.Println()
if status.Enabled {
fmt.Println("PITR Status: ENABLED")
fmt.Println("PITR Status: [OK] ENABLED")
} else {
fmt.Println("PITR Status: NOT CONFIGURED")
fmt.Println("PITR Status: [FAIL] NOT CONFIGURED")
}
// Get binary logging status
var logBin string
db.QueryRowContext(ctx, "SELECT @@log_bin").Scan(&logBin)
if logBin == "1" || logBin == "ON" {
fmt.Println("Binary Logging: ENABLED")
fmt.Println("Binary Logging: [OK] ENABLED")
} else {
fmt.Println("Binary Logging: DISABLED")
fmt.Println("Binary Logging: [FAIL] DISABLED")
}
fmt.Printf("Binlog Format: %s\n", status.LogLevel)
@@ -1205,14 +1205,14 @@ func runMySQLPITRStatus(cmd *cobra.Command, args []string) error {
if status.DatabaseType == pitr.DatabaseMariaDB {
db.QueryRowContext(ctx, "SELECT @@gtid_current_pos").Scan(&gtidMode)
if gtidMode != "" {
fmt.Println("GTID Mode: ENABLED")
fmt.Println("GTID Mode: [OK] ENABLED")
} else {
fmt.Println("GTID Mode: DISABLED")
fmt.Println("GTID Mode: [FAIL] DISABLED")
}
} else {
db.QueryRowContext(ctx, "SELECT @@gtid_mode").Scan(&gtidMode)
if gtidMode == "ON" {
fmt.Println("GTID Mode: ENABLED")
fmt.Println("GTID Mode: [OK] ENABLED")
} else {
fmt.Printf("GTID Mode: %s\n", gtidMode)
}
@@ -1237,12 +1237,12 @@ func runMySQLPITRStatus(cmd *cobra.Command, args []string) error {
fmt.Println()
fmt.Println("PITR Requirements:")
if logBin == "1" || logBin == "ON" {
fmt.Println(" Binary logging enabled")
fmt.Println(" [OK] Binary logging enabled")
} else {
fmt.Println(" Binary logging must be enabled (log_bin = mysql-bin)")
fmt.Println(" [FAIL] Binary logging must be enabled (log_bin = mysql-bin)")
}
if status.LogLevel == "ROW" {
fmt.Println(" Row-based logging (recommended)")
fmt.Println(" [OK] Row-based logging (recommended)")
} else {
fmt.Printf(" ⚠ binlog_format = %s (ROW recommended for PITR)\n", status.LogLevel)
}
@@ -1299,7 +1299,7 @@ func runMySQLPITREnable(cmd *cobra.Command, args []string) error {
return fmt.Errorf("enabling PITR: %w", err)
}
log.Info(" MySQL PITR enabled successfully!")
log.Info("[OK] MySQL PITR enabled successfully!")
log.Info("")
log.Info("Next steps:")
log.Info("1. Start binlog archiving: dbbackup binlog watch --archive-dir " + mysqlArchiveDir)

View File

@@ -141,7 +141,7 @@ func runList(ctx context.Context) error {
continue
}
fmt.Printf("📦 %s\n", file.Name)
fmt.Printf("[FILE] %s\n", file.Name)
fmt.Printf(" Size: %s\n", formatFileSize(stat.Size()))
fmt.Printf(" Modified: %s\n", stat.ModTime().Format("2006-01-02 15:04:05"))
fmt.Printf(" Type: %s\n", getBackupType(file.Name))
@@ -237,56 +237,56 @@ func runPreflight(ctx context.Context) error {
totalChecks := 6
// 1. Database connectivity check
fmt.Print("🔗 Database connectivity... ")
fmt.Print("[1] Database connectivity... ")
if err := testDatabaseConnection(); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
// 2. Required tools check
fmt.Print("🛠️ Required tools (pg_dump/pg_restore)... ")
fmt.Print("[2] Required tools (pg_dump/pg_restore)... ")
if err := checkRequiredTools(); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
// 3. Backup directory check
fmt.Print("📁 Backup directory access... ")
fmt.Print("[3] Backup directory access... ")
if err := checkBackupDirectory(); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
// 4. Disk space check
fmt.Print("💾 Available disk space... ")
fmt.Print("[4] Available disk space... ")
if err := checkDiskSpace(); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
// 5. Permissions check
fmt.Print("🔐 File permissions... ")
fmt.Print("[5] File permissions... ")
if err := checkPermissions(); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
// 6. CPU/Memory resources check
fmt.Print("🖥️ System resources... ")
fmt.Print("[6] System resources... ")
if err := checkSystemResources(); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
@@ -294,10 +294,10 @@ func runPreflight(ctx context.Context) error {
fmt.Printf("Results: %d/%d checks passed\n", checksPassed, totalChecks)
if checksPassed == totalChecks {
fmt.Println("🎉 All preflight checks passed! System is ready for backup operations.")
fmt.Println("[SUCCESS] All preflight checks passed! System is ready for backup operations.")
return nil
} else {
fmt.Printf("⚠️ %d check(s) failed. Please address the issues before running backups.\n", totalChecks-checksPassed)
fmt.Printf("[WARN] %d check(s) failed. Please address the issues before running backups.\n", totalChecks-checksPassed)
return fmt.Errorf("preflight checks failed: %d/%d passed", checksPassed, totalChecks)
}
}
@@ -414,44 +414,44 @@ func runRestore(ctx context.Context, archiveName string) error {
fmt.Println()
// Show warning
fmt.Println("⚠️ WARNING: This will restore data to the target database.")
fmt.Println("[WARN] WARNING: This will restore data to the target database.")
fmt.Println(" Existing data may be overwritten or merged depending on the restore method.")
fmt.Println()
// For safety, show what would be done without actually doing it
switch archiveType {
case "Single Database (.dump)":
fmt.Println("🔄 Would execute: pg_restore to restore single database")
fmt.Println("[EXEC] Would execute: pg_restore to restore single database")
fmt.Printf(" Command: pg_restore -h %s -p %d -U %s -d %s --verbose %s\n",
cfg.Host, cfg.Port, cfg.User, cfg.Database, archivePath)
case "Single Database (.dump.gz)":
fmt.Println("🔄 Would execute: gunzip and pg_restore to restore single database")
fmt.Println("[EXEC] Would execute: gunzip and pg_restore to restore single database")
fmt.Printf(" Command: gunzip -c %s | pg_restore -h %s -p %d -U %s -d %s --verbose\n",
archivePath, cfg.Host, cfg.Port, cfg.User, cfg.Database)
case "SQL Script (.sql)":
if cfg.IsPostgreSQL() {
fmt.Println("🔄 Would execute: psql to run SQL script")
fmt.Println("[EXEC] Would execute: psql to run SQL script")
fmt.Printf(" Command: psql -h %s -p %d -U %s -d %s -f %s\n",
cfg.Host, cfg.Port, cfg.User, cfg.Database, archivePath)
} else if cfg.IsMySQL() {
fmt.Println("🔄 Would execute: mysql to run SQL script")
fmt.Println("[EXEC] Would execute: mysql to run SQL script")
fmt.Printf(" Command: %s\n", mysqlRestoreCommand(archivePath, false))
} else {
fmt.Println("🔄 Would execute: SQL client to run script (database type unknown)")
fmt.Println("[EXEC] Would execute: SQL client to run script (database type unknown)")
}
case "SQL Script (.sql.gz)":
if cfg.IsPostgreSQL() {
fmt.Println("🔄 Would execute: gunzip and psql to run SQL script")
fmt.Println("[EXEC] Would execute: gunzip and psql to run SQL script")
fmt.Printf(" Command: gunzip -c %s | psql -h %s -p %d -U %s -d %s\n",
archivePath, cfg.Host, cfg.Port, cfg.User, cfg.Database)
} else if cfg.IsMySQL() {
fmt.Println("🔄 Would execute: gunzip and mysql to run SQL script")
fmt.Println("[EXEC] Would execute: gunzip and mysql to run SQL script")
fmt.Printf(" Command: %s\n", mysqlRestoreCommand(archivePath, true))
} else {
fmt.Println("🔄 Would execute: gunzip and SQL client to run script (database type unknown)")
fmt.Println("[EXEC] Would execute: gunzip and SQL client to run script (database type unknown)")
}
case "Cluster Backup (.tar.gz)":
fmt.Println("🔄 Would execute: Extract and restore cluster backup")
fmt.Println("[EXEC] Would execute: Extract and restore cluster backup")
fmt.Println(" Steps:")
fmt.Println(" 1. Extract tar.gz archive")
fmt.Println(" 2. Restore global objects (roles, tablespaces)")
@@ -461,7 +461,7 @@ func runRestore(ctx context.Context, archiveName string) error {
}
fmt.Println()
fmt.Println("🛡️ SAFETY MODE: Restore command is in preview mode.")
fmt.Println("[SAFETY] SAFETY MODE: Restore command is in preview mode.")
fmt.Println(" This shows what would be executed without making changes.")
fmt.Println(" To enable actual restore, add --confirm flag (not yet implemented).")
@@ -520,25 +520,25 @@ func runVerify(ctx context.Context, archiveName string) error {
checksPassed := 0
// Basic file existence and readability
fmt.Print("📁 File accessibility... ")
fmt.Print("[CHK] File accessibility... ")
if file, err := os.Open(archivePath); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
file.Close()
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
// File size sanity check
fmt.Print("📏 File size check... ")
fmt.Print("[CHK] File size check... ")
if stat.Size() == 0 {
fmt.Println(" FAILED: File is empty")
fmt.Println("[FAIL] FAILED: File is empty")
} else if stat.Size() < 100 {
fmt.Println("⚠️ WARNING: File is very small (< 100 bytes)")
fmt.Println("[WARN] WARNING: File is very small (< 100 bytes)")
checksPassed++
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
@@ -546,51 +546,51 @@ func runVerify(ctx context.Context, archiveName string) error {
// Type-specific verification
switch archiveType {
case "Single Database (.dump)":
fmt.Print("🔍 PostgreSQL dump format check... ")
fmt.Print("[CHK] PostgreSQL dump format check... ")
if err := verifyPgDump(archivePath); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
case "Single Database (.dump.gz)":
fmt.Print("🔍 PostgreSQL dump format check (gzip)... ")
fmt.Print("[CHK] PostgreSQL dump format check (gzip)... ")
if err := verifyPgDumpGzip(archivePath); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
case "SQL Script (.sql)":
fmt.Print("📜 SQL script validation... ")
fmt.Print("[CHK] SQL script validation... ")
if err := verifySqlScript(archivePath); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
case "SQL Script (.sql.gz)":
fmt.Print("📜 SQL script validation (gzip)... ")
fmt.Print("[CHK] SQL script validation (gzip)... ")
if err := verifyGzipSqlScript(archivePath); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
case "Cluster Backup (.tar.gz)":
fmt.Print("📦 Archive extraction test... ")
fmt.Print("[CHK] Archive extraction test... ")
if err := verifyTarGz(archivePath); err != nil {
fmt.Printf(" FAILED: %v\n", err)
fmt.Printf("[FAIL] FAILED: %v\n", err)
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
@@ -598,11 +598,11 @@ func runVerify(ctx context.Context, archiveName string) error {
// Check for metadata file
metadataPath := archivePath + ".info"
fmt.Print("📋 Metadata file check... ")
fmt.Print("[CHK] Metadata file check... ")
if _, err := os.Stat(metadataPath); os.IsNotExist(err) {
fmt.Println("⚠️ WARNING: No metadata file found")
fmt.Println("[WARN] WARNING: No metadata file found")
} else {
fmt.Println(" PASSED")
fmt.Println("[OK] PASSED")
checksPassed++
}
checksRun++
@@ -611,13 +611,13 @@ func runVerify(ctx context.Context, archiveName string) error {
fmt.Printf("Verification Results: %d/%d checks passed\n", checksPassed, checksRun)
if checksPassed == checksRun {
fmt.Println("🎉 Archive verification completed successfully!")
fmt.Println("[SUCCESS] Archive verification completed successfully!")
return nil
} else if float64(checksPassed)/float64(checksRun) >= 0.8 {
fmt.Println("⚠️ Archive verification completed with warnings.")
fmt.Println("[WARN] Archive verification completed with warnings.")
return nil
} else {
fmt.Println(" Archive verification failed. Archive may be corrupted.")
fmt.Println("[FAIL] Archive verification failed. Archive may be corrupted.")
return fmt.Errorf("verification failed: %d/%d checks passed", checksPassed, checksRun)
}
}

View File

@@ -342,7 +342,7 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error {
return fmt.Errorf("archive not found: %s", archivePath)
}
log.Info("🔍 Diagnosing backup file", "path", archivePath)
log.Info("[DIAG] Diagnosing backup file", "path", archivePath)
diagnoser := restore.NewDiagnoser(log, restoreVerbose)
@@ -387,7 +387,7 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error {
// Summary
if !diagnoseJSON {
fmt.Println("\n" + strings.Repeat("=", 70))
fmt.Printf("📊 CLUSTER SUMMARY: %d databases analyzed\n", len(results))
fmt.Printf("[SUMMARY] CLUSTER SUMMARY: %d databases analyzed\n", len(results))
validCount := 0
for _, r := range results {
@@ -397,9 +397,9 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error {
}
if validCount == len(results) {
fmt.Println(" All dumps are valid")
fmt.Println("[OK] All dumps are valid")
} else {
fmt.Printf(" %d/%d dumps have issues\n", len(results)-validCount, len(results))
fmt.Printf("[FAIL] %d/%d dumps have issues\n", len(results)-validCount, len(results))
}
fmt.Println(strings.Repeat("=", 70))
}
@@ -426,7 +426,7 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error {
return fmt.Errorf("backup file has validation errors")
}
log.Info(" Backup file appears valid")
log.Info("[OK] Backup file appears valid")
return nil
}
@@ -545,7 +545,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error {
isDryRun := restoreDryRun || !restoreConfirm
if isDryRun {
fmt.Println("\n🔍 DRY-RUN MODE - No changes will be made")
fmt.Println("\n[DRY-RUN] DRY-RUN MODE - No changes will be made")
fmt.Printf("\nWould restore:\n")
fmt.Printf(" Archive: %s\n", archivePath)
fmt.Printf(" Format: %s\n", format.String())
@@ -588,7 +588,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error {
// Run pre-restore diagnosis if requested
if restoreDiagnose {
log.Info("🔍 Running pre-restore diagnosis...")
log.Info("[DIAG] Running pre-restore diagnosis...")
diagnoser := restore.NewDiagnoser(log, restoreVerbose)
result, err := diagnoser.DiagnoseFile(archivePath)
@@ -599,7 +599,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error {
diagnoser.PrintDiagnosis(result)
if !result.IsValid {
log.Error(" Pre-restore diagnosis found issues")
log.Error("[FAIL] Pre-restore diagnosis found issues")
if result.IsTruncated {
log.Error(" The backup file appears to be TRUNCATED")
}
@@ -613,7 +613,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error {
}
log.Warn("Continuing despite diagnosis errors (--force enabled)")
} else {
log.Info(" Backup file passed diagnosis")
log.Info("[OK] Backup file passed diagnosis")
}
}
@@ -633,7 +633,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error {
// Audit log: restore success
auditLogger.LogRestoreComplete(user, targetDB, time.Since(startTime))
log.Info(" Restore completed successfully", "database", targetDB)
log.Info("[OK] Restore completed successfully", "database", targetDB)
return nil
}
@@ -701,7 +701,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
}
}
log.Warn("⚠️ Using alternative working directory for extraction")
log.Warn("[WARN] Using alternative working directory for extraction")
log.Warn(" This is recommended when system disk space is limited")
log.Warn(" Location: " + restoreWorkdir)
}
@@ -754,7 +754,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
isDryRun := restoreDryRun || !restoreConfirm
if isDryRun {
fmt.Println("\n🔍 DRY-RUN MODE - No changes will be made")
fmt.Println("\n[DRY-RUN] DRY-RUN MODE - No changes will be made")
fmt.Printf("\nWould restore cluster:\n")
fmt.Printf(" Archive: %s\n", archivePath)
fmt.Printf(" Parallel Jobs: %d (0 = auto)\n", restoreJobs)
@@ -764,7 +764,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
if restoreCleanCluster {
fmt.Printf(" Clean Cluster: true (will drop %d existing database(s))\n", len(existingDBs))
if len(existingDBs) > 0 {
fmt.Printf("\n⚠️ Databases to be dropped:\n")
fmt.Printf("\n[WARN] Databases to be dropped:\n")
for _, dbName := range existingDBs {
fmt.Printf(" - %s\n", dbName)
}
@@ -776,7 +776,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
// Warning for clean-cluster
if restoreCleanCluster && len(existingDBs) > 0 {
log.Warn("🔥 Clean cluster mode enabled")
log.Warn("[!!] Clean cluster mode enabled")
log.Warn(fmt.Sprintf(" %d existing database(s) will be DROPPED before restore!", len(existingDBs)))
for _, dbName := range existingDBs {
log.Warn(" - " + dbName)
@@ -829,7 +829,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
// Run pre-restore diagnosis if requested
if restoreDiagnose {
log.Info("🔍 Running pre-restore diagnosis...")
log.Info("[DIAG] Running pre-restore diagnosis...")
// Create temp directory for extraction in configured WorkDir
workDir := cfg.GetEffectiveWorkDir()
@@ -855,10 +855,10 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
}
if len(invalidDumps) > 0 {
log.Error(" Pre-restore diagnosis found issues",
log.Error("[FAIL] Pre-restore diagnosis found issues",
"invalid_dumps", len(invalidDumps),
"total_dumps", len(results))
fmt.Println("\n⚠️ The following dumps have issues and will likely fail during restore:")
fmt.Println("\n[WARN] The following dumps have issues and will likely fail during restore:")
for _, name := range invalidDumps {
fmt.Printf(" - %s\n", name)
}
@@ -870,7 +870,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
}
log.Warn("Continuing despite diagnosis errors (--force enabled)")
} else {
log.Info(" All dumps passed diagnosis", "count", len(results))
log.Info("[OK] All dumps passed diagnosis", "count", len(results))
}
}
@@ -890,7 +890,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error {
// Audit log: restore success
auditLogger.LogRestoreComplete(user, "all_databases", time.Since(startTime))
log.Info(" Cluster restore completed successfully")
log.Info("[OK] Cluster restore completed successfully")
return nil
}
@@ -939,7 +939,7 @@ func runRestoreList(cmd *cobra.Command, args []string) error {
}
// Print header
fmt.Printf("\n📦 Available backup archives in %s\n\n", backupDir)
fmt.Printf("\n[LIST] Available backup archives in %s\n\n", backupDir)
fmt.Printf("%-40s %-25s %-12s %-20s %s\n",
"FILENAME", "FORMAT", "SIZE", "MODIFIED", "DATABASE")
fmt.Println(strings.Repeat("-", 120))
@@ -1056,9 +1056,9 @@ func runRestorePITR(cmd *cobra.Command, args []string) error {
}
// Display recovery target info
log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
log.Info("=====================================================")
log.Info(" Point-in-Time Recovery (PITR)")
log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
log.Info("=====================================================")
log.Info("")
log.Info(target.String())
log.Info("")
@@ -1082,6 +1082,6 @@ func runRestorePITR(cmd *cobra.Command, args []string) error {
return fmt.Errorf("PITR restore failed: %w", err)
}
log.Info(" PITR restore completed successfully")
log.Info("[OK] PITR restore completed successfully")
return nil
}

View File

@@ -181,13 +181,13 @@ func runRTOStatus(cmd *cobra.Command, args []string) error {
// Display status
fmt.Println()
fmt.Println("╔═══════════════════════════════════════════════════════════╗")
fmt.Println(" RTO/RPO STATUS SUMMARY ")
fmt.Println("╠═══════════════════════════════════════════════════════════╣")
fmt.Printf(" Target RTO: %-15s Target RPO: %-15s \n",
fmt.Println("+-----------------------------------------------------------+")
fmt.Println("| RTO/RPO STATUS SUMMARY |")
fmt.Println("+-----------------------------------------------------------+")
fmt.Printf("| Target RTO: %-15s Target RPO: %-15s |\n",
formatDuration(config.TargetRTO),
formatDuration(config.TargetRPO))
fmt.Println("╠═══════════════════════════════════════════════════════════╣")
fmt.Println("+-----------------------------------------------------------+")
// Compliance status
rpoRate := 0.0
@@ -199,31 +199,31 @@ func runRTOStatus(cmd *cobra.Command, args []string) error {
fullRate = float64(summary.FullyCompliant) / float64(summary.TotalDatabases) * 100
}
fmt.Printf(" Databases: %-5d \n", summary.TotalDatabases)
fmt.Printf(" RPO Compliant: %-5d (%.0f%%) \n", summary.RPOCompliant, rpoRate)
fmt.Printf(" RTO Compliant: %-5d (%.0f%%) \n", summary.RTOCompliant, rtoRate)
fmt.Printf(" Fully Compliant: %-3d (%.0f%%) \n", summary.FullyCompliant, fullRate)
fmt.Printf("| Databases: %-5d |\n", summary.TotalDatabases)
fmt.Printf("| RPO Compliant: %-5d (%.0f%%) |\n", summary.RPOCompliant, rpoRate)
fmt.Printf("| RTO Compliant: %-5d (%.0f%%) |\n", summary.RTOCompliant, rtoRate)
fmt.Printf("| Fully Compliant: %-3d (%.0f%%) |\n", summary.FullyCompliant, fullRate)
if summary.CriticalIssues > 0 {
fmt.Printf(" ⚠️ Critical Issues: %-3d \n", summary.CriticalIssues)
fmt.Printf("| [WARN] Critical Issues: %-3d |\n", summary.CriticalIssues)
}
fmt.Println("╠═══════════════════════════════════════════════════════════╣")
fmt.Printf(" Average RPO: %-15s Worst: %-15s \n",
fmt.Println("+-----------------------------------------------------------+")
fmt.Printf("| Average RPO: %-15s Worst: %-15s |\n",
formatDuration(summary.AverageRPO),
formatDuration(summary.WorstRPO))
fmt.Printf(" Average RTO: %-15s Worst: %-15s \n",
fmt.Printf("| Average RTO: %-15s Worst: %-15s |\n",
formatDuration(summary.AverageRTO),
formatDuration(summary.WorstRTO))
if summary.WorstRPODatabase != "" {
fmt.Printf(" Worst RPO Database: %-38s\n", summary.WorstRPODatabase)
fmt.Printf("| Worst RPO Database: %-38s|\n", summary.WorstRPODatabase)
}
if summary.WorstRTODatabase != "" {
fmt.Printf(" Worst RTO Database: %-38s\n", summary.WorstRTODatabase)
fmt.Printf("| Worst RTO Database: %-38s|\n", summary.WorstRTODatabase)
}
fmt.Println("╚═══════════════════════════════════════════════════════════╝")
fmt.Println("+-----------------------------------------------------------+")
fmt.Println()
// Per-database status
@@ -234,19 +234,19 @@ func runRTOStatus(cmd *cobra.Command, args []string) error {
fmt.Println(strings.Repeat("-", 70))
for _, a := range analyses {
status := ""
status := "[OK]"
if !a.RPOCompliant || !a.RTOCompliant {
status = ""
status = "[FAIL]"
}
rpoStr := formatDuration(a.CurrentRPO)
rtoStr := formatDuration(a.CurrentRTO)
if !a.RPOCompliant {
rpoStr = "⚠️ " + rpoStr
rpoStr = "[WARN] " + rpoStr
}
if !a.RTOCompliant {
rtoStr = "⚠️ " + rtoStr
rtoStr = "[WARN] " + rtoStr
}
fmt.Printf("%-25s %-12s %-12s %s\n",
@@ -306,21 +306,21 @@ func runRTOCheck(cmd *cobra.Command, args []string) error {
exitCode := 0
for _, a := range analyses {
if !a.RPOCompliant {
fmt.Printf(" %s: RPO violation - current %s exceeds target %s\n",
fmt.Printf("[FAIL] %s: RPO violation - current %s exceeds target %s\n",
a.Database,
formatDuration(a.CurrentRPO),
formatDuration(config.TargetRPO))
exitCode = 1
}
if !a.RTOCompliant {
fmt.Printf(" %s: RTO violation - estimated %s exceeds target %s\n",
fmt.Printf("[FAIL] %s: RTO violation - estimated %s exceeds target %s\n",
a.Database,
formatDuration(a.CurrentRTO),
formatDuration(config.TargetRTO))
exitCode = 1
}
if a.RPOCompliant && a.RTOCompliant {
fmt.Printf(" %s: Compliant (RPO: %s, RTO: %s)\n",
fmt.Printf("[OK] %s: Compliant (RPO: %s, RTO: %s)\n",
a.Database,
formatDuration(a.CurrentRPO),
formatDuration(a.CurrentRTO))
@@ -371,13 +371,13 @@ func outputAnalysisText(analyses []*rto.Analysis) error {
fmt.Println(strings.Repeat("=", 60))
// Status
rpoStatus := " Compliant"
rpoStatus := "[OK] Compliant"
if !a.RPOCompliant {
rpoStatus = " Violation"
rpoStatus = "[FAIL] Violation"
}
rtoStatus := " Compliant"
rtoStatus := "[OK] Compliant"
if !a.RTOCompliant {
rtoStatus = " Violation"
rtoStatus = "[FAIL] Violation"
}
fmt.Println()
@@ -420,7 +420,7 @@ func outputAnalysisText(analyses []*rto.Analysis) error {
fmt.Println(" Recommendations:")
fmt.Println(strings.Repeat("-", 50))
for _, r := range a.Recommendations {
icon := "💡"
icon := "[TIP]"
switch r.Priority {
case rto.PriorityCritical:
icon = "🔴"

View File

@@ -141,7 +141,7 @@ func testConnection(ctx context.Context) error {
// Display results
fmt.Println("Connection Test Results:")
fmt.Printf(" Status: Connected \n")
fmt.Printf(" Status: Connected [OK]\n")
fmt.Printf(" Version: %s\n", version)
fmt.Printf(" Databases: %d found\n", len(databases))
@@ -167,7 +167,7 @@ func testConnection(ctx context.Context) error {
}
fmt.Println()
fmt.Println(" Status check completed successfully!")
fmt.Println("[OK] Status check completed successfully!")
return nil
}

View File

@@ -96,17 +96,17 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error {
continue
}
fmt.Printf("📁 %s\n", filepath.Base(backupFile))
fmt.Printf("[FILE] %s\n", filepath.Base(backupFile))
if quickVerify {
// Quick check: size only
err := verification.QuickCheck(backupFile)
if err != nil {
fmt.Printf(" FAILED: %v\n\n", err)
fmt.Printf(" [FAIL] FAILED: %v\n\n", err)
failureCount++
continue
}
fmt.Printf(" VALID (quick check)\n\n")
fmt.Printf(" [OK] VALID (quick check)\n\n")
successCount++
} else {
// Full verification with SHA-256
@@ -116,7 +116,7 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error {
}
if result.Valid {
fmt.Printf(" VALID\n")
fmt.Printf(" [OK] VALID\n")
if verboseVerify {
meta, _ := metadata.Load(backupFile)
fmt.Printf(" Size: %s\n", metadata.FormatSize(meta.SizeBytes))
@@ -127,7 +127,7 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error {
fmt.Println()
successCount++
} else {
fmt.Printf(" FAILED: %v\n", result.Error)
fmt.Printf(" [FAIL] FAILED: %v\n", result.Error)
if verboseVerify {
if !result.FileExists {
fmt.Printf(" File does not exist\n")
@@ -147,11 +147,11 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error {
}
// Summary
fmt.Println(strings.Repeat("", 50))
fmt.Println(strings.Repeat("-", 50))
fmt.Printf("Total: %d backups\n", len(backupFiles))
fmt.Printf(" Valid: %d\n", successCount)
fmt.Printf("[OK] Valid: %d\n", successCount)
if failureCount > 0 {
fmt.Printf(" Failed: %d\n", failureCount)
fmt.Printf("[FAIL] Failed: %d\n", failureCount)
os.Exit(1)
}
@@ -195,16 +195,16 @@ func runVerifyCloudBackup(cmd *cobra.Command, args []string) error {
for _, uri := range args {
if !isCloudURI(uri) {
fmt.Printf("⚠️ Skipping non-cloud URI: %s\n", uri)
fmt.Printf("[WARN] Skipping non-cloud URI: %s\n", uri)
continue
}
fmt.Printf("☁️ %s\n", uri)
fmt.Printf("[CLOUD] %s\n", uri)
// Download and verify
result, err := verifyCloudBackup(cmd.Context(), uri, quickVerify, verboseVerify)
if err != nil {
fmt.Printf(" FAILED: %v\n\n", err)
fmt.Printf(" [FAIL] FAILED: %v\n\n", err)
failureCount++
continue
}
@@ -212,7 +212,7 @@ func runVerifyCloudBackup(cmd *cobra.Command, args []string) error {
// Cleanup temp file
defer result.Cleanup()
fmt.Printf(" VALID\n")
fmt.Printf(" [OK] VALID\n")
if verboseVerify && result.MetadataPath != "" {
meta, _ := metadata.Load(result.MetadataPath)
if meta != nil {
@@ -226,7 +226,7 @@ func runVerifyCloudBackup(cmd *cobra.Command, args []string) error {
successCount++
}
fmt.Printf("\n Summary: %d valid, %d failed\n", successCount, failureCount)
fmt.Printf("\n[OK] Summary: %d valid, %d failed\n", successCount, failureCount)
if failureCount > 0 {
os.Exit(1)