Files
homelab-infra/ops/restore-tests/check-restore-freshness.ps1

105 lines
3.5 KiB
PowerShell

param(
[string]$DumpRoot = "/mnt/user/backups/borg/dumps/latest",
[string]$ReportRoot = "/mnt/user/backups/restore-reports",
[int]$MaxDumpAgeHours = 26,
[int]$MaxReportAgeDays = 45
)
$checks = @(
@{ Name = "postgresql17-paperless.dump"; Path = Join-Path $DumpRoot "postgresql17-paperless.dump" },
@{ Name = "postgresql17-mailarchiver.dump"; Path = Join-Path $DumpRoot "postgresql17-mailarchiver.dump" },
@{ Name = "mealie.dump"; Path = Join-Path $DumpRoot "mealie.dump" },
@{ Name = "immich.dump"; Path = Join-Path $DumpRoot "immich.dump" },
@{ Name = "nextcloud.dump"; Path = Join-Path $DumpRoot "nextcloud.dump" },
@{ Name = "gitea.sqlite.dump"; Path = Join-Path $DumpRoot "gitea.sqlite.dump" },
@{ Name = "vaultwarden.sqlite.dump"; Path = Join-Path $DumpRoot "vaultwarden.sqlite.dump" },
@{ Name = "speedtest-tracker.sqlite.dump"; Path = Join-Path $DumpRoot "speedtest-tracker.sqlite.dump" },
@{ Name = "filebrowser.bolt.dump"; Path = Join-Path $DumpRoot "filebrowser.bolt.dump" },
@{ Name = "unraid-flash-config.tar.gz"; Path = Join-Path $DumpRoot "unraid-flash-config.tar.gz" }
)
$reportChecks = @(
@{ Name = "vaultwarden"; Path = Join-Path $ReportRoot "vaultwarden-*.md" },
@{ Name = "gitea"; Path = Join-Path $ReportRoot "gitea-*.md" },
@{ Name = "paperless"; Path = Join-Path $ReportRoot "paperless-*.md" }
)
$now = Get-Date
$critical = New-Object System.Collections.Generic.List[string]
$warnings = New-Object System.Collections.Generic.List[string]
$info = New-Object System.Collections.Generic.List[string]
foreach ($check in $checks) {
if (-not (Test-Path $check.Path)) {
$critical.Add("DUMP_MISSING $($check.Name)")
continue
}
$item = Get-Item $check.Path
if ($item.Length -le 0) {
$critical.Add("DUMP_EMPTY $($check.Name)")
continue
}
$ageHours = ($now - $item.LastWriteTime).TotalHours
if ($ageHours -gt $MaxDumpAgeHours) {
$critical.Add(("DUMP_STALE {0} age={1:N1}h" -f $check.Name, $ageHours))
} else {
$info.Add(("DUMP_OK {0} age={1:N1}h" -f $check.Name, $ageHours))
}
}
foreach ($check in $reportChecks) {
if (-not (Test-Path $ReportRoot)) {
$warnings.Add("REPORT_ROOT_MISSING $ReportRoot")
break
}
$latest = Get-ChildItem -Path $ReportRoot -Filter ([System.IO.Path]::GetFileName($check.Path)) -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if (-not $latest) {
$warnings.Add("REPORT_MISSING $($check.Name)")
continue
}
$ageDays = ($now - $latest.LastWriteTime).TotalDays
if ($ageDays -gt $MaxReportAgeDays) {
$warnings.Add(("REPORT_STALE {0} age={1:N1}d file={2}" -f $check.Name, $ageDays, $latest.Name))
} else {
$info.Add(("REPORT_OK {0} age={1:N1}d file={2}" -f $check.Name, $ageDays, $latest.Name))
}
}
Write-Output "# Restore Freshness Check"
Write-Output ""
Write-Output ("Timestamp: {0}" -f $now.ToString("yyyy-MM-dd HH:mm:ss"))
Write-Output ("Critical: {0}" -f $critical.Count)
Write-Output ("Warnings: {0}" -f $warnings.Count)
Write-Output ("Info: {0}" -f $info.Count)
Write-Output ""
if ($critical.Count -gt 0) {
Write-Output "## Critical"
$critical | ForEach-Object { Write-Output ("- {0}" -f $_) }
Write-Output ""
}
if ($warnings.Count -gt 0) {
Write-Output "## Warnings"
$warnings | ForEach-Object { Write-Output ("- {0}" -f $_) }
Write-Output ""
}
if ($info.Count -gt 0) {
Write-Output "## Info"
$info | ForEach-Object { Write-Output ("- {0}" -f $_) }
}
if ($critical.Count -gt 0) {
exit 1
}
exit 0