Skip to content

feat(certification): support KAI gang scheduling - #303

Merged
lalitadithya merged 2 commits into
mainfrom
feat/300-certification-gang-scheduler
Sep 4, 2026
Merged

lalitadithya merged 2 commits into
mainfrom
feat/300-certification-gang-scheduler

Conversation

@ndipebot

@ndipebot ndipebot commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

WorkloadRun has supported spec.gangScheduler for a while. Certification had no equivalent, so there was no way to put certification workloads on KAI Scheduler. Multi-node certification jobs could get partially scheduled instead of admitted as a gang, and the placed pods sit on GPUs waiting for peers that never arrive.

This adds the same field to CertificationSpec, reusing the existing GangSchedulerSpec type rather than inventing a second scheduler config shape:

spec:
  gangScheduler:
    schedulerName: kai-scheduler
    queue: high-priority

It is certification wide and applies to every entry in spec.categories.

What it does

For each category, every replicatedJob in the resolved TrainingRuntime gets:

  • schedulerName on the pod spec, at replicatedJobs[].template.spec.template.spec
  • the kai.scheduler/queue label at replicatedJobs[].template.metadata.labels

Those are the same two locations BuildTorchRuntime and BuildMPIRuntime already write for a WorkloadRun, so the two paths stay consistent. An omitted queue defaults to default-queue. MPI categories get both the node and the launcher job.

The rewrite runs after catalog and platform overrides resolve, so an override cannot put default-scheduler back. That ordering matters: training/nemotron5-8b and training/nemotron5-56b both hardcode schedulerName: default-scheduler, and they now pick up the configured scheduler instead.

nvcrectl certification render applies the same rewrite in both the dry-run and the offline branch, so what you inspect matches what the controller creates.

One thing worth calling out

The helper treats an empty schedulerName as "not configured", the same way applyGangScheduler does on the WorkloadRun path. The CRD requires a non-empty value, but nvcrectl render reads a file and never talks to the API server, so without that guard a typo like this:

gangScheduler:
  queue: high-priority

renders every pod template with schedulerName: "" plus a KAI queue label, exits 0, and says nothing. The manifest looks configured but runs on the default scheduler. Found while reviewing this branch, fixed here, and there is a test case that fails without the guard.

Also renamed the local platform variable in the certification controller to detectedPlatform. It was shadowing the pkg/platform package import. workloadrun_controller.go already calls it that.

Verification

  • make build and make test are green. 25 packages, no failures.
  • Certifications without gangScheduler render byte identically to a binary built from main, checked across GB200, H100 and GB300 on aws, gcp, azure and oci.
  • All 36 pre-existing certification reconcile goldens are unchanged, and so is every other pre-existing golden.
  • make lint does not run locally here. bin/golangci-lint is built with go1.26 and the repo targets go 1.27.0. Pristine main fails the same way, so it is not from this change, but it does mean CI is the first real lint check.

No ADR. The original WorkloadRun gang scheduling shipped without one and this reuses that contract rather than deciding anything new.

Related Issue

Closes #300

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 📚 Documentation
  • 🔧 Refactoring
  • 🔨 Build/CI

Component(s) Affected

  • API / CRDs
  • Controller / Reconcilers
  • Catalog / Workloads
  • CLI (nvcrectl)
  • Helm / Deployment
  • Documentation / CI
  • Other: ____________

Testing

  • Tests pass locally
  • Manual testing completed
  • No breaking changes (or documented)

12 new cases, all following the testutil.TestCaseParser and golden file convention:

  • pkg/platform/testdata/apply-gang-scheduler-deps/ (9): torch and MPI shapes, creating a missing labels map, preserving the launcher's existing labels, queue defaulting, overwriting a hardcoded scheduler, non-TrainingRuntime dependencies untouched, malformed shapes skipped, and the empty scheduler name no-op.
  • pkg/certification/testdata/certification-render-gang-scheduler/ (4): render path with an explicit queue, the nemotron default-scheduler replacement, queue defaulting, and gangScheduler omitted. The offline branch of runCertificationRender was extracted into resolveWorkflowsOffline so the test drives the real code path instead of a copy of it.
  • cmd/integration/testdata/validation/certification-gangscheduler-* (6): CRD admission for valid input, empty and omitted schedulerName, a queue failing the label value pattern, a queue over 63 chars, and the field absent.
  • cmd/integration/testdata/reconcile/certification-gangscheduler-{nccl,training} (2): end to end through the controller, covering both MPI jobs and the default-scheduler replacement.

