116 lines
6.9 KiB
PowerShell
116 lines
6.9 KiB
PowerShell
param([string]$DockerContext = "docker-prod")
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Invoke-JsonPost([string]$container, [string]$url, [object]$body, [bool]$internal = $false) {
|
|
$json = $body | ConvertTo-Json -Depth 10 -Compress
|
|
$local = New-TemporaryFile
|
|
$remote = "/tmp/domovoy-$([guid]::NewGuid().ToString('N')).json"
|
|
try {
|
|
[IO.File]::WriteAllText($local, $json, [Text.UTF8Encoding]::new($false))
|
|
docker --context $DockerContext cp $local "${container}:$remote" | Out-Null
|
|
if ($internal) {
|
|
$response = docker --context $DockerContext exec -e "POST_FILE=$remote" -e "POST_URL=$url" $container sh -c 'wget -qO- --header="Content-Type: application/json" --header="Authorization: Bearer $COMMIT_EXECUTOR_TOKEN" --post-file "$POST_FILE" "$POST_URL"'
|
|
} else {
|
|
$response = docker --context $DockerContext exec $container wget -qO- --header "Content-Type: application/json" --post-file $remote $url
|
|
}
|
|
if ($LASTEXITCODE -ne 0) { throw "POST $url failed" }
|
|
return ($response -join "`n")
|
|
} finally {
|
|
docker --context $DockerContext exec --user root $container rm -f $remote 2>$null
|
|
Remove-Item -LiteralPath $local -Force
|
|
}
|
|
}
|
|
|
|
function Expect-InternalPostStatus([string]$container, [string]$url, [object]$body, [int]$status) {
|
|
$json = $body | ConvertTo-Json -Depth 10 -Compress
|
|
$local = New-TemporaryFile
|
|
$remote = "/tmp/domovoy-$([guid]::NewGuid().ToString('N')).json"
|
|
try {
|
|
[IO.File]::WriteAllText($local, $json, [Text.UTF8Encoding]::new($false))
|
|
docker --context $DockerContext cp $local "${container}:$remote" | Out-Null
|
|
$previous = $ErrorActionPreference
|
|
$ErrorActionPreference = "Continue"
|
|
$response = (docker --context $DockerContext exec -e "POST_FILE=$remote" -e "POST_URL=$url" $container sh -c 'wget -S -O /dev/null --header="Content-Type: application/json" --header="Authorization: Bearer $COMMIT_EXECUTOR_TOKEN" --post-file "$POST_FILE" "$POST_URL"' 2>&1) -join "`n"
|
|
$ErrorActionPreference = $previous
|
|
if ($response -notmatch "HTTP/1\.[01] $status") { throw "POST $url did not return $status" }
|
|
} finally {
|
|
docker --context $DockerContext exec --user root $container rm -f $remote 2>$null
|
|
Remove-Item -LiteralPath $local -Force
|
|
}
|
|
}
|
|
|
|
function Internal-Get([string]$container, [string]$url) {
|
|
$response = docker --context $DockerContext exec $container sh -c 'wget -qO- --header="Authorization: Bearer $COMMIT_EXECUTOR_TOKEN" "$1"' sh $url
|
|
if ($LASTEXITCODE -ne 0) { throw "GET $url failed" }
|
|
return ($response -join "`n")
|
|
}
|
|
|
|
$suffix = [guid]::NewGuid().ToString("N")
|
|
$scope = "domovoy-global-scope"
|
|
$goal = "goal-execution-$suffix"
|
|
$task = "task-execution-$suffix"
|
|
$prospective = "prospective-execution-$suffix"
|
|
$intent = "intent-execution-$suffix"
|
|
$authorized = "authorized-execution-$suffix"
|
|
$attempt = "attempt-execution-$suffix"
|
|
$evidence = "artifact://execution/$suffix"
|
|
|
|
Invoke-JsonPost assistant-core-runtime-1 http://localhost:8080/v1/tasks @{
|
|
TaskID=$task; GoalID=$goal; ScopeID=$scope; Revision=1
|
|
} | Out-Null
|
|
Invoke-JsonPost assistant-continuity-runtime-1 http://localhost:8081/v1/prospective-items @{
|
|
ID=$prospective; CommitmentID="commitment-$suffix"; OwnerPrincipalID="domovoy-creator-principal"
|
|
ScopeID=$scope; GoalID=$goal; TriggerAtUnixMillis=1800000000000; State="DELIVERED"; Revision=1
|
|
} | Out-Null
|
|
Invoke-JsonPost assistant-commit-runtime-1 http://localhost:8082/v1/authorized-actions @{
|
|
Proposal=@{
|
|
IntentID=$intent; ActorID="domovoy-creator-actor"; CapabilityID="continuity-transition"
|
|
Target=@{TaskID=$task;GoalID=$goal;ScopeID=$scope;Revision=1}; Operation="satisfy-delivered"
|
|
ContinuitySubject=@{ProspectiveItemID=$prospective;ExpectedRevision=1;FromState="DELIVERED";ToState="SATISFIED";EvidenceRef=$evidence}
|
|
}
|
|
AuthorizedID=$authorized;GrantID="domovoy-creator-grant";PrincipalID="domovoy-creator-principal"
|
|
} | Out-Null
|
|
|
|
# Prove the executable envelope is sufficient after process loss.
|
|
docker --context $DockerContext restart assistant-commit-runtime-1 | Out-Null
|
|
for ($index=0; $index -lt 15; $index++) {
|
|
docker --context $DockerContext exec assistant-commit-runtime-1 wget -qO- http://localhost:8082/healthz 2>$null | Out-Null
|
|
if ($LASTEXITCODE -eq 0) { break }
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
if ($LASTEXITCODE -ne 0) { throw "commit runtime did not recover" }
|
|
|
|
$attemptResult = Invoke-JsonPost assistant-commit-runtime-1 "http://localhost:8082/v1/internal/authorized-actions/$authorized/execute" @{AttemptID=$attempt} $true | ConvertFrom-Json
|
|
if ($attemptResult.State -ne "SUCCEEDED") { throw "execution did not succeed" }
|
|
$item = docker --context $DockerContext exec assistant-continuity-runtime-1 wget -qO- "http://localhost:8081/v1/prospective-items/$prospective" | ConvertFrom-Json
|
|
if ($item.State -ne "SATISFIED" -or $item.Revision -ne 2) { throw "ProspectiveItem did not transition exactly once" }
|
|
|
|
# Exact redelivery is answered from the owner receipt and cannot increment again.
|
|
$sameCommand = @{
|
|
ActionIntentID=$intent;AuthorizedActionID=$authorized;OwnerPrincipalID="domovoy-creator-principal"
|
|
Subject=@{ProspectiveItemID=$prospective;ExpectedRevision=1;FromState="DELIVERED";ToState="SATISFIED";EvidenceRef=$evidence}
|
|
}
|
|
$receipt = Invoke-JsonPost assistant-continuity-runtime-1 http://localhost:8081/v1/internal/executor/satisfy-delivered $sameCommand $true | ConvertFrom-Json
|
|
if ($receipt.ResultRevision -ne 2) { throw "idempotent receipt revision changed" }
|
|
$itemAfterReplay = docker --context $DockerContext exec assistant-continuity-runtime-1 wget -qO- "http://localhost:8081/v1/prospective-items/$prospective" | ConvertFrom-Json
|
|
if ($itemAfterReplay.Revision -ne 2) { throw "duplicate delivery changed canonical revision" }
|
|
Expect-InternalPostStatus assistant-commit-runtime-1 "http://localhost:8082/v1/internal/authorized-actions/$authorized/execute" @{AttemptID="duplicate-$suffix"} 409
|
|
|
|
# A new intent with a stale revision cannot mutate the already satisfied item.
|
|
$stale = @{
|
|
ActionIntentID="stale-$suffix";AuthorizedActionID="stale-authorized-$suffix";OwnerPrincipalID="domovoy-creator-principal"
|
|
Subject=@{ProspectiveItemID=$prospective;ExpectedRevision=1;FromState="DELIVERED";ToState="SATISFIED";EvidenceRef="artifact://stale/$suffix"}
|
|
}
|
|
Expect-InternalPostStatus assistant-continuity-runtime-1 http://localhost:8081/v1/internal/executor/satisfy-delivered $stale 409
|
|
|
|
docker --context $DockerContext restart assistant-continuity-runtime-1 | Out-Null
|
|
for ($index=0; $index -lt 15; $index++) {
|
|
$receiptAfterRestart = Internal-Get assistant-continuity-runtime-1 "http://localhost:8081/v1/internal/executor/receipts/$intent" 2>$null
|
|
if ($LASTEXITCODE -eq 0) { break }
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
if (($receiptAfterRestart | ConvertFrom-Json).ResultRevision -ne 2) { throw "receipt did not survive restart" }
|
|
|
|
Write-Output "Continuity Commit execution verification: PASS"
|