Enterprise-Backup-, Recovery-, Verification-, Security- und Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme. Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes geht in keine Bewertung als "gut" ein. Umfang (Phasen 0-23): - Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll, Katalogaufbau allein aus den Manifesten — ohne Datenbank - Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck - Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell) - Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive - Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit - Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center, Ransomware-Heuristik (meldet, handelt nie) - Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing - Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository - Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64 Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs. Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md und docs/release-candidate.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
146 lines
5.3 KiB
Go
146 lines
5.3 KiB
Go
package jobs
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/syncova/syncova/packages/scheduler"
|
|
)
|
|
|
|
// ExecutionRequest beschreibt einen auszuführenden Sicherungslauf.
|
|
type ExecutionRequest struct {
|
|
// Job ist der auszuführende Auftrag.
|
|
Job *Job
|
|
// RunID ist die Kennung des Laufs.
|
|
RunID uuid.UUID
|
|
// CorrelationID verbindet den Lauf mit seinen Protokollzeilen.
|
|
CorrelationID uuid.UUID
|
|
// AttemptNumber ist die Nummer des Versuchs, beginnend bei 1.
|
|
AttemptNumber int
|
|
// ProgressCallback meldet den Fortschritt.
|
|
//
|
|
// Die Schleife nutzt ihn für die Lebendmeldung: Ein Lauf, der stundenlang
|
|
// nichts von sich hören lässt, wäre von einem abgestürzten Server nicht zu
|
|
// unterscheiden.
|
|
ProgressCallback func(ExecutionProgress)
|
|
}
|
|
|
|
// ExecutionProgress meldet den Stand eines laufenden Vorgangs.
|
|
type ExecutionProgress struct {
|
|
// BytesProcessed ist die bisher gelesene Datenmenge.
|
|
BytesProcessed int64
|
|
// FilesProcessed ist die Zahl bisher bearbeiteter Objekte.
|
|
FilesProcessed int64
|
|
// CurrentSource benennt die gerade bearbeitete Quelle.
|
|
CurrentSource string
|
|
}
|
|
|
|
// ExecutionResult ist das Ergebnis eines Sicherungslaufs.
|
|
type ExecutionResult struct {
|
|
// BytesProcessed ist die gelesene Datenmenge.
|
|
BytesProcessed int64
|
|
// BytesWritten ist die abgelegte Datenmenge.
|
|
BytesWritten int64
|
|
// FilesProcessed ist die Zahl bearbeiteter Objekte.
|
|
FilesProcessed int64
|
|
// FilesSkipped ist die Zahl übergangener Objekte.
|
|
//
|
|
// Ist sie größer als 0, ist der Lauf ein Teilfehler — unabhängig davon,
|
|
// ob der Executor einen Fehler meldet (PROMPT.md §138).
|
|
FilesSkipped int64
|
|
// SkipReasons erklären die übergangenen Objekte.
|
|
//
|
|
// Eine Zahl allein hilft nicht weiter: „17 Objekte übergangen" ist keine
|
|
// Auskunft, „17 Objekte wegen fehlender Leseberechtigung" schon.
|
|
SkipReasons []string
|
|
}
|
|
|
|
// Executor führt einen Sicherungslauf aus.
|
|
//
|
|
// Die Schnittstelle hält die Ausführungsschleife frei von Backup Engine,
|
|
// Repository und Providern. Das ist nicht nur Ordnung: Ohne sie liesse sich das
|
|
// Zusammenspiel von Zeitplan, Wartungsfenster, Nebenläufigkeit und Wiederholung
|
|
// nur mit einem echten Repository prüfen — also praktisch gar nicht.
|
|
//
|
|
// Eine Umsetzung muss zwei Regeln einhalten:
|
|
//
|
|
// 1. Der Kontext wird beachtet. Ein Abbruch muss den Lauf beenden, sonst
|
|
// hinge das Herunterfahren des Dienstes am längsten Backup.
|
|
// 2. Übergangene Objekte werden in FilesSkipped gemeldet, nicht verschwiegen.
|
|
// Ein Lauf mit übergangenen Objekten ist niemals ein Erfolg.
|
|
type Executor interface {
|
|
// Execute führt den Lauf aus.
|
|
Execute(executionContext context.Context, executionRequest ExecutionRequest) (ExecutionResult, error)
|
|
}
|
|
|
|
// ExecutorFunc erlaubt es, eine Funktion als Executor zu verwenden.
|
|
type ExecutorFunc func(executionContext context.Context, executionRequest ExecutionRequest) (ExecutionResult, error)
|
|
|
|
// Execute erfüllt die Executor-Schnittstelle.
|
|
func (executorFunction ExecutorFunc) Execute(executionContext context.Context, executionRequest ExecutionRequest) (ExecutionResult, error) {
|
|
return executorFunction(executionContext, executionRequest)
|
|
}
|
|
|
|
// NotImplementedExecutor meldet, dass keine Ausführung eingerichtet ist.
|
|
//
|
|
// Er ist der Standard, solange die Anbindung an die Backup Engine fehlt. Ein
|
|
// Executor, der stillschweigend Erfolg meldete, wäre das gefährlichste
|
|
// Fake-Feature der ganzen Anlage: Die Oberfläche zeigte grüne Läufe, und im
|
|
// Repository läge nichts.
|
|
type NotImplementedExecutor struct{}
|
|
|
|
// Execute meldet die fehlende Ausführung als Fehler.
|
|
func (executor *NotImplementedExecutor) Execute(_ context.Context, _ ExecutionRequest) (ExecutionResult, error) {
|
|
return ExecutionResult{}, &ExecutionError{
|
|
Code: "EXECUTOR_NOT_CONFIGURED",
|
|
Message: "Für diesen Dienst ist keine Ausführung eingerichtet; der Auftrag wurde nicht gesichert.",
|
|
FailureClass: scheduler.FailureConfiguration,
|
|
}
|
|
}
|
|
|
|
// ExecutionError ist ein eingeordneter Fehler eines Sicherungslaufs.
|
|
//
|
|
// Die Einordnung entscheidet über die Wiederholung. Ein Executor, der nur einen
|
|
// gewöhnlichen Fehler zurückgibt, bekommt die sichere Voreinstellung
|
|
// „dauerhaft" — es wird also nicht wiederholt. Wer Wiederholungen will, muss
|
|
// die Fehlerart ausdrücklich benennen.
|
|
type ExecutionError struct {
|
|
// Code ist die Fehlerkennung in SCREAMING_SNAKE_CASE.
|
|
Code string
|
|
// Message ist die verständliche Meldung.
|
|
//
|
|
// Sie enthält niemals Geheimnisse (PROMPT.md §141).
|
|
Message string
|
|
// FailureClass ordnet den Fehler ein.
|
|
FailureClass scheduler.FailureClass
|
|
// Cause ist der zugrunde liegende Fehler.
|
|
Cause error
|
|
}
|
|
|
|
// Error erfüllt die Fehlerschnittstelle.
|
|
func (executionError *ExecutionError) Error() string {
|
|
return executionError.Message
|
|
}
|
|
|
|
// Unwrap gibt die Ursache frei.
|
|
func (executionError *ExecutionError) Unwrap() error {
|
|
return executionError.Cause
|
|
}
|
|
|
|
// ExecutionOutcome ist das ausgewertete Ergebnis eines Laufs.
|
|
type ExecutionOutcome struct {
|
|
// Status ist der erreichte Zustand.
|
|
Status RunStatus
|
|
// Result sind die Kennzahlen.
|
|
Result ExecutionResult
|
|
// FailureClass ordnet einen Fehler ein.
|
|
FailureClass scheduler.FailureClass
|
|
// ErrorCode ist die Fehlerkennung.
|
|
ErrorCode string
|
|
// ErrorMessage ist die verständliche Meldung.
|
|
ErrorMessage string
|
|
// Duration ist die Dauer des Laufs.
|
|
Duration time.Duration
|
|
}
|