1、增加执行间隔

2、添加推送进度流程
This commit is contained in:
dark 2026-05-28 16:01:27 +08:00
parent 7b53d02e0e
commit f90bccd0ad
7 changed files with 645 additions and 99 deletions

View File

@ -56,7 +56,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
### 3.1 必填业务参数 ### 3.1 必填业务参数
| 规范字段 | 脚本字段 | 必填 | 说明 | | 规范字段 | 脚本字段 | 必填 | 说明 |
| --- | --- | --- | --- | | ----------------- | ---------------- | --- | ------------------ |
| `HOME_BASE_URL` | `HOME_BASE_URL` | 是 | PAM HOME 基础地址 | | `HOME_BASE_URL` | `HOME_BASE_URL` | 是 | PAM HOME 基础地址 |
| `client_id` | `CLIENT_ID` | 是 | OAuth 客户端 ID | | `client_id` | `CLIENT_ID` | 是 | OAuth 客户端 ID |
| `client_secret` | `CLIENT_SECRET` | 是 | OAuth 客户端密钥 | | `client_secret` | `CLIENT_SECRET` | 是 | OAuth 客户端密钥 |
@ -197,6 +197,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
15. 调用 `poll-download-progress`,直到下载完成、失败或超时。 15. 调用 `poll-download-progress`,直到下载完成、失败或超时。
16. 按在线 IP 或过滤后的目标 IP 列表逐台执行: 16. 按在线 IP 或过滤后的目标 IP 列表逐台执行:
- `upgrade-ip` - `upgrade-ip`
- `poll-upgrade-progress`
- `start-ip` - `start-ip`
- `verify-ip` - `verify-ip`
- `download-log` - `download-log`
@ -210,7 +211,8 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
2. 全局 action 与下一 action 之间,按 `stepIntervalSec` 等待。 2. 全局 action 与下一 action 之间,按 `stepIntervalSec` 等待。
3. `create-download-task` 成功后,到首次 `poll-download-progress` 前,按 `firstPollDelaySec` 等待。 3. `create-download-task` 成功后,到首次 `poll-download-progress` 前,按 `firstPollDelaySec` 等待。
4. 同一台 IP 内部: 4. 同一台 IP 内部:
- `upgrade-ip -> start-ip` - `upgrade-ip -> poll-upgrade-progress`
- `poll-upgrade-progress -> start-ip`
- `start-ip -> verify-ip` - `start-ip -> verify-ip`
- `verify-ip -> download-log` - `verify-ip -> download-log`
之间按 `perIpStepIntervalSec` 等待。 之间按 `perIpStepIntervalSec` 等待。
@ -323,7 +325,8 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
- 在线 IP 列表 - 在线 IP 列表
- 过滤后的目标 IP 列表 - 过滤后的目标 IP 列表
- 每台 IP 的执行状态: - 每台 IP 的执行状态:
- `upgrade` - `upgrade-ip`
- `poll-upgrade-progress`
- `start` - `start`
- `verify` - `verify`
- `download-log` - `download-log`
@ -412,7 +415,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
### 4.5 主流程逐步说明 ### 4.5 主流程逐步说明
| 步骤 | 目标 | 调用或动作 | 成功判定 | 失败处理 | | 步骤 | 目标 | 调用或动作 | 成功判定 | 失败处理 |
| --- | --- | --- | --- | --- | | ---- | ----------- | -------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------- |
| 1 | 识别意图 | 判断是否为真实部署 | 意图明确为真实部署 | 若不是,转入分支流程,不执行真实部署 | | 1 | 识别意图 | 判断是否为真实部署 | 意图明确为真实部署 | 若不是,转入分支流程,不执行真实部署 |
| 2 | 归一化参数 | 整理用户输入、补齐默认值 | 参数结构完整 | 参数不清时先追问,不猜测 | | 2 | 归一化参数 | 整理用户输入、补齐默认值 | 参数结构完整 | 参数不清时先追问,不猜测 |
| 3 | 参数确认 | 输出参数确认单 | 用户明确确认 | 未确认前停止 | | 3 | 参数确认 | 输出参数确认单 | 用户明确确认 | 未确认前停止 |
@ -428,10 +431,11 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
| 13 | 过滤目标 IP | 按用户指定 IP 与在线 IP 交集过滤 | 过滤结果明确 | 过滤后为空时停止;范围变化需确认 | | 13 | 过滤目标 IP | 按用户指定 IP 与在线 IP 交集过滤 | 过滤结果明确 | 过滤后为空时停止;范围变化需确认 |
| 14 | 创建云下载任务 | `create-download-task` | 返回 `RESULT=TASK_CREATED` | 停止并报告 `CREATE_DOWNLOAD_TASK` 失败 | | 14 | 创建云下载任务 | `create-download-task` | 返回 `RESULT=TASK_CREATED` | 停止并报告 `CREATE_DOWNLOAD_TASK` 失败 |
| 15 | 轮询下载进度 | `poll-download-progress` | `STEP=DONE``MSG=success``RATE_OF_PROGRESS=100` | 停止并报告 `POLL_DOWNLOAD_PROGRESS` 失败或超时 | | 15 | 轮询下载进度 | `poll-download-progress` | `STEP=DONE``MSG=success``RATE_OF_PROGRESS=100` | 停止并报告 `POLL_DOWNLOAD_PROGRESS` 失败或超时 |
| 16.1 | 升级单 IP | `upgrade-ip --ip ...` | 返回 `SUCCESS=true` | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=false)` | | 16.1 | 创建单 IP 推送任务 | `upgrade-ip --ip ...` | 返回 `RESULT=TASK_CREATED` | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=false)` |
| 16.2 | 启动单 IP | `start-ip --ip ...` | action 成功返回 | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=true)` | | 16.2 | 轮询单 IP 推送进度 | `poll-upgrade-progress --ip ...` | `STEP=DONE``FINISH=true``MSG=success``RATE_OF_PROGRESS=100` | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=false)` |
| 16.3 | 校验单 IP | `verify-ip --ip ...` | 返回 `SUCCESS=true` | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=true)` | | 16.3 | 启动单 IP | `start-ip --ip ...` | action 成功返回 | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=true)` |
| 16.4 | 下载日志 | `download-log --ip ...` | 返回 `LOG_FILE=...` | 记录日志下载失败,但不覆盖原主失败原因 | | 16.4 | 校验单 IP | `verify-ip --ip ...` | 返回 `SUCCESS=true` | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=true)` |
| 16.5 | 下载日志 | `download-log --ip ...` | 返回 `LOG_FILE=...` | 记录日志下载失败,但不覆盖原主失败原因 |
| 17 | 汇总结果 | 汇总每台 IP 的阶段、失败原因、回滚状态、日志路径 | 报告内容完整 | 若汇总失败,至少保留原始 action 输出 | | 17 | 汇总结果 | 汇总每台 IP 的阶段、失败原因、回滚状态、日志路径 | 报告内容完整 | 若汇总失败,至少保留原始 action 输出 |
| 18 | 回滚确认分支 | 发现 `PENDING_AGENT_CONFIRMATION(...)` 时进入回滚确认 | 用户明确是否回滚 | 未确认时停止,不自动回滚 | | 18 | 回滚确认分支 | 发现 `PENDING_AGENT_CONFIRMATION(...)` 时进入回滚确认 | 用户明确是否回滚 | 未确认时停止,不自动回滚 |
| 19 | 最终报告 | 输出最终报告 | 报告包含模式、入口、阶段结果、日志、回滚状态 | 不省略失败细节 | | 19 | 最终报告 | 输出最终报告 | 报告包含模式、入口、阶段结果、日志、回滚状态 | 不省略失败细节 |
@ -461,7 +465,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
### 6.1 Shell 入口 ### 6.1 Shell 入口
```bash ```bash
bash ./deploy.sh --config ./config.txt --action <action-name> [--ip 192.168.1.10] [--hash-code xxx] [--stop-first] bash ./deploy.sh --config ./config.txt --action <action-name> [--ip 192.168.1.10] [--hash-code xxx] [--stop-first] [--trace-file ./logs/api_trace_xxx.log]
``` ```
### 6.2 PowerShell 入口 ### 6.2 PowerShell 入口
@ -473,7 +477,7 @@ powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action <ActionName> [-Ip
### 6.3 可用 action ### 6.3 可用 action
| action | 用途 | 额外参数 | | action | 用途 | 额外参数 |
| --- | --- | --- | | ------------------------ | ------------------------------ | ------------------------------------------------------- |
| `get-token` | 获取访问令牌 | 无 | | `get-token` | 获取访问令牌 | 无 |
| `create-version` | 新建版本记录 | 无 | | `create-version` | 新建版本记录 | 无 |
| `upload-package` | 上传软件包 | 无 | | `upload-package` | 上传软件包 | 无 |
@ -483,7 +487,8 @@ powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action <ActionName> [-Ip
| `create-download-task` | 创建云下载任务 | 无 | | `create-download-task` | 创建云下载任务 | 无 |
| `poll-download-progress` | 轮询下载进度 | 无 | | `poll-download-progress` | 轮询下载进度 | 无 |
| `download-cloud-to-node` | 创建下载任务并轮询至完成,仅调试使用,不得进入正式主流程 | 无 | | `download-cloud-to-node` | 创建下载任务并轮询至完成,仅调试使用,不得进入正式主流程 | 无 |
| `upgrade-ip` | 升级指定 IP | `--ip` / `-Ip` | | `upgrade-ip` | 为指定 IP 创建推送任务,固定使用 `timeOut=0` | `--ip` / `-Ip` |
| `poll-upgrade-progress` | 轮询指定 IP 的推送进度 | `--ip` / `-Ip` |
| `start-ip` | 启动指定 IP 应用 | `--ip` / `-Ip` | | `start-ip` | 启动指定 IP 应用 | `--ip` / `-Ip` |
| `stop-ip` | 停止指定 IP 应用 | `--ip` / `-Ip` | | `stop-ip` | 停止指定 IP 应用 | `--ip` / `-Ip` |
| `verify-ip` | 校验指定 IP | `--ip` / `-Ip` | | `verify-ip` | 校验指定 IP | `--ip` / `-Ip` |

View File

@ -30,8 +30,9 @@
- 仅在用户明确要求时再提供 `deploy.bat` - 仅在用户明确要求时再提供 `deploy.bat`
7. NODE 侧接口路径统一使用 `node-proxy``download-cloud/progress` 需额外携带 `versionNumber`,并以异步轮询方式持续展示下载进度。 7. NODE 侧接口路径统一使用 `node-proxy``download-cloud/progress` 需额外携带 `versionNumber`,并以异步轮询方式持续展示下载进度。
8. `download-cloud/progress` 的完成判定优先读取 `msg``step``rateOfProgress`;当 `msg=success``step=DONE``rateOfProgress=100` 时代表下载完成,其中 `rateOfProgress` 即下载进度值。 8. `download-cloud/progress` 的完成判定优先读取 `msg``step``rateOfProgress`;当 `msg=success``step=DONE``rateOfProgress=100` 时代表下载完成,其中 `rateOfProgress` 即下载进度值。
9. 正式部署脚本不会自动执行回滚;发现需要回滚时,只输出 `PENDING_AGENT_CONFIRMATION(stopFirst=...)`,由 Agent 先和用户确认,再调用手动回滚入口。 9. `upgrade-ip` 固定传 `timeOut=0`,只负责创建推送任务;后续必须通过 `upgrade/progress` 异步轮询指定 IP 的推送进度。
10. `POST /api/mcp/version/upgrade``POST /api/mcp/version/upgrade/start-stop` 的业务参数都直接放在 URL query 中,不再使用 body 表单;启停接口参数名使用 `runStart``download-cloud` 固定传 `timeOut=0` 创建任务。 10. 正式部署脚本不会自动执行回滚;发现需要回滚时,只输出 `PENDING_AGENT_CONFIRMATION(stopFirst=...)`,由 Agent 先和用户确认,再调用手动回滚入口。
11. `POST /api/mcp/version/upgrade``POST /api/mcp/version/upgrade/start-stop` 的业务参数都直接放在 URL query 中,不再使用 body 表单;启停接口参数名使用 `runStart``download-cloud` 固定传 `timeOut=0` 创建任务。
11. 脚本同时提供主流程入口与 `action` 入口;建议 Agent 优先调用 `action` 入口,由 Skill 负责主流程编排。 11. 脚本同时提供主流程入口与 `action` 入口;建议 Agent 优先调用 `action` 入口,由 Skill 负责主流程编排。
12. 当前目录中的真实 `deploy.sh` 已去除 `jq` 依赖,统一使用 Bash 原生兼容 JSON 解析;若本文中的历史代码块仍出现 `jq`,以真实脚本文件为准。 12. 当前目录中的真实 `deploy.sh` 已去除 `jq` 依赖,统一使用 Bash 原生兼容 JSON 解析;若本文中的历史代码块仍出现 `jq`,以真实脚本文件为准。
@ -748,6 +749,7 @@ bash ./deploy.sh --config ./config.txt --action get-online-ips
bash ./deploy.sh --config ./config.txt --action create-download-task bash ./deploy.sh --config ./config.txt --action create-download-task
bash ./deploy.sh --config ./config.txt --action poll-download-progress bash ./deploy.sh --config ./config.txt --action poll-download-progress
bash ./deploy.sh --config ./config.txt --action upgrade-ip --ip 192.168.1.10 bash ./deploy.sh --config ./config.txt --action upgrade-ip --ip 192.168.1.10
bash ./deploy.sh --config ./config.txt --action poll-upgrade-progress --ip 192.168.1.10
``` ```
```powershell ```powershell

View File

@ -39,6 +39,18 @@ $script:DownloadProgressState = [ordered]@{
RateOfProgress = '' RateOfProgress = ''
RawResponse = '' RawResponse = ''
} }
$script:UpgradeProgressState = [ordered]@{
Status = ''
Success = ''
Step = ''
Msg = ''
Message = ''
RateOfProgress = ''
Code = ''
Finish = ''
LastModify = ''
RawResponse = ''
}
function Write-ResultLine([string]$Key, [AllowEmptyString()][string]$Value) { function Write-ResultLine([string]$Key, [AllowEmptyString()][string]$Value) {
if ($null -eq $Value) { if ($null -eq $Value) {
@ -78,6 +90,20 @@ function Write-DownloadProgressResult([string]$ActionName = 'poll-download-progr
Write-ResultLine -Key 'MESSAGE' -Value ([string]$script:DownloadProgressState.Message) Write-ResultLine -Key 'MESSAGE' -Value ([string]$script:DownloadProgressState.Message)
} }
function Write-UpgradeProgressResult([string]$Ip) {
Write-ResultLine -Key 'ACTION' -Value 'poll-upgrade-progress'
Write-ResultLine -Key 'IP' -Value $Ip
Write-ResultLine -Key 'STEP' -Value ([string]$script:UpgradeProgressState.Step)
Write-ResultLine -Key 'MSG' -Value ([string]$script:UpgradeProgressState.Msg)
Write-ResultLine -Key 'RATE_OF_PROGRESS' -Value ([string]$script:UpgradeProgressState.RateOfProgress)
Write-ResultLine -Key 'STATUS' -Value ([string]$script:UpgradeProgressState.Status)
Write-ResultLine -Key 'SUCCESS' -Value ([string]$script:UpgradeProgressState.Success)
Write-ResultLine -Key 'CODE' -Value ([string]$script:UpgradeProgressState.Code)
Write-ResultLine -Key 'FINISH' -Value ([string]$script:UpgradeProgressState.Finish)
Write-ResultLine -Key 'LAST_MODIFY' -Value ([string]$script:UpgradeProgressState.LastModify)
Write-ResultLine -Key 'MESSAGE' -Value ([string]$script:UpgradeProgressState.Message)
}
function Write-FlowStart([string]$Name, [string]$Detail = '') { function Write-FlowStart([string]$Name, [string]$Detail = '') {
if ($Detail) { if ($Detail) {
Write-Info "[FLOW][START] $Name | $Detail" Write-Info "[FLOW][START] $Name | $Detail"
@ -233,6 +259,78 @@ function Get-ResponseValue {
return $null return $null
} }
function Get-PrimaryResponseMessage {
param($Response)
$message = Get-ResponseValue -Response $Response -Candidates @('message')
if (-not $message) {
$message = Get-ResponseValue -Response $Response -Candidates @('msg')
}
return $message
}
function Test-ResponseFailure {
param($Response)
$successFlag = Get-ResponseValue -Response $Response -Candidates @('success')
$code = Get-ResponseValue -Response $Response -Candidates @('code')
$status = Get-ResponseValue -Response $Response -Candidates @('status')
$message = Get-PrimaryResponseMessage -Response $Response
if ($successFlag -eq 'false') {
return $true
}
if ($code -and $code -ne '0') {
return $true
}
if ((@($status, $message) -join ' ') -match '(?i)fail|error') {
return $true
}
return $false
}
function Get-ScopedResponseObject {
param(
$Response,
[string]$ScopeKey
)
if (-not $ScopeKey -or $null -eq $Response -or $Response -is [string]) {
return $Response
}
if ($Response -is [System.Collections.IDictionary]) {
if ($Response.Contains($ScopeKey)) {
return $Response[$ScopeKey]
}
return $Response
}
$property = $Response.PSObject.Properties | Where-Object { $_.Name -eq $ScopeKey } | Select-Object -First 1
if ($property) {
return $property.Value
}
return $Response
}
function Get-ProgressStateMessage {
param(
[System.Collections.IDictionary]$State,
[string]$DefaultMessage
)
$message = [string]$State.Message
if (-not $message) { $message = [string]$State.Msg }
if (-not $message) { $message = [string]$State.Step }
if (-not $message) { $message = $DefaultMessage }
return $message
}
function Get-PamConfig { function Get-PamConfig {
param([string]$Path) param([string]$Path)
@ -626,6 +724,109 @@ function Download-CloudToNode {
Wait-DownloadProgress -Config $Config -Token $Token -NodeUrl $NodeUrl Wait-DownloadProgress -Config $Config -Token $Token -NodeUrl $NodeUrl
} }
function Wait-UpgradeProgress {
param(
$Config,
[string]$Token,
[string]$NodeUrl,
[string]$Ip
)
$query = Join-RequestPairs ([ordered]@{
applicationName = $Config.APP_NAME
moduleName = $Config.MODULE_NAME
airportCode = $Config.AIRPORT_CODE
versionNumber = $Config.VERSION_NUMBER
})
$progressUrl = "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade/progress?$query"
$script:UpgradeProgressState = [ordered]@{
Status = ''
Success = ''
Step = ''
Msg = ''
Message = ''
RateOfProgress = ''
Code = ''
Finish = ''
LastModify = ''
RawResponse = ''
}
for ($attempt = 0; $attempt -lt 60; $attempt++) {
$response = Invoke-PamWebRequest -Method GET -Url $progressUrl -Token $Token -Headers @{
'Target-Node' = $NodeUrl
}
$progressResponse = Get-ScopedResponseObject -Response $response -ScopeKey $Ip
$status = Get-ResponseValue -Response $progressResponse -Candidates @('status')
$successFlag = Get-ResponseValue -Response $progressResponse -Candidates @('success')
$step = Get-ResponseValue -Response $progressResponse -Candidates @('step')
$msg = Get-ResponseValue -Response $progressResponse -Candidates @('msg')
$progressValue = Get-ResponseValue -Response $progressResponse -Candidates @('rateOfProgress', 'progress', 'percent', 'data.rateOfProgress', 'data.progress', 'data.percent')
$message = Get-ResponseValue -Response $progressResponse -Candidates @('message')
$code = Get-ResponseValue -Response $progressResponse -Candidates @('code')
$finish = Get-ResponseValue -Response $progressResponse -Candidates @('finish')
$lastModify = Get-ResponseValue -Response $progressResponse -Candidates @('lastModify')
if (-not $message) { $message = $msg }
$script:UpgradeProgressState = [ordered]@{
Status = [string]$status
Success = [string]$successFlag
Step = [string]$step
Msg = [string]$msg
Message = [string]$message
RateOfProgress = [string]$progressValue
Code = [string]$code
Finish = [string]$finish
LastModify = [string]$lastModify
RawResponse = [string]$response
}
$progressParts = [System.Collections.Generic.List[string]]::new()
$progressParts.Add("ip=$Ip")
if ($msg) { $progressParts.Add("msg=$msg") }
if ($step) { $progressParts.Add("step=$step") }
if ($progressValue) { $progressParts.Add("rateOfProgress=$progressValue") }
if ($code) { $progressParts.Add("code=$code") }
if ($finish) { $progressParts.Add("finish=$finish") }
if ($status) { $progressParts.Add("status=$status") }
if ($successFlag) { $progressParts.Add("success=$successFlag") }
if ($lastModify) { $progressParts.Add("lastModify=$lastModify") }
if ($message -and $message -ne $msg) { $progressParts.Add("message=$message") }
if ($progressParts.Count -gt 1) {
Write-Info ("Step 3.4a: async upgrade progress -> {0}" -f ($progressParts -join ', '))
} else {
Write-Info ("Step 3.4a: async upgrade progress polling... ip={0} ({1}/60)" -f $Ip, ($attempt + 1))
}
if ($step -eq 'DONE' -or $finish -eq 'true' -or $status -eq 'completed' -or $successFlag -eq 'true') {
return
}
if (($msg -eq 'success') -and ($progressValue -eq '100') -and ((-not $code) -or $code -eq '0')) {
return
}
if ($code -and $code -ne '0') {
if (-not $message) { $message = $msg }
if (-not $message) { $message = $step }
if (-not $message) { $message = "code=$code" }
throw "Node upgrade failed: ip=$Ip, message=$message"
}
if ((@($step, $message, $msg, $status) -join ' ') -match '(?i)fail|error') {
if (-not $message) { $message = $step }
if (-not $message) { $message = $msg }
throw "Node upgrade failed: ip=$Ip, message=$message"
}
Start-Sleep -Seconds 2
}
throw "Node upgrade timed out: ip=$Ip"
}
function Invoke-UpgradeRequest { function Invoke-UpgradeRequest {
param($Config, [string]$Token, [string]$NodeUrl, [string]$Ip) param($Config, [string]$Token, [string]$NodeUrl, [string]$Ip)
@ -637,7 +838,7 @@ function Invoke-UpgradeRequest {
versionNumber = $Config.VERSION_NUMBER versionNumber = $Config.VERSION_NUMBER
action = $Config.ACTION_TYPE action = $Config.ACTION_TYPE
autoStart = 'false' autoStart = 'false'
timeOut = $Config.TIMEOUT timeOut = '0'
}) })
Invoke-PamWebRequest -Method POST -Url "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade?$query" -Token $Token -Headers @{ Invoke-PamWebRequest -Method POST -Url "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade?$query" -Token $Token -Headers @{
@ -802,9 +1003,9 @@ function Invoke-IpDeploy {
} }
} }
if ((Get-ResponseValue -Response $upgrade -Candidates @('success')) -ne 'true') { if (Test-ResponseFailure -Response $upgrade) {
$message = Get-ResponseValue -Response $upgrade -Candidates @('message') $message = Get-PrimaryResponseMessage -Response $upgrade
if (-not $message) { $message = 'Upgrade failed' } if (-not $message) { $message = 'Upgrade task creation failed' }
$rollback = Get-PendingRollbackStatus -Ip $Ip -Stage 'UPGRADE' -StopFirst:$false -Reason $message $rollback = Get-PendingRollbackStatus -Ip $Ip -Stage 'UPGRADE' -StopFirst:$false -Reason $message
$logFile = Invoke-FlowStep -Name "Download-DeployLog[$Ip]" -Action { $logFile = Invoke-FlowStep -Name "Download-DeployLog[$Ip]" -Action {
Download-DeployLog -Config $Config -Token $Token -NodeUrl $NodeUrl -Ip $Ip Download-DeployLog -Config $Config -Token $Token -NodeUrl $NodeUrl -Ip $Ip
@ -819,6 +1020,26 @@ function Invoke-IpDeploy {
} }
} }
try {
Invoke-FlowStep -Name "Wait-UpgradeProgress[$Ip]" -Action {
Wait-UpgradeProgress -Config $Config -Token $Token -NodeUrl $NodeUrl -Ip $Ip
} | Out-Null
} catch {
$message = Get-ProgressStateMessage -State $script:UpgradeProgressState -DefaultMessage 'Upgrade progress polling failed'
$rollback = Get-PendingRollbackStatus -Ip $Ip -Stage 'UPGRADE_PROGRESS' -StopFirst:$false -Reason $message
$logFile = Invoke-FlowStep -Name "Download-DeployLog[$Ip]" -Action {
Download-DeployLog -Config $Config -Token $Token -NodeUrl $NodeUrl -Ip $Ip
}
return [pscustomobject]@{
Ip = $Ip
Status = 'FAILED'
Stage = 'UPGRADE_PROGRESS'
Message = $message
Rollback = $rollback
LogFile = $logFile
}
}
try { try {
Invoke-FlowStep -Name "Start-Application[$Ip]" -Action { Invoke-FlowStep -Name "Start-Application[$Ip]" -Action {
Start-Application -Config $Config -Token $Token -NodeUrl $NodeUrl -Ip $Ip Start-Application -Config $Config -Token $Token -NodeUrl $NodeUrl -Ip $Ip
@ -1062,15 +1283,29 @@ function Invoke-PamAction {
Write-DownloadProgressResult -ActionName 'download-cloud-to-node' Write-DownloadProgressResult -ActionName 'download-cloud-to-node'
Write-ResultLine -Key 'RESULT' -Value 'DONE' Write-ResultLine -Key 'RESULT' -Value 'DONE'
} }
'poll-upgrade-progress' {
Require-IpArgument -TargetIp $Ip
$token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config }
$nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token }
Invoke-FlowStep -Name "Wait-UpgradeProgress[$Ip]" -Action { Wait-UpgradeProgress -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip } | Out-Null
Write-UpgradeProgressResult -Ip $Ip
}
'upgrade-ip' { 'upgrade-ip' {
Require-IpArgument -TargetIp $Ip Require-IpArgument -TargetIp $Ip
$token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config }
$nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token }
$response = Invoke-FlowStep -Name "Invoke-UpgradeRequest[$Ip]" -Action { Invoke-UpgradeRequest -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip } $response = Invoke-FlowStep -Name "Invoke-UpgradeRequest[$Ip]" -Action { Invoke-UpgradeRequest -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip }
if (Test-ResponseFailure -Response $response) {
$message = Get-PrimaryResponseMessage -Response $response
if (-not $message) { $message = 'Upgrade task creation failed' }
throw "Upgrade task creation failed: ip=$Ip, message=$message"
}
Write-ResultLine -Key 'ACTION' -Value 'upgrade-ip' Write-ResultLine -Key 'ACTION' -Value 'upgrade-ip'
Write-ResultLine -Key 'IP' -Value $Ip Write-ResultLine -Key 'IP' -Value $Ip
Write-ResultLine -Key 'TIME_OUT' -Value '0'
Write-ResultLine -Key 'RESULT' -Value 'TASK_CREATED'
Write-ResultLine -Key 'SUCCESS' -Value (Get-ResponseValue -Response $response -Candidates @('success')) Write-ResultLine -Key 'SUCCESS' -Value (Get-ResponseValue -Response $response -Candidates @('success'))
Write-ResultLine -Key 'MESSAGE' -Value (Get-ResponseValue -Response $response -Candidates @('message')) Write-ResultLine -Key 'MESSAGE' -Value (Get-PrimaryResponseMessage -Response $response)
Write-ResultLine -Key 'RAW_RESPONSE' -Value ([string]$response) Write-ResultLine -Key 'RAW_RESPONSE' -Value ([string]$response)
} }
'start-ip' { 'start-ip' {

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# PAM 部署主脚本Shell 入口)。 # PAM deployment main script (Shell entry).
set -uo pipefail set -uo pipefail
@ -27,6 +27,16 @@ DOWNLOAD_PROGRESS_MSG=""
DOWNLOAD_PROGRESS_MESSAGE="" DOWNLOAD_PROGRESS_MESSAGE=""
DOWNLOAD_PROGRESS_RATE="" DOWNLOAD_PROGRESS_RATE=""
DOWNLOAD_PROGRESS_RESPONSE="" DOWNLOAD_PROGRESS_RESPONSE=""
UPGRADE_PROGRESS_STATUS=""
UPGRADE_PROGRESS_SUCCESS=""
UPGRADE_PROGRESS_STEP=""
UPGRADE_PROGRESS_MSG=""
UPGRADE_PROGRESS_MESSAGE=""
UPGRADE_PROGRESS_RATE=""
UPGRADE_PROGRESS_CODE=""
UPGRADE_PROGRESS_FINISH=""
UPGRADE_PROGRESS_LAST_MODIFY=""
UPGRADE_PROGRESS_RESPONSE=""
usage() { usage() {
cat <<'EOF' cat <<'EOF'
@ -437,6 +447,21 @@ print_progress_result() {
result_line "MESSAGE" "$DOWNLOAD_PROGRESS_MESSAGE" result_line "MESSAGE" "$DOWNLOAD_PROGRESS_MESSAGE"
} }
print_upgrade_progress_result() {
local ip="$1"
result_line "ACTION" "poll-upgrade-progress"
result_line "IP" "$ip"
result_line "STEP" "$UPGRADE_PROGRESS_STEP"
result_line "MSG" "$UPGRADE_PROGRESS_MSG"
result_line "RATE_OF_PROGRESS" "$UPGRADE_PROGRESS_RATE"
result_line "STATUS" "$UPGRADE_PROGRESS_STATUS"
result_line "SUCCESS" "$UPGRADE_PROGRESS_SUCCESS"
result_line "CODE" "$UPGRADE_PROGRESS_CODE"
result_line "FINISH" "$UPGRADE_PROGRESS_FINISH"
result_line "LAST_MODIFY" "$UPGRADE_PROGRESS_LAST_MODIFY"
result_line "MESSAGE" "$UPGRADE_PROGRESS_MESSAGE"
}
json_value() { json_value() {
local input="$1" local input="$1"
local query="$2" local query="$2"
@ -454,6 +479,15 @@ json_value() {
'.message') '.message')
json_get_string_by_key "$input" "message" json_get_string_by_key "$input" "message"
;; ;;
'.code')
json_get_scalar_by_key "$input" "code"
;;
'.finish')
json_get_scalar_by_key "$input" "finish"
;;
'.lastModify')
json_get_scalar_by_key "$input" "lastModify"
;;
'.msg') '.msg')
json_get_string_by_key "$input" "msg" json_get_string_by_key "$input" "msg"
;; ;;
@ -503,6 +537,11 @@ json_compact() {
printf '%s' "$input" printf '%s' "$input"
} }
regex_escape_ere() {
local text="$1"
printf '%s' "$text" | sed -E 's/[][(){}.^$*+?|\\]/\\&/g'
}
json_unescape_basic() { json_unescape_basic() {
local value="$1" local value="$1"
value="${value//\\\\/\\}" value="${value//\\\\/\\}"
@ -518,9 +557,11 @@ json_get_string_by_key() {
local key="$2" local key="$2"
local compact local compact
local value local value
local pattern_key
compact="$(json_compact "$input")" compact="$(json_compact "$input")"
value="$(printf '%s' "$compact" | sed -nE 's/.*"'$key'"[[:space:]]*:[[:space:]]*"(([^"\\]|\\.)*)".*/\1/p')" pattern_key="$(regex_escape_ere "$key")"
value="$(printf '%s' "$compact" | sed -nE 's/.*"'$pattern_key'"[[:space:]]*:[[:space:]]*"(([^"\\]|\\.)*)".*/\1/p')"
[[ -n "$value" ]] && json_unescape_basic "$value" [[ -n "$value" ]] && json_unescape_basic "$value"
} }
@ -529,9 +570,11 @@ json_get_scalar_by_key() {
local key="$2" local key="$2"
local compact local compact
local value local value
local pattern_key
compact="$(json_compact "$input")" compact="$(json_compact "$input")"
value="$(printf '%s' "$compact" | sed -nE 's/.*"'$key'"[[:space:]]*:[[:space:]]*([^,}]+).*/\1/p')" pattern_key="$(regex_escape_ere "$key")"
value="$(printf '%s' "$compact" | sed -nE 's/.*"'$pattern_key'"[[:space:]]*:[[:space:]]*([^,}]+).*/\1/p')"
value="$(trim "$value")" value="$(trim "$value")"
value="${value%\"}" value="${value%\"}"
value="${value#\"}" value="${value#\"}"
@ -544,12 +587,35 @@ json_get_nested_string_by_key() {
local child_key="$3" local child_key="$3"
local compact local compact
local value local value
local pattern_parent_key
local pattern_child_key
compact="$(json_compact "$input")" compact="$(json_compact "$input")"
value="$(printf '%s' "$compact" | sed -nE 's/.*"'$parent_key'"[[:space:]]*:[[:space:]]*\{[^}]*"'$child_key'"[[:space:]]*:[[:space:]]*"(([^"\\]|\\.)*)".*/\1/p')" pattern_parent_key="$(regex_escape_ere "$parent_key")"
pattern_child_key="$(regex_escape_ere "$child_key")"
value="$(printf '%s' "$compact" | sed -nE 's/.*"'$pattern_parent_key'"[[:space:]]*:[[:space:]]*\{[^}]*"'$pattern_child_key'"[[:space:]]*:[[:space:]]*"(([^"\\]|\\.)*)".*/\1/p')"
[[ -n "$value" ]] && json_unescape_basic "$value" [[ -n "$value" ]] && json_unescape_basic "$value"
} }
json_get_nested_scalar_by_key() {
local input="$1"
local parent_key="$2"
local child_key="$3"
local compact
local value
local pattern_parent_key
local pattern_child_key
compact="$(json_compact "$input")"
pattern_parent_key="$(regex_escape_ere "$parent_key")"
pattern_child_key="$(regex_escape_ere "$child_key")"
value="$(printf '%s' "$compact" | sed -nE 's/.*"'$pattern_parent_key'"[[:space:]]*:[[:space:]]*\{[^}]*"'$pattern_child_key'"[[:space:]]*:[[:space:]]*([^,}]+).*/\1/p')"
value="$(trim "$value")"
value="${value%\"}"
value="${value#\"}"
printf '%s' "$value"
}
json_first_key() { json_first_key() {
local input="$1" local input="$1"
@ -572,6 +638,94 @@ json_array_items() {
| sed -E 's/^[[:space:]]*"//; s/"[[:space:]]*$//; s/"[[:space:]]*,[[:space:]]*"/\n/g' | sed -E 's/^[[:space:]]*"//; s/"[[:space:]]*$//; s/"[[:space:]]*,[[:space:]]*"/\n/g'
} }
json_scoped_value() {
local input="$1"
local scope_key="$2"
local query="$3"
local value=""
if [[ -n "$scope_key" ]]; then
case "$query" in
'.status')
value="$(json_get_nested_string_by_key "$input" "$scope_key" "status")"
;;
'.success')
value="$(json_get_nested_scalar_by_key "$input" "$scope_key" "success")"
;;
'.message')
value="$(json_get_nested_string_by_key "$input" "$scope_key" "message")"
;;
'.msg')
value="$(json_get_nested_string_by_key "$input" "$scope_key" "msg")"
;;
'.step')
value="$(json_get_nested_string_by_key "$input" "$scope_key" "step")"
;;
'.rateOfProgress')
value="$(json_get_nested_scalar_by_key "$input" "$scope_key" "rateOfProgress")"
;;
'.progress')
value="$(json_get_nested_scalar_by_key "$input" "$scope_key" "progress")"
;;
'.percent')
value="$(json_get_nested_scalar_by_key "$input" "$scope_key" "percent")"
;;
'.code')
value="$(json_get_nested_scalar_by_key "$input" "$scope_key" "code")"
;;
'.finish')
value="$(json_get_nested_scalar_by_key "$input" "$scope_key" "finish")"
;;
'.lastModify')
value="$(json_get_nested_scalar_by_key "$input" "$scope_key" "lastModify")"
;;
esac
fi
if [[ -n "$value" ]]; then
printf '%s' "$value"
else
json_value "$input" "$query"
fi
}
response_message() {
local input="$1"
local message
message="$(json_value "$input" '.message')"
[[ -z "$message" ]] && message="$(json_value "$input" '.msg')"
printf '%s' "$message"
}
response_indicates_failure() {
local input="$1"
local success_flag
local code_value
local status
local message
local error_regex='[Ff]ail|[Ee]rror'
success_flag="$(json_value "$input" '.success')"
code_value="$(json_value "$input" '.code')"
status="$(json_value "$input" '.status')"
message="$(response_message "$input")"
if [[ "$success_flag" == "false" ]]; then
return 0
fi
if [[ -n "$code_value" && "$code_value" != "0" ]]; then
return 0
fi
if [[ "${status} ${message}" =~ $error_regex ]]; then
return 0
fi
return 1
}
http_request() { http_request() {
local method="$1" local method="$1"
local url="$2" local url="$2"
@ -899,10 +1053,113 @@ download_cloud_to_node() {
poll_download_progress poll_download_progress
} }
poll_upgrade_progress() {
local ip="$1"
local progress_url="${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade/progress?applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&airportCode=${AIRPORT_CODE}&versionNumber=${VERSION_NUMBER}"
local attempt=0
local max_attempts=600
local error_regex='[Ff]ail|[Ee]rror'
UPGRADE_PROGRESS_STATUS=""
UPGRADE_PROGRESS_SUCCESS=""
UPGRADE_PROGRESS_STEP=""
UPGRADE_PROGRESS_MSG=""
UPGRADE_PROGRESS_MESSAGE=""
UPGRADE_PROGRESS_RATE=""
UPGRADE_PROGRESS_CODE=""
UPGRADE_PROGRESS_FINISH=""
UPGRADE_PROGRESS_LAST_MODIFY=""
UPGRADE_PROGRESS_RESPONSE=""
while (( attempt < max_attempts )); do
local response
response=$(http_request "GET" "$progress_url" "" "" "Target-Node: ${NODE_URL}") || return 1
local status
status="$(json_scoped_value "$response" "$ip" '.status')"
local success_flag
success_flag="$(json_scoped_value "$response" "$ip" '.success')"
local step_value
step_value="$(json_scoped_value "$response" "$ip" '.step')"
local msg_value
msg_value="$(json_scoped_value "$response" "$ip" '.msg')"
local message
message="$(json_scoped_value "$response" "$ip" '.message')"
local progress_value
progress_value="$(json_scoped_value "$response" "$ip" '.rateOfProgress')"
[[ -z "$progress_value" ]] && progress_value="$(json_scoped_value "$response" "$ip" '.progress')"
[[ -z "$progress_value" ]] && progress_value="$(json_scoped_value "$response" "$ip" '.percent')"
local code_value
code_value="$(json_scoped_value "$response" "$ip" '.code')"
local finish_value
finish_value="$(json_scoped_value "$response" "$ip" '.finish')"
local last_modify_value
last_modify_value="$(json_scoped_value "$response" "$ip" '.lastModify')"
[[ -z "$message" ]] && message="$msg_value"
UPGRADE_PROGRESS_STATUS="$status"
UPGRADE_PROGRESS_SUCCESS="$success_flag"
UPGRADE_PROGRESS_STEP="$step_value"
UPGRADE_PROGRESS_MSG="$msg_value"
UPGRADE_PROGRESS_MESSAGE="$message"
UPGRADE_PROGRESS_RATE="$progress_value"
UPGRADE_PROGRESS_CODE="$code_value"
UPGRADE_PROGRESS_FINISH="$finish_value"
UPGRADE_PROGRESS_LAST_MODIFY="$last_modify_value"
UPGRADE_PROGRESS_RESPONSE="$response"
if [[ -n "$msg_value" || -n "$step_value" || -n "$progress_value" || -n "$status" || -n "$success_flag" || -n "$message" || -n "$code_value" || -n "$finish_value" || -n "$last_modify_value" ]]; then
local -a progress_parts=()
progress_parts+=("ip=${ip}")
[[ -n "$msg_value" ]] && progress_parts+=("msg=${msg_value}")
[[ -n "$step_value" ]] && progress_parts+=("step=${step_value}")
[[ -n "$progress_value" ]] && progress_parts+=("rateOfProgress=${progress_value}")
[[ -n "$code_value" ]] && progress_parts+=("code=${code_value}")
[[ -n "$finish_value" ]] && progress_parts+=("finish=${finish_value}")
[[ -n "$status" ]] && progress_parts+=("status=${status}")
[[ -n "$success_flag" ]] && progress_parts+=("success=${success_flag}")
[[ -n "$last_modify_value" ]] && progress_parts+=("lastModify=${last_modify_value}")
[[ -n "$message" && "$message" != "$msg_value" ]] && progress_parts+=("message=${message}")
log_info "Step 3.4a: async push progress -> ${progress_parts[*]}"
else
log_info "Step 3.4a: async push progress polling... ip=${ip} ($((attempt + 1))/${max_attempts})"
fi
if [[ "$step_value" == "DONE" || "$finish_value" == "true" || "$status" == "completed" || "$success_flag" == "true" ]]; then
return 0
fi
if [[ "$msg_value" == "success" && "$progress_value" == "100" ]] && [[ -z "$code_value" || "$code_value" == "0" ]]; then
return 0
fi
if [[ -n "$code_value" && "$code_value" != "0" ]]; then
[[ -z "$message" ]] && message="$msg_value"
[[ -z "$message" ]] && message="$step_value"
[[ -z "$message" ]] && message="code=${code_value}"
log_error "Node push failed: ip=${ip}, message=${message}"
return 1
fi
if [[ "${step_value} ${message} ${msg_value} ${status}" =~ $error_regex ]]; then
[[ -z "$message" ]] && message="$step_value"
[[ -z "$message" ]] && message="$msg_value"
log_error "Node push failed: ip=${ip}, message=${message}"
return 1
fi
attempt=$((attempt + 1))
sleep 2
done
log_error "Node push timed out: ip=${ip}"
return 1
}
upgrade_ip() { upgrade_ip() {
local ip="$1" local ip="$1"
http_request "POST" \ http_request "POST" \
"${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade?airportCode=${AIRPORT_CODE}&targetIp=${ip}&applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&versionNumber=${VERSION_NUMBER}&action=${ACTION_TYPE}&autoStart=false&timeOut=${TIMEOUT}" \ "${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade?airportCode=${AIRPORT_CODE}&targetIp=${ip}&applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&versionNumber=${VERSION_NUMBER}&action=${ACTION_TYPE}&autoStart=false&timeOut=0" \
"" \ "" \
"" \ "" \
"Target-Node: ${NODE_URL}" "Target-Node: ${NODE_URL}"
@ -1130,15 +1387,31 @@ run_action() {
print_progress_result "download-cloud-to-node" print_progress_result "download-cloud-to-node"
result_line "RESULT" "DONE" result_line "RESULT" "DONE"
;; ;;
poll-upgrade-progress)
require_ip_arg "$ip" || return 1
run_flow_step "get_token" get_token || return 1
run_flow_step "get_node_url" get_node_url || return 1
run_flow_step "poll_upgrade_progress[${ip}]" poll_upgrade_progress "$ip" || return 1
print_upgrade_progress_result "$ip"
;;
upgrade-ip) upgrade-ip)
require_ip_arg "$ip" || return 1 require_ip_arg "$ip" || return 1
run_flow_step "get_token" get_token || return 1 run_flow_step "get_token" get_token || return 1
run_flow_step "get_node_url" get_node_url || return 1 run_flow_step "get_node_url" get_node_url || return 1
run_flow_capture response "upgrade_ip[${ip}]" upgrade_ip "$ip" || return 1 run_flow_capture response "upgrade_ip[${ip}]" upgrade_ip "$ip" || return 1
if response_indicates_failure "$response"; then
local message
message="$(response_message "$response")"
[[ -z "$message" ]] && message="Upgrade task creation failed"
log_error "Upgrade task creation failed: ip=${ip}, message=${message}"
return 1
fi
result_line "ACTION" "upgrade-ip" result_line "ACTION" "upgrade-ip"
result_line "IP" "$ip" result_line "IP" "$ip"
result_line "TIME_OUT" "0"
result_line "RESULT" "TASK_CREATED"
result_line "SUCCESS" "$(json_value "$response" '.success')" result_line "SUCCESS" "$(json_value "$response" '.success')"
result_line "MESSAGE" "$(json_value "$response" '.message')" result_line "MESSAGE" "$(response_message "$response")"
result_line "RAW_RESPONSE" "$(sanitize_field "$response")" result_line "RAW_RESPONSE" "$(sanitize_field "$response")"
;; ;;
start-ip) start-ip)
@ -1237,10 +1510,10 @@ deploy_one_ip() {
return return
fi fi
if [[ "$(json_value "$upgrade_response" '.success')" != "true" ]]; then if response_indicates_failure "$upgrade_response"; then
local message local message
message="$(json_value "$upgrade_response" '.message')" message="$(response_message "$upgrade_response")"
[[ -z "$message" ]] && message="Upgrade failed" [[ -z "$message" ]] && message="Upgrade task creation failed"
local rollback_result local rollback_result
rollback_result="$(pending_rollback_status "$ip" "UPGRADE" "$message" "false")" rollback_result="$(pending_rollback_status "$ip" "UPGRADE" "$message" "false")"
local log_file="" local log_file=""
@ -1249,6 +1522,20 @@ deploy_one_ip() {
return return
fi fi
if ! run_flow_step "poll_upgrade_progress[${ip}]" poll_upgrade_progress "$ip"; then
local message
message="$UPGRADE_PROGRESS_MESSAGE"
[[ -z "$message" ]] && message="$UPGRADE_PROGRESS_MSG"
[[ -z "$message" ]] && message="$UPGRADE_PROGRESS_STEP"
[[ -z "$message" ]] && message="Upgrade progress polling failed"
local rollback_result
rollback_result="$(pending_rollback_status "$ip" "UPGRADE_PROGRESS" "$message" "false")"
local log_file=""
run_flow_capture log_file "download_log[${ip}]" download_log "$ip" || true
add_result "$ip" "FAILED" "UPGRADE_PROGRESS" "$message" "$rollback_result" "$log_file"
return
fi
if ! run_flow_step "start_application[${ip}]" start_application "$ip"; then if ! run_flow_step "start_application[${ip}]" start_application "$ip"; then
local rollback_result local rollback_result
rollback_result="$(pending_rollback_status "$ip" "START" "Application start failed" "true")" rollback_result="$(pending_rollback_status "$ip" "START" "Application start failed" "true")"

View File

@ -203,13 +203,19 @@ function Invoke-PamFullTest {
foreach ($ip in $testIps) { foreach ($ip in $testIps) {
try { try {
$upgradeResponse = Invoke-UpgradeRequest -Config $config -Token $token -NodeUrl $nodeUrl -Ip $ip $upgradeResponse = Invoke-UpgradeRequest -Config $config -Token $token -NodeUrl $nodeUrl -Ip $ip
$upgradeSuccess = Get-ResponseValue -Response $upgradeResponse -Candidates @('success') if (Test-ResponseFailure -Response $upgradeResponse) {
$upgradeMessage = Get-ResponseValue -Response $upgradeResponse -Candidates @('message') $upgradeMessage = Get-PrimaryResponseMessage -Response $upgradeResponse
if ($upgradeSuccess -eq 'true') { if (-not $upgradeMessage) { $upgradeMessage = 'task creation failed' }
Add-TestResult -Results $results -Step "UPGRADE [$ip]" -Status 'PASS' -Detail 'success=true'
} else {
if (-not $upgradeMessage) { $upgradeMessage = 'success != true' }
Add-TestResult -Results $results -Step "UPGRADE [$ip]" -Status 'FAIL' -Detail $upgradeMessage Add-TestResult -Results $results -Step "UPGRADE [$ip]" -Status 'FAIL' -Detail $upgradeMessage
} else {
Add-TestResult -Results $results -Step "UPGRADE [$ip]" -Status 'PASS' -Detail 'task created'
try {
Wait-UpgradeProgress -Config $config -Token $token -NodeUrl $nodeUrl -Ip $ip
Add-TestResult -Results $results -Step "UPGRADE_PROGRESS [$ip]" -Status 'PASS' -Detail ("rateOfProgress={0}" -f $script:UpgradeProgressState.RateOfProgress)
} catch {
$upgradeMessage = Get-ProgressStateMessage -State $script:UpgradeProgressState -DefaultMessage 'progress polling failed'
Add-TestResult -Results $results -Step "UPGRADE_PROGRESS [$ip]" -Status 'FAIL' -Detail $upgradeMessage
}
} }
} catch { } catch {
Add-TestResult -Results $results -Step "UPGRADE [$ip]" -Status 'FAIL' -Detail $_.Exception.Message Add-TestResult -Results $results -Step "UPGRADE [$ip]" -Status 'FAIL' -Detail $_.Exception.Message

View File

@ -207,13 +207,20 @@ run_full_test() {
local rollback_result="" local rollback_result=""
if upgrade_response="$(upgrade_ip "$ip")"; then if upgrade_response="$(upgrade_ip "$ip")"; then
upgrade_success="$(json_value "$upgrade_response" '.success')" if response_indicates_failure "$upgrade_response"; then
upgrade_message="$(json_value "$upgrade_response" '.message')" upgrade_message="$(response_message "$upgrade_response")"
if [[ "$upgrade_success" == "true" ]]; then [[ -z "$upgrade_message" ]] && upgrade_message="task creation failed"
add_test_result "UPGRADE [$ip]" "PASS" "success=true"
else
[[ -z "$upgrade_message" ]] && upgrade_message="success != true"
add_test_result "UPGRADE [$ip]" "FAIL" "$upgrade_message" add_test_result "UPGRADE [$ip]" "FAIL" "$upgrade_message"
else
add_test_result "UPGRADE [$ip]" "PASS" "task created"
if poll_upgrade_progress "$ip"; then
add_test_result "UPGRADE_PROGRESS [$ip]" "PASS" "rateOfProgress=${UPGRADE_PROGRESS_RATE}"
else
upgrade_message="$UPGRADE_PROGRESS_MESSAGE"
[[ -z "$upgrade_message" ]] && upgrade_message="$UPGRADE_PROGRESS_MSG"
[[ -z "$upgrade_message" ]] && upgrade_message="progress polling failed"
add_test_result "UPGRADE_PROGRESS [$ip]" "FAIL" "$upgrade_message"
fi
fi fi
else else
add_test_result "UPGRADE [$ip]" "FAIL" "request failed" add_test_result "UPGRADE [$ip]" "FAIL" "request failed"

View File

@ -52,6 +52,7 @@
- `/api/mcp/version/upgrade` 使用 query 参数,不再使用 body 表单。 - `/api/mcp/version/upgrade` 使用 query 参数,不再使用 body 表单。
- `/api/mcp/version/upgrade/start-stop` 使用 query 参数,不再使用 body 表单,且参数名使用 `runStart` - `/api/mcp/version/upgrade/start-stop` 使用 query 参数,不再使用 body 表单,且参数名使用 `runStart`
- `download-cloud` 固定传 `timeOut=0`,仅用于创建下载任务;后续进度通过 `download-cloud/progress` 异步查询。 - `download-cloud` 固定传 `timeOut=0`,仅用于创建下载任务;后续进度通过 `download-cloud/progress` 异步查询。
- `upgrade-ip` 固定传 `timeOut=0`,仅用于创建推送任务;后续进度通过 `upgrade/progress` 异步查询,对外新增 `poll-upgrade-progress` action。
- Shell 侧 action 调用现在支持复用统一的 trace 文件,不再要求每次 action 各自生成单独的 trace 日志。 - Shell 侧 action 调用现在支持复用统一的 trace 文件,不再要求每次 action 各自生成单独的 trace 日志。
- `log-download` 下载结果按 zip 压缩包保存,不再按纯文本日志处理。 - `log-download` 下载结果按 zip 压缩包保存,不再按纯文本日志处理。
@ -75,10 +76,11 @@
7. `api/mcp/version/upgrade/download-cloud` 7. `api/mcp/version/upgrade/download-cloud`
8. `api/mcp/version/upgrade/download-cloud/progress` 8. `api/mcp/version/upgrade/download-cloud/progress`
9. `api/mcp/version/upgrade` 9. `api/mcp/version/upgrade`
10. `api/mcp/version/upgrade/start-stop` 10. `api/mcp/version/upgrade/progress`
11. `api/mcp/version/upgrade/verify` 11. `api/mcp/version/upgrade/start-stop`
12. `api/mcp/version/upgrade/log-download` 12. `api/mcp/version/upgrade/verify`
13. `api/mcp/version/upgrade/rollback` 13. `api/mcp/version/upgrade/log-download`
14. `api/mcp/version/upgrade/rollback`
正式部署脚本当前不会自动执行 `rollback`,而是输出待确认状态;需要实际回滚时,应由 Agent 与用户确认后,再调用手动回滚入口: 正式部署脚本当前不会自动执行 `rollback`,而是输出待确认状态;需要实际回滚时,应由 Agent 与用户确认后,再调用手动回滚入口:
@ -95,11 +97,13 @@ powershell -File .\deploy.ps1 -ConfigPath .\config.txt -RollbackIp 192.168.1.10
```bash ```bash
bash ./deploy.sh --config ./config.txt --action get-online-ips bash ./deploy.sh --config ./config.txt --action get-online-ips
bash ./deploy.sh --config ./config.txt --action upgrade-ip --ip 192.168.1.10 bash ./deploy.sh --config ./config.txt --action upgrade-ip --ip 192.168.1.10
bash ./deploy.sh --config ./config.txt --action poll-upgrade-progress --ip 192.168.1.10
``` ```
```powershell ```powershell
powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action GetOnlineIps powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action GetOnlineIps
powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action UpgradeIp -Ip 192.168.1.10 powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action UpgradeIp -Ip 192.168.1.10
powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action poll-upgrade-progress -Ip 192.168.1.10
``` ```
## 3. 当前运行方式 ## 3. 当前运行方式