Files
call-center/scripts/track9_2_cutover.ps1

250 lines
8.4 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[string]$KubeContext,
[Parameter(Mandatory = $true)]
[string]$Namespace,
[Parameter(Mandatory = $true)]
[string]$Release,
[Parameter(Mandatory = $true)]
[string]$GatewayBaseUrl,
[Parameter(Mandatory = $true)]
[string]$DatabaseUrl,
[Parameter(Mandatory = $true)]
[string]$ImageTag,
[Parameter(Mandatory = $true)]
[string]$AmiHost,
[Parameter(Mandatory = $true)]
[string]$AmiUser,
[Parameter(Mandatory = $true)]
[string]$AmiSecret,
[Parameter(Mandatory = $true)]
[string]$SftpHost,
[Parameter(Mandatory = $true)]
[string]$SftpUser,
[Parameter(Mandatory = $true)]
[string]$SftpPassword,
[Parameter(Mandatory = $true)]
[string]$QueueId,
[Parameter(Mandatory = $true)]
[string]$AppTokenSecret,
[string]$ScaleValuesPath = "deployment/helm/values.scale500.yaml",
[string]$StrictValuesPath = "deployment/helm/values.track9-strict.yaml",
[string]$EvidenceOutDir = "",
[switch]$Execute,
[switch]$ForceUpgrade,
[switch]$SkipChecks
)
$ErrorActionPreference = "Stop"
$root = Split-Path -Parent $PSScriptRoot
Set-Location $root
function Run-Checked {
param(
[Parameter(Mandatory = $true)]
[string]$Label,
[Parameter(Mandatory = $true)]
[string[]]$Args
)
Write-Host ""
Write-Host "==> $Label"
Write-Host " $($Args -join ' ')"
$command = $Args[0]
$arguments = @()
if ($Args.Length -gt 1) {
$arguments = $Args[1..($Args.Length - 1)]
}
& $command @arguments
if ($LASTEXITCODE -ne 0) {
throw "$Label failed with exit code $LASTEXITCODE"
}
}
function Ensure-File {
param([string]$PathValue)
if (-not (Test-Path $PathValue)) {
throw "Required file not found: $PathValue"
}
}
Ensure-File -PathValue $ScaleValuesPath
Ensure-File -PathValue $StrictValuesPath
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$artifactsDir = Join-Path $root ".artifacts/track9_2/$timestamp"
New-Item -ItemType Directory -Path $artifactsDir -Force | Out-Null
$helmTemplateOut = Join-Path $artifactsDir "helm_template.yaml"
$cutoverValuesFile = Join-Path $artifactsDir "cutover.override.yaml"
$podsSnapshotOut = Join-Path $artifactsDir "pods_before.txt"
$historyBeforeOut = Join-Path $artifactsDir "helm_history_before.txt"
$historyAfterOut = Join-Path $artifactsDir "helm_history_after.txt"
$cutoverSummaryOut = Join-Path $artifactsDir "cutover-summary.txt"
function Escape-YamlSingleQuoted {
param([string]$Value)
return ($Value -replace "'", "''")
}
$queueMapJson = '{"voice_lab":"' + $QueueId + '"}'
$overrideYaml = @"
namespace: '$(Escape-YamlSingleQuoted $Namespace)'
image:
tag: '$(Escape-YamlSingleQuoted $ImageTag)'
auth:
appTokenSecret: '$(Escape-YamlSingleQuoted $AppTokenSecret)'
asteriskBridge:
enabled: "1"
amiHost: '$(Escape-YamlSingleQuoted $AmiHost)'
amiUsername: '$(Escape-YamlSingleQuoted $AmiUser)'
amiSecret: '$(Escape-YamlSingleQuoted $AmiSecret)'
queueMapJson: '$(Escape-YamlSingleQuoted $queueMapJson)'
sftpHost: '$(Escape-YamlSingleQuoted $SftpHost)'
sftpUsername: '$(Escape-YamlSingleQuoted $SftpUser)'
sftpPassword: '$(Escape-YamlSingleQuoted $SftpPassword)'
"@
$overrideYaml | Out-File -FilePath $cutoverValuesFile -Encoding utf8
Run-Checked -Label "Switch kube context" -Args @("kubectl", "config", "use-context", $KubeContext)
Run-Checked -Label "Helm lint" -Args @("helm", "lint", "deployment/helm")
Write-Host ""
Write-Host "==> Helm history (before)"
Write-Host " helm -n $Namespace history $Release"
& helm -n $Namespace history $Release | Out-File -FilePath $historyBeforeOut -Encoding utf8
if ($LASTEXITCODE -ne 0) {
"release not found (this can be valid for first install)" | Out-File -FilePath $historyBeforeOut -Encoding utf8
Write-Warning "Release '$Release' not found in namespace '$Namespace'. Continuing with first-install flow."
}
Write-Host ""
Write-Host "==> Pods snapshot (before)"
Write-Host " kubectl -n $Namespace get pods -o wide"
& kubectl -n $Namespace get pods -o wide | Out-File -FilePath $podsSnapshotOut -Encoding utf8
if ($LASTEXITCODE -ne 0) {
"namespace missing or no pods yet (pre-install state)" | Out-File -FilePath $podsSnapshotOut -Encoding utf8
Write-Warning "Could not fetch pods for namespace '$Namespace'. Continuing."
}
Write-Host ""
Write-Host "==> Helm template render"
Write-Host " helm template $Release deployment/helm -n $Namespace -f $ScaleValuesPath -f $StrictValuesPath -f $cutoverValuesFile"
& helm template $Release deployment/helm -n $Namespace `
-f $ScaleValuesPath `
-f $StrictValuesPath `
-f $cutoverValuesFile | Out-File -FilePath $helmTemplateOut -Encoding utf8
if ($LASTEXITCODE -ne 0) {
throw "Helm template render failed with exit code $LASTEXITCODE"
}
$requiredStrictKeys = @(
"ALLOW_LEGACY_HEADER_AUTH"
"ASTERISK_BRIDGE_AUTH_MODE"
"ASTERISK_BRIDGE_AUTH_FALLBACK_LEGACY"
"VOICE_ADAPTER_TRUSTED_SERVICE_SUBJECTS"
"RECORDING_IMPORT_TRUSTED_SERVICE_SUBJECTS"
"RECORDING_IMPORT_ALLOW_ADMIN"
)
foreach ($key in $requiredStrictKeys) {
if (-not (Select-String -Path $helmTemplateOut -Pattern $key -SimpleMatch)) {
throw "Rendered manifest does not contain expected strict key: $key"
}
}
if (-not $Execute) {
@(
"Track 9.2 cutover dry-run completed."
"Artifacts: $artifactsDir"
"Rendered manifest: $helmTemplateOut"
"To execute deploy, rerun with -Execute"
) | Out-File -FilePath $cutoverSummaryOut -Encoding utf8
Write-Host ""
Write-Host "Dry-run completed."
Write-Host "Artifacts: $artifactsDir"
Write-Host "Run again with -Execute to apply Helm upgrade."
exit 0
}
$helmUpgradeArgs = @(
"helm", "upgrade", "--install", $Release, "deployment/helm", "-n", $Namespace,
"-f", $ScaleValuesPath,
"-f", $StrictValuesPath,
"-f", $cutoverValuesFile
)
if ($ForceUpgrade) {
$helmUpgradeArgs += "--force"
}
Run-Checked -Label "Helm upgrade/install" -Args $helmUpgradeArgs
Run-Checked -Label "Rollout status: api-gateway" -Args @("kubectl", "-n", $Namespace, "rollout", "status", "deploy/api-gateway")
Run-Checked -Label "Rollout status: asterisk-bridge-service" -Args @("kubectl", "-n", $Namespace, "rollout", "status", "deploy/asterisk-bridge-service")
Run-Checked -Label "Rollout status: voice-adapter-service" -Args @("kubectl", "-n", $Namespace, "rollout", "status", "deploy/voice-adapter-service")
Run-Checked -Label "Rollout status: recording-service" -Args @("kubectl", "-n", $Namespace, "rollout", "status", "deploy/recording-service")
Run-Checked -Label "Helm history (after)" -Args @("helm", "-n", $Namespace, "history", $Release)
& helm -n $Namespace history $Release | Out-File -FilePath $historyAfterOut -Encoding utf8
if (-not $SkipChecks) {
Run-Checked -Label "Track9 preflight strict" -Args @(
"python", "scripts/track9_preflight.py",
"--base-url", $GatewayBaseUrl,
"--check-sftp",
"--require-strict-service-auth"
)
Run-Checked -Label "Asterisk lab smoke" -Args @(
"python", "scripts/asterisk_lab_smoke.py",
"--base-url", $GatewayBaseUrl,
"--database-url", $DatabaseUrl,
"--require-recording"
)
Run-Checked -Label "Track9 check" -Args @(
"python", "scripts/track9_check.py",
"--base-url", $GatewayBaseUrl,
"--database-url", $DatabaseUrl,
"--require-recording"
)
}
$evidenceArgs = @(
"python", "scripts/track9_collect_evidence.py",
"--base-url", $GatewayBaseUrl,
"--database-url", $DatabaseUrl,
"--run-checks",
"--require-recording"
)
if ($EvidenceOutDir.Trim()) {
$evidenceArgs += @("--out-dir", $EvidenceOutDir)
}
Run-Checked -Label "Collect Track9 evidence" -Args $evidenceArgs
$historyLines = Get-Content $historyAfterOut
$deployedLine = $historyLines | Select-String -Pattern "deployed" | Select-Object -Last 1
$rollbackHint = ""
if ($deployedLine) {
$parts = ($deployedLine.ToString() -split "\s+", [System.StringSplitOptions]::RemoveEmptyEntries)
if ($parts.Length -ge 1) {
$currentRevision = $parts[0]
$previousRevision = [int]$currentRevision - 1
if ($previousRevision -ge 1) {
$rollbackHint = "helm -n $Namespace rollback $Release $previousRevision"
}
}
}
@(
"Track 9.2 cutover execution completed."
"Artifacts: $artifactsDir"
"Gateway: $GatewayBaseUrl"
"Database: $DatabaseUrl"
if ($rollbackHint) { "Rollback command: $rollbackHint" } else { "Rollback command: check helm history manually." }
) | Out-File -FilePath $cutoverSummaryOut -Encoding utf8
Write-Host ""
Write-Host "Track 9.2 cutover completed."
Write-Host "Artifacts: $artifactsDir"
if ($rollbackHint) {
Write-Host "Rollback hint: $rollbackHint"
}