peter-toth commented on code in PR #840:
URL: 
https://github.com/apache/spark-kubernetes-operator/pull/840#discussion_r4036172817


##########
spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/SparkClusterReconcilerTest.java:
##########
@@ -274,6 +274,32 @@ private EventRecord captureRecordedEvent() {
     return captor.getValue();
   }
 
+  @Test
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  void podInformerMapsPodsBySparkClusterNameLabel() {
+    EventSourceContext<SparkCluster> eventSourceContext = 
mock(EventSourceContext.class);
+    List<InformerEventSourceConfiguration<?>> configs = new ArrayList<>();
+    try (MockedConstruction<InformerEventSource> ignored =
+        mockConstruction(
+            InformerEventSource.class,
+            (mock, ctx) ->
+                configs.add((InformerEventSourceConfiguration<?>) 
ctx.arguments().get(0)))) {
+      reconciler.prepareEventSources(eventSourceContext);
+      InformerEventSourceConfiguration<Pod> podConfig =
+          (InformerEventSourceConfiguration<Pod>) configs.get(0);
+      Pod pod = new Pod();
+      pod.setMetadata(
+          new ObjectMetaBuilder()
+              .withName("cluster-1-master-0")
+              .withNamespace("default")
+              .withLabels(Map.of(LABEL_SPARK_CLUSTER_NAME, "cluster-1"))
+              .build());
+      assertEquals(
+          Set.of(new ResourceID("cluster-1", "default")),
+          podConfig.getSecondaryToPrimaryMapper().toPrimaryResourceIDs(pod));

Review Comment:
   **Finding 2.** This pins the mapper but not the selector, and the selector 
is where finding 1 lives. `kueueWorkloadInformerIsRegisteredOnlyWhenEnabled` 
already asserts its own selector, so the pod source is the odd one out.
   
   Adding this next to the `podConfig` cast fails at this head with `expected: 
<spark.operator/spark-cluster-name> but was: 
<spark.operator/name=spark-kubernetes-operator>`, and passes once finding 1 is 
fixed:
   
   ```java
         assertEquals(LABEL_SPARK_CLUSTER_NAME, 
podConfig.getInformerConfig().getLabelSelector());
   ```
   
   A stronger version, and one that survives either fix for finding 1, builds 
the real pod-template labels through 
`SparkClusterResourceSpecFactory.buildResourceSpec` and asserts the selector is 
satisfied by them. That breaks if either side drifts, which is the invariant 
that actually matters here.
   



##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/reconciler/SparkClusterReconciler.java:
##########
@@ -155,7 +154,7 @@ public List<EventSource<?, SparkCluster>> 
prepareEventSources(
         new InformerEventSource<>(
             InformerEventSourceConfiguration.from(Pod.class, 
SparkCluster.class)
                 .withSecondaryToPrimaryMapper(
-                    
basicLabelSecondaryToPrimaryMapper(LABEL_SPARK_APPLICATION_NAME))
+                    
basicLabelSecondaryToPrimaryMapper(LABEL_SPARK_CLUSTER_NAME))
                 .withLabelSelector(commonResourceLabelsStr())

Review Comment:
   **Finding 1.** The mapper now reads the right label, but no cluster pod ever 
reaches it. This selector expands to 
`spark.operator/name=spark-kubernetes-operator`, and master and worker pods do 
not carry that label.
   
   `SparkClusterResourceSpecFactory` hands 
`sparkClusterResourceLabels(cluster)` to `OwnerResourceDecorator`, which writes 
them to the StatefulSet's *own* `metadata.labels`. The pod template is 
untouched. `SparkClusterResourceSpec` gives it only `spark-role`, 
`spark.operator/spark-cluster-name` and `spark-version` 
(`spark-submission-worker/src/main/java/org/apache/spark/k8s/operator/SparkClusterResourceSpec.java:233-237`
 and `:315-319`), and Kubernetes does not copy StatefulSet labels onto its 
pods. JOSDK passes the configured selector to 
`FilterWatchListDeletable.withLabelSelector` before creating the informer, so 
these pods are never listed or watched.
   
   The app side works only because it takes a different path: 
`SparkAppResourceSpecFactory.overrideDependencyConf` pushes every 
`sparkAppResourceLabels` entry through `spark.kubernetes.driver.label.*` and 
`spark.kubernetes.executor.label.*`, so driver and executor pods really do 
carry `spark.operator/name`.
   
   What I ran at `c740424`: built the real spec through 
`SparkClusterResourceSpecFactory.buildResourceSpec` and compared the master 
pod-template labels against the selector this method configures.
   
       ### selector          = spark.operator/name=spark-kubernetes-operator
       ### master pod labels = {spark-role=master, 
spark.operator/spark-cluster-name=cluster-1}
   
   The smallest fix, which I verified makes the selector match those labels:
   
   ```suggestion
                   .withLabelSelector(LABEL_SPARK_CLUSTER_NAME)
   ```
   
   The static import of `commonResourceLabelsStr` has to go with it. Left in 
place it is unused, and `spotlessCheck` fails on it.
   
   The alternative is to add `commonManagedResourceLabels()` to the two pod 
templates and keep this selector. That keeps both pod informers on one shared 
watch, since JOSDK's `InformerClassifier` keys the informer pool on the label 
selector and diverging selectors mean a second Pod watch. It also keeps the 
informer scoped to this operator instance. The cost is a change in 
`spark-submission-worker`. Existing `RunningHealthy` clusters would not roll, 
because `getReconcileSteps` re-applies the StatefulSets only from `Submitted`.
   



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