Enforce coherent Idunn swarm deployments
This commit is contained in:
parent
8d110c3f4b
commit
dccac65ea7
12 changed files with 200 additions and 23 deletions
2
scripts/deploy-yggdrasil-heimdall.cmd
Normal file
2
scripts/deploy-yggdrasil-heimdall.cmd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File E:\Projects\Odin\scripts\deploy-yggdrasil-source-app.ps1 -AppId yggdrasil-heimdall -RepoRoot E:\Projects\Heimdall -AppUser heimdall -RemoteAppHome /srv/heimdall -RemoteTarballName heimdall-source.tar -DeployScript E:\Projects\gamecult-ops\scripts\deploy-heimdall.sh -CheckScript E:\Projects\gamecult-ops\scripts\check-heimdall.sh
|
||||
2
scripts/deploy-yggdrasil-repixelizer.cmd
Normal file
2
scripts/deploy-yggdrasil-repixelizer.cmd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File E:\Projects\Odin\scripts\deploy-yggdrasil-source-app.ps1 -AppId yggdrasil-repixelizer -RepoRoot E:\Projects\repixelizer -AppUser repixelizer -RemoteAppHome /srv/repixelizer -RemoteTarballName repixelizer-source.tar -DeployScript E:\Projects\gamecult-ops\scripts\deploy-repixelizer-gui.sh -CheckScript E:\Projects\gamecult-ops\scripts\check-repixelizer-gui.sh
|
||||
84
scripts/deploy-yggdrasil-source-app.ps1
Normal file
84
scripts/deploy-yggdrasil-source-app.ps1
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
param(
|
||||
[Parameter(Mandatory = $true)] [string] $AppId,
|
||||
[Parameter(Mandatory = $true)] [string] $RepoRoot,
|
||||
[Parameter(Mandatory = $true)] [string] $AppUser,
|
||||
[Parameter(Mandatory = $true)] [string] $RemoteAppHome,
|
||||
[Parameter(Mandatory = $true)] [string] $RemoteTarballName,
|
||||
[Parameter(Mandatory = $true)] [string] $DeployScript,
|
||||
[Parameter(Mandatory = $true)] [string] $CheckScript,
|
||||
[string] $SshTarget = "yggdrasil"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $RepoRoot)) {
|
||||
throw "Repo root not found: $RepoRoot"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $DeployScript)) {
|
||||
throw "Deploy script not found: $DeployScript"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $CheckScript)) {
|
||||
throw "Check script not found: $CheckScript"
|
||||
}
|
||||
|
||||
$commit = (git -C $RepoRoot rev-parse HEAD).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($commit)) {
|
||||
throw "Could not determine git revision for $RepoRoot"
|
||||
}
|
||||
|
||||
$scratch = Join-Path "E:\Projects\Odin\scratch\idunn-deploy" $AppId
|
||||
$tarPath = Join-Path $scratch $RemoteTarballName
|
||||
$manifestPath = Join-Path $scratch "deployment-manifest.txt"
|
||||
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
|
||||
|
||||
git -C $RepoRoot archive --format=tar --output=$tarPath HEAD
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "git archive failed for $RepoRoot"
|
||||
}
|
||||
|
||||
$hash = (Get-FileHash $tarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$deployedAt = [DateTimeOffset]::UtcNow.ToString("O")
|
||||
@(
|
||||
"schema=gamecult.idunn.deployment_manifest.v1"
|
||||
"appId=$AppId"
|
||||
"repoRoot=$RepoRoot"
|
||||
"gitCommit=$commit"
|
||||
"artifact=$RemoteTarballName"
|
||||
"sha256=$hash"
|
||||
"deployedAtUtc=$deployedAt"
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $manifestPath
|
||||
|
||||
$remoteDeploy = "/home/gamecultadmin/$([IO.Path]::GetFileName($DeployScript))"
|
||||
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
|
||||
$remoteTarball = "/home/gamecultadmin/$RemoteTarballName"
|
||||
$remoteManifest = "/home/gamecultadmin/$AppId-deployment-manifest.txt"
|
||||
$targetTarball = "$RemoteAppHome/$RemoteTarballName"
|
||||
$targetManifest = "$RemoteAppHome/deployment-manifest.txt"
|
||||
$sshArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10", $SshTarget)
|
||||
$scpArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10")
|
||||
|
||||
scp.exe @scpArgs $tarPath "${SshTarget}:$remoteTarball"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp source tarball failed for $AppId" }
|
||||
scp.exe @scpArgs $DeployScript "${SshTarget}:$remoteDeploy"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp deploy script failed for $AppId" }
|
||||
scp.exe @scpArgs $CheckScript "${SshTarget}:$remoteCheck"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp check script failed for $AppId" }
|
||||
scp.exe @scpArgs $manifestPath "${SshTarget}:$remoteManifest"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp manifest failed for $AppId" }
|
||||
|
||||
$remoteCommand = @(
|
||||
"sudo -n install -d -o '$AppUser' -g '$AppUser' '$RemoteAppHome'",
|
||||
"sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteTarball' '$targetTarball'",
|
||||
"chmod +x '$remoteDeploy' '$remoteCheck'",
|
||||
"sudo -n bash '$remoteDeploy'",
|
||||
"check_ok=0; for i in `$(seq 1 30); do if sudo -n bash '$remoteCheck'; then check_ok=1; break; fi; sleep 2; done; if [ `$check_ok -ne 1 ]; then sudo -n bash '$remoteCheck'; fi",
|
||||
"sudo -n install -m 644 '$remoteManifest' '$targetManifest'"
|
||||
) -join " && "
|
||||
|
||||
ssh.exe @sshArgs $remoteCommand
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "remote deploy/check failed for $AppId"
|
||||
}
|
||||
|
||||
Write-Host "$AppId deployed to Yggdrasil at $commit."
|
||||
Write-Host " artifactSha256=$hash"
|
||||
2
scripts/deploy-yggdrasil-streampixels.cmd
Normal file
2
scripts/deploy-yggdrasil-streampixels.cmd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File E:\Projects\Odin\scripts\deploy-yggdrasil-source-app.ps1 -AppId yggdrasil-streampixels -RepoRoot E:\Projects\StreamPixels -AppUser streampixels -RemoteAppHome /srv/streampixels -RemoteTarballName streampixels-source.tar -DeployScript E:\Projects\gamecult-ops\scripts\deploy-streampixels-preview.sh -CheckScript E:\Projects\gamecult-ops\scripts\check-streampixels-preview.sh
|
||||
2
scripts/health-yggdrasil-heimdall.cmd
Normal file
2
scripts/health-yggdrasil-heimdall.cmd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File E:\Projects\Odin\scripts\health-yggdrasil-source-app.ps1 -AppId yggdrasil-heimdall -RepoRoot E:\Projects\Heimdall -RemoteAppHome /srv/heimdall -CheckScript E:\Projects\gamecult-ops\scripts\check-heimdall.sh
|
||||
2
scripts/health-yggdrasil-repixelizer.cmd
Normal file
2
scripts/health-yggdrasil-repixelizer.cmd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File E:\Projects\Odin\scripts\health-yggdrasil-source-app.ps1 -AppId yggdrasil-repixelizer -RepoRoot E:\Projects\repixelizer -RemoteAppHome /srv/repixelizer -CheckScript E:\Projects\gamecult-ops\scripts\check-repixelizer-gui.sh
|
||||
43
scripts/health-yggdrasil-source-app.ps1
Normal file
43
scripts/health-yggdrasil-source-app.ps1
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
param(
|
||||
[Parameter(Mandatory = $true)] [string] $AppId,
|
||||
[Parameter(Mandatory = $true)] [string] $RepoRoot,
|
||||
[Parameter(Mandatory = $true)] [string] $RemoteAppHome,
|
||||
[Parameter(Mandatory = $true)] [string] $CheckScript,
|
||||
[string] $SshTarget = "yggdrasil"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$localCommit = (git -C $RepoRoot rev-parse HEAD).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($localCommit)) {
|
||||
throw "Could not determine git revision for $RepoRoot"
|
||||
}
|
||||
|
||||
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
|
||||
$scpArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10")
|
||||
$sshArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10", $SshTarget)
|
||||
scp.exe @scpArgs $CheckScript "${SshTarget}:$remoteCheck" | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp check script failed for $AppId" }
|
||||
|
||||
$manifest = ssh.exe @sshArgs "cat '$RemoteAppHome/deployment-manifest.txt' 2>/dev/null"
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($manifest)) {
|
||||
throw "$AppId deploy manifest is missing on Yggdrasil."
|
||||
}
|
||||
|
||||
$remoteCommit = ""
|
||||
foreach ($line in $manifest) {
|
||||
if ($line -like "gitCommit=*") {
|
||||
$remoteCommit = $line.Substring("gitCommit=".Length).Trim()
|
||||
}
|
||||
}
|
||||
|
||||
if ($remoteCommit -ne $localCommit) {
|
||||
throw "$AppId deploy is stale. remote=$remoteCommit local=$localCommit"
|
||||
}
|
||||
|
||||
ssh.exe @sshArgs "chmod +x '$remoteCheck' && sudo -n bash '$remoteCheck'"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "$AppId remote health check failed."
|
||||
}
|
||||
|
||||
Write-Host "$AppId is active and deployed at $localCommit."
|
||||
2
scripts/health-yggdrasil-streampixels.cmd
Normal file
2
scripts/health-yggdrasil-streampixels.cmd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File E:\Projects\Odin\scripts\health-yggdrasil-source-app.ps1 -AppId yggdrasil-streampixels -RepoRoot E:\Projects\StreamPixels -RemoteAppHome /srv/streampixels -CheckScript E:\Projects\gamecult-ops\scripts\check-streampixels-preview.sh
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$opsRoot = "E:\Projects\gamecult-ops"
|
||||
|
||||
$IdunnDeploymentTargets = @(
|
||||
[pscustomobject]@{
|
||||
|
|
@ -19,12 +18,12 @@ $IdunnDeploymentTargets = @(
|
|||
Repo = "Bifrost"
|
||||
LocalPath = "E:\Projects\Bifrost"
|
||||
Host = "yggdrasil"
|
||||
Service = "bifrost.service/bifrost-web"
|
||||
Service = "bifrost.service"
|
||||
Status = "blocked"
|
||||
Health = "$opsRoot\scripts\check-bifrost-alpha.sh"
|
||||
Deploy = "$opsRoot\scripts\deploy-bifrost-container.ps1"
|
||||
Health = $null
|
||||
Deploy = $null
|
||||
Restart = $null
|
||||
Reason = "Deploy authority exists in gamecult-ops, but desired image digest/tag is not yet published as an Idunn deployment target record."
|
||||
Reason = "Committed Bifrost HEAD currently expects UserAccounts.HeimdallAccountId, but Yggdrasil's production database lacks that column and EF reports no pending migration. Idunn must not enforce Bifrost freshness until the Bifrost schema migration path is fixed."
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "yggdrasil-streampixels"
|
||||
|
|
@ -32,11 +31,11 @@ $IdunnDeploymentTargets = @(
|
|||
LocalPath = "E:\Projects\StreamPixels"
|
||||
Host = "yggdrasil"
|
||||
Service = "streampixels-service/streampixels-web"
|
||||
Status = "blocked"
|
||||
Health = "$opsRoot\scripts\check-streampixels-preview.sh"
|
||||
Deploy = "$opsRoot\scripts\deploy-streampixels-preview.sh"
|
||||
Status = "enforced"
|
||||
Health = "$repoRoot\scripts\health-yggdrasil-streampixels.cmd"
|
||||
Deploy = "$repoRoot\scripts\deploy-yggdrasil-streampixels.cmd"
|
||||
Restart = $null
|
||||
Reason = "Deploy script is remote-root/runbook shaped and needs a committed artifact manifest plus Idunn-safe wrapper before automatic execution."
|
||||
Reason = "Idunn packages committed local StreamPixels HEAD, runs the existing ops deploy/check scripts, and verifies the remote manifest."
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "yggdrasil-heimdall"
|
||||
|
|
@ -44,11 +43,11 @@ $IdunnDeploymentTargets = @(
|
|||
LocalPath = "E:\Projects\Heimdall"
|
||||
Host = "yggdrasil"
|
||||
Service = "heimdall.service"
|
||||
Status = "blocked"
|
||||
Health = "$opsRoot\scripts\check-heimdall.sh"
|
||||
Deploy = "$opsRoot\scripts\deploy-heimdall.sh"
|
||||
Status = "enforced"
|
||||
Health = "$repoRoot\scripts\health-yggdrasil-heimdall.cmd"
|
||||
Deploy = "$repoRoot\scripts\deploy-yggdrasil-heimdall.cmd"
|
||||
Restart = $null
|
||||
Reason = "Deploy script uses a workstation-built source tarball; Idunn needs a manifest-producing wrapper before automatic execution."
|
||||
Reason = "Idunn packages committed local Heimdall HEAD, runs the existing ops deploy/check scripts, and verifies the remote manifest."
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "yggdrasil-repixelizer"
|
||||
|
|
@ -56,11 +55,11 @@ $IdunnDeploymentTargets = @(
|
|||
LocalPath = "E:\Projects\repixelizer"
|
||||
Host = "yggdrasil"
|
||||
Service = "repixelizer-gui.service"
|
||||
Status = "blocked"
|
||||
Health = "$opsRoot\scripts\check-repixelizer-gui.sh"
|
||||
Deploy = "$opsRoot\scripts\deploy-repixelizer-gui.sh"
|
||||
Status = "enforced"
|
||||
Health = "$repoRoot\scripts\health-yggdrasil-repixelizer.cmd"
|
||||
Deploy = "$repoRoot\scripts\deploy-yggdrasil-repixelizer.cmd"
|
||||
Restart = $null
|
||||
Reason = "Deploy script uses a committed source tarball and Python environment mutation; Idunn needs an artifact manifest wrapper before automatic execution."
|
||||
Reason = "Idunn packages committed local repixelizer HEAD, runs the existing ops deploy/check scripts, and verifies the remote manifest."
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "github-pages-gamecult-site"
|
||||
|
|
|
|||
|
|
@ -69,6 +69,30 @@ $watchdogs = @(
|
|||
Health = "$repoRoot\scripts\health-idunn-swarm-deployment-coverage.cmd"
|
||||
Restart = $null
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "yggdrasil-heimdall"
|
||||
Name = "Yggdrasil Heimdall"
|
||||
Verse = "yggdrasil.local"
|
||||
Health = "$repoRoot\scripts\health-yggdrasil-heimdall.cmd"
|
||||
Deploy = "$repoRoot\scripts\deploy-yggdrasil-heimdall.cmd"
|
||||
IntervalSeconds = 300
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "yggdrasil-repixelizer"
|
||||
Name = "Yggdrasil Repixelizer"
|
||||
Verse = "yggdrasil.local"
|
||||
Health = "$repoRoot\scripts\health-yggdrasil-repixelizer.cmd"
|
||||
Deploy = "$repoRoot\scripts\deploy-yggdrasil-repixelizer.cmd"
|
||||
IntervalSeconds = 300
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "yggdrasil-streampixels"
|
||||
Name = "Yggdrasil StreamPixels"
|
||||
Verse = "yggdrasil.local"
|
||||
Health = "$repoRoot\scripts\health-yggdrasil-streampixels.cmd"
|
||||
Deploy = "$repoRoot\scripts\deploy-yggdrasil-streampixels.cmd"
|
||||
IntervalSeconds = 300
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "nightwing-gjallar"
|
||||
Name = "Nightwing Gjallar framebuffer compositor"
|
||||
|
|
|
|||
Loading…
Reference in a new issue