Repository: kubernetes-sigs/kueue
Pull request #190 (2022-04-08 15:38:35)

Add instructions for one-line installation
#### What type of PR is this?

/kind documentation

#### What this PR does / why we need it:

#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

#### Special notes for your reviewer:

We use `devel` as the image tag for the `main` branch.

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/kueue/pull/190#" title="Author self-approved">alculquicondor</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fkueue).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-sigs/kueue/blob/main/OWNERS)~~ [alculquicondor]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/kueue/pull/190#" title="Author self-approved">alculquicondor</a>*, *<a href="https://github.com/kubernetes-sigs/kueue/pull/190#pullrequestreview-936707630" title="Approved">cortespao</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fkueue).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-sigs/kueue/blob/main/OWNERS)~~ [alculquicondor]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/Makefile b/Makefile
index 0bba7e274..09eced871 100644
--- a/Makefile
+++ b/Makefile
@@ -159,7 +159,7 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified
 deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
 	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMAGE_TAG}
 	$(KUSTOMIZE) build config/default | kubectl apply -f -
-	cd config/manager && $(KUSTOMIZE) edit set image controller=gcr.io/k8s-staging-kueue/kueue:latest
+	cd config/manager && $(KUSTOMIZE) edit set image controller=gcr.io/k8s-staging-kueue/kueue:devel
 
 .PHONY: undeploy
 undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
diff --git a/README.md b/README.md
index 5c69ba539..6ef6ab53a 100644
--- a/README.md
+++ b/README.md
@@ -22,15 +22,18 @@ presents the detailed design of the controller.
 
 ## Installation
 
