fix(worker): mount generated shell scripts
This commit is contained in:
+129
-36
@@ -446,6 +446,63 @@ function New-DependencyBundleManifest {
|
||||
}
|
||||
}
|
||||
|
||||
function New-TemporaryShellScript {
|
||||
param(
|
||||
[ValidateSet("build", "smoke")]
|
||||
[string]$Role,
|
||||
[string]$Content
|
||||
)
|
||||
|
||||
if ($Content.IndexOf([char]0) -ge 0) {
|
||||
throw "Worker 006 temporary shell script contains a NUL byte"
|
||||
}
|
||||
$normalizedContent = $Content.Replace("`r`n", "`n").Replace("`r", "`n")
|
||||
if (-not $normalizedContent.EndsWith("`n", [StringComparison]::Ordinal)) {
|
||||
$normalizedContent += "`n"
|
||||
}
|
||||
$stagingRoot = Split-Path -Parent $StagedSnapshotRoot
|
||||
$scriptPath = Join-Path (
|
||||
$stagingRoot
|
||||
) ("observatory-worker-agent-{0}-{1}.sh" -f $Role, [Guid]::NewGuid().ToString("N"))
|
||||
$utf8 = New-Object Text.UTF8Encoding($false)
|
||||
try {
|
||||
[IO.File]::WriteAllText($scriptPath, $normalizedContent, $utf8)
|
||||
$item = Get-Item -LiteralPath $scriptPath -Force
|
||||
$bytes = [IO.File]::ReadAllBytes($item.FullName)
|
||||
if (
|
||||
$bytes.Length -eq 0 -or
|
||||
$bytes[$bytes.Length - 1] -ne 10 -or
|
||||
$bytes -contains 13
|
||||
) {
|
||||
throw "Worker 006 temporary shell script is not UTF-8 LF text"
|
||||
}
|
||||
return [pscustomobject]@{
|
||||
role = $Role
|
||||
path = $item.FullName
|
||||
sha256 = Get-FileSha256 $item.FullName
|
||||
byte_length = [int64]$item.Length
|
||||
}
|
||||
}
|
||||
catch {
|
||||
if (Test-Path -LiteralPath $scriptPath -PathType Leaf) {
|
||||
Remove-Item -LiteralPath $scriptPath -Force
|
||||
}
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-TemporaryShellScriptIdentity {
|
||||
param([object]$Script)
|
||||
|
||||
$item = Get-Item -LiteralPath $Script.path -Force
|
||||
if (
|
||||
[int64]$item.Length -ne [int64]$Script.byte_length -or
|
||||
(Get-FileSha256 $item.FullName) -cne [string]$Script.sha256
|
||||
) {
|
||||
throw "Worker 006 temporary $($Script.role) shell script identity changed"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ImageInspection {
|
||||
param([string]$Reference)
|
||||
|
||||
@@ -569,7 +626,7 @@ function Invoke-InstalledImageSmoke {
|
||||
[object]$DependencyManifest
|
||||
)
|
||||
|
||||
$smokeScript = (
|
||||
$smokeScriptContent = (
|
||||
"set -eu; " +
|
||||
"test ! -e /opt/nodedc/mission-core/experiments; " +
|
||||
"test -z `"`$(find /opt/nodedc/mission-core -perm /022 -print -quit)`"; " +
|
||||
@@ -629,38 +686,58 @@ function Invoke-InstalledImageSmoke {
|
||||
"cd /opt/nodedc/mission-core; " +
|
||||
"python3 -B -c '$PythonSmokeProgram'"
|
||||
)
|
||||
$sourceMount = (
|
||||
"type=bind,source=$SnapshotRoot," +
|
||||
"target=/nodedc-verify-source,readonly"
|
||||
)
|
||||
$manifestMount = (
|
||||
"type=bind,source=$($EmbeddedManifest.path)," +
|
||||
"target=/nodedc-verify-snapshot.json,readonly"
|
||||
)
|
||||
$dependencyMount = (
|
||||
"type=bind,source=$DependencyRoot," +
|
||||
"target=/nodedc-verify-deps,readonly"
|
||||
)
|
||||
$dependencyManifestMount = (
|
||||
"type=bind,source=$($DependencyManifest.path)," +
|
||||
"target=/nodedc-verify-deps-manifest.json,readonly"
|
||||
)
|
||||
docker run `
|
||||
--rm `
|
||||
--network none `
|
||||
--read-only `
|
||||
--tmpfs "/tmp:rw,noexec,nosuid,size=16m" `
|
||||
--cap-drop ALL `
|
||||
--security-opt no-new-privileges `
|
||||
--mount $sourceMount `
|
||||
--mount $manifestMount `
|
||||
--mount $dependencyMount `
|
||||
--mount $dependencyManifestMount `
|
||||
--entrypoint /bin/sh `
|
||||
$Tag `
|
||||
-c $smokeScript
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Installed Worker 006 image smoke failed"
|
||||
$smokeScriptFile = $null
|
||||
try {
|
||||
$smokeScriptFile = New-TemporaryShellScript "smoke" $smokeScriptContent
|
||||
Assert-TemporaryShellScriptIdentity $smokeScriptFile
|
||||
$sourceMount = (
|
||||
"type=bind,source=$SnapshotRoot," +
|
||||
"target=/nodedc-verify-source,readonly"
|
||||
)
|
||||
$manifestMount = (
|
||||
"type=bind,source=$($EmbeddedManifest.path)," +
|
||||
"target=/nodedc-verify-snapshot.json,readonly"
|
||||
)
|
||||
$dependencyMount = (
|
||||
"type=bind,source=$DependencyRoot," +
|
||||
"target=/nodedc-verify-deps,readonly"
|
||||
)
|
||||
$dependencyManifestMount = (
|
||||
"type=bind,source=$($DependencyManifest.path)," +
|
||||
"target=/nodedc-verify-deps-manifest.json,readonly"
|
||||
)
|
||||
$smokeScriptMount = (
|
||||
"type=bind,source=$($smokeScriptFile.path)," +
|
||||
"target=/nodedc-smoke-script.sh,readonly"
|
||||
)
|
||||
docker run `
|
||||
--rm `
|
||||
--network none `
|
||||
--read-only `
|
||||
--tmpfs "/tmp:rw,noexec,nosuid,size=16m" `
|
||||
--cap-drop ALL `
|
||||
--security-opt no-new-privileges `
|
||||
--mount $sourceMount `
|
||||
--mount $manifestMount `
|
||||
--mount $dependencyMount `
|
||||
--mount $dependencyManifestMount `
|
||||
--mount $smokeScriptMount `
|
||||
--entrypoint /bin/sh `
|
||||
$Tag `
|
||||
/nodedc-smoke-script.sh
|
||||
$smokeExitCode = $LASTEXITCODE
|
||||
Assert-TemporaryShellScriptIdentity $smokeScriptFile
|
||||
if ($smokeExitCode -ne 0) {
|
||||
throw "Installed Worker 006 image smoke failed"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (
|
||||
$null -ne $smokeScriptFile -and
|
||||
(Test-Path -LiteralPath $smokeScriptFile.path -PathType Leaf)
|
||||
) {
|
||||
Remove-Item -LiteralPath $smokeScriptFile.path -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,6 +842,7 @@ if ([string]$dependencyBefore.sha256 -cne $ExpectedDependencyBundleSha256) {
|
||||
}
|
||||
$embeddedManifest = $null
|
||||
$dependencyManifest = $null
|
||||
$buildScriptFile = $null
|
||||
try {
|
||||
$embeddedManifest = New-StagedSnapshotManifest $snapshotBefore
|
||||
$dependencyManifest = New-DependencyBundleManifest $dependencyBefore
|
||||
@@ -828,7 +906,7 @@ try {
|
||||
throw "Worker 006 temporary build container name is already occupied"
|
||||
}
|
||||
|
||||
$copyScript = (
|
||||
$copyScriptContent = (
|
||||
"set -eu; " +
|
||||
"test ! -e /opt/nodedc/mission-core; " +
|
||||
"mkdir -p /opt/nodedc/mission-core/src /opt/nodedc/mission-core/release " +
|
||||
@@ -880,6 +958,8 @@ try {
|
||||
"PYTHONNOUSERSITE=1 PYTHONDONTWRITEBYTECODE=1 " +
|
||||
"python3 -B -c '$PythonSmokeProgram'"
|
||||
)
|
||||
$buildScriptFile = New-TemporaryShellScript "build" $copyScriptContent
|
||||
Assert-TemporaryShellScriptIdentity $buildScriptFile
|
||||
$sourceMount = (
|
||||
"type=bind,source=$StagedSnapshotRoot," +
|
||||
"target=/nodedc-build-source,readonly"
|
||||
@@ -896,6 +976,10 @@ try {
|
||||
"type=bind,source=$($dependencyManifest.path)," +
|
||||
"target=/nodedc-build-deps-manifest.json,readonly"
|
||||
)
|
||||
$buildScriptMount = (
|
||||
"type=bind,source=$($buildScriptFile.path)," +
|
||||
"target=/nodedc-build-script.sh,readonly"
|
||||
)
|
||||
$containerId = $null
|
||||
$imageCommitted = $false
|
||||
$committedImageId = $null
|
||||
@@ -911,9 +995,10 @@ try {
|
||||
--mount $manifestMount `
|
||||
--mount $dependencyMount `
|
||||
--mount $dependencyManifestMount `
|
||||
--mount $buildScriptMount `
|
||||
--entrypoint /bin/sh `
|
||||
$BaseImageReference `
|
||||
-c $copyScript
|
||||
/nodedc-build-script.sh
|
||||
)
|
||||
$createdIds = @(
|
||||
$createOutput | Where-Object { $_ -match "^[a-f0-9]{64}$" }
|
||||
@@ -925,7 +1010,9 @@ try {
|
||||
throw "Worker 006 temporary build container creation failed"
|
||||
}
|
||||
docker start --attach $containerId
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$buildExitCode = $LASTEXITCODE
|
||||
Assert-TemporaryShellScriptIdentity $buildScriptFile
|
||||
if ($buildExitCode -ne 0) {
|
||||
throw "Worker 006 source installation command failed"
|
||||
}
|
||||
$snapshotAfter = Get-StagedSnapshotInspection $StagedSnapshotRoot
|
||||
@@ -1092,4 +1179,10 @@ finally {
|
||||
) {
|
||||
Remove-Item -LiteralPath $dependencyManifest.path -Force
|
||||
}
|
||||
if (
|
||||
$null -ne $buildScriptFile -and
|
||||
(Test-Path -LiteralPath $buildScriptFile.path -PathType Leaf)
|
||||
) {
|
||||
Remove-Item -LiteralPath $buildScriptFile.path -Force
|
||||
}
|
||||
}
|
||||
|
||||
+25
-3
@@ -53,6 +53,29 @@
|
||||
"runtime_bind_allowed": false,
|
||||
"network_install_allowed": false
|
||||
},
|
||||
"temporary_shell_scripts": {
|
||||
"encoding": "utf-8",
|
||||
"byte_order_mark": false,
|
||||
"line_endings": "lf",
|
||||
"identity_verification": "SHA-256 and byte length before and after container use",
|
||||
"host_cleanup": "always",
|
||||
"build": {
|
||||
"container_path": "/nodedc-build-script.sh",
|
||||
"mount": "read-only",
|
||||
"invocation": [
|
||||
"/bin/sh",
|
||||
"/nodedc-build-script.sh"
|
||||
]
|
||||
},
|
||||
"installed_image_smoke": {
|
||||
"container_path": "/nodedc-smoke-script.sh",
|
||||
"mount": "read-only",
|
||||
"invocation": [
|
||||
"/bin/sh",
|
||||
"/nodedc-smoke-script.sh"
|
||||
]
|
||||
}
|
||||
},
|
||||
"base_image": {
|
||||
"reference": "nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794",
|
||||
"sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794",
|
||||
@@ -130,9 +153,8 @@
|
||||
"read_only_rootfs": true,
|
||||
"platform": "linux/amd64",
|
||||
"entrypoint": [
|
||||
"python3",
|
||||
"-c",
|
||||
"import k1link.observatory.m49_worker_container_main as e; import k1link.observatory.m49_worker_service as c; assert callable(e.main); assert callable(c.compose_installed_m49_worker_service)"
|
||||
"/bin/sh",
|
||||
"/nodedc-smoke-script.sh"
|
||||
],
|
||||
"staged_source_bytes": "matched",
|
||||
"embedded_context_bytes": "matched",
|
||||
|
||||
@@ -272,6 +272,26 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"runtime_bind_allowed": False,
|
||||
"network_install_allowed": False,
|
||||
}
|
||||
shell_scripts = cast(dict[str, object], build["temporary_shell_scripts"])
|
||||
assert shell_scripts == {
|
||||
"encoding": "utf-8",
|
||||
"byte_order_mark": False,
|
||||
"line_endings": "lf",
|
||||
"identity_verification": (
|
||||
"SHA-256 and byte length before and after container use"
|
||||
),
|
||||
"host_cleanup": "always",
|
||||
"build": {
|
||||
"container_path": "/nodedc-build-script.sh",
|
||||
"mount": "read-only",
|
||||
"invocation": ["/bin/sh", "/nodedc-build-script.sh"],
|
||||
},
|
||||
"installed_image_smoke": {
|
||||
"container_path": "/nodedc-smoke-script.sh",
|
||||
"mount": "read-only",
|
||||
"invocation": ["/bin/sh", "/nodedc-smoke-script.sh"],
|
||||
},
|
||||
}
|
||||
base = cast(dict[str, object], build["base_image"])
|
||||
assert base["reference"] == BASE_REFERENCE
|
||||
assert base["sha256"] == BASE_SHA256
|
||||
@@ -358,6 +378,7 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
assert smoke["network"] == "none"
|
||||
assert smoke["read_only_rootfs"] is True
|
||||
assert smoke["platform"] == "linux/amd64"
|
||||
assert smoke["entrypoint"] == ["/bin/sh", "/nodedc-smoke-script.sh"]
|
||||
assert smoke["staged_source_bytes"] == "matched"
|
||||
assert smoke["embedded_context_bytes"] == "matched"
|
||||
assert smoke["embedded_snapshot_manifest"] == "matched"
|
||||
|
||||
@@ -42,6 +42,26 @@ def test_worker_agent_installer_is_local_offline_and_non_buildkit() -> None:
|
||||
assert "smb" not in lowered
|
||||
|
||||
|
||||
def test_worker_agent_installer_mounts_utf8_lf_shell_scripts_by_file() -> None:
|
||||
script = _script()
|
||||
|
||||
assert "function New-TemporaryShellScript" in script
|
||||
assert '$Content.Replace("`r`n", "`n").Replace("`r", "`n")' in script
|
||||
assert "New-Object Text.UTF8Encoding($false)" in script
|
||||
assert "[IO.File]::ReadAllBytes($item.FullName)" in script
|
||||
assert "$bytes[$bytes.Length - 1] -ne 10" in script
|
||||
assert "$bytes -contains 13" in script
|
||||
assert script.count("Assert-TemporaryShellScriptIdentity") == 5
|
||||
assert 'New-TemporaryShellScript "build" $copyScriptContent' in script
|
||||
assert 'New-TemporaryShellScript "smoke" $smokeScriptContent' in script
|
||||
assert "target=/nodedc-build-script.sh,readonly" in script
|
||||
assert "target=/nodedc-smoke-script.sh,readonly" in script
|
||||
assert "$BaseImageReference `\n /nodedc-build-script.sh" in script
|
||||
assert "$Tag `\n /nodedc-smoke-script.sh" in script
|
||||
assert "-c $copyScript" not in script
|
||||
assert "-c $smokeScript" not in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_separates_archive_and_staged_identities() -> None:
|
||||
script = _script()
|
||||
|
||||
@@ -222,6 +242,8 @@ def test_worker_agent_installer_removes_failed_new_image_and_temporary_files() -
|
||||
assert "image verification failed and committed image cleanup failed" in script
|
||||
assert "Remove-Item -LiteralPath $embeddedManifest.path -Force" in script
|
||||
assert "Remove-Item -LiteralPath $dependencyManifest.path -Force" in script
|
||||
assert "Remove-Item -LiteralPath $buildScriptFile.path -Force" in script
|
||||
assert "Remove-Item -LiteralPath $smokeScriptFile.path -Force" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_seals_runtime_contract_smoke_and_output() -> None:
|
||||
|
||||
Reference in New Issue
Block a user