299 lines
10 KiB
PowerShell
299 lines
10 KiB
PowerShell
param(
|
|
[string]$RestDockerHost = "ssh://docker.cin.su",
|
|
[string]$McpDockerHost = "ssh://docker.cin.su",
|
|
[string]$RestComposePath = "core/deploy/docker/adapter-1c/compose.yaml",
|
|
[string]$McpComposePath = "core/deploy/docker/adapter-1c-mcp/compose.yaml",
|
|
[string]$RestEnvFile,
|
|
[string]$McpEnvFile,
|
|
[string]$RestServiceName = "adapter-1c-rest",
|
|
[string]$RestAuditServiceName = "adapter-1c-audit",
|
|
[string]$McpServiceName = "adapter-1c-mcp",
|
|
[string]$McpAuditServiceName = "adapter-1c-mcp-audit",
|
|
[string[]]$BaseId,
|
|
[string]$AdapterUrl = "http://docker.cin.su:8011",
|
|
[string]$McpUrl = "http://docker.cin.su:8021",
|
|
[string]$ObjectRef,
|
|
[string]$ObjectKind,
|
|
[string]$ObjectName,
|
|
[string]$ObjectGuid,
|
|
[ValidateSet("ConfigSave", "ConfigCASSave")]
|
|
[string]$SavedStateTable = "ConfigSave",
|
|
[int]$TimeoutSec = 120,
|
|
[switch]$NoBuild,
|
|
[switch]$SkipRest,
|
|
[switch]$SkipMcp,
|
|
[switch]$SkipVerify,
|
|
[switch]$SkipDrainCheck,
|
|
[switch]$SkipWritePlanSafetySmoke,
|
|
[switch]$SkipWriteRollbackSafetySmoke,
|
|
[switch]$SkipSavedStateDiffSmoke,
|
|
[switch]$SkipSavedStateWriteSmoke,
|
|
[switch]$SkipCodeWriteSavedStateSmoke,
|
|
[switch]$RequireSavedStateWriteSmoke,
|
|
[switch]$RequireSelectorChainWritePlanComposition
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Invoke-CheckedCommand {
|
|
param(
|
|
[string]$Label,
|
|
[string[]]$Command
|
|
)
|
|
Write-Host "[run] $Label"
|
|
$exe = $Command[0]
|
|
$args = $Command[1..($Command.Length - 1)]
|
|
Write-Host ("[cmd] {0} {1}" -f $exe, ($args -join " "))
|
|
& $exe @args
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "$Label failed with code=$LASTEXITCODE"
|
|
}
|
|
}
|
|
|
|
function Invoke-ComposeUp {
|
|
param(
|
|
[string]$Label,
|
|
[string]$DockerHost,
|
|
[string]$ComposePath,
|
|
[string]$EnvFile,
|
|
[string]$ServiceName
|
|
)
|
|
$command = @("docker", "--host", $DockerHost, "compose", "-f", $ComposePath, "up", "-d", "--no-deps")
|
|
if ($EnvFile) {
|
|
$command = @("docker", "--host", $DockerHost, "compose", "--env-file", $EnvFile, "-f", $ComposePath, "up", "-d", "--no-deps")
|
|
}
|
|
if (-not $NoBuild) {
|
|
$command += "--build"
|
|
}
|
|
$command += $ServiceName
|
|
Invoke-CheckedCommand -Label $Label -Command $command
|
|
}
|
|
|
|
function Wait-RestAdapterIdle {
|
|
if ($SkipDrainCheck) {
|
|
Write-Host "[skip] REST drain check was explicitly skipped"
|
|
return
|
|
}
|
|
$deadline = [DateTime]::UtcNow.AddSeconds($TimeoutSec)
|
|
$lastIssue = ""
|
|
while ([DateTime]::UtcNow -lt $deadline) {
|
|
try {
|
|
$health = Invoke-RestMethod -Method Get -Uri ($AdapterUrl.TrimEnd('/') + "/health") -TimeoutSec 10
|
|
$runtime = $health.runtime
|
|
if (-not $runtime) {
|
|
Write-Warning "REST adapter is a legacy image without runtime drain telemetry; proceeding with this one transition deployment"
|
|
return
|
|
}
|
|
if ($runtime -and $runtime.state -eq "ready" -and [int]$runtime.active_rpc_count -eq 0) {
|
|
Write-Host "[ready] REST adapter has no active RPC calls"
|
|
return
|
|
}
|
|
$lastIssue = "state=$($runtime.state) active_rpc_count=$($runtime.active_rpc_count)"
|
|
} catch {
|
|
$lastIssue = $_.Exception.Message
|
|
}
|
|
Start-Sleep -Seconds 2
|
|
}
|
|
throw "REST adapter did not become idle before deployment: $lastIssue. Re-run later or pass -SkipDrainCheck only after confirming no write is active."
|
|
}
|
|
|
|
function Ensure-RestServiceToken {
|
|
if ($env:ONEC_ADAPTER_SERVICE_TOKEN) {
|
|
return
|
|
}
|
|
if ($env:ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN -match '^(?i:true|1|yes|on)$') {
|
|
Write-Host "[security] Test profile: deploying without REST service token because ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN is enabled"
|
|
return
|
|
}
|
|
$inspectJson = & docker --host $RestDockerHost inspect $RestServiceName 2>$null
|
|
if ($LASTEXITCODE -eq 0 -and $inspectJson) {
|
|
$container = @($inspectJson | ConvertFrom-Json)[0]
|
|
$tokenEntry = @($container.Config.Env | Where-Object { $_ -like "ONEC_ADAPTER_SERVICE_TOKEN=*" })[0]
|
|
if ($tokenEntry) {
|
|
$existingToken = ($tokenEntry -split "=", 2)[1]
|
|
if ($existingToken) {
|
|
$env:ONEC_ADAPTER_SERVICE_TOKEN = $existingToken
|
|
Write-Host "[security] Reusing the existing REST service token without printing or persisting it"
|
|
return
|
|
}
|
|
}
|
|
}
|
|
throw "ONEC_ADAPTER_SERVICE_TOKEN is empty and no protected existing REST container token can be reused. Refusing to deploy an unauthenticated adapter."
|
|
}
|
|
|
|
function Write-ContainerSummary {
|
|
param(
|
|
[string]$Label,
|
|
[string]$DockerHost,
|
|
[string]$ServiceName
|
|
)
|
|
Write-Host "[status] $Label"
|
|
& docker --host $DockerHost ps --filter "name=$ServiceName" --format "name={{.Names}} image={{.Image}} status={{.Status}} ports={{.Ports}}"
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "$Label status failed with code=$LASTEXITCODE"
|
|
}
|
|
}
|
|
|
|
function Get-DuplicateValues {
|
|
param(
|
|
[string[]]$Values
|
|
)
|
|
$seen = @{}
|
|
$duplicates = New-Object System.Collections.Generic.HashSet[string]
|
|
foreach ($value in $Values) {
|
|
if ($seen.ContainsKey($value)) {
|
|
[void]$duplicates.Add($value)
|
|
} else {
|
|
$seen[$value] = $true
|
|
}
|
|
}
|
|
return @($duplicates | Sort-Object)
|
|
}
|
|
|
|
function Normalize-BaseIds {
|
|
param(
|
|
[string[]]$Values
|
|
)
|
|
$result = @()
|
|
foreach ($value in $Values) {
|
|
foreach ($part in ($value -split ",")) {
|
|
$trimmed = $part.Trim()
|
|
if ($trimmed) {
|
|
$result += $trimmed
|
|
}
|
|
}
|
|
}
|
|
return $result
|
|
}
|
|
|
|
try {
|
|
if (-not $SkipRest) {
|
|
if (-not $env:ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN) {
|
|
$env:ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN = "true"
|
|
}
|
|
Ensure-RestServiceToken
|
|
Wait-RestAdapterIdle
|
|
Invoke-ComposeUp `
|
|
-Label "Deploy REST adapter" `
|
|
-DockerHost $RestDockerHost `
|
|
-ComposePath $RestComposePath `
|
|
-EnvFile $RestEnvFile `
|
|
-ServiceName $RestServiceName
|
|
Write-ContainerSummary `
|
|
-Label "REST adapter container" `
|
|
-DockerHost $RestDockerHost `
|
|
-ServiceName $RestServiceName
|
|
Invoke-ComposeUp `
|
|
-Label "Deploy REST audit analyzer" `
|
|
-DockerHost $RestDockerHost `
|
|
-ComposePath $RestComposePath `
|
|
-EnvFile $RestEnvFile `
|
|
-ServiceName $RestAuditServiceName
|
|
Write-ContainerSummary `
|
|
-Label "REST audit analyzer container" `
|
|
-DockerHost $RestDockerHost `
|
|
-ServiceName $RestAuditServiceName
|
|
}
|
|
|
|
if (-not $SkipMcp) {
|
|
Invoke-ComposeUp `
|
|
-Label "Deploy MCP proxy" `
|
|
-DockerHost $McpDockerHost `
|
|
-ComposePath $McpComposePath `
|
|
-EnvFile $McpEnvFile `
|
|
-ServiceName $McpServiceName
|
|
Write-ContainerSummary `
|
|
-Label "MCP proxy container" `
|
|
-DockerHost $McpDockerHost `
|
|
-ServiceName $McpServiceName
|
|
Invoke-ComposeUp `
|
|
-Label "Deploy MCP audit analyzer" `
|
|
-DockerHost $McpDockerHost `
|
|
-ComposePath $McpComposePath `
|
|
-EnvFile $McpEnvFile `
|
|
-ServiceName $McpAuditServiceName
|
|
Write-ContainerSummary `
|
|
-Label "MCP audit analyzer container" `
|
|
-DockerHost $McpDockerHost `
|
|
-ServiceName $McpAuditServiceName
|
|
}
|
|
|
|
if (-not $SkipVerify) {
|
|
$baseIds = @(Normalize-BaseIds -Values $BaseId)
|
|
if (-not $baseIds) {
|
|
Write-Host "[skip] BaseId was not provided; live verification skipped"
|
|
} else {
|
|
$duplicateBaseIds = @(Get-DuplicateValues -Values $baseIds)
|
|
if ($duplicateBaseIds) {
|
|
throw "Duplicate BaseId value(s): $($duplicateBaseIds -join ', ')"
|
|
}
|
|
$verifyCommand = @(
|
|
"powershell",
|
|
"-NoProfile",
|
|
"-ExecutionPolicy",
|
|
"Bypass",
|
|
"-File",
|
|
"scripts/verify_1c_adapter_deployment.ps1",
|
|
"-BaseId"
|
|
)
|
|
$verifyCommand += ($baseIds -join ",")
|
|
$verifyCommand += @(
|
|
"-AdapterUrl",
|
|
$AdapterUrl,
|
|
"-McpUrl",
|
|
$McpUrl,
|
|
"-TimeoutSec",
|
|
$TimeoutSec.ToString(),
|
|
"-SavedStateTable",
|
|
$SavedStateTable
|
|
)
|
|
if ($ObjectRef) {
|
|
$verifyCommand += @("-ObjectRef", $ObjectRef)
|
|
}
|
|
if ($ObjectKind) {
|
|
$verifyCommand += @("-ObjectKind", $ObjectKind)
|
|
}
|
|
if ($ObjectName) {
|
|
$verifyCommand += @("-ObjectName", $ObjectName)
|
|
}
|
|
if ($ObjectGuid) {
|
|
$verifyCommand += @("-ObjectGuid", $ObjectGuid)
|
|
}
|
|
if ($SkipRest) {
|
|
$verifyCommand += "-SkipRest"
|
|
}
|
|
if ($SkipMcp) {
|
|
$verifyCommand += "-SkipMcp"
|
|
}
|
|
if ($SkipWritePlanSafetySmoke) {
|
|
$verifyCommand += "-SkipWritePlanSafetySmoke"
|
|
}
|
|
if ($SkipWriteRollbackSafetySmoke) {
|
|
$verifyCommand += "-SkipWriteRollbackSafetySmoke"
|
|
}
|
|
if ($SkipSavedStateDiffSmoke) {
|
|
$verifyCommand += "-SkipSavedStateDiffSmoke"
|
|
}
|
|
if ($SkipSavedStateWriteSmoke) {
|
|
$verifyCommand += "-SkipSavedStateWriteSmoke"
|
|
}
|
|
if ($SkipCodeWriteSavedStateSmoke) {
|
|
$verifyCommand += "-SkipCodeWriteSavedStateSmoke"
|
|
}
|
|
if ($RequireSavedStateWriteSmoke) {
|
|
$verifyCommand += "-RequireSavedStateWriteSmoke"
|
|
}
|
|
if ($RequireSelectorChainWritePlanComposition) {
|
|
$verifyCommand += "-RequireSelectorChainWritePlanComposition"
|
|
}
|
|
Invoke-CheckedCommand -Label "Verify 1C adapter deployment" -Command $verifyCommand
|
|
}
|
|
}
|
|
|
|
Write-Host "[done] 1C adapter stack deploy finished"
|
|
exit 0
|
|
} catch {
|
|
Write-Error $_
|
|
exit 1
|
|
}
|