-**Requires Kubernetes 1.22 or newer**. Learn how to [install the Kubernetes tools](https://kubernetes.io/docs/tasks/tools/).
+**Requires Kubernetes 1.22 or newer**.
 
-You can deploy Kueue in your cluster with the following command:
+To install the latest development version of Kueue in your cluster, run the
+following command:
 
-```sh
-IMAGE_REGISTRY=registry.example.com/my-user make image-build image-push deploy
+```shell
+kubectl apply -k github.com/kubernetes-sigs/kueue/config/default
 ```
 
-The controller will run in the `kueue-system` namespace.
+The controller runs in the `kueue-system` namespace.
+
+Read the [installation guide](/docs/setup/install.md) to learn more.
 
 ## Usage
 
diff --git a/api/v1alpha1/clusterqueue_types.go b/api/v1alpha1/clusterqueue_types.go
index 84a2ed970..40f77e28f 100644
--- a/api/v1alpha1/clusterqueue_types.go
+++ b/api/v1alpha1/clusterqueue_types.go
@@ -32,11 +32,11 @@ type ClusterQueueSpec struct {
 	// - name: cpu
 	//   flavors:
 	//   - quota:
-	//       min: 100
+	//       guaranteed: 100
 	// - name: memory
 	//   flavors:
 	//   - quota:
-	//       min: 100Gi
+	//       guaranteed: 100Gi
 	//
 	// +listType=map
 	// +listMapKey=name
@@ -53,17 +53,31 @@ type ClusterQueueSpec struct {
 	// 1. tenantB can run a workload consuming up to 20 k80 GPUs, meaning a resource
 	//    can be allocated from more than one clusterQueue in a cohort.
 	// 2. tenantB can not consume any p100 GPUs or spot because its QC has no quota
-	//    defined for them, and so the max is implicitly 0.
+	//    defined for them, and so the ceiling is practically 0.
 	// 3. If both tenantA and tenantB are running jobs such that current usage for
-	//    tenantA is lower than its min quota (e.g., 5 k80 GPUS) while
-	//    tenantB’s usage is higher than its min quota (e.g., 12 k80 GPUs),
+	//    tenantA is lower than its guaranteed quota (e.g., 5 k80 GPUS) while
+	//    tenantB’s usage is higher than its guaranteed quota (e.g., 12 k80 GPUs),
 	//    and both tenants have pending jobs requesting the remaining clusterQueue of
 	//    the cohort (the 3 k80 GPUs), then tenantA jobs will get this remaining
-	//    clusterQueue since tenantA is below its min limit.
+	//    clusterQueue since tenantA is below its guaranteed limit.
 	// 4. If a tenantA workload doesn’t tolerate spot, then the workload will only
 	//    be eligible to consume on-demand cores (the next in the list of cpu flavors).
-	// 5. Before considering on-demand, the workload will get assigned spot if
-	//    the quota can be borrowed from the cohort.
+	//
+	//  <UNRESOLVED>
+	// 5. While evaluating a resource flavor’s list, what should take precedence:
+	//    honoring the preferred order in the list or keeping a usage under the
+	//    guaranteed clusterQueue? For example, if tenantA’s current k80 usage is 10 and
+	//    tenantB’s usage is 5, should a future tenantA workload that asks for any
+	//    GPU model be assigned borrowed k80 clusterQueue (since it is ordered first in
+	//    the list) or p100 since its usage is under tenantA’s guaranteed limit?
+	//    The tradeoff is honoring tenantA’s preferred order vs honoring fair
+	//    sharing of future tenantB’s jobs in a timely manner (or, when we have
+	//    preemption, reduce the chance of preempting tenantA’s workload)
+	//
+	//    We could make that a user choice via a knob on the QC or Cohort if we
+	//    decide to have a dedicated object API for it and start with preferring to
+	//    consume guaranteed clusterQueue first.
+	//  </UNRESOLVED>
 	//
 	// metadata:
 	//  name: tenantA
@@ -73,21 +87,26 @@ type ClusterQueueSpec struct {
 	// - name: cpu
 	//   - name: spot
 	//     quota:
-	//       min: 1000
+	//       guaranteed: 1000
+	//     labels
+	//     - cloud.provider.com/spot:true
+	//     taints
+	//     - key: cloud.provider.com/spot
+	//       effect: NoSchedule
 	//   - name: on-demand
 	//     quota:
-	//       min: 100
+	//       guaranteed: 100
 	// - name: nvidia.com/gpus
 	//   - name: k80
 	//     quota:
-	//       min: 10
-	//       max: 20
+	//       guaranteed: 10
+	//       ceiling: 20
 	//     labels:
 	//     - cloud.provider.com/accelerator: nvidia-tesla-k80
 	//   - name: p100
 	//     quota:
-	//       min: 10
-	//       max: 20
+	//       guaranteed: 10
+	//       ceiling: 20
 	//     labels:
 	//     - cloud.provider.com/accelerator: nvidia-tesla-p100
 	//
@@ -99,12 +118,12 @@ type ClusterQueueSpec struct {
 	// - name: cpu
 	//   - name: on-demand
 	//     quota:
-	//       min: 100
+	//       guaranteed: 100
 	// - name: nvidia.com/gpus
 	//   - name: k80
 	//     quota:
-	//       min: 10
-	//       max: 20
+	//       guaranteed: 10
+	//       ceiling: 20
 	//     labels:
 	//     - cloud.provider.com/accelerator: nvidia-tesla-k80
 	//
@@ -170,17 +189,48 @@ type Resource struct {
 	// - name: nvidia.com/gpus
 	//   - name: k80
 	//     quota:
-	//       min: 10
+	//       guaranteed: 10
+	//     labels:
+	//       cloud.provider.com/accelerator: nvidia-tesla-k80
 	//   - name: p100
 	//     quota:
-	//       min: 10
+	//       guaranteed: 10
+	//     labels:
+	//       cloud.provider.com/accelerator: nvidia-tesla-p100
 	//
 	// The flavors are evaluated in order, selecting the first to satisfy a
 	// workload’s requirements. Also the quantities are additive, in the example
 	// above the GPU quota in total is 20 (10 k80 + 10 p100).
 	// A workload is limited to the selected type by converting the labels to a node
-	// selector that gets injected into the workload. This list can’t be empty, at
-	// least one flavor must exist.
+	// selector that gets injected into the workload. ​​This list can’t be empty, at
+	// least one must exist.
+	//
+	// Note that a workload’s node affinity/selector constraints are evaluated
+	// against the labels, and so batch users can “filter” the flavors, but can’t
+	// force a different order. For example, the following workload affinity will
+	// only start the workload if P100 quota is available:
+	//
+	// matchExpressions:
+	// - key: cloud.provider.com/accelerator
+	//   value: nvidia-tesla-p100
+	//
+	// Each type can also set taints so that it is opt-out by default.
+	// A workload’s tolerations are evaluated against those taints, and only the
+	// flavors that the workload tolerates are considered. For example, an admin
+	// may choose to taint Spot CPU clusterQueue, and if a workload doesn't tolerate it
+	// will only be eligible to consume on-demand clusterQueue:
+	//
+	// - name: spot
+	//   quota:
+	//     guaranteed: 1000
+	//   labels
+	//   - cloud.provider.com/spot:true
+	//   taints
+	//   - key: cloud.provider.com/spot
+	//     effect: NoSchedule
+	// - name: on-demand
+	//   quota:
+	//     guaranteed: 100
 	//
 	// +listType=map
 	// +listMapKey=resourceFlavor
@@ -200,19 +250,17 @@ type Flavor struct {
 type ResourceFlavorReference string
 
 type Quota struct {
-	// min amount of resource requests that are available to be used by workloads
-	// admitted by this ClusterQueue at a point in time.
-	// The sum of min quotas for a flavor in a cohort defines the maximum amount
-	// of resources that can be allocated by a ClusterQueue in the cohort.
-	Min resource.Quantity `json:"min,omitempty"`
+	// guaranteed amount of resource requests that are available to be used by
+	// running workloads assigned to this quota. This value should not exceed
+	// the Ceiling. The sum of guaranteed values in a cohort defines the maximum
+	// clusterQueue that can be allocated for the cohort.
+	Guaranteed resource.Quantity `json:"guaranteed,omitempty"`
 
-	// max is the upper limit on the amount of resource requests that
-	// can be used by workloads admitted by this ClusterQueue at a point in time.
-	// Resources can be borrowed from unused min quota of other
-	// ClusterQueues in the same cohort.
-	// If not null, it must be greater than or equal to min.
-	// If null, there is no upper limit for borrowing.
-	Max *resource.Quantity `json:"max,omitempty"`
+	// 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 null, it is unlimited.
+	Ceiling *resource.Quantity `json:"ceiling,omitempty"`
 }
 
 // ClusterQueueStatus defines the observed state of ClusterQueue
@@ -240,7 +288,8 @@ type Usage struct {
 	// borrowed from the cohort.
 	Total *resource.Quantity `json:"total,omitempty"`
 
-	// Borrowed is the used quantity past the min quota, borrowed from the cohort.
+	// Borrowed is the used quantity past the guaranteed quota, borrowed from
+	// the cohort.
 	Borrowed *resource.Quantity `json:"borrowing,omitempty"`
 }
 
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 0b7bd0372..56ee144e6 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -313,9 +313,9 @@ func (in *QueueStatus) DeepCopy() *QueueStatus {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Quota) DeepCopyInto(out *Quota) {
 	*out = *in
-	out.Min = in.Min.DeepCopy()
-	if in.Max != nil {
-		in, out := &in.Max, &out.Max
+	out.Guaranteed = in.Guaranteed.DeepCopy()
+	if in.Ceiling != nil {
+		in, out := &in.Ceiling, &out.Ceiling
 		x := (*in).DeepCopy()
 		*out = &x
 	}
diff --git a/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml b/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
index a59ea3b07..63c0db40d 100644
--- a/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
+++ b/config/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
@@ -64,29 +64,43 @@ spec:
                   tenantB can run a workload consuming up to 20 k80 GPUs, meaning
                   a resource can be allocated from more than one clusterQueue in a
                   cohort. 2. tenantB can not consume any p100 GPUs or spot because
-                  its QC has no quota defined for them, and so the max is implicitly
+                  its QC has no quota defined for them, and so the ceiling is practically
                   0. 3. If both tenantA and tenantB are running jobs such that current
-                  usage for tenantA is lower than its min quota (e.g., 5 k80 GPUS)
-                  while tenantB’s usage is higher than its min quota (e.g., 12 k80
-                  GPUs), and both tenants have pending jobs requesting the remaining
-                  clusterQueue of the cohort (the 3 k80 GPUs), then tenantA jobs will
-                  get this remaining clusterQueue since tenantA is below its min limit.
-                  4. If a tenantA workload doesn’t tolerate spot, then the workload
-                  will only be eligible to consume on-demand cores (the next in the
-                  list of cpu flavors). 5. Before considering on-demand, the workload
-                  will get assigned spot if the quota can be borrowed from the cohort.
-                  \n metadata: name: tenantA spec: cohort: borrowing-cohort requestableResources:
-                  - name: cpu - name: spot quota: min: 1000 - name: on-demand quota:
-                  min: 100 - name: nvidia.com/gpus - name: k80 quota: min: 10 max:
-                  20 labels: - cloud.provider.com/accelerator: nvidia-tesla-k80 -
-                  name: p100 quota: min: 10 max: 20 labels: - cloud.provider.com/accelerator:
-                  nvidia-tesla-p100 \n metadata: name: tenantB spec: cohort: borrowing-cohort
-                  requestableResources: - name: cpu - name: on-demand quota: min:
-                  100 - name: nvidia.com/gpus - name: k80 quota: min: 10 max: 20 labels:
-                  - cloud.provider.com/accelerator: nvidia-tesla-k80 \n If empty,
-                  this ClusterQueue cannot borrow from any other ClusterQueue and
-                  vice versa. \n The name style is similar to label keys. These are
-                  just names to link QCs together, and they are meaningless otherwise."
+                  usage for tenantA is lower than its guaranteed quota (e.g., 5 k80
+                  GPUS) while tenantB’s usage is higher than its guaranteed quota
+                  (e.g., 12 k80 GPUs), and both tenants have pending jobs requesting
+                  the remaining clusterQueue of the cohort (the 3 k80 GPUs), then
+                  tenantA jobs will get this remaining clusterQueue since tenantA
+                  is below its guaranteed limit. 4. If a tenantA workload doesn’t
+                  tolerate spot, then the workload will only be eligible to consume
+                  on-demand cores (the next in the list of cpu flavors). \n <UNRESOLVED>
+                  5. While evaluating a resource flavor’s list, what should take precedence:
+                  honoring the preferred order in the list or keeping a usage under
+                  the guaranteed clusterQueue? For example, if tenantA’s current k80
+                  usage is 10 and tenantB’s usage is 5, should a future tenantA workload
+                  that asks for any GPU model be assigned borrowed k80 clusterQueue
+                  (since it is ordered first in the list) or p100 since its usage
+                  is under tenantA’s guaranteed limit? The tradeoff is honoring tenantA’s
+                  preferred order vs honoring fair sharing of future tenantB’s jobs
+                  in a timely manner (or, when we have preemption, reduce the chance
+                  of preempting tenantA’s workload) \n We could make that a user choice
+                  via a knob on the QC or Cohort if we decide to have a dedicated
+                  object API for it and start with preferring to consume guaranteed
+                  clusterQueue first. </UNRESOLVED> \n metadata: name: tenantA spec:
+                  cohort: borrowing-cohort requestableResources: - name: cpu - name:
+                  spot quota: guaranteed: 1000 labels - cloud.provider.com/spot:true
+                  taints - key: cloud.provider.com/spot effect: NoSchedule - name:
+                  on-demand quota: guaranteed: 100 - name: nvidia.com/gpus - name:
+                  k80 quota: guaranteed: 10 ceiling: 20 labels: - cloud.provider.com/accelerator:
+                  nvidia-tesla-k80 - name: p100 quota: guaranteed: 10 ceiling: 20
+                  labels: - cloud.provider.com/accelerator: nvidia-tesla-p100 \n metadata:
+                  name: tenantB spec: cohort: borrowing-cohort requestableResources:
+                  - name: cpu - name: on-demand quota: guaranteed: 100 - name: nvidia.com/gpus
+                  - name: k80 quota: guaranteed: 10 ceiling: 20 labels: - cloud.provider.com/accelerator:
+                  nvidia-tesla-k80 \n If empty, this ClusterQueue cannot borrow from
+                  any other ClusterQueue and vice versa. \n The name style is similar
+                  to label keys. These are just names to link QCs together, and they
+                  are meaningless otherwise."
                 type: string
               namespaceSelector:
                 description: namespaceSelector defines which namespaces are allowed
@@ -156,8 +170,8 @@ spec:
                   of workloads dispatched via this clusterQueue. This doesn’t guarantee
                   the actual availability of resources, although an integration with
                   a resource provisioner like Cluster Autoscaler is possible to achieve
-                  that. Example: \n - name: cpu flavors: - quota: min: 100 - name:
-                  memory flavors: - quota: min: 100Gi"
+                  that. Example: \n - name: cpu flavors: - quota: guaranteed: 100
+                  - name: memory flavors: - quota: guaranteed: 100Gi"
                 items:
                   properties:
                     flavors:
@@ -170,43 +184,58 @@ spec:
                         want to define different limits for different gpu models,
                         then each model is mapped to a flavor and must set different
                         values of a shared key. For example: \n spec: requestableResources:
-                        - name: nvidia.com/gpus - name: k80 quota: min: 10 - name:
-                        p100 quota: min: 10 \n The flavors are evaluated in order,
-                        selecting the first to satisfy a workload’s requirements.
-                        Also the quantities are additive, in the example above the
-                        GPU quota in total is 20 (10 k80 + 10 p100). A workload is
-                        limited to the selected type by converting the labels to a
-                        node selector that gets injected into the workload. This list
-                        can’t be empty, at least one flavor must exist."
+                        - name: nvidia.com/gpus - name: k80 quota: guaranteed: 10
+                        labels: cloud.provider.com/accelerator: nvidia-tesla-k80 -
+                        name: p100 quota: guaranteed: 10 labels: cloud.provider.com/accelerator:
+                        nvidia-tesla-p100 \n The flavors are evaluated in order, selecting
+                        the first to satisfy a workload’s requirements. Also the quantities
+                        are additive, in the example above the GPU quota in total
+                        is 20 (10 k80 + 10 p100). A workload is limited to the selected
+                        type by converting the labels to a node selector that gets
+                        injected into the workload. ​​This list can’t be empty, at
+                        least one must exist. \n Note that a workload’s node affinity/selector
+                        constraints are evaluated against the labels, and so batch
+                        users can “filter” the flavors, but can’t force a different
+                        order. For example, the following workload affinity will only
+                        start the workload if P100 quota is available: \n matchExpressions:
+                        - key: cloud.provider.com/accelerator value: nvidia-tesla-p100
+                        \n Each type can also set taints so that it is opt-out by
+                        default. A workload’s tolerations are evaluated against those
+                        taints, and only the flavors that the workload tolerates are
+                        considered. For example, an admin may choose to taint Spot
+                        CPU clusterQueue, and if a workload doesn't tolerate it will
+                        only be eligible to consume on-demand clusterQueue: \n - name:
+                        spot quota: guaranteed: 1000 labels - cloud.provider.com/spot:true
+                        taints - key: cloud.provider.com/spot effect: NoSchedule -
+                        name: on-demand quota: guaranteed: 100"
                       items:
                         properties:
                           quota:
                             description: quota is the limit of resource usage at a
                               point in time.
                             properties:
-                              max:
+                              ceiling:
                                 anyOf:
                                 - type: integer
                                 - type: string
-                                description: max is the upper limit on the amount
-                                  of resource requests that can be used by workloads
-                                  admitted by this ClusterQueue at a point in time.
-                                  Resources can be borrowed from unused min quota
-                                  of other ClusterQueues in the same cohort. If not
-                                  null, it must be greater than or equal to min. If
-                                  null, there is no upper limit for borrowing.
+                                description: 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 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
-                              min:
+                              guaranteed:
                                 anyOf:
                                 - type: integer
                                 - type: string
-                                description: min amount of resource requests that
-                                  are available to be used by workloads admitted by
-                                  this ClusterQueue at a point in time. The sum of
-                                  min quotas for a flavor in a cohort defines the
-                                  maximum amount of resources that can be allocated
-                                  by a ClusterQueue in the cohort.
+                                description: guaranteed amount of resource requests
+                                  that are available to be used by running workloads
+                                  assigned to this quota. This value should not exceed
+                                  the Ceiling. The sum of guaranteed values in a cohort
+                                  defines the maximum clusterQueue that can be allocated
+                                  for the cohort.
                                 pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                                 x-kubernetes-int-or-string: true
                             type: object
@@ -256,8 +285,8 @@ spec:
                         anyOf:
                         - type: integer
                         - type: string
-                        description: Borrowed is the used quantity past the min quota,
-                          borrowed from the cohort.
+                        description: Borrowed is the used quantity past the guaranteed
+                          quota, borrowed from the cohort.
                         pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                         x-kubernetes-int-or-string: true
                       total:
diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml
index 70cb9594f..44371efa0 100644
--- a/config/manager/kustomization.yaml
+++ b/config/manager/kustomization.yaml
@@ -13,4 +13,4 @@ kind: Kustomization
 images:
 - name: controller
   newName: gcr.io/k8s-staging-kueue/kueue
-  newTag: latest
+  newTag: devel
diff --git a/config/samples/single-clusterqueue-setup.yaml b/config/samples/single-clusterqueue-setup.yaml
index 7b11c8988..0d0a1b902 100644
--- a/config/samples/single-clusterqueue-setup.yaml
+++ b/config/samples/single-clusterqueue-setup.yaml
@@ -14,12 +14,12 @@ spec:
     flavors:
     - resourceFlavor: default
       quota:
-        min: 9
+        guaranteed: 9
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 36Gi
+        guaranteed: 36Gi
 ---
 apiVersion: kueue.x-k8s.io/v1alpha1
 kind: Queue
diff --git a/docs/concepts/cluster_queue.md b/docs/concepts/cluster_queue.md
index 5e94b676b..e62d78383 100644
--- a/docs/concepts/cluster_queue.md
+++ b/docs/concepts/cluster_queue.md
@@ -21,12 +21,12 @@ spec:
     flavors:
     - resourceFlavor: default
       quota:
-        min: 9
+        guaranteed: 9
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 36Gi
+        guaranteed: 36Gi
 ```
 
 This ClusterQueue admits [workloads](workload.md) if and only if:
@@ -177,10 +177,9 @@ When borrowing, Kueue satisfies the following semantics:
 
 - When assigning flavors, Kueue goes through the list of flavors in
   `.spec.requestableResources[*].flavors`. For each flavor, Kueue attempts to
-  fit the workload using the min quota of the ClusterQueue or the unused
-  min quota of other ClusterQueues in the cohort, up to the max quota of the
-  ClusterQueue. If the workload doesn't fit, Kueue proceeds evaluating the next
-  flavor in the list.
+  fit the workload using the guaranteed quota of the ClusterQueue or the unused
+  quota in the cohort. If the workload doesn't fit, Kueue proceeds evaluating
+  the next flavor in the list.
 - Borrowing happens per-flavor. A ClusterQueue can only borrow quota of flavors
   it defines.
 
@@ -201,12 +200,12 @@ spec:
     flavors:
     - resourceFlavor: default
       quota:
-        min: 9
+        guaranteed: 9
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 36Gi
+        guaranteed: 36Gi
 ```
 
 ```yaml
@@ -222,12 +221,12 @@ spec:
     flavors:
     - resourceFlavor: default
       quota:
-        min: 12
+        guaranteed: 12
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 48Gi
+        guaranteed: 48Gi
 ```
 
 ClusterQueue `team-a-cq` can admit workloads depending on the following
@@ -237,22 +236,22 @@ scenarios:
   `team-a-cq` can admit workloads with resources adding up to `12+9=21` CPUs and
   `48+36=84Gi` of memory.
 - If ClusterQueue `team-b-cq` has pending workloads and the ClusterQueue
-  `team-a-cq` has all its `min` quota used, Kueue will admit workloads in
+  `team-a-cq` has all its `guaranteed` quota used, Kueue will admit workloads in
   ClusterQueue `team-b-cq` before admitting any new workloads in `team-a-cq`.
-  Therefore, Kueue ensures the `min` quota for `team-b-cq` is met.
+  Therefore, Kueue ensures the `guaranteed` quota for `team-b-cq` is met.
 
 **Note**: Kueue [does not support preemption](https://github.com/kubernetes-sigs/kueue/issues/83).
 No admitted workloads will be stopped to make space for new workloads.
 
-### Max quotas
+### Ceilings
 
 To limit the amount of resources that a ClusterQueue can borrow from others,
-you can set the `.spec.requestableResources[*].flavors[*].quota.max`
+you can set the `.spec.requestableResources[*].flavors[*].quota.ceiling`
 [quantity](https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/quantity/) field.
-`max` must be greater than or equal to `min`.
+The `ceiling` must be greater than or equal to `guaranteed`.
 
-If, for a given flavor, the `max` field is empty or null, a ClusterQueue can
-borrow up to the sum of min quotas from all the ClusterQueues in the cohort.
+If, for a given flavor, the `ceiling` field is empty or null, a ClusterQueue can
+borrow the sum of the guaranteed quotas in all the ClusterQueues in the cohort.
 
 ## What's next?
 
diff --git a/docs/setup/install.md b/docs/setup/install.md
index 88eb54627..0e9174a91 100644
--- a/docs/setup/install.md
+++ b/docs/setup/install.md
@@ -8,9 +8,29 @@ Make sure the following conditions are met:
 - The SuspendJob [feature gate](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/) is enabled. In Kubernetes 1.22 or newer, the feature gate is enabled by default.
 - The kubectl command-line tool has communication with your cluster.
 
-## Install Kueue
+## Install the latest development version
 
-To deploy Kueue in your cluster, run the following commands:
+To install the latest development version of Kueue in your cluster, run the
+following command:
+
+```shell
+kubectl apply -k github.com/kubernetes-sigs/kueue/config/default
+```
+
+The controller runs in the `kueue-system` namespace.
+
+### Uninstall
+
+To uninstall Kueue, run the following command:
+
+```shell
+kubectl delete -k github.com/kubernetes-sigs/kueue/config/default
+```
+
+## Build and install from source
+
+To build Kueue from source and install Kueue in your cluster, run the following
+commands:
 
 ```sh
 git clone https://github.com/kubernetes-sigs/kueue.git
@@ -18,9 +38,7 @@ cd kueue
 IMAGE_REGISTRY=registry.example.com/my-user make image-build image-push deploy
 ```
 
-The controller will run in the `kueue-system` namespace.
-
-## Uninstall Kueue
+### Uninstall
 
 To uninstall Kueue, run the following command:
 
diff --git a/docs/tasks/administer_cluster_quotas.md b/docs/tasks/administer_cluster_quotas.md
index b6e292d97..f2414205c 100644
--- a/docs/tasks/administer_cluster_quotas.md
+++ b/docs/tasks/administer_cluster_quotas.md
@@ -45,12 +45,12 @@ spec:
     flavors:
     - resourceFlavor: default
       quota:
-        min: 9
+        guaranteed: 9
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 36Gi
+        guaranteed: 36Gi
 ```
 
 To create the ClusterQueue, run the following command:
@@ -61,7 +61,7 @@ kubectl apply -f cluster-total.yaml
 
 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#resourceflavor-object),
-named `default` with a min quota.
+named `default` with a guaranteed quota.
 
 The empty `namespaceSelector` allows any namespace to use these resources.
 
@@ -186,15 +186,15 @@ spec:
     flavors:
     - resourceFlavor: x86
       quota:
-        min: 9
+        guaranteed: 9
     - resourceFlavor: arm
       quota:
-        min: 12
+        guaranteed: 12
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 84Gi
+        guaranteed: 84Gi
 ```
 
 The flavor names in the fields `.spec.requestableResources[*].flavors[*].resourceFlavor`
@@ -232,14 +232,14 @@ spec:
     flavors:
     - resourceFlavor: default
       quota:
-        min: 9
-        max: 15
+        guaranteed: 9
+        ceiling: 15
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 36Gi
-        max: 60Gi
+        guaranteed: 36Gi
+        ceiling: 60Gi
 ```
 
 ```yaml
@@ -256,17 +256,17 @@ spec:
     flavors:
     - resourceFlavor: default
       quota:
-        min: 12
+        guaranteed: 12
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 48Gi
+        guaranteed: 48Gi
 ```
 
-Note that the ClusterQueue `team-a-cq` also defines [max quotas](/docs/concepts/cluster_queue.md#max-quotas).
+Note that the ClusterQueue `team-a-cq` also defines [ceilings](/docs/concepts/cluster_queue.md#ceilings).
 This restricts the ability of the ClusterQueue to borrow the unused quota from
-the cohort up to the configured `max`, even if the quota is completely unused.
+the cohort up to the configured ceiling, even if the quota is completely unused.
 
 To create these ClusterQueues, save the preceding manifests and run the
 following command:
@@ -278,9 +278,9 @@ kubectl apply -f team-a-cq.yaml -f team-b-cq.yaml
 ## Multiple ClusterQueue with dedicated and fallback flavors
 
 A ClusterQueue can borrow resources from the [cohort](/docs/concepts/cluster_queue.md#cohort)
-even if the ClusterQueue has zero min quota for a flavor. This allows you to
-give dedicated quota for a flavor and fallback to quota for a different flavor,
-shared with other tenants.
+even if the ClusterQueue has zero guaranteed quota for a flavor. This allows you
+to give dedicated quota for a flavor and fallback to quota for a different
+flavor, shared with other tenants.
 
 Such setup can be accomplished with a ClusterQueue for each tenant and an extra
 ClusterQueue for the shared resources. For example, the manifests for two
@@ -300,16 +300,16 @@ spec:
     flavors:
     - resourceFlavor: arm
       quota:
-        min: 9
-        max: 9
+        guaranteed: 9
+        ceiling: 9
     - resourceFlavor: x86
       quota:
-        min: 0
+        guaranteed: 0
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 36Gi
+        guaranteed: 36Gi
 ```
 
 ```yaml
@@ -326,15 +326,15 @@ spec:
     flavors:
     - resourceFlavor: arm
       quota:
-        min: 12
+        guaranteed: 12
     - resourceFlavor: x86
       quota:
-        min: 0
+        guaranteed: 0
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 48Gi
+        guaranteed: 48Gi
 ```
 
 ```yaml
@@ -351,20 +351,20 @@ spec:
     flavors:
     - resourceFlavor: x86
       quota:
-        min: 6
+        guaranteed: 6
   - name: "memory"
     flavors:
     - resourceFlavor: default
       quota:
-        min: 24Gi
+        guaranteed: 24Gi
 ```
 
 Note the following setup:
 
-- `team-a-cq` and `team-b-cq` define a `max` equal to their `min`
+- `team-a-cq` and `team-b-cq` define a `ceiling` equal to their `guaranteed`
   quota for the `arm` flavor. Therefore, they can't borrow this flavor from each
   other.
-- `team-a-cq` and `team-b-cq` define `min: 0` for the `x86` flavor.
+- `team-a-cq` and `team-b-cq` define `guaranteed: 0` for the `x86` flavor.
   Therefore, they don't have any dedicated quota for the flavor and they can
   only borrow it from `shared-cq`.
 
diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go
index 5f89110d5..7c0b3e80e 100644
--- a/pkg/cache/cache.go
+++ b/pkg/cache/cache.go
@@ -94,9 +94,9 @@ type ClusterQueue struct {
 
 // FlavorLimits holds a processed ClusterQueue flavor quota.
 type FlavorLimits struct {
-	Name string
-	Min  int64
-	Max  *int64
+	Name       string
+	Guaranteed int64
+	Ceiling    *int64
 }
 
 func (c *Cache) newClusterQueue(cq *kueue.ClusterQueue) (*ClusterQueue, error) {
@@ -413,7 +413,7 @@ func (c *Cache) Usage(cqObj *kueue.ClusterQueue) (kueue.UsedResources, int, erro
 			fUsage := kueue.Usage{
 				Total: pointer.Quantity(workload.ResourceQuantity(rName, used)),
 			}
-			borrowing := used - flavor.Min
+			borrowing := used - flavor.Guaranteed
 			if borrowing > 0 {
 				fUsage.Borrowed = pointer.Quantity(workload.ResourceQuantity(rName, borrowing))
 			}
@@ -470,11 +470,11 @@ func resourceLimitsByName(in []kueue.Resource) map[corev1.ResourceName][]FlavorL
 		for i := range flavors {
 			f := &r.Flavors[i]
 			fLimits := FlavorLimits{
-				Name: string(f.ResourceFlavor),
-				Min:  workload.ResourceValue(r.Name, f.Quota.Min),
+				Name:       string(f.ResourceFlavor),
+				Guaranteed: workload.ResourceValue(r.Name, f.Quota.Guaranteed),
 			}
-			if f.Quota.Max != nil {
-				fLimits.Max = pointer.Int64(workload.ResourceValue(r.Name, *f.Quota.Max))
+			if f.Quota.Ceiling != nil {
+				fLimits.Ceiling = pointer.Int64(workload.ResourceValue(r.Name, *f.Quota.Ceiling))
 			}
 			flavors[i] = fLimits
 
diff --git a/pkg/cache/snapshot.go b/pkg/cache/snapshot.go
index 7808ec70c..1c3d6aa7b 100644
--- a/pkg/cache/snapshot.go
+++ b/pkg/cache/snapshot.go
@@ -89,7 +89,7 @@ func (c *ClusterQueue) accumulateResources(cohort *Cohort) {
 			cohort.RequestableResources[name] = req
 		}
 		for _, flavor := range flavors {
-			req[flavor.Name] += flavor.Min
+			req[flavor.Name] += flavor.Guaranteed
 		}
 	}
 	if cohort.UsedResources == nil {
diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go
index f00157e8d..8040b2d1a 100644
--- a/pkg/scheduler/scheduler.go
+++ b/pkg/scheduler/scheduler.go
@@ -375,17 +375,17 @@ 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 flavor.Max != nil && used+val > *flavor.Max {
+	if flavor.Ceiling != nil && used+val > *flavor.Ceiling {
 		// Past borrowing limit.
 		return false, 0
 	}
 	cohortUsed := used
-	cohortTotal := flavor.Min
+	cohortTotal := flavor.Guaranteed
 	if cq.Cohort != nil {
 		cohortUsed = cq.Cohort.UsedResources[name][flavor.Name]
 		cohortTotal = cq.Cohort.RequestableResources[name][flavor.Name]
 	}
-	borrow := used + val - flavor.Min
+	borrow := used + val - flavor.Guaranteed
 	if borrow < 0 {
 		borrow = 0
 	}
@@ -408,18 +408,18 @@ func (e entryOrdering) Swap(i, j int) {
 }
 
 // Less is the ordering criteria:
-// 1. request under min quota before borrowing.
+// 1. guaranteed before borrowing.
 // 2. FIFO on creation timestamp.
 func (e entryOrdering) Less(i, j int) bool {
 	a := e[i]
 	b := e[j]
-	// 1. Request under min quota.
-	aMin := len(a.borrows) == 0
-	bMin := len(b.borrows) == 0
-	if aMin != bMin {
-		return aMin
+	// 1. Prefer guaranteed (not borrowing)
+	aGuaranteed := len(a.borrows) == 0
+	bGuaranteed := len(b.borrows) == 0
+	if aGuaranteed != bGuaranteed {
+		return aGuaranteed
 	}
-	// 2. FIFO.
+	// 2. FIFO
 	return a.Obj.CreationTimestamp.Before(&b.Obj.CreationTimestamp)
 }
 


Test patch:
diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go
index 626d2c25f..15fcae135 100644
--- a/pkg/cache/cache_test.go
+++ b/pkg/cache/cache_test.go
@@ -48,8 +48,8 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 						Flavors: []kueue.Flavor{{
 							ResourceFlavor: "default",
 							Quota: kueue.Quota{
-								Min: resource.MustParse("10"),
-								Max: pointer.Quantity(resource.MustParse("20")),
+								Guaranteed: resource.MustParse("10"),
+								Ceiling:    pointer.Quantity(resource.MustParse("20")),
 							},
 						}},
 					},
@@ -66,7 +66,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 						Flavors: []kueue.Flavor{{
 							ResourceFlavor: "default",
 							Quota: kueue.Quota{
-								Min: resource.MustParse("15"),
+								Guaranteed: resource.MustParse("15"),
 							},
 						}},
 					},
@@ -112,7 +112,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"a": {
 					Name: "a",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Min: 10000, Max: pointer.Int64(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")},
@@ -121,7 +121,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"b": {
 					Name: "b",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Min: 15000}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 15000}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					UsedResources:     Resources{corev1.ResourceCPU: {"default": 0}},
@@ -162,7 +162,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"a": {
 					Name: "a",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Min: 10000, Max: pointer.Int64(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")},
@@ -171,7 +171,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"b": {
 					Name: "b",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Min: 15000}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 15000}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					UsedResources:     Resources{corev1.ResourceCPU: {"default": 0}},
@@ -210,8 +210,8 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 										{
 											ResourceFlavor: "default",
 											Quota: kueue.Quota{
-												Min: resource.MustParse("5"),
-												Max: pointer.Quantity(resource.MustParse("10")),
+												Guaranteed: resource.MustParse("5"),
+												Ceiling:    pointer.Quantity(resource.MustParse("10")),
 											},
 										},
 									},
@@ -242,7 +242,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"a": {
 					Name: "a",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Min: 5000, Max: pointer.Int64(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")},
@@ -288,7 +288,7 @@ func TestCacheClusterQueueOperations(t *testing.T) {
 				"b": {
 					Name: "b",
 					RequestableResources: map[corev1.ResourceName][]FlavorLimits{
-						corev1.ResourceCPU: {{Name: "default", Min: 15000}},
+						corev1.ResourceCPU: {{Name: "default", Guaranteed: 15000}},
 					},
 					NamespaceSelector: labels.Nothing(),
 					UsedResources:     Resources{corev1.ResourceCPU: {"default": 0}},
@@ -803,8 +803,8 @@ func TestClusterQueueUsage(t *testing.T) {
 						{
 							ResourceFlavor: "default",
 							Quota: kueue.Quota{
-								Min: resource.MustParse("10"),
-								Max: pointer.Quantity(resource.MustParse("20")),
+								Guaranteed: resource.MustParse("10"),
+								Ceiling:    pointer.Quantity(resource.MustParse("20")),
 							},
 						},
 					},
@@ -815,15 +815,15 @@ func TestClusterQueueUsage(t *testing.T) {
 						{
 							ResourceFlavor: "model_a",
 							Quota: kueue.Quota{
-								Min: resource.MustParse("5"),
-								Max: pointer.Quantity(resource.MustParse("10")),
+								Guaranteed: resource.MustParse("5"),
+								Ceiling:    pointer.Quantity(resource.MustParse("10")),
 							},
 						},
 						{
 							ResourceFlavor: "model_b",
 							Quota: kueue.Quota{
-								Min: resource.MustParse("5"),
-								// No max.
+								Guaranteed: resource.MustParse("5"),
+								// No ceiling.
 							},
 						},
 					},
diff --git a/pkg/cache/snapshot_test.go b/pkg/cache/snapshot_test.go
index 7de795e2c..55fceb6f0 100644
--- a/pkg/cache/snapshot_test.go
+++ b/pkg/cache/snapshot_test.go
@@ -55,13 +55,13 @@ func TestSnapshot(t *testing.T) {
 							{
 								ResourceFlavor: "demand",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("100"),
+									Guaranteed: resource.MustParse("100"),
 								},
 							},
 							{
 								ResourceFlavor: "spot",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("200"),
+									Guaranteed: resource.MustParse("200"),
 								},
 							},
 						},
@@ -82,7 +82,7 @@ func TestSnapshot(t *testing.T) {
 							{
 								ResourceFlavor: "spot",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("100"),
+									Guaranteed: resource.MustParse("100"),
 								},
 							},
 						},
@@ -93,7 +93,7 @@ func TestSnapshot(t *testing.T) {
 							{
 								ResourceFlavor: "default",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("50"),
+									Guaranteed: resource.MustParse("50"),
 								},
 							},
 						},
@@ -113,7 +113,7 @@ func TestSnapshot(t *testing.T) {
 							{
 								ResourceFlavor: "default",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("100"),
+									Guaranteed: resource.MustParse("100"),
 								},
 							},
 						},
@@ -258,12 +258,12 @@ func TestSnapshot(t *testing.T) {
 				RequestableResources: map[corev1.ResourceName][]FlavorLimits{
 					corev1.ResourceCPU: {
 						{
-							Name: "demand",
-							Min:  100_000,
+							Name:       "demand",
+							Guaranteed: 100_000,
 						},
 						{
-							Name: "spot",
-							Min:  200_000,
+							Name:       "spot",
+							Guaranteed: 200_000,
 						},
 					},
 				},
@@ -285,14 +285,14 @@ func TestSnapshot(t *testing.T) {
 				RequestableResources: map[corev1.ResourceName][]FlavorLimits{
 					corev1.ResourceCPU: {
 						{
-							Name: "spot",
-							Min:  100_000,
+							Name:       "spot",
+							Guaranteed: 100_000,
 						},
 					},
 					"example.com/gpu": {
 						{
-							Name: "default",
-							Min:  50,
+							Name:       "default",
+							Guaranteed: 50,
 						},
 					},
 				},
@@ -316,8 +316,8 @@ func TestSnapshot(t *testing.T) {
 				RequestableResources: map[corev1.ResourceName][]FlavorLimits{
 					corev1.ResourceCPU: {
 						{
-							Name: "default",
-							Min:  100_000,
+							Name:       "default",
+							Guaranteed: 100_000,
 						},
 					},
 				},
diff --git a/pkg/scheduler/scheduler_test.go b/pkg/scheduler/scheduler_test.go
index a445bd5fe..741c965dc 100644
--- a/pkg/scheduler/scheduler_test.go
+++ b/pkg/scheduler/scheduler_test.go
@@ -77,8 +77,8 @@ func TestSchedule(t *testing.T) {
 							{
 								ResourceFlavor: "default",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("50"),
-									Max: pointer.Quantity(resource.MustParse("50")),
+									Guaranteed: resource.MustParse("50"),
+									Ceiling:    pointer.Quantity(resource.MustParse("50")),
 								},
 							},
 						},
@@ -107,15 +107,15 @@ func TestSchedule(t *testing.T) {
 							{
 								ResourceFlavor: "on-demand",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("50"),
-									Max: pointer.Quantity(resource.MustParse("100")),
+									Guaranteed: resource.MustParse("50"),
+									Ceiling:    pointer.Quantity(resource.MustParse("100")),
 								},
 							},
 							{
 								ResourceFlavor: "spot",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("100"),
-									Max: pointer.Quantity(resource.MustParse("100")),
+									Guaranteed: resource.MustParse("100"),
+									Ceiling:    pointer.Quantity(resource.MustParse("100")),
 								},
 							},
 						},
@@ -144,15 +144,15 @@ func TestSchedule(t *testing.T) {
 							{
 								ResourceFlavor: "on-demand",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("50"),
-									Max: pointer.Quantity(resource.MustParse("60")),
+									Guaranteed: resource.MustParse("50"),
+									Ceiling:    pointer.Quantity(resource.MustParse("60")),
 								},
 							},
 							{
 								ResourceFlavor: "spot",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("0"),
-									Max: pointer.Quantity(resource.MustParse("100")),
+									Guaranteed: resource.MustParse("0"),
+									Ceiling:    pointer.Quantity(resource.MustParse("100")),
 								},
 							},
 						},
@@ -163,8 +163,8 @@ func TestSchedule(t *testing.T) {
 							{
 								ResourceFlavor: "model-a",
 								Quota: kueue.Quota{
-									Min: resource.MustParse("20"),
-									Max: pointer.Quantity(resource.MustParse("20")),
+									Guaranteed: resource.MustParse("20"),
+									Ceiling:    pointer.Quantity(resource.MustParse("20")),
 								},
 							},
 						},
@@ -843,8 +843,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU:    {{Name: "default", Min: 1000}},
-					corev1.ResourceMemory: {{Name: "default", Min: 2 * utiltesting.Mi}},
+					corev1.ResourceCPU:    {{Name: "default", Guaranteed: 1000}},
+					corev1.ResourceMemory: {{Name: "default", Guaranteed: 2 * utiltesting.Mi}},
 				},
 			},
 			wantFits: true,
@@ -880,7 +880,7 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "tainted", Min: 4000},
+						{Name: "tainted", Guaranteed: 4000},
 					},
 				},
 			},
@@ -903,7 +903,7 @@ func TestEntryAssignFlavors(t *testing.T) {
 			},
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
-					corev1.ResourceCPU: {{Name: "default", Min: 4000}},
+					corev1.ResourceCPU: {{Name: "default", Guaranteed: 4000}},
 				},
 				UsedResources: cache.Resources{
 					corev1.ResourceCPU: {
@@ -926,12 +926,12 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "one", Min: 2000},
-						{Name: "two", Min: 4000},
+						{Name: "one", Guaranteed: 2000},
+						{Name: "two", Guaranteed: 4000},
 					},
 					corev1.ResourceMemory: {
-						{Name: "one", Min: utiltesting.Gi},
-						{Name: "two", Min: 5 * utiltesting.Mi},
+						{Name: "one", Guaranteed: utiltesting.Gi},
+						{Name: "two", Guaranteed: 5 * utiltesting.Mi},
 					},
 				},
 			},
@@ -957,12 +957,12 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "one", Min: 2000},
-						{Name: "two", Min: 4000},
+						{Name: "one", Guaranteed: 2000},
+						{Name: "two", Guaranteed: 4000},
 					},
 					corev1.ResourceMemory: {
-						{Name: "one", Min: utiltesting.Gi},
-						{Name: "two", Min: 5 * utiltesting.Mi},
+						{Name: "one", Guaranteed: utiltesting.Gi},
+						{Name: "two", Guaranteed: 5 * utiltesting.Mi},
 					},
 				},
 			},
@@ -980,8 +980,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "tainted", Min: 4000},
-						{Name: "two", Min: 4000},
+						{Name: "tainted", Guaranteed: 4000},
+						{Name: "two", Guaranteed: 4000},
 					},
 				},
 			},
@@ -1005,8 +1005,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "non-existent", Min: 4000},
-						{Name: "two", Min: 4000},
+						{Name: "non-existent", Guaranteed: 4000},
+						{Name: "two", Guaranteed: 4000},
 					},
 				},
 			},
@@ -1054,8 +1054,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "one", Min: 4000},
-						{Name: "two", Min: 4000},
+						{Name: "one", Guaranteed: 4000},
+						{Name: "two", Guaranteed: 4000},
 					},
 				},
 				LabelKeys: map[corev1.ResourceName]sets.String{corev1.ResourceCPU: sets.NewString("cpuType")},
@@ -1105,12 +1105,12 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "one", Min: 4000},
-						{Name: "two", Min: 4000},
+						{Name: "one", Guaranteed: 4000},
+						{Name: "two", Guaranteed: 4000},
 					},
 					corev1.ResourceMemory: {
-						{Name: "one", Min: utiltesting.Gi},
-						{Name: "two", Min: utiltesting.Gi},
+						{Name: "one", Guaranteed: utiltesting.Gi},
+						{Name: "two", Guaranteed: utiltesting.Gi},
 					},
 				},
 			},
@@ -1169,8 +1169,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "one", Min: 4000},
-						{Name: "two", Min: 4000},
+						{Name: "one", Guaranteed: 4000},
+						{Name: "two", Guaranteed: 4000},
 					},
 				},
 			},
@@ -1215,8 +1215,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "one", Min: 4000},
-						{Name: "two", Min: 4000},
+						{Name: "one", Guaranteed: 4000},
+						{Name: "two", Guaranteed: 4000},
 					},
 				},
 				LabelKeys: map[corev1.ResourceName]sets.String{corev1.ResourceCPU: sets.NewString("cpuType")},
@@ -1243,8 +1243,8 @@ func TestEntryAssignFlavors(t *testing.T) {
 			clusterQueue: cache.ClusterQueue{
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
-						{Name: "one", Min: 4000},
-						{Name: "two", Min: 10_000},
+						{Name: "one", Guaranteed: 4000},
+						{Name: "two", Guaranteed: 10_000},
 					},
 				},
 			},
@@ -1281,16 +1281,16 @@ func TestEntryAssignFlavors(t *testing.T) {
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
 						{
-							Name: "default",
-							Min:  2000,
-							Max:  pointer.Int64(100_000),
+							Name:       "default",
+							Guaranteed: 2000,
+							Ceiling:    pointer.Int64(100_000),
 						},
 					},
 					corev1.ResourceMemory: {
 						{
-							Name: "default",
-							Min:  2 * utiltesting.Gi,
-							// No max.
+							Name:       "default",
+							Guaranteed: 2 * utiltesting.Gi,
+							// No ceiling.
 						},
 					},
 				},
@@ -1339,9 +1339,9 @@ func TestEntryAssignFlavors(t *testing.T) {
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
 						{
-							Name: "one",
-							Min:  1000,
-							// No max.
+							Name:       "one",
+							Guaranteed: 1000,
+							// No ceiling.
 						},
 					},
 				},
@@ -1355,7 +1355,7 @@ func TestEntryAssignFlavors(t *testing.T) {
 				},
 			},
 		},
-		"past max": {
+		"past ceiling": {
 			wlPods: []kueue.PodSet{
 				{
 					Count: 1,
@@ -1369,9 +1369,9 @@ func TestEntryAssignFlavors(t *testing.T) {
 				RequestableResources: map[corev1.ResourceName][]cache.FlavorLimits{
 					corev1.ResourceCPU: {
 						{
-							Name: "one",
-							Min:  1000,
-							Max:  pointer.Int64(10_000),
+							Name:       "one",
+							Guaranteed: 1000,
+							Ceiling:    pointer.Int64(10_000),
 						},
 					},
 				},
diff --git a/pkg/util/testing/wrappers.go b/pkg/util/testing/wrappers.go
index 39b567e4c..05e3f0e39 100644
--- a/pkg/util/testing/wrappers.go
+++ b/pkg/util/testing/wrappers.go
@@ -310,11 +310,11 @@ func (r *ResourceWrapper) Flavor(f *kueue.Flavor) *ResourceWrapper {
 type FlavorWrapper struct{ kueue.Flavor }
 
 // MakeFlavor creates a wrapper for a resource flavor.
-func MakeFlavor(rf, min string) *FlavorWrapper {
+func MakeFlavor(rf, guaranteed string) *FlavorWrapper {
 	return &FlavorWrapper{kueue.Flavor{
 		ResourceFlavor: kueue.ResourceFlavorReference(rf),
 		Quota: kueue.Quota{
-			Min: resource.MustParse(min),
+			Guaranteed: resource.MustParse(guaranteed),
 		},
 	}}
 }
@@ -324,9 +324,9 @@ func (f *FlavorWrapper) Obj() *kueue.Flavor {
 	return &f.Flavor
 }
 
-// Max updates the flavor max.
-func (f *FlavorWrapper) Max(c string) *FlavorWrapper {
-	f.Quota.Max = pointer.Quantity(resource.MustParse(c))
+// Ceiling updates the flavor ceiling.
+func (f *FlavorWrapper) Ceiling(c string) *FlavorWrapper {
+	f.Quota.Ceiling = pointer.Quantity(resource.MustParse(c))
 	return f
 }
 
diff --git a/test/integration/controller/core/clusterqueue_controller_test.go b/test/integration/controller/core/clusterqueue_controller_test.go
index 18f76ec55..d0710facf 100644
--- a/test/integration/controller/core/clusterqueue_controller_test.go
+++ b/test/integration/controller/core/clusterqueue_controller_test.go
@@ -74,11 +74,11 @@ var _ = ginkgo.Describe("ClusterQueue controller", func() {
 	ginkgo.BeforeEach(func() {
 		clusterQueue = testing.MakeClusterQueue("cluster-queue").
 			Resource(testing.MakeResource(corev1.ResourceCPU).
-				Flavor(testing.MakeFlavor(flavorOnDemand, "5").Max("10").Obj()).
-				Flavor(testing.MakeFlavor(flavorSpot, "5").Max("10").Obj()).Obj()).
+				Flavor(testing.MakeFlavor(flavorOnDemand, "5").Ceiling("10").Obj()).
+				Flavor(testing.MakeFlavor(flavorSpot, "5").Ceiling("10").Obj()).Obj()).
 			Resource(testing.MakeResource(resourceGPU).
-				Flavor(testing.MakeFlavor(flavorModelA, "5").Max("10").Obj()).
-				Flavor(testing.MakeFlavor(flavorModelB, "5").Max("10").Obj()).Obj()).Obj()
+				Flavor(testing.MakeFlavor(flavorModelA, "5").Ceiling("10").Obj()).
+				Flavor(testing.MakeFlavor(flavorModelB, "5").Ceiling("10").Obj()).Obj()).Obj()
 		gomega.Expect(k8sClient.Create(ctx, clusterQueue)).To(gomega.Succeed())
 		queue = testing.MakeQueue("queue", ns.Name).ClusterQueue(clusterQueue.Name).Obj()
 		gomega.Expect(k8sClient.Create(ctx, queue)).To(gomega.Succeed())
diff --git a/test/integration/controller/core/queue_controller_test.go b/test/integration/controller/core/queue_controller_test.go
index 5b21a228d..830dc5395 100644
--- a/test/integration/controller/core/queue_controller_test.go
+++ b/test/integration/controller/core/queue_controller_test.go
@@ -49,8 +49,8 @@ var _ = ginkgo.Describe("Queue controller", func() {
 	ginkgo.BeforeEach(func() {
 		clusterQueue = testing.MakeClusterQueue("clusterQueue_queue-controller").
 			Resource(testing.MakeResource(resourceGPU).
-				Flavor(testing.MakeFlavor(flavorModelA, "5").Max("10").Obj()).
-				Flavor(testing.MakeFlavor(flavorModelB, "5").Max("10").Obj()).Obj()).Obj()
+				Flavor(testing.MakeFlavor(flavorModelA, "5").Ceiling("10").Obj()).
+				Flavor(testing.MakeFlavor(flavorModelB, "5").Ceiling("10").Obj()).Obj()).Obj()
 		queue = testing.MakeQueue("queue", ns.Name).ClusterQueue(clusterQueue.Name).Obj()
 		gomega.Expect(k8sClient.Create(ctx, queue)).To(gomega.Succeed())
 	})
diff --git a/test/integration/framework/framework.go b/test/integration/framework/framework.go
index 8372caab2..54ee52686 100644
--- a/test/integration/framework/framework.go
+++ b/test/integration/framework/framework.go
@@ -164,7 +164,7 @@ func ExpectWorkloadsToBePending(ctx context.Context, k8sClient client.Client, wl
 			}
 		}
 		return pending
-	}, Timeout, Interval).Should(gomega.Equal(len(wls)), "Not enough workloads are pending")
+	}).Should(gomega.Equal(len(wls)), "Not enough workloads are pending")
 }
 
 func UpdateWorkloadStatus(ctx context.Context, k8sClient client.Client, wl *kueue.Workload, update func(*kueue.Workload)) {
diff --git a/test/integration/scheduler/scheduler_test.go b/test/integration/scheduler/scheduler_test.go
index 3029026a5..bf555af2b 100644
--- a/test/integration/scheduler/scheduler_test.go
+++ b/test/integration/scheduler/scheduler_test.go
@@ -86,7 +86,7 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		prodClusterQ = testing.MakeClusterQueue("prod-cq").
 			Cohort("prod").
 			Resource(testing.MakeResource(corev1.ResourceCPU).
-				Flavor(testing.MakeFlavor(spotTaintedFlavor.Name, "5").Max("5").Obj()).
+				Flavor(testing.MakeFlavor(spotTaintedFlavor.Name, "5").Ceiling("5").Obj()).
 				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Obj()).
 				Obj()).
 			Obj()
@@ -103,7 +103,7 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		prodBEClusterQ = testing.MakeClusterQueue("prod-be-cq").
 			Cohort("be").QueueingStrategy(kueue.BestEffortFIFO).
 			Resource(testing.MakeResource(corev1.ResourceCPU).
-				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Max("10").Obj()).
+				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Ceiling("10").Obj()).
 				Obj()).
 			Obj()
 		gomega.Expect(k8sClient.Create(ctx, prodBEClusterQ)).Should(gomega.Succeed())
@@ -111,7 +111,7 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		devBEClusterQ = testing.MakeClusterQueue("dev-be-cq").
 			Cohort("be").QueueingStrategy(kueue.BestEffortFIFO).
 			Resource(testing.MakeResource(corev1.ResourceCPU).
-				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Max("10").Obj()).
+				Flavor(testing.MakeFlavor(onDemandFlavor.Name, "5").Ceiling("10").Obj()).
 				Obj()).
 			Obj()
 		gomega.Expect(k8sClient.Create(ctx, devBEClusterQ)).Should(gomega.Succeed())
@@ -237,7 +237,7 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		fallbackClusterQueue := testing.MakeClusterQueue("fallback-cq").
 			Cohort(prodClusterQ.Spec.Cohort).
 			Resource(testing.MakeResource(corev1.ResourceCPU).
-				Flavor(testing.MakeFlavor(spotTaintedFlavor.Name, "5").Obj()). // prod-cq can't borrow this flavor due to its max quota.
+				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()
@@ -501,7 +501,7 @@ var _ = ginkgo.Describe("Scheduler", func() {
 		devCq := &kueue.ClusterQueue{}
 		gomega.Expect(k8sClient.Get(ctx, types.NamespacedName{Name: devBEClusterQ.Name}, devCq)).Should(gomega.Succeed())
 
-		updatedResource := testing.MakeResource(corev1.ResourceCPU).Flavor(testing.MakeFlavor(onDemandFlavor.Name, "13").Max("13").Obj()).Obj()
+		updatedResource := testing.MakeResource(corev1.ResourceCPU).Flavor(testing.MakeFlavor(onDemandFlavor.Name, "13").Ceiling("13").Obj()).Obj()
 		devCq.Spec.RequestableResources = []kueue.Resource{*updatedResource}
 		gomega.Expect(k8sClient.Update(ctx, devCq)).Should(gomega.Succeed())