Manual check with nvcrectl certification render --platform aws on a cert with communication/nccl-all-reduce plus training/nemotron5-8b: 3 pod specs get kai-scheduler and 3 jobs get the queue label. Dropping queue gives default-queue. Dropping gangScheduler gives byte identical output to main.

Checklist

  • Self-review completed
  • Commits are signed off for the DCO (git commit -s)
  • make manifests generate run (if *_types.go was modified)
  • Golden files updated (if integration test output changed)
  • Documentation updated (if needed)
  • Ready for review

Certification had no way to select a gang-aware scheduler, so multi-node
certification jobs could be partially scheduled instead of admitted as a
gang. WorkloadRun already exposes spec.gangScheduler; this reuses the same
GangSchedulerSpec on CertificationSpec rather than adding a second shape.

When set, every category's resolved TrainingRuntime gets schedulerName on
each replicatedJob pod spec and the kai.scheduler/queue label on the job
template metadata, which is where the WorkloadRun path already writes them.
An omitted queue defaults to default-queue.

The rewrite runs after catalog and platform overrides resolve, so an
override cannot restore default-scheduler. training/nemotron5-8b and
nemotron5-56b hardcode schedulerName: default-scheduler and now pick up the
configured scheduler instead. nvcrectl certification render applies the same
rewrite in both the dry-run and offline branches, so rendered manifests match
what the controller creates.

An empty schedulerName is treated as "not configured", matching
applyGangScheduler on the WorkloadRun path. The CRD requires a non-empty
value, but nvcrectl renders straight from a file without consulting the API
server, so without the guard a typo would render pod templates pinned to an
empty scheduler while still carrying a queue label.

Certifications without gangScheduler render byte-identically to before,
verified against a binary built from main across GB200, H100 and GB300 on
aws, gcp, azure and oci.

Renames the local platform variable in the certification controller to
detectedPlatform so it stops shadowing the pkg/platform package import,
matching what workloadrun_controller.go already does.

Closes #300

Signed-off-by: Ebot Ndip-Agbor <endipagbor@nvidia.com>
@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 16cf4101-e96e-4e56-ac5e-bb8aa860ab48

📥 Commits

Reviewing files that changed from the base of the PR and between 9cfddbd and dc58e02.

