From ba4283ca8f07b00bfa4efd8618ecd876c869636b Mon Sep 17 00:00:00 2001 From: San Dang Date: Mon, 20 Jul 2026 11:45:21 +0700 Subject: [PATCH 1/4] fix(installer): retain qualified Station DKMS revision Signed-off-by: San Dang --- docs/get-started/dgx-station-preparation.mdx | 1 + scripts/prepare-dgx-station-host.sh | 60 +++++++++++---- ...tall-station-container-coexistence.test.ts | 12 +-- test/install-station-host-preparation.test.ts | 12 +-- ...nstall-station-package-transaction.test.ts | 74 +++++++++++++++++-- 5 files changed, 125 insertions(+), 34 deletions(-) diff --git a/docs/get-started/dgx-station-preparation.mdx b/docs/get-started/dgx-station-preparation.mdx index 4bac497c0c7..0345879aefc 100644 --- a/docs/get-started/dgx-station-preparation.mdx +++ b/docs/get-started/dgx-station-preparation.mdx @@ -54,6 +54,7 @@ The installer records the override in the printed relogin command when Docker-gr On the generic Ubuntu path, accepting express install prepares the host with NVIDIA open driver `610.43.02`, Docker CE `29.6.1` with Buildx, and NVIDIA Container Toolkit `1.19.1`. Preparation probes package and runtime state first, reuses exact matches, and installs only missing pinned packages, including the NVIDIA Container Toolkit libraries and `nvidia-ctk` CLI. It permits only the reviewed factory transition from `dkms` `3.0.11-1ubuntu13` to `1:3.4.0-1ubuntu1`. +It also accepts an installed `dkms` `1:3.4.1-1ubuntu1` as a retained-compatible forward revision, warns that it differs from the validated `1:3.4.0-1ubuntu1` pin, excludes it from APT transactions, and continues runtime validation. After reboot, preparation enables NVIDIA's packaged CDI refresh path and service, requires the `nvidia.com/gpu=all` device, and verifies it with a real container launch. If the packaged refresh fails or does not produce that device, preparation prints service diagnostics and stops for administrator repair. diff --git a/scripts/prepare-dgx-station-host.sh b/scripts/prepare-dgx-station-host.sh index 57f808c1765..5b88cb05ee9 100755 --- a/scripts/prepare-dgx-station-host.sh +++ b/scripts/prepare-dgx-station-host.sh @@ -35,6 +35,7 @@ readonly DOCKER_VERSION="29.6.1" readonly TOOLKIT_VERSION="1.19.1" readonly FACTORY_DKMS_VERSION="3.0.11-1ubuntu13" readonly TARGET_DKMS_VERSION="1:3.4.0-1ubuntu1" +readonly RETAINED_DKMS_VERSION="1:3.4.1-1ubuntu1" # Keep this as a plain Ubuntu image: NVIDIA Container Toolkit injects the host # driver utility when CDI or --gpus is requested. This intentionally exercises # the documented runtime contract instead of relying on a CUDA image payload: @@ -566,6 +567,8 @@ package_state() { printf 'missing\n' elif [[ "$actual" == "$expected" ]]; then printf 'exact\n' + elif [[ "$name" == "dkms" && "$actual" == "$RETAINED_DKMS_VERSION" && "$expected" == "$TARGET_DKMS_VERSION" ]]; then + printf 'retained-compatible\n' elif [[ "$name" == "dkms" && "$actual" == "$FACTORY_DKMS_VERSION" && "$expected" == "$TARGET_DKMS_VERSION" ]]; then printf 'approved-transition\n' else @@ -573,6 +576,23 @@ package_state() { fi } +warn_retained_package_version() { + local spec=$1 state name expected actual + state="$(package_state "$spec")" + [[ "$state" == "retained-compatible" ]] || return 0 + name="$(package_name "$spec")" + expected="$(package_expected_version "$spec")" + actual="$(installed_version "$name")" + warn "package=${name} status=retained_compatible actual=${actual} validated=${expected} decision=retain" +} + +warn_retained_package_versions() { + local spec + for spec in "${PACKAGE_SPECS[@]}"; do + warn_retained_package_version "$spec" + done +} + assert_no_package_mismatches() { local spec state name expected actual mismatch=0 for spec in "${PACKAGE_SPECS[@]}"; do @@ -591,13 +611,19 @@ assert_no_package_mismatches() { warn "package=${name} status=mismatch actual=${actual} expected=${expected}" mismatch=1 done - ((mismatch == 0)) || fatal "Existing Station prerequisite versions differ from the validated pins or approved factory transition; refusing to change them automatically" + ((mismatch == 0)) || fatal "Existing Station prerequisite versions differ from the validated pins, retained-compatible versions, or approved factory transition; refusing to change them automatically" } -all_packages_exact() { +package_is_ready() { + local state + state="$(package_state "$1")" + [[ "$state" == "exact" || "$state" == "retained-compatible" ]] +} + +all_packages_ready() { local spec for spec in "${PACKAGE_SPECS[@]}"; do - package_is_exact "$spec" || return 1 + package_is_ready "$spec" || return 1 done return 0 } @@ -887,7 +913,7 @@ check_failed_units() { return 0 fi for unit in "${units[@]}"; do - if is_driver_transitional_unit "$unit" && all_packages_exact && ! driver_loaded_exact; then + if is_driver_transitional_unit "$unit" && all_packages_ready && ! driver_loaded_exact; then warn "driver unit failure allowed only until post-reboot verification: ${unit}" elif is_preparation_critical_unit "$unit"; then warn "failed preparation-critical unit: ${unit}" @@ -1040,6 +1066,8 @@ print_package_status() { info "package=${name} status=missing expected=${expected}" elif [[ "$name" == "dkms" && "$actual" == "$FACTORY_DKMS_VERSION" ]]; then info "package=${name} status=approved_transition actual=${actual} expected=${expected}" + elif [[ "$name" == "dkms" && "$actual" == "$RETAINED_DKMS_VERSION" ]]; then + warn_retained_package_version "$spec" else warn "package=${name} status=mismatch actual=${actual} expected=${expected}" fi @@ -1251,7 +1279,7 @@ collect_package_transaction_specs() { state="$(package_state "$spec")" case "$state" in missing | approved-transition) PACKAGE_TRANSACTION_SPECS+=("$spec") ;; - exact) ;; + exact | retained-compatible) ;; *) fatal "Package transaction contains an unapproved prerequisite state: ${spec} (${state})" ;; esac done @@ -1528,9 +1556,9 @@ install_packages() { local spec for spec in "${PACKAGE_SPECS[@]}"; do - package_is_exact "$spec" || fatal "Installed package does not match ${spec}" + package_is_ready "$spec" || fatal "Installed package is outside the accepted state for ${spec}" done - info "pinned_packages=installed" + info "prerequisite_packages=ready" } ensure_docker_group() { @@ -1860,7 +1888,7 @@ verify_dgx_os_runtime_user() { verify_apply_state() { local spec for spec in "${PACKAGE_SPECS[@]}"; do - package_is_exact "$spec" || fatal "Package verification failed: ${spec}" + package_is_ready "$spec" || fatal "Package verification failed: ${spec}" done verify_gpu systemctl is-active --quiet nvidia-persistenced.service || fatal "nvidia-persistenced.service is not active" @@ -1928,7 +1956,7 @@ verify_gpu() { verify_host() { local spec user_name=${SUDO_USER:-$USER} for spec in "${PACKAGE_SPECS[@]}"; do - package_is_exact "$spec" || fatal "Package verification failed: ${spec}" + package_is_ready "$spec" || fatal "Package verification failed: ${spec}" done verify_gpu systemctl is-active --quiet nvidia-persistenced.service || fatal "nvidia-persistenced.service is not active" @@ -1954,14 +1982,14 @@ run_check() { return 0 fi print_package_status - if all_packages_exact; then + if all_packages_ready; then if install_boot_marker_matches_current_boot; then warn "Package installation completed in the current boot; reboot is required" info "CHECK_RESULT=REBOOT_REQUIRED" elif driver_loaded_exact; then info "CHECK_RESULT=PACKAGES_AND_DRIVER_PRESENT" else - warn "Exact packages are installed but driver ${DRIVER_VERSION} is not loaded; reboot is required" + warn "Accepted prerequisite packages are installed but driver ${DRIVER_VERSION} is not loaded; reboot is required" info "CHECK_RESULT=REBOOT_REQUIRED" fi else @@ -2003,16 +2031,17 @@ run_apply() { require_command grep require_command readlink require_command sha256sum + warn_retained_package_versions if reboot_required; then - if all_packages_exact && ! driver_loaded_exact; then + if all_packages_ready && ! driver_loaded_exact; then warn "A reboot is required before runtime setup can continue" exit_reboot_required fi fatal "An unrelated reboot is already pending" fi - if ! all_packages_exact; then + if ! all_packages_ready; then assert_no_package_mismatches install_packages ensure_docker_group @@ -2029,7 +2058,7 @@ run_apply() { fi driver_loaded_exact || { - warn "Pinned packages are installed but driver ${DRIVER_VERSION} is not loaded" + warn "Accepted prerequisite packages are installed but driver ${DRIVER_VERSION} is not loaded" exit_reboot_required } @@ -2055,7 +2084,8 @@ run_verify() { verify_dgx_os_runtime_user return 0 fi - all_packages_exact || fatal "Pinned prerequisite packages are incomplete; run --apply" + warn_retained_package_versions + all_packages_ready || fatal "Accepted prerequisite packages are incomplete; run --apply" driver_loaded_exact || fatal "Pinned driver is not loaded; reboot, then run --apply" verify_host } diff --git a/test/install-station-container-coexistence.test.ts b/test/install-station-container-coexistence.test.ts index 812d8f5558f..dd055bd9ac3 100644 --- a/test/install-station-container-coexistence.test.ts +++ b/test/install-station-container-coexistence.test.ts @@ -110,7 +110,7 @@ check_package_managers_idle() { :; } check_failed_units() { :; } check_agent_and_inference_conflicts() { :; } driver_loaded_exact() { return 0; } -package_is_exact() { return 0; } +package_is_ready() { return 0; } verify_gpu() { :; } systemctl() { case "$*" in @@ -185,7 +185,7 @@ require_docker_mutation_quiescence "refreshing NVIDIA CDI configuration" name: "pending prerequisite", setup: ` reboot_required() { return 0; } -all_packages_exact() { return 0; } +all_packages_ready() { return 0; } driver_loaded_exact() { return 1; } `, expectedGate: "REBOOT_HANDOFF_BLOCKED check=1", @@ -194,7 +194,7 @@ driver_loaded_exact() { return 1; } name: "post-install", setup: ` reboot_required() { return 1; } -all_packages_exact() { return 1; } +all_packages_ready() { return 1; } require_docker_restart_quiescence() { local checks checks="$(cat "$HOME/reboot-gate-checks" 2>/dev/null || printf '0')" @@ -211,7 +211,7 @@ require_docker_restart_quiescence() { name: "same-boot marker", setup: ` reboot_required() { return 1; } -all_packages_exact() { return 0; } +all_packages_ready() { return 0; } install_boot_marker_matches_current_boot() { return 0; } `, expectedGate: "REBOOT_HANDOFF_BLOCKED check=1", @@ -220,7 +220,7 @@ install_boot_marker_matches_current_boot() { return 0; } name: "unloaded driver", setup: ` reboot_required() { return 1; } -all_packages_exact() { return 0; } +all_packages_ready() { return 0; } install_boot_marker_matches_current_boot() { return 1; } driver_loaded_exact() { return 1; } `, @@ -230,7 +230,7 @@ driver_loaded_exact() { return 1; } name: "Docker group", setup: ` reboot_required() { return 1; } -all_packages_exact() { return 0; } +all_packages_ready() { return 0; } install_boot_marker_matches_current_boot() { return 1; } driver_loaded_exact() { return 0; } finish_runtime() { DOCKER_GROUP_ADDED=1; } diff --git a/test/install-station-host-preparation.test.ts b/test/install-station-host-preparation.test.ts index 592a75a151a..691e82c2fb8 100644 --- a/test/install-station-host-preparation.test.ts +++ b/test/install-station-host-preparation.test.ts @@ -360,7 +360,7 @@ check_failed_units common_preflight() { :; } require_command() { :; } acquire_sudo() { :; } -all_packages_exact() { return 0; } +all_packages_ready() { return 0; } install_boot_marker_matches_current_boot() { return 1; } driver_loaded_exact() { return 0; } install_packages() { printf 'INSTALL_PACKAGES\n'; } @@ -384,7 +384,7 @@ run_apply common_preflight() { :; } require_command() { :; } acquire_sudo() { :; } -all_packages_exact() { return 1; } +all_packages_ready() { return 1; } installed_version() { if [[ "$1" == "dkms" ]]; then printf '3.0.11-1ubuntu13'; fi } @@ -418,7 +418,7 @@ validate_package_availability() { printf 'VALIDATE_PACKAGES\n'; } simulate_install() { printf 'SIMULATE_INSTALL\n'; } require_docker_restart_quiescence() { printf 'RECHECK_RESTART_QUIESCENCE\n'; } package_state() { printf 'missing\n'; } -package_is_exact() { return 0; } +package_is_ready() { return 0; } create_apt_transaction_guard() { APT_TRANSACTION_GUARD_DIR=/run/nemoclaw-apt-transaction.TEST APT_TRANSACTION_HOOK="/bin/bash $APT_TRANSACTION_GUARD_DIR/verify-plan" @@ -441,7 +441,7 @@ install_packages ]) { expect(output).toContain(spec); } - expect(output).toContain("pinned_packages=installed"); + expect(output).toContain("prerequisite_packages=ready"); }); it("does not refresh CDI when the GPU launch probe already passes", () => { const { result, output } = runSourced( @@ -622,7 +622,7 @@ assert_root_directory_safe /etc/apt/keyrings test_directory common_preflight() { :; } require_command() { :; } acquire_sudo() { :; } -all_packages_exact() { return 0; } +all_packages_ready() { return 0; } install_boot_marker_matches_current_boot() { return 1; } driver_loaded_exact() { return 0; } finish_runtime() { DOCKER_GROUP_ADDED=1; printf 'FINISH_RUNTIME\n'; } @@ -894,7 +894,7 @@ main "$READ_MODE" ` common_preflight() { :; } require_command() { :; } -all_packages_exact() { return 0; } +all_packages_ready() { return 0; } driver_loaded_exact() { return 1; } run_verify `, diff --git a/test/install-station-package-transaction.test.ts b/test/install-station-package-transaction.test.ts index 94786bf1e8d..094aaa4ff7b 100644 --- a/test/install-station-package-transaction.test.ts +++ b/test/install-station-package-transaction.test.ts @@ -72,6 +72,53 @@ validate_apt_preinstall_plan "$HOME/targets" <<<"$APT_PLAN" } describe("DGX Station package transaction", () => { + it("warns and retains the qualified DKMS forward revision (#7211)", () => { + const { result, output } = runSourced( + ` +installed_version() { + if [[ "$1" == "dkms" ]]; then printf '%s' "$DKMS_ACTUAL"; fi +} +printf 'state=' +package_state 'dkms=1:3.4.0-1ubuntu1' +package_is_ready 'dkms=1:3.4.0-1ubuntu1' +warn_retained_package_version 'dkms=1:3.4.0-1ubuntu1' +`, + { DKMS_ACTUAL: "1:3.4.1-1ubuntu1" }, + ); + + expect(result.status, output).toBe(0); + expect(output).toContain("state=retained-compatible"); + expect(output).toContain( + "package=dkms status=retained_compatible actual=1:3.4.1-1ubuntu1 validated=1:3.4.0-1ubuntu1 decision=retain", + ); + }); + + it("retains qualified DKMS without entering package installation (#7211)", () => { + const { result, output } = runSourced(` +common_preflight() { :; } +require_command() { :; } +acquire_sudo() { :; } +package_state() { + if [[ "$1" == dkms=* ]]; then printf 'retained-compatible\n'; else printf 'exact\n'; fi +} +installed_version() { + if [[ "$1" == "dkms" ]]; then printf '1:3.4.1-1ubuntu1'; fi +} +install_boot_marker_matches_current_boot() { return 1; } +driver_loaded_exact() { return 0; } +install_packages() { printf 'INSTALL_PACKAGES\n'; } +finish_runtime() { printf 'FINISH_RUNTIME\n'; } +verify_apply_state() { printf 'VERIFY_APPLY_STATE\n'; } +run_apply +`); + + expect(result.status, output).toBe(0); + expect(output).toContain("status=retained_compatible"); + expect(output).toContain("decision=retain"); + expect(output).not.toContain("INSTALL_PACKAGES"); + expect(output).toContain("APPLY_RESULT=COMPLETE"); + }); + it("passes the complete pinned tuple when every package is missing", () => { const { result, output } = runSourced(` configure_repositories() { printf 'CONFIGURE_REPOSITORIES\n'; } @@ -91,7 +138,7 @@ apt-get() { } require_docker_restart_quiescence() { printf 'RECHECK_DOCKER_RESTART %s\n' "$1"; } package_state() { printf 'missing\n'; } -package_is_exact() { return 0; } +package_is_ready() { return 0; } create_apt_transaction_guard() { APT_TRANSACTION_GUARD_DIR=/run/nemoclaw-apt-transaction.TEST APT_TRANSACTION_HOOK="/bin/bash $APT_TRANSACTION_GUARD_DIR/verify-plan" @@ -135,11 +182,24 @@ cat "$HOME/apt-cache-calls" expect(output).toContain(quiescenceMarker); expect(output.indexOf(installMarker)).toBeGreaterThan(output.indexOf(quiescenceMarker)); expect(output).toContain("CLEANUP_GUARD"); - expect(output).toContain("pinned_packages=installed"); + expect(output).toContain("prerequisite_packages=ready"); }); - it("excludes retained exact packages from every APT transaction command", () => { - const retainedSpec = "docker-ce=5:29.6.1-1~ubuntu.24.04~noble"; + it.each([ + { + label: "exact", + retainedSpec: "docker-ce=5:29.6.1-1~ubuntu.24.04~noble", + retainedState: "exact", + }, + { + label: "qualified", + retainedSpec: DKMS_SPEC, + retainedState: "retained-compatible", + }, + ])("excludes $label retained packages from every APT transaction command (#7211)", ({ + retainedSpec, + retainedState, + }) => { const missingSpecs = EXPECTED_PACKAGE_SPECS.filter((spec) => spec !== retainedSpec); const { result, output } = runSourced(` configure_repositories() { :; } @@ -159,9 +219,9 @@ apt-get() { } require_docker_restart_quiescence() { :; } package_state() { - if [[ "$1" == '${retainedSpec}' ]]; then printf 'exact\n'; else printf 'missing\n'; fi + if [[ "$1" == '${retainedSpec}' ]]; then printf '${retainedState}\n'; else printf 'missing\n'; fi } -package_is_exact() { return 0; } +package_is_ready() { return 0; } create_apt_transaction_guard() { APT_TRANSACTION_GUARD_DIR=/run/nemoclaw-apt-transaction.TEST APT_TRANSACTION_HOOK="/bin/bash $APT_TRANSACTION_GUARD_DIR/verify-plan" @@ -214,7 +274,7 @@ package_state() { if [[ "$1" == docker-ce=* ]]; then printf 'missing\n'; else printf 'exact\n'; fi } installed_version() { if [[ "$1" == "libc6" ]]; then printf '2.39-0ubuntu8'; fi; } -package_is_exact() { return 0; } +package_is_ready() { return 0; } create_apt_transaction_guard() { APT_TRANSACTION_GUARD_DIR=/run/nemoclaw-apt-transaction.TEST APT_TRANSACTION_HOOK="/bin/bash $APT_TRANSACTION_GUARD_DIR/verify-plan" From 8eac70c939f36454f6429dbf2b070b555bd685d1 Mon Sep 17 00:00:00 2001 From: San Dang Date: Mon, 20 Jul 2026 12:03:54 +0700 Subject: [PATCH 2/4] fix(installer): bind retained DKMS policy to package tuple Signed-off-by: San Dang --- docs/get-started/dgx-station-preparation.mdx | 3 ++ scripts/prepare-dgx-station-host.sh | 43 +++++++++++++++++-- ...nstall-station-package-transaction.test.ts | 2 +- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/docs/get-started/dgx-station-preparation.mdx b/docs/get-started/dgx-station-preparation.mdx index 0345879aefc..72775b72782 100644 --- a/docs/get-started/dgx-station-preparation.mdx +++ b/docs/get-started/dgx-station-preparation.mdx @@ -55,6 +55,9 @@ On the generic Ubuntu path, accepting express install prepares the host with NVI Preparation probes package and runtime state first, reuses exact matches, and installs only missing pinned packages, including the NVIDIA Container Toolkit libraries and `nvidia-ctk` CLI. It permits only the reviewed factory transition from `dkms` `3.0.11-1ubuntu13` to `1:3.4.0-1ubuntu1`. It also accepts an installed `dkms` `1:3.4.1-1ubuntu1` as a retained-compatible forward revision, warns that it differs from the validated `1:3.4.0-1ubuntu1` pin, excludes it from APT transactions, and continues runtime validation. +NemoClaw DGX Station maintainers own this retained-version allowlist and qualify it against the complete generic-Ubuntu package tuple. +Any package pin change invalidates retention until maintainers requalify the tuple. +Maintainers remove a retained revision when runtime validation no longer passes. After reboot, preparation enables NVIDIA's packaged CDI refresh path and service, requires the `nvidia.com/gpu=all` device, and verifies it with a real container launch. If the packaged refresh fails or does not produce that device, preparation prints service diagnostics and stops for administrator repair. diff --git a/scripts/prepare-dgx-station-host.sh b/scripts/prepare-dgx-station-host.sh index 5b88cb05ee9..d15303df183 100755 --- a/scripts/prepare-dgx-station-host.sh +++ b/scripts/prepare-dgx-station-host.sh @@ -35,7 +35,13 @@ readonly DOCKER_VERSION="29.6.1" readonly TOOLKIT_VERSION="1.19.1" readonly FACTORY_DKMS_VERSION="3.0.11-1ubuntu13" readonly TARGET_DKMS_VERSION="1:3.4.0-1ubuntu1" -readonly RETAINED_DKMS_VERSION="1:3.4.1-1ubuntu1" +# NemoClaw DGX Station maintainers own this allowlist. The qualified tuple below +# binds each retained revision to the complete generic-Ubuntu package contract. +# Update both only after requalification; remove an entry when runtime +# validation no longer passes. Any PACKAGE_SPECS change invalidates retention. +readonly -a RETAINED_DKMS_VERSIONS=( + "1:3.4.1-1ubuntu1" +) # Keep this as a plain Ubuntu image: NVIDIA Container Toolkit injects the host # driver utility when CDI or --gpus is requested. This intentionally exercises # the documented runtime contract instead of relying on a CUDA image payload: @@ -63,6 +69,20 @@ readonly -a PACKAGE_SPECS=( "nvidia-container-toolkit-base=1.19.1-1" ) +readonly -a RETAINED_DKMS_QUALIFIED_PACKAGE_SPECS=( + "dkms=1:3.4.0-1ubuntu1" + "nvidia-driver-pinning-610=610-2ubuntu1" + "nvidia-driver-open=610.43.02-1ubuntu1" + "containerd.io=2.2.6-1~ubuntu.24.04~noble" + "docker-buildx-plugin=0.35.0-1~ubuntu.24.04~noble" + "docker-ce=5:29.6.1-1~ubuntu.24.04~noble" + "docker-ce-cli=5:29.6.1-1~ubuntu.24.04~noble" + "libnvidia-container-tools=1.19.1-1" + "libnvidia-container1=1.19.1-1" + "nvidia-container-toolkit=1.19.1-1" + "nvidia-container-toolkit-base=1.19.1-1" +) + readonly -a BASEOS_PACKAGE_SPECS=( "dgx-release=7.5.0" "dgx-repo=25.10-2" @@ -557,6 +577,23 @@ package_is_exact() { [[ "$actual" == "$expected" ]] } +retained_dkms_policy_is_current() { + local index + ((${#PACKAGE_SPECS[@]} == ${#RETAINED_DKMS_QUALIFIED_PACKAGE_SPECS[@]})) || return 1 + for index in "${!PACKAGE_SPECS[@]}"; do + [[ "${PACKAGE_SPECS[$index]}" == "${RETAINED_DKMS_QUALIFIED_PACKAGE_SPECS[$index]}" ]] || return 1 + done +} + +dkms_version_is_retained() { + local actual=$1 retained + retained_dkms_policy_is_current || return 1 + for retained in "${RETAINED_DKMS_VERSIONS[@]}"; do + [[ "$actual" == "$retained" ]] && return 0 + done + return 1 +} + package_state() { local spec=$1 local name expected actual @@ -567,7 +604,7 @@ package_state() { printf 'missing\n' elif [[ "$actual" == "$expected" ]]; then printf 'exact\n' - elif [[ "$name" == "dkms" && "$actual" == "$RETAINED_DKMS_VERSION" && "$expected" == "$TARGET_DKMS_VERSION" ]]; then + elif [[ "$name" == "dkms" && "$expected" == "$TARGET_DKMS_VERSION" ]] && dkms_version_is_retained "$actual"; then printf 'retained-compatible\n' elif [[ "$name" == "dkms" && "$actual" == "$FACTORY_DKMS_VERSION" && "$expected" == "$TARGET_DKMS_VERSION" ]]; then printf 'approved-transition\n' @@ -1066,7 +1103,7 @@ print_package_status() { info "package=${name} status=missing expected=${expected}" elif [[ "$name" == "dkms" && "$actual" == "$FACTORY_DKMS_VERSION" ]]; then info "package=${name} status=approved_transition actual=${actual} expected=${expected}" - elif [[ "$name" == "dkms" && "$actual" == "$RETAINED_DKMS_VERSION" ]]; then + elif [[ "$name" == "dkms" && "$expected" == "$TARGET_DKMS_VERSION" ]] && dkms_version_is_retained "$actual"; then warn_retained_package_version "$spec" else warn "package=${name} status=mismatch actual=${actual} expected=${expected}" diff --git a/test/install-station-package-transaction.test.ts b/test/install-station-package-transaction.test.ts index 094aaa4ff7b..461f092183a 100644 --- a/test/install-station-package-transaction.test.ts +++ b/test/install-station-package-transaction.test.ts @@ -72,7 +72,7 @@ validate_apt_preinstall_plan "$HOME/targets" <<<"$APT_PLAN" } describe("DGX Station package transaction", () => { - it("warns and retains the qualified DKMS forward revision (#7211)", () => { + it("warns and retains the qualified DKMS forward revision for its package tuple (#7211)", () => { const { result, output } = runSourced( ` installed_version() { From 70b374ac03d9d08e92e564e89a26266398daea52 Mon Sep 17 00:00:00 2001 From: San Dang Date: Mon, 20 Jul 2026 12:11:11 +0700 Subject: [PATCH 3/4] test(installer): cover retained DKMS rejection --- ...nstall-station-package-transaction.test.ts | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/test/install-station-package-transaction.test.ts b/test/install-station-package-transaction.test.ts index 461f092183a..77d91cb13e7 100644 --- a/test/install-station-package-transaction.test.ts +++ b/test/install-station-package-transaction.test.ts @@ -26,7 +26,11 @@ const EXPECTED_PACKAGE_SPECS = [ const DOCKER_CE_SPEC = "docker-ce=5:29.6.1-1~ubuntu.24.04~noble"; const DKMS_SPEC = "dkms=1:3.4.0-1ubuntu1"; -function runSourced(body: string, extraEnv: NodeJS.ProcessEnv = {}) { +function runSourced( + body: string, + extraEnv: NodeJS.ProcessEnv = {}, + scriptUnderTest = STATION_PREPARE, +) { const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-station-package-transaction-")); const result = spawnSync( "bash", @@ -37,7 +41,7 @@ function runSourced(body: string, extraEnv: NodeJS.ProcessEnv = {}) { env: { HOME: home, PATH: TEST_SYSTEM_PATH, - SCRIPT_UNDER_TEST: STATION_PREPARE, + SCRIPT_UNDER_TEST: scriptUnderTest, ...extraEnv, }, timeout: 15_000, @@ -93,6 +97,67 @@ warn_retained_package_version 'dkms=1:3.4.0-1ubuntu1' ); }); + it("rejects an unlisted DKMS revision (#7211)", () => { + const { result, output } = runSourced( + ` +installed_version() { + if [[ "$1" == "dkms" ]]; then printf '%s' "$DKMS_ACTUAL"; fi +} +printf 'state=' +package_state 'dkms=1:3.4.0-1ubuntu1' +if package_is_ready 'dkms=1:3.4.0-1ubuntu1'; then + printf 'ready=yes\n' +else + printf 'ready=no\n' +fi +`, + { DKMS_ACTUAL: "1:3.4.2-1ubuntu1" }, + ); + + expect(result.status, output).toBe(0); + expect(output).toContain("state=mismatch"); + expect(output).toContain("ready=no"); + }); + + it("rejects retained DKMS after a companion package pin changes (#7211)", () => { + const source = fs.readFileSync(STATION_PREPARE, "utf-8"); + const qualifiedTuple = `readonly -a RETAINED_DKMS_QUALIFIED_PACKAGE_SPECS=(\n${EXPECTED_PACKAGE_SPECS.map((spec) => ` "${spec}"`).join("\n")}\n)`; + const staleQualifiedTuple = qualifiedTuple.replace( + DOCKER_CE_SPEC, + "docker-ce=5:29.6.0-1~ubuntu.24.04~noble", + ); + const stalePolicySource = source.replace(qualifiedTuple, staleQualifiedTuple); + expect(stalePolicySource).not.toBe(source); + + const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-station-stale-policy-")); + const stalePolicyScript = path.join(fixtureDir, "prepare-dgx-station-host.sh"); + fs.writeFileSync(stalePolicyScript, stalePolicySource); + try { + const { result, output } = runSourced( + ` +installed_version() { + if [[ "$1" == "dkms" ]]; then printf '%s' "$DKMS_ACTUAL"; fi +} +printf 'state=' +package_state 'dkms=1:3.4.0-1ubuntu1' +if package_is_ready 'dkms=1:3.4.0-1ubuntu1'; then + printf 'ready=yes\n' +else + printf 'ready=no\n' +fi +`, + { DKMS_ACTUAL: "1:3.4.1-1ubuntu1" }, + stalePolicyScript, + ); + + expect(result.status, output).toBe(0); + expect(output).toContain("state=mismatch"); + expect(output).toContain("ready=no"); + } finally { + fs.rmSync(fixtureDir, { recursive: true, force: true }); + } + }); + it("retains qualified DKMS without entering package installation (#7211)", () => { const { result, output } = runSourced(` common_preflight() { :; } From 404f68a74553b92dddfce9a47da17939aead5e32 Mon Sep 17 00:00:00 2001 From: San Dang Date: Mon, 20 Jul 2026 12:27:34 +0700 Subject: [PATCH 4/4] docs(installer): clarify retained DKMS tuple gate --- docs/get-started/dgx-station-preparation.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/get-started/dgx-station-preparation.mdx b/docs/get-started/dgx-station-preparation.mdx index 72775b72782..40b2ef6d3ab 100644 --- a/docs/get-started/dgx-station-preparation.mdx +++ b/docs/get-started/dgx-station-preparation.mdx @@ -55,7 +55,8 @@ On the generic Ubuntu path, accepting express install prepares the host with NVI Preparation probes package and runtime state first, reuses exact matches, and installs only missing pinned packages, including the NVIDIA Container Toolkit libraries and `nvidia-ctk` CLI. It permits only the reviewed factory transition from `dkms` `3.0.11-1ubuntu13` to `1:3.4.0-1ubuntu1`. It also accepts an installed `dkms` `1:3.4.1-1ubuntu1` as a retained-compatible forward revision, warns that it differs from the validated `1:3.4.0-1ubuntu1` pin, excludes it from APT transactions, and continues runtime validation. -NemoClaw DGX Station maintainers own this retained-version allowlist and qualify it against the complete generic-Ubuntu package tuple. +The installer accepts the retained revision only when every package pin in the active generic-Ubuntu tuple matches its corresponding value in the qualified tuple. +NemoClaw DGX Station maintainers own this retained-version allowlist. Any package pin change invalidates retention until maintainers requalify the tuple. Maintainers remove a retained revision when runtime validation no longer passes.