diff --git a/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller.go b/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller.go index 3e9c052ec40..7660d503a72 100644 --- a/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller.go +++ b/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller.go @@ -491,14 +491,17 @@ func (c *Controller) syncPool(ctx context.Context, poolName string) error { if pool, ok := resources.Pools[poolName]; ok { // Match each existing slice against the desired slices. - // Two slices match if they contain exactly the same - // device IDs, in an arbitrary order. Such a matched - // slice gets updated with the desired content if - // there is a difference. + // Two slices "match" if they contain exactly the + // same device IDs, in an arbitrary order. As a + // special case, slices are also considered + // "matched" in the scenario where there's a single + // existing slice and a single desired slice. Such a + // matched slice gets updated with the desired + // content if there is a difference. // - // This supports updating the definition of devices - // in a slice. Adding or removing devices is done - // by deleting the old slice and creating a new one. + // In the case where there is more than one existing + // or desired slices, adding or removing devices is + // done by deleting the old slice and creating a new one. // // This is primarily a simplification of the code: // to support adding or removing devices from @@ -511,24 +514,32 @@ func (c *Controller) syncPool(ctx context.Context, poolName string) error { // At the end of the loop, each current slice is either // a match or obsolete. currentSliceForDesiredSlice := make(map[int]*resourceapi.ResourceSlice, len(pool.Slices)) - for _, currentSlice := range currentSlices { - matched := false - for i := range pool.Slices { - if _, ok := currentSliceForDesiredSlice[i]; ok { - // Already has a match. - continue + if len(currentSlices) == 1 && len(pool.Slices) == 1 { + // If there's just one existing slice and one desired slice, assume + // they "matched" such that if required, it is the existing slice + // which gets updated and we avoid an unnecessary deletion and + // recreation of the slice. + currentSliceForDesiredSlice[0] = currentSlices[0] + } else { + for _, currentSlice := range currentSlices { + matched := false + for i := range pool.Slices { + if _, ok := currentSliceForDesiredSlice[i]; ok { + // Already has a match. + continue + } + if sameSlice(currentSlice, &pool.Slices[i]) { + currentSliceForDesiredSlice[i] = currentSlice + logger.V(5).Info("Matched existing slice", "slice", klog.KObj(currentSlice), "matchIndex", i) + matched = true + break + } } - if sameSlice(currentSlice, &pool.Slices[i]) { - currentSliceForDesiredSlice[i] = currentSlice - logger.V(5).Info("Matched existing slice", "slice", klog.KObj(currentSlice), "matchIndex", i) - matched = true - break + if !matched { + obsoleteSlices = append(obsoleteSlices, currentSlice) + logger.V(5).Info("Unmatched existing slice", "slice", klog.KObj(currentSlice)) } } - if !matched { - obsoleteSlices = append(obsoleteSlices, currentSlice) - logger.V(5).Info("Unmatched existing slice", "slice", klog.KObj(currentSlice)) - } } // Desired metadata which must be set in each slice. diff --git a/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller_test.go b/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller_test.go index 226b998071a..faa7ac18a98 100644 --- a/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller_test.go +++ b/staging/src/k8s.io/dynamic-resource-allocation/resourceslice/resourceslicecontroller_test.go @@ -272,7 +272,7 @@ func TestControllerSyncPool(t *testing.T) { }, expectedResourceSlices: nil, }, - "delete-and-add-slice": { + "one-existing-and-one-desired-slice-should-be-updated-inplace": { nodeUID: nodeUID, initialObjects: []runtime.Object{ // no devices @@ -288,14 +288,52 @@ func TestControllerSyncPool(t *testing.T) { }, }, expectedStats: Stats{ - NumDeletes: 1, + NumUpdates: 1, + }, + expectedResourceSlices: []resourceapi.ResourceSlice{ + *MakeResourceSlice().Name(resourceSlice1).UID(resourceSlice1).ResourceVersion("1"). + NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName). + Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName}}). + Pool(resourceapi.ResourcePool{Name: poolName, Generation: 1, ResourceSliceCount: 1}).Obj(), + }, + }, + "delete-and-add-slice-when-more-than-one-existing-or-desired-slice": { + nodeUID: nodeUID, + initialObjects: []runtime.Object{ + // No devices in first ResourceSlice. + MakeResourceSlice().Name(resourceSlice1).UID(resourceSlice1). + NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName). + Driver(driverName).Devices([]resourceapi.Device{}). + Pool(resourceapi.ResourcePool{Name: poolName, Generation: 1, ResourceSliceCount: 2}).Obj(), + MakeResourceSlice().Name(resourceSlice2).UID(resourceSlice2). + NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName). + Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName2}}). + Pool(resourceapi.ResourcePool{Name: poolName, Generation: 1, ResourceSliceCount: 2}).Obj(), + }, + inputDriverResources: &DriverResources{ + Pools: map[string]Pool{ + poolName: { + Slices: []Slice{ + {Devices: []resourceapi.Device{{Name: deviceName1}}}, + {Devices: []resourceapi.Device{{Name: deviceName2}}}, + }, + }, + }, + }, + expectedStats: Stats{ NumCreates: 1, + NumUpdates: 1, + NumDeletes: 1, }, expectedResourceSlices: []resourceapi.ResourceSlice{ *MakeResourceSlice().Name(generatedName1).GenerateName(generateName). NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName). - Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName}}). - Pool(resourceapi.ResourcePool{Name: poolName, Generation: 2, ResourceSliceCount: 1}).Obj(), + Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName1}}). + Pool(resourceapi.ResourcePool{Name: poolName, Generation: 2, ResourceSliceCount: 2}).Obj(), + *MakeResourceSlice().Name(resourceSlice2).UID(resourceSlice2).ResourceVersion("1"). + NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName). + Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName2}}). + Pool(resourceapi.ResourcePool{Name: poolName, Generation: 2, ResourceSliceCount: 2}).Obj(), }, }, "delete-redundant-slice": {