DRA: remove immediate allocation

As agreed in https://github.com/kubernetes/enhancements/pull/4709, immediate
allocation is one of those features which can be removed because it makes no
sense for structured parameters and the justification for classic DRA is weak.
This commit is contained in:
Patrick Ohly
2024-06-13 17:25:39 +02:00
parent b51d68bb87
commit de5742ae83
41 changed files with 395 additions and 1198 deletions

View File

@@ -17,24 +17,10 @@ limitations under the License.
package fuzzer
import (
fuzz "github.com/google/gofuzz"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/apis/resource"
)
// Funcs contains the fuzzer functions for the resource group.
var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
func(obj *resource.ResourceClaimSpec, c fuzz.Continue) {
c.FuzzNoCustom(obj) // fuzz self without calling this function again
// Custom fuzzing for allocation mode: pick one valid mode randomly.
modes := []resource.AllocationMode{
resource.AllocationModeImmediate,
resource.AllocationModeWaitForFirstConsumer,
}
obj.AllocationMode = modes[c.Rand.Intn(len(modes))]
},
}
return nil
}

View File

@@ -62,34 +62,8 @@ type ResourceClaimSpec struct {
// The object must be in the same namespace as the ResourceClaim.
// +optional
ParametersRef *ResourceClaimParametersReference
// Allocation can start immediately or when a Pod wants to use the
// resource. "WaitForFirstConsumer" is the default.
// +optional
AllocationMode AllocationMode
}
// AllocationMode describes whether a ResourceClaim gets allocated immediately
// when it gets created (AllocationModeImmediate) or whether allocation is
// delayed until it is needed for a Pod
// (AllocationModeWaitForFirstConsumer). Other modes might get added in the
// future.
type AllocationMode string
const (
// When a ResourceClaim has AllocationModeWaitForFirstConsumer, allocation is
// delayed until a Pod gets scheduled that needs the ResourceClaim. The
// scheduler will consider all resource requirements of that Pod and
// trigger allocation for a node that fits the Pod.
AllocationModeWaitForFirstConsumer AllocationMode = "WaitForFirstConsumer"
// When a ResourceClaim has AllocationModeImmediate, allocation starts
// as soon as the ResourceClaim gets created. This is done without
// considering the needs of Pods that will use the ResourceClaim
// because those Pods are not known yet.
AllocationModeImmediate AllocationMode = "Immediate"
)
// ResourceClaimStatus tracks whether the resource has been allocated and what
// the resulting attributes are.
type ResourceClaimStatus struct {

View File

@@ -17,16 +17,9 @@ limitations under the License.
package v1alpha3
import (
"k8s.io/api/resource/v1alpha3"
"k8s.io/apimachinery/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}
func SetDefaults_ResourceClaimSpec(obj *v1alpha3.ResourceClaimSpec) {
if obj.AllocationMode == "" {
obj.AllocationMode = v1alpha3.AllocationModeWaitForFirstConsumer
}
}

View File

@@ -1,75 +0,0 @@
/*
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 v1alpha3_test
import (
"reflect"
"testing"
v1alpha3 "k8s.io/api/resource/v1alpha3"
"k8s.io/apimachinery/pkg/runtime"
// ensure types are installed
"k8s.io/kubernetes/pkg/api/legacyscheme"
_ "k8s.io/kubernetes/pkg/apis/resource/install"
)
func TestSetDefaultAllocationMode(t *testing.T) {
claim := &v1alpha3.ResourceClaim{}
// field should be defaulted
defaultMode := v1alpha3.AllocationModeWaitForFirstConsumer
output := roundTrip(t, runtime.Object(claim)).(*v1alpha3.ResourceClaim)
outMode := output.Spec.AllocationMode
if outMode != defaultMode {
t.Errorf("Expected AllocationMode to be defaulted to: %+v, got: %+v", defaultMode, outMode)
}
// field should not change
nonDefaultMode := v1alpha3.AllocationModeImmediate
claim = &v1alpha3.ResourceClaim{
Spec: v1alpha3.ResourceClaimSpec{
AllocationMode: nonDefaultMode,
},
}
output = roundTrip(t, runtime.Object(claim)).(*v1alpha3.ResourceClaim)
outMode = output.Spec.AllocationMode
if outMode != v1alpha3.AllocationModeImmediate {
t.Errorf("Expected AllocationMode to remain %+v, got: %+v", nonDefaultMode, outMode)
}
}
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
codec := legacyscheme.Codecs.LegacyCodec(v1alpha3.SchemeGroupVersion)
data, err := runtime.Encode(codec, obj)
if err != nil {
t.Errorf("%v\n %#v", err, obj)
return nil
}
obj2, err := runtime.Decode(codec, data)
if err != nil {
t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj)
return nil
}
obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object)
err = legacyscheme.Scheme.Convert(obj2, obj3, nil)
if err != nil {
t.Errorf("%v\nSource: %#v", err, obj2)
return nil
}
return obj3
}

View File

@@ -1159,7 +1159,6 @@ func Convert_resource_ResourceClaimSchedulingStatus_To_v1alpha3_ResourceClaimSch
func autoConvert_v1alpha3_ResourceClaimSpec_To_resource_ResourceClaimSpec(in *v1alpha3.ResourceClaimSpec, out *resource.ResourceClaimSpec, s conversion.Scope) error {
out.ResourceClassName = in.ResourceClassName
out.ParametersRef = (*resource.ResourceClaimParametersReference)(unsafe.Pointer(in.ParametersRef))
out.AllocationMode = resource.AllocationMode(in.AllocationMode)
return nil
}
@@ -1171,7 +1170,6 @@ func Convert_v1alpha3_ResourceClaimSpec_To_resource_ResourceClaimSpec(in *v1alph
func autoConvert_resource_ResourceClaimSpec_To_v1alpha3_ResourceClaimSpec(in *resource.ResourceClaimSpec, out *v1alpha3.ResourceClaimSpec, s conversion.Scope) error {
out.ResourceClassName = in.ResourceClassName
out.ParametersRef = (*v1alpha3.ResourceClaimParametersReference)(unsafe.Pointer(in.ParametersRef))
out.AllocationMode = v1alpha3.AllocationMode(in.AllocationMode)
return nil
}

View File

@@ -22,7 +22,6 @@ limitations under the License.
package v1alpha3
import (
v1alpha3 "k8s.io/api/resource/v1alpha3"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@@ -30,33 +29,5 @@ import (
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&v1alpha3.ResourceClaim{}, func(obj interface{}) { SetObjectDefaults_ResourceClaim(obj.(*v1alpha3.ResourceClaim)) })
scheme.AddTypeDefaultingFunc(&v1alpha3.ResourceClaimList{}, func(obj interface{}) { SetObjectDefaults_ResourceClaimList(obj.(*v1alpha3.ResourceClaimList)) })
scheme.AddTypeDefaultingFunc(&v1alpha3.ResourceClaimTemplate{}, func(obj interface{}) { SetObjectDefaults_ResourceClaimTemplate(obj.(*v1alpha3.ResourceClaimTemplate)) })
scheme.AddTypeDefaultingFunc(&v1alpha3.ResourceClaimTemplateList{}, func(obj interface{}) {
SetObjectDefaults_ResourceClaimTemplateList(obj.(*v1alpha3.ResourceClaimTemplateList))
})
return nil
}
func SetObjectDefaults_ResourceClaim(in *v1alpha3.ResourceClaim) {
SetDefaults_ResourceClaimSpec(&in.Spec)
}
func SetObjectDefaults_ResourceClaimList(in *v1alpha3.ResourceClaimList) {
for i := range in.Items {
a := &in.Items[i]
SetObjectDefaults_ResourceClaim(a)
}
}
func SetObjectDefaults_ResourceClaimTemplate(in *v1alpha3.ResourceClaimTemplate) {
SetDefaults_ResourceClaimSpec(&in.Spec.Spec)
}
func SetObjectDefaults_ResourceClaimTemplateList(in *v1alpha3.ResourceClaimTemplateList) {
for i := range in.Items {
a := &in.Items[i]
SetObjectDefaults_ResourceClaimTemplate(a)
}
}

View File

@@ -46,14 +46,9 @@ func validateResourceClaimSpec(spec *resource.ResourceClaimSpec, fldPath *field.
allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceClassName"), spec.ResourceClassName, msg))
}
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()))
}
return allErrs
}
var supportedAllocationModes = sets.NewString(string(resource.AllocationModeImmediate), string(resource.AllocationModeWaitForFirstConsumer))
// It would have been nice to use Go generics to reuse the same validation
// function for Kind and Name in both types, but generics cannot be used to
// access common fields in structs.

View File

@@ -42,14 +42,11 @@ func testClaim(name, namespace string, spec resource.ResourceClaimSpec) *resourc
}
func TestValidateClaim(t *testing.T) {
validMode := resource.AllocationModeImmediate
invalidMode := resource.AllocationMode("invalid")
goodName := "foo"
badName := "!@#$%^"
goodNS := "ns"
goodClaimSpec := resource.ResourceClaimSpec{
ResourceClassName: goodName,
AllocationMode: validMode,
}
now := metav1.Now()
badValue := "spaces not allowed"
@@ -200,14 +197,6 @@ func TestValidateClaim(t *testing.T) {
return claim
}(),
},
"bad-mode": {
wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "allocationMode"), invalidMode, supportedAllocationModes.List())},
claim: func() *resource.ResourceClaim {
claim := testClaim(goodName, goodNS, goodClaimSpec)
claim.Spec.AllocationMode = invalidMode
return claim
}(),
},
"good-parameters": {
claim: func() *resource.ResourceClaim {
claim := testClaim(goodName, goodNS, goodClaimSpec)
@@ -279,7 +268,6 @@ func TestValidateClaimUpdate(t *testing.T) {
}
validClaim := testClaim("foo", "ns", resource.ResourceClaimSpec{
ResourceClassName: name,
AllocationMode: resource.AllocationModeImmediate,
ParametersRef: parameters,
})
@@ -316,18 +304,6 @@ func TestValidateClaimUpdate(t *testing.T) {
return claim
},
},
"invalid-update-mode": {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec"), func() resource.ResourceClaimSpec {
spec := validClaim.Spec.DeepCopy()
spec.AllocationMode = resource.AllocationModeWaitForFirstConsumer
return *spec
}(), "field is immutable")},
oldClaim: validClaim,
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Spec.AllocationMode = resource.AllocationModeWaitForFirstConsumer
return claim
},
},
}
for name, scenario := range scenarios {
@@ -343,7 +319,6 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
invalidName := "!@#$%^"
validClaim := testClaim("foo", "ns", resource.ResourceClaimSpec{
ResourceClassName: "valid",
AllocationMode: resource.AllocationModeImmediate,
})
validAllocatedClaim := validClaim.DeepCopy()

View File

@@ -40,14 +40,11 @@ func testClaimTemplate(name, namespace string, spec resource.ResourceClaimSpec)
}
func TestValidateClaimTemplate(t *testing.T) {
validMode := resource.AllocationModeImmediate
invalidMode := resource.AllocationMode("invalid")
goodName := "foo"
badName := "!@#$%^"
goodNS := "ns"
goodClaimSpec := resource.ResourceClaimSpec{
ResourceClassName: goodName,
AllocationMode: validMode,
}
now := metav1.Now()
badValue := "spaces not allowed"
@@ -198,14 +195,6 @@ func TestValidateClaimTemplate(t *testing.T) {
return template
}(),
},
"bad-mode": {
wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "spec", "allocationMode"), invalidMode, supportedAllocationModes.List())},
template: func() *resource.ResourceClaimTemplate {
template := testClaimTemplate(goodName, goodNS, goodClaimSpec)
template.Spec.Spec.AllocationMode = invalidMode
return template
}(),
},
"good-parameters": {
template: func() *resource.ResourceClaimTemplate {
template := testClaimTemplate(goodName, goodNS, goodClaimSpec)
@@ -277,7 +266,6 @@ func TestValidateClaimTemplateUpdate(t *testing.T) {
}
validClaimTemplate := testClaimTemplate("foo", "ns", resource.ResourceClaimSpec{
ResourceClassName: name,
AllocationMode: resource.AllocationModeImmediate,
ParametersRef: parameters,
})
@@ -314,18 +302,6 @@ func TestValidateClaimTemplateUpdate(t *testing.T) {
return template
},
},
"invalid-update-mode": {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec"), func() resource.ResourceClaimTemplateSpec {
spec := validClaimTemplate.Spec.DeepCopy()
spec.Spec.AllocationMode = resource.AllocationModeWaitForFirstConsumer
return *spec
}(), "field is immutable")},
oldClaimTemplate: validClaimTemplate,
update: func(template *resource.ResourceClaimTemplate) *resource.ResourceClaimTemplate {
template.Spec.Spec.AllocationMode = resource.AllocationModeWaitForFirstConsumer
return template
},
},
}
for name, scenario := range scenarios {