Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
425c6b1
Fix MaxLenError() to say bytes instead of characters
thockin Mar 4, 2025
455d652
Add +k8s:minimum validation tag
thockin Nov 30, 2024
82fe969
Fix validation_test for ReplicationController
thockin Dec 1, 2024
0c2aa5f
Change internal RC.Spec.Relicas to a pointer
thockin Dec 22, 2024
799abea
Add a DeclarativeValidation feature gate
thockin Dec 16, 2024
de875cf
ReplicationController: Call declarative validation
thockin Dec 16, 2024
880d940
Enable validation-gen on core/v1
thockin Mar 3, 2025
e86abeb
Declaratively validate min value for RC Replicas
thockin Nov 30, 2024
ed17836
Remove manual validation of RC Replicas min value
thockin Dec 1, 2024
7baee29
Add tests to prove declarative validation works
thockin Dec 11, 2024
107f93c
Add tests to validate against all API versions
thockin Dec 11, 2024
9f3e8ed
Add declarative default for RC.Spec.Replicas
thockin Dec 19, 2024
de2b206
WIP: optional+default == required
thockin Dec 23, 2024
3e9c99e
Declaratively validate RC...Replicas optionality
thockin Dec 19, 2024
5bdc0da
Use declarative default for RC...MinReadySeconds
thockin Dec 23, 2024
16a9309
Use declarative validation for minval of RC...MinReadySeconds
thockin Dec 23, 2024
6ccb3b7
Add comments to FunctionGen
thockin Feb 21, 2025
82c1f47
Handle optional value-types with defaults
thockin Feb 23, 2025
b1c7343
Declaratively validate MinReadySeconds optionality
thockin Feb 23, 2025
463038e
Refactor FunctionGen - no interface needed
thockin Feb 23, 2025
b795bb3
Non-pointer FunctionGen
thockin Mar 4, 2025
f9fa698
Refactor VariableGen - no interface needed
thockin Feb 23, 2025
f4a4132
Non-pointer VariableGen
thockin Mar 4, 2025
76f6175
WIP:TODO
thockin Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions TODO.validation-gen
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ P2:
- Revisit whether eachKey/eachVal can be a single function call
- Add a way to store a comment with a FunctionGen, so we could emit things like
the which tag caused it
- When we do CRDs:
- accept or warn on kubebuilder comments?


