fix: use v.Stdout/v.Stdin instead of os.Stdout for all user-facing output
Multiple methods wrote directly to os.Stdout instead of using the injectable v.Stdout writer, breaking the TestVaultik testing infrastructure and making output impossible to capture or redirect. Fixed in: ListSnapshots, PurgeSnapshots, VerifySnapshotWithOptions, PruneBlobs, outputPruneBlobsJSON, outputRemoveJSON, ShowInfo, RemoteInfo.
This commit is contained in:
parent
46c2ea3079
commit
24539ced5b
@ -15,99 +15,99 @@ import (
|
|||||||
// ShowInfo displays system and configuration information
|
// ShowInfo displays system and configuration information
|
||||||
func (v *Vaultik) ShowInfo() error {
|
func (v *Vaultik) ShowInfo() error {
|
||||||
// System Information
|
// System Information
|
||||||
fmt.Printf("=== System Information ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "=== System Information ===\n")
|
||||||
fmt.Printf("OS/Architecture: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
_, _ = fmt.Fprintf(v.Stdout, "OS/Architecture: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||||
fmt.Printf("Version: %s\n", v.Globals.Version)
|
_, _ = fmt.Fprintf(v.Stdout, "Version: %s\n", v.Globals.Version)
|
||||||
fmt.Printf("Commit: %s\n", v.Globals.Commit)
|
_, _ = fmt.Fprintf(v.Stdout, "Commit: %s\n", v.Globals.Commit)
|
||||||
fmt.Printf("Go Version: %s\n", runtime.Version())
|
_, _ = fmt.Fprintf(v.Stdout, "Go Version: %s\n", runtime.Version())
|
||||||
fmt.Println()
|
_, _ = fmt.Fprintln(v.Stdout, )
|
||||||
|
|
||||||
// Storage Configuration
|
// Storage Configuration
|
||||||
fmt.Printf("=== Storage Configuration ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "=== Storage Configuration ===\n")
|
||||||
fmt.Printf("S3 Bucket: %s\n", v.Config.S3.Bucket)
|
_, _ = fmt.Fprintf(v.Stdout, "S3 Bucket: %s\n", v.Config.S3.Bucket)
|
||||||
if v.Config.S3.Prefix != "" {
|
if v.Config.S3.Prefix != "" {
|
||||||
fmt.Printf("S3 Prefix: %s\n", v.Config.S3.Prefix)
|
_, _ = fmt.Fprintf(v.Stdout, "S3 Prefix: %s\n", v.Config.S3.Prefix)
|
||||||
}
|
}
|
||||||
fmt.Printf("S3 Endpoint: %s\n", v.Config.S3.Endpoint)
|
_, _ = fmt.Fprintf(v.Stdout, "S3 Endpoint: %s\n", v.Config.S3.Endpoint)
|
||||||
fmt.Printf("S3 Region: %s\n", v.Config.S3.Region)
|
_, _ = fmt.Fprintf(v.Stdout, "S3 Region: %s\n", v.Config.S3.Region)
|
||||||
fmt.Println()
|
_, _ = fmt.Fprintln(v.Stdout, )
|
||||||
|
|
||||||
// Backup Settings
|
// Backup Settings
|
||||||
fmt.Printf("=== Backup Settings ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "=== Backup Settings ===\n")
|
||||||
|
|
||||||
// Show configured snapshots
|
// Show configured snapshots
|
||||||
fmt.Printf("Snapshots:\n")
|
_, _ = fmt.Fprintf(v.Stdout, "Snapshots:\n")
|
||||||
for _, name := range v.Config.SnapshotNames() {
|
for _, name := range v.Config.SnapshotNames() {
|
||||||
snap := v.Config.Snapshots[name]
|
snap := v.Config.Snapshots[name]
|
||||||
fmt.Printf(" %s:\n", name)
|
_, _ = fmt.Fprintf(v.Stdout, " %s:\n", name)
|
||||||
for _, path := range snap.Paths {
|
for _, path := range snap.Paths {
|
||||||
fmt.Printf(" - %s\n", path)
|
_, _ = fmt.Fprintf(v.Stdout, " - %s\n", path)
|
||||||
}
|
}
|
||||||
if len(snap.Exclude) > 0 {
|
if len(snap.Exclude) > 0 {
|
||||||
fmt.Printf(" exclude: %s\n", strings.Join(snap.Exclude, ", "))
|
_, _ = fmt.Fprintf(v.Stdout, " exclude: %s\n", strings.Join(snap.Exclude, ", "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global exclude patterns
|
// Global exclude patterns
|
||||||
if len(v.Config.Exclude) > 0 {
|
if len(v.Config.Exclude) > 0 {
|
||||||
fmt.Printf("Global Exclude: %s\n", strings.Join(v.Config.Exclude, ", "))
|
_, _ = fmt.Fprintf(v.Stdout, "Global Exclude: %s\n", strings.Join(v.Config.Exclude, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Compression: zstd level %d\n", v.Config.CompressionLevel)
|
_, _ = fmt.Fprintf(v.Stdout, "Compression: zstd level %d\n", v.Config.CompressionLevel)
|
||||||
fmt.Printf("Chunk Size: %s\n", humanize.Bytes(uint64(v.Config.ChunkSize)))
|
_, _ = fmt.Fprintf(v.Stdout, "Chunk Size: %s\n", humanize.Bytes(uint64(v.Config.ChunkSize)))
|
||||||
fmt.Printf("Blob Size Limit: %s\n", humanize.Bytes(uint64(v.Config.BlobSizeLimit)))
|
_, _ = fmt.Fprintf(v.Stdout, "Blob Size Limit: %s\n", humanize.Bytes(uint64(v.Config.BlobSizeLimit)))
|
||||||
fmt.Println()
|
_, _ = fmt.Fprintln(v.Stdout, )
|
||||||
|
|
||||||
// Encryption Configuration
|
// Encryption Configuration
|
||||||
fmt.Printf("=== Encryption Configuration ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "=== Encryption Configuration ===\n")
|
||||||
fmt.Printf("Recipients:\n")
|
_, _ = fmt.Fprintf(v.Stdout, "Recipients:\n")
|
||||||
for _, recipient := range v.Config.AgeRecipients {
|
for _, recipient := range v.Config.AgeRecipients {
|
||||||
fmt.Printf(" - %s\n", recipient)
|
_, _ = fmt.Fprintf(v.Stdout, " - %s\n", recipient)
|
||||||
}
|
}
|
||||||
fmt.Println()
|
_, _ = fmt.Fprintln(v.Stdout, )
|
||||||
|
|
||||||
// Daemon Settings (if applicable)
|
// Daemon Settings (if applicable)
|
||||||
if v.Config.BackupInterval > 0 || v.Config.MinTimeBetweenRun > 0 {
|
if v.Config.BackupInterval > 0 || v.Config.MinTimeBetweenRun > 0 {
|
||||||
fmt.Printf("=== Daemon Settings ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "=== Daemon Settings ===\n")
|
||||||
if v.Config.BackupInterval > 0 {
|
if v.Config.BackupInterval > 0 {
|
||||||
fmt.Printf("Backup Interval: %s\n", v.Config.BackupInterval)
|
_, _ = fmt.Fprintf(v.Stdout, "Backup Interval: %s\n", v.Config.BackupInterval)
|
||||||
}
|
}
|
||||||
if v.Config.MinTimeBetweenRun > 0 {
|
if v.Config.MinTimeBetweenRun > 0 {
|
||||||
fmt.Printf("Minimum Time: %s\n", v.Config.MinTimeBetweenRun)
|
_, _ = fmt.Fprintf(v.Stdout, "Minimum Time: %s\n", v.Config.MinTimeBetweenRun)
|
||||||
}
|
}
|
||||||
fmt.Println()
|
_, _ = fmt.Fprintln(v.Stdout, )
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local Database
|
// Local Database
|
||||||
fmt.Printf("=== Local Database ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "=== Local Database ===\n")
|
||||||
fmt.Printf("Index Path: %s\n", v.Config.IndexPath)
|
_, _ = fmt.Fprintf(v.Stdout, "Index Path: %s\n", v.Config.IndexPath)
|
||||||
|
|
||||||
// Check if index file exists and get its size
|
// Check if index file exists and get its size
|
||||||
if info, err := v.Fs.Stat(v.Config.IndexPath); err == nil {
|
if info, err := v.Fs.Stat(v.Config.IndexPath); err == nil {
|
||||||
fmt.Printf("Index Size: %s\n", humanize.Bytes(uint64(info.Size())))
|
_, _ = fmt.Fprintf(v.Stdout, "Index Size: %s\n", humanize.Bytes(uint64(info.Size())))
|
||||||
|
|
||||||
// Get snapshot count from database
|
// Get snapshot count from database
|
||||||
query := `SELECT COUNT(*) FROM snapshots WHERE completed_at IS NOT NULL`
|
query := `SELECT COUNT(*) FROM snapshots WHERE completed_at IS NOT NULL`
|
||||||
var snapshotCount int
|
var snapshotCount int
|
||||||
if err := v.DB.Conn().QueryRowContext(v.ctx, query).Scan(&snapshotCount); err == nil {
|
if err := v.DB.Conn().QueryRowContext(v.ctx, query).Scan(&snapshotCount); err == nil {
|
||||||
fmt.Printf("Snapshots: %d\n", snapshotCount)
|
_, _ = fmt.Fprintf(v.Stdout, "Snapshots: %d\n", snapshotCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get blob count from database
|
// Get blob count from database
|
||||||
query = `SELECT COUNT(*) FROM blobs`
|
query = `SELECT COUNT(*) FROM blobs`
|
||||||
var blobCount int
|
var blobCount int
|
||||||
if err := v.DB.Conn().QueryRowContext(v.ctx, query).Scan(&blobCount); err == nil {
|
if err := v.DB.Conn().QueryRowContext(v.ctx, query).Scan(&blobCount); err == nil {
|
||||||
fmt.Printf("Blobs: %d\n", blobCount)
|
_, _ = fmt.Fprintf(v.Stdout, "Blobs: %d\n", blobCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get file count from database
|
// Get file count from database
|
||||||
query = `SELECT COUNT(*) FROM files`
|
query = `SELECT COUNT(*) FROM files`
|
||||||
var fileCount int
|
var fileCount int
|
||||||
if err := v.DB.Conn().QueryRowContext(v.ctx, query).Scan(&fileCount); err == nil {
|
if err := v.DB.Conn().QueryRowContext(v.ctx, query).Scan(&fileCount); err == nil {
|
||||||
fmt.Printf("Files: %d\n", fileCount)
|
_, _ = fmt.Fprintf(v.Stdout, "Files: %d\n", fileCount)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Index Size: (not created)\n")
|
_, _ = fmt.Fprintf(v.Stdout, "Index Size: (not created)\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -157,15 +157,15 @@ func (v *Vaultik) RemoteInfo(jsonOutput bool) error {
|
|||||||
result.StorageLocation = storageInfo.Location
|
result.StorageLocation = storageInfo.Location
|
||||||
|
|
||||||
if !jsonOutput {
|
if !jsonOutput {
|
||||||
fmt.Printf("=== Remote Storage ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "=== Remote Storage ===\n")
|
||||||
fmt.Printf("Type: %s\n", storageInfo.Type)
|
_, _ = fmt.Fprintf(v.Stdout, "Type: %s\n", storageInfo.Type)
|
||||||
fmt.Printf("Location: %s\n", storageInfo.Location)
|
_, _ = fmt.Fprintf(v.Stdout, "Location: %s\n", storageInfo.Location)
|
||||||
fmt.Println()
|
_, _ = fmt.Fprintln(v.Stdout, )
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all snapshot metadata
|
// List all snapshot metadata
|
||||||
if !jsonOutput {
|
if !jsonOutput {
|
||||||
fmt.Printf("Scanning snapshot metadata...\n")
|
_, _ = fmt.Fprintf(v.Stdout, "Scanning snapshot metadata...\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotMetadata := make(map[string]*SnapshotMetadataInfo)
|
snapshotMetadata := make(map[string]*SnapshotMetadataInfo)
|
||||||
@ -210,7 +210,7 @@ func (v *Vaultik) RemoteInfo(jsonOutput bool) error {
|
|||||||
|
|
||||||
// Download and parse all manifests to get referenced blobs
|
// Download and parse all manifests to get referenced blobs
|
||||||
if !jsonOutput {
|
if !jsonOutput {
|
||||||
fmt.Printf("Downloading %d manifest(s)...\n", len(snapshotIDs))
|
_, _ = fmt.Fprintf(v.Stdout, "Downloading %d manifest(s)...\n", len(snapshotIDs))
|
||||||
}
|
}
|
||||||
|
|
||||||
referencedBlobs := make(map[string]int64) // hash -> compressed size
|
referencedBlobs := make(map[string]int64) // hash -> compressed size
|
||||||
@ -260,7 +260,7 @@ func (v *Vaultik) RemoteInfo(jsonOutput bool) error {
|
|||||||
|
|
||||||
// List all blobs on remote
|
// List all blobs on remote
|
||||||
if !jsonOutput {
|
if !jsonOutput {
|
||||||
fmt.Printf("Scanning blobs...\n")
|
_, _ = fmt.Fprintf(v.Stdout, "Scanning blobs...\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
allBlobs := make(map[string]int64) // hash -> size from storage
|
allBlobs := make(map[string]int64) // hash -> size from storage
|
||||||
@ -298,14 +298,14 @@ func (v *Vaultik) RemoteInfo(jsonOutput bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Human-readable output
|
// Human-readable output
|
||||||
fmt.Printf("\n=== Snapshot Metadata ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "\n=== Snapshot Metadata ===\n")
|
||||||
if len(result.Snapshots) == 0 {
|
if len(result.Snapshots) == 0 {
|
||||||
fmt.Printf("No snapshots found\n")
|
_, _ = fmt.Fprintf(v.Stdout, "No snapshots found\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%-45s %12s %12s %12s %10s %12s\n", "SNAPSHOT", "MANIFEST", "DATABASE", "TOTAL", "BLOBS", "BLOB SIZE")
|
_, _ = fmt.Fprintf(v.Stdout, "%-45s %12s %12s %12s %10s %12s\n", "SNAPSHOT", "MANIFEST", "DATABASE", "TOTAL", "BLOBS", "BLOB SIZE")
|
||||||
fmt.Printf("%-45s %12s %12s %12s %10s %12s\n", strings.Repeat("-", 45), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 10), strings.Repeat("-", 12))
|
_, _ = fmt.Fprintf(v.Stdout, "%-45s %12s %12s %12s %10s %12s\n", strings.Repeat("-", 45), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 10), strings.Repeat("-", 12))
|
||||||
for _, info := range result.Snapshots {
|
for _, info := range result.Snapshots {
|
||||||
fmt.Printf("%-45s %12s %12s %12s %10s %12s\n",
|
_, _ = fmt.Fprintf(v.Stdout, "%-45s %12s %12s %12s %10s %12s\n",
|
||||||
truncateString(info.SnapshotID, 45),
|
truncateString(info.SnapshotID, 45),
|
||||||
humanize.Bytes(uint64(info.ManifestSize)),
|
humanize.Bytes(uint64(info.ManifestSize)),
|
||||||
humanize.Bytes(uint64(info.DatabaseSize)),
|
humanize.Bytes(uint64(info.DatabaseSize)),
|
||||||
@ -314,23 +314,23 @@ func (v *Vaultik) RemoteInfo(jsonOutput bool) error {
|
|||||||
humanize.Bytes(uint64(info.BlobsSize)),
|
humanize.Bytes(uint64(info.BlobsSize)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fmt.Printf("%-45s %12s %12s %12s %10s %12s\n", strings.Repeat("-", 45), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 10), strings.Repeat("-", 12))
|
_, _ = fmt.Fprintf(v.Stdout, "%-45s %12s %12s %12s %10s %12s\n", strings.Repeat("-", 45), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 12), strings.Repeat("-", 10), strings.Repeat("-", 12))
|
||||||
fmt.Printf("%-45s %12s %12s %12s\n", fmt.Sprintf("Total (%d snapshots)", result.TotalMetadataCount), "", "", humanize.Bytes(uint64(result.TotalMetadataSize)))
|
_, _ = fmt.Fprintf(v.Stdout, "%-45s %12s %12s %12s\n", fmt.Sprintf("Total (%d snapshots)", result.TotalMetadataCount), "", "", humanize.Bytes(uint64(result.TotalMetadataSize)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n=== Blob Storage ===\n")
|
_, _ = fmt.Fprintf(v.Stdout, "\n=== Blob Storage ===\n")
|
||||||
fmt.Printf("Total blobs on remote: %s (%s)\n",
|
_, _ = fmt.Fprintf(v.Stdout, "Total blobs on remote: %s (%s)\n",
|
||||||
humanize.Comma(int64(result.TotalBlobCount)),
|
humanize.Comma(int64(result.TotalBlobCount)),
|
||||||
humanize.Bytes(uint64(result.TotalBlobSize)))
|
humanize.Bytes(uint64(result.TotalBlobSize)))
|
||||||
fmt.Printf("Referenced by snapshots: %s (%s)\n",
|
_, _ = fmt.Fprintf(v.Stdout, "Referenced by snapshots: %s (%s)\n",
|
||||||
humanize.Comma(int64(result.ReferencedBlobCount)),
|
humanize.Comma(int64(result.ReferencedBlobCount)),
|
||||||
humanize.Bytes(uint64(result.ReferencedBlobSize)))
|
humanize.Bytes(uint64(result.ReferencedBlobSize)))
|
||||||
fmt.Printf("Orphaned (unreferenced): %s (%s)\n",
|
_, _ = fmt.Fprintf(v.Stdout, "Orphaned (unreferenced): %s (%s)\n",
|
||||||
humanize.Comma(int64(result.OrphanedBlobCount)),
|
humanize.Comma(int64(result.OrphanedBlobCount)),
|
||||||
humanize.Bytes(uint64(result.OrphanedBlobSize)))
|
humanize.Bytes(uint64(result.OrphanedBlobSize)))
|
||||||
|
|
||||||
if result.OrphanedBlobCount > 0 {
|
if result.OrphanedBlobCount > 0 {
|
||||||
fmt.Printf("\nRun 'vaultik prune --remote' to remove orphaned blobs.\n")
|
_, _ = fmt.Fprintf(v.Stdout, "\nRun 'vaultik prune --remote' to remove orphaned blobs.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package vaultik
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/vaultik/internal/log"
|
"git.eeqj.de/sneak/vaultik/internal/log"
|
||||||
@ -121,29 +120,29 @@ func (v *Vaultik) PruneBlobs(opts *PruneOptions) error {
|
|||||||
if len(unreferencedBlobs) == 0 {
|
if len(unreferencedBlobs) == 0 {
|
||||||
log.Info("No unreferenced blobs found")
|
log.Info("No unreferenced blobs found")
|
||||||
if opts.JSON {
|
if opts.JSON {
|
||||||
return outputPruneBlobsJSON(result)
|
return v.outputPruneBlobsJSON(result)
|
||||||
}
|
}
|
||||||
fmt.Println("No unreferenced blobs to remove.")
|
_, _ = fmt.Fprintln(v.Stdout, "No unreferenced blobs to remove.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show what will be deleted
|
// Show what will be deleted
|
||||||
log.Info("Found unreferenced blobs", "count", len(unreferencedBlobs), "total_size", humanize.Bytes(uint64(totalSize)))
|
log.Info("Found unreferenced blobs", "count", len(unreferencedBlobs), "total_size", humanize.Bytes(uint64(totalSize)))
|
||||||
if !opts.JSON {
|
if !opts.JSON {
|
||||||
fmt.Printf("Found %d unreferenced blob(s) totaling %s\n", len(unreferencedBlobs), humanize.Bytes(uint64(totalSize)))
|
_, _ = fmt.Fprintf(v.Stdout, "Found %d unreferenced blob(s) totaling %s\n", len(unreferencedBlobs), humanize.Bytes(uint64(totalSize)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm unless --force is used (skip in JSON mode - require --force)
|
// Confirm unless --force is used (skip in JSON mode - require --force)
|
||||||
if !opts.Force && !opts.JSON {
|
if !opts.Force && !opts.JSON {
|
||||||
fmt.Printf("\nDelete %d unreferenced blob(s)? [y/N] ", len(unreferencedBlobs))
|
_, _ = fmt.Fprintf(v.Stdout, "\nDelete %d unreferenced blob(s)? [y/N] ", len(unreferencedBlobs))
|
||||||
var confirm string
|
var confirm string
|
||||||
if _, err := fmt.Scanln(&confirm); err != nil {
|
if _, err := fmt.Fscanln(v.Stdin, &confirm); err != nil {
|
||||||
// Treat EOF or error as "no"
|
// Treat EOF or error as "no"
|
||||||
fmt.Println("Cancelled")
|
_, _ = fmt.Fprintln(v.Stdout, "Cancelled")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if strings.ToLower(confirm) != "y" {
|
if strings.ToLower(confirm) != "y" {
|
||||||
fmt.Println("Cancelled")
|
_, _ = fmt.Fprintln(v.Stdout, "Cancelled")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,20 +184,20 @@ func (v *Vaultik) PruneBlobs(opts *PruneOptions) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if opts.JSON {
|
if opts.JSON {
|
||||||
return outputPruneBlobsJSON(result)
|
return v.outputPruneBlobsJSON(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\nDeleted %d blob(s) totaling %s\n", deletedCount, humanize.Bytes(uint64(deletedSize)))
|
_, _ = fmt.Fprintf(v.Stdout, "\nDeleted %d blob(s) totaling %s\n", deletedCount, humanize.Bytes(uint64(deletedSize)))
|
||||||
if deletedCount < len(unreferencedBlobs) {
|
if deletedCount < len(unreferencedBlobs) {
|
||||||
fmt.Printf("Failed to delete %d blob(s)\n", len(unreferencedBlobs)-deletedCount)
|
_, _ = fmt.Fprintf(v.Stdout, "Failed to delete %d blob(s)\n", len(unreferencedBlobs)-deletedCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// outputPruneBlobsJSON outputs the prune result as JSON
|
// outputPruneBlobsJSON outputs the prune result as JSON
|
||||||
func outputPruneBlobsJSON(result *PruneBlobsResult) error {
|
func (v *Vaultik) outputPruneBlobsJSON(result *PruneBlobsResult) error {
|
||||||
encoder := json.NewEncoder(os.Stdout)
|
encoder := json.NewEncoder(v.Stdout)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
return encoder.Encode(result)
|
return encoder.Encode(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -422,13 +422,13 @@ func (v *Vaultik) ListSnapshots(jsonOutput bool) error {
|
|||||||
|
|
||||||
if jsonOutput {
|
if jsonOutput {
|
||||||
// JSON output
|
// JSON output
|
||||||
encoder := json.NewEncoder(os.Stdout)
|
encoder := json.NewEncoder(v.Stdout)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
return encoder.Encode(snapshots)
|
return encoder.Encode(snapshots)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table output
|
// Table output
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
w := tabwriter.NewWriter(v.Stdout, 0, 0, 3, ' ', 0)
|
||||||
|
|
||||||
// Show configured snapshots from config file
|
// Show configured snapshots from config file
|
||||||
if _, err := fmt.Fprintln(w, "CONFIGURED SNAPSHOTS:"); err != nil {
|
if _, err := fmt.Fprintln(w, "CONFIGURED SNAPSHOTS:"); err != nil {
|
||||||
@ -527,14 +527,14 @@ func (v *Vaultik) PurgeSnapshots(keepLatest bool, olderThan string, force bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(toDelete) == 0 {
|
if len(toDelete) == 0 {
|
||||||
fmt.Println("No snapshots to delete")
|
_, _ = fmt.Fprintln(v.Stdout, "No snapshots to delete")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show what will be deleted
|
// Show what will be deleted
|
||||||
fmt.Printf("The following snapshots will be deleted:\n\n")
|
_, _ = fmt.Fprintf(v.Stdout, "The following snapshots will be deleted:\n\n")
|
||||||
for _, snap := range toDelete {
|
for _, snap := range toDelete {
|
||||||
fmt.Printf(" %s (%s, %s)\n",
|
_, _ = fmt.Fprintf(v.Stdout, " %s (%s, %s)\n",
|
||||||
snap.ID,
|
snap.ID,
|
||||||
snap.Timestamp.Format("2006-01-02 15:04:05"),
|
snap.Timestamp.Format("2006-01-02 15:04:05"),
|
||||||
formatBytes(snap.CompressedSize))
|
formatBytes(snap.CompressedSize))
|
||||||
@ -542,19 +542,19 @@ func (v *Vaultik) PurgeSnapshots(keepLatest bool, olderThan string, force bool)
|
|||||||
|
|
||||||
// Confirm unless --force is used
|
// Confirm unless --force is used
|
||||||
if !force {
|
if !force {
|
||||||
fmt.Printf("\nDelete %d snapshot(s)? [y/N] ", len(toDelete))
|
_, _ = fmt.Fprintf(v.Stdout, "\nDelete %d snapshot(s)? [y/N] ", len(toDelete))
|
||||||
var confirm string
|
var confirm string
|
||||||
if _, err := fmt.Scanln(&confirm); err != nil {
|
if _, err := fmt.Fscanln(v.Stdin, &confirm); err != nil {
|
||||||
// Treat EOF or error as "no"
|
// Treat EOF or error as "no"
|
||||||
fmt.Println("Cancelled")
|
_, _ = fmt.Fprintln(v.Stdout, "Cancelled")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if strings.ToLower(confirm) != "y" {
|
if strings.ToLower(confirm) != "y" {
|
||||||
fmt.Println("Cancelled")
|
_, _ = fmt.Fprintln(v.Stdout, "Cancelled")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\nDeleting %d snapshot(s) (--force specified)\n", len(toDelete))
|
_, _ = fmt.Fprintf(v.Stdout, "\nDeleting %d snapshot(s) (--force specified)\n", len(toDelete))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete snapshots (both local and remote)
|
// Delete snapshots (both local and remote)
|
||||||
@ -569,10 +569,10 @@ func (v *Vaultik) PurgeSnapshots(keepLatest bool, olderThan string, force bool)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Deleted %d snapshot(s)\n", len(toDelete))
|
_, _ = fmt.Fprintf(v.Stdout, "Deleted %d snapshot(s)\n", len(toDelete))
|
||||||
|
|
||||||
// Note: Run 'vaultik prune' separately to clean up unreferenced blobs
|
// Note: Run 'vaultik prune' separately to clean up unreferenced blobs
|
||||||
fmt.Println("\nNote: Run 'vaultik prune' to clean up unreferenced blobs.")
|
_, _ = fmt.Fprintln(v.Stdout, "\nNote: Run 'vaultik prune' to clean up unreferenced blobs.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -613,9 +613,9 @@ func (v *Vaultik) VerifySnapshotWithOptions(snapshotID string, opts *VerifyOptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !opts.JSON {
|
if !opts.JSON {
|
||||||
fmt.Printf("Verifying snapshot %s\n", snapshotID)
|
_, _ = fmt.Fprintf(v.Stdout, "Verifying snapshot %s\n", snapshotID)
|
||||||
if !snapshotTime.IsZero() {
|
if !snapshotTime.IsZero() {
|
||||||
fmt.Printf("Snapshot time: %s\n", snapshotTime.Format("2006-01-02 15:04:05 MST"))
|
_, _ = fmt.Fprintf(v.Stdout, "Snapshot time: %s\n", snapshotTime.Format("2006-01-02 15:04:05 MST"))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
@ -635,18 +635,18 @@ func (v *Vaultik) VerifySnapshotWithOptions(snapshotID string, opts *VerifyOptio
|
|||||||
result.TotalSize = manifest.TotalCompressedSize
|
result.TotalSize = manifest.TotalCompressedSize
|
||||||
|
|
||||||
if !opts.JSON {
|
if !opts.JSON {
|
||||||
fmt.Printf("Snapshot information:\n")
|
_, _ = fmt.Fprintf(v.Stdout, "Snapshot information:\n")
|
||||||
fmt.Printf(" Blob count: %d\n", manifest.BlobCount)
|
_, _ = fmt.Fprintf(v.Stdout, " Blob count: %d\n", manifest.BlobCount)
|
||||||
fmt.Printf(" Total size: %s\n", humanize.Bytes(uint64(manifest.TotalCompressedSize)))
|
_, _ = fmt.Fprintf(v.Stdout, " Total size: %s\n", humanize.Bytes(uint64(manifest.TotalCompressedSize)))
|
||||||
if manifest.Timestamp != "" {
|
if manifest.Timestamp != "" {
|
||||||
if t, err := time.Parse(time.RFC3339, manifest.Timestamp); err == nil {
|
if t, err := time.Parse(time.RFC3339, manifest.Timestamp); err == nil {
|
||||||
fmt.Printf(" Created: %s\n", t.Format("2006-01-02 15:04:05 MST"))
|
_, _ = fmt.Fprintf(v.Stdout, " Created: %s\n", t.Format("2006-01-02 15:04:05 MST"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println()
|
_, _ = fmt.Fprintln(v.Stdout)
|
||||||
|
|
||||||
// Check each blob exists
|
// Check each blob exists
|
||||||
fmt.Printf("Checking blob existence...\n")
|
_, _ = fmt.Fprintf(v.Stdout, "Checking blob existence...\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
missing := 0
|
missing := 0
|
||||||
@ -660,7 +660,7 @@ func (v *Vaultik) VerifySnapshotWithOptions(snapshotID string, opts *VerifyOptio
|
|||||||
_, err := v.Storage.Stat(v.ctx, blobPath)
|
_, err := v.Storage.Stat(v.ctx, blobPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !opts.JSON {
|
if !opts.JSON {
|
||||||
fmt.Printf(" Missing: %s (%s)\n", blob.Hash, humanize.Bytes(uint64(blob.CompressedSize)))
|
_, _ = fmt.Fprintf(v.Stdout, " Missing: %s (%s)\n", blob.Hash, humanize.Bytes(uint64(blob.CompressedSize)))
|
||||||
}
|
}
|
||||||
missing++
|
missing++
|
||||||
missingSize += blob.CompressedSize
|
missingSize += blob.CompressedSize
|
||||||
@ -683,20 +683,20 @@ func (v *Vaultik) VerifySnapshotWithOptions(snapshotID string, opts *VerifyOptio
|
|||||||
return v.outputVerifyJSON(result)
|
return v.outputVerifyJSON(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\nVerification complete:\n")
|
_, _ = fmt.Fprintf(v.Stdout, "\nVerification complete:\n")
|
||||||
fmt.Printf(" Verified: %d blobs (%s)\n", verified,
|
_, _ = fmt.Fprintf(v.Stdout, " Verified: %d blobs (%s)\n", verified,
|
||||||
humanize.Bytes(uint64(manifest.TotalCompressedSize-missingSize)))
|
humanize.Bytes(uint64(manifest.TotalCompressedSize-missingSize)))
|
||||||
if missing > 0 {
|
if missing > 0 {
|
||||||
fmt.Printf(" Missing: %d blobs (%s)\n", missing, humanize.Bytes(uint64(missingSize)))
|
_, _ = fmt.Fprintf(v.Stdout, " Missing: %d blobs (%s)\n", missing, humanize.Bytes(uint64(missingSize)))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" Missing: 0 blobs\n")
|
_, _ = fmt.Fprintf(v.Stdout, " Missing: 0 blobs\n")
|
||||||
}
|
}
|
||||||
fmt.Printf(" Status: ")
|
_, _ = fmt.Fprintf(v.Stdout, " Status: ")
|
||||||
if missing > 0 {
|
if missing > 0 {
|
||||||
fmt.Printf("FAILED - %d blobs are missing\n", missing)
|
_, _ = fmt.Fprintf(v.Stdout, "FAILED - %d blobs are missing\n", missing)
|
||||||
return fmt.Errorf("%d blobs are missing", missing)
|
return fmt.Errorf("%d blobs are missing", missing)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("OK - All blobs verified\n")
|
_, _ = fmt.Fprintf(v.Stdout, "OK - All blobs verified\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -704,7 +704,7 @@ func (v *Vaultik) VerifySnapshotWithOptions(snapshotID string, opts *VerifyOptio
|
|||||||
|
|
||||||
// outputVerifyJSON outputs the verification result as JSON
|
// outputVerifyJSON outputs the verification result as JSON
|
||||||
func (v *Vaultik) outputVerifyJSON(result *VerifyResult) error {
|
func (v *Vaultik) outputVerifyJSON(result *VerifyResult) error {
|
||||||
encoder := json.NewEncoder(os.Stdout)
|
encoder := json.NewEncoder(v.Stdout)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
if err := encoder.Encode(result); err != nil {
|
if err := encoder.Encode(result); err != nil {
|
||||||
return fmt.Errorf("encoding JSON: %w", err)
|
return fmt.Errorf("encoding JSON: %w", err)
|
||||||
@ -1043,7 +1043,7 @@ func (v *Vaultik) deleteSnapshotFromRemote(snapshotID string) error {
|
|||||||
|
|
||||||
// outputRemoveJSON outputs the removal result as JSON
|
// outputRemoveJSON outputs the removal result as JSON
|
||||||
func (v *Vaultik) outputRemoveJSON(result *RemoveResult) error {
|
func (v *Vaultik) outputRemoveJSON(result *RemoveResult) error {
|
||||||
encoder := json.NewEncoder(os.Stdout)
|
encoder := json.NewEncoder(v.Stdout)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
return encoder.Encode(result)
|
return encoder.Encode(result)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user