08f7bb1757
Exported functions: - Invoke-SshCommand run a command on a Linux guest via ssh.exe (BatchMode=yes) - Copy-SshItem scp a local path to a remote path (or vice versa) - Test-SshReady test TCP:22 + ssh echo round-trip Used by Wait-VMReady.ps1 -Transport SSH, Invoke-RemoteBuild.ps1 -GuestOS Linux, Get-BuildArtifacts.ps1 -GuestOS Linux.
182 lines
4.7 KiB
PowerShell
182 lines
4.7 KiB
PowerShell
#Requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
SSH/SCP transport helpers for Linux build VMs.
|
|
|
|
.DESCRIPTION
|
|
SSH-only transport layer for the Linux branch of the CI/CD system.
|
|
The existing WinRM transport (used by Windows VMs) is NOT modified.
|
|
|
|
Exported functions:
|
|
Invoke-SshCommand — run a command in a Linux guest via ssh.exe
|
|
Copy-SshItem — copy files to/from a Linux guest via scp.exe
|
|
Test-SshReady — poll until SSH is ready (port 22 open + login succeeds)
|
|
|
|
Requirements:
|
|
- ssh.exe and scp.exe in PATH (default on Windows 11 / Server 2022)
|
|
- SSH private key at the path provided
|
|
- Guest user must be set up for key-based auth (no passphrase)
|
|
#>
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Invoke-SshCommand {
|
|
<#
|
|
.SYNOPSIS
|
|
Run a command in a Linux guest via ssh.exe
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $IP,
|
|
|
|
[string] $User = 'ci_build',
|
|
|
|
[string] $KeyPath = 'F:\CI\keys\ci_linux',
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $Command,
|
|
|
|
[int] $ConnectTimeout = 10,
|
|
|
|
# Capture + return stdout/stderr instead of printing to console.
|
|
[switch] $PassThru,
|
|
|
|
# Don't throw on non-zero exit — just return the output.
|
|
[switch] $AllowFail
|
|
)
|
|
|
|
$sshArgs = @(
|
|
'-i', $KeyPath,
|
|
'-o', 'StrictHostKeyChecking=no',
|
|
'-o', 'UserKnownHostsFile=NUL',
|
|
'-o', "ConnectTimeout=$ConnectTimeout",
|
|
'-o', 'BatchMode=yes',
|
|
"$User@$IP",
|
|
$Command
|
|
)
|
|
|
|
if ($PassThru) {
|
|
$output = & ssh @sshArgs 2>&1
|
|
$exit = $LASTEXITCODE
|
|
}
|
|
else {
|
|
& ssh @sshArgs
|
|
$exit = $LASTEXITCODE
|
|
$output = @()
|
|
}
|
|
|
|
if ($exit -ne 0 -and -not $AllowFail) {
|
|
throw "[SSH] Command failed (exit $exit): $Command"
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
ExitCode = [int]$exit
|
|
Output = [string[]]$output
|
|
}
|
|
}
|
|
|
|
function Copy-SshItem {
|
|
<#
|
|
.SYNOPSIS
|
|
Copy files to/from Linux guest via scp.exe
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $Source,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $Destination,
|
|
|
|
# Guest IP — used to build user@host prefix when Direction is set.
|
|
[string] $IP = '',
|
|
|
|
[string] $User = 'ci_build',
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $KeyPath,
|
|
|
|
[ValidateSet('ToGuest', 'FromGuest', 'Direct')]
|
|
[string] $Direction = 'Direct',
|
|
|
|
# Pass -r to scp (recursive copy).
|
|
[switch] $Recurse,
|
|
|
|
[int] $ConnectTimeout = 10
|
|
)
|
|
|
|
if ($Direction -eq 'ToGuest') {
|
|
$Destination = "$User@${IP}:$Destination"
|
|
}
|
|
elseif ($Direction -eq 'FromGuest') {
|
|
$Source = "$User@${IP}:$Source"
|
|
}
|
|
# Direction = 'Direct': use Source + Destination as-is
|
|
|
|
$scpArgs = @(
|
|
'-i', $KeyPath,
|
|
'-o', 'StrictHostKeyChecking=no',
|
|
'-o', 'UserKnownHostsFile=NUL',
|
|
'-o', "ConnectTimeout=$ConnectTimeout"
|
|
)
|
|
if ($Recurse) { $scpArgs += '-r' }
|
|
$scpArgs += @($Source, $Destination)
|
|
|
|
& scp @scpArgs
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "[SCP] Copy failed (exit $LASTEXITCODE): $Source -> $Destination"
|
|
}
|
|
}
|
|
|
|
function Test-SshReady {
|
|
<#
|
|
.SYNOPSIS
|
|
Poll until SSH port 22 is open and a login command succeeds.
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $IP,
|
|
|
|
[string] $User = 'ci_build',
|
|
|
|
[string] $KeyPath = 'F:\CI\keys\ci_linux',
|
|
|
|
[int] $TimeoutSec = 300,
|
|
|
|
[int] $PollSec = 10
|
|
)
|
|
|
|
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
|
|
|
# Phase 1: wait for port 22 to be open
|
|
$portOpen = $false
|
|
while (-not $portOpen -and (Get-Date) -lt $deadline) {
|
|
$portOpen = (Test-NetConnection -ComputerName $IP -Port 22 `
|
|
-InformationLevel Quiet -WarningAction SilentlyContinue `
|
|
-ErrorAction SilentlyContinue)
|
|
if (-not $portOpen) {
|
|
Start-Sleep -Seconds $PollSec
|
|
}
|
|
}
|
|
|
|
if (-not $portOpen) {
|
|
throw "[Test-SshReady] Timed out after ${TimeoutSec}s waiting for SSH at $IP"
|
|
}
|
|
|
|
# Phase 2: try ssh echo — success when output contains 'ready'
|
|
while ((Get-Date) -lt $deadline) {
|
|
$result = Invoke-SshCommand -IP $IP -User $User -KeyPath $KeyPath `
|
|
-Command 'echo ready' -AllowFail -PassThru
|
|
if ($result.ExitCode -eq 0 -and ($result.Output -join '') -like '*ready*') {
|
|
return $true
|
|
}
|
|
Start-Sleep -Seconds $PollSec
|
|
}
|
|
|
|
throw "[Test-SshReady] Timed out after ${TimeoutSec}s waiting for SSH at $IP"
|
|
}
|
|
|
|
Export-ModuleMember -Function Invoke-SshCommand, Copy-SshItem, Test-SshReady
|