This is an automated email from the ASF dual-hosted git repository.

rmetzger pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-kubernetes-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new 6eb5d9c09 [FLINK-37448] Report actual TaskManager count in 
`status.taskManager.replicas` (#1146)
6eb5d9c09 is described below

commit 6eb5d9c09d57806114d3e1d66438b5b5c2675c48
Author: Santwana Verma <[email protected]>
AuthorDate: Thu Oct 29 00:26:24 2026 +0530

    [FLINK-37448] Report actual TaskManager count in 
`status.taskManager.replicas` (#1146)
    
    * [FLINK-37448] Report actual TaskManager count in 
status.taskManager.replicas
    
    status.taskManager.replicas was computed from the spec
    (ceil(parallelism / slots)), which is wrong when the TaskManager count
    changes at runtime, e.g. in standalone reactive mode. The observer now
    sets it from the TaskManagers registered with the cluster, for
    application and session clusters.
    
    Generated-by: Claude Code (Claude Opus 4.8)
    
    * [FLINK-37448] Reuse the getClusterInfo REST call and keep the count on 
in-place changes
    
    - getClusterInfo returns the TaskManager count with the info map, so the
      observer makes no extra /taskmanagers call.
    - A RUNNING reconcile keeps the last observed count instead of resetting
      it to 0. This covers in-place changes such as scaling; a redeploy still
      reports 0 until the new cluster is observed.
    - Drop comments that restate the code.
    
    Generated-by: Claude Code (Claude Opus 4.8, Claude Opus 5.5)
    
    * [FLINK-37448][docs] Update taskManager row in the status and lifecycle 
page
    
    The taskManager status is now populated for session clusters too and
    reports the number of TaskManagers registered with the running cluster.
    
    Generated-by: Claude Code (Claude Opus 5.5)
---
 docs/content.zh/docs/custom-resource/reference.md  |  2 +-
 .../docs/custom-resource/status-and-lifecycle.md   |  2 +-
 docs/content/docs/custom-resource/reference.md     |  2 +-
 .../docs/custom-resource/status-and-lifecycle.md   |  2 +-
 .../operator/api/status/TaskManagerInfo.java       |  5 +++-
 .../AbstractFlinkDeploymentObserver.java           | 12 ++++++--
 .../operator/reconciler/ReconciliationUtils.java   | 20 +++++++------
 .../operator/service/AbstractFlinkService.java     |  5 ++--
 .../kubernetes/operator/service/ClusterInfo.java   | 29 +++++++++++++++++++
 .../kubernetes/operator/service/FlinkService.java  |  2 +-
 .../kubernetes/operator/utils/FlinkUtils.java      |  4 +++
 .../kubernetes/operator/TestingFlinkService.java   | 17 +++++++++--
 .../controller/FlinkDeploymentControllerTest.java  |  8 ++++--
 .../deployment/ApplicationObserverTest.java        | 33 ++++++++++++++++++++++
 .../observer/deployment/SessionObserverTest.java   | 24 ++++++++++++++++
 .../operator/service/AbstractFlinkServiceTest.java |  6 ++--
 .../operator/utils/ReconciliationUtilsTest.java    | 15 ++++++++++
 17 files changed, 161 insertions(+), 27 deletions(-)

diff --git a/docs/content.zh/docs/custom-resource/reference.md 
b/docs/content.zh/docs/custom-resource/reference.md
index 4348451ec..51bf7b3e1 100644
--- a/docs/content.zh/docs/custom-resource/reference.md
+++ b/docs/content.zh/docs/custom-resource/reference.md
@@ -554,4 +554,4 @@ This serves as a full reference for the FlinkDeployment, 
FlinkSessionJob, FlinkS
 | Parameter | Type | Docs |
 | ----------| ---- | ---- |
 | labelSelector | java.lang.String | TaskManager label selector. |
-| replicas | int | Number of TaskManager replicas if defined in the spec. |
+| replicas | int | Number of TaskManagers registered with the running Flink 
cluster, as of the operator's last successful observation. |
diff --git a/docs/content.zh/docs/custom-resource/status-and-lifecycle.md 
b/docs/content.zh/docs/custom-resource/status-and-lifecycle.md
index 8c3167c03..ba288d9c0 100644
--- a/docs/content.zh/docs/custom-resource/status-and-lifecycle.md
+++ b/docs/content.zh/docs/custom-resource/status-and-lifecycle.md
@@ -61,7 +61,7 @@ The status is the operator's write-back half of the contract: 
users declare the
 | `observedGeneration`         | Both              | The generation of the 
resource the operator last observed                                             
                                                                     |
 | `jobManagerDeploymentStatus` | `FlinkDeployment` | The observed state of the 
JobManager's Kubernetes Deployment, see [JobManager Deployment 
Status](#jobmanager-deployment-status)                                          
  |
 | `clusterInfo`                | `FlinkDeployment` | The Flink version and 
revision of the running cluster, its total CPU and memory, the last observed 
state size, and the operator's cluster health bookkeeping               |
-| `taskManager`                | `FlinkDeployment` | The TaskManager label 
selector and replica count backing the Kubernetes scale subresource, populated 
in application mode only                                              |
+| `taskManager`                | `FlinkDeployment` | The TaskManager label 
selector and the number of TaskManagers registered with the running cluster at 
the last observation, backing the Kubernetes scale subresource |
 | `conditions`                 | `FlinkDeployment` | Kubernetes-standard 
conditions derived from the status, see [Conditions](#conditions)               
                                                                       |
 
 ### Reconciliation Status
diff --git a/docs/content/docs/custom-resource/reference.md 
b/docs/content/docs/custom-resource/reference.md
index 4348451ec..51bf7b3e1 100644
--- a/docs/content/docs/custom-resource/reference.md
+++ b/docs/content/docs/custom-resource/reference.md
@@ -554,4 +554,4 @@ This serves as a full reference for the FlinkDeployment, 
FlinkSessionJob, FlinkS
 | Parameter | Type | Docs |
 | ----------| ---- | ---- |
 | labelSelector | java.lang.String | TaskManager label selector. |
-| replicas | int | Number of TaskManager replicas if defined in the spec. |
+| replicas | int | Number of TaskManagers registered with the running Flink 
cluster, as of the operator's last successful observation. |
diff --git a/docs/content/docs/custom-resource/status-and-lifecycle.md 
b/docs/content/docs/custom-resource/status-and-lifecycle.md
index 8c3167c03..ba288d9c0 100644
--- a/docs/content/docs/custom-resource/status-and-lifecycle.md
+++ b/docs/content/docs/custom-resource/status-and-lifecycle.md
@@ -61,7 +61,7 @@ The status is the operator's write-back half of the contract: 
users declare the
 | `observedGeneration`         | Both              | The generation of the 
resource the operator last observed                                             
                                                                     |
 | `jobManagerDeploymentStatus` | `FlinkDeployment` | The observed state of the 
JobManager's Kubernetes Deployment, see [JobManager Deployment 
Status](#jobmanager-deployment-status)                                          
  |
 | `clusterInfo`                | `FlinkDeployment` | The Flink version and 
revision of the running cluster, its total CPU and memory, the last observed 
state size, and the operator's cluster health bookkeeping               |
-| `taskManager`                | `FlinkDeployment` | The TaskManager label 
selector and replica count backing the Kubernetes scale subresource, populated 
in application mode only                                              |
+| `taskManager`                | `FlinkDeployment` | The TaskManager label 
selector and the number of TaskManagers registered with the running cluster at 
the last observation, backing the Kubernetes scale subresource |
 | `conditions`                 | `FlinkDeployment` | Kubernetes-standard 
conditions derived from the status, see [Conditions](#conditions)               
                                                                       |
 
 ### Reconciliation Status
diff --git 
a/flink-kubernetes-operator-api/src/main/java/org/apache/flink/kubernetes/operator/api/status/TaskManagerInfo.java
 
b/flink-kubernetes-operator-api/src/main/java/org/apache/flink/kubernetes/operator/api/status/TaskManagerInfo.java
index c5973af54..d1503b00b 100644
--- 
a/flink-kubernetes-operator-api/src/main/java/org/apache/flink/kubernetes/operator/api/status/TaskManagerInfo.java
+++ 
b/flink-kubernetes-operator-api/src/main/java/org/apache/flink/kubernetes/operator/api/status/TaskManagerInfo.java
@@ -38,6 +38,9 @@ public class TaskManagerInfo {
     /** TaskManager label selector. */
     @LabelSelector private String labelSelector;
 
-    /** Number of TaskManager replicas if defined in the spec. */
+    /**
+     * Number of TaskManagers registered with the running Flink cluster, as of 
the operator's last
+     * successful observation.
+     */
     @StatusReplicas private int replicas = 0;
 }
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/AbstractFlinkDeploymentObserver.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/AbstractFlinkDeploymentObserver.java
index 54e1573bf..91d63a477 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/AbstractFlinkDeploymentObserver.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/AbstractFlinkDeploymentObserver.java
@@ -24,6 +24,7 @@ import org.apache.flink.kubernetes.operator.api.spec.JobSpec;
 import org.apache.flink.kubernetes.operator.api.spec.JobState;
 import org.apache.flink.kubernetes.operator.api.status.FlinkDeploymentStatus;
 import 
org.apache.flink.kubernetes.operator.api.status.JobManagerDeploymentStatus;
+import org.apache.flink.kubernetes.operator.api.status.TaskManagerInfo;
 import org.apache.flink.kubernetes.operator.controller.FlinkResourceContext;
 import 
org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
 import 
org.apache.flink.kubernetes.operator.exception.MissingJobManagerException;
@@ -80,13 +81,20 @@ public abstract class AbstractFlinkDeploymentObserver
     private void observeClusterInfo(FlinkResourceContext<FlinkDeployment> ctx) 
{
         var flinkApp = ctx.getResource();
         try {
-            Map<String, String> clusterInfo =
+            var clusterInfo =
                     ctx.getFlinkService()
                             .getClusterInfo(
                                     ctx.getObserveConfig(),
                                     
flinkApp.getStatus().getJobStatus().getJobId());
-            flinkApp.getStatus().getClusterInfo().putAll(clusterInfo);
+            
flinkApp.getStatus().getClusterInfo().putAll(clusterInfo.getInfo());
             logger.debug("ClusterInfo: {}", 
flinkApp.getStatus().getClusterInfo());
+
+            var labelSelector =
+                    
FlinkUtils.getTaskManagerLabelSelector(flinkApp.getMetadata().getName());
+            flinkApp.getStatus()
+                    .setTaskManager(
+                            new TaskManagerInfo(
+                                    labelSelector, 
clusterInfo.getTaskManagerReplicas()));
         } catch (Exception e) {
             logger.warn("Exception while fetching cluster info", e);
         }
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/ReconciliationUtils.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/ReconciliationUtils.java
index 94140c3ff..282deb193 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/ReconciliationUtils.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/ReconciliationUtils.java
@@ -50,6 +50,8 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nullable;
+
 import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
@@ -169,12 +171,12 @@ public class ReconciliationUtils {
 
                 if (target instanceof FlinkDeployment) {
                     // For application deployments we update the taskmanager 
info
-                    ((FlinkDeploymentStatus) status)
-                            .setTaskManager(
-                                    getTaskManagerInfo(
-                                            target.getMetadata().getName(),
-                                            conf,
-                                            stateAfterReconcile));
+                    var deploymentStatus = (FlinkDeploymentStatus) status;
+                    deploymentStatus.setTaskManager(
+                            getTaskManagerInfo(
+                                    target.getMetadata().getName(),
+                                    stateAfterReconcile,
+                                    deploymentStatus.getTaskManager()));
                 }
                 
reconciliationStatus.serializeAndSetLastReconciledSpec(clonedSpec, target);
                 if (spec.getJob().getState() == JobState.SUSPENDED) {
@@ -240,10 +242,10 @@ public class ReconciliationUtils {
     }
 
     private static TaskManagerInfo getTaskManagerInfo(
-            String name, Configuration conf, JobState jobState) {
-        var labelSelector = "component=taskmanager,app=" + name;
+            String name, JobState jobState, @Nullable TaskManagerInfo current) 
{
         if (jobState == JobState.RUNNING) {
-            return new TaskManagerInfo(labelSelector, 
FlinkUtils.getNumTaskManagers(conf));
+            int replicas = current != null ? current.getReplicas() : 0;
+            return new 
TaskManagerInfo(FlinkUtils.getTaskManagerLabelSelector(name), replicas);
         } else {
             return new TaskManagerInfo("", 0);
         }
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkService.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkService.java
index 99753a7e2..693d7b985 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkService.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkService.java
@@ -803,8 +803,7 @@ public abstract class AbstractFlinkService implements 
FlinkService {
     }
 
     @Override
-    public Map<String, String> getClusterInfo(Configuration conf, @Nullable 
String jobId)
-            throws Exception {
+    public ClusterInfo getClusterInfo(Configuration conf, @Nullable String 
jobId) throws Exception {
         Map<String, String> clusterInfo = new HashMap<>();
 
         populateFlinkVersion(conf, clusterInfo);
@@ -819,7 +818,7 @@ public abstract class AbstractFlinkService implements 
FlinkService {
 
         populateStateSize(conf, jobId, clusterInfo);
 
-        return clusterInfo;
+        return new ClusterInfo(clusterInfo, taskManagerReplicas);
     }
 
     private void populateFlinkVersion(Configuration conf, Map<String, String> 
clusterInfo)
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/ClusterInfo.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/ClusterInfo.java
new file mode 100644
index 000000000..908e39606
--- /dev/null
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/ClusterInfo.java
@@ -0,0 +1,29 @@
+/*
+ * 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.flink.kubernetes.operator.service;
+
+import lombok.Value;
+
+import java.util.Map;
+
+/** Snapshot of the observed Flink cluster returned by {@link 
FlinkService#getClusterInfo}. */
+@Value
+public class ClusterInfo {
+    Map<String, String> info;
+    int taskManagerReplicas;
+}
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkService.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkService.java
index b1a078fbb..d6f7a966d 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkService.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkService.java
@@ -117,7 +117,7 @@ public interface FlinkService {
 
     void disposeSavepoint(String savepointPath, Configuration conf) throws 
Exception;
 
-    Map<String, String> getClusterInfo(Configuration conf, @Nullable String 
jobId) throws Exception;
+    ClusterInfo getClusterInfo(Configuration conf, @Nullable String jobId) 
throws Exception;
 
     PodList getJmPodList(FlinkDeployment deployment, Configuration conf);
 
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/FlinkUtils.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/FlinkUtils.java
index dbd68ba4e..b0a0f0259 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/FlinkUtils.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/FlinkUtils.java
@@ -351,6 +351,10 @@ public class FlinkUtils {
                 || haMode.equalsIgnoreCase("kubernetes");
     }
 
+    public static String getTaskManagerLabelSelector(String clusterId) {
+        return "component=taskmanager,app=" + clusterId;
+    }
+
     public static int getNumTaskManagers(Configuration conf) {
         int parallelism = conf.get(CoreOptions.DEFAULT_PARALLELISM);
         return getNumTaskManagers(conf, parallelism);
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkService.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkService.java
index b17fb624a..a8eaa9fae 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkService.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkService.java
@@ -49,8 +49,10 @@ import 
org.apache.flink.kubernetes.operator.observer.CheckpointStatsResult;
 import org.apache.flink.kubernetes.operator.observer.SavepointFetchResult;
 import org.apache.flink.kubernetes.operator.service.AbstractFlinkService;
 import org.apache.flink.kubernetes.operator.service.CheckpointHistoryWrapper;
+import org.apache.flink.kubernetes.operator.service.ClusterInfo;
 import org.apache.flink.kubernetes.operator.service.SuspendMode;
 import 
org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
+import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
 import org.apache.flink.runtime.client.DuplicateJobSubmissionException;
 import org.apache.flink.runtime.client.JobStatusMessage;
 import org.apache.flink.runtime.clusterframework.ApplicationStatus;
@@ -156,6 +158,9 @@ public class TestingFlinkService extends 
AbstractFlinkService {
     @Getter private int desiredReplicas = 0;
     @Getter private int cancelJobCallCount = 0;
 
+    @Setter private Integer taskManagerReplicas = null;
+    @Setter private boolean taskManagerReplicasError = false;
+
     @Getter private Configuration submittedConf;
 
     @Setter
@@ -717,12 +722,18 @@ public class TestingFlinkService extends 
AbstractFlinkService {
     }
 
     @Override
-    public Map<String, String> getClusterInfo(Configuration conf, String jobId)
-            throws TimeoutException {
+    public ClusterInfo getClusterInfo(Configuration conf, String jobId) throws 
TimeoutException {
         if (!isPortReady) {
             throw new TimeoutException("JM port is unavailable");
         }
-        return CLUSTER_INFO;
+        if (taskManagerReplicasError) {
+            throw new RuntimeException("Failed to fetch TaskManager count");
+        }
+        int replicas =
+                taskManagerReplicas != null
+                        ? taskManagerReplicas
+                        : FlinkUtils.getNumTaskManagers(conf);
+        return new ClusterInfo(CLUSTER_INFO, replicas);
     }
 
     @Override
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/FlinkDeploymentControllerTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/FlinkDeploymentControllerTest.java
index 7581fce6b..982919963 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/FlinkDeploymentControllerTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/FlinkDeploymentControllerTest.java
@@ -39,6 +39,7 @@ import 
org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
 import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
 import 
org.apache.flink.kubernetes.operator.reconciler.deployment.AbstractFlinkResourceReconciler;
 import org.apache.flink.kubernetes.operator.utils.EventRecorder;
+import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
 import org.apache.flink.kubernetes.operator.utils.IngressUtils;
 import org.apache.flink.runtime.client.JobStatusMessage;
 import org.apache.flink.util.SerializedThrowable;
@@ -213,13 +214,15 @@ public class FlinkDeploymentControllerTest {
         }
 
         appCluster.getSpec().getJob().setState(JobState.RUNNING);
+        flinkService.setTaskManagerReplicas(3);
         verifyReconcileNormalLifecycle(appCluster);
         var jobs = flinkService.listJobs();
         assertEquals(1, jobs.size());
         assertEquals("s0", jobs.get(0).f0);
         assertEquals(
                 new TaskManagerInfo(
-                        "component=taskmanager,app=" + 
appCluster.getMetadata().getName(), 1),
+                        
FlinkUtils.getTaskManagerLabelSelector(appCluster.getMetadata().getName()),
+                        3),
                 appCluster.getStatus().getTaskManager());
     }
 
@@ -378,7 +381,8 @@ public class FlinkDeploymentControllerTest {
         assertEquals("s0", jobs.get(0).f0);
         assertEquals(
                 new TaskManagerInfo(
-                        "component=taskmanager,app=" + 
appCluster.getMetadata().getName(), 1),
+                        
FlinkUtils.getTaskManagerLabelSelector(appCluster.getMetadata().getName()),
+                        0),
                 appCluster.getStatus().getTaskManager());
         assertEquals("s0", 
appCluster.getStatus().getJobStatus().getUpgradeSavepointPath());
 
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/ApplicationObserverTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/ApplicationObserverTest.java
index 31493b501..67265eaf7 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/ApplicationObserverTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/ApplicationObserverTest.java
@@ -31,6 +31,7 @@ import 
org.apache.flink.kubernetes.operator.api.status.JobStatus;
 import org.apache.flink.kubernetes.operator.api.status.ReconciliationState;
 import org.apache.flink.kubernetes.operator.api.status.SavepointFormatType;
 import org.apache.flink.kubernetes.operator.api.status.SnapshotTriggerType;
+import org.apache.flink.kubernetes.operator.api.status.TaskManagerInfo;
 import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
 import 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions;
 import 
org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
@@ -89,6 +90,38 @@ public class ApplicationObserverTest extends 
OperatorTestBase {
         deployment.getStatus().getJobStatus().setJobId(jobId);
     }
 
+    @Test
+    public void observeReportsActualTaskManagerReplicas() throws Exception {
+        Configuration conf =
+                configManager.getDeployConfig(deployment.getMetadata(), 
deployment.getSpec());
+
+        observer.observe(deployment, TestUtils.createEmptyContext());
+        flinkService.submitApplicationCluster(deployment.getSpec().getJob(), 
conf, false);
+        ReconciliationUtils.updateStatusForDeployedSpec(deployment, new 
Configuration());
+
+        flinkService.setTaskManagerReplicas(5);
+        observer.observe(deployment, readyContext);
+        observer.observe(deployment, readyContext);
+        assertEquals(
+                JobManagerDeploymentStatus.READY,
+                deployment.getStatus().getJobManagerDeploymentStatus());
+
+        var labelSelector =
+                
FlinkUtils.getTaskManagerLabelSelector(deployment.getMetadata().getName());
+        assertEquals(
+                new TaskManagerInfo(labelSelector, 5), 
deployment.getStatus().getTaskManager());
+
+        flinkService.setTaskManagerReplicas(3);
+        observer.observe(deployment, readyContext);
+        assertEquals(
+                new TaskManagerInfo(labelSelector, 3), 
deployment.getStatus().getTaskManager());
+
+        flinkService.setTaskManagerReplicasError(true);
+        observer.observe(deployment, readyContext);
+        assertEquals(
+                new TaskManagerInfo(labelSelector, 3), 
deployment.getStatus().getTaskManager());
+    }
+
     @Test
     public void observeApplicationCluster() throws Exception {
         Configuration conf =
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/SessionObserverTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/SessionObserverTest.java
index cfb1910cb..e5fbb8b4e 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/SessionObserverTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/deployment/SessionObserverTest.java
@@ -23,6 +23,7 @@ import org.apache.flink.kubernetes.operator.TestUtils;
 import org.apache.flink.kubernetes.operator.api.FlinkDeployment;
 import 
org.apache.flink.kubernetes.operator.api.status.JobManagerDeploymentStatus;
 import org.apache.flink.kubernetes.operator.api.status.ReconciliationState;
+import org.apache.flink.kubernetes.operator.api.status.TaskManagerInfo;
 import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
 import org.apache.flink.kubernetes.operator.observer.TestObserverAdapter;
 import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
@@ -101,6 +102,29 @@ public class SessionObserverTest extends OperatorTestBase {
                 deployment.getStatus().getJobManagerDeploymentStatus());
     }
 
+    @Test
+    public void observeReportsActualTaskManagerReplicas() {
+        FlinkDeployment deployment = TestUtils.buildSessionCluster();
+        ReconciliationUtils.updateStatusForDeployedSpec(deployment, new 
Configuration());
+
+        flinkService.setTaskManagerReplicas(5);
+        observer.observe(deployment, readyContext);
+        observer.observe(deployment, readyContext);
+        assertEquals(
+                JobManagerDeploymentStatus.READY,
+                deployment.getStatus().getJobManagerDeploymentStatus());
+
+        var labelSelector =
+                
FlinkUtils.getTaskManagerLabelSelector(deployment.getMetadata().getName());
+        assertEquals(
+                new TaskManagerInfo(labelSelector, 5), 
deployment.getStatus().getTaskManager());
+
+        flinkService.setTaskManagerReplicas(2);
+        observer.observe(deployment, readyContext);
+        assertEquals(
+                new TaskManagerInfo(labelSelector, 2), 
deployment.getStatus().getTaskManager());
+    }
+
     @Test
     public void observeAlreadyUpgraded() {
         var kubernetesDeployment = TestUtils.createDeployment(true);
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkServiceTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkServiceTest.java
index abac03963..baf275a78 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkServiceTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkServiceTest.java
@@ -1254,14 +1254,16 @@ public class AbstractFlinkServiceTest {
                         AbstractFlinkService.FIELD_NAME_TOTAL_MEMORY,
                         "" + MemorySize.ofMebiBytes(1000).getBytes() * 2);
 
-        assertEquals(expectedEntries, flinkService.getClusterInfo(conf, null));
+        var clusterInfo = flinkService.getClusterInfo(conf, null);
+        assertEquals(1, clusterInfo.getTaskManagerReplicas());
+        assertEquals(expectedEntries, clusterInfo.getInfo());
 
         assertEquals(
                 ImmutableMap.<String, String>builder()
                         .putAll(expectedEntries)
                         .put(AbstractFlinkService.FIELD_NAME_STATE_SIZE, 
"42424242")
                         .build(),
-                flinkService.getClusterInfo(conf, 
JobID.generate().toHexString()));
+                flinkService.getClusterInfo(conf, 
JobID.generate().toHexString()).getInfo());
     }
 
     @Test
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/ReconciliationUtilsTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/ReconciliationUtilsTest.java
index f5afefa16..6cbcce4cb 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/ReconciliationUtilsTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/ReconciliationUtilsTest.java
@@ -21,6 +21,7 @@ import org.apache.flink.configuration.Configuration;
 import org.apache.flink.kubernetes.operator.api.FlinkDeployment;
 import org.apache.flink.kubernetes.operator.api.spec.JobState;
 import org.apache.flink.kubernetes.operator.api.status.ReconciliationState;
+import org.apache.flink.kubernetes.operator.api.status.TaskManagerInfo;
 import org.apache.flink.kubernetes.operator.api.utils.BaseTestUtils;
 import org.apache.flink.kubernetes.operator.config.FlinkOperatorConfiguration;
 import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
@@ -64,6 +65,20 @@ public class ReconciliationUtilsTest {
         assertNotEquals(0, updateControl.getScheduleDelay().get());
     }
 
+    @Test
+    public void taskManagerReplicasPreservedOnRunningSpecUpdate() {
+        FlinkDeployment app = BaseTestUtils.buildApplicationCluster();
+        app.getSpec().getJob().setState(JobState.RUNNING);
+        
app.getStatus().getReconciliationStatus().setState(ReconciliationState.DEPLOYED);
+
+        var labelSelector = 
FlinkUtils.getTaskManagerLabelSelector(app.getMetadata().getName());
+        app.getStatus().setTaskManager(new TaskManagerInfo(labelSelector, 7));
+
+        ReconciliationUtils.updateStatusForDeployedSpec(app, new 
Configuration());
+
+        assertEquals(new TaskManagerInfo(labelSelector, 7), 
app.getStatus().getTaskManager());
+    }
+
     @Test
     public void testObservedGenerationStatus() throws Exception {
         FlinkDeployment app = BaseTestUtils.buildApplicationCluster();

Reply via email to