package backup import ( "backea/internal/mail" "context" "fmt" "log" "sync" ) // PerformBackups executes backups for multiple services based on configuration func PerformBackups(ctx context.Context, configPath string, serviceName string, serviceIndex string) error { // Create backup factory factory, err := NewBackupFactory(configPath) if err != nil { return err } // Initialize mailer mailer := mail.NewMailer() // Process services if serviceName != "" { // Process single service group or specific service if serviceIndex != "" { // Process specific service within a group return processSpecificService(ctx, factory, serviceName, serviceIndex, mailer) } else { // Process all services in the specified group return processServiceGroup(ctx, factory, serviceName, mailer) } } else { // Process all service groups in parallel var wg sync.WaitGroup errs := make(chan error, len(factory.Config.Services)) for groupName := range factory.Config.Services { wg.Add(1) go func(group string) { defer wg.Done() if err := processServiceGroup(ctx, factory, group, mailer); err != nil { log.Printf("Failed to backup service group %s: %v", group, err) errs <- fmt.Errorf("backup failed for group %s: %w", group, err) } }(groupName) } // Wait for all backups to complete wg.Wait() close(errs) // Check if any errors occurred var lastErr error for err := range errs { lastErr = err } return lastErr } } // processServiceGroup handles the backup for all services in a group func processServiceGroup(ctx context.Context, factory *BackupFactory, groupName string, mailer *mail.Mailer) error { // Get service group configuration serviceGroup, exists := factory.Config.Services[groupName] if !exists { log.Printf("Service group not found: %s", groupName) return nil } // Execute the before hook once for the entire group if serviceGroup.Hooks.BeforeHook != "" { log.Printf("Executing before hook for group %s: %s", groupName, serviceGroup.Hooks.BeforeHook) if err := RunCommand(serviceGroup.Source.Path, serviceGroup.Hooks.BeforeHook); err != nil { log.Printf("Failed to execute before hook for group %s: %v", groupName, err) return err } } // Process all services in the group in parallel var wg sync.WaitGroup errs := make(chan error, len(serviceGroup.BackupConfigs)) for configIndex := range serviceGroup.BackupConfigs { wg.Add(1) go func(group, index string) { defer wg.Done() if err := processSpecificService(ctx, factory, group, index, mailer); err != nil { log.Printf("Failed to backup service %s.%s: %v", group, index, err) errs <- fmt.Errorf("backup failed for %s.%s: %w", group, index, err) } }(groupName, configIndex) } // Wait for all backups to complete wg.Wait() close(errs) // Execute the after hook once for the entire group if serviceGroup.Hooks.AfterHook != "" { log.Printf("Executing after hook for group %s: %s", groupName, serviceGroup.Hooks.AfterHook) if err := RunCommand(serviceGroup.Source.Path, serviceGroup.Hooks.AfterHook); err != nil { log.Printf("Failed to execute after hook for group %s: %v", groupName, err) // We don't return here because we want to process the errors from the backups } } // Check if any errors occurred var lastErr error for err := range errs { lastErr = err } return lastErr } // processSpecificService handles the backup for a specific service in a group func processSpecificService(ctx context.Context, factory *BackupFactory, groupName string, configIndex string, mailer *mail.Mailer) error { // Get service configuration serviceGroup := factory.Config.Services[groupName] // Create the appropriate backup strategy using the factory strategy, err := factory.CreateBackupStrategyForService(groupName, configIndex) if err != nil { log.Printf("Failed to create backup strategy for service %s.%s: %v", groupName, configIndex, err) return err } // Create and run service service := NewService( fmt.Sprintf("%s.%s", groupName, configIndex), serviceGroup.Source.Path, strategy, mailer, ) return service.Backup(ctx) }