Skip to content

Conversation

harche
Copy link
Contributor

@harche harche commented Oct 10, 2025

What type of PR is this?

/kind bug

What this PR does / why we need it:

This PR adds validation to reject DRA (Dynamic Resource Allocation) v1 API features that are enabled
by default in Kubernetes 1.34 but explicitly excluded from Kueue's DRA support scope per KEP-2941.

Without validation, Kueue silently ignores unsupported DRA features, leading to:

  1. Silent admission failures - FirstAvailable workloads are admitted with 0 DRA resources, causing
    runtime failures
  2. Misleading errors - 5 out of 6 unsupported features produce confusing "DeviceClass not mapped"
    errors instead of clear feature-not-supported messages
  3. Poor UX - Users waste time debugging DeviceClass mappings when the actual issue is using
    unsupported features

Solution is to add early validation in pkg/dra/claims.go to explicitly reject all 6 enabled-by-default DRA features
that Kueue doesn't support:

GA Features:

  • AllocationMode 'All'
  • CEL Selectors
  • Device Constraints
  • Device Config

Beta Features (enabled by default in K8s 1.34):

  • FirstAvailable (DRAPrioritizedList)
  • AdminAccess (DRAAdminAccess)

Each feature now returns a clear, actionable error message like "FirstAvailable device selection is
not supported" instead of misleading DeviceClass mapping errors.

Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?

Kueue now properly validates and rejects unsupported DRA (Dynamic Resource Allocation) features with clear error messages instead of silently failing or producing misleading "DeviceClass not mapped" errors. Unsupported features include: AllocationMode 'All', CEL Selectors, Device Constraints, Device Config, FirstAvailable device selection, and AdminAccess.

@k8s-ci-robot k8s-ci-robot added kind/bug Categorizes issue or PR as related to a bug. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Oct 10, 2025
Copy link

netlify bot commented Oct 10, 2025

Deploy Preview for kubernetes-sigs-kueue canceled.

Name Link
🔨 Latest commit 8de8f8c
🔍 Latest deploy log https://app.netlify.com/projects/kubernetes-sigs-kueue/deploys/68f659fe9cf72200082b1be9

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: harche
Once this PR has been reviewed and has the lgtm label, please assign gabesaba for approval. For more information see the Code Review Process.

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

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

@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Oct 10, 2025
@k8s-ci-robot
Copy link
Contributor

Hi @harche. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

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-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Oct 10, 2025
@mimowo
Copy link
Contributor

mimowo commented Oct 10, 2025

/ok-to-test
@alaypatel07 would you like to give it a pass?

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Oct 10, 2025
@kannon92
Copy link
Contributor

/ok-to-test

/cc @alaypatel07

@alaypatel07
Copy link
Contributor

Yes I'll take a look.

var dcName string
var q int64
if req.Exactly != nil {
// Check for CEL selectors
Copy link
Contributor

Choose a reason for hiding this comment

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

can you please convert the entire if condition into a switch case handling all the unsupported features first before considering the valid supported case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

@harche why does this have to be nested ifs and switch cases? Can we work out a logic with one single switch case?

For example:

		switch {
		case req.FirstAvailable != nil:
		case req.Exactly != nil && len(req.Exactly.Selectors) > 0:
		// error
		case req.Exactly != nil && req.Exactly.AdminAccess != nil && *req.Exactly.AdminAccess:
		// error
		case req.Exactly != nil && req.Exactly.AllocationMode == DeviceAllocationModeAll:
		// error
		}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alaypatel07 I didn't want to repeat req.Exactly != nil, would something like following do?

switch {
		case req.FirstAvailable != nil:
			return nil, fmt.Errorf("%w: FirstAvailable device selection is not supported", ErrUnsupportedDRAFeature)
		case req.Exactly == nil:
			continue
		case len(req.Exactly.Selectors) > 0:
			return nil, fmt.Errorf("%w: CEL selectors are not supported", ErrUnsupportedDRAFeature)
		case req.Exactly.AdminAccess != nil && *req.Exactly.AdminAccess:
			return nil, fmt.Errorf("%w: AdminAccess is not supported", ErrUnsupportedDRAFeature)
		case req.Exactly.AllocationMode == resourcev1.DeviceAllocationModeAll:
			return nil, fmt.Errorf("%w: AllocationMode 'All' is not supported", ErrUnsupportedDRAFeature)
		case req.Exactly.AllocationMode == resourcev1.DeviceAllocationModeExactCount:
			dcName = req.Exactly.DeviceClassName
			q = req.Exactly.Count
		case req.Exactly.AllocationMode == "":
			dcName = req.Exactly.DeviceClassName
		default:
			return nil, fmt.Errorf("%w: unsupported allocation mode: %s", ErrUnsupportedDRAFeature, req.Exactly.AllocationMode)
		}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated, let me know if that works, but I am open to changing it to repeated req.Exactly != nil.

ErrDeviceClassNotMapped = errors.New("DeviceClass is not mapped in DRA configuration")
ErrResourceClaimInUse = errors.New("ResourceClaim is in use")
ErrClaimSpecNotFound = errors.New("failed to get claim spec")
ErrDeviceClassNotMapped = errors.New("DeviceClass is not mapped in DRA configuration")
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: can we use a common error "UnsupportedDRAfeature" and then wrap a human readable error message to be returned from the function later.

I think we only need to have the following concrete error types:

  1. ErrResourceClaimInUse and other
  2. ErrClaimSpecNotFound
  3. ErrUnsupportedDRAfeature

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed, thanks.

},
want: map[kueuev1beta1.PodSetReference]corev1.ResourceList{"main": {"res-1": resource.MustParse("2")}},
},
{
Copy link
Contributor

Choose a reason for hiding this comment

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

we should also update the integration tests for these errors at https://github.com/kubernetes-sigs/kueue/tree/main/test/integration/singlecluster/controller/dra

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added integration tests.

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Oct 10, 2025
@harche harche requested a review from alaypatel07 October 10, 2025 18:54
@harche
Copy link
Contributor Author

harche commented Oct 10, 2025

/test pull-kueue-test-e2e-multikueue-main

@harche
Copy link
Contributor Author

harche commented Oct 10, 2025

@alaypatel07 I have updated the PR. Can you take another look? Thanks.

wl := utiltesting.MakeWorkload("test-wl-all-mode", ns.Name).
Queue("test-lq").
Obj()
wl.Spec.PodSets[0].Template.Spec.ResourceClaims = []corev1.PodResourceClaim{
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated, thanks.


ginkgo.It("Should reject workload with AllocationMode 'All'", func() {
ginkgo.By("Creating a ResourceClaimTemplate with AllocationMode All")
rct := &resourcev1.ResourceClaimTemplate{
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can create a ResourceClaimWrapper to produce these templates/claims in utiltesting:

type PodSetWrapper struct{ kueue.PodSet }

it will be helpful in unit testing as well as integration tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds like a great idea, but should we tackle this outside this PR? I can create a separate issue to look into it. I am afraid it might escalate the scope of this PR (which is just to harden the validation around unsupported DRA fields/features)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

+1. IMHO, since this PR is adding all the permutations of creating resource slices with different features, this is the right moment to add the wrapper. I dont really think it expands the scope. Thanks for tackling that.

@harche harche force-pushed the dra_validation branch 2 times, most recently from e6127d8 to 03ca256 Compare October 10, 2025 23:12
q = req.Exactly.Count
}
q = req.Exactly.Count
case req.Exactly.AllocationMode == "":
Copy link
Contributor

Choose a reason for hiding this comment

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

This case is not needed, Kueue should assume that the defaults are set appropriately during resource admission: https://github.com/kubernetes/kubernetes/blob/f306bb14b4a288cd04c6ddad65a5a91bc1f665f8/pkg/apis/resource/v1/defaults.go#L32-L37.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed, thanks.


// ResourceClaimTemplateWrapper wraps a resourcev1.ResourceClaimTemplate
type ResourceClaimTemplateWrapper struct {
resourcev1.ResourceClaimTemplate
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Instead of having separate structs for RCT and RC, there should be a common struct for ResourceClaimSpec and the With* functions use that struct to generate the spec. I think will remove a lot of duplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Between ResourceClaimTemplateWrapper and ResourceClaimWrapper, there's now a shared ResourceClaimSpecBuilder that both wrappers use, let me know if that aligns with what you had in mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kannon92
Copy link
Contributor

Please add a release note.

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. labels Oct 16, 2025
@harche
Copy link
Contributor Author

harche commented Oct 16, 2025

Please add a release note.

Thanks, done.

@alaypatel07
Copy link
Contributor

@harche I'm falling behind on reviews due to the upcoming KEP freeze. I'll take a look first thing tomorrow morning.

@harche
Copy link
Contributor Author

harche commented Oct 16, 2025

@harche I'm falling behind on reviews due to the upcoming KEP freeze. I'll take a look first thing tomorrow morning.

No worries @alaypatel07 , take your time. This can wait until the KEP freeze deadline passes. Thanks for your valuable feedback.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 16, 2025
Comment on lines 429 to 434
resourceClaims: []*resourcev1.ResourceClaim{
utiltesting.MakeResourceClaim("rc1", "ns").
DeviceRequest("device-request", "gpu.example.com", 1).
Obj(),
},
Copy link
Contributor

@mimowo mimowo Oct 16, 2025

Choose a reason for hiding this comment

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

Actually could you please send a preaparatory non-functional PR just for the cleanup?

So that we don't mix cleanups with features or bugfixes.

It will make the diff here smaller for the ease of reviewing / focusing on the user-changing experience.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @mimowo, I have brought this up earlier, #7226 (comment)

@alaypatel07 are you okay with splitting the refactoring of tests utils from this PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

@harche yes, I think what @mimowo is suggesting is little different from earlier conversation, instead of following up on the refactor, I think he wants the refactor to go in first as a separate PR, I am okay with that. Correct me if I am wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @alaypatel07 and @mimowo, I have raised #7300 to add wrapper for existing tests cases only.

We will deal with this PR once that one is merged.

/hold

Copy link
Contributor

Choose a reason for hiding this comment

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

I merged the other PR already, please rebase, let me unhold in the meanwhile, it will not merge anyway while not rebased.
/unhold

harche added a commit to harche/kueue that referenced this pull request Oct 16, 2025
This commit introduces test helper wrappers for DRA
ResourceClaim and ResourceClaimTemplate objects to improve
test readability and reduce boilerplate.

Related to PR kubernetes-sigs#7226 review comment:
kubernetes-sigs#7226 (comment)
@k8s-ci-robot k8s-ci-robot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. and removed do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. labels Oct 16, 2025
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Oct 20, 2025
Comment on lines 237 to 240
if dc == "test-deviceclass-1" {
return "res-1", true
}
return "", false
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks very verbose and repeatable, can we replace it somehow with some default lookups maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a common defaultLookup function

}
return "", false
},
wantErr: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of asserting true/false it is better to assert on specific validation error, please check https://github.com/kubernetes-sigs/kueue/blob/main/pkg/controller/jobs/jobset/jobset_webhook_test.go#L49

I know it was like this before, but I would like to use it as an occasion to cleanup

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, updated to assert exact error.

@harche
Copy link
Contributor Author

harche commented Oct 20, 2025

/hold just like unit tests, the integration tests should also check for exact error.

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 20, 2025
@harche
Copy link
Contributor Author

harche commented Oct 20, 2025

/hold just like unit tests, the integration tests should also check for exact error.

updated.

/unhold

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 20, 2025
Comment on lines +37 to +47
ErrResourceClaimInUse = errors.New("ResourceClaim is in use")
ErrClaimSpecNotFound = errors.New("failed to get claim spec")
ErrUnsupportedDRAFeature = errors.New("unsupported DRA feature")

// Specific unsupported DRA feature errors
ErrUnsupportedDRADeviceConstraints = errors.New("device constraints (MatchAttribute) are not supported")
ErrUnsupportedDRADeviceConfig = errors.New("device config is not supported")
ErrUnsupportedDRAFirstAvailable = errors.New("FirstAvailable device selection is not supported")
ErrUnsupportedDRACELSelectors = errors.New("CEL selectors are not supported")
ErrUnsupportedDRAAdminAccess = errors.New("AdminAccess is not supported")
ErrUnsupportedDRAAllocationModeAll = errors.New("AllocationMode 'All' is not supported")
Copy link
Contributor

Choose a reason for hiding this comment

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

Do these need to be exposed? If not then unexport (lowercase)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are used in integration tests, besides that, I am not sure having errors exported causes any harm, future callers might be able to verify if the errors belongs to any of the DRA related errors.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can always export them later if needed. If they are unexported it is immediately obvious which are used or not outside the package for example during review in GH. Also, sometimes exported constants become unused but linters don't complain, so making them unused is easy to miss during review/ code changing.

Comment on lines +355 to +366
if tc.wantErr != nil {
if err == nil {
t.Fatalf("expected error but got none")
}
if !errors.Is(err, tc.wantErr) {
t.Fatalf("unexpected error: got=%v, want=%v", err, tc.wantErr)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to assert errors as we do here, using cmp.Diff:

wantErr: field.ErrorList{
field.Invalid(field.NewPath("spec.replicatedJobs[0].template.spec.template.metadata.annotations"), field.OmitValueType{}, "must specify 'kueue.x-k8s.io/podset-required-topology' or 'kueue.x-k8s.io/podset-preferred-topology' topology consistent with 'spec.replicatedJobs[1].template.spec.template.metadata.annotations' in group 'groupname'"),
field.Invalid(field.NewPath("spec.replicatedJobs[1].template.spec.template.metadata.annotations"), field.OmitValueType{}, "must specify 'kueue.x-k8s.io/podset-required-topology' or 'kueue.x-k8s.io/podset-preferred-topology' topology consistent with 'spec.replicatedJobs[0].template.spec.template.metadata.annotations' in group 'groupname'"),
}.ToAggregate(),
topologyAwareScheduling: true,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
features.SetFeatureGateDuringTest(t, features.TopologyAwareScheduling, tc.topologyAwareScheduling)
jsw := &JobSetWebhook{}
ctx, _ := utiltesting.ContextWithLog(t)
_, gotErr := jsw.ValidateCreate(ctx, tc.job)
if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
t.Errorf("validateCreate() mismatch (-want +got):\n%s", diff)
}

Would it work here? I'm not sure maybe it works better if the validation errors are using field.ErrorList type.

Copy link
Contributor Author

@harche harche Oct 20, 2025

Choose a reason for hiding this comment

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

The current tests use errors.Is, this is exactly what we want. IMO, achieving this in any other way would not be more optimal than simply using errors.Is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/bug Categorizes issue or PR as related to a bug. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants