<# AZA - Step 14: Docker/Compose Smoke-Test (PowerShell 5.1, ASCII-safe) Build + start backend via docker compose, wait for /health 200, then run smoke_suite.ps1 against the container. Run (from project root OR from deploy folder): cd "C:\Users\surov\Documents\AZA\backup 24.2.26" powershell -ExecutionPolicy Bypass -File .\deploy\docker_smoke.ps1 Optional parameters: -BaseUrl http://127.0.0.1:8000 -WaitSeconds 90 -DownAfter (stop containers after test) #> [CmdletBinding()] param( [string]$BaseUrl = 'http://127.0.0.1:8000', [int]$WaitSeconds = 60, [switch]$DownAfter ) $ErrorActionPreference = 'Stop' function Write-Info([string]$msg) { Write-Host $msg } function Write-Fail([string]$msg) { Write-Host $msg; exit 1 } function Test-DockerServer() { try { $out = & docker version 2>&1 if ($LASTEXITCODE -ne 0) { return $false } if (($out -match 'Client:') -and ($out -match 'Server:')) { if ($out -match 'failed to connect to the docker API') { return $false } return $true } return $false } catch { return $false } } function Get-DockerContexts() { try { $lines = & docker context ls --format '{{.Name}}' 2>$null if ($LASTEXITCODE -ne 0) { return @() } $ctx = @() foreach ($l in $lines) { $n = ($l | ForEach-Object { $_.Trim() }) if ($n) { $ctx += $n } } return $ctx } catch { return @() } } function Ensure-DockerRunning([int]$timeoutSec = 180) { if (Test-DockerServer) { return } $contexts = Get-DockerContexts $preferred = @('desktop-linux','desktop-windows') foreach ($p in $preferred) { if ($contexts -contains $p) { cmd /c "docker context use $p >nul 2>nul" if (Test-DockerServer) { return } } } foreach ($c in $contexts) { cmd /c "docker context use $c >nul 2>nul" if (Test-DockerServer) { return } } $exeCandidates = @( "$env:ProgramFiles\Docker\Docker\Docker Desktop.exe", "${env:ProgramFiles(x86)}\Docker\Docker\Docker Desktop.exe" ) $exe = $exeCandidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 if ($exe) { try { Write-Info '[DOCKER] Starting Docker Desktop...' Start-Process -FilePath $exe | Out-Null } catch { } } $deadline = (Get-Date).AddSeconds($timeoutSec) while ((Get-Date) -lt $deadline) { $contexts = Get-DockerContexts foreach ($p in $preferred) { if ($contexts -contains $p) { cmd /c "docker context use $p >nul 2>nul" if (Test-DockerServer) { return } } } if (Test-DockerServer) { return } Start-Sleep -Seconds 2 } Write-Fail "[FAIL] Docker daemon not reachable after ${timeoutSec}s. Open Docker Desktop and wait until it shows 'Engine running', then rerun this script." } # Resolve project root robustly (works from root and from deploy/) $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path if ((Split-Path -Leaf $ScriptDir) -eq 'deploy') { $Root = Split-Path -Parent $ScriptDir } else { $Root = $ScriptDir } Set-Location -LiteralPath $Root $ComposeFile = Join-Path $Root 'deploy\docker-compose.yml' $EnvFile = Join-Path $Root 'deploy\.env' $SmokeScript = Join-Path $Root 'deploy\smoke_suite.ps1' Write-Host '[AZA] Step 14 - Docker/Compose Smoke' Write-Host (' Root: ' + $Root) Write-Host (' Compose: ' + $ComposeFile) Write-Host (' EnvFile: ' + $EnvFile) Write-Host (' BaseUrl: ' + $BaseUrl) Write-Host (' Timeout: ' + $WaitSeconds + 's') Write-Host '' # Pre-flight checks if (-not (Test-Path -LiteralPath $ComposeFile)) { Write-Host '[FAIL] Compose file not found.' exit 1 } if (-not (Test-Path -LiteralPath $EnvFile)) { Write-Host '[FAIL] .env file not found.' exit 1 } # Ensure docker daemon is reachable (auto-start + wait + context fix) Ensure-DockerRunning -timeoutSec 180 Write-Host '[OK] Docker daemon reachable.' # Step 1: Build and start Write-Host '[DOCKER] docker compose up -d --build' & docker compose -f $ComposeFile --env-file $EnvFile up -d --build if ($LASTEXITCODE -ne 0) { Write-Host '[FAIL] docker compose up --build failed.' exit 1 } Write-Host '[DOCKER] UP' Write-Host '' # Step 2: Wait for /health 200 $base = $BaseUrl.TrimEnd('/') $healthUrl = $base + '/health' Write-Host ('[WAIT] Waiting for ' + $healthUrl + ' (max ' + $WaitSeconds + 's)...') $healthy = $false for ($i = 0; $i -lt $WaitSeconds; $i++) { try { $resp = Invoke-WebRequest -Method GET -Uri $healthUrl -TimeoutSec 5 -UseBasicParsing if ([int]$resp.StatusCode -eq 200) { $healthy = $true break } } catch { # Not ready yet } Start-Sleep -Seconds 1 if (($i % 10) -eq 9) { Write-Host (' ... ' + ($i + 1) + 's elapsed') } } if (-not $healthy) { Write-Host '' Write-Host '[WAIT] TIMEOUT - backend did not become healthy.' Write-Host '' Write-Host 'Container logs (last 80 lines):' try { & docker compose -f $ComposeFile logs --tail 80 } catch { } Write-Host '' Write-Host 'Hint: containers are still running so you can inspect logs:' Write-Host (' docker compose -f "' + $ComposeFile + '" logs --follow') Write-Host (' docker compose -f "' + $ComposeFile + '" down') Write-Host '' Write-Host '[RESULT] FAIL' exit 1 } Write-Host '[WAIT] HEALTH OK' Write-Host '' # Step 3: Run smoke suite if (-not (Test-Path -LiteralPath $SmokeScript)) { Write-Host '[FAIL] smoke_suite.ps1 not found.' exit 1 } Write-Host '[SMOKE] Running smoke_suite.ps1 against docker...' & powershell -ExecutionPolicy Bypass -File $SmokeScript -BaseUrl $base -EnvFile $EnvFile $smokeExit = $LASTEXITCODE Write-Host '' # Optional: stop containers if ($DownAfter) { Write-Host '[DOCKER] DownAfter set - stopping containers...' try { & docker compose -f $ComposeFile down } catch { } Write-Host '[DOCKER] DOWN' Write-Host '' } if ($smokeExit -ne 0) { Write-Host '[SMOKE] FAIL' if (-not $DownAfter) { Write-Host '' Write-Host 'Hint: containers are still running for inspection:' Write-Host (' docker compose -f "' + $ComposeFile + '" logs --follow') Write-Host (' docker compose -f "' + $ComposeFile + '" down') } Write-Host '' Write-Host '[RESULT] FAIL' exit 1 } Write-Host '[SMOKE] PASS' Write-Host '' Write-Host '[RESULT] PASS - Step 14 Docker/Compose smoke test complete.' exit 0