mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-17 20:00:07 +00:00
Merge pull request #134078 from aaron-prindle/vg-enable-deviceclass
feat(validation-gen) enable declarative validation for resource.k8s.io DeviceClass
This commit is contained in:
129
pkg/registry/resource/deviceclass/declarative_validation_test.go
Normal file
129
pkg/registry/resource/deviceclass/declarative_validation_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright 2025 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 deviceclass
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
"k8s.io/kubernetes/pkg/apis/resource"
|
||||
_ "k8s.io/kubernetes/pkg/apis/resource/install"
|
||||
)
|
||||
|
||||
var apiVersions = []string{"v1", "v1beta1", "v1beta2"}
|
||||
|
||||
func TestDeclarativeValidate(t *testing.T) {
|
||||
for _, apiVersion := range apiVersions {
|
||||
t.Run(apiVersion, func(t *testing.T) {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "resource.k8s.io",
|
||||
APIVersion: apiVersion,
|
||||
Resource: "deviceclasses",
|
||||
})
|
||||
|
||||
strategy := Strategy
|
||||
|
||||
testCases := map[string]struct {
|
||||
input resource.DeviceClass
|
||||
expectedErrs field.ErrorList
|
||||
}{
|
||||
"valid": {
|
||||
input: mkDeviceClass(),
|
||||
},
|
||||
// TODO: Add more test cases
|
||||
}
|
||||
|
||||
for k, tc := range testCases {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
apitesting.VerifyValidationEquivalence(t, ctx, &tc.input, strategy.Validate, tc.expectedErrs)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeclarativeValidateUpdate(t *testing.T) {
|
||||
for _, apiVersion := range apiVersions {
|
||||
t.Run(apiVersion, func(t *testing.T) {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "resource.k8s.io",
|
||||
APIVersion: apiVersion,
|
||||
Resource: "deviceclasses",
|
||||
})
|
||||
|
||||
strategy := Strategy
|
||||
|
||||
testCases := map[string]struct {
|
||||
old resource.DeviceClass
|
||||
update resource.DeviceClass
|
||||
expectedErrs field.ErrorList
|
||||
}{
|
||||
"valid no changes": {
|
||||
old: mkDeviceClass(),
|
||||
update: mkDeviceClass(),
|
||||
},
|
||||
// TODO: Add more test cases
|
||||
}
|
||||
|
||||
for k, tc := range testCases {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
tc.old.ResourceVersion = "1"
|
||||
tc.update.ResourceVersion = "1"
|
||||
apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.update, &tc.old, strategy.ValidateUpdate, tc.expectedErrs)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create a DeviceClass with default values and optional mutators
|
||||
func mkDeviceClass(mutators ...func(*resource.DeviceClass)) resource.DeviceClass {
|
||||
dc := resource.DeviceClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-class",
|
||||
},
|
||||
Spec: resource.DeviceClassSpec{
|
||||
Selectors: []resource.DeviceSelector{
|
||||
{
|
||||
CEL: &resource.CELDeviceSelector{
|
||||
Expression: "device.driver == \"test.driver.io\"",
|
||||
},
|
||||
},
|
||||
},
|
||||
Config: []resource.DeviceClassConfiguration{
|
||||
{
|
||||
DeviceConfiguration: resource.DeviceConfiguration{
|
||||
Opaque: &resource.OpaqueDeviceConfiguration{
|
||||
Driver: "test.driver.io",
|
||||
Parameters: runtime.RawExtension{
|
||||
Raw: []byte(`{"key":"value"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, mutate := range mutators {
|
||||
mutate(&dc)
|
||||
}
|
||||
return dc
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
@@ -50,7 +51,18 @@ func (deviceClassStrategy) PrepareForCreate(ctx context.Context, obj runtime.Obj
|
||||
|
||||
func (deviceClassStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
deviceClass := obj.(*resource.DeviceClass)
|
||||
return validation.ValidateDeviceClass(deviceClass)
|
||||
errorList := validation.ValidateDeviceClass(deviceClass)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) {
|
||||
takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover)
|
||||
declarativeErrs := rest.ValidateDeclaratively(ctx, legacyscheme.Scheme, deviceClass, rest.WithTakeover(takeover))
|
||||
validationIdentifier := "deviceclass_create"
|
||||
rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errorList, declarativeErrs, takeover, validationIdentifier)
|
||||
if takeover {
|
||||
errorList = append(errorList.RemoveCoveredByDeclarative(), declarativeErrs...)
|
||||
}
|
||||
}
|
||||
return errorList
|
||||
}
|
||||
|
||||
func (deviceClassStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
|
||||
@@ -77,8 +89,22 @@ func (deviceClassStrategy) PrepareForUpdate(ctx context.Context, obj, old runtim
|
||||
}
|
||||
|
||||
func (deviceClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
errorList := validation.ValidateDeviceClass(obj.(*resource.DeviceClass))
|
||||
return append(errorList, validation.ValidateDeviceClassUpdate(obj.(*resource.DeviceClass), old.(*resource.DeviceClass))...)
|
||||
newClass := obj.(*resource.DeviceClass)
|
||||
oldClass := old.(*resource.DeviceClass)
|
||||
|
||||
errorList := validation.ValidateDeviceClass(newClass)
|
||||
errorList = append(errorList, validation.ValidateDeviceClassUpdate(newClass, oldClass)...)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) {
|
||||
takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover)
|
||||
declarativeErrs := rest.ValidateUpdateDeclaratively(ctx, legacyscheme.Scheme, newClass, oldClass, rest.WithTakeover(takeover))
|
||||
validationIdentifier := "deviceclass_update"
|
||||
rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errorList, declarativeErrs, takeover, validationIdentifier)
|
||||
if takeover {
|
||||
errorList = append(errorList.RemoveCoveredByDeclarative(), declarativeErrs...)
|
||||
}
|
||||
}
|
||||
return errorList
|
||||
}
|
||||
|
||||
func (deviceClassStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
|
||||
|
||||
Reference in New Issue
Block a user