feat(templates): install ci-static-ip/ci-report-ip via Prepare+Install scripts
Windows (Prepare-WinBuild2025/2022): read ci-static-ip.ps1 from guest-setup/windows/ on the host, push content over WinRM, register the CI-StaticIp scheduled task (AtStartup/SYSTEM). Adds StaticIpTask and StaticIpScript post-setup assertions. Linux (Install-CIToolchain-Linux2404.sh): embed ci-report-ip.sh and ci-report-ip.service content inline via heredoc tee, replacing the broken cp-from-relative-path approach (the script runs inside the VM where the host-side guest-setup/ directory does not exist). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -174,12 +174,106 @@ assert_step "99-ci-dhcp-allnics.yaml exists" test -f /etc/netplan/99-ci-dhcp-all
|
||||
# Static (ip_pool): reads guestinfo.ip-assignment injected by the host
|
||||
# orchestrator, applies static NIC config, reports back.
|
||||
# DHCP fallback: waits for DHCP address, reports via guestinfo.ci-ip.
|
||||
# The script and service are sourced from template/guest-setup/linux/.
|
||||
# Content embedded inline — this script runs inside the VM where the host-side
|
||||
# template/guest-setup/linux/ directory is not present.
|
||||
echo ""
|
||||
echo "=== Step 7b: CI IP reporter (with static IP support) ==="
|
||||
sudo cp "$(dirname "$0")/guest-setup/linux/ci-report-ip.sh" /usr/local/bin/ci-report-ip.sh
|
||||
sudo tee /usr/local/bin/ci-report-ip.sh > /dev/null << 'EOREPORTIP'
|
||||
#!/bin/bash
|
||||
# CI VM IP reporter — called by ci-report-ip.service at boot.
|
||||
#
|
||||
# Supports two modes:
|
||||
# Static (ip_pool): reads guestinfo.ip-assignment injected by the host
|
||||
# orchestrator, applies static NIC config, reports back.
|
||||
# DHCP fallback: waits for DHCP address, reports via guestinfo.ci-ip.
|
||||
#
|
||||
# The VMCI channel (vmware-rpctool) does not require TCP/IP to be initialised —
|
||||
# it communicates via the VMware virtual hardware interface. This means the
|
||||
# static IP can be configured before network-online.target, so by the time SSH
|
||||
# starts listening the NIC already has the correct address.
|
||||
|
||||
RPCTOOL=/usr/bin/vmware-rpctool
|
||||
[ -x "$RPCTOOL" ] || exit 0
|
||||
|
||||
# ── Check for pre-assigned IP from orchestrator ───────────────────────────────
|
||||
assigned_ip=$("$RPCTOOL" "info-get guestinfo.ip-assignment" 2>/dev/null | tr -d '[:space:]')
|
||||
|
||||
if [[ "$assigned_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
netmask=$("$RPCTOOL" "info-get guestinfo.netmask" 2>/dev/null | tr -d '[:space:]')
|
||||
gateway=$("$RPCTOOL" "info-get guestinfo.gateway" 2>/dev/null | tr -d '[:space:]')
|
||||
|
||||
# Resolve primary NIC (first non-loopback).
|
||||
iface=$(ip -o link show | awk -F': ' '$2 != "lo" {print $2; exit}')
|
||||
if [ -z "$iface" ]; then
|
||||
echo "ci-report-ip: no non-loopback interface found" | systemd-cat -t ci-report-ip
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Compute prefix length from netmask (default /24).
|
||||
prefix=24
|
||||
if [ -n "$netmask" ]; then
|
||||
prefix=$(python3 -c \
|
||||
"import ipaddress; print(ipaddress.IPv4Network('0.0.0.0/$netmask',strict=False).prefixlen)" \
|
||||
2>/dev/null) || prefix=24
|
||||
fi
|
||||
|
||||
# Flush any existing config (DHCP or stale static) and apply.
|
||||
ip addr flush dev "$iface" 2>/dev/null || true
|
||||
ip addr add "${assigned_ip}/${prefix}" dev "$iface"
|
||||
ip link set "$iface" up
|
||||
|
||||
if [[ "$gateway" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
ip route add default via "$gateway" dev "$iface" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Report IP back to host via guestinfo.ci-ip.
|
||||
"$RPCTOOL" "info-set guestinfo.ci-ip $assigned_ip" 2>/dev/null
|
||||
echo "ci-report-ip: applied static IP $assigned_ip/$prefix via guestinfo" \
|
||||
| systemd-cat -t ci-report-ip
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Fallback: DHCP — wait for IP and report ───────────────────────────────────
|
||||
# Retries for up to 60 s to tolerate open-vm-tools re-init after UUID change
|
||||
# (linked clones get a new UUID — vmware-rpctool may fail briefly on first boot).
|
||||
for i in $(seq 1 30); do
|
||||
IP=$(ip -4 route get 1.0.0.0 2>/dev/null \
|
||||
| awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}' \
|
||||
| head -1)
|
||||
if [ -n "$IP" ]; then
|
||||
if "$RPCTOOL" "info-set guestinfo.ci-ip $IP" 2>/dev/null; then
|
||||
echo "ci-report-ip: set guestinfo.ci-ip=$IP via DHCP (attempt $i)" \
|
||||
| systemd-cat -t ci-report-ip
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "ci-report-ip: failed to set guestinfo.ci-ip after 30 attempts" \
|
||||
| systemd-cat -t ci-report-ip
|
||||
exit 1
|
||||
EOREPORTIP
|
||||
sudo chmod +x /usr/local/bin/ci-report-ip.sh
|
||||
sudo cp "$(dirname "$0")/guest-setup/linux/ci-report-ip.service" /etc/systemd/system/ci-report-ip.service
|
||||
|
||||
sudo tee /etc/systemd/system/ci-report-ip.service > /dev/null << 'EOSERVICE'
|
||||
[Unit]
|
||||
Description=Report CI VM IP to VMware host via guestinfo
|
||||
# Run after open-vm-tools so vmware-rpctool is available.
|
||||
# Do NOT add network-online.target — we want to run BEFORE the network stack
|
||||
# settles so static IP is applied before SSH/other services start.
|
||||
After=open-vm-tools.service
|
||||
Wants=open-vm-tools.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/ci-report-ip.sh
|
||||
RemainAfterExit=yes
|
||||
TimeoutStartSec=120
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOSERVICE
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable ci-report-ip.service
|
||||
|
||||
@@ -497,6 +497,29 @@ try {
|
||||
-Authentication Basic -ErrorAction Stop
|
||||
}
|
||||
|
||||
# ── Install CI-StaticIp scheduled task (ip_pool guestinfo static IP) ────────
|
||||
$staticIpScriptPath = Join-Path $scriptDir 'guest-setup\windows\ci-static-ip.ps1'
|
||||
if (Test-Path $staticIpScriptPath -PathType Leaf) {
|
||||
Write-Host "[Prepare] Installing CI-StaticIp scheduled task on guest..."
|
||||
$staticIpContent = Get-Content -LiteralPath $staticIpScriptPath -Raw
|
||||
Invoke-Command -Session $session -ScriptBlock {
|
||||
param([string]$ScriptContent)
|
||||
Set-Content -LiteralPath 'C:\CI\ci-static-ip.ps1' -Value $ScriptContent `
|
||||
-Encoding UTF8 -Force
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute 'powershell.exe' `
|
||||
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\CI\ci-static-ip.ps1'
|
||||
$trigger = New-ScheduledTaskTrigger -AtStartup
|
||||
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' `
|
||||
-LogonType ServiceAccount -RunLevel Highest
|
||||
Register-ScheduledTask -TaskName 'CI-StaticIp' -Action $action `
|
||||
-Trigger $trigger -Principal $principal -Force | Out-Null
|
||||
} -ArgumentList $staticIpContent
|
||||
Write-Host "[Prepare] CI-StaticIp task registered." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Warning "[Prepare] guest-setup\windows\ci-static-ip.ps1 not found alongside this script — skipping CI-StaticIp task. ip_pool feature will not work for this template."
|
||||
}
|
||||
|
||||
# ── Post-setup remote validation ──────────────────────────────────────────
|
||||
Write-Host "[Prepare] Running post-setup validation against guest..."
|
||||
$guestState = Invoke-Command -Session $session -ScriptBlock {
|
||||
@@ -514,6 +537,8 @@ try {
|
||||
PythonExists = Test-Path 'C:\Python\python.exe' -PathType Leaf
|
||||
MSBuildExists = Test-Path $msbuildPath -PathType Leaf
|
||||
CIDirsPresent = (Test-Path 'C:\CI\build') -and (Test-Path 'C:\CI\output') -and (Test-Path 'C:\CI\scripts')
|
||||
StaticIpTask = [bool](Get-ScheduledTask -TaskName 'CI-StaticIp' -ErrorAction SilentlyContinue)
|
||||
StaticIpScript = Test-Path 'C:\CI\ci-static-ip.ps1' -PathType Leaf
|
||||
}
|
||||
} -ArgumentList $BuildUsername
|
||||
|
||||
@@ -526,6 +551,8 @@ try {
|
||||
Assert-Step 'PostSetup' 'guest Python installed' { $guestState.PythonExists }
|
||||
Assert-Step 'PostSetup' 'guest MSBuild present' { $guestState.MSBuildExists }
|
||||
Assert-Step 'PostSetup' 'guest C:\CI\{build,output,scripts} present' { $guestState.CIDirsPresent }
|
||||
Assert-Step 'PostSetup' 'guest CI-StaticIp task registered' { $guestState.StaticIpTask }
|
||||
Assert-Step 'PostSetup' 'guest C:\CI\ci-static-ip.ps1 present' { $guestState.StaticIpScript }
|
||||
|
||||
Write-Host "[Prepare] All post-setup validations passed (.NET $($guestState.DotNetVersion))." -ForegroundColor Green
|
||||
|
||||
|
||||
@@ -497,6 +497,33 @@ try {
|
||||
-Authentication Basic -ErrorAction Stop
|
||||
}
|
||||
|
||||
# ── Install CI-StaticIp scheduled task (ip_pool guestinfo static IP) ────────
|
||||
# Reads ci-static-ip.ps1 from alongside this script and installs it into the
|
||||
# guest as C:\CI\ci-static-ip.ps1, then registers the CI-StaticIp task
|
||||
# (At Startup, SYSTEM) so the guest applies a pre-assigned static IP from
|
||||
# guestinfo.ip-assignment before WinRM starts on each clone boot.
|
||||
$staticIpScriptPath = Join-Path $scriptDir 'guest-setup\windows\ci-static-ip.ps1'
|
||||
if (Test-Path $staticIpScriptPath -PathType Leaf) {
|
||||
Write-Host "[Prepare] Installing CI-StaticIp scheduled task on guest..."
|
||||
$staticIpContent = Get-Content -LiteralPath $staticIpScriptPath -Raw
|
||||
Invoke-Command -Session $session -ScriptBlock {
|
||||
param([string]$ScriptContent)
|
||||
Set-Content -LiteralPath 'C:\CI\ci-static-ip.ps1' -Value $ScriptContent `
|
||||
-Encoding UTF8 -Force
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute 'powershell.exe' `
|
||||
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\CI\ci-static-ip.ps1'
|
||||
$trigger = New-ScheduledTaskTrigger -AtStartup
|
||||
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' `
|
||||
-LogonType ServiceAccount -RunLevel Highest
|
||||
Register-ScheduledTask -TaskName 'CI-StaticIp' -Action $action `
|
||||
-Trigger $trigger -Principal $principal -Force | Out-Null
|
||||
} -ArgumentList $staticIpContent
|
||||
Write-Host "[Prepare] CI-StaticIp task registered." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Warning "[Prepare] guest-setup\windows\ci-static-ip.ps1 not found alongside this script — skipping CI-StaticIp task. ip_pool feature will not work for this template."
|
||||
}
|
||||
|
||||
# ── Post-setup remote validation ──────────────────────────────────────────
|
||||
Write-Host "[Prepare] Running post-setup validation against guest..."
|
||||
$guestState = Invoke-Command -Session $session -ScriptBlock {
|
||||
@@ -514,6 +541,8 @@ try {
|
||||
PythonExists = Test-Path 'C:\Python\python.exe' -PathType Leaf
|
||||
MSBuildExists = Test-Path $msbuildPath -PathType Leaf
|
||||
CIDirsPresent = (Test-Path 'C:\CI\build') -and (Test-Path 'C:\CI\output') -and (Test-Path 'C:\CI\scripts')
|
||||
StaticIpTask = [bool](Get-ScheduledTask -TaskName 'CI-StaticIp' -ErrorAction SilentlyContinue)
|
||||
StaticIpScript = Test-Path 'C:\CI\ci-static-ip.ps1' -PathType Leaf
|
||||
}
|
||||
} -ArgumentList $BuildUsername
|
||||
|
||||
@@ -526,6 +555,8 @@ try {
|
||||
Assert-Step 'PostSetup' 'guest Python installed' { $guestState.PythonExists }
|
||||
Assert-Step 'PostSetup' 'guest MSBuild present' { $guestState.MSBuildExists }
|
||||
Assert-Step 'PostSetup' 'guest C:\CI\{build,output,scripts} present' { $guestState.CIDirsPresent }
|
||||
Assert-Step 'PostSetup' 'guest CI-StaticIp task registered' { $guestState.StaticIpTask }
|
||||
Assert-Step 'PostSetup' 'guest C:\CI\ci-static-ip.ps1 present' { $guestState.StaticIpScript }
|
||||
|
||||
Write-Host "[Prepare] All post-setup validations passed (.NET $($guestState.DotNetVersion))." -ForegroundColor Green
|
||||
|
||||
|
||||
Reference in New Issue
Block a user