dongjoon-hyun commented on code in PR #925:
URL: 
https://github.com/apache/spark-kubernetes-operator/pull/925#discussion_r4203226528


##########
docs/migration_guide.md:
##########
@@ -159,6 +159,18 @@ affect building the project, CI, tests or examples.
   `driverStartTimeoutMillis` was disabled. Since `DriverStartTimedOut` is an 
infrastructure failure
   while the others are not, `restartPolicy: OnInfrastructureFailure` no longer 
restarts such an
   application. Use `OnFailure` to restart it.
+- Since 1.1.0, a label set on a secondary resource of the driver takes 
precedence over a label of
+  the same key on the driver pod, instead of being overwritten by it. This 
affects the user-defined
+  labels in `spec.driverServiceIngressList` (`serviceMetadata`, 
`ingressMetadata`),
+  `spec.driverHttpRouteList` (`serviceMetadata`, `httpRouteMetadata`), 
`spec.driverGrpcRouteList`
+  (`serviceMetadata`, `grpcRouteMetadata`) and `spec.configMapSpecs`, as well 
as the other
+  Kubernetes resources of the driver, e.g. its config map and on-demand PVCs. 
1.0 overwrote these
+  labels with the driver-pod labels of the same key, including the ones 
injected into the driver pod
+  by admission webhooks. The operator- and Spark-managed identity labels, i.e. 
any `spark.operator/`
+  key, `spark-role`, `spark-version`, `spark-app-selector` and 
`spark-app-name`, still take the
+  driver-pod value, so the secondary-to-primary mapping and the pod and 
service selectors keep
+  working. To restore the behavior before 1.1.0 for a given key, remove it 
from the secondary

Review Comment:
   This reason doesn't match the code. The decorator changes only 
`metadata.labels` of the secondary resources other than the driver pod, so 
these labels don't take part in the secondary-to-primary mapping or the 
selectors.
   
   - The label-based secondary-to-primary mapper is registered only for `Pod` 
in `SparkAppReconciler.prepareEventSources`, and the decorator doesn't decorate 
the driver pod.
   - The `spec.selector` of the Services is set before the decoration, in 
`DriverServiceIngressUtils`, `DriverGatewayRouteUtils` and Spark's 
`DriverServiceFeatureStep`, and the decorator doesn't change it.
   
   What depends on these labels of the secondary resources is Spark itself. 
`KubernetesClusterSchedulerBackend.stop()` deletes the Services and PVCs 
labeled `spark-app-selector=<app ID>` (with 
`spark.kubernetes.driver.service.deleteOnTermination` and 
`spark.kubernetes.driver.ownPersistentVolumeClaim`) and the config maps labeled 
`spark-app-selector=<app ID>,spark-role=executor`, and `ExecutorPodsAllocator` 
finds the reusable PVCs by `spark-app-selector`. Could you fix the reason? For 
example,
   
   > ... still take the driver-pod value, since they identify the resources of 
an application, e.g. Spark deletes its services, PVCs and executor config maps 
by `spark-app-selector` and `spark-role`.
   
   The same reason is in the Javadoc of 
`managedLabelsAreNotOverriddenByResource` and in the PR description ("would 
break reconciliation").



##########
spark-operator/src/test/java/org/apache/spark/k8s/operator/decorators/DriverResourceDecoratorTest.java:
##########
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.spark.k8s.operator.decorators;
+
+import static 
org.apache.spark.k8s.operator.Constants.LABEL_SPARK_APPLICATION_NAME;
+import static 
org.apache.spark.k8s.operator.Constants.LABEL_SPARK_OPERATOR_NAME;
+import static org.apache.spark.k8s.operator.Constants.LABEL_SPARK_ROLE_NAME;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Map;
+
+import io.fabric8.kubernetes.api.model.OwnerReference;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServiceBuilder;
+import org.junit.jupiter.api.Test;
+
+class DriverResourceDecoratorTest {
+
+  private static Pod driverPod() {
+    return new PodBuilder()
+        .withKind("Pod")
+        .withApiVersion("v1")
+        .withNewMetadata()
+        .withName("spark-driver")
+        .withUid("driver-uid")
+        .addToLabels(LABEL_SPARK_OPERATOR_NAME, "spark-operator")
+        .addToLabels(LABEL_SPARK_APPLICATION_NAME, "app1")
+        // Prefix-matched managed label that is not one of the exact-match 
keys.
+        .addToLabels("spark.operator/submission-id", "sub-1")
+        .addToLabels(LABEL_SPARK_ROLE_NAME, "driver")
+        .addToLabels("spark-app-selector", "spark-app-selector-1")
+        .addToLabels("overlapping-label", "driver-value")

Review Comment:
   These fixtures don't cover some of the claimed behaviors, so each of the 
following changes still passes all tests.
   
   - Removing `LABEL_SPARK_VERSION_NAME` or `LABEL_SPARK_APP_NAME` from 
`MANAGED_LABEL_KEYS`, or a typo in the new `LABEL_SPARK_APP_NAME`. The driver 
pod here has neither `spark-version` nor `spark-app-name`, although a real 
driver pod always has both. Only `spark-role` and `spark-app-selector` exercise 
the exact-match keys, since `spark.operator/name` and 
`spark.operator/spark-app-name` also match the prefix.
   - Propagating only the managed labels of the driver pod. The driver pod has 
no neutral label which the resource doesn't set, although the Javadoc of 
`driverLabelsPropagatedAndResourceLabelWinsOnCollision` claims the propagation.
   - Dropping a label which only the resource sets, since no decorated resource 
here has one.
   
   Could you add `spark-version` and `spark-app-name` to this driver pod and to 
the resource of `managedLabelsAreNotOverriddenByResource`, a neutral label only 
on the driver pod, e.g. `driver-only-label`, and a neutral label only on the 
resource?



##########
docs/migration_guide.md:
##########
@@ -159,6 +159,18 @@ affect building the project, CI, tests or examples.
   `driverStartTimeoutMillis` was disabled. Since `DriverStartTimedOut` is an 
infrastructure failure
   while the others are not, `restartPolicy: OnInfrastructureFailure` no longer 
restarts such an
   application. Use `OnFailure` to restart it.
+- Since 1.1.0, a label set on a secondary resource of the driver takes 
precedence over a label of
+  the same key on the driver pod, instead of being overwritten by it. This 
affects the user-defined
+  labels in `spec.driverServiceIngressList` (`serviceMetadata`, 
`ingressMetadata`),
+  `spec.driverHttpRouteList` (`serviceMetadata`, `httpRouteMetadata`), 
`spec.driverGrpcRouteList`
+  (`serviceMetadata`, `grpcRouteMetadata`) and `spec.configMapSpecs`, as well 
as the other
+  Kubernetes resources of the driver, e.g. its config map and on-demand PVCs. 
1.0 overwrote these

Review Comment:
   `its config map` is not affected. The driver's config map has no labels of 
its own, since `SparkAppResourceSpec` builds it with 
`KubernetesClientUtils.buildConfigMapJava(..., confFilesMap, Map.of())`, and 
neither do Spark's Hadoop, Kerberos and pod template config maps. So their 
labels are the same as in 1.0.
   
   On the other hand, the driver Service of Spark is affected via 
`spark.kubernetes.driver.service.label.*`. For example, with 
`spark.kubernetes.driver.label.team=a` and 
`spark.kubernetes.driver.service.label.team=b`, the driver Service has `team=a` 
in 1.0, but `team=b` with this PR. Could you mention it instead? For example,
   
   > ... as well as the other Kubernetes resources of the driver, e.g. its 
service with `spark.kubernetes.driver.service.label.*` and its on-demand PVCs 
with `spark.kubernetes.driver.volumes.persistentVolumeClaim.<name>.label.*`.



##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/decorators/DriverResourceDecorator.java:
##########
@@ -73,9 +106,38 @@ public <T extends HasMetadata> T decorate(T resource) {
           new ObjectMetaBuilder(resource.getMetadata())
               .addToOwnerReferences(buildOwnerReferenceTo(driverPod))
               .addToLabels(driverPod.getMetadata().getLabels())
+              
.addToLabels(overridableLabels(resource.getMetadata().getLabels()))

Review Comment:
   nit. Since this copies the resource labels, overwrites them with the driver 
pod labels and then re-applies the non-managed resource labels, the precedence 
is hard to follow. Adding a driver pod label only when it is managed or absent 
on the resource gives the same result in a single pass, without 
`overridableLabels` and `Collectors`. For example,
   
   ```java
         Map<String, String> resourceLabels = 
resource.getMetadata().getLabels();
         ObjectMetaBuilder builder =
             new ObjectMetaBuilder(resource.getMetadata())
                 .addToOwnerReferences(buildOwnerReferenceTo(driverPod));
         driverPod.getMetadata().getLabels().forEach((key, value) -> {
           if (isManagedLabel(key) || resourceLabels == null || 
!resourceLabels.containsKey(key)) {
             builder.addToLabels(key, value);
           }
         });
         resource.setMetadata(builder.build());
   ```



##########
docs/migration_guide.md:
##########
@@ -159,6 +159,18 @@ affect building the project, CI, tests or examples.
   `driverStartTimeoutMillis` was disabled. Since `DriverStartTimedOut` is an 
infrastructure failure
   while the others are not, `restartPolicy: OnInfrastructureFailure` no longer 
restarts such an
   application. Use `OnFailure` to restart it.
+- Since 1.1.0, a label set on a secondary resource of the driver takes 
precedence over a label of
+  the same key on the driver pod, instead of being overwritten by it. This 
affects the user-defined
+  labels in `spec.driverServiceIngressList` (`serviceMetadata`, 
`ingressMetadata`),
+  `spec.driverHttpRouteList` (`serviceMetadata`, `httpRouteMetadata`), 
`spec.driverGrpcRouteList`
+  (`serviceMetadata`, `grpcRouteMetadata`) and `spec.configMapSpecs`, as well 
as the other
+  Kubernetes resources of the driver, e.g. its config map and on-demand PVCs. 
1.0 overwrote these
+  labels with the driver-pod labels of the same key, including the ones 
injected into the driver pod
+  by admission webhooks. The operator- and Spark-managed identity labels, i.e. 
any `spark.operator/`
+  key, `spark-role`, `spark-version`, `spark-app-selector` and 
`spark-app-name`, still take the
+  driver-pod value, so the secondary-to-primary mapping and the pod and 
service selectors keep
+  working. To restore the behavior before 1.1.0 for a given key, remove it 
from the secondary
+  resource so it inherits the driver-pod label.

Review Comment:
   This item still doesn't mention the `{{APP_ID}}` case of my previous 
comment. Spark substitutes `{{APP_ID}}` only in the driver pod labels 
(`spark.kubernetes.driver.label.*`), not in 
`spark.kubernetes.driver.service.label.*` or in the PVC labels 
(`spark.kubernetes.driver.volumes.persistentVolumeClaim.<name>.label.*`). For 
example,
   
   ```yaml
   sparkConf:
     spark.kubernetes.driver.label.app-id: "{{APP_ID}}"
     spark.kubernetes.driver.service.label.app-id: "{{APP_ID}}"
   ```
   
   1.0 created the driver Service with `app-id=<app ID>`, because the 
substituted value of the driver pod overwrote the raw one. With this PR, the 
raw `{{APP_ID}}` wins, so the API server rejects the Service with `422` and the 
application ends in `SchedulingFailure` after its driver pod is created. Could 
you add it to this item? For example,
   
   > ... resource so it inherits the driver-pod label. Since Spark substitutes 
`{{APP_ID}}` only in the driver pod labels, a raw `{{APP_ID}}` in 
`spark.kubernetes.driver.service.label.*` or in the PVC labels is no longer 
replaced with the value of the driver pod, and the application fails with 
`SchedulingFailure`. Set such a label on the driver pod only, so that the 
resource inherits the substituted value.



##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/decorators/DriverResourceDecorator.java:
##########
@@ -45,10 +55,33 @@
 public class DriverResourceDecorator implements ResourceDecorator {
   private final Pod driverPod;
 
+  /**
+   * Operator- and Spark-managed identity labels whose driver-pod value must 
always propagate to
+   * secondary resources unchanged. A resource's own label of the same key
+   * must never override them. Any label key under the {@value 
#MANAGED_LABEL_PREFIX} prefix is
+   * protected the same way (which already covers the operator-namespaced keys 
listed here).
+   */
+  private static final Set<String> MANAGED_LABEL_KEYS =
+      Set.of(
+          LABEL_SPARK_OPERATOR_NAME,
+          LABEL_SPARK_APPLICATION_NAME,

Review Comment:
   nit. `LABEL_SPARK_OPERATOR_NAME` (`spark.operator/name`) and 
`LABEL_SPARK_APPLICATION_NAME` (`spark.operator/spark-app-name`) are covered by 
`MANAGED_LABEL_PREFIX` already, as the Javadoc above says, so they never change 
the result of `isManagedLabel`. Could you remove them, with their static 
imports and the parenthetical of the Javadoc, so that this set has only the 
four labels of Spark?



-- 
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]

Reply via email to