diff --git a/pkg/apis/resource/types.go b/pkg/apis/resource/types.go index faac04e6dc8..dd9e76d8bdc 100644 --- a/pkg/apis/resource/types.go +++ b/pkg/apis/resource/types.go @@ -1602,8 +1602,8 @@ type AllocationConfigSource string // Valid [DeviceAllocationConfiguration.Source] values. const ( - AllocationConfigSourceClass = "FromClass" - AllocationConfigSourceClaim = "FromClaim" + AllocationConfigSourceClass AllocationConfigSource = "FromClass" + AllocationConfigSourceClaim AllocationConfigSource = "FromClaim" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/resource/v1/zz_generated.validations.go b/pkg/apis/resource/v1/zz_generated.validations.go index 5646a737884..51ad4ae6a26 100644 --- a/pkg/apis/resource/v1/zz_generated.validations.go +++ b/pkg/apis/resource/v1/zz_generated.validations.go @@ -124,6 +124,16 @@ func Validate_AllocatedDeviceStatus(ctx context.Context, op operation.Operation, return errs } +var symbolsForAllocationConfigSource = sets.New(resourcev1.AllocationConfigSourceClaim, resourcev1.AllocationConfigSourceClass) + +// Validate_AllocationConfigSource validates an instance of AllocationConfigSource according +// to declarative validation rules in the API schema. +func Validate_AllocationConfigSource(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.AllocationConfigSource) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForAllocationConfigSource, nil)...) + + return errs +} + // Validate_AllocationResult validates an instance of AllocationResult according // to declarative validation rules in the API schema. func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.AllocationResult) (errs field.ErrorList) { @@ -147,7 +157,29 @@ func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldP // Validate_DeviceAllocationConfiguration validates an instance of DeviceAllocationConfiguration according // to declarative validation rules in the API schema. func Validate_DeviceAllocationConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationConfiguration) (errs field.ErrorList) { - // field resourcev1.DeviceAllocationConfiguration.Source has no validation + // field resourcev1.DeviceAllocationConfiguration.Source + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.AllocationConfigSource) (errs field.ErrorList) { + // don't revalidate unchanged data + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationConfigSource(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("source"), &obj.Source, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAllocationConfiguration) *resourcev1.AllocationConfigSource { + return &oldObj.Source + }))...) + // field resourcev1.DeviceAllocationConfiguration.Requests has no validation // field resourcev1.DeviceAllocationConfiguration.DeviceConfiguration diff --git a/pkg/apis/resource/v1beta1/zz_generated.validations.go b/pkg/apis/resource/v1beta1/zz_generated.validations.go index 3f0faed68fd..ce7ecf73c0e 100644 --- a/pkg/apis/resource/v1beta1/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta1/zz_generated.validations.go @@ -124,6 +124,16 @@ func Validate_AllocatedDeviceStatus(ctx context.Context, op operation.Operation, return errs } +var symbolsForAllocationConfigSource = sets.New(resourcev1beta1.AllocationConfigSourceClaim, resourcev1beta1.AllocationConfigSourceClass) + +// Validate_AllocationConfigSource validates an instance of AllocationConfigSource according +// to declarative validation rules in the API schema. +func Validate_AllocationConfigSource(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationConfigSource) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForAllocationConfigSource, nil)...) + + return errs +} + // Validate_AllocationResult validates an instance of AllocationResult according // to declarative validation rules in the API schema. func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationResult) (errs field.ErrorList) { @@ -149,7 +159,29 @@ func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldP // Validate_DeviceAllocationConfiguration validates an instance of DeviceAllocationConfiguration according // to declarative validation rules in the API schema. func Validate_DeviceAllocationConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationConfiguration) (errs field.ErrorList) { - // field resourcev1beta1.DeviceAllocationConfiguration.Source has no validation + // field resourcev1beta1.DeviceAllocationConfiguration.Source + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationConfigSource) (errs field.ErrorList) { + // don't revalidate unchanged data + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationConfigSource(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("source"), &obj.Source, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAllocationConfiguration) *resourcev1beta1.AllocationConfigSource { + return &oldObj.Source + }))...) + // field resourcev1beta1.DeviceAllocationConfiguration.Requests has no validation // field resourcev1beta1.DeviceAllocationConfiguration.DeviceConfiguration diff --git a/pkg/apis/resource/v1beta2/zz_generated.validations.go b/pkg/apis/resource/v1beta2/zz_generated.validations.go index 0234fdf299d..fa243373ee9 100644 --- a/pkg/apis/resource/v1beta2/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta2/zz_generated.validations.go @@ -124,6 +124,16 @@ func Validate_AllocatedDeviceStatus(ctx context.Context, op operation.Operation, return errs } +var symbolsForAllocationConfigSource = sets.New(resourcev1beta2.AllocationConfigSourceClaim, resourcev1beta2.AllocationConfigSourceClass) + +// Validate_AllocationConfigSource validates an instance of AllocationConfigSource according +// to declarative validation rules in the API schema. +func Validate_AllocationConfigSource(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationConfigSource) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForAllocationConfigSource, nil)...) + + return errs +} + // Validate_AllocationResult validates an instance of AllocationResult according // to declarative validation rules in the API schema. func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationResult) (errs field.ErrorList) { @@ -149,7 +159,29 @@ func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldP // Validate_DeviceAllocationConfiguration validates an instance of DeviceAllocationConfiguration according // to declarative validation rules in the API schema. func Validate_DeviceAllocationConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationConfiguration) (errs field.ErrorList) { - // field resourcev1beta2.DeviceAllocationConfiguration.Source has no validation + // field resourcev1beta2.DeviceAllocationConfiguration.Source + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationConfigSource) (errs field.ErrorList) { + // don't revalidate unchanged data + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationConfigSource(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("source"), &obj.Source, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAllocationConfiguration) *resourcev1beta2.AllocationConfigSource { + return &oldObj.Source + }))...) + // field resourcev1beta2.DeviceAllocationConfiguration.Requests has no validation // field resourcev1beta2.DeviceAllocationConfiguration.DeviceConfiguration diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index 39633e99037..9af9eafed1f 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -525,10 +525,10 @@ func validateAllocationConfigSource(source resource.AllocationConfigSource, fldP var allErrs field.ErrorList switch source { case "": - allErrs = append(allErrs, field.Required(fldPath, "")) + allErrs = append(allErrs, field.Required(fldPath, "").MarkCoveredByDeclarative()) case resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass: default: - allErrs = append(allErrs, field.NotSupported(fldPath, source, []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass})) + allErrs = append(allErrs, field.NotSupported(fldPath, source, []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass}).MarkCoveredByDeclarative()) } return allErrs } diff --git a/pkg/apis/resource/validation/validation_resourceclaim_test.go b/pkg/apis/resource/validation/validation_resourceclaim_test.go index dd78523a3aa..272830cd812 100644 --- a/pkg/apis/resource/validation/validation_resourceclaim_test.go +++ b/pkg/apis/resource/validation/validation_resourceclaim_test.go @@ -1198,8 +1198,8 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "configuration": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("status", "allocation", "devices", "config").Index(1).Child("source"), ""), - field.NotSupported(field.NewPath("status", "allocation", "devices", "config").Index(2).Child("source"), resource.AllocationConfigSource("no-such-source"), []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass}), + field.Required(field.NewPath("status", "allocation", "devices", "config").Index(1).Child("source"), "").MarkCoveredByDeclarative(), + field.NotSupported(field.NewPath("status", "allocation", "devices", "config").Index(2).Child("source"), resource.AllocationConfigSource("no-such-source"), []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass}).MarkCoveredByDeclarative(), field.Required(field.NewPath("status", "allocation", "devices", "config").Index(3).Child("opaque"), ""), field.Required(field.NewPath("status", "allocation", "devices", "config").Index(4).Child("opaque", "driver"), "").MarkCoveredByDeclarative(), field.Required(field.NewPath("status", "allocation", "devices", "config").Index(4).Child("opaque", "parameters"), ""), diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index d0116b33d9a..1e44669b46e 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -48461,10 +48461,11 @@ func schema_k8sio_api_resource_v1_DeviceAllocationConfiguration(ref common.Refer Properties: map[string]spec.Schema{ "source": { SchemaProps: spec.SchemaProps{ - Description: "Source records whether the configuration comes from a class and thus is not something that a normal user would have been able to set or from a claim.", + Description: "Source records whether the configuration comes from a class and thus is not something that a normal user would have been able to set or from a claim.\n\n\nPossible enum values:\n - `\"FromClaim\"`\n - `\"FromClass\"`", Default: "", Type: []string{"string"}, Format: "", + Enum: []interface{}{"FromClaim", "FromClass"}, }, }, "requests": { @@ -50950,10 +50951,11 @@ func schema_k8sio_api_resource_v1beta1_DeviceAllocationConfiguration(ref common. Properties: map[string]spec.Schema{ "source": { SchemaProps: spec.SchemaProps{ - Description: "Source records whether the configuration comes from a class and thus is not something that a normal user would have been able to set or from a claim.", + Description: "Source records whether the configuration comes from a class and thus is not something that a normal user would have been able to set or from a claim.\n\n\nPossible enum values:\n - `\"FromClaim\"`\n - `\"FromClass\"`", Default: "", Type: []string{"string"}, Format: "", + Enum: []interface{}{"FromClaim", "FromClass"}, }, }, "requests": { @@ -53121,10 +53123,11 @@ func schema_k8sio_api_resource_v1beta2_DeviceAllocationConfiguration(ref common. Properties: map[string]spec.Schema{ "source": { SchemaProps: spec.SchemaProps{ - Description: "Source records whether the configuration comes from a class and thus is not something that a normal user would have been able to set or from a claim.", + Description: "Source records whether the configuration comes from a class and thus is not something that a normal user would have been able to set or from a claim.\n\n\nPossible enum values:\n - `\"FromClaim\"`\n - `\"FromClass\"`", Default: "", Type: []string{"string"}, Format: "", + Enum: []interface{}{"FromClaim", "FromClass"}, }, }, "requests": { diff --git a/pkg/registry/resource/resourceclaim/declarative_validation_test.go b/pkg/registry/resource/resourceclaim/declarative_validation_test.go index 4de08284980..eaca2c72339 100644 --- a/pkg/registry/resource/resourceclaim/declarative_validation_test.go +++ b/pkg/registry/resource/resourceclaim/declarative_validation_test.go @@ -652,6 +652,7 @@ func testValidateStatusUpdateForDeclarative(t *testing.T, apiVersion string) { Subresource: "status", }) poolPath := field.NewPath("status", "allocation", "devices", "results").Index(0).Child("pool") + configSourcePath := field.NewPath("status", "allocation", "devices", "config").Index(0).Child("source") testCases := map[string]struct { old resource.ResourceClaim update resource.ResourceClaim @@ -880,6 +881,29 @@ func testValidateStatusUpdateForDeclarative(t *testing.T, apiVersion string) { field.TooMany(field.NewPath("status", "allocation", "devices", "config"), 33, 32).WithOrigin("maxItems"), }, }, + // .Status.Allocation.Devices.Config[%d].Source + "valid status.allocation.devices.config source FromClass": { + old: mkValidResourceClaim(), + update: mkResourceClaimWithStatus(tweakStatusAllocationConfigSource(resource.AllocationConfigSourceClass)), + }, + "valid status.allocation.devices.config source FromClaim": { + old: mkValidResourceClaim(), + update: mkResourceClaimWithStatus(tweakStatusAllocationConfigSource(resource.AllocationConfigSourceClaim)), + }, + "invalid status.allocation.devices.config source empty": { + old: mkValidResourceClaim(), + update: mkResourceClaimWithStatus(tweakStatusAllocationConfigSource("")), + expectedErrs: field.ErrorList{ + field.Required(configSourcePath, "").MarkCoveredByDeclarative(), + }, + }, + "invalid status.allocation.devices.config source invalid": { + old: mkValidResourceClaim(), + update: mkResourceClaimWithStatus(tweakStatusAllocationConfigSource("invalid")), + expectedErrs: field.ErrorList{ + field.NotSupported(configSourcePath, resource.AllocationConfigSource("invalid"), []string{string(resource.AllocationConfigSourceClaim), string(resource.AllocationConfigSourceClass)}).MarkCoveredByDeclarative(), + }, + }, } for k, tc := range testCases { t.Run(k, func(t *testing.T) { @@ -1142,3 +1166,26 @@ func addStatusAllocationResult(obj resource.ResourceClaim) resource.ResourceClai } return obj } + +func tweakStatusAllocationConfigSource(source resource.AllocationConfigSource) func(rc *resource.ResourceClaim) { + return func(rc *resource.ResourceClaim) { + if rc.Status.Allocation == nil { + rc.Status.Allocation = &resource.AllocationResult{} + } + if len(rc.Status.Allocation.Devices.Config) == 0 { + rc.Status.Allocation.Devices.Config = append(rc.Status.Allocation.Devices.Config, resource.DeviceAllocationConfiguration{ + Source: resource.AllocationConfigSourceClaim, + Requests: []string{"req-0"}, + DeviceConfiguration: resource.DeviceConfiguration{ + Opaque: &resource.OpaqueDeviceConfiguration{ + Driver: "dra.example.com", + Parameters: runtime.RawExtension{ + Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`), + }, + }, + }, + }) + } + rc.Status.Allocation.Devices.Config[0].Source = source + } +} diff --git a/staging/src/k8s.io/api/resource/v1/generated.proto b/staging/src/k8s.io/api/resource/v1/generated.proto index 44e95b6221b..15e63c6c930 100644 --- a/staging/src/k8s.io/api/resource/v1/generated.proto +++ b/staging/src/k8s.io/api/resource/v1/generated.proto @@ -464,6 +464,7 @@ message DeviceAllocationConfiguration { // or from a claim. // // +required + // +k8s:required optional string source = 1; // Requests lists the names of requests where the configuration applies. diff --git a/staging/src/k8s.io/api/resource/v1/types.go b/staging/src/k8s.io/api/resource/v1/types.go index 75fee50d27e..c1e78a41939 100644 --- a/staging/src/k8s.io/api/resource/v1/types.go +++ b/staging/src/k8s.io/api/resource/v1/types.go @@ -1632,6 +1632,7 @@ type DeviceAllocationConfiguration struct { // or from a claim. // // +required + // +k8s:required Source AllocationConfigSource `json:"source" protobuf:"bytes,1,name=source"` // Requests lists the names of requests where the configuration applies. @@ -1648,12 +1649,14 @@ type DeviceAllocationConfiguration struct { DeviceConfiguration `json:",inline" protobuf:"bytes,3,name=deviceConfiguration"` } +// +enum +// +k8s:enum type AllocationConfigSource string // Valid [DeviceAllocationConfiguration.Source] values. const ( - AllocationConfigSourceClass = "FromClass" - AllocationConfigSourceClaim = "FromClaim" + AllocationConfigSourceClass AllocationConfigSource = "FromClass" + AllocationConfigSourceClaim AllocationConfigSource = "FromClaim" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/staging/src/k8s.io/api/resource/v1beta1/generated.proto b/staging/src/k8s.io/api/resource/v1beta1/generated.proto index ea9b92880e6..315b4860566 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/resource/v1beta1/generated.proto @@ -472,6 +472,7 @@ message DeviceAllocationConfiguration { // or from a claim. // // +required + // +k8s:required optional string source = 1; // Requests lists the names of requests where the configuration applies. diff --git a/staging/src/k8s.io/api/resource/v1beta1/types.go b/staging/src/k8s.io/api/resource/v1beta1/types.go index 22bf9c3d28c..a655415b95a 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/types.go +++ b/staging/src/k8s.io/api/resource/v1beta1/types.go @@ -1640,6 +1640,7 @@ type DeviceAllocationConfiguration struct { // or from a claim. // // +required + // +k8s:required Source AllocationConfigSource `json:"source" protobuf:"bytes,1,name=source"` // Requests lists the names of requests where the configuration applies. @@ -1656,12 +1657,14 @@ type DeviceAllocationConfiguration struct { DeviceConfiguration `json:",inline" protobuf:"bytes,3,name=deviceConfiguration"` } +// +enum +// +k8s:enum type AllocationConfigSource string // Valid [DeviceAllocationConfiguration.Source] values. const ( - AllocationConfigSourceClass = "FromClass" - AllocationConfigSourceClaim = "FromClaim" + AllocationConfigSourceClass AllocationConfigSource = "FromClass" + AllocationConfigSourceClaim AllocationConfigSource = "FromClaim" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/staging/src/k8s.io/api/resource/v1beta2/generated.proto b/staging/src/k8s.io/api/resource/v1beta2/generated.proto index c864bdf4c15..ab05a3e0f54 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/generated.proto +++ b/staging/src/k8s.io/api/resource/v1beta2/generated.proto @@ -464,6 +464,7 @@ message DeviceAllocationConfiguration { // or from a claim. // // +required + // +k8s:required optional string source = 1; // Requests lists the names of requests where the configuration applies. diff --git a/staging/src/k8s.io/api/resource/v1beta2/types.go b/staging/src/k8s.io/api/resource/v1beta2/types.go index 63b68845cb9..d621a702569 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/types.go +++ b/staging/src/k8s.io/api/resource/v1beta2/types.go @@ -1632,6 +1632,7 @@ type DeviceAllocationConfiguration struct { // or from a claim. // // +required + // +k8s:required Source AllocationConfigSource `json:"source" protobuf:"bytes,1,name=source"` // Requests lists the names of requests where the configuration applies. @@ -1648,12 +1649,14 @@ type DeviceAllocationConfiguration struct { DeviceConfiguration `json:",inline" protobuf:"bytes,3,name=deviceConfiguration"` } +// +enum +// +k8s:enum type AllocationConfigSource string // Valid [DeviceAllocationConfiguration.Source] values. const ( - AllocationConfigSourceClass = "FromClass" - AllocationConfigSourceClaim = "FromClaim" + AllocationConfigSourceClass AllocationConfigSource = "FromClass" + AllocationConfigSourceClaim AllocationConfigSource = "FromClaim" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object