Skip to content

feat(api): Replace PodTemplateOverrides with RuntimePatches API #3309

Merged
google-oss-prow[bot] merged 10 commits into
kubeflow:masterfrom
astefanutti:pr-runtime-patches
Mar 12, 2026
Merged

feat(api): Replace PodTemplateOverrides with RuntimePatches API #3309
google-oss-prow[bot] merged 10 commits into
kubeflow:masterfrom
astefanutti:pr-runtime-patches

Conversation

@astefanutti

@astefanutti astefanutti commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

This PR replaces PodTemplateOverrides with RuntimePatches API.

It implements the controller changes required by the API changes on top of #3199.

Checklist:

  • Docs included if any changes are user facing

@astefanutti astefanutti marked this pull request as ready for review March 11, 2026 18:06
Copilot AI review requested due to automatic review settings March 11, 2026 18:06
@google-oss-prow google-oss-prow Bot requested a review from jinchihe March 11, 2026 18:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the TrainJob customization mechanism from PodTemplateOverrides (and TrainJob .spec.labels/.spec.annotations) to a structured, multi-owner RuntimePatches API, updating runtime merge logic, validations, tests, and generated clients/specs accordingly.

Changes:

  • Replaced PodTemplateOverrides with RuntimePatches across API types, runtime merge/validation logic, and integration/e2e tests.
  • Updated applyconfigurations, OpenAPI spec, and generated Python client models for the new patch types.
  • Modified manager manifests (webhook enablement, leader election, cert management).

Reviewed changes

