dongjoon-hyun commented on code in PR #864:
URL:
https://github.com/apache/spark-kubernetes-operator/pull/864#discussion_r4073869969
##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/kueue/KueueWorkloadUtils.java:
##########
@@ -218,13 +229,96 @@ public static Optional<ReconcileProgress>
holdForAdmission(
requested);
return Optional.of(ReconcileProgress.completeAndDefaultRequeue());
}
+ Map<String, KueuePodSetFlavor> flavors;
+ try {
+ flavors = resolvePodSetFlavors(context.getClient(),
admission.workload());
+ checkNoNodeSelectorConflict(flavors, desired);
+ } catch (KubernetesClientException e) {
+ return Optional.of(retryAfterFlavorReadFailure(context, e,
workloadName));
+ } catch (IllegalArgumentException e) {
+ // Like Kueue, a node selector conflict is permanent, so the quota is
released before the
+ // caller fails the resource, which this step does not reach again. The
release itself is
+ // retried, since a Workload left behind holds the quota of a resource
that never starts.
+ try {
+ deleteWorkloadOf(context.getClient(), context.getResource());
+ } catch (KubernetesClientException releaseFailure) {
+ return Optional.of(
+ retryAfterRequestFailure(
+ context,
+ releaseFailure,
+ "Failed to release the Kueue Workload of a rejected
admission"));
+ }
+ throw e;
+ }
EventUtils.normal(
context.getEventRecorder(),
EventUtils.REASON_KUEUE_ADMITTED,
"Kueue admitted Workload " + workloadName + ", requesting " +
requested + ".");
+ context.setKueuePodSetFlavors(flavors);
+ return Optional.empty();
+ }
+
+ /**
+ * Sets the flavors of the Workload which Kueue admitted before on the
context of a resource
+ * whose driver or master exists already. Such a reconcile applies the
secondary resources again
+ * without requesting the admission, so rebuilding them without the flavors
would remove the node
+ * selector and the tolerations which Kueue assigned to the pods.
+ *
+ * @param context The context of the resource to be reconciled.
+ * @return The progress to return while the flavors cannot be read, or empty
to proceed.
+ */
+ public static Optional<ReconcileProgress> applyAdmittedFlavors(final
BaseContext<?> context) {
+ HasMetadata resource = context.getResource();
+ String workloadName = KueueWorkloadFactory.getWorkloadName(resource);
+ Workload workload;
+ try {
+ workload =
+ context
+ .getClient()
+ .resources(Workload.class)
+ .inNamespace(resource.getMetadata().getNamespace())
+ .withName(workloadName)
+ .get();
+ } catch (KubernetesClientException e) {
+ return Optional.of(retryAfterRequestFailure(context, e, "Failed to read
the Kueue Workload"));
+ }
+ if (workload == null || !isAdmitted(workload)) {
+ // A Workload which is gone or evicted must not hold the resources which
are already running.
+ log.debug("The Kueue Workload {} is not admitted, applying no flavors.",
workloadName);
+ return Optional.empty();
+ }
+ try {
+ context.setKueuePodSetFlavors(resolvePodSetFlavors(context.getClient(),
workload));
Review Comment:
Thank you for catching this, and for the two-arm probe. Fixed in the latest
commit.
`applyAdmittedFlavors` now runs the check against the stored `Workload` as
you suggested, so both paths agree and the `IllegalArgumentException` lands in
the same `catch (Exception)` of the init steps:
```java
Map<String, KueuePodSetFlavor> flavors =
resolvePodSetFlavors(context.getClient(), workload);
checkNoNodeSelectorConflict(flavors, workload);
context.setKueuePodSetFlavors(flavors);
```
One deliberate difference from the admission path: the quota is not released
here. There the resource never started, so holding the quota would be wrong.
Here the driver or the master is running already and its pods still occupy what
the `Workload` reserved, so deleting it would make Kueue under-account the
usage this change exists to keep honest. The comment on the call states that,
and the new test asserts the `Workload` survives.
Added
`KueueWorkloadUtilsTest.admittedFlavorsConflictingWithTheNodeSelectorFail` in
the shape you described: an admitted `Workload` whose `executor` pod set
template asks for `pool: on-demand` against a flavor carrying `pool: spot`,
asserting the exception and `verify(context,
never()).setKueuePodSetFlavors(any())`.
Also fixed the sentence you flagged in `docs/spark_custom_resources.md`:
`SchedulingFailure` now covers both paths, while the `Workload` deletion is
qualified with the running driver or master.
Agreed on the boundary you drew. A resource spec edited after the admission
is not covered, since that pass skips `requestAdmission` and never re-validates
the pod sets hash either. That is the wider gap and belongs in its own change.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]