Skip to content

Commit a4a24c4

Browse files
committed
Rotate logs and backup database on deployed gateways
Sets up a Windows schedule to backup databases and rotate logs every Sunday at 3am.
1 parent 9d3e46f commit a4a24c4

3 files changed

Lines changed: 201 additions & 1 deletion

File tree

scripts/bash/package_release.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ echo ""
6161

6262
# ── Validate required files ───────────────────────────────────────────────────
6363

64-
REQUIRED_FILES=("src/" "sample_images/" "scripts/python/" "pyproject.toml" "uv.lock" "README.md" "LICENCE.md")
64+
REQUIRED_FILES=("src/" "sample_images/" "scripts/python/" "scripts/powershell/maintenance.ps1" "pyproject.toml" "uv.lock" "README.md" "LICENCE.md")
6565

6666
for item in "${REQUIRED_FILES[@]}"; do
6767
if [[ ! -e "${REPO_ROOT}/${item}" ]]; then

scripts/powershell/deploy.ps1

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,39 @@ if (-not $cutoverFailed) {
560560
}
561561
}
562562

563+
# -- Maintenance schedule -------------------------------------------------------------
564+
565+
if (-not $cutoverFailed) {
566+
$maintenanceScriptPath = Join-Path $BaseInstallPath "current\scripts\powershell\maintenance.ps1"
567+
$backupAction = New-ScheduledTaskAction `
568+
-Execute "powershell.exe" `
569+
-Argument "-ExecutionPolicy Bypass -File `"$maintenanceScriptPath`" -Action StopBackupRotate"
570+
571+
$backupTrigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 3AM
572+
573+
Register-ScheduledTask `
574+
-TaskName "Gateway-Daily-Maintenance" `
575+
-Action $backupAction `
576+
-Trigger $backupTrigger `
577+
-User "SYSTEM" `
578+
-RunLevel Highest `
579+
-Force
580+
581+
$startAction = New-ScheduledTaskAction `
582+
-Execute "powershell.exe" `
583+
-Argument "-ExecutionPolicy Bypass -File `"$maintenanceScriptPath`" -Action StartServices"
584+
585+
$startTrigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 3:15AM
586+
587+
Register-ScheduledTask `
588+
-TaskName "Gateway-Daily-Service-Start" `
589+
-Action $startAction `
590+
-Trigger $startTrigger `
591+
-User "SYSTEM" `
592+
-RunLevel Highest `
593+
-Force
594+
}
595+
563596
# -- Rollback on Failure ------------------------------------------------------
564597

