250 lines
8.4 KiB
PowerShell
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"
|
|
}
|