KEP-5491: strategy(dropDisableFields) for list fields in DeviceAttribute

This commit is contained in:
Shingo Omura
2026-03-16 19:05:04 +09:00
parent b43153602c
commit e4e45ed589
2 changed files with 110 additions and 0 deletions

View File

@@ -197,6 +197,7 @@ func dropDisabledFields(newSlice, oldSlice *resource.ResourceSlice) {
dropDisabledDRADeviceBindingConditionsFields(newSlice, oldSlice)
dropDisabledDRAConsumableCapacityFields(newSlice, oldSlice)
dropDisabledDRANodeAllocatableResourcesFields(newSlice, oldSlice)
dropDisableDRAListTypeAttributesFields(newSlice, oldSlice)
}
func dropDisabledDRADeviceTaintsFields(newSlice, oldSlice *resource.ResourceSlice) {
@@ -348,3 +349,47 @@ func draNodeAllocatableResourcesFeatureInUse(slice *resource.ResourceSlice) bool
}
return false
}
func dropDisableDRAListTypeAttributesFields(newSlice, oldSlice *resource.ResourceSlice) {
if utilfeature.DefaultFeatureGate.Enabled(features.DRAListTypeAttributes) ||
draListTypeAttributesFeatureInUse(oldSlice) {
return
}
for i := range newSlice.Spec.Devices {
for k, deviceAttribute := range newSlice.Spec.Devices[i].Attributes {
if deviceAttribute.BoolValues != nil {
deviceAttribute.BoolValues = nil
newSlice.Spec.Devices[i].Attributes[k] = deviceAttribute
}
if deviceAttribute.IntValues != nil {
deviceAttribute.IntValues = nil
newSlice.Spec.Devices[i].Attributes[k] = deviceAttribute
}
if deviceAttribute.StringValues != nil {
deviceAttribute.StringValues = nil
newSlice.Spec.Devices[i].Attributes[k] = deviceAttribute
}
if deviceAttribute.VersionValues != nil {
deviceAttribute.VersionValues = nil
newSlice.Spec.Devices[i].Attributes[k] = deviceAttribute
}
}
}
}
func draListTypeAttributesFeatureInUse(slice *resource.ResourceSlice) bool {
if slice == nil {
return false
}
for _, device := range slice.Spec.Devices {
for _, deviceAttribute := range device.Attributes {
if deviceAttribute.BoolValues != nil || deviceAttribute.IntValues != nil || deviceAttribute.StringValues != nil || deviceAttribute.VersionValues != nil {
return true
}
}
}
return false
}

View File

@@ -199,6 +199,16 @@ var sliceWithNodeAllocatableResources = func() *resource.ResourceSlice {
return obj
}()
var sliceWithListTypeAttributes = func() *resource.ResourceSlice {
obj := slice.DeepCopy()
obj.Spec.Devices[0].Attributes = map[resource.QualifiedName]resource.DeviceAttribute{
resource.QualifiedName("list_attribute"): {
StringValues: []string{"value1", "value2"},
},
}
return obj
}()
func TestResourceSliceStrategy(t *testing.T) {
if Strategy.NamespaceScoped() {
t.Errorf("ResourceSlice must not be namespace scoped")
@@ -218,6 +228,7 @@ func TestResourceSliceStrategyCreate(t *testing.T) {
deviceStatus bool
consumableCapacity bool
draNodeAllocatableResources bool
listTypeAttributes bool
expectedValidationError bool
expectObj *resource.ResourceSlice
}{
@@ -382,6 +393,20 @@ func TestResourceSliceStrategyCreate(t *testing.T) {
return obj
}(),
},
"keep-fields-list-type-attributes": {
obj: sliceWithListTypeAttributes,
listTypeAttributes: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithListTypeAttributes.DeepCopy()
obj.Generation = 1
return obj
}(),
},
"drop-fields-list-type-attributes": {
obj: sliceWithListTypeAttributes,
listTypeAttributes: false,
expectedValidationError: true,
},
}
for name, tc := range testCases {
@@ -393,6 +418,7 @@ func TestResourceSliceStrategyCreate(t *testing.T) {
features.DRAResourceClaimDeviceStatus: tc.deviceStatus,
features.DRAConsumableCapacity: tc.consumableCapacity,
features.DRANodeAllocatableResources: tc.draNodeAllocatableResources,
features.DRAListTypeAttributes: tc.listTypeAttributes,
})
obj := tc.obj.DeepCopy()
@@ -424,6 +450,7 @@ func TestResourceSliceStrategyUpdate(t *testing.T) {
deviceStatus bool
bindingConditions bool
consumableCapacity bool
listTypeAttributes bool
expectValidationError bool
expectObj *resource.ResourceSlice
}{
@@ -782,6 +809,44 @@ func TestResourceSliceStrategyUpdate(t *testing.T) {
return obj
}(),
},
"drop-list-type-attributes": {
oldObj: slice.DeepCopy(),
newObj: func() *resource.ResourceSlice {
obj := sliceWithListTypeAttributes.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
listTypeAttributes: false,
expectValidationError: true,
},
"keep-list-type-attributes": {
oldObj: sliceWithListTypeAttributes,
newObj: func() *resource.ResourceSlice {
obj := sliceWithListTypeAttributes.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
listTypeAttributes: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithListTypeAttributes.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
"keep-existing-list-type-attributes-without-featuregate-enabled": {
oldObj: sliceWithListTypeAttributes,
newObj: func() *resource.ResourceSlice {
obj := sliceWithListTypeAttributes.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
listTypeAttributes: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithListTypeAttributes.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
}
for name, tc := range testcases {