Release 2026.09.27.r002: clarify rsync dry runs and expand run diagnostics
This commit is contained in:
+59
-21
@@ -1135,13 +1135,20 @@ func (s *Service) executeRsync(ctx context.Context, run model.Run) (result model
|
||||
if !ok {
|
||||
return failed(run, "validation", "job no longer exists")
|
||||
}
|
||||
run.RsyncDryRun = job.Rsync.DryRun
|
||||
defer func() { result = s.finishJob(run, job, result) }()
|
||||
live := run
|
||||
logRsyncSetup(&live, job)
|
||||
defer func() {
|
||||
if result.Status == "failed" || result.Status == "cancelled" {
|
||||
appendLiveLog(&live, "Rsync abgebrochen/fehlgeschlagen: "+result.Message)
|
||||
}
|
||||
result.LiveLog = live.LiveLog
|
||||
}()
|
||||
s.updateActive(live)
|
||||
if err := platform.ValidateRsyncPaths(job); err != nil {
|
||||
return failedError(run, err)
|
||||
}
|
||||
live := run
|
||||
appendLiveLog(&live, fmt.Sprintf("Rsync-Kopie wird vorbereitet: %d Quelle(n) nach %s", len(job.Sources), job.Rsync.Target))
|
||||
s.updateActive(live)
|
||||
for _, source := range job.Sources {
|
||||
if _, err := os.Stat(source.Path); err != nil {
|
||||
return failed(run, "source", "source unavailable: "+source.Path)
|
||||
@@ -1154,7 +1161,7 @@ func (s *Service) executeRsync(ctx context.Context, run model.Run) (result model
|
||||
return failed(run, "environment", "rsync target unavailable: "+err.Error())
|
||||
}
|
||||
if !job.Rsync.DryRun && !job.Rsync.SkipSpaceCheck {
|
||||
appendLiveLog(&live, "Rsync-Vorabprüfung ermittelt den tatsächlich zu übertragenden Datenumfang")
|
||||
appendLiveLog(&live, "Speicherplatz-Vorabprüfung: rsync vergleicht Quellen und Ziel ohne Änderungen; bei großen Verzeichnissen kann dies dauern")
|
||||
s.updateActive(live)
|
||||
estimate, err := s.rsync.Estimate(ctx, run.ID, job)
|
||||
if err != nil {
|
||||
@@ -1170,6 +1177,14 @@ func (s *Service) executeRsync(ctx context.Context, run model.Run) (result model
|
||||
return failed(run, "environment", fmt.Sprintf("Rsync-Ziel hat nicht genügend freien Speicher: ungefähr %s für %d Dateien erforderlich, aber nur %s frei unter %s", formatBytes(estimate.Bytes), estimate.Files, formatBytes(available), job.Rsync.Target))
|
||||
}
|
||||
}
|
||||
if job.Rsync.DryRun {
|
||||
appendLiveLog(&live, "Speicherplatz-Vorabprüfung entfällt beim Testlauf")
|
||||
} else if job.Rsync.SkipSpaceCheck {
|
||||
appendLiveLog(&live, "Speicherplatz-Vorabprüfung laut Job-Einstellung übersprungen")
|
||||
}
|
||||
appendLiveLog(&live, "Rsync startet den Dateivergleich; Änderungen werden als Stichprobe protokolliert (max. 20 Einträge)")
|
||||
s.updateActive(live)
|
||||
changesLogged := 0
|
||||
started := time.Now()
|
||||
lastLoggedPercent := -10
|
||||
summary, err := s.rsync.Run(ctx, run.ID, job, func(progress rsync.Progress) {
|
||||
@@ -1184,28 +1199,37 @@ func (s *Service) executeRsync(ctx context.Context, run model.Run) (result model
|
||||
}
|
||||
percent := int(progress.Percent)
|
||||
if percent >= lastLoggedPercent+10 {
|
||||
appendLiveLog(&live, fmt.Sprintf("Rsync-Fortschritt: %.0f%%, %s übertragen", progress.Percent, formatBytes(progress.Bytes)))
|
||||
if job.Rsync.DryRun {
|
||||
appendLiveLog(&live, fmt.Sprintf("Testlauf-Fortschritt: %.0f%% (keine Übertragung)", progress.Percent))
|
||||
} else {
|
||||
appendLiveLog(&live, fmt.Sprintf("Rsync-Fortschritt: %.0f%%, %s übertragen", progress.Percent, formatBytes(progress.Bytes)))
|
||||
}
|
||||
lastLoggedPercent = percent - percent%10
|
||||
}
|
||||
}
|
||||
if progress.CurrentFile != "" {
|
||||
live.CurrentFile = progress.CurrentFile
|
||||
if changesLogged < 20 {
|
||||
prefix := "Änderung: "
|
||||
if job.Rsync.DryRun {
|
||||
prefix = "Geplant: "
|
||||
}
|
||||
appendLiveLog(&live, prefix+rsyncChangeLabel(progress.Change)+" · "+progress.CurrentFile)
|
||||
} else if changesLogged == 20 {
|
||||
appendLiveLog(&live, "Weitere Einzeländerungen ausgeblendet; die Zusammenfassung zählt alle Dateien und Löschungen")
|
||||
}
|
||||
changesLogged++
|
||||
}
|
||||
s.updateActive(live)
|
||||
})
|
||||
if err != nil {
|
||||
appendLiveLog(&live, "Rsync-Kopie fehlgeschlagen: "+err.Error())
|
||||
failedRun := failedError(run, err)
|
||||
failedRun.LiveLog = live.LiveLog
|
||||
return failedRun
|
||||
return failedError(run, err)
|
||||
}
|
||||
run.Status, run.Message = "success", "rsync completed"
|
||||
run.BytesAdded, run.BytesProcessed = summary.Bytes, summary.Bytes
|
||||
run.FilesNew, run.FilesProcessed = summary.Files, summary.Files
|
||||
applyRsyncSummary(&run, summary)
|
||||
run.ProgressPercent, run.ProgressBytes, run.ProgressTotal = 100, summary.Bytes, summary.Bytes
|
||||
run.ProgressFiles, run.ProgressFileTotal = summary.Files, summary.Files
|
||||
run.BytesPerSecond, run.CurrentFile = live.BytesPerSecond, live.CurrentFile
|
||||
appendLiveLog(&live, fmt.Sprintf("Rsync-Kopie abgeschlossen: %d Dateien, %s", summary.Files, formatBytes(summary.Bytes)))
|
||||
logRsyncSummary(&live, summary)
|
||||
run.LiveLog = live.LiveLog
|
||||
return run
|
||||
}
|
||||
@@ -1447,6 +1471,7 @@ func diffKindLabel(kind string) string {
|
||||
|
||||
func (s *Service) updateActive(run model.Run) {
|
||||
s.queue.UpdateActive(run.ID, func(active *model.Run) {
|
||||
active.RsyncDryRun = run.RsyncDryRun
|
||||
active.ProgressPercent, active.ProgressBytes, active.ProgressTotal = run.ProgressPercent, run.ProgressBytes, run.ProgressTotal
|
||||
active.ProgressFiles, active.ProgressFileTotal = run.ProgressFiles, run.ProgressFileTotal
|
||||
active.BytesPerSecond, active.SecondsRemaining = run.BytesPerSecond, run.SecondsRemaining
|
||||
@@ -1592,6 +1617,9 @@ func notificationMessage(name, repository string, run model.Run, now time.Time)
|
||||
if status == "" {
|
||||
status = run.Status
|
||||
}
|
||||
if run.TaskType == "rsync" && run.RsyncDryRun && run.Status == "success" {
|
||||
status = "Testlauf erfolgreich – keine Änderungen vorgenommen"
|
||||
}
|
||||
summaryTitle := map[string]string{"backup": "📊 Backup-Zusammenfassung", "rsync": "📊 Rsync-Zusammenfassung", "restore": "📊 Restore-Zusammenfassung", "prune": "🧹 Prune-Zusammenfassung", "check": "🔎 Prüfungs-Zusammenfassung"}[run.TaskType]
|
||||
if summaryTitle == "" {
|
||||
summaryTitle = "📊 URBM-Zusammenfassung"
|
||||
@@ -1615,19 +1643,29 @@ func notificationMessage(name, repository string, run model.Run, now time.Time)
|
||||
lines = append(lines, "", "⏱️ Dauer: "+formatDuration(duration))
|
||||
}
|
||||
}
|
||||
if (run.TaskType == "backup" || run.TaskType == "rsync") && (run.Status == "success" || run.Status == "warning") {
|
||||
if run.TaskType == "backup" && (run.Status == "success" || run.Status == "warning") {
|
||||
lines = append(lines,
|
||||
fmt.Sprintf("📂 Dateien: %d verarbeitet", run.FilesProcessed),
|
||||
"💾 Verarbeitet: "+formatBytes(run.BytesProcessed),
|
||||
)
|
||||
if run.TaskType == "backup" {
|
||||
lines = append(lines,
|
||||
fmt.Sprintf("➕ Neu: %d Dateien", run.FilesNew),
|
||||
fmt.Sprintf("✏️ Geändert: %d Dateien", run.FilesChanged),
|
||||
"📤 Neu gespeichert: "+formatBytes(run.BytesAdded),
|
||||
)
|
||||
}
|
||||
if run.TaskType == "rsync" && (run.Status == "success" || run.Status == "warning") {
|
||||
if run.RsyncDryRun {
|
||||
lines = append(lines,
|
||||
fmt.Sprintf("➕ Neu: %d Dateien", run.FilesNew),
|
||||
fmt.Sprintf("✏️ Geändert: %d Dateien", run.FilesChanged),
|
||||
"📤 Neu gespeichert: "+formatBytes(run.BytesAdded),
|
||||
)
|
||||
"🧪 Testlauf – keine Änderungen vorgenommen",
|
||||
fmt.Sprintf("📂 Dateien würden kopiert/aktualisiert: %d", run.FilesProcessed),
|
||||
"💾 Geplanter Dateiinhalt: "+formatBytes(run.BytesProcessed),
|
||||
fmt.Sprintf("🗑️ Einträge würden gelöscht (inkl. Ordner/Symlinks): %d", run.EntriesDeleted))
|
||||
} else {
|
||||
lines = append(lines, "📤 Kopiert: "+formatBytes(run.BytesAdded))
|
||||
lines = append(lines,
|
||||
fmt.Sprintf("📂 Dateien kopiert/aktualisiert: %d", run.FilesProcessed),
|
||||
"📤 Kopierter Dateiinhalt: "+formatBytes(run.BytesAdded),
|
||||
fmt.Sprintf("🗑️ Einträge gelöscht (inkl. Ordner/Symlinks): %d", run.EntriesDeleted))
|
||||
}
|
||||
}
|
||||
if run.TaskType == "backup" && run.RetentionAfter > 0 {
|
||||
@@ -1646,7 +1684,7 @@ func notificationMessage(name, repository string, run model.Run, now time.Time)
|
||||
)
|
||||
}
|
||||
lines = append(lines, "", "🔐 Ziel: "+notificationTargetLabel(run.TaskType), statusIcon(run.Status)+" Status: "+status)
|
||||
if run.Message != "" && run.Message != run.TaskType+" completed" {
|
||||
if run.Message != "" && run.Message != run.TaskType+" completed" && !(run.RsyncDryRun && run.Status == "success") {
|
||||
lines = append(lines, "⚠️ Details: "+run.Message)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
|
||||
Reference in New Issue
Block a user