⛔ Files ignored due to path filters (50)
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated.*.go
  • cmd/integration/testdata/reconcile/certification-gangscheduler-nccl/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/reconcile/certification-gangscheduler-nccl/input_client_objects.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/reconcile/certification-gangscheduler-nccl/input_config.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/reconcile/certification-gangscheduler-training/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/reconcile/certification-gangscheduler-training/input_client_objects.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/reconcile/certification-gangscheduler-training/input_config.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-absent/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-absent/input.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-accepted/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-accepted/input.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-empty-scheduler-name/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-empty-scheduler-name/input.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-queue-invalid-pattern/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-queue-invalid-pattern/input.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-queue-too-long/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-queue-too-long/input.yaml is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-scheduler-name-omitted/expected.json is excluded by !**/testdata/**
  • cmd/integration/testdata/validation/certification-gangscheduler-scheduler-name-omitted/input.yaml is excluded by !**/testdata/**
  • helm/cluster-readiness-engine/crds/nvcre.nvidia.com_certifications.yaml is excluded by !helm/cluster-readiness-engine/crds/**
  • pkg/certification/testdata/certification-render-gang-scheduler/gang-scheduler-omitted/expected.json is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/gang-scheduler-omitted/input.yaml is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/gang-scheduler-omitted/input_certification.yaml is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/nccl-explicit-queue/expected.json is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/nccl-explicit-queue/input.yaml is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/nccl-explicit-queue/input_certification.yaml is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/queue-omitted-defaults/expected.json is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/queue-omitted-defaults/input.yaml is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/queue-omitted-defaults/input_certification.yaml is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/training-replaces-default-scheduler/expected.json is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/training-replaces-default-scheduler/input.yaml is excluded by !**/testdata/**
  • pkg/certification/testdata/certification-render-gang-scheduler/training-replaces-default-scheduler/input_certification.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/empty-scheduler-name-is-a-no-op/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/empty-scheduler-name-is-a-no-op/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/hardcoded-scheduler-is-overwritten/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/hardcoded-scheduler-is-overwritten/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/malformed-shapes-are-skipped/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/malformed-shapes-are-skipped/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/mpi-launcher-keeps-existing-labels/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/mpi-launcher-keeps-existing-labels/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/no-gang-scheduler-is-a-no-op/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/no-gang-scheduler-is-a-no-op/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/non-training-runtime-untouched/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/non-training-runtime-untouched/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/queue-omitted-uses-default-queue/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/queue-omitted-uses-default-queue/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/torch-single-job-creates-labels-map/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/torch-single-job-creates-labels-map/input.yaml is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/training-runtime-without-replicated-jobs/expected.json is excluded by !**/testdata/**
  • pkg/platform/testdata/apply-gang-scheduler-deps/training-runtime-without-replicated-jobs/input.yaml is excluded by !**/testdata/**
📒 Files selected for processing (12)
  • api/v1alpha1/certification_types.go
  • cmd/integration/validation_test.go
  • docs/api-reference/certification.md
  • docs/cli-reference/certification.md
  • docs/how-to-guides/certify-a-cluster.md
  • pkg/certification/certification.go
  • pkg/certification/render_gang_scheduler_test.go
  • pkg/controller/certification_controller.go
  • pkg/platform/gang_scheduler.go
  • pkg/platform/gang_scheduler_test.go
  • pkg/platform/runtime.go
  • pkg/platform/runtime_test.go

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.


📝 Walkthrough

Walkthrough

Certification adds an optional GangScheduler field to CertificationSpec. The platform helper applies scheduler names and queue labels to TrainingRuntime replicated-job pod templates. Offline rendering and controller workflow creation apply the settings after resolving overrides. Tests cover configured and omitted scheduling, dependency preservation, malformed resources, queue defaults, and rendered output. Documentation describes configuration and behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to c46b0

The generated CRD already exposes the new gang scheduler configuration, so no actionable merge risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The reviewable changes satisfy the main requirements in issue #300, including the optional API field, scheduler and queue propagation, MPI coverage, override ordering, render consistency, omitted-fiel… Inspect the excluded generated artifacts, especially helm/cluster-readiness-engine/crds/nvcre.nvidia.com_certifications.yaml, and confirm that the Certification CRD exposes spec.gangScheduler with the expected schedulerName and queue schema…
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding KAI gang scheduling support to Certification.
Description check ✅ Passed The description directly explains the Certification gang-scheduling feature, implementation behavior, testing, and linked issue.
Out of Scope Changes check ✅ Passed The changes remain within issue #300. The controller variable rename, shared helper updates, documentation, rendering changes, validation tests, reconciliation tests, and platform tests support the re…
Docstring Coverage ✅ Passed Docstring coverage is 82.35% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 9 files. (3 skipped: 3 …
Full details: Linked Issues check

Explanation

The reviewable changes satisfy the main requirements in issue #300, including the optional API field, scheduler and queue propagation, MPI coverage, override ordering, render consistency, omitted-field behavior, and tests. The generated Helm CRD file and generated deepcopy file are excluded by path filters, so the final CRD artifact cannot be fully verified.

Resolution

Inspect the excluded generated artifacts, especially helm/cluster-readiness-engine/crds/nvcre.nvidia.com_certifications.yaml, and confirm that the Certification CRD exposes spec.gangScheduler with the expected schedulerName and queue schema. Also verify api/v1alpha1/zz_generated.deepcopy.go if required by the repository generation process.

Full details: Out of Scope Changes check

Explanation

The changes remain within issue #300. The controller variable rename, shared helper updates, documentation, rendering changes, validation tests, reconciliation tests, and platform tests support the requested Certification gang-scheduling behavior.

Full details: Docstring Coverage

Explanation

Docstring coverage is 82.35% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 9 files. (3 skipped: 3 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/300-certification-gang-scheduler

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

@lalitadithya
lalitadithya merged commit c460f14 into main Sep 4, 2026
14 checks passed
@ndipebot
ndipebot deleted the feat/300-certification-gang-scheduler branch September 10, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Support KAI gang scheduling for Certification

2 participants