Files
zfs/cmd/zfs-client/main.go
2026-02-13 14:59:43 +01:00

254 lines
7.5 KiB
Go

// Command zfs-client is the CLI tool for creating and uploading ZFS snapshots.
// It provides commands for backup, status checking, snapshot rotation, and incremental backups.
package main
import (
"fmt"
"os"
"git.ma-al.com/goc_marek/zfs/internal/client"
)
func main() {
if len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
// Load configuration from environment and .env file
config := client.LoadConfig()
c := client.New(config)
command := os.Args[1]
switch command {
case "backup":
// Default: create manual backup (full or incremental)
fmt.Println("=== Creating and sending backup ===\n")
snapshot, err := c.CreateSnapshot()
if err != nil {
fmt.Printf("Error creating snapshot: %v\n", err)
os.Exit(1)
}
if err := c.SendSnapshot(snapshot); err != nil {
fmt.Printf("Error sending snapshot: %v\n", err)
os.Exit(1)
}
fmt.Println("\n✓ Backup completed successfully!")
case "backup-full":
// Force full backup (no incremental)
fmt.Println("=== Creating full backup ===\n")
snapshot, err := c.CreateSnapshot()
if err != nil {
fmt.Printf("Error creating snapshot: %v\n", err)
os.Exit(1)
}
if err := c.SendIncremental(snapshot, ""); err != nil {
fmt.Printf("Error sending snapshot: %v\n", err)
os.Exit(1)
}
// Create bookmark for future incremental backups
if err := c.CreateBookmark(snapshot); err != nil {
fmt.Printf("Warning: failed to create bookmark: %v\n", err)
}
fmt.Println("\n✓ Full backup completed successfully!")
case "backup-incremental":
// Incremental backup from last bookmark
fmt.Println("=== Creating incremental backup ===\n")
// Check for existing bookmark
lastBookmark, err := c.GetLastBookmark()
if err != nil {
fmt.Printf("Error checking bookmarks: %v\n", err)
os.Exit(1)
}
if lastBookmark == "" {
fmt.Println("No existing bookmark found. Use 'backup-full' for initial backup.")
os.Exit(1)
}
snapshot, err := c.CreateSnapshot()
if err != nil {
fmt.Printf("Error creating snapshot: %v\n", err)
os.Exit(1)
}
if err := c.SendIncremental(snapshot, lastBookmark); err != nil {
fmt.Printf("Error sending incremental snapshot: %v\n", err)
os.Exit(1)
}
// Create bookmark for future incremental backups
if err := c.CreateBookmark(snapshot); err != nil {
fmt.Printf("Warning: failed to create bookmark: %v\n", err)
}
fmt.Println("\n✓ Incremental backup completed successfully!")
case "snapshot":
// Create typed snapshots (hourly, daily, weekly, monthly)
if len(os.Args) < 3 {
fmt.Println("Usage: zfs-client snapshot <hourly|daily|weekly|monthly>")
os.Exit(1)
}
snapType := client.SnapshotType(os.Args[2])
switch snapType {
case client.SnapshotHourly, client.SnapshotDaily, client.SnapshotWeekly, client.SnapshotMonthly:
// Valid type
default:
fmt.Printf("Invalid snapshot type: %s\n", snapType)
fmt.Println("Valid types: hourly, daily, weekly, monthly")
os.Exit(1)
}
fmt.Printf("=== Creating %s snapshot ===\n\n", snapType)
snapshot, err := c.CreateSnapshotWithType(snapType)
if err != nil {
fmt.Printf("Error creating snapshot: %v\n", err)
os.Exit(1)
}
// Check for existing bookmark for incremental
lastBookmark, _ := c.GetLastBookmark()
if err := c.SendIncremental(snapshot, lastBookmark); err != nil {
fmt.Printf("Error sending snapshot: %v\n", err)
os.Exit(1)
}
// Create bookmark
if err := c.CreateBookmark(snapshot); err != nil {
fmt.Printf("Warning: failed to create bookmark: %v\n", err)
}
// Rotate local snapshots using server policy if available
policy, err := getRotationPolicy(c)
if err != nil {
fmt.Printf("Warning: failed to get rotation policy: %v\n", err)
policy = client.DefaultPolicy()
}
if err := c.RotateLocalSnapshots(policy); err != nil {
fmt.Printf("Warning: failed to rotate snapshots: %v\n", err)
}
fmt.Printf("\n✓ %s snapshot completed successfully!\n", snapType)
case "rotate":
// Rotate local snapshots using server policy if available
fmt.Println("=== Rotating local snapshots ===\n")
policy, err := getRotationPolicy(c)
if err != nil {
fmt.Printf("Warning: failed to get rotation policy: %v\n", err)
policy = client.DefaultPolicy()
}
if err := c.RotateLocalSnapshots(policy); err != nil {
fmt.Printf("Error rotating snapshots: %v\n", err)
os.Exit(1)
}
fmt.Println("\n✓ Rotation completed!")
case "rotate-remote":
// Request server to rotate remote snapshots
if err := c.RequestRotation(); err != nil {
fmt.Printf("Error requesting rotation: %v\n", err)
os.Exit(1)
}
case "status":
if err := c.GetStatus(); err != nil {
fmt.Printf("Error getting status: %v\n", err)
os.Exit(1)
}
case "bookmarks":
// List bookmarks
fmt.Println("=== ZFS Bookmarks ===\n")
bookmark, err := c.GetLastBookmark()
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
if bookmark == "" {
fmt.Println("No bookmarks found")
} else {
fmt.Printf("Last bookmark: %s\n", bookmark)
}
case "help", "-h", "--help":
printUsage()
default:
fmt.Printf("Unknown command: %s\n", command)
printUsage()
os.Exit(1)
}
}
// getRotationPolicy fetches the rotation policy from the server.
// If the server has a policy configured, it must be used.
// Otherwise, the default policy is returned.
func getRotationPolicy(c *client.Client) (*client.SnapshotPolicy, error) {
serverPolicy, err := c.GetRotationPolicy()
if err != nil {
return nil, err
}
if serverPolicy.ServerManaged && serverPolicy.RotationPolicy != nil {
fmt.Println(" Using server-managed rotation policy")
return serverPolicy.RotationPolicy, nil
}
// No server policy, use default
fmt.Println(" Using default rotation policy")
return client.DefaultPolicy(), nil
}
func printUsage() {
fmt.Println("ZFS Snapshot Backup Client")
fmt.Println("\nUsage: zfs-client [command]")
fmt.Println("\nCommands:")
fmt.Println(" backup - Create snapshot and send (auto incremental if bookmark exists)")
fmt.Println(" backup-full - Create full backup (no incremental)")
fmt.Println(" backup-incremental - Create incremental backup from last bookmark")
fmt.Println(" snapshot <type> - Create typed snapshot (hourly|daily|weekly|monthly)")
fmt.Println(" rotate - Rotate local snapshots based on retention policy")
fmt.Println(" rotate-remote - Request server to rotate old remote snapshots")
fmt.Println(" status - Check server status and quota")
fmt.Println(" bookmarks - List ZFS bookmarks")
fmt.Println(" help - Show this help message")
fmt.Println("\nSnapshot Retention Policy (default):")
fmt.Println(" Hourly: 24 snapshots")
fmt.Println(" Daily: 7 snapshots")
fmt.Println(" Weekly: 4 snapshots")
fmt.Println(" Monthly: 12 snapshots")
fmt.Println("\nEnvironment Variables (can be set in .env file):")
fmt.Println(" CLIENT_ID - Client identifier (default: client1)")
fmt.Println(" API_KEY - API key for authentication (default: secret123)")
fmt.Println(" SERVER_URL - Backup server URL (default: http://localhost:8080)")
fmt.Println(" LOCAL_DATASET - ZFS dataset to backup (default: tank/data)")
fmt.Println(" COMPRESS - Enable gzip compression (default: true)")
fmt.Println(" STORAGE_TYPE - Storage type: s3 or local (default: s3)")
fmt.Println("\nExamples:")
fmt.Println(" zfs-client backup")
fmt.Println(" zfs-client backup-full")
fmt.Println(" zfs-client snapshot hourly")
fmt.Println(" zfs-client rotate")
fmt.Println(" CLIENT_ID=myclient zfs-client backup")
}