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

gyfora 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 5e3e9962 [FLINK-29313] Observe spec should include latest operator 
configs + improved test coverage
5e3e9962 is described below

commit 5e3e9962906c605ea55d4caf2fef2ee5ba400019
Author: Gyula Fora <[email protected]>
AuthorDate: Mon Sep 19 15:58:42 2022 +0200

    [FLINK-29313] Observe spec should include latest operator configs + 
improved test coverage
---
 .../kubernetes_operator_metric_configuration.html  | 36 ++++-----
 .../operator/config/FlinkConfigManager.java        | 19 ++++-
 .../config/KubernetesOperatorConfigOptions.java    | 88 ++++++++++++----------
 .../operator/crd/spec/AbstractFlinkSpec.java       |  5 +-
 .../operator/listener/ListenerUtils.java           |  4 +-
 .../metrics/KubernetesOperatorMetricOptions.java   | 25 +++---
 .../operator/metrics/OperatorMetricUtils.java      |  4 +-
 .../operator/observer/SavepointObserver.java       | 30 +++++---
 .../kubernetes/operator/TestingFlinkService.java   | 23 +++---
 .../operator/config/FlinkConfigManagerTest.java    | 11 +++
 .../controller/FlinkDeploymentControllerTest.java  |  7 +-
 .../operator/controller/RollbackTest.java          | 26 +++++++
 .../operator/observer/SavepointObserverTest.java   | 10 +--
 .../deployment/ApplicationObserverTest.java        | 30 +++++++-
 .../sessionjob/SessionJobObserverTest.java         |  3 +-
 .../deployment/ApplicationReconcilerTest.java      | 27 +++++--
 .../sessionjob/SessionJobReconcilerTest.java       |  9 ++-
 17 files changed, 236 insertions(+), 121 deletions(-)

diff --git 
a/docs/layouts/shortcodes/generated/kubernetes_operator_metric_configuration.html
 
b/docs/layouts/shortcodes/generated/kubernetes_operator_metric_configuration.html
index 57859b88..939339e7 100644
--- 
a/docs/layouts/shortcodes/generated/kubernetes_operator_metric_configuration.html
+++ 
b/docs/layouts/shortcodes/generated/kubernetes_operator_metric_configuration.html
@@ -32,6 +32,24 @@
             <td>Integer</td>
             <td>Defines the number of measured samples when calculating 
statistics.</td>
         </tr>
+        <tr>
+            
<td><h5>kubernetes.operator.metrics.scope.k8soperator.resource</h5></td>
+            <td style="word-wrap: 
break-word;">"&lt;host&gt;.k8soperator.&lt;namespace&gt;.&lt;name&gt;.resource.&lt;resourcens&gt;.&lt;resourcename&gt;.&lt;resourcetype&gt;"</td>
+            <td>String</td>
+            <td>Defines the scope format string that is applied to all metrics 
scoped to the kubernetes operator resource.</td>
+        </tr>
+        <tr>
+            
<td><h5>kubernetes.operator.metrics.scope.k8soperator.resourcens</h5></td>
+            <td style="word-wrap: 
break-word;">"&lt;host&gt;.k8soperator.&lt;namespace&gt;.&lt;name&gt;.namespace.&lt;resourcens&gt;.&lt;resourcetype&gt;"</td>
+            <td>String</td>
+            <td>Defines the scope format string that is applied to all metrics 
scoped to the kubernetes operator resource namespace.</td>
+        </tr>
+        <tr>
+            
<td><h5>kubernetes.operator.metrics.scope.k8soperator.system</h5></td>
+            <td style="word-wrap: 
break-word;">"&lt;host&gt;.k8soperator.&lt;namespace&gt;.&lt;name&gt;.system"</td>
+            <td>String</td>
+            <td>Defines the scope format string that is applied to all metrics 
scoped to the kubernetes operator.</td>
+        </tr>
         <tr>
             
<td><h5>kubernetes.operator.resource.lifecycle.metrics.enabled</h5></td>
             <td style="word-wrap: break-word;">true</td>
@@ -50,23 +68,5 @@
             <td>Boolean</td>
             <td>Enables metrics for FlinkDeployment and FlinkSessionJob custom 
resources.</td>
         </tr>
-        <tr>
-            <td><h5>metrics.scope.k8soperator.resource</h5></td>
-            <td style="word-wrap: 
break-word;">"&lt;host&gt;.k8soperator.&lt;namespace&gt;.&lt;name&gt;.resource.&lt;resourcens&gt;.&lt;resourcename&gt;.&lt;resourcetype&gt;"</td>
-            <td>String</td>
-            <td>Defines the scope format string that is applied to all metrics 
scoped to the kubernetes operator resource.</td>
-        </tr>
-        <tr>
-            <td><h5>metrics.scope.k8soperator.resourcens</h5></td>
-            <td style="word-wrap: 
break-word;">"&lt;host&gt;.k8soperator.&lt;namespace&gt;.&lt;name&gt;.namespace.&lt;resourcens&gt;.&lt;resourcetype&gt;"</td>
-            <td>String</td>
-            <td>Defines the scope format string that is applied to all metrics 
scoped to the kubernetes operator resource namespace.</td>
-        </tr>
-        <tr>
-            <td><h5>metrics.scope.k8soperator.system</h5></td>
-            <td style="word-wrap: 
break-word;">"&lt;host&gt;.k8soperator.&lt;namespace&gt;.&lt;name&gt;.system"</td>
-            <td>String</td>
-            <td>Defines the scope format string that is applied to all metrics 
scoped to the kubernetes operator.</td>
-        </tr>
     </tbody>
 </table>
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManager.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManager.java
index 0340891a..49181886 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManager.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManager.java
@@ -22,6 +22,7 @@ import org.apache.flink.annotation.VisibleForTesting;
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.configuration.GlobalConfiguration;
 import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
+import org.apache.flink.kubernetes.operator.crd.spec.AbstractFlinkSpec;
 import org.apache.flink.kubernetes.operator.crd.spec.FlinkDeploymentSpec;
 import org.apache.flink.kubernetes.operator.crd.spec.FlinkSessionJobSpec;
 import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
@@ -52,6 +53,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 
+import static 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions.K8S_OP_CONF_PREFIX;
 import static 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions.OPERATOR_DYNAMIC_CONFIG_CHECK_INTERVAL;
 import static 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions.OPERATOR_DYNAMIC_CONFIG_ENABLED;
 