565598
if ($cutoverFailed) {

scripts/powershell/maintenance.ps1

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
param(
2+
[ValidateSet("StopBackupRotate","StartServices")]
3+
[string]$BaseInstallPath = "C:\Program Files\NHS\ManageBreastScreeningGateway",
4+
[string]$Action
5+
)
6+
7+
8+
# -- Service control helpers -------------------------------------------------------
9+
10+
function Stop-AllServices {
11+
param([array]$Services, [int]$TimeoutSeconds)
12+
foreach ($svc in $Services) {
13+
$status = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
14+
if (-not $status -or $status.Status -eq 'Stopped') { continue }
15+
16+
Write-Log "Stopping $($svc.Name) (timeout: ${TimeoutSeconds}s)..." "INFO"
17+
Stop-Service -Name $svc.Name -Force
18+
19+
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
20+
while ($stopwatch.Elapsed.TotalSeconds -lt $TimeoutSeconds) {
21+
$current = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
22+
if (-not $current -or $current.Status -eq 'Stopped') { break }
23+
Start-Sleep -Milliseconds 500
24+
}
25+
$stopwatch.Stop()
26+
27+
$finalStatus = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
28+
if ($finalStatus -and $finalStatus.Status -ne 'Stopped') {
29+
throw "Service $($svc.Name) did not stop within ${TimeoutSeconds}s (state: $($finalStatus.Status))."
30+
}
31+
Write-Log "$($svc.Name) stopped in $([math]::Round($stopwatch.Elapsed.TotalSeconds, 1))s." "INFO"
32+
}
33+
}
34+
35+
function Start-AllServices {
36+
param(
37+
[array]$Services, [int]$TimeoutSeconds
38+
)
39+
40+
foreach ($svc in $Services) {
41+
Write-Log "Starting $($svc.Name)..." "INFO"
42+
43+
Start-Service -Name $svc.Name
44+
45+
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
46+
while ($stopwatch.Elapsed.TotalSeconds -lt $TimeoutSeconds) {
47+
$status = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
48+
if ($status -and $status.Status -eq 'Running') { break }
49+
Start-Sleep -Milliseconds 500
50+
}
51+
52+
if ($status.Status -ne 'Running') {
53+
throw "Failed to start $($svc.Name)"
54+
}
55+
56+
Write-Log "$($svc.Name) started." "SUCCESS"
57+
}
58+
}
59+
60+
# -- Database backup ----------------------------------------------------------
61+
62+
function Invoke-DatabaseBackup {
63+
param(
64+
[string]$BaseInstallPath
65+
)
66+
67+
$batPath = Join-Path $versionDir "backup_database.bat"
68+
69+
if (-not (Test-Path $batPath)) {
70+
$batContent = @(
71+
'@echo off',
72+
"cd /d `"$BaseInstallPath`"",
73+
'set "PYTHONPATH=current\src"',
74+
('"current\.venv\Scripts\python.exe" "-c import scripts.python.database; scripts.python.database.backup_databases()"')
75+
) -join "`r`n"
76+
[System.IO.File]::WriteAllText($batPath, $batContent, [System.Text.Encoding]::ASCII)
77+
}
78+
79+
Write-Log "Starting database backup..." "INFO"
80+
81+
& cmd.exe /c "`"$batPath`""
82+
83+
if ($LASTEXITCODE -ne 0) {
84+
throw "Database backup failed with exit code $LASTEXITCODE"
85+
}
86+
87+
Write-Log "Database backup completed successfully." "SUCCESS"
88+
}
89+
90+
# -- Log Rotation -------------------------------------------------------------
91+
92+
function Rotate-LogFile {
93+
param(
94+
[Parameter(Mandatory)]
95+
[string]$LogFile,
96+
97+
[int]$RetainCount = 5
98+
)
99+
100+
if (-not (Test-Path $LogFile)) {
101+
return
102+
}
103+
104+
# Remove oldest
105+
$oldest = "$LogFile.$RetainCount"
106+
if (Test-Path $oldest) {
107+
Remove-Item $oldest -Force
108+
}
109+
110+
# Shift existing rotations
111+
for ($i = $RetainCount - 1; $i -ge 1; $i--) {
112+
$src = "$LogFile.$i"
113+
$dst = "$LogFile." + ($i + 1)
114+
115+
if (Test-Path $src) {
116+
Move-Item $src $dst -Force
117+
}
118+
}
119+
120+
Move-Item $LogFile "$LogFile.1" -Force
121+
}
122+
123+
function Rotate-ServiceLogs {
124+
param(
125+
[array]$Services,
126+
[string]$LogsDir
127+
)
128+
129+
foreach ($svc in $Services) {
130+
$logFile = Join-Path $LogsDir "$($svc.Name).log"
131+
Rotate-LogFile -LogFile $logFile -RetainCount 5
132+
}
133+
}
134+
135+
# -- Main ----------------------------------------------------------------------
136+
137+
$logsDir = Join-Path $BaseInstallPath "logs"
138+
139+
$services = Get-Service |
140+
Where-Object { $_.Name -like "Gateway-*" } |
141+
ForEach-Object {
142+
@{ Name = $_.Name }
143+
}
144+
145+
switch ($Action) {
146+
147+
"StopBackupRotate" {
148+
149+
Stop-AllServices `
150+
-Services $services `
151+
-TimeoutSeconds 30
152+
153+
Invoke-DatabaseBackup `
154+
-BaseInstallPath $BaseInstallPath
155+
156+
Rotate-ServiceLogs `
157+
-Services $services `
158+
-LogsDir $logsDir
159+
}
160+
161+
"StartServices" {
162+
163+
Start-AllServices `
164+
-Services $services
165+
-TimeoutSeconds 30
166+
}
167+
}

0 commit comments

Comments
 (0)