NODEDC_PLATFORM/services/dc-amd-connector/install.ps1

171 lines
6.9 KiB
PowerShell

[CmdletBinding()]
param(
[string]$Destination = 'C:\NODEDC\dc-amd-connector',
[string]$NasAddress = '172.22.0.222',
[string]$BindAddress = '172.22.0.183',
[int]$Port = 8791,
[bool]$EnableAutoStart = $true
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Require-Administrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this installer from an elevated PowerShell window.'
}
}
function Test-PackageChecksums([string]$Root) {
$manifest = Join-Path $Root 'SHA256SUMS'
if (-not (Test-Path -LiteralPath $manifest -PathType Leaf)) {
throw "Checksum manifest not found: $manifest"
}
foreach ($line in Get-Content -LiteralPath $manifest) {
if ([string]::IsNullOrWhiteSpace($line)) { continue }
$parts = $line -split '\s{2,}', 2
if ($parts.Count -ne 2 -or $parts[0] -notmatch '^[a-f0-9]{64}$') {
throw "Invalid checksum line: $line"
}
$relative = $parts[1] -replace '^\./', ''
$file = Join-Path $Root $relative
$actual = (Get-FileHash -LiteralPath $file -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actual -ne $parts[0]) { throw "Checksum mismatch: $relative" }
}
}
function Get-DockerDesktopStartCommand([string]$DockerCliPath) {
& $DockerCliPath desktop start --help 2>$null | Out-Null
if ($LASTEXITCODE -eq 0) {
return "`"$DockerCliPath`" desktop start --detach"
}
$dockerRoot = Split-Path (Split-Path (Split-Path $DockerCliPath -Parent) -Parent) -Parent
$candidates = @(
(Join-Path $dockerRoot 'Docker Desktop.exe'),
(Join-Path $env:ProgramFiles 'Docker\Docker\Docker Desktop.exe'),
(Join-Path $env:LOCALAPPDATA 'Programs\Docker\Docker\Docker Desktop.exe')
) | Select-Object -Unique
$desktopExe = $candidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if (-not $desktopExe) {
throw 'Unable to find Docker Desktop start command or Docker Desktop.exe for restart recovery.'
}
return "start `"`" `"$desktopExe`""
}
function New-DockerDesktopAutoStart([string]$StartCommand) {
$startup = [Environment]::GetFolderPath([Environment+SpecialFolder]::Startup)
if ([string]::IsNullOrWhiteSpace($startup)) { throw 'Unable to resolve the current user Startup folder.' }
$launcher = Join-Path $startup 'NODE.DC DC AMD Connector - Docker Desktop.cmd'
if (Test-Path -LiteralPath $launcher) {
throw "Docker Desktop auto-start launcher already exists: $launcher"
}
@(
'@echo off',
'rem NODE.DC DC AMD Connector - starts Docker Desktop after this user signs in.',
$StartCommand
) | Set-Content -LiteralPath $launcher -Encoding ascii
return $launcher
}
Require-Administrator
$source = Split-Path -Parent $PSCommandPath
if (-not (Test-Path -LiteralPath (Join-Path $source 'VERSION') -PathType Leaf)) {
throw 'Run install.ps1 from the DC AMD Connector package folder.'
}
Test-PackageChecksums $source
if (Test-Path -LiteralPath $Destination) {
throw "Destination already exists; refusing to overwrite: $Destination"
}
$null = [Net.IPAddress]::Parse($NasAddress)
$null = [Net.IPAddress]::Parse($BindAddress)
if ($Port -lt 1 -or $Port -gt 65535) {
throw "Port must be between 1 and 65535: $Port"
}
$dockerCli = (Get-Command docker -CommandType Application -ErrorAction Stop | Select-Object -First 1).Path
if ([string]::IsNullOrWhiteSpace($dockerCli) -or -not (Test-Path -LiteralPath $dockerCli -PathType Leaf)) {
throw 'Docker CLI executable could not be resolved.'
}
& $dockerCli version --format '{{.Server.Os}}/{{.Server.Arch}} {{.Server.Version}}' | Out-Null
if ($LASTEXITCODE -ne 0) {
throw 'Docker Desktop engine is not available. Start it and wait for Engine running.'
}
$dockerDesktopStartCommand = Get-DockerDesktopStartCommand $dockerCli
$destinationCreated = $false
$firewallCreated = $false
$autoStartLauncher = $null
$parent = Split-Path -Parent $Destination
try {
New-Item -ItemType Directory -Force $parent | Out-Null
Copy-Item -LiteralPath $source -Destination $Destination -Recurse
$destinationCreated = $true
Test-PackageChecksums $Destination
Set-Location $Destination
New-Item -ItemType Directory -Force runtime | Out-Null
$tokenPath = Join-Path $Destination 'runtime\connector-access'
if (Test-Path -LiteralPath $tokenPath) { throw "Runtime token already exists: $tokenPath" }
$bytes = New-Object byte[] 48
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
try { $rng.GetBytes($bytes) } finally { $rng.Dispose() }
$token = [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
[IO.File]::WriteAllText($tokenPath, $token, [Text.UTF8Encoding]::new($false))
Copy-Item -LiteralPath (Join-Path $Destination '.env.example') -Destination (Join-Path $Destination '.env')
@(
"AMD_CONNECTOR_BIND_IP=$BindAddress",
"AMD_CONNECTOR_PORT=$Port"
) | Set-Content -LiteralPath (Join-Path $Destination '.env') -Encoding ascii
$ruleName = 'NODE.DC DC AMD Connector (NAS only)'
if (Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue) {
throw "Firewall rule already exists; refusing to replace: $ruleName"
}
New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Action Allow -Protocol TCP -Profile Any -LocalAddress $BindAddress -LocalPort $Port -RemoteAddress $NasAddress | Out-Null
$firewallCreated = $true
if ($EnableAutoStart) {
$autoStartLauncher = New-DockerDesktopAutoStart $dockerDesktopStartCommand
}
& $dockerCli compose up -d --build
$state = ''
foreach ($attempt in 1..30) {
$state = & $dockerCli inspect dc-amd-connector --format '{{.State.Status}} health={{if .State.Health}}{{.State.Health.Status}}{{end}}'
if ($LASTEXITCODE -ne 0) { throw 'Container inspection failed after Docker Compose apply.' }
if ($state -eq 'running health=healthy') { break }
Start-Sleep -Seconds 2
}
if ($state -ne 'running health=healthy') {
throw "Connector did not become healthy: $state"
}
Write-Host "DC AMD Connector installed: $state"
Write-Host "Bound to $BindAddress`:$Port; inbound firewall scope is NAS $NasAddress."
if ($autoStartLauncher) {
Write-Host "Docker Desktop will start at the next sign-in through: $autoStartLauncher"
}
Write-Host 'The connector access token remains only in runtime\connector-access. Do not print or send it in chat.'
}
catch {
if ($autoStartLauncher -and (Test-Path -LiteralPath $autoStartLauncher)) {
Remove-Item -LiteralPath $autoStartLauncher -Force -ErrorAction SilentlyContinue
}
if ($firewallCreated) {
Remove-NetFirewallRule -DisplayName 'NODE.DC DC AMD Connector (NAS only)' -ErrorAction SilentlyContinue
}
if ($destinationCreated -and (Test-Path -LiteralPath $Destination)) {
Remove-Item -LiteralPath $Destination -Recurse -Force -ErrorAction SilentlyContinue
}
throw
}