done
This commit is contained in:
218
internal/config/manager.go
Normal file
218
internal/config/manager.go
Normal file
@@ -0,0 +1,218 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.gostacks.org/iwasforcedtobehere/fastestmirror/internal/distro"
|
||||
)
|
||||
|
||||
// ConfigManager handles backup and modification of distribution config files
|
||||
type ConfigManager struct {
|
||||
DistroInfo *distro.DistroInfo
|
||||
BackupDir string
|
||||
}
|
||||
|
||||
// NewConfigManager creates a new configuration manager
|
||||
func NewConfigManager(distroInfo *distro.DistroInfo) *ConfigManager {
|
||||
return &ConfigManager{
|
||||
DistroInfo: distroInfo,
|
||||
BackupDir: "/etc/fastestmirror/backups",
|
||||
}
|
||||
}
|
||||
|
||||
// BackupConfig creates a backup of the current configuration
|
||||
func (cm *ConfigManager) BackupConfig() (string, error) {
|
||||
configFile := cm.DistroInfo.GetConfigFile()
|
||||
if configFile == "" {
|
||||
return "", fmt.Errorf("no configuration file defined for %s", cm.DistroInfo.Family)
|
||||
}
|
||||
|
||||
// Create backup directory if it doesn't exist
|
||||
if err := os.MkdirAll(cm.BackupDir, 0755); err != nil {
|
||||
return "", fmt.Errorf("failed to create backup directory: %w", err)
|
||||
}
|
||||
|
||||
// Generate backup filename with timestamp
|
||||
timestamp := time.Now().Format("20060102-150405")
|
||||
backupName := fmt.Sprintf("%s.backup.%s", filepath.Base(configFile), timestamp)
|
||||
backupPath := filepath.Join(cm.BackupDir, backupName)
|
||||
|
||||
// Copy original file to backup location
|
||||
if err := cm.copyFile(configFile, backupPath); err != nil {
|
||||
return "", fmt.Errorf("failed to create backup: %w", err)
|
||||
}
|
||||
|
||||
return backupPath, nil
|
||||
}
|
||||
|
||||
// ApplyMirror applies the fastest mirror to the configuration
|
||||
func (cm *ConfigManager) ApplyMirror(mirrorURL string) error {
|
||||
switch cm.DistroInfo.Family {
|
||||
case "debian":
|
||||
return cm.applyDebianMirror(mirrorURL)
|
||||
case "arch":
|
||||
return cm.applyArchMirror(mirrorURL)
|
||||
default:
|
||||
return fmt.Errorf("configuration management for %s family not implemented yet", cm.DistroInfo.Family)
|
||||
}
|
||||
}
|
||||
|
||||
// applyDebianMirror updates /etc/apt/sources.list for Debian-based systems
|
||||
func (cm *ConfigManager) applyDebianMirror(mirrorURL string) error {
|
||||
configFile := "/etc/apt/sources.list"
|
||||
|
||||
// Read current configuration
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read %s: %w", configFile, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// Process each line and replace mirror URLs
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.TrimSpace(line) == "" || strings.HasPrefix(strings.TrimSpace(line), "#") {
|
||||
// Keep comments and empty lines as-is
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse and update deb/deb-src lines
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "deb") {
|
||||
updatedLine := cm.updateDebianLine(line, mirrorURL)
|
||||
lines = append(lines, updatedLine)
|
||||
} else {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", configFile, err)
|
||||
}
|
||||
|
||||
// Write updated configuration
|
||||
return cm.writeLines(configFile, lines)
|
||||
}
|
||||
|
||||
// updateDebianLine updates a single deb line with the new mirror URL
|
||||
func (cm *ConfigManager) updateDebianLine(line, newMirrorURL string) string {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 3 {
|
||||
return line // Invalid line, keep as-is
|
||||
}
|
||||
|
||||
// parts[0] = "deb" or "deb-src"
|
||||
// parts[1] = URL
|
||||
// parts[2] = suite/codename
|
||||
// parts[3+] = components
|
||||
|
||||
parts[1] = strings.TrimSuffix(newMirrorURL, "/") + "/"
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// applyArchMirror updates /etc/pacman.d/mirrorlist for Arch-based systems
|
||||
func (cm *ConfigManager) applyArchMirror(mirrorURL string) error {
|
||||
configFile := "/etc/pacman.d/mirrorlist"
|
||||
|
||||
// Read current configuration
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read %s: %w", configFile, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
firstMirrorAdded := false
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// Add the fastest mirror at the top (uncommented)
|
||||
lines = append(lines, fmt.Sprintf("# FastestMirror: Added %s", time.Now().Format("2006-01-02 15:04:05")))
|
||||
lines = append(lines, fmt.Sprintf("Server = %s/$repo/os/$arch", strings.TrimSuffix(mirrorURL, "/")))
|
||||
lines = append(lines, "")
|
||||
firstMirrorAdded = true
|
||||
|
||||
// Process existing lines, commenting out other Server entries
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
trimmed := strings.TrimSpace(line)
|
||||
|
||||
// Comment out existing Server lines (but keep them for reference)
|
||||
if strings.HasPrefix(trimmed, "Server = ") && firstMirrorAdded {
|
||||
lines = append(lines, "#"+line)
|
||||
} else {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", configFile, err)
|
||||
}
|
||||
|
||||
// Write updated configuration
|
||||
return cm.writeLines(configFile, lines)
|
||||
}
|
||||
|
||||
// copyFile copies a file from src to dst
|
||||
func (cm *ConfigManager) copyFile(src, dst string) error {
|
||||
sourceFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
destFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
_, err = io.Copy(destFile, sourceFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy file permissions
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Chmod(dst, srcInfo.Mode())
|
||||
}
|
||||
|
||||
// writeLines writes lines to a file
|
||||
func (cm *ConfigManager) writeLines(filename string, lines []string) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
writer := bufio.NewWriter(file)
|
||||
for _, line := range lines {
|
||||
if _, err := writer.WriteString(line + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return writer.Flush()
|
||||
}
|
||||
|
||||
// RestoreBackup restores a configuration from backup
|
||||
func (cm *ConfigManager) RestoreBackup(backupPath string) error {
|
||||
configFile := cm.DistroInfo.GetConfigFile()
|
||||
if configFile == "" {
|
||||
return fmt.Errorf("no configuration file defined for %s", cm.DistroInfo.Family)
|
||||
}
|
||||
|
||||
return cm.copyFile(backupPath, configFile)
|
||||
}
|
Reference in New Issue
Block a user