Repository: kubernetes-sigs/kueue (go)

Issue:
Ceiling not set equal zero, it should mean unlimited
In the API we mention that [no ceiling means unlimited](https://github.com/kubernetes-sigs/kueue/blob/27b05145f94ac528ff92790efc70d62d8c9feafc/api/v1alpha1/clusterqueue_types.go#L250), which is currently not true and the ceiling must be explicitly set.

We need to change the type to a pointer to distinguish between not set and 0.


Pull request:
Make .quota.ceiling a pointer
#### What type of PR is this?

/kind bug
/kind api-change

#### What this PR does / why we need it:

Makes `ceiling` a pointer with the semantics that `nil` means no limit. This should simplify ClusterQueue configurations, specially when there are no cohorts.

#### Which issue(s) this PR fixes:

Fixes #144

#### Special notes for your reviewer:



Patch:
diff --git a/api/v1alpha1/clusterqueue_types.go b/api/v1alpha1/clusterqueue_types.go
index 18bca462b..1f77ffe9f 100644
--- a/api/v1alpha1/clusterqueue_types.go
+++ b/api/v1alpha1/clusterqueue_types.go
@@ -247,8 +247,8 @@ type Quota struct {
 	// ceiling is the upper limit on the amount of resource requests that
 	// could be used by running workloads assigned to this quota at a point in time.
 	// Resources can be borrowed from unused guaranteed quota of other
-	// ClusterQueues in the same cohort. When not set, it is unlimited.
-	Ceiling resource.Quantity `json:"ceiling,omitempty"`
+	// ClusterQueues in the same cohort. When null, it is unlimited.
+	Ceiling *resource.Quantity `json:"ceiling,omitempty"`
 }
 
 // ClusterQueueStatus defines the observed state of ClusterQueue
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 054cbe3a0..59fff2bca 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -445,7 +445,11 @@ func (in *QueuedWorkloadStatus) DeepCopy() *QueuedWorkloadStatus {
 func (in *Quota) DeepCopyInto(out *Quota) {
 	*out = *in
 	out.Guaranteed = in.Guaranteed.DeepCopy()
-	out.Ceiling = in.Ceiling.DeepCopy()
+	if in.Ceiling != nil {
+		in, out := &in.Ceiling, &out.Ceiling
+		x := (*in).DeepCopy()
+		*out = &x
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Quota.
diff --git a/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml b/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
index 36d4e5923..350857d74 100644
--- a/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
+++ b/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
@@ -199,7 +199,7 @@ spec:
                                   workloads assigned to this quota at a point in time.
                                   Resources can be borrowed from unused guaranteed
                                   quota of other ClusterQueues in the same cohort.
-                                  When not set, it is unlimited.
+                                  When null, it is unlimited.
                                 pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                                 x-kubernetes-int-or-string: true
                               guaranteed:
diff --git a/config/samples/single-clusterqueue-setup.yaml b/config/samples/single-clusterqueue-setup.yaml
index 203521b11..0d0a1b902 100644
--- a/config/samples/single-clusterqueue-setup.yaml
+++ b/config/samples/single-clusterqueue-setup.yaml
@@ -15,13 +15,11 @@ spec:
     - resourceFlavor: default
       quota:
         guaranteed: 9
-        ceiling: 9
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
         guaranteed: 36Gi
-        ceiling: 36Gi
 ---
 apiVersion: kueue.x-k8s.io/v1alpha1
 kind: Queue
diff --git a/docs/tasks/administer_cluster_resources.md b/docs/tasks/administer_cluster_resources.md
index 2bb085deb..8a9083476 100644
--- a/docs/tasks/administer_cluster_resources.md
+++ b/docs/tasks/administer_cluster_resources.md
@@ -44,21 +44,16 @@ spec:
     - resourceFlavor: default
       quota:
         guaranteed: 9
-        ceiling: 9
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
         guaranteed: 36Gi
-        ceiling: 36Gi
 ```
 
 This ClusterQueue governs the usage of [resource types](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-types)
 `cpu` and `memory`. Each resource type has a single [resource flavor](/docs/concepts/cluster_queue.md#resource-flavors),
 named `default` with a guaranteed quota.
-As there is a single ClusterQueue in the cluster, it is not possible for the
-ClusterQueue to borrow resources from anywhere. Thus, the `ceiling` is equal
-to the `guaranteed` quota.
 
 The empty `namespaceSelector` allows any namespace to use these resources.
 
diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go
index d8bc033bc..a5e46e9d4 100644
--- a/pkg/cache/cache.go
+++ b/pkg/cache/cache.go
@@ -97,7 +97,7 @@ type ClusterQueue struct {
 type FlavorLimits struct {
 	Name       string
 	Guaranteed int64
-	Ceiling    int64
+	Ceiling    *int64
 }
 
 func (c *Cache) newClusterQueue(cq *kueue.ClusterQueue) (*ClusterQueue, error) {
@@ -138,7 +138,7 @@ func (c *ClusterQueue) update(in *kueue.ClusterQueue, resourceFlavors map[string
 	return nil
 }
 
-// UpdateLabelkeys updates a ClusterQueue's LabelKeys based on the passed ResourceFlavors set.
+// UpdateLabelKeys updates a ClusterQueue's LabelKeys based on the passed ResourceFlavors set.
 // Exported only for testing.
 func (c *ClusterQueue) UpdateLabelKeys(flavors map[string]*kueue.ResourceFlavor) {
 	labelKeys := map[corev1.ResourceName]sets.String{}
@@ -473,7 +473,9 @@ func resourceLimitsByName(in []kueue.Resource) map[corev1.ResourceName][]FlavorL
 			fLimits := FlavorLimits{
 				Name:       string(f.ResourceFlavor),
 				Guaranteed: workload.ResourceValue(r.Name, f.Quota.Guaranteed),
-				Ceiling:    workload.ResourceValue(r.Name, f.Quota.Ceiling),
+			}
+			if f.Quota.Ceiling != nil {
+				fLimits.Ceiling = pointer.Int64(workload.ResourceValue(r.Name, *f.Quota.Ceiling))
 			}
 			flavors[i] = fLimits
 
diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go
index f544c265b..a00f68c49 100644
--- a/pkg/scheduler/scheduler.go
+++ b/pkg/scheduler/scheduler.go
@@ -348,7 +348,7 @@ func flavorSelector(spec *corev1.PodSpec, allowedKeys sets.String) nodeaffinity.
 // If it fits, also returns any borrowing required.
 func fitsFlavorLimits(name corev1.ResourceName, val int64, cq *cache.ClusterQueue, flavor *cache.FlavorLimits) (bool, int64) {
 	used := cq.UsedResources[name][flavor.Name]
-	if used+val > flavor.Ceiling {
+	if flavor.Ceiling != nil && used+val > *flavor.Ceiling {
 		// Past borrowing limit.
 		return false, 0
 	}
diff --git a/pkg/util/pointer/pointer.go b/pkg/util/pointer/pointer.go
index 00f9e8b45..63d6a0b0c 100644
--- a/pkg/util/pointer/pointer.go
+++ b/pkg/util/pointer/pointer.go
@@ -16,8 +16,17 @@ limitations under the License.
 
 package pointer
 
-import "k8s.io/apimachinery/pkg/api/resource"
+import (
+	"k8s.io/apimachinery/pkg/api/resource"
+	"k8s.io/utils/pointer"
+)
 
 func Quantity(q resource.Quantity) *resource.Quantity {
 	return &q
 }
+
+var (
+	Int32 = pointer.Int32
+	Int64 = pointer.Int64
+	Bool  = pointer.Bool
+)


Test patch:
diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go
index cdeb94e31..adcfe542d 100644
--- a/pkg/cache/cache_test.go
+++ b/pkg/cache/cache_test.go
@@ -45,16 +45,15 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				RequestableResources: []kueue.Resource{
 					{
 						Name: corev1.ResourceCPU,
-						Flavors: []kueue.Flavor{
-							{
-								ResourceFlavor: "default",
-								Quota: kueue.Quota{
-									Guaranteed: resource.MustParse("10"),
-									Ceiling:    resource.MustParse("20"),
-								},
+						Flavors: []kueue.Flavor{{
+							ResourceFlavor: "default",
+							Quota: kueue.Quota{
+								Guaranteed: resource.MustParse("10"),
+								Ceiling:    pointer.Quantity(resource.MustParse("20")),
 							},
-						},
-					}},
+						}},
+					},
+				},
 				Cohort: "one",
 			},
 		},
@@ -63,9 +62,15 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 			Spec: kueue.ClusterQueueSpec{
 				RequestableResources: []kueue.Resource{
 					{
-						Name:    corev1.ResourceCPU,
-						Flavors: []kueue.Flavor{{ResourceFlavor: "default"}},
-					}},
+						Name: corev1.ResourceCPU,
+						Flavors: []kueue.Flavor{{
+							ResourceFlavor: "default",
+							Quota: kueue.Quota{
+								Guaranteed: resource.MustParse("15"),
+							},
+						}},
+					},
+				},
 				Cohort: "one",
 			},
 		},
@@ -107,7 +112,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"a": {
 					Name: "a",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Guaranteed: 10000, Ceiling: 20000}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 10000, Ceiling: pointer.Int64(20000)}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					LabelKeys:         map[corev1.ResourceName]sets.String{corev1.ResourceCPU: sets.NewString("cpuType")},
@@ -116,7 +121,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"b": {
 					Name: "b",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default"}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 15000}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					UsedResources:     Resources{corev1.ResourceCPU: {"default": 0}},
@@ -141,7 +146,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 			},
 		},
 		{
-			name: "add flavors after queue capacties",
+			name: "add flavors after queue capacities",
 			operation: func(cache *Cache) {
 				for _, c := range initialClusterQueues {
 					if err := cache.AddClusterQueue(context.Background(), &c); err != nil {
@@ -157,7 +162,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"a": {
 					Name: "a",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Guaranteed: 10000, Ceiling: 20000}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 10000, Ceiling: pointer.Int64(20000)}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					LabelKeys:         map[corev1.ResourceName]sets.String{corev1.ResourceCPU: sets.NewString("cpuType")},
@@ -166,7 +171,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"b": {
 					Name: "b",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default"}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 15000}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					UsedResources:     Resources{corev1.ResourceCPU: {"default": 0}},
@@ -206,7 +211,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 											ResourceFlavor: "default",
 											Quota: kueue.Quota{
 												Guaranteed: resource.MustParse("5"),
-												Ceiling:    resource.MustParse("10"),
+												Ceiling:    pointer.Quantity(resource.MustParse("10")),
 											},
 										},
 									},
@@ -237,7 +242,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"a": {
 					Name: "a",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Guaranteed: 5000, Ceiling: 10000}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 5000, Ceiling: pointer.Int64(10000)}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					LabelKeys:         map[corev1.ResourceName]sets.String{corev1.ResourceCPU: sets.NewString("cpuType", "region")},
@@ -283,7 +288,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"b": {
 					Name: "b",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default"}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 15000}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					UsedResources:     Resources{corev1.ResourceCPU: {"default": 0}},
@@ -799,7 +804,7 @@ func TestClusterQueueUsage(t *testing.T) {
 							ResourceFlavor: "default",
 							Quota: kueue.Quota{
 								Guaranteed: resource.MustParse("10"),
-								Ceiling:    resource.MustParse("20"),
+								Ceiling:    pointer.Quantity(resource.MustParse("20")),
 							},
 						},
 					},
@@ -811,14 +816,14 @@ func TestClusterQueueUsage(t *testing.T) {
 							ResourceFlavor: "model_a",
 							Quota: kueue.Quota{
 								Guaranteed: resource.MustParse("5"),
-								Ceiling:    resource.MustParse("10"),
+								Ceiling:    pointer.Quantity(resource.MustParse("10")),
 							},
 						},
 						{
 							ResourceFlavor: "model_b",
 							Quota: kueue.Quota{
 								Guaranteed: resource.MustParse("5"),
-								Ceiling:    resource.MustParse("10"),
+								// No ceiling.
 							},
 						},
 					},
diff --git a/pkg/scheduler/scheduler_test.go b/pkg/scheduler/scheduler_test.go
index 541235707..6f44f0a92 100644
--- a/pkg/scheduler/scheduler_test.go
+++ b/pkg/scheduler/scheduler_test.go
@@ -33,6 +33,7 @@ import (
 	"k8s.io/client-go/tools/record"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+	"sigs.k8s.io/kueue/pkg/util/pointer"
 
 	kueue "sigs.k8s.io/kueue/api/v1alpha1"
 	"sigs.k8s.io/kueue/pkg/cache"
@@ -77,7 +78,7 @@ func TestSchedule(t *testing.T) {
 								ResourceFlavor: "default",
 								Quota: kueue.Quota{
 									Guaranteed: resource.MustParse("50"),
-									Ceiling:    resource.MustParse("50"),
+									Ceiling:    pointer.Quantity(resource.MustParse("50")),
 								},
 							},
 						},
@@ -107,14 +108,14 @@ func TestSchedule(t *testing.T) {
 								ResourceFlavor: "on-demand",
 								Quota: kueue.Quota{
 									Guaranteed: resource.MustParse("50"),
-									Ceiling:    resource.MustParse("100"),
+									Ceiling:    pointer.Quantity(resource.MustParse("100")),
 								},
 							},
 							{
 								ResourceFlavor: "spot",
 								Quota: kueue.Quota{
 									Guaranteed: resource.MustParse("100"),
-									Ceiling:    resource.MustParse("100"),
+									Ceiling:    pointer.Quantity(resource.MustParse("100")),
 								},
 							},
 						},
@@ -144,14 +145,14 @@ func TestSchedule(t *testing.T) {
 								ResourceFlavor: "on-demand",
 								Quota: kueue.Quota{
 									Guaranteed: resource.MustParse("50"),
-									Ceiling:    resource.MustParse("60"),
+									Ceiling:    pointer.Quantity(resource.MustParse("60")),
 								},
 							},
 							{
 								ResourceFlavor: "spot",
 								Quota: kueue.Quota{
 									Guaranteed: resource.MustParse("0"),
-									Ceiling:    resource.MustParse("100"),
+									Ceiling:    pointer.Quantity(resource.MustParse("100")),
 								},
 							},
 						},
@@ -163,7 +164,7 @@ func TestSchedule(t *testing.T) {
 								ResourceFlavor: "model-a",
 								Quota: kueue.Quota{
 									Guaranteed: resource.MustParse("20"),
-									Ceiling:    resource.MustParse("20"),
+									Ceiling:    pointer.Quantity(resource.MustParse("20")),
 								},
 							},
 						},
@@ -842,8 +843,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU:    defaultFlavorNoBorrowing(1000),
-					corev1.ResourceMemory: defaultFlavorNoBorrowing(2 * utiltesting.Mi),
+					corev1.ResourceCPU:    {{Name: "default", Guaranteed: 1000}},
+					corev1.ResourceMemory: {{Name: "default", Guaranteed: 2 * utiltesting.Mi}},
 				},
 			},
 			wantFits: true,
@@ -867,18 +868,20 @@ func TestEntryAssignFlavors(t *testing.T) {
 								},
 							},
 						},
-						Tolerations: []corev1.Toleration{corev1.Toleration{
+						Tolerations: []corev1.Toleration{{
 							Key:      "instance",
 							Operator: corev1.TolerationOpEqual,
 							Value:    "spot",
 							Effect:   corev1.TaintEffectNoSchedule,
 						}},
-					}}},
+					},
+				},
+			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "tainted", Guaranteed: 4000},
-					}),
+					},
 				},
 			},
 			wantFits: true,
@@ -900,7 +903,7 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: defaultFlavorNoBorrowing(4000),
+					corev1.ResourceCPU: {{Name: "default", Guaranteed: 4000}},
 				},
 				UsedResources: cache.Resources{
 					corev1.ResourceCPU: {
@@ -922,14 +925,14 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "one", Guaranteed: 2000},
 						{Name: "two", Guaranteed: 4000},
-					}),
-					corev1.ResourceMemory: noBorrowing([]cache.FlavorLimits{
+					},
+					corev1.ResourceMemory: {
 						{Name: "one", Guaranteed: utiltesting.Gi},
 						{Name: "two", Guaranteed: 5 * utiltesting.Mi},
-					}),
+					},
 				},
 			},
 			wantFits: true,
@@ -953,14 +956,14 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "one", Guaranteed: 2000},
 						{Name: "two", Guaranteed: 4000},
-					}),
-					corev1.ResourceMemory: noBorrowing([]cache.FlavorLimits{
+					},
+					corev1.ResourceMemory: {
 						{Name: "one", Guaranteed: utiltesting.Gi},
 						{Name: "two", Guaranteed: 5 * utiltesting.Mi},
-					}),
+					},
 				},
 			},
 		},
@@ -976,10 +979,10 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "tainted", Guaranteed: 4000},
 						{Name: "two", Guaranteed: 4000},
-					}),
+					},
 				},
 			},
 			wantFits: true,
@@ -1001,10 +1004,10 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "non-existent", Guaranteed: 4000},
 						{Name: "two", Guaranteed: 4000},
-					}),
+					},
 				},
 			},
 			wantFits: true,
@@ -1050,10 +1053,10 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "one", Guaranteed: 4000},
 						{Name: "two", Guaranteed: 4000},
-					}),
+					},
 				},
 				LabelKeys: map[corev1.ResourceName]sets.String{corev1.ResourceCPU: sets.NewString("cpuType")},
 			},
@@ -1101,14 +1104,14 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "one", Guaranteed: 4000},
 						{Name: "two", Guaranteed: 4000},
-					}),
-					corev1.ResourceMemory: noBorrowing([]cache.FlavorLimits{
+					},
+					corev1.ResourceMemory: {
 						{Name: "one", Guaranteed: utiltesting.Gi},
 						{Name: "two", Guaranteed: utiltesting.Gi},
-					}),
+					},
 				},
 			},
 			wantFits: true,
@@ -1165,10 +1168,10 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "one", Guaranteed: 4000},
 						{Name: "two", Guaranteed: 4000},
-					}),
+					},
 				},
 			},
 			wantFits: true,
@@ -1211,10 +1214,10 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "one", Guaranteed: 4000},
 						{Name: "two", Guaranteed: 4000},
-					}),
+					},
 				},
 				LabelKeys: map[corev1.ResourceName]sets.String{corev1.ResourceCPU: sets.NewString("cpuType")},
 			},
@@ -1239,10 +1242,10 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: noBorrowing([]cache.FlavorLimits{
+					corev1.ResourceCPU: {
 						{Name: "one", Guaranteed: 4000},
 						{Name: "two", Guaranteed: 10_000},
-					}),
+					},
 				},
 			},
 			wantFits: true,
@@ -1280,14 +1283,14 @@ func TestEntryAssignFlavors(t *testing.T) {
 						{
 							Name:       "default",
 							Guaranteed: 2000,
-							Ceiling:    100_000,
+							Ceiling:    pointer.Int64(100_000),
 						},
 					},
 					corev1.ResourceMemory: {
 						{
 							Name:       "default",
 							Guaranteed: 2 * utiltesting.Gi,
-							Ceiling:    100 * utiltesting.Gi,
+							// No ceiling.
 						},
 					},
 				},
@@ -1338,7 +1341,7 @@ func TestEntryAssignFlavors(t *testing.T) {
 						{
 							Name:       "one",
 							Guaranteed: 1000,
-							Ceiling:    10_000,
+							// No ceiling.
 						},
 					},
 				},
@@ -1368,7 +1371,7 @@ func TestEntryAssignFlavors(t *testing.T) {
 						{
 							Name:       "one",
 							Guaranteed: 1000,
-							Ceiling:    10_000,
+							Ceiling:    pointer.Int64(10_000),
 						},
 					},
 				},
@@ -1472,14 +1475,3 @@ func TestEntryOrdering(t *testing.T) {
 		t.Errorf("Unexpected order (-want,+got):\n%s", diff)
 	}
 }
-
-func defaultFlavorNoBorrowing(guaranteed int64) []cache.FlavorLimits {
-	return noBorrowing([]cache.FlavorLimits{{Name: "default", Guaranteed: guaranteed}})
-}
-
-func noBorrowing(flavors []cache.FlavorLimits) []cache.FlavorLimits {
-	for i := range flavors {
-		flavors[i].Ceiling = flavors[i].Guaranteed
-	}
-	return flavors
-}
diff --git a/pkg/util/testing/wrappers.go b/pkg/util/testing/wrappers.go
index 2aa4ecedd..bf48e11d2 100644
--- a/pkg/util/testing/wrappers.go
+++ b/pkg/util/testing/wrappers.go
@@ -24,10 +24,10 @@ import (
 	schedulingv1 "k8s.io/api/scheduling/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/utils/pointer"
 
 	kueue "sigs.k8s.io/kueue/api/v1alpha1"
 	"sigs.k8s.io/kueue/pkg/constants"
+	"sigs.k8s.io/kueue/pkg/util/pointer"
 )
 
 // JobWrapper wraps a Job.
@@ -42,8 +42,8 @@ func MakeJob(name, ns string) *JobWrapper {
 			Annotations: make(map[string]string, 1),
 		},
 		Spec: batchv1.JobSpec{
-			Parallelism: pointer.Int32Ptr(1),
-			Suspend:     pointer.BoolPtr(true),
+			Parallelism: pointer.Int32(1),
+			Suspend:     pointer.Bool(true),
 			Template: corev1.PodTemplateSpec{
 				Spec: corev1.PodSpec{
 					RestartPolicy: "Never",
@@ -69,13 +69,13 @@ func (j *JobWrapper) Obj() *batchv1.Job {
 
 // Suspend updates the suspend status of the job
 func (j *JobWrapper) Suspend(s bool) *JobWrapper {
-	j.Spec.Suspend = pointer.BoolPtr(s)
+	j.Spec.Suspend = pointer.Bool(s)
 	return j
 }
 
 // Parallelism updates job parallelism.
 func (j *JobWrapper) Parallelism(p int32) *JobWrapper {
-	j.Spec.Parallelism = pointer.Int32Ptr(p)
+	j.Spec.Parallelism = pointer.Int32(p)
 	return j
 }
 
@@ -315,7 +315,6 @@ func MakeFlavor(rf, guaranteed string) *FlavorWrapper {
 		ResourceFlavor: kueue.ResourceFlavorReference(rf),
 		Quota: kueue.Quota{
 			Guaranteed: resource.MustParse(guaranteed),
-			Ceiling:    resource.MustParse(guaranteed),
 		},
 	}}
 }
@@ -327,7 +326,7 @@ func (f *FlavorWrapper) Obj() *kueue.Flavor {
 
 // Ceiling updates the flavor ceiling.
 func (f *FlavorWrapper) Ceiling(c string) *FlavorWrapper {
-	f.Quota.Ceiling = resource.MustParse(c)
+	f.Quota.Ceiling = pointer.Quantity(resource.MustParse(c))
 	return f
 }
 
diff --git a/test/integration/scheduler/scheduler_test.go b/test/integration/scheduler/scheduler_test.go
index 159aca133..996184c06 100644
--- a/test/integration/scheduler/scheduler_test.go
+++ b/test/integration/scheduler/scheduler_test.go
@@ -46,6 +46,7 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		onDemandFlavor      *kueue.ResourceFlavor
 		spotTaintedFlavor   *kueue.ResourceFlavor
 		spotUntaintedFlavor *kueue.ResourceFlavor
+		spotToleration      corev1.Toleration
 	)
 
 	ginkgo.BeforeEach(func() {
@@ -67,6 +68,12 @@ var _ = ginkgo.Describe("Scheduler", func() {
 				Effect: corev1.TaintEffectNoSchedule,
 			}).Obj()
 		gomega.Expect(k8sClient.Create(ctx, spotTaintedFlavor)).Should(gomega.Succeed())
+		spotToleration = corev1.Toleration{
+			Key:      instanceKey,
+			Operator: corev1.TolerationOpEqual,
+			Value:    spotTaintedFlavor.Name,
+			Effect:   corev1.TaintEffectNoSchedule,
+		}
 
 		spotUntaintedFlavor = testing.MakeResourceFlavor("spot-untainted").Label(instanceKey, "spot-untainted").Obj()
 		gomega.Expect(k8sClient.Create(ctx, spotUntaintedFlavor)).Should(gomega.Succeed())
@@ -74,8 +81,8 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		prodClusterQ = testing.MakeClusterQueue("prod-cq").
 			Cohort("prod").
 			Resource(testing.MakeResource(corev1.ResourceCPU).
-				Flavor(testing.MakeFlavor(spotTaintedFlavor.Name, "5").Obj()).
-				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Ceiling("10").Obj()).
+				Flavor(testing.MakeFlavor(spotTaintedFlavor.Name, "5").Ceiling("5").Obj()).
+				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Obj()).
 				Obj()).
 			Obj()
 		gomega.Expect(k8sClient.Create(ctx, prodClusterQ)).Should(gomega.Succeed())
@@ -175,12 +182,7 @@ var _ = ginkgo.Describe("Scheduler", func() {
 
 		ginkgo.By("checking a third job with toleration starts")
 		job3 := testing.MakeJob("spot-job3", ns.Name).Queue(prodQueue.Name).
-			Toleration(corev1.Toleration{
-				Key:      instanceKey,
-				Operator: corev1.TolerationOpEqual,
-				Value:    spotTaintedFlavor.Name,
-				Effect:   corev1.TaintEffectNoSchedule,
-			}).
+			Toleration(spotToleration).
 			Request(corev1.ResourceCPU, "5").Obj()
 		gomega.Expect(k8sClient.Create(ctx, job3)).Should(gomega.Succeed())
 		createdJob3 := &batchv1.Job{}
@@ -193,7 +195,8 @@ var _ = ginkgo.Describe("Scheduler", func() {
 
 	ginkgo.It("Should schedule jobs using borrowed ClusterQueue", func() {
 		ginkgo.By("checking a no-fit job does not start")
-		job := testing.MakeJob("job", ns.Name).Queue(prodQueue.Name).Request(corev1.ResourceCPU, "10").Obj()
+		job := testing.MakeJob("job", ns.Name).Queue(prodQueue.Name).
+			Request(corev1.ResourceCPU, "10").Toleration(spotToleration).Obj()
 		gomega.Expect(k8sClient.Create(ctx, job)).Should(gomega.Succeed())
 		lookupKey := types.NamespacedName{Name: job.Name, Namespace: job.Namespace}
 		createdJob := &batchv1.Job{}
@@ -205,7 +208,8 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		fallbackClusterQueue := testing.MakeClusterQueue("fallback-cq").
 			Cohort(prodClusterQ.Spec.Cohort).
 			Resource(testing.MakeResource(corev1.ResourceCPU).
-				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Ceiling("10").Obj()).
+				Flavor(testing.MakeFlavor(spotTaintedFlavor.Name, "5").Obj()). // prod-cq can't borrow this flavor due to its ceiling.
+				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Obj()).
 				Obj()).
 			Obj()
 		gomega.Expect(k8sClient.Create(ctx, fallbackClusterQueue)).Should(gomega.Succeed())