feat(api): Replace PodTemplateOverrides with RuntimePatches API #3309
Conversation
Signed-off-by: Andrey Velichkevich <[email protected]>
Signed-off-by: Andrey Velichkevich <[email protected]>
Signed-off-by: Andrey Velichkevich <[email protected]>
There was a problem hiding this comment.
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
PodTemplateOverrideswithRuntimePatchesacross 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.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
andreyvelich
left a comment
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Do you know if k8s has similar limits for args/commands max length?
There was a problem hiding this comment.
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.
| continue | ||
| } | ||
| for _, rJobPatch := range runtimePatch.TrainingRuntimeSpec.Template.Spec.ReplicatedJobs { | ||
| if rJobPatch.Template == nil || rJobPatch.Template.Spec == nil || rJobPatch.Template.Spec.Template == nil { |
There was a problem hiding this comment.
What about rJobPatch.Template.Metadata.Labels and .Annotations which should be applied to the Job's metadata?
E.g. this API: https://github.com/astefanutti/trainer/blob/a2bd703ff029feab8e80979b9fa1c4b724c4b33f/pkg/apis/trainer/v1alpha1/trainjob_types.go#L339-L342
There was a problem hiding this comment.
Right, it should be fixed by applying SMT at the level of batchv1.JobTemplateSpec.
| if err != nil { | ||
| return err | ||
| } | ||
| merged, err := strategicpatch.StrategicMergePatch(source, patch, corev1.PodTemplateSpec{}) |
There was a problem hiding this comment.
if that works, maybe we can directly apply SMP to the batchv1.JobTemplateSpec{} which should apply Job labels too.
There was a problem hiding this comment.
That's a good idea! Updated to apply SMT at the level of batchv1.JobTemplateSpec.
|
@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. DetailsIn response to this:
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. |
a2bd703 to
3aa024e
Compare
Signed-off-by: Antonin Stefanutti <[email protected]>
Signed-off-by: Antonin Stefanutti <[email protected]>
Signed-off-by: Antonin Stefanutti <[email protected]>
Signed-off-by: Antonin Stefanutti <[email protected]>
Signed-off-by: Antonin Stefanutti <[email protected]>
3aa024e to
bc7c0ce
Compare
Signed-off-by: Antonin Stefanutti <[email protected]>
Signed-off-by: Antonin Stefanutti <[email protected]>
@andreyvelich thanks, I should have addressed your comments, PTAL. |
andreyvelich
left a comment
There was a problem hiding this comment.
Thanks @astefanutti for this work!
/lgtm
/assign @tenzen-y
|
[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 DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
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/...
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/...
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/...
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.
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.
…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]>
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]>
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]>
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: