dra api: add structured parameters
NodeResourceSlice will be used by kubelet to publish resource information on behalf of DRA drivers on the node. NodeName and DriverName in NodeResourceSlice must be immutable. This simplifies tracking the different objects because what they are for cannot change after creation. The new field in ResourceClass tells scheduler and autoscaler that they are expected to handle allocation. ResourceClaimParameters and ResourceClassParameters are new types for telling in-tree components how to handle claims.
This commit is contained in:
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package validation
|
||||
|
||||
import (
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
@@ -41,7 +42,7 @@ func validateResourceClaimSpec(spec *resource.ResourceClaimSpec, fldPath *field.
|
||||
for _, msg := range corevalidation.ValidateClassName(spec.ResourceClassName, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceClassName"), spec.ResourceClassName, msg))
|
||||
}
|
||||
allErrs = append(allErrs, validateResourceClaimParameters(spec.ParametersRef, fldPath.Child("parametersRef"))...)
|
||||
allErrs = append(allErrs, validateResourceClaimParametersRef(spec.ParametersRef, fldPath.Child("parametersRef"))...)
|
||||
if !supportedAllocationModes.Has(string(spec.AllocationMode)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("allocationMode"), spec.AllocationMode, supportedAllocationModes.List()))
|
||||
}
|
||||
@@ -54,7 +55,7 @@ var supportedAllocationModes = sets.NewString(string(resource.AllocationModeImme
|
||||
// function for Kind and Name in both types, but generics cannot be used to
|
||||
// access common fields in structs.
|
||||
|
||||
func validateResourceClaimParameters(ref *resource.ResourceClaimParametersReference, fldPath *field.Path) field.ErrorList {
|
||||
func validateResourceClaimParametersRef(ref *resource.ResourceClaimParametersReference, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if ref != nil {
|
||||
if ref.Kind == "" {
|
||||
@@ -200,6 +201,12 @@ func validateResourceHandles(resourceHandles []resource.ResourceHandle, maxSize
|
||||
if len(resourceHandle.Data) > resource.ResourceHandleDataMaxSize {
|
||||
allErrs = append(allErrs, field.TooLongMaxLength(idxPath.Child("data"), len(resourceHandle.Data), resource.ResourceHandleDataMaxSize))
|
||||
}
|
||||
if resourceHandle.StructuredData != nil {
|
||||
allErrs = append(allErrs, validateStructuredResourceHandle(resourceHandle.StructuredData, idxPath.Child("structuredData"))...)
|
||||
}
|
||||
if len(resourceHandle.Data) > 0 && resourceHandle.StructuredData != nil {
|
||||
allErrs = append(allErrs, field.Invalid(idxPath, nil, "data and structuredData are mutually exclusive"))
|
||||
}
|
||||
}
|
||||
if len(resourceHandles) > maxSize {
|
||||
// Dumping the entire field into the error message is likely to be too long,
|
||||
@@ -210,6 +217,37 @@ func validateResourceHandles(resourceHandles []resource.ResourceHandle, maxSize
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateStructuredResourceHandle(handle *resource.StructuredResourceHandle, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
allErrs = append(allErrs, validateNodeName(handle.NodeName, fldPath.Child("nodeName"))...)
|
||||
allErrs = append(allErrs, validateDriverAllocationResults(handle.Results, fldPath.Child("results"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateDriverAllocationResults(results []resource.DriverAllocationResult, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
for index, result := range results {
|
||||
idxPath := fldPath.Index(index)
|
||||
allErrs = append(allErrs, validateAllocationResultModel(&result.AllocationResultModel, idxPath)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateAllocationResultModel(model *resource.AllocationResultModel, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
entries := sets.New[string]()
|
||||
// TODO: implement one structured parameter model
|
||||
switch len(entries) {
|
||||
case 0:
|
||||
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
|
||||
case 1:
|
||||
// Okay.
|
||||
default:
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, sets.List(entries), "exactly one field must be set, not several"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateResourceClaimUserReference(ref resource.ResourceClaimConsumerReference, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if ref.Resource == "" {
|
||||
@@ -345,3 +383,170 @@ func validateNodeName(name string, fldPath *field.Path) field.ErrorList {
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateNodeResourceSlice tests if a NodeResourceSlice object is valid.
|
||||
func ValidateNodeResourceSlice(nodeResourceSlice *resource.NodeResourceSlice) field.ErrorList {
|
||||
allErrs := corevalidation.ValidateObjectMeta(&nodeResourceSlice.ObjectMeta, false, apimachineryvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, validateNodeName(nodeResourceSlice.NodeName, field.NewPath("nodeName"))...)
|
||||
allErrs = append(allErrs, validateResourceDriverName(nodeResourceSlice.DriverName, field.NewPath("driverName"))...)
|
||||
allErrs = append(allErrs, validateNodeResourceModel(&nodeResourceSlice.NodeResourceModel, nil)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateNodeResourceModel(model *resource.NodeResourceModel, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
entries := sets.New[string]()
|
||||
// TODO: implement one structured parameter model
|
||||
switch len(entries) {
|
||||
case 0:
|
||||
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
|
||||
case 1:
|
||||
// Okay.
|
||||
default:
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, sets.List(entries), "exactly one field must be set, not several"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateNodeResourceSlice tests if a NodeResourceSlice update is valid.
|
||||
func ValidateNodeResourceSliceUpdate(nodeResourceSlice, oldNodeResourceSlice *resource.NodeResourceSlice) field.ErrorList {
|
||||
allErrs := corevalidation.ValidateObjectMetaUpdate(&nodeResourceSlice.ObjectMeta, &oldNodeResourceSlice.ObjectMeta, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidateNodeResourceSlice(nodeResourceSlice)...)
|
||||
allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(nodeResourceSlice.NodeName, oldNodeResourceSlice.NodeName, field.NewPath("nodeName"))...)
|
||||
allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(nodeResourceSlice.DriverName, oldNodeResourceSlice.DriverName, field.NewPath("driverName"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateResourceClaimParameters tests if a ResourceClaimParameters object is valid for creation.
|
||||
func ValidateResourceClaimParameters(parameters *resource.ResourceClaimParameters) field.ErrorList {
|
||||
return validateResourceClaimParameters(parameters, false)
|
||||
}
|
||||
|
||||
func validateResourceClaimParameters(parameters *resource.ResourceClaimParameters, requestStored bool) field.ErrorList {
|
||||
allErrs := corevalidation.ValidateObjectMeta(¶meters.ObjectMeta, true, apimachineryvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, validateResourceClaimParametersRef(parameters.GeneratedFrom, field.NewPath("generatedFrom"))...)
|
||||
allErrs = append(allErrs, validateDriverRequests(parameters.DriverRequests, field.NewPath("driverRequests"), requestStored)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateDriverRequests(requests []resource.DriverRequests, fldPath *field.Path, requestStored bool) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
driverNames := sets.New[string]()
|
||||
for i, request := range requests {
|
||||
idxPath := fldPath.Index(i)
|
||||
driverName := request.DriverName
|
||||
allErrs = append(allErrs, validateResourceDriverName(driverName, idxPath.Child("driverName"))...)
|
||||
if driverNames.Has(driverName) {
|
||||
allErrs = append(allErrs, field.Duplicate(idxPath.Child("driverName"), driverName))
|
||||
} else {
|
||||
driverNames.Insert(driverName)
|
||||
}
|
||||
allErrs = append(allErrs, validateResourceRequests(request.Requests, idxPath.Child("requests"), requestStored)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateResourceRequests(requests []resource.ResourceRequest, fldPath *field.Path, requestStored bool) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
for i, request := range requests {
|
||||
idxPath := fldPath.Index(i)
|
||||
allErrs = append(allErrs, validateResourceRequestModel(&request.ResourceRequestModel, idxPath, requestStored)...)
|
||||
}
|
||||
if len(requests) == 0 {
|
||||
// We could allow this, it just doesn't make sense: the entire entry would get ignored and thus
|
||||
// should have been left out entirely.
|
||||
allErrs = append(allErrs, field.Required(fldPath, "empty entries with no requests are not allowed"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
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
|
||||
switch len(entries) {
|
||||
case 0:
|
||||
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
|
||||
case 1:
|
||||
// Okay.
|
||||
default:
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, sets.List(entries), "exactly one field must be set, not several"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateResourceClaimParameters tests if a ResourceClaimParameters update is valid.
|
||||
func ValidateResourceClaimParametersUpdate(resourceClaimParameters, oldResourceClaimParameters *resource.ResourceClaimParameters) field.ErrorList {
|
||||
allErrs := corevalidation.ValidateObjectMetaUpdate(&resourceClaimParameters.ObjectMeta, &oldResourceClaimParameters.ObjectMeta, field.NewPath("metadata"))
|
||||
requestStored := apiequality.Semantic.DeepEqual(oldResourceClaimParameters.DriverRequests, resourceClaimParameters.DriverRequests)
|
||||
allErrs = append(allErrs, validateResourceClaimParameters(resourceClaimParameters, requestStored)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateResourceClassParameters tests if a ResourceClassParameters object is valid for creation.
|
||||
func ValidateResourceClassParameters(parameters *resource.ResourceClassParameters) field.ErrorList {
|
||||
return validateResourceClassParameters(parameters, false)
|
||||
}
|
||||
|
||||
func validateResourceClassParameters(parameters *resource.ResourceClassParameters, filtersStored bool) field.ErrorList {
|
||||
allErrs := corevalidation.ValidateObjectMeta(¶meters.ObjectMeta, true, apimachineryvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, validateClassParameters(parameters.GeneratedFrom, field.NewPath("generatedFrom"))...)
|
||||
allErrs = append(allErrs, validateResourceFilters(parameters.Filters, field.NewPath("filters"), filtersStored)...)
|
||||
allErrs = append(allErrs, validateVendorParameters(parameters.VendorParameters, field.NewPath("vendorParameters"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateResourceFilters(filters []resource.ResourceFilter, fldPath *field.Path, filtersStored bool) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
driverNames := sets.New[string]()
|
||||
for i, filter := range filters {
|
||||
idxPath := fldPath.Index(i)
|
||||
driverName := filter.DriverName
|
||||
allErrs = append(allErrs, validateResourceDriverName(driverName, idxPath.Child("driverName"))...)
|
||||
if driverNames.Has(driverName) {
|
||||
allErrs = append(allErrs, field.Duplicate(idxPath.Child("driverName"), driverName))
|
||||
} else {
|
||||
driverNames.Insert(driverName)
|
||||
}
|
||||
allErrs = append(allErrs, validateResourceFilterModel(&filter.ResourceFilterModel, idxPath, filtersStored)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
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
|
||||
switch len(entries) {
|
||||
case 0:
|
||||
// allErrs = append(allErrs, field.Required(fldPath, "exactly one structured model field must be set"))
|
||||
case 1:
|
||||
// Okay.
|
||||
default:
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, sets.List(entries), "exactly one field must be set, not several"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateVendorParameters(parameters []resource.VendorParameters, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
driverNames := sets.New[string]()
|
||||
for i, parameters := range parameters {
|
||||
idxPath := fldPath.Index(i)
|
||||
driverName := parameters.DriverName
|
||||
allErrs = append(allErrs, validateResourceDriverName(driverName, idxPath.Child("driverName"))...)
|
||||
if driverNames.Has(driverName) {
|
||||
allErrs = append(allErrs, field.Duplicate(idxPath.Child("driverName"), driverName))
|
||||
} else {
|
||||
driverNames.Insert(driverName)
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateResourceClassParameters tests if a ResourceClassParameters update is valid.
|
||||
func ValidateResourceClassParametersUpdate(resourceClassParameters, oldResourceClassParameters *resource.ResourceClassParameters) field.ErrorList {
|
||||
allErrs := corevalidation.ValidateObjectMetaUpdate(&resourceClassParameters.ObjectMeta, &oldResourceClassParameters.ObjectMeta, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidateResourceClassParameters(resourceClassParameters)...)
|
||||
return allErrs
|
||||
}
|
||||
|
Reference in New Issue
Block a user