171 lines
6.9 KiB
PowerShell
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
|
|
}
|