@@ -63,11 +63,13 @@ TRAINJOB_NAME="${TRAINJOB_NAME:-pytorch-mnist}"
6363TRAINJOB_IMAGE=" ${TRAINJOB_IMAGE:- kubeflow/ pytorch-dist-mnist: v1-9e12c68} "
6464TRAINJOB_TIMEOUT_SECONDS=" ${TRAINJOB_TIMEOUT_SECONDS:- 1200} " # 20 min
6565HELMFILE_TIMEOUT_SECONDS=" ${HELMFILE_TIMEOUT_SECONDS:- 1200} " # 20 min
66- # Convergence budget for the post-install readiness gate. Cold-boot p5.48xlarge
66+ # Convergence budget for the post-install readiness gate. Spans nodewright
67+ # (skyhook) node tuning -- which REBOOTS the GPU node and re-inits the operator
68+ # stack after gpu-operator first reports ready -- plus cold-boot p5.48xlarge
6769# GPU-operator convergence (driver build/load -> toolkit -> device-plugin ->
68- # DCGM exporter) plus prometheus-adapter APIService aggregation can take many
69- # minutes, so this is generous; the gate fails closed if exceeded.
70- READINESS_TIMEOUT_SECONDS=" ${READINESS_TIMEOUT_SECONDS:- 900 } " # 15 min
70+ # DCGM exporter) and prometheus-adapter APIService aggregation. Generous; the
71+ # gate fails closed if exceeded.
72+ READINESS_TIMEOUT_SECONDS=" ${READINESS_TIMEOUT_SECONDS:- 1200 } " # 20 min
7173
7274# wait_for polls <check-cmd...> every 10s until it succeeds (exit 0) or
7375# <timeout-seconds> elapses. Fail-closed: returns non-zero with diagnostics on
@@ -106,6 +108,21 @@ _apiservice_available() {
106108 [[ " $( kubectl get apiservice " $1 " \
107109 -o jsonpath=' {.status.conditions[?(@.type=="Available")].status}' 2> /dev/null) " == " True" ]]
108110}
111+ # _skyhook_complete is true when every nodewright (Skyhook) CR reports
112+ # status.status=complete. Skyhook node tuning REBOOTS the GPU node, which
113+ # re-initializes the gpu-operator operands AFTER ClusterPolicy first reports
114+ # ready — so this must clear before the GPU/metrics checks, else the gate passes
115+ # on a transient pre-reboot ready state and validate races the re-convergence.
116+ # Empty list (CR not created yet) => not complete => keep waiting. If the Skyhook
117+ # CRD is absent the recipe has no nodewright, so skip rather than block. Skyhook
118+ # CRs are cluster-scoped (see validators/deployment/expected_resources.go).
119+ _skyhook_complete () {
120+ kubectl get crd skyhooks.skyhook.nvidia.com > /dev/null 2>&1 || return 0
121+ local states
122+ states=" $( kubectl get skyhooks.skyhook.nvidia.com \
123+ -o jsonpath=' {range .items[*]}{.status.status}{"\n"}{end}' 2> /dev/null) "
124+ [[ -n " $states " ]] && ! grep -qxv ' complete' <<< " $states"
125+ }
109126
110127# Per-run unique OCI repo suffix. The committed config carries the stable
111128# prefix only; we append `-${RUN_ID}` once, idempotently, so re-running a
@@ -223,10 +240,18 @@ phase_install() {
223240 # conformance-phase checks evaluate a ready cluster. ClusterPolicy=ready implies
224241 # the DCGM exporter operand is rolled out; the two aggregated APIServices imply
225242 # prometheus-adapter is serving custom/external metrics.
226- # All three checks share a SINGLE READINESS_TIMEOUT_SECONDS budget: compute one
227- # deadline up front and pass each call the time remaining until it, so the
243+ # All readiness checks share a SINGLE READINESS_TIMEOUT_SECONDS budget: compute
244+ # one deadline up front and pass each call the time remaining until it, so the
228245 # combined barrier cannot run N x the per-check timeout.
246+ #
247+ # nodewright (skyhook) tuning is waited on FIRST: it reboots the GPU node to
248+ # apply kernel tuning, re-initializing the gpu-operator operands after the
249+ # operator first reports ClusterPolicy=ready. Checking ClusterPolicy/operands
250+ # before that reboot lets the gate pass on a transient pre-reboot ready state,
251+ # so wait for tuning to complete before the GPU/metrics checks.
229252 local ready_deadline=$(( SECONDS + READINESS_TIMEOUT_SECONDS ))
253+ wait_for " nodewright (skyhook) tuning complete" " $(( ready_deadline - SECONDS )) " \
254+ _skyhook_complete
230255 wait_for " gpu-operator ClusterPolicy state=ready" " $(( ready_deadline - SECONDS )) " \
231256 _clusterpolicy_ready
232257 wait_for " custom.metrics.k8s.io APIService Available" " $(( ready_deadline - SECONDS )) " \
0 commit comments