package backup import ( "fmt" "os" "path/filepath" "sort" ) // ListGenerations liefert alle Generation-IDs in backupDir, aufsteigend // sortiert (die GenerationID selbst ist chronologisch sortierbar, siehe // NewGenerationID — kein Blick auf Dateisystem-Zeitstempel nötig, die bei // einem Restore/Kopiervorgang verändert werden könnten). func ListGenerations(backupDir string) ([]string, error) { entries, err := os.ReadDir(backupDir) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, fmt.Errorf("backup: sicherungsverzeichnis lesen: %w", err) } var generations []string for _, e := range entries { if e.IsDir() { generations = append(generations, e.Name()) } } sort.Strings(generations) return generations, nil } // Rotate entfernt alle bis auf die `keep` NEUESTEN Generationen // (Akzeptanzkriterium 3) — jede Generation umfasst ihre Vollsicherung UND // alle davon abhängigen Inkremente, ein Löschen der gesamten // Generationsverzeichnisses entfernt beides konsistent zusammen. func Rotate(backupDir string, keep int) (removed []string, err error) { if keep < 0 { keep = 0 } generations, err := ListGenerations(backupDir) if err != nil { return nil, err } if len(generations) <= keep { return nil, nil } toRemove := generations[:len(generations)-keep] for _, gen := range toRemove { if err := os.RemoveAll(filepath.Join(backupDir, gen)); err != nil { return removed, fmt.Errorf("backup: generation %q entfernen: %w", gen, err) } removed = append(removed, gen) } return removed, nil } // PruneRotate wendet BAK-07s gestaffelte RetentionPolicy (PruneTiered, // siehe retention.go) tatsächlich an — entfernt alle nicht mehr zu // behaltenden Generationsverzeichnisse. Automatisierte Löschung nach // Ablauf der Frist (Akzeptanzkriterium 2), unabhängig von RET-02s // Dokumenten-Retention. func PruneRotate(backupDir string, policy RetentionPolicy) (removed []string, err error) { generations, err := ListGenerations(backupDir) if err != nil { return nil, err } _, toRemove := PruneTiered(generations, policy) for _, gen := range toRemove { if err := os.RemoveAll(filepath.Join(backupDir, gen)); err != nil { return removed, fmt.Errorf("backup: generation %q entfernen: %w", gen, err) } removed = append(removed, gen) } return removed, nil }