aboutsummaryrefslogtreecommitdiffstats
path: root/packaging/win/smoke-node-runtime.ps1
blob: bfcabacdb1e2c8293621d54632368324a9e5bdff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<#
.SYNOPSIS
    Start a frozen meshbay-node as a daemon and check it answers as the version
    it claims to be.

.DESCRIPTION
    `meshbay-node --help` imports the parser and nothing else, so it passes for
    a bundle that cannot start the daemon at all. This starts the real daemon,
    in a throwaway profile (its own LOCALAPPDATA, an unused port, a hub URL
    nothing listens on -- it never touches a real hub or the developer's own
    node), waits for its control API, and checks:
      - /api/status answers, with the expected version;
      - the Windows log file was written (a service-mode daemon has no console,
        and this file is the only place its errors go).

    Also useful against an installed build:
      smoke-node-runtime.ps1 -Exe "$env:LOCALAPPDATA\Programs\MeshBay\resources\node-runtime\meshbay-node.exe" -ExpectVersion 0.16.0

.PARAMETER Exe
    The meshbay-node.exe to start.

.PARAMETER ExpectVersion
    The version /api/status must report.
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)][string]$Exe,
    [Parameter(Mandatory = $true)][string]$ExpectVersion,
    [int]$TimeoutSeconds = 45
)

$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest

$profileDir = Join-Path ([IO.Path]::GetTempPath()) ("meshbay-smoke-" + [guid]::NewGuid().ToString("N"))
$meshbay = Join-Path $profileDir "meshbay"
New-Item -ItemType Directory -Force $meshbay | Out-Null

# A free loopback port, so a node already running here on 18000 is untouched.
$listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0)
$listener.Start()
$port = $listener.LocalEndpoint.Port
$listener.Stop()

$bytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
$unlock = Join-Path $meshbay "unlock.key"
Set-Content -Encoding ascii -LiteralPath $unlock ([Convert]::ToBase64String($bytes))
$unlockToml = $unlock.Replace([char]92, [char]47)
Set-Content -Encoding ascii -LiteralPath (Join-Path $meshbay "node.toml") @"
[hub]
url      = "http://127.0.0.1:1"
username = "smoke"

[node]
quic_enabled = false
ui_port   = $port

[keystore]
unlock_file = "$unlockToml"
"@

$oldLocal = $env:LOCALAPPDATA
$env:LOCALAPPDATA = $profileDir
$stderr = Join-Path $profileDir "stderr.txt"
$proc = $null
try {
    $proc = Start-Process -FilePath $Exe -PassThru -WindowStyle Hidden `
        -RedirectStandardError $stderr -RedirectStandardOutput (Join-Path $profileDir "stdout.txt")
    $env:LOCALAPPDATA = $oldLocal

    $tokenFile = Join-Path $meshbay "data\ui-token"
    $status = $null
    $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
    while ((Get-Date) -lt $deadline -and -not $proc.HasExited) {
        if (Test-Path -LiteralPath $tokenFile) {
            try {
                $token = (Get-Content -LiteralPath $tokenFile -Raw).Trim()
                $status = Invoke-RestMethod "http://127.0.0.1:$port/api/status?t=$token" -TimeoutSec 3
                break
            } catch { }
        }
        Start-Sleep -Milliseconds 500
    }

    $tail = if (Test-Path -LiteralPath $stderr) { (Get-Content -LiteralPath $stderr -Tail 30) -join "`n" } else { "" }
    if ($proc.HasExited) {
        throw "the frozen daemon exited (code $($proc.ExitCode)) before its control API answered:`n$tail"
    }
    if ($null -eq $status) {
        throw "the frozen daemon's control API did not answer within ${TimeoutSeconds}s:`n$tail"
    }
    if ($status.version -ne $ExpectVersion) {
        throw "the frozen daemon reports version '$($status.version)', expected '$ExpectVersion'"
    }
    $log = Join-Path $meshbay "state\node.log"
    if (-not (Test-Path -LiteralPath $log) -or (Get-Item -LiteralPath $log).Length -eq 0) {
        throw "the frozen daemon wrote no log file at $log"
    }
    Write-Host "OK  daemon answered: version $($status.version), status $($status.status), log $log" -ForegroundColor Green
}
finally {
    $env:LOCALAPPDATA = $oldLocal
    if ($proc -and -not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue }
    if ($proc) { $proc.WaitForExit(10000) | Out-Null }
    Remove-Item -LiteralPath $profileDir -Recurse -Force -ErrorAction SilentlyContinue
}