@@ -157,7 +159,22 @@ public class FlinkConfigManager {
             throw new RuntimeException(
                     "Cannot create observe config before first deployment, 
this indicates a bug.");
         }
-        return getConfig(deployment.getMetadata(), deployedSpec);
+        var conf = getConfig(deployment.getMetadata(), deployedSpec);
+        addOperatorConfigsFromSpec(deployment.getSpec(), conf);
+        return conf;
+    }
+
+    private void addOperatorConfigsFromSpec(AbstractFlinkSpec spec, 
Configuration conf) {
+        // Observe config should include the latest operator related settings
+        if (spec.getFlinkConfiguration() != null) {
+            spec.getFlinkConfiguration()
+                    .forEach(
+                            (k, v) -> {
+                                if (k.startsWith(K8S_OP_CONF_PREFIX)) {
+                                    conf.setString(k, v);
+                                }
+                            });
+        }
     }
 
     public Configuration getSessionJobConfig(
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/KubernetesOperatorConfigOptions.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/KubernetesOperatorConfigOptions.java
index 5736c540..18664a1f 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/KubernetesOperatorConfigOptions.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/KubernetesOperatorConfigOptions.java
@@ -25,6 +25,7 @@ import org.apache.flink.core.execution.SavepointFormatType;
 
 import io.javaoperatorsdk.operator.api.config.ConfigurationService;
 import io.javaoperatorsdk.operator.api.reconciler.Constants;
+import org.jetbrains.annotations.NotNull;
 
 import java.time.Duration;
 import java.util.Map;
@@ -32,22 +33,33 @@ import java.util.Map;
 /** This class holds configuration constants used by flink operator. */
 public class KubernetesOperatorConfigOptions {
 
+    public static final String K8S_OP_CONF_PREFIX = "kubernetes.operator.";
+
     public static final String SECTION_SYSTEM = "system";
     public static final String SECTION_ADVANCED = "system_advanced";
     public static final String SECTION_DYNAMIC = "dynamic";
 
+    @NotNull
+    public static ConfigOptions.OptionBuilder operatorConfig(String key) {
+        return ConfigOptions.key(K8S_OP_CONF_PREFIX + key);
+    }
+
+    public static String operatorConfigKey(String key) {
+        return K8S_OP_CONF_PREFIX + key;
+    }
+
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Duration> OPERATOR_RECONCILE_INTERVAL =
-            ConfigOptions.key("kubernetes.operator.reconcile.interval")
+            operatorConfig("reconcile.interval")
                     .durationType()
                     .defaultValue(Duration.ofSeconds(60))
-                    
.withDeprecatedKeys("kubernetes.operator.reconciler.reschedule.interval")
+                    .withDeprecatedKeys(K8S_OP_CONF_PREFIX + 
"reconciler.reschedule.interval")
                     .withDescription(
                             "The interval for the controller to reschedule the 
reconcile process.");
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Duration> 
OPERATOR_OBSERVER_REST_READY_DELAY =
-            ConfigOptions.key("kubernetes.operator.observer.rest-ready.delay")
+            operatorConfig("observer.rest-ready.delay")
                     .durationType()
                     .defaultValue(Duration.ofSeconds(10))
                     .withDescription(
@@ -55,16 +67,16 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Integer> OPERATOR_RECONCILE_PARALLELISM =
-            ConfigOptions.key("kubernetes.operator.reconcile.parallelism")
+            operatorConfig("reconcile.parallelism")
                     .intType()
                     
.defaultValue(ConfigurationService.DEFAULT_RECONCILIATION_THREADS_NUMBER)
-                    
.withDeprecatedKeys("kubernetes.operator.reconciler.max.parallelism")
+                    
.withDeprecatedKeys(operatorConfigKey("reconciler.max.parallelism"))
                     .withDescription(
                             "The maximum number of threads running the 
reconciliation loop. Use -1 for infinite.");
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Duration> 
OPERATOR_OBSERVER_PROGRESS_CHECK_INTERVAL =
-            
ConfigOptions.key("kubernetes.operator.observer.progress-check.interval")
+            operatorConfig("observer.progress-check.interval")
                     .durationType()
                     .defaultValue(Duration.ofSeconds(10))
                     .withDescription(
@@ -72,52 +84,52 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Duration> 
OPERATOR_SAVEPOINT_TRIGGER_GRACE_PERIOD =
-            
ConfigOptions.key("kubernetes.operator.savepoint.trigger.grace-period")
+            operatorConfig("savepoint.trigger.grace-period")
                     .durationType()
                     .defaultValue(Duration.ofMinutes(1))
                     .withDeprecatedKeys(
-                            
"kubernetes.operator.observer.savepoint.trigger.grace-period")
+                            
operatorConfigKey("observer.savepoint.trigger.grace-period"))
                     .withDescription(
                             "The interval before a savepoint trigger attempt 
is marked as unsuccessful.");
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Duration> OPERATOR_FLINK_CLIENT_TIMEOUT =
-            ConfigOptions.key("kubernetes.operator.flink.client.timeout")
+            operatorConfig("flink.client.timeout")
                     .durationType()
                     .defaultValue(Duration.ofSeconds(10))
-                    
.withDeprecatedKeys("kubernetes.operator.observer.flink.client.timeout")
+                    
.withDeprecatedKeys(operatorConfigKey("observer.flink.client.timeout"))
                     .withDescription(
                             "The timeout for the observer to wait the flink 
rest client to return.");
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Duration> 
OPERATOR_FLINK_CLIENT_CANCEL_TIMEOUT =
-            
ConfigOptions.key("kubernetes.operator.flink.client.cancel.timeout")
+            operatorConfig("flink.client.cancel.timeout")
                     .durationType()
                     .defaultValue(Duration.ofMinutes(1))
-                    
.withDeprecatedKeys("kubernetes.operator.reconciler.flink.cancel.job.timeout")
+                    
.withDeprecatedKeys(operatorConfigKey("reconciler.flink.cancel.job.timeout"))
                     .withDescription(
                             "The timeout for the reconciler to wait for flink 
to cancel job.");
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Duration> 
OPERATOR_RESOURCE_CLEANUP_TIMEOUT =
-            ConfigOptions.key("kubernetes.operator.resource.cleanup.timeout")
+            operatorConfig("resource.cleanup.timeout")
                     .durationType()
                     .defaultValue(Duration.ofSeconds(60))
                     .withDeprecatedKeys(
-                            
"kubernetes.operator.reconciler.flink.cluster.shutdown.timeout")
+                            
operatorConfigKey("reconciler.flink.cluster.shutdown.timeout"))
                     .withDescription(
                             "The timeout for the resource clean up to wait for 
flink to shutdown cluster.");
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Boolean> DEPLOYMENT_ROLLBACK_ENABLED =
-            
ConfigOptions.key("kubernetes.operator.deployment.rollback.enabled")
+            operatorConfig("deployment.rollback.enabled")
                     .booleanType()
                     .defaultValue(false)
                     .withDescription("Whether to enable rolling back failed 
deployment upgrades.");
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Duration> DEPLOYMENT_READINESS_TIMEOUT =
-            
ConfigOptions.key("kubernetes.operator.deployment.readiness.timeout")
+            operatorConfig("deployment.readiness.timeout")
                     .durationType()
                     .defaultValue(Duration.ofMinutes(1))
                     .withDescription(
@@ -126,21 +138,21 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<String> OPERATOR_USER_ARTIFACTS_BASE_DIR =
-            ConfigOptions.key("kubernetes.operator.user.artifacts.base.dir")
+            operatorConfig("user.artifacts.base.dir")
                     .stringType()
                     .defaultValue("/opt/flink/artifacts")
                     .withDescription("The base dir to put the session job 
artifacts.");
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Boolean> 
JOB_UPGRADE_IGNORE_PENDING_SAVEPOINT =
-            
ConfigOptions.key("kubernetes.operator.job.upgrade.ignore-pending-savepoint")
+            operatorConfig("job.upgrade.ignore-pending-savepoint")
                     .booleanType()
                     .defaultValue(false)
                     .withDescription("Whether to ignore pending savepoint 
during job upgrade.");
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Boolean> OPERATOR_DYNAMIC_CONFIG_ENABLED =
-            ConfigOptions.key("kubernetes.operator.dynamic.config.enabled")
+            operatorConfig("dynamic.config.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription(
@@ -148,38 +160,38 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Duration> 
OPERATOR_DYNAMIC_CONFIG_CHECK_INTERVAL =
-            
ConfigOptions.key("kubernetes.operator.dynamic.config.check.interval")
+            operatorConfig("dynamic.config.check.interval")
                     .durationType()
                     .defaultValue(Duration.ofMinutes(5))
                     .withDescription("Time interval for checking config 
changes.");
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Duration> OPERATOR_CONFIG_CACHE_TIMEOUT =
-            ConfigOptions.key("kubernetes.operator.config.cache.timeout")
+            operatorConfig("config.cache.timeout")
                     .durationType()
                     .defaultValue(Duration.ofMinutes(10))
                     .withDescription("Expiration time for cached configs.");
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Integer> OPERATOR_CONFIG_CACHE_SIZE =
-            ConfigOptions.key("kubernetes.operator.config.cache.size")
+            operatorConfig("config.cache.size")
                     .intType()
                     .defaultValue(1000)
                     .withDescription("Max config cache size.");
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Boolean> 
OPERATOR_JM_DEPLOYMENT_RECOVERY_ENABLED =
-            
ConfigOptions.key("kubernetes.operator.jm-deployment-recovery.enabled")
+            operatorConfig("jm-deployment-recovery.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDeprecatedKeys(
-                            
"kubernetes.operator.reconciler.jm-deployment-recovery.enabled")
+                            
operatorConfigKey("reconciler.jm-deployment-recovery.enabled"))
                     .withDescription(
                             "Whether to enable recovery of missing/deleted 
jobmanager deployments.");
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Integer> 
OPERATOR_SAVEPOINT_HISTORY_MAX_COUNT =
-            
ConfigOptions.key("kubernetes.operator.savepoint.history.max.count")
+            operatorConfig("savepoint.history.max.count")
                     .intType()
                     .defaultValue(10)
                     .withDescription("Maximum number of savepoint history 
entries to retain.");
@@ -194,7 +206,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Duration> 
OPERATOR_SAVEPOINT_HISTORY_MAX_AGE =
-            ConfigOptions.key("kubernetes.operator.savepoint.history.max.age")
+            operatorConfig("savepoint.history.max.age")
                     .durationType()
                     .defaultValue(Duration.ofHours(24))
                     .withDescription(
@@ -210,7 +222,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Map<String, String>> 
JAR_ARTIFACT_HTTP_HEADER =
-            ConfigOptions.key("kubernetes.operator.user.artifacts.http.header")
+            operatorConfig("user.artifacts.http.header")
                     .mapType()
                     .noDefaultValue()
                     .withDescription(
@@ -219,7 +231,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Duration> PERIODIC_SAVEPOINT_INTERVAL =
-            
ConfigOptions.key("kubernetes.operator.periodic.savepoint.interval")
+            operatorConfig("periodic.savepoint.interval")
                     .durationType()
                     .defaultValue(Duration.ZERO)
                     .withDescription(
@@ -228,7 +240,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<String> OPERATOR_WATCHED_NAMESPACES =
-            ConfigOptions.key("kubernetes.operator.watched.namespaces")
+            operatorConfig("watched.namespaces")
                     .stringType()
                     .defaultValue(Constants.WATCH_ALL_NAMESPACES)
                     .withDescription(
@@ -236,7 +248,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<String> OPERATOR_LABEL_SELECTOR =
-            ConfigOptions.key("kubernetes.operator.label.selector")
+            operatorConfig("label.selector")
                     .stringType()
                     .noDefaultValue()
                     .withDescription(
@@ -245,14 +257,14 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Boolean> 
OPERATOR_DYNAMIC_NAMESPACES_ENABLED =
-            ConfigOptions.key("kubernetes.operator.dynamic.namespaces.enabled")
+            operatorConfig("dynamic.namespaces.enabled")
                     .booleanType()
                     .defaultValue(false)
                     .withDescription("Enables dynamic change of 
watched/monitored namespaces.");
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Duration> OPERATOR_RETRY_INITIAL_INTERVAL 
=
-            ConfigOptions.key("kubernetes.operator.retry.initial.interval")
+            operatorConfig("retry.initial.interval")
                     .durationType()
                     .defaultValue(Duration.ofSeconds(5))
                     .withDescription(
@@ -260,7 +272,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Double> 
OPERATOR_RETRY_INTERVAL_MULTIPLIER =
-            ConfigOptions.key("kubernetes.operator.retry.interval.multiplier")
+            operatorConfig("retry.interval.multiplier")
                     .doubleType()
                     .defaultValue(2.0)
                     .withDescription(
@@ -268,7 +280,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_SYSTEM)
     public static final ConfigOption<Integer> OPERATOR_RETRY_MAX_ATTEMPTS =
-            ConfigOptions.key("kubernetes.operator.retry.max.attempts")
+            operatorConfig("retry.max.attempts")
                     .intType()
                     .defaultValue(10)
                     .withDescription(
@@ -276,7 +288,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<Boolean> 
OPERATOR_JOB_UPGRADE_LAST_STATE_FALLBACK_ENABLED =
-            
ConfigOptions.key("kubernetes.operator.job.upgrade.last-state-fallback.enabled")
+            operatorConfig("job.upgrade.last-state-fallback.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription(
@@ -284,7 +296,7 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_DYNAMIC)
     public static final ConfigOption<SavepointFormatType> 
OPERATOR_SAVEPOINT_FORMAT_TYPE =
-            ConfigOptions.key("kubernetes.operator.savepoint.format.type")
+            operatorConfig("savepoint.format.type")
                     .enumType(SavepointFormatType.class)
                     .defaultValue(SavepointFormatType.DEFAULT)
                     .withDescription(
@@ -292,14 +304,14 @@ public class KubernetesOperatorConfigOptions {
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Boolean> OPERATOR_HEALTH_PROBE_ENABLED =
-            ConfigOptions.key("kubernetes.operator.health.probe.enabled")
+            operatorConfig("health.probe.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription("Enables health probe for the kubernetes 
operator.");
 
     @Documentation.Section(SECTION_ADVANCED)
     public static final ConfigOption<Integer> OPERATOR_HEALTH_PROBE_PORT =
-            ConfigOptions.key("kubernetes.operator.health.probe.port")
+            operatorConfig("health.probe.port")
                     .intType()
                     .defaultValue(8085)
                     .withDescription("The port the health probe will use to 
expose the status.");
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/AbstractFlinkSpec.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/AbstractFlinkSpec.java
index d7d1e2a5..b5e3a1fd 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/AbstractFlinkSpec.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/AbstractFlinkSpec.java
@@ -31,6 +31,8 @@ import lombok.experimental.SuperBuilder;
 
 import java.util.Map;
 
+import static 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions.K8S_OP_CONF_PREFIX;
+
 /** The common spec. */
 @Experimental
 @Data
@@ -51,8 +53,7 @@ public abstract class AbstractFlinkSpec implements 
Diffable<AbstractFlinkSpec> {
     /** Flink configuration overrides for the Flink deployment or Flink 
session job. */
     @SpecDiff.Config({
         @SpecDiff.Entry(prefix = "parallelism.default", type = 
DiffType.IGNORE),
-        @SpecDiff.Entry(prefix = "kubernetes.operator", type = 
DiffType.IGNORE),
-        @SpecDiff.Entry(prefix = "metrics.scope.k8soperator", type = 
DiffType.IGNORE)
+        @SpecDiff.Entry(prefix = K8S_OP_CONF_PREFIX, type = DiffType.IGNORE),
     })
     private Map<String, String> flinkConfiguration;
 
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/listener/ListenerUtils.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/listener/ListenerUtils.java
index 843cdf26..d99fd028 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/listener/ListenerUtils.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/listener/ListenerUtils.java
@@ -41,12 +41,14 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions.K8S_OP_CONF_PREFIX;
+
 /** Flink resource listener utilities. */
 public class ListenerUtils {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(ListenerUtils.class);
 
-    private static final String PREFIX = 
"kubernetes.operator.plugins.listeners.";
+    private static final String PREFIX = K8S_OP_CONF_PREFIX + 
"plugins.listeners.";
     private static final String SUFFIX = ".class";
     private static final Pattern PTN =
             Pattern.compile(Pattern.quote(PREFIX) + "([\\S&&[^.]]*)" + 
Pattern.quote(SUFFIX));
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/KubernetesOperatorMetricOptions.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/KubernetesOperatorMetricOptions.java
index 9da0b360..dfc876d4 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/KubernetesOperatorMetricOptions.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/KubernetesOperatorMetricOptions.java
@@ -18,61 +18,62 @@
 package org.apache.flink.kubernetes.operator.metrics;
 
 import org.apache.flink.configuration.ConfigOption;
-import org.apache.flink.configuration.ConfigOptions;
+
+import static 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions.operatorConfig;
 
 /** Configuration options for metrics. */
 public class KubernetesOperatorMetricOptions {
 
     public static final ConfigOption<Boolean> OPERATOR_JVM_METRICS_ENABLED =
-            ConfigOptions.key("kubernetes.operator.jvm.metrics.enabled")
+            operatorConfig("jvm.metrics.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription("Enable Kubernetes Operator JVM 
metrics.");
 
     public static final ConfigOption<Boolean> OPERATOR_JOSDK_METRICS_ENABLED =
-            ConfigOptions.key("kubernetes.operator.josdk.metrics.enabled")
+            operatorConfig("josdk.metrics.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription(
                             "Enable forwarding of Java Operator SDK metrics to 
the Flink metric registry.");
 
     public static final ConfigOption<Boolean> 
OPERATOR_KUBERNETES_CLIENT_METRICS_ENABLED =
-            
ConfigOptions.key("kubernetes.operator.kubernetes.client.metrics.enabled")
+            operatorConfig("kubernetes.client.metrics.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription(
                             "Enable KubernetesClient metrics for measuring the 
HTTP traffic to the Kubernetes API Server.");
 
     public static final ConfigOption<Boolean> 
OPERATOR_RESOURCE_METRICS_ENABLED =
-            ConfigOptions.key("kubernetes.operator.resource.metrics.enabled")
+            operatorConfig("resource.metrics.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription(
                             "Enables metrics for FlinkDeployment and 
FlinkSessionJob custom resources.");
 
     public static final ConfigOption<Boolean> 
OPERATOR_LIFECYCLE_METRICS_ENABLED =
-            
ConfigOptions.key("kubernetes.operator.resource.lifecycle.metrics.enabled")
+            operatorConfig("resource.lifecycle.metrics.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription(
                             "Enable resource lifecycle state metrics. This 
enables both state and transition counts/histograms.");
 
     public static final ConfigOption<Boolean> 
OPERATOR_LIFECYCLE_NAMESPACE_HISTOGRAMS_ENABLED =
-            
ConfigOptions.key("kubernetes.operator.resource.lifecycle.namespace.histograms.enabled")
+            operatorConfig("resource.lifecycle.namespace.histograms.enabled")
                     .booleanType()
                     .defaultValue(true)
                     .withDescription(
                             "In addition to the system level histograms, 
enable per namespace tracking of state and transition times.");
 
     public static final ConfigOption<Integer> 
OPERATOR_METRICS_HISTOGRAM_SAMPLE_SIZE =
-            
ConfigOptions.key("kubernetes.operator.metrics.histogram.sample.size")
+            operatorConfig("metrics.histogram.sample.size")
                     .intType()
                     .defaultValue(1000)
                     .withDescription(
                             "Defines the number of measured samples when 
calculating statistics.");
 
     public static final ConfigOption<String> SCOPE_NAMING_KUBERNETES_OPERATOR =
-            ConfigOptions.key("metrics.scope.k8soperator.system")
+            operatorConfig("metrics.scope.k8soperator.system")
                     .stringType()
                     
.defaultValue("<host>.k8soperator.<namespace>.<name>.system")
                     .withDeprecatedKeys("metrics.scope.k8soperator")
@@ -80,18 +81,20 @@ public class KubernetesOperatorMetricOptions {
                             "Defines the scope format string that is applied 
to all metrics scoped to the kubernetes operator.");
 
     public static final ConfigOption<String> 
SCOPE_NAMING_KUBERNETES_OPERATOR_RESOURCENS =
-            ConfigOptions.key("metrics.scope.k8soperator.resourcens")
+            operatorConfig("metrics.scope.k8soperator.resourcens")
                     .stringType()
                     .defaultValue(
                             
"<host>.k8soperator.<namespace>.<name>.namespace.<resourcens>.<resourcetype>")
+                    .withDeprecatedKeys("metrics.scope.k8soperator.resourcens")
                     .withDescription(
                             "Defines the scope format string that is applied 
to all metrics scoped to the kubernetes operator resource namespace.");
 
     public static final ConfigOption<String> 
SCOPE_NAMING_KUBERNETES_OPERATOR_RESOURCE =
-            ConfigOptions.key("metrics.scope.k8soperator.resource")
+            operatorConfig("metrics.scope.k8soperator.resource")
                     .stringType()
                     .defaultValue(
                             
"<host>.k8soperator.<namespace>.<name>.resource.<resourcens>.<resourcename>.<resourcetype>")
+                    .withDeprecatedKeys("metrics.scope.k8soperator.resource")
                     .withDescription(
                             "Defines the scope format string that is applied 
to all metrics scoped to the kubernetes operator resource.");
 }
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/OperatorMetricUtils.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/OperatorMetricUtils.java
index c5f6be75..155817a8 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/OperatorMetricUtils.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/OperatorMetricUtils.java
@@ -43,12 +43,14 @@ import org.slf4j.LoggerFactory;
 import java.util.HashMap;
 import java.util.Map;
 
+import static 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions.K8S_OP_CONF_PREFIX;
+
 /** Utility class for flink based operator metrics. */
 public class OperatorMetricUtils {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(OperatorMetricUtils.class);
 
-    private static final String OPERATOR_METRICS_PREFIX = 
"kubernetes.operator.metrics.";
+    private static final String OPERATOR_METRICS_PREFIX = K8S_OP_CONF_PREFIX + 
"metrics.";
     private static final String METRICS_PREFIX = "metrics.";
 
     public static KubernetesOperatorMetricGroup 
initOperatorMetrics(Configuration defaultConfig) {
diff --git 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/SavepointObserver.java
 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/SavepointObserver.java
index 091badfc..aa2d69de 100644
--- 
a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/SavepointObserver.java
+++ 
b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/SavepointObserver.java
@@ -74,6 +74,8 @@ public class SavepointObserver<
         if (ReconciliationUtils.isJobInTerminalState(resource.getStatus())) {
             observeLatestSavepoint(savepointInfo, jobId, deployedConfig);
         }
+
+        cleanupSavepointHistory(savepointInfo, deployedConfig);
     }
 
     /**
@@ -129,25 +131,29 @@ public class SavepointObserver<
 
         
ReconciliationUtils.updateLastReconciledSavepointTriggerNonce(savepointInfo, 
resource);
         savepointInfo.updateLastSavepoint(savepoint);
-        cleanupSavepointHistory(savepointInfo, savepoint, deployedConfig);
     }
 
     /** Clean up and dispose savepoints according to the configured max 
size/age. */
     @VisibleForTesting
-    void cleanupSavepointHistory(
-            SavepointInfo currentSavepointInfo,
-            Savepoint newSavepoint,
-            Configuration deployedConfig) {
+    void cleanupSavepointHistory(SavepointInfo currentSavepointInfo, 
Configuration deployedConfig) {
 
         // maintain history
         List<Savepoint> savepointHistory = 
currentSavepointInfo.getSavepointHistory();
+        if (savepointHistory.size() < 2) {
+            return;
+        }
+        var lastSavepoint = savepointHistory.get(savepointHistory.size() - 1);
+
         int maxCount =
-                ConfigOptionUtils.getValueWithThreshold(
-                        deployedConfig,
-                        
KubernetesOperatorConfigOptions.OPERATOR_SAVEPOINT_HISTORY_MAX_COUNT,
-                        configManager
-                                .getOperatorConfiguration()
-                                .getSavepointHistoryCountThreshold());
+                Math.max(
+                        1,
+                        ConfigOptionUtils.getValueWithThreshold(
+                                deployedConfig,
+                                KubernetesOperatorConfigOptions
+                                        .OPERATOR_SAVEPOINT_HISTORY_MAX_COUNT,
+                                configManager
+                                        .getOperatorConfiguration()
+                                        .getSavepointHistoryCountThreshold()));
         while (savepointHistory.size() > maxCount) {
             // remove oldest entries
             disposeSavepointQuietly(savepointHistory.remove(0), 
deployedConfig);
@@ -162,7 +168,7 @@ public class SavepointObserver<
         Iterator<Savepoint> it = savepointHistory.iterator();
         while (it.hasNext()) {
             Savepoint sp = it.next();
-            if (sp.getTimeStamp() < maxTms && sp != newSavepoint) {
+            if (sp.getTimeStamp() < maxTms && sp != lastSavepoint) {
                 it.remove();
                 disposeSavepointQuietly(sp, deployedConfig);
             }
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 f25118e1..c030db69 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
@@ -19,7 +19,7 @@ package org.apache.flink.kubernetes.operator;
 
 import org.apache.flink.api.common.JobID;
 import org.apache.flink.api.common.JobStatus;
-import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.api.java.tuple.Tuple3;
 import org.apache.flink.client.program.ClusterClient;
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.configuration.DeploymentOptions;
@@ -89,7 +89,7 @@ public class TestingFlinkService extends AbstractFlinkService 
{
     private int savepointCounter = 0;
     private int triggerCounter = 0;
 
-    private final List<Tuple2<String, JobStatusMessage>> jobs = new 
ArrayList<>();
+    private final List<Tuple3<String, JobStatusMessage, Configuration>> jobs = 
new ArrayList<>();
     private final Map<JobID, String> jobErrors = new HashMap<>();
     private final Set<String> sessions = new HashSet<>();
     private boolean isPortReady = true;
@@ -98,8 +98,8 @@ public class TestingFlinkService extends AbstractFlinkService 
{
     private Runnable sessionJobSubmittedCallback;
     private PodList podList = new PodList();
     private Consumer<Configuration> listJobConsumer = conf -> {};
-    private List<String> disposedSavepoints = new ArrayList<>();
-    private Map<String, Boolean> savepointTriggers = new HashMap<>();
+    private final List<String> disposedSavepoints = new ArrayList<>();
+    private final Map<String, Boolean> savepointTriggers = new HashMap<>();
     private int desiredReplicas = 0;
 
     public TestingFlinkService() {
@@ -157,7 +157,8 @@ public class TestingFlinkService extends 
AbstractFlinkService {
                         JobStatus.RUNNING,
                         System.currentTimeMillis());
 
-        jobs.add(Tuple2.of(conf.get(SavepointConfigOptions.SAVEPOINT_PATH), 
jobStatusMessage));
+        jobs.add(
+                Tuple3.of(conf.get(SavepointConfigOptions.SAVEPOINT_PATH), 
jobStatusMessage, conf));
     }
 
     protected void validateHaMetadataExists(Configuration conf) {
@@ -214,7 +215,7 @@ public class TestingFlinkService extends 
AbstractFlinkService {
                         JobStatus.RUNNING,
                         System.currentTimeMillis());
 
-        jobs.add(Tuple2.of(savepoint, jobStatusMessage));
+        jobs.add(Tuple3.of(savepoint, jobStatusMessage, conf));
         if (sessionJobSubmittedCallback != null) {
             sessionJobSubmittedCallback.run();
         }
@@ -233,7 +234,7 @@ public class TestingFlinkService extends 
AbstractFlinkService {
         this.listJobConsumer = listJobConsumer;
     }
 
-    public List<Tuple2<String, JobStatusMessage>> listJobs() {
+    public List<Tuple3<String, JobStatusMessage, Configuration>> listJobs() {
         return jobs;
     }
 
@@ -354,8 +355,7 @@ public class TestingFlinkService extends 
AbstractFlinkService {
 
     private String cancelJob(FlinkVersion flinkVersion, JobID jobID, boolean 
savepoint)
             throws Exception {
-        Optional<Tuple2<String, JobStatusMessage>> jobOpt =
-                jobs.stream().filter(js -> 
js.f1.getJobId().equals(jobID)).findAny();
+        var jobOpt = jobs.stream().filter(js -> 
js.f1.getJobId().equals(jobID)).findAny();
 
         if (jobOpt.isEmpty()) {
             throw new Exception("Job not found");
@@ -402,14 +402,13 @@ public class TestingFlinkService extends 
AbstractFlinkService {
 
     @Override
     public Optional<Savepoint> getLastCheckpoint(JobID jobId, Configuration 
conf) throws Exception {
-        Optional<Tuple2<String, JobStatusMessage>> jobOpt =
-                jobs.stream().filter(js -> 
js.f1.getJobId().equals(jobId)).findAny();
+        var jobOpt = jobs.stream().filter(js -> 
js.f1.getJobId().equals(jobId)).findAny();
 
         if (jobOpt.isEmpty()) {
             throw new Exception("Job not found");
         }
 
-        Tuple2<String, JobStatusMessage> t = jobOpt.get();
+        var t = jobOpt.get();
         if (!t.f1.getJobState().isGloballyTerminalState()) {
             throw new Exception("Checkpoint should not be queried if job is 
not in terminal state");
         }
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManagerTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManagerTest.java
index abeee8b3..bdd101b0 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManagerTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigManagerTest.java
@@ -54,6 +54,8 @@ public class FlinkConfigManagerTest {
     @Test
     public void testConfigGeneration() {
         ConfigOption<String> testConf = 
ConfigOptions.key("test").stringType().noDefaultValue();
+        ConfigOption<String> opTestConf =
+                
ConfigOptions.key("kubernetes.operator.test").stringType().noDefaultValue();
 
         FlinkConfigManager configManager =
                 new FlinkConfigManager(
@@ -67,16 +69,25 @@ public class FlinkConfigManagerTest {
         var reconciliationStatus = 
deployment.getStatus().getReconciliationStatus();
 
         deployment.getSpec().getFlinkConfiguration().put(testConf.key(), 
"reconciled");
+        deployment.getSpec().getFlinkConfiguration().put(opTestConf.key(), 
"reconciled");
         
reconciliationStatus.serializeAndSetLastReconciledSpec(deployment.getSpec(), 
deployment);
         reconciliationStatus.markReconciledSpecAsStable();
 
         deployment.getSpec().getFlinkConfiguration().put(testConf.key(), 
"latest");
+        deployment.getSpec().getFlinkConfiguration().put(opTestConf.key(), 
"latest");
+
         assertEquals(
                 "latest",
                 configManager
                         .getDeployConfig(deployment.getMetadata(), 
deployment.getSpec())
                         .get(testConf));
+        assertEquals(
+                "latest",
+                configManager
+                        .getDeployConfig(deployment.getMetadata(), 
deployment.getSpec())
+                        .get(opTestConf));
         assertEquals("reconciled", 
configManager.getObserveConfig(deployment).get(testConf));
+        assertEquals("latest", 
configManager.getObserveConfig(deployment).get(opTestConf));
 
         deployment.getSpec().getFlinkConfiguration().put(testConf.key(), 
"stable");
         
reconciliationStatus.serializeAndSetLastReconciledSpec(deployment.getSpec(), 
deployment);
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 e0fafc47..f5bd8d4c 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
@@ -17,7 +17,6 @@
 
 package org.apache.flink.kubernetes.operator.controller;
 
-import org.apache.flink.api.java.tuple.Tuple2;
 import org.apache.flink.configuration.CheckpointingOptions;
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.configuration.RestOptions;
@@ -381,7 +380,7 @@ public class FlinkDeploymentControllerTest {
                         CheckpointingOptions.SAVEPOINT_DIRECTORY.key(),
                         "file:///flink-data/savepoints");
         testController.reconcile(appCluster, context);
-        List<Tuple2<String, JobStatusMessage>> jobs = flinkService.listJobs();
+        var jobs = flinkService.listJobs();
         assertEquals(1, jobs.size());
         assertEquals("s0", jobs.get(0).f0);
         assertEquals(
@@ -389,7 +388,7 @@ public class FlinkDeploymentControllerTest {
                         "component=taskmanager,app=" + 
appCluster.getMetadata().getName(), 1),
                 appCluster.getStatus().getTaskManager());
 
-        List<Tuple2<String, JobStatusMessage>> previousJobs = new 
ArrayList<>(jobs);
+        var previousJobs = new ArrayList<>(jobs);
         appCluster.getSpec().getJob().setInitialSavepointPath("s1");
 
         // Send in a no-op change
@@ -470,7 +469,7 @@ public class FlinkDeploymentControllerTest {
         appCluster.getSpec().getJob().setInitialSavepointPath("s0");
 
         testController.reconcile(appCluster, context);
-        List<Tuple2<String, JobStatusMessage>> jobs = flinkService.listJobs();
+        var jobs = flinkService.listJobs();
         assertEquals(1, jobs.size());
         assertEquals("s0", jobs.get(0).f0);
         assertEquals(1, testController.events().size());
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/RollbackTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/RollbackTest.java
index a6a57b4b..0e34d05a 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/RollbackTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/RollbackTest.java
@@ -156,11 +156,13 @@ public class RollbackTest {
     public void testRollbackFailureWithLastState(FlinkVersion flinkVersion) 
throws Exception {
         var dep = TestUtils.buildApplicationCluster(flinkVersion);
         dep.getSpec().getJob().setUpgradeMode(UpgradeMode.LAST_STATE);
+        dep.getSpec().getFlinkConfiguration().put("t", "1");
 
         testRollback(
                 dep,
                 () -> {
                     dep.getSpec().getJob().setParallelism(9999);
+                    dep.getSpec().getFlinkConfiguration().remove("t");
                     testController.reconcile(dep, context);
                     assertEquals(
                             JobState.SUSPENDED,
@@ -179,6 +181,17 @@ public class RollbackTest {
                     assertEquals("RUNNING", 
dep.getStatus().getJobStatus().getState());
                     assertEquals(1, flinkService.listJobs().size());
 
+                    // Trigger deployment recovery
+                    flinkService.clear();
+                    flinkService.setPortReady(false);
+
+                    testController.reconcile(dep, context);
+                    flinkService.setPortReady(true);
+                    testController.reconcile(dep, context);
+                    var jobs = flinkService.listJobs();
+                    // Make sure deployment was recovered with correct 
spec/config
+                    assertTrue(jobs.get(jobs.size() - 1).f2.containsKey("t"));
+
                     // Remove job to simulate rollback failure
                     flinkService.clear();
                     flinkService.setPortReady(false);
@@ -199,6 +212,12 @@ public class RollbackTest {
         testRollback(
                 dep,
                 () -> {
+                    dep.getSpec()
+                            .getFlinkConfiguration()
+                            .put(
+                                    
KubernetesOperatorConfigOptions.DEPLOYMENT_ROLLBACK_ENABLED
+                                            .key(),
+                                    "false");
                     dep.getSpec().getJob().setParallelism(9999);
                     testController.reconcile(dep, context);
                     assertEquals(
@@ -209,6 +228,13 @@ public class RollbackTest {
                                     .getJob()
                                     .getState());
                     testController.reconcile(dep, context);
+                    // Validate that rollback config is picked up from latest 
deploy conf
+                    dep.getSpec()
+                            .getFlinkConfiguration()
+                            .put(
+                                    
KubernetesOperatorConfigOptions.DEPLOYMENT_ROLLBACK_ENABLED
+                                            .key(),
+                                    "true");
 
                     // Trigger rollback by delaying the recovery
                     Thread.sleep(200);
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/SavepointObserverTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/SavepointObserverTest.java
index 6a94fe3c..af42289f 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/SavepointObserverTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/SavepointObserverTest.java
@@ -65,7 +65,7 @@ public class SavepointObserverTest {
 
         Savepoint sp = new Savepoint(1, "sp1", SavepointTriggerType.MANUAL);
         spInfo.updateLastSavepoint(sp);
-        observer.cleanupSavepointHistory(spInfo, sp, 
configManager.getDefaultConfig());
+        observer.cleanupSavepointHistory(spInfo, 
configManager.getDefaultConfig());
 
         Assertions.assertNotNull(spInfo.getSavepointHistory());
         Assertions.assertIterableEquals(
@@ -83,7 +83,7 @@ public class SavepointObserverTest {
 
         Savepoint sp1 = new Savepoint(1, "sp1", SavepointTriggerType.MANUAL);
         spInfo.updateLastSavepoint(sp1);
-        observer.cleanupSavepointHistory(spInfo, sp1, conf);
+        observer.cleanupSavepointHistory(spInfo, conf);
         Assertions.assertIterableEquals(
                 Collections.singletonList(sp1), spInfo.getSavepointHistory());
         Assertions.assertIterableEquals(
@@ -91,7 +91,7 @@ public class SavepointObserverTest {
 
         Savepoint sp2 = new Savepoint(2, "sp2", SavepointTriggerType.MANUAL);
         spInfo.updateLastSavepoint(sp2);
-        observer.cleanupSavepointHistory(spInfo, sp2, conf);
+        observer.cleanupSavepointHistory(spInfo, conf);
         Assertions.assertIterableEquals(
                 Collections.singletonList(sp2), spInfo.getSavepointHistory());
         Assertions.assertIterableEquals(
@@ -112,7 +112,7 @@ public class SavepointObserverTest {
 
         Savepoint sp1 = new Savepoint(1, "sp1", SavepointTriggerType.MANUAL);
         spInfo.updateLastSavepoint(sp1);
-        observer.cleanupSavepointHistory(spInfo, sp1, conf);
+        observer.cleanupSavepointHistory(spInfo, conf);
         Assertions.assertIterableEquals(
                 Collections.singletonList(sp1), spInfo.getSavepointHistory());
         Assertions.assertIterableEquals(
@@ -120,7 +120,7 @@ public class SavepointObserverTest {
 
         Savepoint sp2 = new Savepoint(2, "sp2", SavepointTriggerType.MANUAL);
         spInfo.updateLastSavepoint(sp2);
-        observer.cleanupSavepointHistory(spInfo, sp2, conf);
+        observer.cleanupSavepointHistory(spInfo, conf);
         Assertions.assertIterableEquals(
                 Collections.singletonList(sp2), spInfo.getSavepointHistory());
         Assertions.assertIterableEquals(
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 aa9504de..cbe59f85 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
@@ -18,11 +18,11 @@
 package org.apache.flink.kubernetes.operator.observer.deployment;
 
 import org.apache.flink.api.common.JobID;
-import org.apache.flink.api.java.tuple.Tuple2;
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.kubernetes.operator.TestUtils;
 import org.apache.flink.kubernetes.operator.TestingFlinkService;
 import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
+import 
org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions;
 import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
 import org.apache.flink.kubernetes.operator.crd.spec.JobState;
 import 
org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatus;
@@ -379,7 +379,7 @@ public class ApplicationObserverTest {
         flinkService.setPortReady(true);
         observer.observe(deployment, readyContext);
         // Simulate Failed job
-        Tuple2<String, JobStatusMessage> jobTuple = 
flinkService.listJobs().get(0);
+        var jobTuple = flinkService.listJobs().get(0);
         jobTuple.f0 = "last-SP";
         jobTuple.f1 =
                 new JobStatusMessage(
@@ -403,6 +403,32 @@ public class ApplicationObserverTest {
                         .getLastSavepoint()
                         .getLocation());
         
assertFalse(SavepointUtils.savepointInProgress(deployment.getStatus().getJobStatus()));
+
+        observer.observe(deployment, readyContext);
+        assertEquals(
+                3,
+                deployment
+                        .getStatus()
+                        .getJobStatus()
+                        .getSavepointInfo()
+                        .getSavepointHistory()
+                        .size());
+
+        deployment
+                .getSpec()
+                .getFlinkConfiguration()
+                .put(
+                        
KubernetesOperatorConfigOptions.OPERATOR_SAVEPOINT_HISTORY_MAX_COUNT.key(),
+                        "1");
+        observer.observe(deployment, readyContext);
+        assertEquals(
+                1,
+                deployment
+                        .getStatus()
+                        .getJobStatus()
+                        .getSavepointInfo()
+                        .getSavepointHistory()
+                        .size());
     }
 
     private void bringToReadyStatus(FlinkDeployment deployment) {
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserverTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserverTest.java
index 37db442b..05ad76f3 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserverTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserverTest.java
@@ -19,7 +19,6 @@ package 
org.apache.flink.kubernetes.operator.observer.sessionjob;
 
 import org.apache.flink.api.common.JobID;
 import org.apache.flink.api.common.JobStatus;
-import org.apache.flink.api.java.tuple.Tuple2;
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.configuration.RestOptions;
 import org.apache.flink.kubernetes.operator.TestUtils;
@@ -326,7 +325,7 @@ public class SessionJobObserverTest {
 
         // mock a job with different id of the target CR occurs
         var jobs = flinkService.listJobs();
-        for (Tuple2<String, JobStatusMessage> job : jobs) {
+        for (var job : jobs) {
             if (!job.f1.getJobState().isGloballyTerminalState()
                     && !job.f1.getJobId().toHexString().equals(jobID)) {
                 job.f1 =
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconcilerTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconcilerTest.java
index e1f04ec5..b8f5e9af 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconcilerTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconcilerTest.java
@@ -17,7 +17,7 @@
 
 package org.apache.flink.kubernetes.operator.reconciler.deployment;
 
-import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.api.java.tuple.Tuple3;
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.configuration.CoreOptions;
 import org.apache.flink.configuration.HighAvailabilityOptions;
@@ -100,7 +100,7 @@ public class ApplicationReconcilerTest {
         FlinkDeployment deployment = 
TestUtils.buildApplicationCluster(flinkVersion);
 
         reconciler.reconcile(deployment, context);
-        List<Tuple2<String, JobStatusMessage>> runningJobs = 
flinkService.listJobs();
+        var runningJobs = flinkService.listJobs();
         verifyAndSetRunningJobsToStatus(deployment, runningJobs);
 
         // Test stateless upgrade
@@ -228,7 +228,7 @@ public class ApplicationReconcilerTest {
         final FlinkDeployment deployment = TestUtils.buildApplicationCluster();
 
         reconciler.reconcile(deployment, context);
-        List<Tuple2<String, JobStatusMessage>> runningJobs = 
flinkService.listJobs();
+        var runningJobs = flinkService.listJobs();
         verifyAndSetRunningJobsToStatus(deployment, runningJobs);
 
         // Suspend FlinkDeployment with savepoint upgrade mode
@@ -266,7 +266,7 @@ public class ApplicationReconcilerTest {
         FlinkDeployment deployment = TestUtils.buildApplicationCluster();
 
         reconciler.reconcile(deployment, context);
-        List<Tuple2<String, JobStatusMessage>> runningJobs = 
flinkService.listJobs();
+        var runningJobs = flinkService.listJobs();
         verifyAndSetRunningJobsToStatus(deployment, runningJobs);
         
assertFalse(SavepointUtils.savepointInProgress(deployment.getStatus().getJobStatus()));
 
@@ -346,6 +346,16 @@ public class ApplicationReconcilerTest {
         spDeployment.getSpec().getJob().setSavepointTriggerNonce(null);
         reconciler.reconcile(spDeployment, context);
         
assertFalse(SavepointUtils.savepointInProgress(spDeployment.getStatus().getJobStatus()));
+
+        reconciler.reconcile(spDeployment, context);
+        
assertFalse(SavepointUtils.savepointInProgress(spDeployment.getStatus().getJobStatus()));
+
+        spDeployment
+                .getSpec()
+                .getFlinkConfiguration()
+                
.put(KubernetesOperatorConfigOptions.PERIODIC_SAVEPOINT_INTERVAL.key(), "1");
+        reconciler.reconcile(spDeployment, context);
+        
assertTrue(SavepointUtils.savepointInProgress(spDeployment.getStatus().getJobStatus()));
     }
 
     @Test
@@ -389,7 +399,7 @@ public class ApplicationReconcilerTest {
                         .getImage());
         // Upgrade mode changes from stateless to last-state should trigger a 
savepoint
         final String expectedSavepointPath = "savepoint_0";
-        final List<Tuple2<String, JobStatusMessage>> runningJobs = 
flinkService.listJobs();
+        var runningJobs = flinkService.listJobs();
         assertEquals(expectedSavepointPath, runningJobs.get(0).f0);
     }
 
@@ -431,7 +441,7 @@ public class ApplicationReconcilerTest {
         FlinkDeployment deployment = TestUtils.buildApplicationCluster();
 
         reconciler.reconcile(deployment, context);
-        List<Tuple2<String, JobStatusMessage>> runningJobs = 
flinkService.listJobs();
+        var runningJobs = flinkService.listJobs();
         verifyAndSetRunningJobsToStatus(deployment, runningJobs);
 
         // Test restart job
@@ -468,7 +478,8 @@ public class ApplicationReconcilerTest {
     }
 
     private void verifyAndSetRunningJobsToStatus(
-            FlinkDeployment deployment, List<Tuple2<String, JobStatusMessage>> 
runningJobs) {
+            FlinkDeployment deployment,
+            List<Tuple3<String, JobStatusMessage, Configuration>> runningJobs) 
{
         assertEquals(1, runningJobs.size());
         assertNull(runningJobs.get(0).f0);
         deployment
@@ -487,7 +498,7 @@ public class ApplicationReconcilerTest {
     public void testJobUpgradeIgnorePendingSavepoint() throws Exception {
         FlinkDeployment deployment = TestUtils.buildApplicationCluster();
         reconciler.reconcile(deployment, context);
-        List<Tuple2<String, JobStatusMessage>> runningJobs = 
flinkService.listJobs();
+        var runningJobs = flinkService.listJobs();
         verifyAndSetRunningJobsToStatus(deployment, runningJobs);
 
         FlinkDeployment spDeployment = ReconciliationUtils.clone(deployment);
diff --git 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconcilerTest.java
 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconcilerTest.java
index 6b1cf078..13ef730a 100644
--- 
a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconcilerTest.java
+++ 
b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconcilerTest.java
@@ -18,7 +18,7 @@
 package org.apache.flink.kubernetes.operator.reconciler.sessionjob;
 
 import org.apache.flink.api.common.JobID;
-import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.api.java.tuple.Tuple3;
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.kubernetes.operator.TestUtils;
 import org.apache.flink.kubernetes.operator.TestingFlinkService;
@@ -394,8 +394,9 @@ public class SessionJobReconcilerTest {
         
assertFalse(SavepointUtils.savepointInProgress(sp1SessionJob.getStatus().getJobStatus()));
     }
 
-    private Tuple2<String, JobStatusMessage> verifyAndReturnTheSubmittedJob(
-            FlinkSessionJob sessionJob, List<Tuple2<String, JobStatusMessage>> 
jobs) {
+    private Tuple3<String, JobStatusMessage, Configuration> 
verifyAndReturnTheSubmittedJob(
+            FlinkSessionJob sessionJob,
+            List<Tuple3<String, JobStatusMessage, Configuration>> jobs) {
         var jobID = 
JobID.fromHexString(sessionJob.getStatus().getJobStatus().getJobId());
         var submittedJobInfo =
                 jobs.stream().filter(t -> 
t.f1.getJobId().equals(jobID)).findAny().get();
@@ -408,7 +409,7 @@ public class SessionJobReconcilerTest {
             JobState expectedState,
             String jobStatusObserved,
             @Nullable String expectedSavepointPath,
-            List<Tuple2<String, JobStatusMessage>> jobs) {
+            List<Tuple3<String, JobStatusMessage, Configuration>> jobs) {
 
         var submittedJobInfo = verifyAndReturnTheSubmittedJob(sessionJob, 
jobs);
         assertEquals(expectedSavepointPath, submittedJobInfo.f0);

Reply via email to