dra: add "named resources" structured parameter model

Like the current device plugin interface, a DRA driver using this model
announces a list of resource instances. In contrast to device plugins, this
list is made available to the scheduler together with attributes that can be
used to select suitable instances when they are not all alike.

Because this is the first structured parameter model, some checks that
previously were not possible, in particular "is one structured parameter field
set", now gets enabled. Adding another structured parameter model will be
similar.

The applyconfigs code generator assumes that all types in an API are defined in
a single package. If it wasn't for that, it would be possible to place the
"named resources" types in separate packages, which makes their names in the Go
code more natural and provides an indication of their stability level because
the package name could include a version.
This commit is contained in:
Patrick Ohly
2024-02-23 15:22:02 +01:00
parent 096e948905
commit d4d5ade7f5
66 changed files with 6143 additions and 274 deletions

View File

@@ -0,0 +1,114 @@
/*
Copyright 2024 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 resource
import "k8s.io/apimachinery/pkg/api/resource"
// NamedResourcesResources is used in NodeResourceModel.
type NamedResourcesResources struct {
// The list of all individual resources instances currently available.
Instances []NamedResourcesInstance
}
// NamedResourcesInstance represents one individual hardware instance that can be selected based
// on its attributes.
type NamedResourcesInstance struct {
// Name is unique identifier among all resource instances managed by
// the driver on the node. It must be a DNS subdomain.
Name string
// Attributes defines the attributes of this resource instance.
// The name of each attribute must be unique.
Attributes []NamedResourcesAttribute
}
// NamedResourcesAttribute is a combination of an attribute name and its value.
type NamedResourcesAttribute struct {
// Name is unique identifier among all resource instances managed by
// the driver on the node. It must be a DNS subdomain.
Name string
NamedResourcesAttributeValue
}
// NamedResourcesAttributeValue must have one and only one field set.
type NamedResourcesAttributeValue struct {
// QuantityValue is a quantity.
QuantityValue *resource.Quantity
// BoolValue is a true/false value.
BoolValue *bool
// IntValue is a 64-bit integer.
IntValue *int64
// IntSliceValue is an array of 64-bit integers.
IntSliceValue *NamedResourcesIntSlice
// StringValue is a string.
StringValue *string
// StringSliceValue is an array of strings.
StringSliceValue *NamedResourcesStringSlice
// TODO: VersionValue *SemVersion
}
// NamedResourcesIntSlice contains a slice of 64-bit integers.
type NamedResourcesIntSlice struct {
// Ints is the slice of 64-bit integers.
Ints []int64
}
// NamedResourcesStringSlice contains a slice of strings.
type NamedResourcesStringSlice struct {
// Strings is the slice of strings.
Strings []string
}
// TODO
//
// A wrapper around https://pkg.go.dev/github.com/blang/semver/v4#Version which
// is encoded as a string. During decoding, it validates that the string
// can be parsed using tolerant parsing (currently trims spaces, removes a "v" prefix,
// adds a 0 patch number to versions with only major and minor components specified,
// and removes leading 0s).
// type SemVersion struct {
// semver.Version
//}
// NamedResourcesRequest is used in ResourceRequestModel.
type NamedResourcesRequest struct {
// Selector is a CEL expression which must evaluate to true if a
// resource instance is suitable. The language is as defined in
// https://kubernetes.io/docs/reference/using-api/cel/
//
// In addition, for each type NamedResourcesin AttributeValue there is a map that
// resolves to the corresponding value of the instance under evaluation.
// For example:
//
// attributes.quantity["a"].isGreaterThan(quantity("0")) &&
// attributes.stringslice["b"].isSorted()
Selector string
}
// NamedResourcesFilter is used in ResourceFilterModel.
type NamedResourcesFilter struct {
// Selector is a selector like the one in Request. It must be true for
// a resource instance to be suitable for a claim using the class.
Selector string
}
// NamedResourcesAllocationResult is used in AllocationResultModel.
type NamedResourcesAllocationResult struct {
// Name is the name of the selected resource instance.
Name string
}

View File

@@ -0,0 +1,157 @@
/*
Copyright 2022 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 validation
import (
"fmt"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/environment"
namedresourcescel "k8s.io/dynamic-resource-allocation/structured/namedresources/cel"
corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/resource"
)
var (
validateInstanceName = corevalidation.ValidateDNS1123Subdomain
validateAttributeName = corevalidation.ValidateDNS1123Subdomain
)
type Options struct {
// StoredExpressions must be true if and only if validating CEL
// expressions that were already stored persistently. This makes
// validation more permissive by enabling CEL definitions that are not
// valid yet for new expressions.
StoredExpressions bool
}
func ValidateResources(resources *resource.NamedResourcesResources, fldPath *field.Path) field.ErrorList {
allErrs := validateInstances(resources.Instances, fldPath.Child("instances"))
return allErrs
}
func validateInstances(instances []resource.NamedResourcesInstance, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
instanceNames := sets.New[string]()
for i, instance := range instances {
idxPath := fldPath.Index(i)
instanceName := instance.Name
allErrs = append(allErrs, validateInstanceName(instanceName, idxPath.Child("name"))...)
if instanceNames.Has(instanceName) {
allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), instanceName))
} else {
instanceNames.Insert(instanceName)
}
allErrs = append(allErrs, validateAttributes(instance.Attributes, idxPath.Child("attributes"))...)
}
return allErrs
}
func validateAttributes(attributes []resource.NamedResourcesAttribute, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
attributeNames := sets.New[string]()
for i, attribute := range attributes {
idxPath := fldPath.Index(i)
attributeName := attribute.Name
allErrs = append(allErrs, validateAttributeName(attributeName, idxPath.Child("name"))...)
if attributeNames.Has(attributeName) {
allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), attributeName))
} else {
attributeNames.Insert(attributeName)
}
entries := sets.New[string]()
if attribute.QuantityValue != nil {
entries.Insert("quantity")
}
if attribute.BoolValue != nil {
entries.Insert("bool")
}
if attribute.IntValue != nil {
entries.Insert("int")
}
if attribute.IntSliceValue != nil {
entries.Insert("intSlice")
}
if attribute.StringValue != nil {
entries.Insert("string")
}
if attribute.StringSliceValue != nil {
entries.Insert("stringSlice")
}
// TODO: VersionValue
switch len(entries) {
case 0:
allErrs = append(allErrs, field.Required(idxPath, "exactly one value must be set"))
case 1:
// Okay.
default:
allErrs = append(allErrs, field.Invalid(idxPath, sets.List(entries), "exactly one field must be set, not several"))
}
}
return allErrs
}
func ValidateRequest(opts Options, request *resource.NamedResourcesRequest, fldPath *field.Path) field.ErrorList {
return validateSelector(opts, request.Selector, fldPath.Child("selector"))
}
func ValidateFilter(opts Options, filter *resource.NamedResourcesFilter, fldPath *field.Path) field.ErrorList {
return validateSelector(opts, filter.Selector, fldPath.Child("selector"))
}
func validateSelector(opts Options, selector string, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if selector == "" {
allErrs = append(allErrs, field.Required(fldPath, ""))
} else {
// TODO (https://github.com/kubernetes/kubernetes/issues/123687):
// when this API gets promoted to beta, we have to
// validate new and stored expressions differently.
// While it is alpha, new expressions are allowed to
// use everything that is currently available.
// envType := environment.NewExpressions
// if opts.StoredExpressions {
// envType = environment.StoredExpressions
// }
envType := environment.StoredExpressions
result := namedresourcescel.Compiler.CompileCELExpression(selector, envType)
if result.Error != nil {
allErrs = append(allErrs, convertCELErrorToValidationError(fldPath, selector, result.Error))
}
}
return allErrs
}
func convertCELErrorToValidationError(fldPath *field.Path, expression string, err *cel.Error) *field.Error {
switch err.Type {
case cel.ErrorTypeRequired:
return field.Required(fldPath, err.Detail)
case cel.ErrorTypeInvalid:
return field.Invalid(fldPath, expression, err.Detail)
case cel.ErrorTypeInternal:
return field.InternalError(fldPath, err)
}
return field.InternalError(fldPath, fmt.Errorf("unsupported error type: %w", err))
}
func ValidateAllocationResult(result *resource.NamedResourcesAllocationResult, fldPath *field.Path) field.ErrorList {
return validateInstanceName(result.Name, fldPath.Child("name"))
}

View File

@@ -0,0 +1,149 @@
/*
Copyright 2022 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 validation
import (
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/validation/field"
resourceapi "k8s.io/kubernetes/pkg/apis/resource"
"k8s.io/utils/ptr"
)
func testResources(instances []resourceapi.NamedResourcesInstance) *resourceapi.NamedResourcesResources {
resources := &resourceapi.NamedResourcesResources{
Instances: instances,
}
return resources
}
func TestValidateResources(t *testing.T) {
goodName := "foo"
badName := "!@#$%^"
quantity := resource.MustParse("1")
scenarios := map[string]struct {
resources *resourceapi.NamedResourcesResources
wantFailures field.ErrorList
}{
"empty": {
resources: testResources(nil),
},
"good": {
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName}}),
},
"bad-name": {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("instances").Index(0).Child("name"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")},
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: badName}}),
},
"duplicate-name": {
wantFailures: field.ErrorList{field.Duplicate(field.NewPath("instances").Index(1).Child("name"), goodName)},
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName}, {Name: goodName}}),
},
"quantity": {
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName, NamedResourcesAttributeValue: resourceapi.NamedResourcesAttributeValue{QuantityValue: &quantity}}}}}),
},
"bool": {
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName, NamedResourcesAttributeValue: resourceapi.NamedResourcesAttributeValue{BoolValue: ptr.To(true)}}}}}),
},
"int": {
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName, NamedResourcesAttributeValue: resourceapi.NamedResourcesAttributeValue{IntValue: ptr.To(int64(1))}}}}}),
},
"int-slice": {
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName, NamedResourcesAttributeValue: resourceapi.NamedResourcesAttributeValue{IntSliceValue: &resourceapi.NamedResourcesIntSlice{Ints: []int64{1, 2, 3}}}}}}}),
},
"string": {
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName, NamedResourcesAttributeValue: resourceapi.NamedResourcesAttributeValue{StringValue: ptr.To("hello")}}}}}),
},
"string-slice": {
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName, NamedResourcesAttributeValue: resourceapi.NamedResourcesAttributeValue{StringSliceValue: &resourceapi.NamedResourcesStringSlice{Strings: []string{"hello"}}}}}}}),
},
// TODO: semver
"empty-attribute": {
wantFailures: field.ErrorList{field.Required(field.NewPath("instances").Index(0).Child("attributes").Index(0), "exactly one value must be set")},
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName}}}}),
},
"duplicate-value": {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("instances").Index(0).Child("attributes").Index(0), []string{"bool", "int"}, "exactly one field must be set, not several")},
resources: testResources([]resourceapi.NamedResourcesInstance{{Name: goodName, Attributes: []resourceapi.NamedResourcesAttribute{{Name: goodName, NamedResourcesAttributeValue: resourceapi.NamedResourcesAttributeValue{BoolValue: ptr.To(true), IntValue: ptr.To(int64(1))}}}}}),
},
}
for name, scenario := range scenarios {
t.Run(name, func(t *testing.T) {
errs := ValidateResources(scenario.resources, nil)
assert.Equal(t, scenario.wantFailures, errs)
})
}
}
func TestValidateSelector(t *testing.T) {
scenarios := map[string]struct {
selector string
wantFailures field.ErrorList
}{
"okay": {
selector: "true",
},
"empty": {
selector: "",
wantFailures: field.ErrorList{field.Required(nil, "")},
},
"undefined": {
selector: "nosuchvar",
wantFailures: field.ErrorList{field.Invalid(nil, "nosuchvar", "compilation failed: ERROR: <input>:1:1: undeclared reference to 'nosuchvar' (in container '')\n | nosuchvar\n | ^")},
},
"wrong-type": {
selector: "1",
wantFailures: field.ErrorList{field.Invalid(nil, "1", "must evaluate to bool")},
},
"quantity": {
selector: `attributes.quantity["name"].isGreaterThan(quantity("0"))`,
},
"bool": {
selector: `attributes.bool["name"]`,
},
"int": {
selector: `attributes.int["name"] > 0`,
},
"intslice": {
selector: `attributes.intslice["name"].isSorted()`,
},
"string": {
selector: `attributes.string["name"] == "fish"`,
},
"stringslice": {
selector: `attributes.stringslice["name"].isSorted()`,
},
// TODO: semver
}
for name, scenario := range scenarios {
t.Run(name, func(t *testing.T) {
// At the moment, there's no difference between stored and new expressions.
// This uses the stricter validation.
opts := Options{
StoredExpressions: false,
}
errs := validateSelector(opts, scenario.selector, nil)
assert.Equal(t, scenario.wantFailures, errs)
})
}
}

View File

@@ -224,7 +224,8 @@ type DriverAllocationResult struct {
// AllocationResultModel must have one and only one field set.
type AllocationResultModel struct {
// TODO: implement one structured parameter model
// NamedResources describes the allocation result when using the named resources model.
NamedResources *NamedResourcesAllocationResult
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -500,7 +501,8 @@ type NodeResourceSlice struct {
// NodeResourceModel must have one and only one field set.
type NodeResourceModel struct {
// TODO: implement one structured parameter model
// NamedResources describes available resources using the named resources model.
NamedResources *NamedResourcesResources
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -567,7 +569,8 @@ type ResourceRequest struct {
// ResourceRequestModel must have one and only one field set.
type ResourceRequestModel struct {
// TODO: implement one structured parameter model
// NamedResources describes a request for resources with the named resources model.
NamedResources *NamedResourcesRequest
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -616,7 +619,8 @@ type ResourceFilter struct {
// ResourceFilterModel must have one and only one field set.
type ResourceFilterModel struct {
// TODO: implement one structured parameter model
// NamedResources describes a resource filter using the named resources model.
NamedResources *NamedResourcesFilter
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -26,6 +26,7 @@ import (
v1 "k8s.io/api/core/v1"
v1alpha2 "k8s.io/api/resource/v1alpha2"
apiresource "k8s.io/apimachinery/pkg/api/resource"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
types "k8s.io/apimachinery/pkg/types"
@@ -80,6 +81,96 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesAllocationResult)(nil), (*resource.NamedResourcesAllocationResult)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesAllocationResult_To_resource_NamedResourcesAllocationResult(a.(*v1alpha2.NamedResourcesAllocationResult), b.(*resource.NamedResourcesAllocationResult), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesAllocationResult)(nil), (*v1alpha2.NamedResourcesAllocationResult)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesAllocationResult_To_v1alpha2_NamedResourcesAllocationResult(a.(*resource.NamedResourcesAllocationResult), b.(*v1alpha2.NamedResourcesAllocationResult), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesAttribute)(nil), (*resource.NamedResourcesAttribute)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesAttribute_To_resource_NamedResourcesAttribute(a.(*v1alpha2.NamedResourcesAttribute), b.(*resource.NamedResourcesAttribute), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesAttribute)(nil), (*v1alpha2.NamedResourcesAttribute)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesAttribute_To_v1alpha2_NamedResourcesAttribute(a.(*resource.NamedResourcesAttribute), b.(*v1alpha2.NamedResourcesAttribute), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesAttributeValue)(nil), (*resource.NamedResourcesAttributeValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesAttributeValue_To_resource_NamedResourcesAttributeValue(a.(*v1alpha2.NamedResourcesAttributeValue), b.(*resource.NamedResourcesAttributeValue), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesAttributeValue)(nil), (*v1alpha2.NamedResourcesAttributeValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesAttributeValue_To_v1alpha2_NamedResourcesAttributeValue(a.(*resource.NamedResourcesAttributeValue), b.(*v1alpha2.NamedResourcesAttributeValue), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesFilter)(nil), (*resource.NamedResourcesFilter)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesFilter_To_resource_NamedResourcesFilter(a.(*v1alpha2.NamedResourcesFilter), b.(*resource.NamedResourcesFilter), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesFilter)(nil), (*v1alpha2.NamedResourcesFilter)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesFilter_To_v1alpha2_NamedResourcesFilter(a.(*resource.NamedResourcesFilter), b.(*v1alpha2.NamedResourcesFilter), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesInstance)(nil), (*resource.NamedResourcesInstance)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesInstance_To_resource_NamedResourcesInstance(a.(*v1alpha2.NamedResourcesInstance), b.(*resource.NamedResourcesInstance), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesInstance)(nil), (*v1alpha2.NamedResourcesInstance)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesInstance_To_v1alpha2_NamedResourcesInstance(a.(*resource.NamedResourcesInstance), b.(*v1alpha2.NamedResourcesInstance), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesIntSlice)(nil), (*resource.NamedResourcesIntSlice)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesIntSlice_To_resource_NamedResourcesIntSlice(a.(*v1alpha2.NamedResourcesIntSlice), b.(*resource.NamedResourcesIntSlice), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesIntSlice)(nil), (*v1alpha2.NamedResourcesIntSlice)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesIntSlice_To_v1alpha2_NamedResourcesIntSlice(a.(*resource.NamedResourcesIntSlice), b.(*v1alpha2.NamedResourcesIntSlice), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesRequest)(nil), (*resource.NamedResourcesRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesRequest_To_resource_NamedResourcesRequest(a.(*v1alpha2.NamedResourcesRequest), b.(*resource.NamedResourcesRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesRequest)(nil), (*v1alpha2.NamedResourcesRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesRequest_To_v1alpha2_NamedResourcesRequest(a.(*resource.NamedResourcesRequest), b.(*v1alpha2.NamedResourcesRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesResources)(nil), (*resource.NamedResourcesResources)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesResources_To_resource_NamedResourcesResources(a.(*v1alpha2.NamedResourcesResources), b.(*resource.NamedResourcesResources), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesResources)(nil), (*v1alpha2.NamedResourcesResources)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesResources_To_v1alpha2_NamedResourcesResources(a.(*resource.NamedResourcesResources), b.(*v1alpha2.NamedResourcesResources), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NamedResourcesStringSlice)(nil), (*resource.NamedResourcesStringSlice)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NamedResourcesStringSlice_To_resource_NamedResourcesStringSlice(a.(*v1alpha2.NamedResourcesStringSlice), b.(*resource.NamedResourcesStringSlice), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NamedResourcesStringSlice)(nil), (*v1alpha2.NamedResourcesStringSlice)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NamedResourcesStringSlice_To_v1alpha2_NamedResourcesStringSlice(a.(*resource.NamedResourcesStringSlice), b.(*v1alpha2.NamedResourcesStringSlice), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.NodeResourceModel)(nil), (*resource.NodeResourceModel)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_NodeResourceModel_To_resource_NodeResourceModel(a.(*v1alpha2.NodeResourceModel), b.(*resource.NodeResourceModel), scope)
}); err != nil {
@@ -438,6 +529,7 @@ func Convert_resource_AllocationResult_To_v1alpha2_AllocationResult(in *resource
}
func autoConvert_v1alpha2_AllocationResultModel_To_resource_AllocationResultModel(in *v1alpha2.AllocationResultModel, out *resource.AllocationResultModel, s conversion.Scope) error {
out.NamedResources = (*resource.NamedResourcesAllocationResult)(unsafe.Pointer(in.NamedResources))
return nil
}
@@ -447,6 +539,7 @@ func Convert_v1alpha2_AllocationResultModel_To_resource_AllocationResultModel(in
}
func autoConvert_resource_AllocationResultModel_To_v1alpha2_AllocationResultModel(in *resource.AllocationResultModel, out *v1alpha2.AllocationResultModel, s conversion.Scope) error {
out.NamedResources = (*v1alpha2.NamedResourcesAllocationResult)(unsafe.Pointer(in.NamedResources))
return nil
}
@@ -533,7 +626,206 @@ func Convert_resource_DriverRequests_To_v1alpha2_DriverRequests(in *resource.Dri
return autoConvert_resource_DriverRequests_To_v1alpha2_DriverRequests(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesAllocationResult_To_resource_NamedResourcesAllocationResult(in *v1alpha2.NamedResourcesAllocationResult, out *resource.NamedResourcesAllocationResult, s conversion.Scope) error {
out.Name = in.Name
return nil
}
// Convert_v1alpha2_NamedResourcesAllocationResult_To_resource_NamedResourcesAllocationResult is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesAllocationResult_To_resource_NamedResourcesAllocationResult(in *v1alpha2.NamedResourcesAllocationResult, out *resource.NamedResourcesAllocationResult, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesAllocationResult_To_resource_NamedResourcesAllocationResult(in, out, s)
}
func autoConvert_resource_NamedResourcesAllocationResult_To_v1alpha2_NamedResourcesAllocationResult(in *resource.NamedResourcesAllocationResult, out *v1alpha2.NamedResourcesAllocationResult, s conversion.Scope) error {
out.Name = in.Name
return nil
}
// Convert_resource_NamedResourcesAllocationResult_To_v1alpha2_NamedResourcesAllocationResult is an autogenerated conversion function.
func Convert_resource_NamedResourcesAllocationResult_To_v1alpha2_NamedResourcesAllocationResult(in *resource.NamedResourcesAllocationResult, out *v1alpha2.NamedResourcesAllocationResult, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesAllocationResult_To_v1alpha2_NamedResourcesAllocationResult(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesAttribute_To_resource_NamedResourcesAttribute(in *v1alpha2.NamedResourcesAttribute, out *resource.NamedResourcesAttribute, s conversion.Scope) error {
out.Name = in.Name
if err := Convert_v1alpha2_NamedResourcesAttributeValue_To_resource_NamedResourcesAttributeValue(&in.NamedResourcesAttributeValue, &out.NamedResourcesAttributeValue, s); err != nil {
return err
}
return nil
}
// Convert_v1alpha2_NamedResourcesAttribute_To_resource_NamedResourcesAttribute is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesAttribute_To_resource_NamedResourcesAttribute(in *v1alpha2.NamedResourcesAttribute, out *resource.NamedResourcesAttribute, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesAttribute_To_resource_NamedResourcesAttribute(in, out, s)
}
func autoConvert_resource_NamedResourcesAttribute_To_v1alpha2_NamedResourcesAttribute(in *resource.NamedResourcesAttribute, out *v1alpha2.NamedResourcesAttribute, s conversion.Scope) error {
out.Name = in.Name
if err := Convert_resource_NamedResourcesAttributeValue_To_v1alpha2_NamedResourcesAttributeValue(&in.NamedResourcesAttributeValue, &out.NamedResourcesAttributeValue, s); err != nil {
return err
}
return nil
}
// Convert_resource_NamedResourcesAttribute_To_v1alpha2_NamedResourcesAttribute is an autogenerated conversion function.
func Convert_resource_NamedResourcesAttribute_To_v1alpha2_NamedResourcesAttribute(in *resource.NamedResourcesAttribute, out *v1alpha2.NamedResourcesAttribute, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesAttribute_To_v1alpha2_NamedResourcesAttribute(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesAttributeValue_To_resource_NamedResourcesAttributeValue(in *v1alpha2.NamedResourcesAttributeValue, out *resource.NamedResourcesAttributeValue, s conversion.Scope) error {
out.QuantityValue = (*apiresource.Quantity)(unsafe.Pointer(in.QuantityValue))
out.BoolValue = (*bool)(unsafe.Pointer(in.BoolValue))
out.IntValue = (*int64)(unsafe.Pointer(in.IntValue))
out.IntSliceValue = (*resource.NamedResourcesIntSlice)(unsafe.Pointer(in.IntSliceValue))
out.StringValue = (*string)(unsafe.Pointer(in.StringValue))
out.StringSliceValue = (*resource.NamedResourcesStringSlice)(unsafe.Pointer(in.StringSliceValue))
return nil
}
// Convert_v1alpha2_NamedResourcesAttributeValue_To_resource_NamedResourcesAttributeValue is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesAttributeValue_To_resource_NamedResourcesAttributeValue(in *v1alpha2.NamedResourcesAttributeValue, out *resource.NamedResourcesAttributeValue, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesAttributeValue_To_resource_NamedResourcesAttributeValue(in, out, s)
}
func autoConvert_resource_NamedResourcesAttributeValue_To_v1alpha2_NamedResourcesAttributeValue(in *resource.NamedResourcesAttributeValue, out *v1alpha2.NamedResourcesAttributeValue, s conversion.Scope) error {
out.QuantityValue = (*apiresource.Quantity)(unsafe.Pointer(in.QuantityValue))
out.BoolValue = (*bool)(unsafe.Pointer(in.BoolValue))
out.IntValue = (*int64)(unsafe.Pointer(in.IntValue))
out.IntSliceValue = (*v1alpha2.NamedResourcesIntSlice)(unsafe.Pointer(in.IntSliceValue))
out.StringValue = (*string)(unsafe.Pointer(in.StringValue))
out.StringSliceValue = (*v1alpha2.NamedResourcesStringSlice)(unsafe.Pointer(in.StringSliceValue))
return nil
}
// Convert_resource_NamedResourcesAttributeValue_To_v1alpha2_NamedResourcesAttributeValue is an autogenerated conversion function.
func Convert_resource_NamedResourcesAttributeValue_To_v1alpha2_NamedResourcesAttributeValue(in *resource.NamedResourcesAttributeValue, out *v1alpha2.NamedResourcesAttributeValue, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesAttributeValue_To_v1alpha2_NamedResourcesAttributeValue(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesFilter_To_resource_NamedResourcesFilter(in *v1alpha2.NamedResourcesFilter, out *resource.NamedResourcesFilter, s conversion.Scope) error {
out.Selector = in.Selector
return nil
}
// Convert_v1alpha2_NamedResourcesFilter_To_resource_NamedResourcesFilter is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesFilter_To_resource_NamedResourcesFilter(in *v1alpha2.NamedResourcesFilter, out *resource.NamedResourcesFilter, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesFilter_To_resource_NamedResourcesFilter(in, out, s)
}
func autoConvert_resource_NamedResourcesFilter_To_v1alpha2_NamedResourcesFilter(in *resource.NamedResourcesFilter, out *v1alpha2.NamedResourcesFilter, s conversion.Scope) error {
out.Selector = in.Selector
return nil
}
// Convert_resource_NamedResourcesFilter_To_v1alpha2_NamedResourcesFilter is an autogenerated conversion function.
func Convert_resource_NamedResourcesFilter_To_v1alpha2_NamedResourcesFilter(in *resource.NamedResourcesFilter, out *v1alpha2.NamedResourcesFilter, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesFilter_To_v1alpha2_NamedResourcesFilter(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesInstance_To_resource_NamedResourcesInstance(in *v1alpha2.NamedResourcesInstance, out *resource.NamedResourcesInstance, s conversion.Scope) error {
out.Name = in.Name
out.Attributes = *(*[]resource.NamedResourcesAttribute)(unsafe.Pointer(&in.Attributes))
return nil
}
// Convert_v1alpha2_NamedResourcesInstance_To_resource_NamedResourcesInstance is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesInstance_To_resource_NamedResourcesInstance(in *v1alpha2.NamedResourcesInstance, out *resource.NamedResourcesInstance, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesInstance_To_resource_NamedResourcesInstance(in, out, s)
}
func autoConvert_resource_NamedResourcesInstance_To_v1alpha2_NamedResourcesInstance(in *resource.NamedResourcesInstance, out *v1alpha2.NamedResourcesInstance, s conversion.Scope) error {
out.Name = in.Name
out.Attributes = *(*[]v1alpha2.NamedResourcesAttribute)(unsafe.Pointer(&in.Attributes))
return nil
}
// Convert_resource_NamedResourcesInstance_To_v1alpha2_NamedResourcesInstance is an autogenerated conversion function.
func Convert_resource_NamedResourcesInstance_To_v1alpha2_NamedResourcesInstance(in *resource.NamedResourcesInstance, out *v1alpha2.NamedResourcesInstance, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesInstance_To_v1alpha2_NamedResourcesInstance(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesIntSlice_To_resource_NamedResourcesIntSlice(in *v1alpha2.NamedResourcesIntSlice, out *resource.NamedResourcesIntSlice, s conversion.Scope) error {
out.Ints = *(*[]int64)(unsafe.Pointer(&in.Ints))
return nil
}
// Convert_v1alpha2_NamedResourcesIntSlice_To_resource_NamedResourcesIntSlice is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesIntSlice_To_resource_NamedResourcesIntSlice(in *v1alpha2.NamedResourcesIntSlice, out *resource.NamedResourcesIntSlice, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesIntSlice_To_resource_NamedResourcesIntSlice(in, out, s)
}
func autoConvert_resource_NamedResourcesIntSlice_To_v1alpha2_NamedResourcesIntSlice(in *resource.NamedResourcesIntSlice, out *v1alpha2.NamedResourcesIntSlice, s conversion.Scope) error {
out.Ints = *(*[]int64)(unsafe.Pointer(&in.Ints))
return nil
}
// Convert_resource_NamedResourcesIntSlice_To_v1alpha2_NamedResourcesIntSlice is an autogenerated conversion function.
func Convert_resource_NamedResourcesIntSlice_To_v1alpha2_NamedResourcesIntSlice(in *resource.NamedResourcesIntSlice, out *v1alpha2.NamedResourcesIntSlice, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesIntSlice_To_v1alpha2_NamedResourcesIntSlice(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesRequest_To_resource_NamedResourcesRequest(in *v1alpha2.NamedResourcesRequest, out *resource.NamedResourcesRequest, s conversion.Scope) error {
out.Selector = in.Selector
return nil
}
// Convert_v1alpha2_NamedResourcesRequest_To_resource_NamedResourcesRequest is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesRequest_To_resource_NamedResourcesRequest(in *v1alpha2.NamedResourcesRequest, out *resource.NamedResourcesRequest, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesRequest_To_resource_NamedResourcesRequest(in, out, s)
}
func autoConvert_resource_NamedResourcesRequest_To_v1alpha2_NamedResourcesRequest(in *resource.NamedResourcesRequest, out *v1alpha2.NamedResourcesRequest, s conversion.Scope) error {
out.Selector = in.Selector
return nil
}
// Convert_resource_NamedResourcesRequest_To_v1alpha2_NamedResourcesRequest is an autogenerated conversion function.
func Convert_resource_NamedResourcesRequest_To_v1alpha2_NamedResourcesRequest(in *resource.NamedResourcesRequest, out *v1alpha2.NamedResourcesRequest, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesRequest_To_v1alpha2_NamedResourcesRequest(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesResources_To_resource_NamedResourcesResources(in *v1alpha2.NamedResourcesResources, out *resource.NamedResourcesResources, s conversion.Scope) error {
out.Instances = *(*[]resource.NamedResourcesInstance)(unsafe.Pointer(&in.Instances))
return nil
}
// Convert_v1alpha2_NamedResourcesResources_To_resource_NamedResourcesResources is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesResources_To_resource_NamedResourcesResources(in *v1alpha2.NamedResourcesResources, out *resource.NamedResourcesResources, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesResources_To_resource_NamedResourcesResources(in, out, s)
}
func autoConvert_resource_NamedResourcesResources_To_v1alpha2_NamedResourcesResources(in *resource.NamedResourcesResources, out *v1alpha2.NamedResourcesResources, s conversion.Scope) error {
out.Instances = *(*[]v1alpha2.NamedResourcesInstance)(unsafe.Pointer(&in.Instances))
return nil
}
// Convert_resource_NamedResourcesResources_To_v1alpha2_NamedResourcesResources is an autogenerated conversion function.
func Convert_resource_NamedResourcesResources_To_v1alpha2_NamedResourcesResources(in *resource.NamedResourcesResources, out *v1alpha2.NamedResourcesResources, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesResources_To_v1alpha2_NamedResourcesResources(in, out, s)
}
func autoConvert_v1alpha2_NamedResourcesStringSlice_To_resource_NamedResourcesStringSlice(in *v1alpha2.NamedResourcesStringSlice, out *resource.NamedResourcesStringSlice, s conversion.Scope) error {
out.Strings = *(*[]string)(unsafe.Pointer(&in.Strings))
return nil
}
// Convert_v1alpha2_NamedResourcesStringSlice_To_resource_NamedResourcesStringSlice is an autogenerated conversion function.
func Convert_v1alpha2_NamedResourcesStringSlice_To_resource_NamedResourcesStringSlice(in *v1alpha2.NamedResourcesStringSlice, out *resource.NamedResourcesStringSlice, s conversion.Scope) error {
return autoConvert_v1alpha2_NamedResourcesStringSlice_To_resource_NamedResourcesStringSlice(in, out, s)
}
func autoConvert_resource_NamedResourcesStringSlice_To_v1alpha2_NamedResourcesStringSlice(in *resource.NamedResourcesStringSlice, out *v1alpha2.NamedResourcesStringSlice, s conversion.Scope) error {
out.Strings = *(*[]string)(unsafe.Pointer(&in.Strings))
return nil
}
// Convert_resource_NamedResourcesStringSlice_To_v1alpha2_NamedResourcesStringSlice is an autogenerated conversion function.
func Convert_resource_NamedResourcesStringSlice_To_v1alpha2_NamedResourcesStringSlice(in *resource.NamedResourcesStringSlice, out *v1alpha2.NamedResourcesStringSlice, s conversion.Scope) error {
return autoConvert_resource_NamedResourcesStringSlice_To_v1alpha2_NamedResourcesStringSlice(in, out, s)
}
func autoConvert_v1alpha2_NodeResourceModel_To_resource_NodeResourceModel(in *v1alpha2.NodeResourceModel, out *resource.NodeResourceModel, s conversion.Scope) error {
out.NamedResources = (*resource.NamedResourcesResources)(unsafe.Pointer(in.NamedResources))
return nil
}
@@ -543,6 +835,7 @@ func Convert_v1alpha2_NodeResourceModel_To_resource_NodeResourceModel(in *v1alph
}
func autoConvert_resource_NodeResourceModel_To_v1alpha2_NodeResourceModel(in *resource.NodeResourceModel, out *v1alpha2.NodeResourceModel, s conversion.Scope) error {
out.NamedResources = (*v1alpha2.NamedResourcesResources)(unsafe.Pointer(in.NamedResources))
return nil
}
@@ -1264,6 +1557,7 @@ func Convert_resource_ResourceFilter_To_v1alpha2_ResourceFilter(in *resource.Res
}
func autoConvert_v1alpha2_ResourceFilterModel_To_resource_ResourceFilterModel(in *v1alpha2.ResourceFilterModel, out *resource.ResourceFilterModel, s conversion.Scope) error {
out.NamedResources = (*resource.NamedResourcesFilter)(unsafe.Pointer(in.NamedResources))
return nil
}
@@ -1273,6 +1567,7 @@ func Convert_v1alpha2_ResourceFilterModel_To_resource_ResourceFilterModel(in *v1
}
func autoConvert_resource_ResourceFilterModel_To_v1alpha2_ResourceFilterModel(in *resource.ResourceFilterModel, out *v1alpha2.ResourceFilterModel, s conversion.Scope) error {
out.NamedResources = (*v1alpha2.NamedResourcesFilter)(unsafe.Pointer(in.NamedResources))
return nil
}
@@ -1352,6 +1647,7 @@ func Convert_resource_ResourceRequest_To_v1alpha2_ResourceRequest(in *resource.R
}
func autoConvert_v1alpha2_ResourceRequestModel_To_resource_ResourceRequestModel(in *v1alpha2.ResourceRequestModel, out *resource.ResourceRequestModel, s conversion.Scope) error {
out.NamedResources = (*resource.NamedResourcesRequest)(unsafe.Pointer(in.NamedResources))
return nil
}
@@ -1361,6 +1657,7 @@ func Convert_v1alpha2_ResourceRequestModel_To_resource_ResourceRequestModel(in *
}
func autoConvert_resource_ResourceRequestModel_To_v1alpha2_ResourceRequestModel(in *resource.ResourceRequestModel, out *v1alpha2.ResourceRequestModel, s conversion.Scope) error {
out.NamedResources = (*v1alpha2.NamedResourcesRequest)(unsafe.Pointer(in.NamedResources))
return nil
}

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/resource"
namedresourcesvalidation "k8s.io/kubernetes/pkg/apis/resource/structured/namedresources/validation"
)
// validateResourceDriverName reuses the validation of a CSI driver because
@@ -236,10 +237,13 @@ func validateDriverAllocationResults(results []resource.DriverAllocationResult,
func validateAllocationResultModel(model *resource.AllocationResultModel, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
entries := sets.New[string]()
// TODO: implement one structured parameter model
if model.NamedResources != nil {
entries.Insert("namedResources")
allErrs = append(allErrs, namedresourcesvalidation.ValidateAllocationResult(model.NamedResources, fldPath.Child("namedResources"))...)
}
switch len(entries) {
case 0:
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
case 1:
// Okay.
default:
@@ -396,10 +400,13 @@ func ValidateNodeResourceSlice(nodeResourceSlice *resource.NodeResourceSlice) fi
func validateNodeResourceModel(model *resource.NodeResourceModel, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
entries := sets.New[string]()
// TODO: implement one structured parameter model
if model.NamedResources != nil {
entries.Insert("namedResources")
allErrs = append(allErrs, namedresourcesvalidation.ValidateResources(model.NamedResources, fldPath.Child("namedResources"))...)
}
switch len(entries) {
case 0:
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
case 1:
// Okay.
default:
@@ -463,10 +470,13 @@ func validateResourceRequests(requests []resource.ResourceRequest, fldPath *fiel
func validateResourceRequestModel(model *resource.ResourceRequestModel, fldPath *field.Path, requestStored bool) field.ErrorList {
var allErrs field.ErrorList
entries := sets.New[string]()
// TODO: implement one structured parameter model
if model.NamedResources != nil {
entries.Insert("namedResources")
allErrs = append(allErrs, namedresourcesvalidation.ValidateRequest(namedresourcesvalidation.Options{StoredExpressions: requestStored}, model.NamedResources, fldPath.Child("namedResources"))...)
}
switch len(entries) {
case 0:
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
case 1:
// Okay.
default:
@@ -516,10 +526,13 @@ func validateResourceFilters(filters []resource.ResourceFilter, fldPath *field.P
func validateResourceFilterModel(model *resource.ResourceFilterModel, fldPath *field.Path, filtersStored bool) field.ErrorList {
var allErrs field.ErrorList
entries := sets.New[string]()
// TODO: implement one structured parameter model
if model.NamedResources != nil {
entries.Insert("namedResources")
allErrs = append(allErrs, namedresourcesvalidation.ValidateFilter(namedresourcesvalidation.Options{StoredExpressions: filtersStored}, model.NamedResources, fldPath.Child("namedResources"))...)
}
switch len(entries) {
case 0:
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
case 1:
// Okay.
default:

View File

@@ -32,10 +32,10 @@ func testNodeResourceSlice(name, nodeName, driverName string) *resource.NodeReso
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
NodeName: nodeName,
DriverName: driverName,
NodeName: nodeName,
DriverName: driverName,
NodeResourceModel: resource.NodeResourceModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesResources{},
},
}
}
@@ -188,15 +188,14 @@ func TestValidateNodeResourceSlice(t *testing.T) {
slice: testNodeResourceSlice(goodName, goodName, badName),
},
// TODO: implement one structured parameter model
// "empty-model": {
// wantFailures: field.ErrorList{field.Required(nil, "exactly one structured model field must be set")},
// slice: func() *resource.NodeResourceSlice {
// slice := testNodeResourceSlice(goodName, goodName, driverName)
// slice.NodeResourceModel = resource.NodeResourceModel{}
// return slice
// }(),
// },
"empty-model": {
wantFailures: field.ErrorList{field.Required(nil, "exactly one structured model field must be set")},
slice: func() *resource.NodeResourceSlice {
slice := testNodeResourceSlice(goodName, goodName, driverName)
slice.NodeResourceModel = resource.NodeResourceModel{}
return slice
}(),
},
}
for name, scenario := range scenarios {

View File

@@ -400,6 +400,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
"invalid-add-allocation-structured": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("status", "allocation", "resourceHandles").Index(0).Child("structuredData", "nodeName"), "", "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')"),
field.Required(field.NewPath("status", "allocation", "resourceHandles").Index(0).Child("structuredData", "results").Index(1), "exactly one structured model field must be set"),
},
oldClaim: validClaim,
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
@@ -410,7 +411,16 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
DriverName: "valid",
StructuredData: &resource.StructuredResourceHandle{
Results: []resource.DriverAllocationResult{
// TODO: empty AllocationResultMode
{
AllocationResultModel: resource.AllocationResultModel{
NamedResources: &resource.NamedResourcesAllocationResult{
Name: "some-resource-instance",
},
},
},
{
AllocationResultModel: resource.AllocationResultModel{}, // invalid
},
},
},
},

View File

@@ -186,15 +186,14 @@ func TestValidateResourceClaimParameters(t *testing.T) {
}(),
},
// TODO: implement one structured parameter model
// "empty-model": {
// wantFailures: field.ErrorList{field.Required(field.NewPath("driverRequests").Index(0).Child("requests").Index(0), "exactly one structured model field must be set")},
// parameters: func() *resource.ResourceClaimParameters {
// parameters := testResourceClaimParameters(goodName, goodName, goodRequests)
// parameters.DriverRequests = []resource.DriverRequests{{DriverName: goodName, Requests: []resource.ResourceRequest{{}}}}
// return parameters
// }(),
// },
"empty-model": {
wantFailures: field.ErrorList{field.Required(field.NewPath("driverRequests").Index(0).Child("requests").Index(0), "exactly one structured model field must be set")},
parameters: func() *resource.ResourceClaimParameters {
parameters := testResourceClaimParameters(goodName, goodName, goodRequests)
parameters.DriverRequests = []resource.DriverRequests{{DriverName: goodName, Requests: []resource.ResourceRequest{{}}}}
return parameters
}(),
},
"empty-requests": {
wantFailures: field.ErrorList{field.Required(field.NewPath("driverRequests").Index(0).Child("requests"), "empty entries with no requests are not allowed")},
@@ -215,7 +214,7 @@ func TestValidateResourceClaimParameters(t *testing.T) {
Requests: []resource.ResourceRequest{
{
ResourceRequestModel: resource.ResourceRequestModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesRequest{Selector: "true"},
},
},
},
@@ -225,7 +224,7 @@ func TestValidateResourceClaimParameters(t *testing.T) {
Requests: []resource.ResourceRequest{
{
ResourceRequestModel: resource.ResourceRequestModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesRequest{Selector: "true"},
},
},
},
@@ -245,8 +244,7 @@ func TestValidateResourceClaimParameters(t *testing.T) {
Requests: []resource.ResourceRequest{
{
ResourceRequestModel: resource.ResourceRequestModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesRequest{Selector: "true"},
},
},
},
@@ -256,7 +254,7 @@ func TestValidateResourceClaimParameters(t *testing.T) {
Requests: []resource.ResourceRequest{
{
ResourceRequestModel: resource.ResourceRequestModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesRequest{Selector: "true"},
},
},
},

View File

@@ -186,15 +186,14 @@ func TestValidateResourceClassParameters(t *testing.T) {
}(),
},
// TODO: implement one structured parameter model
// "empty-model": {
// wantFailures: field.ErrorList{field.Required(field.NewPath("filters").Index(0), "exactly one structured model field must be set")},
// parameters: func() *resource.ResourceClassParameters {
// parameters := testResourceClassParameters(goodName, goodName, goodFilters)
// parameters.Filters = []resource.ResourceFilter{{DriverName: goodName}}
// return parameters
// }(),
// },
"empty-model": {
wantFailures: field.ErrorList{field.Required(field.NewPath("filters").Index(0), "exactly one structured model field must be set")},
parameters: func() *resource.ResourceClassParameters {
parameters := testResourceClassParameters(goodName, goodName, goodFilters)
parameters.Filters = []resource.ResourceFilter{{DriverName: goodName}}
return parameters
}(),
},
"filters-invalid-driver": {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("filters").Index(1).Child("driverName"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")},
@@ -202,15 +201,15 @@ func TestValidateResourceClassParameters(t *testing.T) {
parameters := testResourceClassParameters(goodName, goodName, goodFilters)
parameters.Filters = []resource.ResourceFilter{
{
DriverName: goodName,
DriverName: goodName,
ResourceFilterModel: resource.ResourceFilterModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesFilter{Selector: "true"},
},
},
{
DriverName: badName,
DriverName: badName,
ResourceFilterModel: resource.ResourceFilterModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesFilter{Selector: "true"},
},
},
}
@@ -224,14 +223,16 @@ func TestValidateResourceClassParameters(t *testing.T) {
parameters := testResourceClassParameters(goodName, goodName, goodFilters)
parameters.Filters = []resource.ResourceFilter{
{
DriverName: goodName,
DriverName: goodName,
ResourceFilterModel: resource.ResourceFilterModel{
// TODO: implement one structured parameter model
NamedResources: &resource.NamedResourcesFilter{Selector: "true"},
},
},
{
DriverName: goodName,
ResourceFilterModel: resource.ResourceFilterModel{},
DriverName: goodName,
ResourceFilterModel: resource.ResourceFilterModel{
NamedResources: &resource.NamedResourcesFilter{Selector: "true"},
},
},
}
return parameters

View File

@@ -57,6 +57,11 @@ func (in *AllocationResult) DeepCopy() *AllocationResult {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllocationResultModel) DeepCopyInto(out *AllocationResultModel) {
*out = *in
if in.NamedResources != nil {
in, out := &in.NamedResources, &out.NamedResources
*out = new(NamedResourcesAllocationResult)
**out = **in
}
return
}
@@ -76,7 +81,7 @@ func (in *DriverAllocationResult) DeepCopyInto(out *DriverAllocationResult) {
if in.VendorRequestParameters != nil {
out.VendorRequestParameters = in.VendorRequestParameters.DeepCopyObject()
}
out.AllocationResultModel = in.AllocationResultModel
in.AllocationResultModel.DeepCopyInto(&out.AllocationResultModel)
return
}
@@ -116,9 +121,213 @@ func (in *DriverRequests) DeepCopy() *DriverRequests {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesAllocationResult) DeepCopyInto(out *NamedResourcesAllocationResult) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesAllocationResult.
func (in *NamedResourcesAllocationResult) DeepCopy() *NamedResourcesAllocationResult {
if in == nil {
return nil
}
out := new(NamedResourcesAllocationResult)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesAttribute) DeepCopyInto(out *NamedResourcesAttribute) {
*out = *in
in.NamedResourcesAttributeValue.DeepCopyInto(&out.NamedResourcesAttributeValue)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesAttribute.
func (in *NamedResourcesAttribute) DeepCopy() *NamedResourcesAttribute {
if in == nil {
return nil
}
out := new(NamedResourcesAttribute)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesAttributeValue) DeepCopyInto(out *NamedResourcesAttributeValue) {
*out = *in
if in.QuantityValue != nil {
in, out := &in.QuantityValue, &out.QuantityValue
x := (*in).DeepCopy()
*out = &x
}
if in.BoolValue != nil {
in, out := &in.BoolValue, &out.BoolValue
*out = new(bool)
**out = **in
}
if in.IntValue != nil {
in, out := &in.IntValue, &out.IntValue
*out = new(int64)
**out = **in
}
if in.IntSliceValue != nil {
in, out := &in.IntSliceValue, &out.IntSliceValue
*out = new(NamedResourcesIntSlice)
(*in).DeepCopyInto(*out)
}
if in.StringValue != nil {
in, out := &in.StringValue, &out.StringValue
*out = new(string)
**out = **in
}
if in.StringSliceValue != nil {
in, out := &in.StringSliceValue, &out.StringSliceValue
*out = new(NamedResourcesStringSlice)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesAttributeValue.
func (in *NamedResourcesAttributeValue) DeepCopy() *NamedResourcesAttributeValue {
if in == nil {
return nil
}
out := new(NamedResourcesAttributeValue)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesFilter) DeepCopyInto(out *NamedResourcesFilter) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesFilter.
func (in *NamedResourcesFilter) DeepCopy() *NamedResourcesFilter {
if in == nil {
return nil
}
out := new(NamedResourcesFilter)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesInstance) DeepCopyInto(out *NamedResourcesInstance) {
*out = *in
if in.Attributes != nil {
in, out := &in.Attributes, &out.Attributes
*out = make([]NamedResourcesAttribute, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesInstance.
func (in *NamedResourcesInstance) DeepCopy() *NamedResourcesInstance {
if in == nil {
return nil
}
out := new(NamedResourcesInstance)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesIntSlice) DeepCopyInto(out *NamedResourcesIntSlice) {
*out = *in
if in.Ints != nil {
in, out := &in.Ints, &out.Ints
*out = make([]int64, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesIntSlice.
func (in *NamedResourcesIntSlice) DeepCopy() *NamedResourcesIntSlice {
if in == nil {
return nil
}
out := new(NamedResourcesIntSlice)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesRequest) DeepCopyInto(out *NamedResourcesRequest) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesRequest.
func (in *NamedResourcesRequest) DeepCopy() *NamedResourcesRequest {
if in == nil {
return nil
}
out := new(NamedResourcesRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesResources) DeepCopyInto(out *NamedResourcesResources) {
*out = *in
if in.Instances != nil {
in, out := &in.Instances, &out.Instances
*out = make([]NamedResourcesInstance, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesResources.
func (in *NamedResourcesResources) DeepCopy() *NamedResourcesResources {
if in == nil {
return nil
}
out := new(NamedResourcesResources)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamedResourcesStringSlice) DeepCopyInto(out *NamedResourcesStringSlice) {
*out = *in
if in.Strings != nil {
in, out := &in.Strings, &out.Strings
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedResourcesStringSlice.
func (in *NamedResourcesStringSlice) DeepCopy() *NamedResourcesStringSlice {
if in == nil {
return nil
}
out := new(NamedResourcesStringSlice)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeResourceModel) DeepCopyInto(out *NodeResourceModel) {
*out = *in
if in.NamedResources != nil {
in, out := &in.NamedResources, &out.NamedResources
*out = new(NamedResourcesResources)
(*in).DeepCopyInto(*out)
}
return
}
@@ -137,7 +346,7 @@ func (in *NodeResourceSlice) DeepCopyInto(out *NodeResourceSlice) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.NodeResourceModel = in.NodeResourceModel
in.NodeResourceModel.DeepCopyInto(&out.NodeResourceModel)
return
}
@@ -701,7 +910,9 @@ func (in *ResourceClassParameters) DeepCopyInto(out *ResourceClassParameters) {
if in.Filters != nil {
in, out := &in.Filters, &out.Filters
*out = make([]ResourceFilter, len(*in))
copy(*out, *in)
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
@@ -776,7 +987,7 @@ func (in *ResourceClassParametersReference) DeepCopy() *ResourceClassParametersR
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceFilter) DeepCopyInto(out *ResourceFilter) {
*out = *in
out.ResourceFilterModel = in.ResourceFilterModel
in.ResourceFilterModel.DeepCopyInto(&out.ResourceFilterModel)
return
}
@@ -793,6 +1004,11 @@ func (in *ResourceFilter) DeepCopy() *ResourceFilter {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceFilterModel) DeepCopyInto(out *ResourceFilterModel) {
*out = *in
if in.NamedResources != nil {
in, out := &in.NamedResources, &out.NamedResources
*out = new(NamedResourcesFilter)
**out = **in
}
return
}
@@ -833,7 +1049,7 @@ func (in *ResourceRequest) DeepCopyInto(out *ResourceRequest) {
if in.VendorParameters != nil {
out.VendorParameters = in.VendorParameters.DeepCopyObject()
}
out.ResourceRequestModel = in.ResourceRequestModel
in.ResourceRequestModel.DeepCopyInto(&out.ResourceRequestModel)
return
}
@@ -850,6 +1066,11 @@ func (in *ResourceRequest) DeepCopy() *ResourceRequest {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceRequestModel) DeepCopyInto(out *ResourceRequestModel) {
*out = *in
if in.NamedResources != nil {
in, out := &in.NamedResources, &out.NamedResources
*out = new(NamedResourcesRequest)
**out = **in
}
return
}