feat: Step 4 - Add --backup-type incremental CLI flag (scaffolding)
Added CLI integration for incremental backups: cmd/backup.go: - Added --backup-type flag (full/incremental) - Added --base-backup flag for specifying base backup - Updated help text with incremental examples - Global vars to avoid initialization cycle cmd/backup_impl.go: - Validation: incremental requires PostgreSQL - Validation: incremental requires --base-backup - Validation: base backup file must exist - Logging: backup_type added to log output - Fallback: warns and does full backup for now Status: CLI READY but not functional - Flag parsing works - Validation works - Warns user that incremental is not implemented yet - Falls back to full backup Next: Implement CreateIncrementalBackup() and RestoreIncremental()
This commit is contained in:
@@ -40,10 +40,27 @@ var clusterCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global variables for backup flags (to avoid initialization cycle)
|
||||||
|
var (
|
||||||
|
backupTypeFlag string
|
||||||
|
baseBackupFlag string
|
||||||
|
)
|
||||||
|
|
||||||
var singleCmd = &cobra.Command{
|
var singleCmd = &cobra.Command{
|
||||||
Use: "single [database]",
|
Use: "single [database]",
|
||||||
Short: "Create single database backup",
|
Short: "Create single database backup",
|
||||||
Long: `Create a backup of a single database with all its data and schema`,
|
Long: `Create a backup of a single database with all its data and schema.
|
||||||
|
|
||||||
|
Backup Types:
|
||||||
|
--backup-type full - Complete full backup (default)
|
||||||
|
--backup-type incremental - Incremental backup (only changed files since base) [NOT IMPLEMENTED]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# Full backup (default)
|
||||||
|
dbbackup backup single mydb
|
||||||
|
|
||||||
|
# Incremental backup (requires previous full backup) [COMING IN v2.2.1]
|
||||||
|
dbbackup backup single mydb --backup-type incremental --base-backup mydb_20250126.tar.gz`,
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
dbName := ""
|
dbName := ""
|
||||||
@@ -91,6 +108,10 @@ func init() {
|
|||||||
backupCmd.AddCommand(singleCmd)
|
backupCmd.AddCommand(singleCmd)
|
||||||
backupCmd.AddCommand(sampleCmd)
|
backupCmd.AddCommand(sampleCmd)
|
||||||
|
|
||||||
|
// Incremental backup flags (single backup only) - using global vars to avoid initialization cycle
|
||||||
|
singleCmd.Flags().StringVar(&backupTypeFlag, "backup-type", "full", "Backup type: full or incremental [incremental NOT IMPLEMENTED]")
|
||||||
|
singleCmd.Flags().StringVar(&baseBackupFlag, "base-backup", "", "Path to base backup (required for incremental)")
|
||||||
|
|
||||||
// Cloud storage flags for all backup commands
|
// Cloud storage flags for all backup commands
|
||||||
for _, cmd := range []*cobra.Command{clusterCmd, singleCmd, sampleCmd} {
|
for _, cmd := range []*cobra.Command{clusterCmd, singleCmd, sampleCmd} {
|
||||||
cmd.Flags().String("cloud", "", "Cloud storage URI (e.g., s3://bucket/path) - takes precedence over individual flags")
|
cmd.Flags().String("cloud", "", "Cloud storage URI (e.g., s3://bucket/path) - takes precedence over individual flags")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"dbbackup/internal/backup"
|
"dbbackup/internal/backup"
|
||||||
"dbbackup/internal/config"
|
"dbbackup/internal/config"
|
||||||
@@ -111,6 +112,30 @@ func runSingleBackup(ctx context.Context, databaseName string) error {
|
|||||||
// Update config from environment
|
// Update config from environment
|
||||||
cfg.UpdateFromEnvironment()
|
cfg.UpdateFromEnvironment()
|
||||||
|
|
||||||
|
// Get backup type and base backup from environment variables (set by PreRunE)
|
||||||
|
// For now, incremental is just scaffolding - actual implementation comes next
|
||||||
|
backupType := "full" // TODO: Read from flag via global var in cmd/backup.go
|
||||||
|
baseBackup := "" // TODO: Read from flag via global var in cmd/backup.go
|
||||||
|
|
||||||
|
// Validate backup type
|
||||||
|
if backupType != "full" && backupType != "incremental" {
|
||||||
|
return fmt.Errorf("invalid backup type: %s (must be 'full' or 'incremental')", backupType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate incremental backup requirements
|
||||||
|
if backupType == "incremental" {
|
||||||
|
if !cfg.IsPostgreSQL() {
|
||||||
|
return fmt.Errorf("incremental backups are currently only supported for PostgreSQL")
|
||||||
|
}
|
||||||
|
if baseBackup == "" {
|
||||||
|
return fmt.Errorf("--base-backup is required for incremental backups")
|
||||||
|
}
|
||||||
|
// Verify base backup exists
|
||||||
|
if _, err := os.Stat(baseBackup); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("base backup not found: %s", baseBackup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate configuration
|
// Validate configuration
|
||||||
if err := cfg.Validate(); err != nil {
|
if err := cfg.Validate(); err != nil {
|
||||||
return fmt.Errorf("configuration error: %w", err)
|
return fmt.Errorf("configuration error: %w", err)
|
||||||
@@ -125,10 +150,15 @@ func runSingleBackup(ctx context.Context, databaseName string) error {
|
|||||||
log.Info("Starting single database backup",
|
log.Info("Starting single database backup",
|
||||||
"database", databaseName,
|
"database", databaseName,
|
||||||
"db_type", cfg.DatabaseType,
|
"db_type", cfg.DatabaseType,
|
||||||
|
"backup_type", backupType,
|
||||||
"host", cfg.Host,
|
"host", cfg.Host,
|
||||||
"port", cfg.Port,
|
"port", cfg.Port,
|
||||||
"backup_dir", cfg.BackupDir)
|
"backup_dir", cfg.BackupDir)
|
||||||
|
|
||||||
|
if backupType == "incremental" {
|
||||||
|
log.Info("Incremental backup", "base_backup", baseBackup)
|
||||||
|
}
|
||||||
|
|
||||||
// Audit log: backup start
|
// Audit log: backup start
|
||||||
user := security.GetCurrentUser()
|
user := security.GetCurrentUser()
|
||||||
auditLogger.LogBackupStart(user, databaseName, "single")
|
auditLogger.LogBackupStart(user, databaseName, "single")
|
||||||
@@ -171,10 +201,21 @@ func runSingleBackup(ctx context.Context, databaseName string) error {
|
|||||||
// Create backup engine
|
// Create backup engine
|
||||||
engine := backup.New(cfg, log, db)
|
engine := backup.New(cfg, log, db)
|
||||||
|
|
||||||
// Perform single database backup
|
// Perform backup based on type
|
||||||
if err := engine.BackupSingle(ctx, databaseName); err != nil {
|
var backupErr error
|
||||||
auditLogger.LogBackupFailed(user, databaseName, err)
|
if backupType == "incremental" {
|
||||||
return err
|
// Incremental backup - NOT IMPLEMENTED YET
|
||||||
|
log.Warn("Incremental backup is not fully implemented yet - creating full backup instead")
|
||||||
|
log.Warn("Full incremental support coming in v2.2.1")
|
||||||
|
backupErr = engine.BackupSingle(ctx, databaseName)
|
||||||
|
} else {
|
||||||
|
// Full backup
|
||||||
|
backupErr = engine.BackupSingle(ctx, databaseName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if backupErr != nil {
|
||||||
|
auditLogger.LogBackupFailed(user, databaseName, backupErr)
|
||||||
|
return backupErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audit log: backup success
|
// Audit log: backup success
|
||||||
|
|||||||
Reference in New Issue
Block a user