Later:
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/apiserverinternal/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,13 @@ func TestValidateStorageVersionName(t *testing.T) {
expectedErr: "",
}, {
name: strings.Repeat("x", 254) + ".tokenreviews",
expectedErr: `the group segment must be no more than 253 characters`,
expectedErr: `the group segment must be no more than 253 bytes`,
}, {
name: "authentication.k8s.io." + strings.Repeat("x", 63),
expectedErr: "",
}, {
name: "authentication.k8s.io." + strings.Repeat("x", 64),
expectedErr: `the resource segment must be no more than 63 characters`,
expectedErr: `the resource segment must be no more than 63 bytes`,
}}
for _, tc := range cases {
errs := ValidateStorageVersionName(tc.name, false)
Expand Down
5 changes: 3 additions & 2 deletions pkg/apis/batch/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/robfig/cron/v3"

apiequality "k8s.io/apimachinery/pkg/api/equality"
"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/labels"
Expand Down Expand Up @@ -716,12 +717,12 @@ func ValidateCronJobCreate(cronJob *batch.CronJob, opts apivalidation.PodValidat
// CronJobs and rcs have the same name validation
allErrs := apivalidation.ValidateObjectMeta(&cronJob.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
allErrs = append(allErrs, validateCronJobSpec(&cronJob.Spec, nil, field.NewPath("spec"), opts)...)
if len(cronJob.ObjectMeta.Name) > apimachineryvalidation.DNS1035LabelMaxLength-11 {
if max := apimachineryvalidation.DNS1035LabelMaxLength - 11; len(cronJob.ObjectMeta.Name) > max {
// The cronjob controller appends a 11-character suffix to the cronjob (`-$TIMESTAMP`) when
// creating a job. The job name length limit is 63 characters.
// Therefore cronjob names must have length <= 63-11=52. If we don't validate this here,
// then job creation will fail later.
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("name"), cronJob.ObjectMeta.Name, "must be no more than 52 characters"))
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("name"), cronJob.ObjectMeta.Name, content.MaxLenError(max)))
}
return allErrs
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/batch/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2773,7 +2773,7 @@ func TestValidateCronJob(t *testing.T) {
},
},
},
"metadata.name: must be no more than 52 characters": {
"metadata.name: must be no more than 52 bytes": {
ObjectMeta: metav1.ObjectMeta{
Name: "10000000002000000000300000000040000000005000000000123",
Namespace: metav1.NamespaceDefault,
Expand Down Expand Up @@ -2885,7 +2885,7 @@ func TestValidateCronJob(t *testing.T) {

errs = ValidateCronJobUpdate(&newSpec, &oldSpec, corevalidation.PodValidationOptions{})
if len(errs) == 0 {
if k == "metadata.name: must be no more than 52 characters" {
if k == "metadata.name: must be no more than 52 bytes" {
return
}
t.Errorf("expected failure for %s", k)
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/certificates/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func TestValidateCertificateSigningRequestCreate(t *testing.T) {
},
},
errs: field.ErrorList{
field.Invalid(specPath.Child("signerName"), fmt.Sprintf("%s.example.io", repeatString("a", 66)), fmt.Sprintf(`validating label "%s": must be no more than 63 characters`, repeatString("a", 66))),
field.Invalid(specPath.Child("signerName"), fmt.Sprintf("%s.example.io", repeatString("a", 66)), fmt.Sprintf(`validating label "%s": must be no more than 63 bytes`, repeatString("a", 66))),
},
},
"signerName of max length in format <fully-qualified-domain-name>/<resource-namespace>.<resource-name> is valid": {
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4326,7 +4326,7 @@ type PodTemplateList struct {
// a TemplateRef or a Template set.
type ReplicationControllerSpec struct {
// Replicas is the number of desired replicas.
Replicas int32
Replicas *int32

// Minimum number of seconds for which a newly created pod should be ready
// without any of its container crashing, for it to be considered available.
Expand Down
8 changes: 5 additions & 3 deletions pkg/apis/core/v1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ func Convert_apps_ReplicaSetStatus_To_v1_ReplicationControllerStatus(in *apps.Re
}

func Convert_core_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(in *core.ReplicationControllerSpec, out *v1.ReplicationControllerSpec, s conversion.Scope) error {
out.Replicas = &in.Replicas
if err := autoConvert_core_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(in, out, s); err != nil {
return err
}
out.MinReadySeconds = in.MinReadySeconds
out.Selector = in.Selector
if in.Template != nil {
Expand All @@ -214,8 +216,8 @@ func Convert_core_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(in *
}

func Convert_v1_ReplicationControllerSpec_To_core_ReplicationControllerSpec(in *v1.ReplicationControllerSpec, out *core.ReplicationControllerSpec, s conversion.Scope) error {
if in.Replicas != nil {
out.Replicas = *in.Replicas
if err := autoConvert_v1_ReplicationControllerSpec_To_core_ReplicationControllerSpec(in, out, s); err != nil {
return err
}
out.MinReadySeconds = in.MinReadySeconds
out.Selector = in.Selector
Expand Down
5 changes: 1 addition & 4 deletions pkg/apis/core/v1/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,7 @@ func SetDefaults_ReplicationController(obj *v1.ReplicationController) {
obj.Labels = labels
}
}
if obj.Spec.Replicas == nil {
obj.Spec.Replicas = new(int32)
*obj.Spec.Replicas = 1
}
// obj.Spec.Replicas is defaulted declaratively
}
func SetDefaults_Volume(obj *v1.Volume) {
if ptr.AllPtrFieldsNil(&obj.VolumeSource) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/core/v1/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ limitations under the License.
// +k8s:conversion-gen-external-types=k8s.io/api/core/v1
// +k8s:defaulter-gen=TypeMeta
// +k8s:defaulter-gen-input=k8s.io/api/core/v1
// +k8s:validation-gen=TypeMeta
// +k8s:validation-gen-input=k8s.io/api/core/v1

// Package v1 is the v1 version of the API.
package v1
8 changes: 2 additions & 6 deletions pkg/apis/core/v1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/apis/core/v1/zz_generated.defaults.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

112 changes: 112 additions & 0 deletions pkg/apis/core/v1/zz_generated.validations.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 10 additions & 4 deletions pkg/apis/core/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6245,7 +6245,7 @@ func ValidateNonEmptySelector(selectorMap map[string]string, fldPath *field.Path
}

// Validates the given template and ensures that it is in accordance with the desired selector and replicas.
func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap map[string]string, replicas int32, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap map[string]string, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
if template == nil {
allErrs = append(allErrs, field.Required(fldPath, ""))
Expand Down Expand Up @@ -6273,10 +6273,16 @@ func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap ma
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
func ValidateReplicationControllerSpec(spec, oldSpec *core.ReplicationControllerSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"), opts)...)
if !utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) {
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
if spec.Replicas == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("replicas"), ""))
} else {
allErrs = append(allErrs, ValidateNonnegativeField(int64(*spec.Replicas), fldPath.Child("replicas"))...)
}
}
allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, fldPath.Child("template"), opts)...)
return allErrs
}

Expand Down
Loading
Loading