param( [string]$BaseUrl = "http://192.168.200.61:18121/api/v1", [string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa", [string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700", [string]$EntryNodeName = "test-1", [string]$ExitNodeName = "test-2", [string]$ResultPath = "artifacts\c18z-service-channel-load-smoke-result.json" ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath $agentRoot = Join-Path $repoRoot "agents\rap-node-agent" $runId = "c18z-" + (Get-Date -Format "yyyyMMdd-HHmmss") $testPattern = "TestFabricClientPacketIngressBoundedLoad(RebuildsAwayFromWithdrawnRoute|ReportsPerChannelDrops)" function Invoke-ApiJson { param( [string]$Method = "GET", [string]$Path, [object]$Body = $null ) $uri = "$BaseUrl$Path" if ($null -eq $Body) { return Invoke-RestMethod -Method $Method -Uri $uri } $json = $Body | ConvertTo-Json -Depth 20 return Invoke-RestMethod -Method $Method -Uri $uri -ContentType "application/json" -Body $json } function New-SmokeRouteIntent { param( [string]$Label, [int]$Priority ) $expiresAt = (Get-Date).ToUniversalTime().AddMinutes(10).ToString("o") return Invoke-ApiJson -Method "POST" -Path "/clusters/$ClusterID/mesh/route-intents" -Body @{ actor_user_id = $ActorUserID source_selector = @{ node_id = $entryNode.id } destination_selector = @{ node_id = $exitNode.id } service_class = "vpn_packets" priority = $Priority policy = @{ synthetic_enabled = $true allowed_channels = @("vpn_packet", "fabric_control") hops = @($entryNode.id, $exitNode.id) expires_at = $expiresAt max_ttl = 8 max_hops = 8 route_version = "$runId-$Label" policy_version = "$runId-$Label" peer_directory_version = "$runId-$Label" metadata = @{ smoke = "c18z_service_channel_load" run_id = $runId label = $Label } } } } function Get-RouteIntent { param([string]$RouteIntentID) $items = (Invoke-ApiJson -Path "/clusters/$ClusterID/mesh/route-intents?actor_user_id=$ActorUserID").route_intents return @($items | Where-Object { $_.id -eq $RouteIntentID })[0] } function Get-NodeRouteIDs { param([string]$NodeID) $config = (Invoke-ApiJson -Path "/clusters/$ClusterID/nodes/$NodeID/mesh/synthetic-config").synthetic_mesh_config return @($config.routes | ForEach-Object { $_.route_id }) } Push-Location $agentRoot try { $testOutput = & go test ./internal/vpnruntime -run $testPattern -v 2>&1 | ForEach-Object { $_.ToString() } $testExitCode = $LASTEXITCODE } finally { Pop-Location } $nodes = (Invoke-ApiJson -Path "/clusters/$ClusterID/nodes?actor_user_id=$ActorUserID").nodes $entryNode = @($nodes | Where-Object { $_.name -eq $EntryNodeName })[0] $exitNode = @($nodes | Where-Object { $_.name -eq $ExitNodeName })[0] if (-not $entryNode -or -not $exitNode) { throw "Required nodes not found: entry=$EntryNodeName exit=$ExitNodeName" } $expireIntent = (New-SmokeRouteIntent -Label "expire-during-load" -Priority 9830).route_intent $disableIntent = (New-SmokeRouteIntent -Label "disable-during-load" -Priority 9840).route_intent $routeIDsBefore = Get-NodeRouteIDs -NodeID $entryNode.id $expired = (Invoke-ApiJson -Method "POST" -Path "/clusters/$ClusterID/mesh/route-intents/$($expireIntent.id)/expire" -Body @{ actor_user_id = $ActorUserID reason = "C18Z smoke expire during bounded load" }).route_intent $disabled = (Invoke-ApiJson -Method "POST" -Path "/clusters/$ClusterID/mesh/route-intents/$($disableIntent.id)/disable" -Body @{ actor_user_id = $ActorUserID reason = "C18Z smoke disable during bounded load" }).route_intent $routeIDsAfter = Get-NodeRouteIDs -NodeID $entryNode.id $expiredListed = Get-RouteIntent -RouteIntentID $expireIntent.id $disabledListed = Get-RouteIntent -RouteIntentID $disableIntent.id $checks = [ordered]@{ bounded_load_rebuild_away_from_withdrawn_route = [bool]($testOutput -match "PASS: TestFabricClientPacketIngressBoundedLoadRebuildsAwayFromWithdrawnRoute") bounded_load_drop_telemetry = [bool]($testOutput -match "PASS: TestFabricClientPacketIngressBoundedLoadReportsPerChannelDrops") live_route_visible_before_expire = [bool]($routeIDsBefore -contains $expireIntent.id) live_route_visible_before_disable = [bool]($routeIDsBefore -contains $disableIntent.id) live_expire_marks_expired = [bool]($expired.lifecycle_status -eq "expired" -and $expired.is_expired -eq $true) live_disable_marks_disabled = [bool]($disabled.lifecycle_status -eq "disabled" -and $disabled.status -eq "disabled") live_expired_route_removed_from_synthetic_config = [bool](-not ($routeIDsAfter -contains $expireIntent.id)) live_disabled_route_removed_from_synthetic_config = [bool](-not ($routeIDsAfter -contains $disableIntent.id)) live_list_reports_expired = [bool]($expiredListed.lifecycle_status -eq "expired") live_list_reports_disabled = [bool]($disabledListed.lifecycle_status -eq "disabled") } $passed = $testExitCode -eq 0 -and -not ($checks.Values -contains $false) $result = [ordered]@{ schema_version = "c18z.service_channel_load_smoke.v1" run_id = $runId base_url = $BaseUrl cluster_id = $ClusterID entry_node = @{ id = $entryNode.id; name = $entryNode.name } exit_node = @{ id = $exitNode.id; name = $exitNode.name } test_package = "github.com/example/remote-access-platform/agents/rap-node-agent/internal/vpnruntime" test_pattern = $testPattern test_exit_code = $testExitCode expire_route_intent_id = $expireIntent.id disable_route_intent_id = $disableIntent.id passed = $passed checks = $checks test_output = $testOutput } $absoluteResultPath = Join-Path $repoRoot $ResultPath New-Item -ItemType Directory -Force -Path (Split-Path -Parent $absoluteResultPath) | Out-Null $result | ConvertTo-Json -Depth 20 | Set-Content -Encoding UTF8 $absoluteResultPath if (-not $passed) { throw "C18Z service-channel load smoke failed. Result: $absoluteResultPath" } Write-Host "C18Z service-channel load smoke passed. Result: $absoluteResultPath" $result