151 lines
4.6 KiB
Go
151 lines
4.6 KiB
Go
package strategy
|
|
|
|
import (
|
|
"backea/internal/backup/models"
|
|
"backea/internal/backup/strategy/kopia"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Factory handles backup strategy creation
|
|
type Factory struct {
|
|
Config *models.Configuration
|
|
ConfigPath string
|
|
}
|
|
|
|
// NewFactory initializes and returns a Factory
|
|
func NewFactory(configPath string) (*Factory, error) {
|
|
cfg, err := models.LoadConfig(configPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
return &Factory{Config: cfg, ConfigPath: configPath}, nil
|
|
}
|
|
|
|
// CreateBackupStrategyForService returns a backup strategy based on config
|
|
func (f *Factory) CreateBackupStrategyForService(groupName, serviceIndex string) (Strategy, error) {
|
|
serviceConfig, err := f.getServiceConfig(groupName, serviceIndex)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sourcePath, err := f.getServicePath(groupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch serviceConfig.BackupStrategy.Type {
|
|
case "kopia":
|
|
return f.createKopiaStrategy(serviceConfig, sourcePath)
|
|
default:
|
|
return nil, fmt.Errorf("unknown strategy type: %s", serviceConfig.BackupStrategy.Type)
|
|
}
|
|
}
|
|
|
|
// createKopiaStrategy initializes a Kopia strategy
|
|
func (f *Factory) createKopiaStrategy(serviceConfig *models.BackupConfig, sourcePath string) (Strategy, error) {
|
|
provider, err := f.createKopiaProvider(serviceConfig.BackupStrategy)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create kopia provider: %w", err)
|
|
}
|
|
|
|
retention := kopia.Retention(serviceConfig.BackupStrategy.Retention)
|
|
configPath := generateTempConfigPath(provider.GetProviderType())
|
|
|
|
return kopia.NewStrategy(retention, provider, configPath, sourcePath), nil
|
|
}
|
|
|
|
// createKopiaProvider returns a Kopia storage provider
|
|
func (f *Factory) createKopiaProvider(strategyConfig models.StrategyConfig) (kopia.Provider, error) {
|
|
switch strategyConfig.Provider {
|
|
case "local":
|
|
return kopia.NewLocalProvider(getDefaultPath(strategyConfig.Destination.Path)), nil
|
|
case "b2", "backblaze":
|
|
return kopia.NewB2Provider(), nil
|
|
// case "sftp":
|
|
// return createSFTPProvider(strategyConfig.Destination)
|
|
default:
|
|
return nil, fmt.Errorf("unknown kopia provider type: %s", strategyConfig.Provider)
|
|
}
|
|
}
|
|
|
|
// createSFTPProvider handles SFTP provider creation
|
|
func createSFTPProvider(dest DestinationConfig) (kopia.Provider, error) {
|
|
host, username := parseSFTPHost(dest.Host)
|
|
if host == "" {
|
|
return nil, fmt.Errorf("SFTP host not specified")
|
|
}
|
|
return kopia.NewSFTPProvider(host, getDefaultPath(dest.Path), username, getSSHKeyPath(dest.SSHKey)), nil
|
|
}
|
|
|
|
// parseSFTPHost extracts the username and host from "user@host"
|
|
func parseSFTPHost(host string) (string, string) {
|
|
if atIndex := strings.Index(host, "@"); atIndex > 0 {
|
|
return host[atIndex+1:], host[:atIndex]
|
|
}
|
|
return host, getEnvUser()
|
|
}
|
|
|
|
// getDefaultPath returns the given path or a default
|
|
func getDefaultPath(path string) string {
|
|
if path != "" {
|
|
return path
|
|
}
|
|
home, _ := os.UserHomeDir()
|
|
return filepath.Join(home, ".backea", "repos")
|
|
}
|
|
|
|
// getEnvUser returns the system username
|
|
func getEnvUser() string {
|
|
if user := os.Getenv("USER"); user != "" {
|
|
return user
|
|
}
|
|
return "root"
|
|
}
|
|
|
|
// getSSHKeyPath returns the SSH key path
|
|
func getSSHKeyPath(key string) string {
|
|
if key != "" {
|
|
return key
|
|
}
|
|
home, _ := os.UserHomeDir()
|
|
return filepath.Join(home, ".ssh", "id_rsa")
|
|
}
|
|
|
|
// getServicePath returns a service's path
|
|
func (f *Factory) getServicePath(groupName string) (string, error) {
|
|
if serviceGroup, exists := f.Config.Services[groupName]; exists {
|
|
return serviceGroup.Source.Path, nil
|
|
}
|
|
return "", fmt.Errorf("service group not found: %s", groupName)
|
|
}
|
|
|
|
// getServiceConfig returns a service's config
|
|
func (f *Factory) getServiceConfig(groupName, serviceIndex string) (*models.BackupConfig, error) {
|
|
if serviceGroup, exists := f.Config.Services[groupName]; exists {
|
|
if backupConfig, exists := serviceGroup.BackupConfigs[serviceIndex]; exists {
|
|
return &backupConfig, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("backup config not found: %s.%s", groupName, serviceIndex)
|
|
}
|
|
|
|
// ReloadConfig reloads configuration
|
|
func (f *Factory) ReloadConfig() error {
|
|
cfg, err := models.LoadConfig(f.ConfigPath)
|
|
if err != nil {
|
|
return fmt.Errorf("could not reload config: %w", err)
|
|
}
|
|
f.Config = cfg
|
|
return nil
|
|
}
|
|
|
|
// generateTempConfigPath creates a temp Kopia config path
|
|
func generateTempConfigPath(providerType string) string {
|
|
tmpConfigDir := filepath.Join(os.TempDir(), "kopia_configs")
|
|
_ = os.MkdirAll(tmpConfigDir, 0755) // Ensure directory exists
|
|
return filepath.Join(tmpConfigDir, fmt.Sprintf("kopia_%s_%d.config", providerType, time.Now().UnixNano()))
|
|
}
|