backea/internal/backup/backup_factory.go
2025-03-20 22:14:45 +01:00

188 lines
5.7 KiB
Go

package backup
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
// BackupFactory creates backup strategies and managers
type BackupFactory struct {
Config *Configuration
ConfigPath string
}
// NewBackupFactory creates a new backup factory
func NewBackupFactory(configPath string) (*BackupFactory, error) {
// Load configuration
config, err := LoadConfig(configPath)
if err != nil {
return nil, fmt.Errorf("could not load config: %w", err)
}
return &BackupFactory{
Config: config,
ConfigPath: configPath,
}, nil
}
// CreateKopiaStrategy creates a new Kopia backup strategy with specific retention settings and provider
func (f *BackupFactory) CreateKopiaStrategy(retention Retention, provider KopiaProvider) *KopiaStrategy {
// Create temp directory for Kopia configs if it doesn't exist
tmpConfigDir := filepath.Join(os.TempDir(), "kopia_configs")
if err := os.MkdirAll(tmpConfigDir, 0755); err != nil {
log.Printf("Warning: failed to create temp config directory: %v", err)
// Fall back to default config path
tmpConfigDir = os.TempDir()
}
// Generate a unique config file path for this instance
configPath := filepath.Join(tmpConfigDir, fmt.Sprintf("kopia_%s_%d.config",
provider.GetProviderType(), time.Now().UnixNano()))
// Need to update this line to pass configPath
return NewKopiaStrategy(retention, provider, configPath)
}
// CreateKopiaProvider creates the appropriate Kopia provider based on config
func (f *BackupFactory) CreateKopiaProvider(strategyConfig StrategyConfig) (KopiaProvider, error) {
switch strategyConfig.Provider {
case "b2_backblaze":
return NewKopiaB2Provider(), nil
case "local":
// Extract options for local path if specified
basePath := ""
if strategyConfig.Options != "" {
basePath = strategyConfig.Options
} else {
// Default to ~/.backea/repos if not specified
basePath = filepath.Join(os.Getenv("HOME"), ".backea", "repos")
}
return NewKopiaLocalProvider(basePath), nil
case "sftp":
// Parse options for SFTP - expected format: "user@host:/path/to/backups"
host := strategyConfig.Destination.Host
path := strategyConfig.Destination.Path
sshKey := strategyConfig.Destination.SSHKey
if host == "" {
return nil, fmt.Errorf("SFTP provider requires host in destination config")
}
if path == "" {
return nil, fmt.Errorf("SFTP provider requires path in destination config")
}
// Default SSH key if not specified
if sshKey == "" {
sshKey = filepath.Join(os.Getenv("HOME"), ".ssh", "id_rsa")
}
// Extract username from host if in format user@host
username := "root" // default
if hostParts := strings.Split(host, "@"); len(hostParts) > 1 {
username = hostParts[0]
host = hostParts[1]
}
return NewKopiaSFTPProvider(host, path, username, sshKey), nil
default:
return nil, fmt.Errorf("unsupported Kopia provider: %s", strategyConfig.Provider)
}
}
// CreateBackupStrategyForService creates a backup strategy for the specified service within a group
func (f *BackupFactory) CreateBackupStrategyForService(groupName string, serviceIndex string) (Strategy, error) {
// Find service group
serviceGroup, exists := f.Config.Services[groupName]
if !exists {
return nil, fmt.Errorf("service group not found: %s", groupName)
}
// Find specific backup config within the group
backupConfig, exists := serviceGroup.BackupConfigs[serviceIndex]
if !exists {
return nil, fmt.Errorf("backup config not found: %s.%s", groupName, serviceIndex)
}
// Create appropriate strategy based on type
strategyConfig := backupConfig.BackupStrategy
// Extract retention settings once
retention := Retention{
KeepLatest: strategyConfig.Retention.KeepLatest,
KeepHourly: strategyConfig.Retention.KeepHourly,
KeepDaily: strategyConfig.Retention.KeepDaily,
KeepWeekly: strategyConfig.Retention.KeepWeekly,
KeepMonthly: strategyConfig.Retention.KeepMonthly,
KeepYearly: strategyConfig.Retention.KeepYearly,
}
switch strategyConfig.Type {
case "kopia":
// Create appropriate provider based on configuration
provider, err := f.CreateKopiaProvider(strategyConfig)
if err != nil {
return nil, fmt.Errorf("failed to create kopia provider: %w", err)
}
return f.CreateKopiaStrategy(retention, provider), nil
case "rsync":
// Uncomment when rsync implementation is ready
// return NewRsyncStrategy(
// strategyConfig.Options,
// Destination{
// Host: strategyConfig.Destination.Host,
// Path: strategyConfig.Destination.Path,
// SSHKey: strategyConfig.Destination.SSHKey,
// },
// ), nil
fallthrough
default:
// Default to local kopia if type is unknown or rsync (temporarily)
provider, _ := f.CreateKopiaProvider(StrategyConfig{Provider: "local"})
return f.CreateKopiaStrategy(retention, provider), nil
}
}
// ExecuteGroupHooks runs the hooks for a service group
func (f *BackupFactory) ExecuteGroupHooks(groupName string, isBeforeHook bool) error {
serviceGroup, exists := f.Config.Services[groupName]
if !exists {
return fmt.Errorf("service group not found: %s", groupName)
}
var hook string
if isBeforeHook {
hook = serviceGroup.Hooks.BeforeHook
} else {
hook = serviceGroup.Hooks.AfterHook
}
if hook == "" {
return nil
}
// Execute the hook
cmd := exec.Command("sh", "-c", hook)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("hook execution failed: %w, output: %s", err, string(output))
}
log.Printf("Hook executed successfully: %s, output: %s", hook, string(output))
return nil
}
// ReloadConfig refreshes the configuration from the config file
func (f *BackupFactory) ReloadConfig() error {
config, err := LoadConfig(f.ConfigPath)
if err != nil {
return fmt.Errorf("could not reload config: %w", err)
}
f.Config = config
return nil
}