Copilot reviewed 44 out of 47 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/integration/webhooks/trainjob_test.go Updates webhook integration tests to use RuntimePatches validation/defaulting behavior.
test/integration/controller/trainjob_controller_test.go Updates controller integration tests to patch runtime via RuntimePatches.
test/e2e/e2e_test.go Updates e2e scenarios from PodTemplateOverrides to RuntimePatches.
pkg/util/testing/wrapper.go Removes spec label/annotation helpers and replaces PodTemplateOverrides builder with RuntimePatches.
pkg/runtime/framework/plugins/jobset/jobset.go Switches validation/immutability checks from PodTemplateOverrides to RuntimePatches.
pkg/runtime/framework/plugins/jobset/jobset_test.go Updates JobSet plugin unit tests for RuntimePatches validation/immutability.
pkg/runtime/core/trainingruntime.go Replaces override merge logic with mergeRuntimePatches and updates propagation labels/annotations sourcing.
pkg/runtime/core/trainingruntime_test.go Updates runtime object-building tests for RuntimePatches.
pkg/controller/trainingruntime_controller_test.go Adjusts controller tests to trigger events using RuntimePatches instead of spec labels.
pkg/controller/clustertrainingruntime_controller_test.go Same as above for cluster runtime reconciler tests.
pkg/client/applyconfiguration/utils.go Updates kind-to-applyconfig mapping for new Patch types.
pkg/client/applyconfiguration/trainer/v1alpha1/trainjobspec.go Removes Labels/Annotations/PodTemplateOverrides applyconfig; adds RuntimePatches.
pkg/client/applyconfiguration/trainer/v1alpha1/trainingruntimespecpatch.go Adds generated applyconfig for TrainingRuntimeSpecPatch.
pkg/client/applyconfiguration/trainer/v1alpha1/runtimepatch.go Adds generated applyconfig for RuntimePatch.
pkg/client/applyconfiguration/trainer/v1alpha1/replicatedjobpatch.go Adds generated applyconfig for ReplicatedJobPatch.
pkg/client/applyconfiguration/trainer/v1alpha1/podtemplatepatch.go Adds generated applyconfig for PodTemplatePatch.
pkg/client/applyconfiguration/trainer/v1alpha1/podtemplateoverridetargetjob.go Removes generated applyconfig for legacy override target job type.
pkg/client/applyconfiguration/trainer/v1alpha1/podtemplateoverride.go Removes generated applyconfig for legacy PodTemplateOverride type.
pkg/client/applyconfiguration/trainer/v1alpha1/podspecpatch.go Renames/reworks generated applyconfig to PodSpecPatch and related patch fields.
pkg/client/applyconfiguration/trainer/v1alpha1/jobtemplatepatch.go Adds generated applyconfig for JobTemplatePatch.
pkg/client/applyconfiguration/trainer/v1alpha1/jobspecpatch.go Adds generated applyconfig for JobSpecPatch.
pkg/client/applyconfiguration/trainer/v1alpha1/jobsettemplatepatch.go Adds generated applyconfig for JobSetTemplatePatch.
pkg/client/applyconfiguration/trainer/v1alpha1/jobsetspecpatch.go Adds generated applyconfig for JobSetSpecPatch.
pkg/client/applyconfiguration/trainer/v1alpha1/containerpatch.go Renames generated ContainerOverride applyconfig to ContainerPatch.
pkg/apis/trainer/v1alpha1/zz_generated.defaults.go Regenerated defaults traversal to reflect RuntimePatches structure.
pkg/apis/trainer/v1alpha1/zz_generated.deepcopy.go Regenerated deepcopy functions for new patch types and removed legacy override types.
pkg/apis/trainer/v1alpha1/trainjob_types.go API type changes: introduces RuntimePatches and patch subtypes; removes legacy overrides and spec labels/annotations.
manifests/overlays/manager/kustomization.yaml Disables webhook base resources in the manager overlay.
manifests/base/manager/controller_manager_config.yaml Disables leader election and built-in cert management in base configuration.
docs/proposals/2170-kubeflow-trainer-v2/README.md Updates proposal doc to describe RuntimePatches API and examples.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_training_runtime_spec_patch.py Adds generated Python model for TrainingRuntimeSpecPatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_train_job_spec.py Updates Python TrainJobSpec model: replaces overrides/labels/annotations with runtimePatches.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_runtime_patch.py Adds generated Python model for RuntimePatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_replicated_job_patch.py Adds generated Python model for ReplicatedJobPatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_pod_template_patch.py Adds generated Python model for PodTemplatePatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_pod_spec_patch.py Adds generated Python model for PodSpecPatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_job_template_patch.py Adds generated Python model for JobTemplatePatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_job_spec_patch.py Adds generated Python model for JobSpecPatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_job_set_template_patch.py Adds generated Python model for JobSetTemplatePatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_job_set_spec_patch.py Adds generated Python model for JobSetSpecPatch.
api/python_api/kubeflow_trainer_api/models/trainer_v1alpha1_container_patch.py Adds generated Python model for ContainerPatch.
api/python_api/kubeflow_trainer_api/models/init.py Updates Python model exports to include new patch models and remove legacy override models.
api/openapi-spec/swagger.json Updates OpenAPI schema to remove legacy override fields/types and add patch types.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread pkg/runtime/core/trainingruntime.go Outdated
Comment on lines 217 to 232
podTemplatePatch := map[string]any{}
if rJobPatch.Template.Spec.Template.Metadata != nil {
metadata := map[string]any{}
if rJobPatch.Template.Spec.Template.Metadata.Labels != nil {
metadata["labels"] = rJobPatch.Template.Spec.Template.Metadata.Labels
}
if rJobPatch.Template.Spec.Template.Metadata.Annotations != nil {
metadata["annotations"] = rJobPatch.Template.Spec.Template.Metadata.Annotations
}
if len(metadata) > 0 {
podTemplatePatch["metadata"] = metadata
}
}
if len(metadata) > 0 {
podTemplatePatch["metadata"] = metadata
if rJobPatch.Template.Spec.Template.Spec != nil {
podTemplatePatch["spec"] = rJobPatch.Template.Spec.Template.Spec
}

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mergeRuntimePatches only applies the PodTemplatePatch (pod metadata + pod spec) but ignores ReplicatedJobPatch.Template.Metadata (JobTemplatePatch metadata), so label/annotation patches intended for the Job object will be silently dropped; either apply those JobTemplatePatch metadata fields to the replicated job's JobTemplateSpec metadata as well, or remove the field from the API to avoid a misleading no-op.

Copilot uses AI. Check for mistakes.
Comment thread manifests/overlays/manager/kustomization.yaml Outdated
Comment thread manifests/base/manager/controller_manager_config.yaml Outdated
Comment thread manifests/base/manager/controller_manager_config.yaml Outdated

@andreyvelich andreyvelich left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your help working on this @astefanutti!
Overall lgtm, just a few nits.
/assign @tenzen-y @akshaychitneni @robert-bell

// args for the entrypoint for the training container.
// +listType=atomic
// +kubebuilder:validation:MaxItems=128
// +kubebuilder:validation:items:MaxLength=65536

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if k8s has similar limits for args/commands max length?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not widely used, but the Kube API linter maxlength rule enforces it.

I think it makes sense to put an upper bound anyway.

Comment thread pkg/runtime/core/trainingruntime.go Outdated
continue
}
for _, rJobPatch := range runtimePatch.TrainingRuntimeSpec.Template.Spec.ReplicatedJobs {
if rJobPatch.Template == nil || rJobPatch.Template.Spec == nil || rJobPatch.Template.Spec.Template == nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, it should be fixed by applying SMT at the level of batchv1.JobTemplateSpec.

Comment thread pkg/runtime/core/trainingruntime.go Outdated
if err != nil {
return err
}
merged, err := strategicpatch.StrategicMergePatch(source, patch, corev1.PodTemplateSpec{})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that works, maybe we can directly apply SMP to the batchv1.JobTemplateSpec{} which should apply Job labels too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea! Updated to apply SMT at the level of batchv1.JobTemplateSpec.

@google-oss-prow

Copy link
Copy Markdown

@andreyvelich: GitHub didn't allow me to assign the following users: robert-bell.

Note that only kubeflow members with read permissions, repo collaborators and people who have commented on this issue/PR can be assigned. Additionally, issues/PRs can only have 10 assignees at the same time.
For more information please see the contributor guide

Details

In response to this:

Thanks a lot for your help working on this @astefanutti!
Overall lgtm, just a few nits.
/assign @tenzen-y @akshaychitneni @robert-bell

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@astefanutti

Copy link
Copy Markdown
Contributor Author

Thanks a lot for your help working on this @astefanutti!
Overall lgtm, just a few nits.

@andreyvelich thanks, I should have addressed your comments, PTAL.

@andreyvelich andreyvelich left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @astefanutti for this work!
/lgtm
/assign @tenzen-y

@andreyvelich andreyvelich mentioned this pull request Mar 12, 2026
8 tasks

@tenzen-y tenzen-y left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!
/lgtm
/approve

@google-oss-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: tenzen-y

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@google-oss-prow google-oss-prow Bot merged commit 38d8243 into kubeflow:master Mar 12, 2026
33 checks passed
@google-oss-prow google-oss-prow Bot added this to the v2.2 milestone Mar 12, 2026
@astefanutti astefanutti deleted the pr-runtime-patches branch March 12, 2026 16:32
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Apr 30, 2026
Two Phase-2 follow-ups from NVIDIA#698, batched together because both are
small chart-pin changes coupled to a single non-pin tweak each.

Components bumped:

  kai-scheduler           v0.13.0 -> v0.14.1
  kubeflow-trainer        2.1.0   -> 2.2.0

kai-scheduler — chart bump and OCI registry namespace migration
(NVIDIA#698 follow-up NVIDIA#3):

KAI-Scheduler was transferred from the NVIDIA org to its own
`kai-scheduler` org and chart publishing moved with it. The old
namespace `oci://ghcr.io/nvidia/kai-scheduler` is frozen at v0.13.0;
the new namespace `oci://ghcr.io/kai-scheduler/kai-scheduler` carries
the full release stream. v0.14.1 verified clean: 41/41 templates and
identical kinds/counts vs v0.13.0; only values.yaml addition is an
opt-in `vpa:` block (`enabled: false` default). Our customizations
(`global.tolerations`, `admission.gpuPodRuntimeClassName`,
`postCleanup.enabled`) all still apply unchanged.

kubeflow-trainer — chart bump, validator fallback URL update, and
demo migration to the new RuntimePatches API
(NVIDIA#698 follow-up NVIDIA#5):

The chart pin in `recipes/registry.yaml` and the hardcoded fallback
archive URL in `validators/performance/trainer_lifecycle.go` are
coupled: the validator's no-CRD install path downloads
`https://github.com/kubeflow/trainer/archive/refs/tags/<version>.tar.gz`
and applies the `manifests/overlays/manager` kustomize. If the chart
pin moves but the validator URL doesn't, the fallback installs the
old release while the chart deploys the new one. v2.2.0 archive
layout is unchanged from v2.1.0 (same `manifests/overlays/manager`
kustomize, same `trainjobs.trainer.kubeflow.org/v1alpha1` CRD); the
only difference is the controller-manager image tag.

v2.2.0 ships a breaking API change to TrainJob: `podTemplateOverrides`
is replaced by `runtimePatches` (kubeflow/trainer#3309). The CRD still
admits the old field name for compat, but the controller no longer
applies it — pods come out with no override fields, and on AICR's
tainted GPU nodes the `tolerations: [{operator: Exists}]` shorthand
the demo previously used silently no-ops, leaving pods Pending.

The `pytorch-mnist` demo TrainJob in `demos/cuj1-eks.md` and
`demos/cuj1-gke.md` is migrated to the new shape:

  spec:
    runtimePatches:
      - manager: aicr.nvidia.com/demo
        trainingRuntimeSpec:
          template:
            spec:
              replicatedJobs:
                - name: node
                  template:
                    spec:
                      template:
                        spec:
                          nodeSelector: {nodeGroup: gpu-worker}
                          tolerations:
                            - {key: dedicated, operator: Equal,
                               value: worker-workload, effect: NoSchedule}
                            - {key: dedicated, operator: Equal,
                               value: worker-workload, effect: NoExecute}

Validated end-to-end on a real EKS H100 cluster (aicr1) post-upgrade:
TrainJob admitted, pod scheduled to the GPU node with the expected
tolerations + nodeSelector, training completed in 2m39s with
accuracy=0.7413 (matches pre-upgrade baseline).

Verified locally:

  $ helm pull oci://ghcr.io/kai-scheduler/kai-scheduler/kai-scheduler --version v0.14.1
  $ helm pull oci://ghcr.io/kubeflow/charts/kubeflow-trainer --version 2.2.0
  $ make tidy && make lint && go test -count=1 ./pkg/recipe/... ./validators/performance/...
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Apr 30, 2026
Two Phase-2 follow-ups from NVIDIA#698, batched together because both are
small chart-pin changes coupled to a single non-pin tweak each.

Components bumped:

  kai-scheduler           v0.13.0 -> v0.14.1
  kubeflow-trainer        2.1.0   -> 2.2.0

kai-scheduler — chart bump and OCI registry namespace migration
(NVIDIA#698 follow-up NVIDIA#3):

KAI-Scheduler was transferred from the NVIDIA org to its own
`kai-scheduler` org and chart publishing moved with it. The old
namespace `oci://ghcr.io/nvidia/kai-scheduler` is frozen at v0.13.0;
the new namespace `oci://ghcr.io/kai-scheduler/kai-scheduler` carries
the full release stream. v0.14.1 verified clean: 41/41 templates and
identical kinds/counts vs v0.13.0; only values.yaml addition is an
opt-in `vpa:` block (`enabled: false` default). Our customizations
(`global.tolerations`, `admission.gpuPodRuntimeClassName`,
`postCleanup.enabled`) all still apply unchanged.

kubeflow-trainer — chart bump, validator fallback URL update, and
demo migration to the new RuntimePatches API
(NVIDIA#698 follow-up NVIDIA#5):

The chart pin in `recipes/registry.yaml` and the hardcoded fallback
archive URL in `validators/performance/trainer_lifecycle.go` are
coupled: the validator's no-CRD install path downloads
`https://github.com/kubeflow/trainer/archive/refs/tags/<version>.tar.gz`
and applies the `manifests/overlays/manager` kustomize. If the chart
pin moves but the validator URL doesn't, the fallback installs the
old release while the chart deploys the new one. v2.2.0 archive
layout is unchanged from v2.1.0 (same `manifests/overlays/manager`
kustomize, same `trainjobs.trainer.kubeflow.org/v1alpha1` CRD); the
only difference is the controller-manager image tag.

v2.2.0 ships a breaking API change to TrainJob: `podTemplateOverrides`
is replaced by `runtimePatches` (kubeflow/trainer#3309). The CRD still
admits the old field name for compat, but the controller no longer
applies it — pods come out with no override fields, and on AICR's
tainted GPU nodes the `tolerations: [{operator: Exists}]` shorthand
the demo previously used silently no-ops, leaving pods Pending.

The `pytorch-mnist` demo TrainJob in `demos/cuj1-eks.md` and
`demos/cuj1-gke.md` is migrated to the new shape:

  spec:
    runtimePatches:
      - manager: aicr.nvidia.com/demo
        trainingRuntimeSpec:
          template:
            spec:
              replicatedJobs:
                - name: node
                  template:
                    spec:
                      template:
                        spec:
                          nodeSelector: {nodeGroup: gpu-worker}
                          tolerations:
                            - {key: dedicated, operator: Equal,
                               value: worker-workload, effect: NoSchedule}
                            - {key: dedicated, operator: Equal,
                               value: worker-workload, effect: NoExecute}

Validated end-to-end on a real EKS H100 cluster (aicr1) post-upgrade:
TrainJob admitted, pod scheduled to the GPU node with the expected
tolerations + nodeSelector, training completed in 2m39s with
accuracy=0.7413 (matches pre-upgrade baseline).

Verified locally:

  $ helm pull oci://ghcr.io/kai-scheduler/kai-scheduler/kai-scheduler --version v0.14.1
  $ helm pull oci://ghcr.io/kubeflow/charts/kubeflow-trainer --version 2.2.0
  $ make tidy && make lint && go test -count=1 ./pkg/recipe/... ./validators/performance/...
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Apr 30, 2026
Two Phase-2 follow-ups from NVIDIA#698, batched together because both are
small chart-pin changes coupled to a single non-pin tweak each.

Components bumped:

  kai-scheduler           v0.13.0 -> v0.14.1
  kubeflow-trainer        2.1.0   -> 2.2.0

kai-scheduler — chart bump and OCI registry namespace migration
(NVIDIA#698 follow-up NVIDIA#3):

KAI-Scheduler was transferred from the NVIDIA org to its own
`kai-scheduler` org and chart publishing moved with it. The old
namespace `oci://ghcr.io/nvidia/kai-scheduler` is frozen at v0.13.0;
the new namespace `oci://ghcr.io/kai-scheduler/kai-scheduler` carries
the full release stream. v0.14.1 verified clean: 41/41 templates and
identical kinds/counts vs v0.13.0; only values.yaml addition is an
opt-in `vpa:` block (`enabled: false` default). Our customizations
(`global.tolerations`, `admission.gpuPodRuntimeClassName`,
`postCleanup.enabled`) all still apply unchanged.

kubeflow-trainer — chart bump, validator fallback URL update, demo
migration to RuntimePatches, and ClusterTrainingRuntime alignment
(NVIDIA#698 follow-up NVIDIA#5):

The chart pin in `recipes/registry.yaml` and the hardcoded fallback
archive URL in `validators/performance/trainer_lifecycle.go` are
coupled: the validator's no-CRD install path downloads
`https://github.com/kubeflow/trainer/archive/refs/tags/<version>.tar.gz`
and applies the `manifests/overlays/manager` kustomize. If the chart
pin moves but the validator URL doesn't, the fallback installs the
old release while the chart deploys the new one. v2.2.0 archive
layout is unchanged from v2.1.0 (same `manifests/overlays/manager`
kustomize, same `trainjobs.trainer.kubeflow.org/v1alpha1` CRD); the
only difference is the controller-manager image tag.

v2.2.0 ships two breaking API changes that touch AICR:

  1. PodTemplateOverrides → RuntimePatches (kubeflow/trainer#3309).
     The CRD still admits the old field for compat but the v2.2
     controller no longer applies it. The pytorch-mnist demo TrainJob
     in `demos/cuj1-eks.md` and `demos/cuj1-gke.md` is migrated to
     the `runtimePatches` shape with `manager: aicr.nvidia.com/demo`
     and explicit per-cluster scheduling (the EKS demo carries the
     AICR-standard `dedicated=worker-workload` tolerations + NoExecute
     effect; the GKE demo carries `dedicated=gpu-workload:NoSchedule`
     and `nvidia.com/gpu=present:NoSchedule` to match the rest of the
     GKE flow).

  2. mlPolicy.torch.numProcPerNode removal (kubeflow/trainer#3239).
     Upstream removed the field from the Torch policy because it now
     infers parallelism from the container's `nvidia.com/gpu` limit.
     `mlPolicy.mpi.numProcPerNode` is unaffected, so the existing MPI
     test fixtures stay as-is. AICR's `torch-distributed`
     ClusterTrainingRuntime is updated from
     `mlPolicy.torch: { numProcPerNode: auto }` to
     `mlPolicy.torch: {}`, matching the v2.2.0 reference runtime.

Validated end-to-end on a real EKS H100 cluster (aicr1) post-upgrade:
demo TrainJob admitted, pod scheduled with the migrated runtimePatches,
training completed in 2m39s with accuracy=0.7413 (matches pre-upgrade
baseline). 2-replica Deployment with `schedulerName: kai-scheduler` +
DRA `ResourceClaimTemplate` referencing `gpu.nvidia.com` also
scheduled cleanly with `priorityClassName: train` (each replica got
its own H100 via DRA).

Verified locally:

  $ helm pull oci://ghcr.io/kai-scheduler/kai-scheduler/kai-scheduler --version v0.14.1
  $ helm pull oci://ghcr.io/kubeflow/charts/kubeflow-trainer --version 2.2.0
  $ make tidy && make lint && go test -count=1 ./pkg/recipe/... ./validators/performance/... ./pkg/bundler/deployer/helm/...
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Apr 30, 2026
The pytorch demo TrainJobs in demos/cuj1-{eks,gke}.md carry per-cluster
scheduling boilerplate (`podTemplateOverrides` with cluster-specific
tolerations) so the resulting pods land on AICR's tainted GPU nodes.
Each TrainJob author has to repeat this; each demo has to be edited
per-cluster vocabulary; and the override mechanism keeps changing
upstream (PodTemplateOverrides was deprecated in v2.1, replaced by
RuntimePatches in v2.2 — kubeflow/trainer#3309).

Move the per-cluster scheduling into the runtime instead. AICR's
existing `nodeScheduling.accelerated` bundler injection (already used
by gpu-operator, nfd, nodewright-customizations, kgateway) writes the
CLI flag values into the chart's values.yaml at the listed paths.
kubeflow-trainer was the only manifestFiles-using component without an
`accelerated:` block. This commit adds it and templates the
torch-distributed ClusterTrainingRuntime to consume the injected
values, mirroring nodewright-customizations/manifests/tuning.yaml.

Three coordinated changes:

1. recipes/registry.yaml — add `nodeScheduling.accelerated` block to
   the kubeflow-trainer entry. Targets top-level keys
   `acceleratedNodeSelector` and `acceleratedTolerations`.

2. recipes/components/kubeflow-trainer/manifests/
   torch-distributed-cluster-training-runtime.yaml — replace the
   static pod-spec scheduling region with Helm template directives:

       {{- $kft := index .Values "kubeflow-trainer" }}
       {{- with $kft.acceleratedNodeSelector }}
       nodeSelector:
         {{- toYaml . | nindent 20 }}
       {{- end }}
       {{- with $kft.acceleratedTolerations }}
       tolerations:
         {{- toYaml . | nindent 20 }}
       {{- end }}

   `index .Values "kubeflow-trainer"` matches the bundler's
   `manifest.RenderInput.Values` shape (values nested under
   ComponentName). The bundler renders this template at bundle time —
   the artifact in `bundle/<NNN>-kubeflow-trainer-post/templates/`
   is plain YAML with concrete values substituted.

3. demos/cuj1-eks.md and demos/cuj1-gke.md — drop the entire
   `podTemplateOverrides` block. Demo TrainJob is just `trainer:` +
   `runtimeRef:`.

API-version-agnostic: works on kubeflow-trainer v2.1 (PodTemplateOverrides
era) and v2.2+ (RuntimePatches era) identically, because the TrainJob
no longer overrides anything — the runtime carries the scheduling.

Validated end-to-end on a real EKS H100 cluster:
helm-upgrade kubeflow-trainer-post → CTR live with baked tolerations
+ nodeSelector → bare pytorch-mnist TrainJob admits, schedules with
the correct tolerations + nodeSelector inherited from the runtime,
trains to completion (accuracy=0.7424 in 21s).

`pkg/recipe.TestManifestHelmHooksRequired` still passes — the
`helm.sh/hook` annotations are preserved.
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Apr 30, 2026
The pytorch demo TrainJobs in demos/cuj1-{eks,gke}.md carry per-cluster
scheduling boilerplate (`podTemplateOverrides` with cluster-specific
tolerations) so the resulting pods land on AICR's tainted GPU nodes.
Each TrainJob author has to repeat this; each demo has to be edited
per-cluster vocabulary; and the override mechanism keeps changing
upstream (PodTemplateOverrides was deprecated in v2.1, replaced by
RuntimePatches in v2.2 — kubeflow/trainer#3309).

Move the per-cluster scheduling into the runtime instead. AICR's
existing `nodeScheduling.accelerated` bundler injection (already used
by gpu-operator, nfd, nodewright-customizations, kgateway) writes the
CLI flag values into the chart's values.yaml at the listed paths.
kubeflow-trainer was the only manifestFiles-using component without an
`accelerated:` block. This commit adds it and templates the
torch-distributed ClusterTrainingRuntime to consume the injected
values, mirroring nodewright-customizations/manifests/tuning.yaml.

Three coordinated changes:

1. recipes/registry.yaml — add `nodeScheduling.accelerated` block to
   the kubeflow-trainer entry. Targets top-level keys
   `acceleratedNodeSelector` and `acceleratedTolerations`.

2. recipes/components/kubeflow-trainer/manifests/
   torch-distributed-cluster-training-runtime.yaml — replace the
   static pod-spec scheduling region with Helm template directives:

       {{- $kft := index .Values "kubeflow-trainer" }}
       {{- with $kft.acceleratedNodeSelector }}
       nodeSelector:
         {{- toYaml . | nindent 20 }}
       {{- end }}
       {{- with $kft.acceleratedTolerations }}
       tolerations:
         {{- toYaml . | nindent 20 }}
       {{- end }}

   `index .Values "kubeflow-trainer"` matches the bundler's
   `manifest.RenderInput.Values` shape (values nested under
   ComponentName). The bundler renders this template at bundle time —
   the artifact in `bundle/<NNN>-kubeflow-trainer-post/templates/`
   is plain YAML with concrete values substituted.

3. demos/cuj1-eks.md and demos/cuj1-gke.md — drop the entire
   `podTemplateOverrides` block. Demo TrainJob is just `trainer:` +
   `runtimeRef:`.

API-version-agnostic: works on kubeflow-trainer v2.1 (PodTemplateOverrides
era) and v2.2+ (RuntimePatches era) identically, because the TrainJob
no longer overrides anything — the runtime carries the scheduling.

Validated end-to-end on a real EKS H100 cluster:
helm-upgrade kubeflow-trainer-post → CTR live with baked tolerations
+ nodeSelector → bare pytorch-mnist TrainJob admits, schedules with
the correct tolerations + nodeSelector inherited from the runtime,
trains to completion (accuracy=0.7424 in 21s).

`pkg/recipe.TestManifestHelmHooksRequired` still passes — the
`helm.sh/hook` annotations are preserved.
pulkit-999 pushed a commit to pulkit-999/trainer that referenced this pull request May 12, 2026
…flow#3309)

* feat(api): Replace PodTemplateOverrides with RuntimePatches API

Signed-off-by: Andrey Velichkevich <[email protected]>

* Update goal with RuntimePatches API

Signed-off-by: Andrey Velichkevich <[email protected]>

* Add diagram for RuntimePatches

Signed-off-by: Andrey Velichkevich <[email protected]>

* feat(api): Replace PodTemplateOverrides with RuntimePatches API

Signed-off-by: Antonin Stefanutti <[email protected]>

* Fix kube lint errors

Signed-off-by: Antonin Stefanutti <[email protected]>

* Update Python models

Signed-off-by: Antonin Stefanutti <[email protected]>

* Enable maxlength Kube-API-Linter rule

Signed-off-by: Antonin Stefanutti <[email protected]>

* Restore manger field IsDomainPrefixedPath validation

Signed-off-by: Antonin Stefanutti <[email protected]>

* Fix integration tests

Signed-off-by: Antonin Stefanutti <[email protected]>

* Apply StrategicMergePatch to batchv1.JobTemplateSpec

Signed-off-by: Antonin Stefanutti <[email protected]>

---------

Signed-off-by: Andrey Velichkevich <[email protected]>
Signed-off-by: Antonin Stefanutti <[email protected]>
Co-authored-by: Andrey Velichkevich <[email protected]>
Signed-off-by: Pulkit Agrawal <[email protected]>
sutaakar added a commit to sutaakar/distributed-workloads that referenced this pull request Jun 9, 2026
Exercises wider TrainJob API surface in the upgrade test so spec
integrity checks can detect API migrations like kubeflow/trainer#3309
(PodTemplateOverrides → RuntimePatches).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
openshift-merge-bot Bot pushed a commit to opendatahub-io/distributed-workloads that referenced this pull request Jun 10, 2026
Exercises wider TrainJob API surface in the upgrade test so spec
integrity checks can detect API migrations like kubeflow/trainer#3309
(PodTemplateOverrides → RuntimePatches).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants