Skip to content

KEP-4671: Add Declarative Validation to Workload API#135164

Merged
k8s-ci-robot merged 14 commits intokubernetes:masterfrom
helayoty:helayoty/workload-declarative-validation
Jan 16, 2026
Merged

KEP-4671: Add Declarative Validation to Workload API#135164
k8s-ci-robot merged 14 commits intokubernetes:masterfrom
helayoty:helayoty/workload-declarative-validation

Conversation

@helayoty
Copy link
Copy Markdown
Member

@helayoty helayoty commented Nov 6, 2025

What type of PR is this?

  • A followup request to add declarative validation to Workload API.
  • Add support to k8s-path-segment-name validation.

/kind feature
/sig scheduling

What this PR does / why we need it:

Which issue(s) this PR is related to:

Special notes for your reviewer:

Does this PR introduce a user-facing change?


Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:


@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/feature Categorizes issue or PR as related to a new feature. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. sig/scheduling Categorizes an issue or PR as relevant to SIG Scheduling. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Nov 6, 2025
@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. labels Nov 6, 2025
@k8s-ci-robot k8s-ci-robot added area/apiserver area/code-generation area/kubectl area/test kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/apps Categorizes an issue or PR as relevant to SIG Apps. sig/auth Categorizes an issue or PR as relevant to SIG Auth. labels Nov 6, 2025
@k8s-ci-robot k8s-ci-robot added sig/cli Categorizes an issue or PR as relevant to SIG CLI. sig/etcd Categorizes an issue or PR as relevant to SIG Etcd. labels Nov 6, 2025
@github-project-automation github-project-automation Bot moved this to Needs Triage in SIG Apps Nov 6, 2025
@k8s-ci-robot k8s-ci-robot added the sig/testing Categorizes an issue or PR as relevant to SIG Testing. label Nov 6, 2025
@github-project-automation github-project-automation Bot moved this to Needs Triage in SIG CLI Nov 6, 2025
Copy link
Copy Markdown
Member

@thockin thockin left a comment

Choose a reason for hiding this comment

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

I didn't go into depth, but it looks on the right track!

@lalitc375 @aaron-prindle @yongruilin

The way I would ask this to be structured is probably:

  • 1 commit for wiring it up and activating it, no tags
  • N commits with either 1 tag per commit (e.g. all the optionals) or 1 field per commit or even 1 single field-tag per commit.

This makes it much easier to review the conversion - we can see the generated code and which manual changes you made to handwritten code, and can evaluate the test cases one by one. The goal is to end up with better tests which are easier to comprehend, but it's hard to review an omnibus commit.

Given how this is new API, we should aim for 100% coverage, or else some issues one what was missing to reach 100%

In 1.36 we may be able to ONLY do DV for this.1

Comment thread staging/src/k8s.io/api/scheduling/v1alpha1/types.go
Comment thread staging/src/k8s.io/api/scheduling/v1alpha1/types.go
Comment thread pkg/apis/scheduling/types.go Outdated
Comment thread pkg/apis/scheduling/validation/validation.go Outdated
Comment thread pkg/apis/scheduling/validation/validation.go Outdated
Comment thread pkg/apis/scheduling/validation/validation.go Outdated
Comment thread staging/src/k8s.io/api/scheduling/v1alpha1/types.go
Copy link
Copy Markdown
Contributor

@lalitc375 lalitc375 left a comment

Choose a reason for hiding this comment

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

/lgtm with respect to the DV part.

@thockin Can you do the review?

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

LGTM label has been added.

DetailsGit tree hash: 46c18750893a6bb17caf67bd0515e6e49aef653d

Copy link
Copy Markdown
Member

@thockin thockin left a comment

Choose a reason for hiding this comment

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

Comments on tests.

One of the side-quests of DV is scrubbing string-format validations and consolidating to a single library. I prepared a commit to move the path-segment stuff. I can either followup on this PR with my own, or I can just get you to paste it into your PR as distinct commit, and call the newly named func instead of the old (content.IsPathSegmentName() vs path.IsValidPathSegmentName())

commit 6bd2283ae502135ebaea0fdaac42b3313869cdcf
Good "git" signature for [email protected] with ED25519 key SHA256:M2+rk0pn34LtCBIVneq+UkpS+MLUTLylIUjHPq6OGSE
Author: Tim Hockin <[email protected]>
Date:   Wed Dec 10 18:43:09 2025 -0800

    Move path-segment validation to pkg content
    
    This is where all the "scrubbed" validation helpers are going.
    
    Note: This does NOT check for "" or too-long inputs, and changing it now
    would be a breaking change.

diff --git pkg/apis/admissionregistration/validation/validation.go pkg/apis/admissionregistration/validation/validation.go
index 815f51a497a..56ab3e5af92 100644
--- pkg/apis/admissionregistration/validation/validation.go
+++ pkg/apis/admissionregistration/validation/validation.go
@@ -24,8 +24,8 @@ import (
 	"sync"
 
 	"k8s.io/apimachinery/pkg/api/equality"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	genericvalidation "k8s.io/apimachinery/pkg/api/validation"
-	"k8s.io/apimachinery/pkg/api/validation/path"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -948,7 +948,7 @@ func validateNamedRuleWithOperations(n *admissionregistration.NamedRuleWithOpera
 	var allErrors field.ErrorList
 	resourceNames := sets.NewString()
 	for i, rName := range n.ResourceNames {
-		for _, msg := range path.ValidatePathSegmentName(rName, false) {
+		for _, msg := range content.IsPathSegmentName(rName) {
 			allErrors = append(allErrors, field.Invalid(fldPath.Child("resourceNames").Index(i), rName, msg))
 		}
 		if resourceNames.Has(rName) {
@@ -1202,7 +1202,7 @@ func validateParamRef(pr *admissionregistration.ParamRef, fldPath *field.Path) f
 	}
 
 	if len(pr.Name) > 0 {
-		for _, msg := range path.ValidatePathSegmentName(pr.Name, false) {
+		for _, msg := range content.IsPathSegmentName(pr.Name) {
 			allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), pr.Name, msg))
 		}
 
diff --git pkg/apis/autoscaling/validation/validation.go pkg/apis/autoscaling/validation/validation.go
index 524212f53a1..de542aac63a 100644
--- pkg/apis/autoscaling/validation/validation.go
+++ pkg/apis/autoscaling/validation/validation.go
@@ -19,8 +19,8 @@ package validation
 import (
 	"fmt"
 
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
-	pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -84,7 +84,7 @@ func ValidateCrossVersionObjectReference(ref autoscaling.CrossVersionObjectRefer
 	if len(ref.Kind) == 0 {
 		allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(ref.Kind) {
+		for _, msg := range content.IsPathSegmentName(ref.Kind) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ref.Kind, msg))
 		}
 	}
@@ -92,7 +92,7 @@ func ValidateCrossVersionObjectReference(ref autoscaling.CrossVersionObjectRefer
 	if len(ref.Name) == 0 {
 		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(ref.Name) {
+		for _, msg := range content.IsPathSegmentName(ref.Name) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ref.Name, msg))
 		}
 	}
@@ -472,7 +472,7 @@ func validateMetricIdentifier(id autoscaling.MetricIdentifier, fldPath *field.Pa
 	if len(id.Name) == 0 {
 		allErrs = append(allErrs, field.Required(fldPath.Child("name"), "must specify a metric name"))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(id.Name) {
+		for _, msg := range content.IsPathSegmentName(id.Name) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), id.Name, msg))
 		}
 	}
diff --git pkg/apis/networking/validation/validation.go pkg/apis/networking/validation/validation.go
index 31e5b29a2f5..066171598a0 100644
--- pkg/apis/networking/validation/validation.go
+++ pkg/apis/networking/validation/validation.go
@@ -20,8 +20,8 @@ import (
 	"fmt"
 	"strings"
 
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
-	pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
 	unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/apimachinery/pkg/util/sets"
@@ -605,7 +605,7 @@ func validateIngressTypedLocalObjectReference(params *api.TypedLocalObjectRefere
 	if params.Kind == "" {
 		allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Kind) {
+		for _, msg := range content.IsPathSegmentName(params.Kind) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), params.Kind, msg))
 		}
 	}
@@ -613,7 +613,7 @@ func validateIngressTypedLocalObjectReference(params *api.TypedLocalObjectRefere
 	if params.Name == "" {
 		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Name) {
+		for _, msg := range content.IsPathSegmentName(params.Name) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg))
 		}
 	}
@@ -785,7 +785,7 @@ func validateIPAddressParentReference(params *networking.ParentReference, fldPat
 	if params.Resource == "" {
 		allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Resource) {
+		for _, msg := range content.IsPathSegmentName(params.Resource) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("resource"), params.Resource, msg))
 		}
 	}
@@ -794,14 +794,14 @@ func validateIPAddressParentReference(params *networking.ParentReference, fldPat
 	if params.Name == "" {
 		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Name) {
+		for _, msg := range content.IsPathSegmentName(params.Name) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg))
 		}
 	}
 
 	// namespace is optional
 	if params.Namespace != "" {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Namespace) {
+		for _, msg := range content.IsPathSegmentName(params.Namespace) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), params.Namespace, msg))
 		}
 	}
diff --git pkg/apis/rbac/validation/validation.go pkg/apis/rbac/validation/validation.go
index fcc672dbd2a..2de94a9749e 100644
--- pkg/apis/rbac/validation/validation.go
+++ pkg/apis/rbac/validation/validation.go
@@ -17,7 +17,7 @@ limitations under the License.
 package validation
 
 import (
-	"k8s.io/apimachinery/pkg/api/validation/path"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -30,7 +30,7 @@ import (
 // * https://github.com/kubernetes/kubernetes/blob/60db507b279ce45bd16ea3db49bf181f2aeb3c3d/pkg/api/validation/name.go
 // * https://github.com/openshift/origin/blob/388478c40e751c4295dcb9a44dd69e5ac65d0e3b/pkg/api/helpers.go
 func ValidateRBACName(name string, prefix bool) []string {
-	return path.IsValidPathSegmentName(name)
+	return content.IsPathSegmentName(name)
 }
 
 func ValidateRole(role *rbac.Role) field.ErrorList {
diff --git pkg/apis/scheduling/validation/validation.go pkg/apis/scheduling/validation/validation.go
index 6ca22364544..e824bec3b72 100644
--- pkg/apis/scheduling/validation/validation.go
+++ pkg/apis/scheduling/validation/validation.go
@@ -20,8 +20,8 @@ import (
 	"fmt"
 	"strings"
 
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
-	pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/validation"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -101,14 +101,14 @@ func validateControllerRef(ref *scheduling.TypedLocalObjectReference, fldPath *f
 	if ref.Kind == "" {
 		allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(ref.Kind) {
+		for _, msg := range content.IsPathSegmentName(ref.Kind) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ref.Kind, msg))
 		}
 	}
 	if ref.Name == "" {
 		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
 	} else {
-		for _, msg := range pathvalidation.IsValidPathSegmentName(ref.Name) {
+		for _, msg := range content.IsPathSegmentName(ref.Name) {
 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ref.Name, msg))
 		}
 	}
diff --git staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation.go staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation.go
index 569c1f257a7..ce4a6036d3f 100644
--- staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation.go
+++ staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation.go
@@ -20,8 +20,8 @@ import (
 	"strings"
 
 	structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	metavalidation "k8s.io/apimachinery/pkg/api/validation"
-	"k8s.io/apimachinery/pkg/api/validation/path"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	utilvalidation "k8s.io/apimachinery/pkg/util/validation"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -75,6 +75,13 @@ func validate(pth *field.Path, x interface{}, s *structuralschema.Structural) fi
 	return allErrs
 }
 
+func validatePathSegment(name string, prefix bool) []string {
+	if prefix {
+		return content.IsPathSegmentPrefix(name)
+	}
+	return content.IsPathSegmentName(name)
+}
+
 func validateEmbeddedResource(pth *field.Path, x map[string]interface{}, s *structuralschema.Structural) field.ErrorList {
 	var allErrs field.ErrorList
 
@@ -112,7 +119,7 @@ func validateEmbeddedResource(pth *field.Path, x map[string]interface{}, s *stru
 				if len(meta.Name) == 0 {
 					meta.Name = "fakename" // we have to set something to avoid an error
 				}
-				allErrs = append(allErrs, metavalidation.ValidateObjectMeta(meta, len(meta.Namespace) > 0, path.ValidatePathSegmentName, pth.Child("metadata"))...)
+				allErrs = append(allErrs, metavalidation.ValidateObjectMeta(meta, len(meta.Namespace) > 0, validatePathSegment, pth.Child("metadata"))...)
 			}
 		}
 	}
diff --git staging/src/k8s.io/apimachinery/pkg/api/validate/content/path.go staging/src/k8s.io/apimachinery/pkg/api/validate/content/path.go
new file mode 100644
index 00000000000..c41b1d47312
--- /dev/null
+++ staging/src/k8s.io/apimachinery/pkg/api/validate/content/path.go
@@ -0,0 +1,63 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package content
+
+import (
+	"fmt"
+	"strings"
+)
+
+// Strings that cannot be used as names specified as path segments (like the
+// REST API or etcd store).
+var pathSegmentNameMayNotBe = []string{".", ".."}
+
+// Substrings that cannot be used in names specified as path segments (like the
+// REST API or etcd store).
+var pathSegmentNameMayNotContain = []string{"/", "%"}
+
+// IsPathSegmentName validates the name can be safely encoded as a path
+// segment.
+//
+// Note that, for historical reason, this function does not check for
+// empty strings or impose a limit on the length of the input.
+func IsPathSegmentName(name string) []string {
+	for _, illegalName := range pathSegmentNameMayNotBe {
+		if name == illegalName {
+			return []string{fmt.Sprintf(`may not be '%s'`, illegalName)}
+		}
+	}
+
+	return IsPathSegmentPrefix(name)
+}
+
+// IsPathSegmentPrefix validates the name can be used as a prefix for a
+// name which will be encoded as a path segment It does not check for exact
+// matches with disallowed names, since an arbitrary suffix might make the name
+// valid.
+//
+// Note that, for historical reason, this function does not check for
+// empty strings or impose a limit on the length of the input.
+func IsPathSegmentPrefix(name string) []string {
+	var errors []string
+	for _, illegalContent := range pathSegmentNameMayNotContain {
+		if strings.Contains(name, illegalContent) {
+			errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
+		}
+	}
+
+	return errors
+}
diff --git staging/src/k8s.io/apimachinery/pkg/api/validation/path/name_test.go staging/src/k8s.io/apimachinery/pkg/api/validate/content/path_test.go
similarity index 58%
rename from staging/src/k8s.io/apimachinery/pkg/api/validation/path/name_test.go
rename to staging/src/k8s.io/apimachinery/pkg/api/validate/content/path_test.go
index 42890527388..16a1119e820 100644
--- staging/src/k8s.io/apimachinery/pkg/api/validation/path/name_test.go
+++ staging/src/k8s.io/apimachinery/pkg/api/validate/content/path_test.go
@@ -14,155 +14,145 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package path
+package content
 
 import (
 	"strings"
 	"testing"
 )
 
-func TestValidatePathSegmentName(t *testing.T) {
+func TestIsPathSegmentPrefix(t *testing.T) {
 	testcases := map[string]struct {
 		Name        string
-		Prefix      bool
 		ExpectedMsg string
 	}{
 		"empty": {
 			Name:        "",
-			Prefix:      false,
+			ExpectedMsg: "", // NOTE: this probably should fail
+		},
+		// NOTE: no validation of max length
+		"valid short": {
+			Name:        "foo",
 			ExpectedMsg: "",
 		},
-		"empty,prefix": {
-			Name:        "",
-			Prefix:      true,
-			ExpectedMsg: "",
-		},
-
-		"valid": {
+		"valid long": {
+			Name:        "foo.bar.baz",
+			ExpectedMsg: "",
+		},
+		// Make sure mixed case, non DNS subdomain characters are tolerated
+		"valid complex": {
+			Name:        "sha256:ABCDEF012345@ABCDEF012345",
+			ExpectedMsg: "",
+		},
+		// Make sure non-ascii characters are tolerated
+		"valid extended charset": {
+			Name:        "Iñtërnâtiônàlizætiøn",
+			ExpectedMsg: "",
+		},
+		"dot": {
+			Name:        ".",
+			ExpectedMsg: "",
+		},
+		"dot leading": {
+			Name:        ".test",
+			ExpectedMsg: "",
+		},
+		"dot dot": {
+			Name:        "..",
+			ExpectedMsg: "",
+		},
+		"dot dot leading": {
+			Name:        "..test",
+			ExpectedMsg: "",
+		},
+		"slash": {
+			Name:        "foo/bar",
+			ExpectedMsg: "/",
+		},
+		"percent": {
+			Name:        "foo%bar",
+			ExpectedMsg: "%",
+		},
+	}
+
+	for k, tc := range testcases {
+		msgs := IsPathSegmentPrefix(tc.Name)
+		if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
+			t.Errorf("%s: expected no error, got %v", k, msgs)
+		}
+		if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
+			t.Errorf("%s: expected error, got none", k)
+		}
+		if len(tc.ExpectedMsg) > 0 && !strings.Contains(msgs[0], tc.ExpectedMsg) {
+			t.Errorf("%s: expected error containing %q, got %v", k, tc.ExpectedMsg, msgs[0])
+		}
+	}
+}
+
+func TestIsPathSegmentName(t *testing.T) {
+	testcases := map[string]struct {
+		Name        string
+		ExpectedMsg string
+	}{
+		"empty": {
+			Name:        "",
+			ExpectedMsg: "", // NOTE: this probably should fail
+		},
+		// NOTE: no validation of max length
+		"valid short": {
+			Name:        "foo",
+			ExpectedMsg: "",
+		},
+		"valid long": {
 			Name:        "foo.bar.baz",
-			Prefix:      false,
 			ExpectedMsg: "",
 		},
-		"valid,prefix": {
-			Name:        "foo.bar.baz",
-			Prefix:      true,
-			ExpectedMsg: "",
-		},
-
 		// Make sure mixed case, non DNS subdomain characters are tolerated
 		"valid complex": {
 			Name:        "sha256:ABCDEF012345@ABCDEF012345",
-			Prefix:      false,
 			ExpectedMsg: "",
 		},
 		// Make sure non-ascii characters are tolerated
 		"valid extended charset": {
 			Name:        "Iñtërnâtiônàlizætiøn",
-			Prefix:      false,
 			ExpectedMsg: "",
 		},
-
 		"dot": {
 			Name:        ".",
-			Prefix:      false,
 			ExpectedMsg: ".",
 		},
 		"dot leading": {
 			Name:        ".test",
-			Prefix:      false,
 			ExpectedMsg: "",
 		},
-		"dot,prefix": {
-			Name:        ".",
-			Prefix:      true,
-			ExpectedMsg: "",
-		},
-
 		"dot dot": {
 			Name:        "..",
-			Prefix:      false,
 			ExpectedMsg: "..",
 		},
 		"dot dot leading": {
 			Name:        "..test",
-			Prefix:      false,
 			ExpectedMsg: "",
 		},
-		"dot dot,prefix": {
-			Name:        "..",
-			Prefix:      true,
-			ExpectedMsg: "",
-		},
-
 		"slash": {
 			Name:        "foo/bar",
-			Prefix:      false,
 			ExpectedMsg: "/",
 		},
-		"slash,prefix": {
-			Name:        "foo/bar",
-			Prefix:      true,
-			ExpectedMsg: "/",
-		},
-
 		"percent": {
 			Name:        "foo%bar",
-			Prefix:      false,
-			ExpectedMsg: "%",
-		},
-		"percent,prefix": {
-			Name:        "foo%bar",
-			Prefix:      true,
 			ExpectedMsg: "%",
 		},
 	}
 
 	for k, tc := range testcases {
-		msgs := ValidatePathSegmentName(tc.Name, tc.Prefix)
+		msgs := IsPathSegmentName(tc.Name)
 		if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
-			t.Errorf("%s: expected no message, got %v", k, msgs)
+			t.Errorf("%s: expected no error, got %v", k, msgs)
 		}
 		if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
-			t.Errorf("%s: expected error message, got none", k)
+			t.Errorf("%s: expected error, got none", k)
 		}
 		if len(tc.ExpectedMsg) > 0 && !strings.Contains(msgs[0], tc.ExpectedMsg) {
-			t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg, msgs[0])
-		}
-	}
-}
-
-func TestValidateWithMultiErrors(t *testing.T) {
-	testcases := map[string]struct {
-		Name        string
-		Prefix      bool
-		ExpectedMsg []string
-	}{
-		"slash,percent": {
-			Name:        "foo//bar%",
-			Prefix:      false,
-			ExpectedMsg: []string{"may not contain '/'", "may not contain '%'"},
-		},
-		"slash,percent,prefix": {
-			Name:        "foo//bar%",
-			Prefix:      true,
-			ExpectedMsg: []string{"may not contain '/'", "may not contain '%'"},
-		},
-	}
-
-	for k, tc := range testcases {
-		msgs := ValidatePathSegmentName(tc.Name, tc.Prefix)
-		if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
-			t.Errorf("%s: expected no message, got %v", k, msgs)
-		}
-		if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
-			t.Errorf("%s: expected error message, got none", k)
-		}
-		if len(tc.ExpectedMsg) > 0 {
-			for i := 0; i < len(tc.ExpectedMsg); i++ {
-				if msgs[i] != tc.ExpectedMsg[i] {
-					t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg[i], msgs[i])
-				}
-			}
+			t.Errorf("%s: expected error containing %q, got %v", k, tc.ExpectedMsg, msgs[0])
 		}
 	}
 }
diff --git staging/src/k8s.io/apimachinery/pkg/api/validation/path/name.go staging/src/k8s.io/apimachinery/pkg/api/validation/path/name.go
index ffb9f56d800..c0dee8108a2 100644
--- staging/src/k8s.io/apimachinery/pkg/api/validation/path/name.go
+++ staging/src/k8s.io/apimachinery/pkg/api/validation/path/name.go
@@ -17,52 +17,26 @@ limitations under the License.
 package path
 
 import (
-	"fmt"
-	"strings"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 )
 
-// NameMayNotBe specifies strings that cannot be used as names specified as path segments (like the REST API or etcd store)
-var NameMayNotBe = []string{".", ".."}
-
-// NameMayNotContain specifies substrings that cannot be used in names specified as path segments (like the REST API or etcd store)
-var NameMayNotContain = []string{"/", "%"}
-
 // IsValidPathSegmentName validates the name can be safely encoded as a path segment
-func IsValidPathSegmentName(name string) []string {
-	for _, illegalName := range NameMayNotBe {
-		if name == illegalName {
-			return []string{fmt.Sprintf(`may not be '%s'`, illegalName)}
-		}
-	}
-
-	var errors []string
-	for _, illegalContent := range NameMayNotContain {
-		if strings.Contains(name, illegalContent) {
-			errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
-		}
-	}
-
-	return errors
-}
+//
+// Deprecated: use content.IsPathSegmentName directly.
+var IsValidPathSegmentName = content.IsPathSegmentName
 
 // IsValidPathSegmentPrefix validates the name can be used as a prefix for a name which will be encoded as a path segment
 // It does not check for exact matches with disallowed names, since an arbitrary suffix might make the name valid
-func IsValidPathSegmentPrefix(name string) []string {
-	var errors []string
-	for _, illegalContent := range NameMayNotContain {
-		if strings.Contains(name, illegalContent) {
-			errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
-		}
-	}
-
-	return errors
-}
+//
+// Deprecated: use content.IsPathSegmentPrefix directly.
+var IsValidPathSegmentPrefix = content.IsPathSegmentPrefix
 
 // ValidatePathSegmentName validates the name can be safely encoded as a path segment
+//
+// Deprecated: use a locally defined function.
 func ValidatePathSegmentName(name string, prefix bool) []string {
 	if prefix {
 		return IsValidPathSegmentPrefix(name)
 	}
-
 	return IsValidPathSegmentName(name)
 }
diff --git staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go
index d6bf062a19b..1cf14d892fd 100644
--- staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go
+++ staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go
@@ -22,7 +22,7 @@ import (
 	"net/http"
 	"strings"
 
-	"k8s.io/apimachinery/pkg/api/validation/path"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
 	metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -246,7 +246,7 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
 
 		if opts.FieldSelector != nil {
 			if name, ok := opts.FieldSelector.RequiresExactMatch("metadata.name"); ok {
-				if len(path.IsValidPathSegmentName(name)) == 0 {
+				if len(content.IsPathSegmentName(name)) == 0 {
 					requestInfo.Name = name
 				}
 			}
diff --git staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go
index 722a312270a..906cdd7cdbb 100644
--- staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go
+++ staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go
@@ -27,8 +27,8 @@ import (
 
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	"k8s.io/apimachinery/pkg/api/validation"
-	"k8s.io/apimachinery/pkg/api/validation/path"
 	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/fields"
@@ -286,7 +286,7 @@ func NamespaceKeyFunc(ctx context.Context, prefix string, name string) (string,
 	if len(name) == 0 {
 		return "", apierrors.NewBadRequest("Name parameter required.")
 	}
-	if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
+	if msgs := content.IsPathSegmentName(name); len(msgs) != 0 {
 		return "", apierrors.NewBadRequest(fmt.Sprintf("Name parameter invalid: %q: %s", name, strings.Join(msgs, ";")))
 	}
 	key = key + "/" + name
@@ -299,7 +299,7 @@ func NoNamespaceKeyFunc(ctx context.Context, prefix string, name string) (string
 	if len(name) == 0 {
 		return "", apierrors.NewBadRequest("Name parameter required.")
 	}
-	if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
+	if msgs := content.IsPathSegmentName(name); len(msgs) != 0 {
 		return "", apierrors.NewBadRequest(fmt.Sprintf("Name parameter invalid: %q: %s", name, strings.Join(msgs, ";")))
 	}
 	key := prefix + "/" + name
diff --git staging/src/k8s.io/apiserver/pkg/registry/rest/create.go staging/src/k8s.io/apiserver/pkg/registry/rest/create.go
index b3e57d0a457..87b2c9d81bf 100644
--- staging/src/k8s.io/apiserver/pkg/registry/rest/create.go
+++ staging/src/k8s.io/apiserver/pkg/registry/rest/create.go
@@ -22,8 +22,8 @@ import (
 
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	genericvalidation "k8s.io/apimachinery/pkg/api/validation"
-	"k8s.io/apimachinery/pkg/api/validation/path"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -89,6 +89,13 @@ type RESTCreateStrategy interface {
 	Canonicalize(obj runtime.Object)
 }
 
+func validatePathSegment(name string, prefix bool) []string {
+	if prefix {
+		return content.IsPathSegmentPrefix(name)
+	}
+	return content.IsPathSegmentName(name)
+}
+
 // BeforeCreate ensures that common operations for all resources are performed on creation. It only returns
 // errors that can be converted to api.Status. It invokes PrepareForCreate, then Validate.
 // It returns nil if the object should be created.
@@ -126,7 +133,7 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime.
 	// Custom validation (including name validation) passed
 	// Now run common validation on object meta
 	// Do this *after* custom validation so that specific error messages are shown whenever possible
-	if errs := genericvalidation.ValidateObjectMetaAccessor(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata")); len(errs) > 0 {
+	if errs := genericvalidation.ValidateObjectMetaAccessor(objectMeta, strategy.NamespaceScoped(), validatePathSegment, field.NewPath("metadata")); len(errs) > 0 {
 		return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs)
 	}
 
diff --git staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go
index 000bb52497a..39833f1cbf7 100644
--- staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go
+++ staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go
@@ -28,7 +28,6 @@ import (
 	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
-	"k8s.io/apimachinery/pkg/api/validation/path"
 	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/conversion"
@@ -449,7 +448,8 @@ func (t *Tester) testCreateIgnoresMismatchedNamespace(valid runtime.Object, opts
 }
 
 func (t *Tester) testCreateValidatesNames(valid runtime.Object, opts metav1.CreateOptions) {
-	for _, invalidName := range path.NameMayNotBe {
+	invalidPathElements := []string{".", ".."}
+	for _, invalidName := range invalidPathElements {
 		objCopy := valid.DeepCopyObject()
 		objCopyMeta := t.getObjectMetaOrFail(objCopy)
 		objCopyMeta.SetName(invalidName)
@@ -461,7 +461,8 @@ func (t *Tester) testCreateValidatesNames(valid runtime.Object, opts metav1.Crea
 		}
 	}
 
-	for _, invalidSuffix := range path.NameMayNotContain {
+	invalidInPath := []string{"/", "%"}
+	for _, invalidSuffix := range invalidInPath {
 		objCopy := valid.DeepCopyObject()
 		objCopyMeta := t.getObjectMetaOrFail(objCopy)
 		objCopyMeta.SetName(objCopyMeta.GetName() + invalidSuffix)
diff --git staging/src/k8s.io/apiserver/pkg/registry/rest/update.go staging/src/k8s.io/apiserver/pkg/registry/rest/update.go
index 00f42cfe7c7..32de351aa7a 100644
--- staging/src/k8s.io/apiserver/pkg/registry/rest/update.go
+++ staging/src/k8s.io/apiserver/pkg/registry/rest/update.go
@@ -23,7 +23,6 @@ import (
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	genericvalidation "k8s.io/apimachinery/pkg/api/validation"
-	"k8s.io/apimachinery/pkg/api/validation/path"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -94,7 +93,7 @@ func validateCommonFields(obj, old runtime.Object, strategy RESTUpdateStrategy)
 	if err != nil {
 		return nil, fmt.Errorf("failed to get old object metadata: %v", err)
 	}
-	allErrs = append(allErrs, genericvalidation.ValidateObjectMetaAccessor(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata"))...)
+	allErrs = append(allErrs, genericvalidation.ValidateObjectMetaAccessor(objectMeta, strategy.NamespaceScoped(), validatePathSegment, field.NewPath("metadata"))...)
 	allErrs = append(allErrs, genericvalidation.ValidateObjectMetaAccessorUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...)
 
 	return allErrs, nil
diff --git staging/src/k8s.io/apiserver/pkg/storage/util.go staging/src/k8s.io/apiserver/pkg/storage/util.go
index 3ca9acf3bd2..941422ef5e2 100644
--- staging/src/k8s.io/apiserver/pkg/storage/util.go
+++ staging/src/k8s.io/apiserver/pkg/storage/util.go
@@ -22,7 +22,7 @@ import (
 	"sync/atomic"
 
 	"k8s.io/apimachinery/pkg/api/meta"
-	"k8s.io/apimachinery/pkg/api/validation/path"
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 )
@@ -46,7 +46,7 @@ func NamespaceKeyFunc(prefix string, obj runtime.Object) (string, error) {
 		return "", err
 	}
 	name := meta.GetName()
-	if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
+	if msgs := content.IsPathSegmentName(name); len(msgs) != 0 {
 		return "", fmt.Errorf("invalid name: %v", msgs)
 	}
 	return prefix + meta.GetNamespace() + "/" + name, nil
@@ -61,7 +61,7 @@ func NoNamespaceKeyFunc(prefix string, obj runtime.Object) (string, error) {
 		return "", err
 	}
 	name := meta.GetName()
-	if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
+	if msgs := content.IsPathSegmentName(name); len(msgs) != 0 {
 		return "", fmt.Errorf("invalid name: %v", msgs)
 	}
 	return prefix + name, nil
diff --git staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go
index 82754d622a0..4e44494dfc3 100644
--- staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go
+++ staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go
@@ -20,8 +20,8 @@ import (
 	"fmt"
 	"strings"
 
+	"k8s.io/apimachinery/pkg/api/validate/content"
 	"k8s.io/apimachinery/pkg/api/validation"
-	"k8s.io/apimachinery/pkg/api/validation/path"
 	utilvalidation "k8s.io/apimachinery/pkg/util/validation"
 	"k8s.io/apimachinery/pkg/util/validation/field"
 
@@ -34,7 +34,7 @@ func ValidateAPIService(apiService *apiregistration.APIService) field.ErrorList
 
 	allErrs := validation.ValidateObjectMeta(&apiService.ObjectMeta, false,
 		func(name string, prefix bool) []string {
-			if minimalFailures := path.IsValidPathSegmentName(name); len(minimalFailures) > 0 {
+			if minimalFailures := content.IsPathSegmentName(name); len(minimalFailures) > 0 {
 				return minimalFailures
 			}
 			// the name *must* be version.group

obj.Spec.PodGroups[0].Name = "Invalid_Name"
}),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "podGroups").Index(0).Child("name"), "Invalid_Name", "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')").WithOrigin("format=k8s-short-name"),
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.

Reviewing per-commit, so this may be fixed in a later commit?

You don't need (and we don't want) to embed the whole error string here. It just makes tests brittle. E.g. I want to fix that validation to return multiple errors, one for "begins with" and another for "contains". If I do that, I have to also fix your tests.

The check for WithOrigin("format=k8s-short-name") is sufficiently precise that I have confidence that I won't accidentally fail for a different reason but satisfy the test (which really happens in a TON of manual tests). Similarly, you don't need the value to be duplicated. So this can become:

field.Invalid(field.NewPath("spec.podGroups[0].name", nil, "").WithOrigin("format=k8s-short-name")

And it is just as precise a match, and WAY more understandable. And less brittle :)

Almost every Invalid() error can follow this pattern.

},
"duplicate podGroup names": {
input: mkValidWorkload(func(obj *scheduling.Workload) {
obj.Spec.PodGroups = append(obj.Spec.PodGroups, scheduling.PodGroup{
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.

Consider encapsulating this into a function like:

func addPodGroup(name string) func(obj *scheduling.Workload) {
    return func(obj *scheduling.Workload) {
        obj.Spec.PodGroups = append(obj.Spec.PodGroups, scheduling.PodGroup{
            //... use `name`
        })
    }
}

So this test-case becomes:

		"duplicate podGroup names": {
            input: mkValidWorkload(addPodGroup("main")),
        expectedErrs: field.ErrorList{
            field.Duplicate(field.NewPath("spec.podGroups[1]"), nil))
        }

Note that I elided the value argument because it doesn't actually add clarity (and is not actually considered in the error matcher anyway!!).

Less LOCs, way more readable.

// Declarative validation treats 0 as "missing" and returns Required error
// instead of checking minimum constraint and returning Invalid error.
"gang minCount zero": {
input: mkValidWorkload(func(obj *scheduling.Workload) {
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.

could add a tweak func setPodGroupMinCount(pgIdx int, min int) so this becomes mkValidWorkload(setPodGroupMinCount(0, 0))

},
"valid with controllerRef": {
input: mkValidWorkload(func(obj *scheduling.Workload) {
obj.Spec.ControllerRef = &scheduling.TypedLocalObjectReference{
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.

could make it a tweak func. I won't repeat this any more, I think you see the pattern. Using a literal func as a tweak should be reserved for very special cases. Prefer instead to build up a library of tweaks.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes. Added tweak funcs for the repeatatives patterns.

Comment thread staging/src/k8s.io/apimachinery/pkg/api/validate/strfmt_test.go
allErrs = append(allErrs, field.Required(podGroupsPath, "must have at least one item").MarkCoveredByDeclarative())
} else if len(spec.PodGroups) > scheduling.WorkloadMaxPodGroups {
allErrs = append(allErrs, field.TooMany(fldPath.Child("podGroups"), len(spec.PodGroups), scheduling.WorkloadMaxPodGroups))
allErrs = append(allErrs, field.TooMany(podGroupsPath, len(spec.PodGroups), scheduling.WorkloadMaxPodGroups).WithOrigin("maxItems").MarkCoveredByDeclarative())
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.

ideally these would be in the commits that made the change, but it's OK for this one, just to get it done :)

@helayoty
Copy link
Copy Markdown
Member Author

One of the side-quests of DV is scrubbing string-format validations and consolidating to a single library. I prepared a commit to move the path-segment stuff. I can either followup on this PR with my own, or I can just get you to paste it into your PR as distinct commit, and call the newly named func instead of the old (content.IsPathSegmentName() vs path.IsValidPathSegmentName())

Merged your commit here. PTAL

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

k8s-ci-robot commented Jan 13, 2026

@helayoty: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-kubernetes-apidiff-client-go 3bdde90 link false /test pull-kubernetes-apidiff-client-go
pull-kubernetes-audit-kind-conformance 3bdde90 link false /test pull-kubernetes-audit-kind-conformance
pull-kubernetes-e2e-kind-alpha-beta-enabled-conformance 3bdde90 link false /test pull-kubernetes-e2e-kind-alpha-beta-enabled-conformance
pull-kubernetes-e2e-kind-alpha-beta-enabled 3bdde90 link false /test pull-kubernetes-e2e-kind-alpha-beta-enabled
pull-kubernetes-e2e-kind-alpha-beta-features 3bdde90 link false /test pull-kubernetes-e2e-kind-alpha-beta-features
pull-kubernetes-node-e2e-containerd-2-0-dra 68fb46d link false /test pull-kubernetes-node-e2e-containerd-2-0-dra
pull-kubernetes-node-e2e-containerd-2-0-dra-alpha-beta-features 68fb46d link false /test pull-kubernetes-node-e2e-containerd-2-0-dra-alpha-beta-features
pull-kubernetes-local-e2e 68fb46d link false /test pull-kubernetes-local-e2e
pull-kubernetes-e2e-gce-cos-alpha-features 68fb46d link false /test pull-kubernetes-e2e-gce-cos-alpha-features
pull-kubernetes-e2e-gce-network-proxy-grpc 68fb46d link false /test pull-kubernetes-e2e-gce-network-proxy-grpc
pull-kubernetes-e2e-gce-network-proxy-http-connect 68fb46d link true /test pull-kubernetes-e2e-gce-network-proxy-http-connect
pull-kubernetes-e2e-gci-gce-autoscaling 68fb46d link false /test pull-kubernetes-e2e-gci-gce-autoscaling
pull-kubernetes-e2e-gce-csi-serial 68fb46d link false /test pull-kubernetes-e2e-gce-csi-serial
pull-kubernetes-e2e-gce-storage-snapshot 68fb46d link false /test pull-kubernetes-e2e-gce-storage-snapshot
pull-kubernetes-e2e-autoscaling-hpa-cpu 68fb46d link false /test pull-kubernetes-e2e-autoscaling-hpa-cpu
pull-kubernetes-e2e-autoscaling-hpa-cm 68fb46d link false /test pull-kubernetes-e2e-autoscaling-hpa-cm
check-dependency-stats 68fb46d link false /test check-dependency-stats
pull-kubernetes-dra-integration 68fb46d link false /test pull-kubernetes-dra-integration
pull-kubernetes-kind-dra-n-2 68fb46d link false /test pull-kubernetes-kind-dra-n-2
pull-kubernetes-e2e-gce-storage-slow 68fb46d link false /test pull-kubernetes-e2e-gce-storage-slow
pull-kubernetes-cross 68fb46d link false /test pull-kubernetes-cross
pull-kubernetes-kind-dra 68fb46d link false /test pull-kubernetes-kind-dra
pull-kubernetes-kind-dra-n-3 68fb46d link false /test pull-kubernetes-kind-dra-n-3
pull-kubernetes-e2e-storage-kind-disruptive 68fb46d link false /test pull-kubernetes-e2e-storage-kind-disruptive
pull-kubernetes-e2e-capz-windows-master 68fb46d link false /test pull-kubernetes-e2e-capz-windows-master
pull-kubernetes-e2e-storage-kind-vac 68fb46d link false /test pull-kubernetes-e2e-storage-kind-vac
pull-kubernetes-unit-windows-master 68fb46d link false /test pull-kubernetes-unit-windows-master

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

Details

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. I understand the commands that are listed here.

Signed-off-by: Heba Elayoty <[email protected]>
@thockin
Copy link
Copy Markdown
Member

thockin commented Jan 13, 2026

/approve

Comment thread pkg/apis/scheduling/validation/validation.go Outdated
Co-Author: Lalit Chauhan <[email protected]>

Signed-off-by: Heba Elayoty <[email protected]>
Copy link
Copy Markdown
Member

@thockin thockin left a comment

Choose a reason for hiding this comment

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

/approve

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: helayoty, thockin

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

Copy link
Copy Markdown
Contributor

@lalitc375 lalitc375 left a comment

Choose a reason for hiding this comment

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

/lgtm

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

LGTM label has been added.

DetailsGit tree hash: 96f1c5c58f401e89145a58b3ba65f3d847fee500

@lalitc375
Copy link
Copy Markdown
Contributor

/release-note-none

@pacoxu
Copy link
Copy Markdown
Member

pacoxu commented Jan 16, 2026

/release-note-none

For a feature PR with KEP, a release note is suggested. @helayoty

@helayoty
Copy link
Copy Markdown
Member Author

/area workload-aware

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. area/apiserver area/code-generation area/dependency Issues or PRs related to dependency changes area/kube-proxy area/kubeadm area/kubectl area/kubelet area/provider/gcp Issues or PRs related to gcp provider area/release-eng Issues or PRs related to the Release Engineering subproject area/test area/workload-aware Categorizes an issue or PR as relevant to Workload-aware and Topology-aware scheduling subprojects. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. release-note-none Denotes a PR that doesn't merit a release note. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/apps Categorizes an issue or PR as relevant to SIG Apps. sig/auth Categorizes an issue or PR as relevant to SIG Auth. sig/autoscaling Categorizes an issue or PR as relevant to SIG Autoscaling. sig/cli Categorizes an issue or PR as relevant to SIG CLI. sig/cloud-provider Categorizes an issue or PR as relevant to SIG Cloud Provider. sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle. sig/contributor-experience Categorizes an issue or PR as relevant to SIG Contributor Experience. sig/etcd Categorizes an issue or PR as relevant to SIG Etcd. sig/instrumentation Categorizes an issue or PR as relevant to SIG Instrumentation. sig/network Categorizes an issue or PR as relevant to SIG Network. sig/node Categorizes an issue or PR as relevant to SIG Node. sig/release Categorizes an issue or PR as relevant to SIG Release. sig/scalability Categorizes an issue or PR as relevant to SIG Scalability. sig/scheduling Categorizes an issue or PR as relevant to SIG Scheduling. sig/storage Categorizes an issue or PR as relevant to SIG Storage. sig/testing Categorizes an issue or PR as relevant to SIG Testing. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. triage/accepted Indicates an issue or PR is ready to be actively worked on. wg/device-management Categorizes an issue or PR as relevant to WG Device Management.

Projects

Archived in project
Archived in project
Archived in project
Archived in project
Archived in project

Development

Successfully merging this pull request may close these issues.