Private/Seed.ps1

# Copie une arborescence de fichiers texte dans la distro, chemin relatif = destination
# (files\usr\local\bin\x -> /usr/local/bin/x). Le lien éventuel est remplacé, pas suivi.
# PowerShell ajoute une fin de ligne à ce qu'il envoie : head -c -1 la retire.
function Copy-FileTree($m, [string]$Source) {
    if (-not (Test-Path -Path $Source)) { return }
    foreach ($file in Get-ChildItem -Path $Source -Recurse -File) {
        $dest = '/' + ([IO.Path]::GetRelativePath($Source, $file.FullName) -replace '\\', '/')
        $dir = $dest.Substring(0, $dest.LastIndexOf('/'))
        Get-Content -Path $file.FullName -Raw |
            Invoke-Root $m "mkdir -p '$dir' && rm -f '$dest' && tr -d '\r' | head -c -1 > '$dest'"
    }
}

# Seed NoCloud, puis rejeu de cloud-init :
# 1. files\ et machines\<nom>\files\ copiés tels quels, /etc/ws-creator-profile ;
# 2. les fragments, puis user-data.yaml de la machine (en dernier : il complète), validés par
# cloud-init schema et assemblés par make-mime ; meta-data : l'instance-id seul ;
# 3. clean, redémarrage, attente du résultat.
# cloud-init ajoute et ne retire jamais : retirer un droit ou changer l'isolation passe par
# purge puis new.
function Invoke-CloudInit($m, [string[]]$Fragments) {
    $PSNativeCommandUseErrorActionPreference = $false   # codes de sortie lus plus bas
    $seed = '/var/lib/cloud/seed/nocloud'
    $parts = '/var/lib/ws-creator/cloud-config'

    Copy-FileTree $m (Join-Path -Path $script:Root -ChildPath 'files')
    Copy-FileTree $m (Join-Path -Path $m.Dir -ChildPath 'files')
    $m.Name | Invoke-Root $m "tr -d '\r' > /etc/ws-creator-profile"

    Invoke-Root $m "rm -rf $parts && mkdir -p $parts $seed"
    $sources = @($Fragments | ForEach-Object { Join-Path -Path $script:Root -ChildPath "cloud-config\$_.yaml" }) + $m.UserData
    $names = @($Fragments) + 'user-data'
    $mime = @()
    for ($i = 0; $i -lt $sources.Count; $i++) {
        $part = '{0}/{1:d2}-{2}.yaml' -f $parts, $i, $names[$i]
        $mime += "-a ${part}:cloud-config"
        Get-Content -Path $sources[$i] -Raw | Invoke-Root $m "tr -d '\r' > $part"
        Invoke-Root $m "cloud-init schema -c $part >/dev/null" 2>&1 | Where-Object { $_ -notmatch 'datasource not detected' } |
            ForEach-Object { Write-Warning $_ }
        if ($LASTEXITCODE) { throw "cloud-init schema: $($sources[$i]) is not valid cloud-config" }
    }
    "instance-id: $($m.Name)" | Invoke-Root $m "tr -d '\r' > $seed/meta-data"
    Invoke-Root $m "cloud-init devel make-mime $($mime -join ' ') > $seed/user-data"
    if ($LASTEXITCODE) { throw "cloud-init devel make-mime failed in $($m.Name)" }

    # l'accueil d'Ubuntu (wsl-setup) pose ce marqueur au premier lancement interactif
    Invoke-Root $m 'rm -f /etc/cloud/cloud-init.disabled && cloud-init clean --logs'
    Stop-Distro $m
    $status = Invoke-Root $m 'cloud-init status --wait --format json' | ConvertFrom-Json
    Invoke-Root $m "grep -E 'WARNING|ERROR' /var/log/cloud-init.log || true" | ForEach-Object { Write-Warning $_ }
    if ($status.status -ne 'done') {
        throw "cloud-init in $($m.Name): $($status.status) ($($status.boot_status_code)), see /var/log/cloud-init.log"
    }
}