diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 20432bed8e..93f09bcf1c 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -82,7 +82,7 @@ jobs:
-Dcheckstyle.skip=true \
-Dspotless.check.skip=true \
-Denforcer.skip=true \
- -Dheader="Back to Flink Website
"
+ -Dheader="Back to Flink Website
"
mv target/site/apidocs docs/target/api/java
- name: Upload documentation
diff --git a/docs/layouts/_default/baseof.html b/docs/layouts/_default/baseof.html
index 733bf1b6d2..0db6bc887b 100644
--- a/docs/layouts/_default/baseof.html
+++ b/docs/layouts/_default/baseof.html
@@ -34,7 +34,7 @@
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
- var u="//matomo.privacy.apache.org/";
+ var u="//analytics.apache.org/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
diff --git a/docs/layouts/shortcodes/generated/kubernetes_operator_config_configuration.html b/docs/layouts/shortcodes/generated/kubernetes_operator_config_configuration.html
index db57a55382..e92f04f5ca 100644
--- a/docs/layouts/shortcodes/generated/kubernetes_operator_config_configuration.html
+++ b/docs/layouts/shortcodes/generated/kubernetes_operator_config_configuration.html
@@ -212,6 +212,12 @@
getPrimaryCache() {
public boolean isNextReconciliationImminent() {
return false;
}
+
+ @Override
+ public boolean isPrimaryResourceDeleted() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrimaryResourceFinalStateUnknown() {
+ return false;
+ }
}
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/bluegreen/BlueGreenUtilsTest.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/bluegreen/BlueGreenUtilsTest.java
index 859d19f5d3..a898662ec8 100644
--- a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/bluegreen/BlueGreenUtilsTest.java
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/bluegreen/BlueGreenUtilsTest.java
@@ -27,6 +27,9 @@
import org.apache.flink.kubernetes.operator.api.spec.JobSpec;
import org.apache.flink.kubernetes.operator.api.spec.UpgradeMode;
import org.apache.flink.kubernetes.operator.api.status.FlinkBlueGreenDeploymentStatus;
+import org.apache.flink.kubernetes.operator.api.status.Savepoint;
+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.utils.SpecUtils;
import org.apache.flink.kubernetes.operator.controller.bluegreen.BlueGreenContext;
@@ -38,6 +41,9 @@
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
/** Tests for {@link BlueGreenUtils}. */
public class BlueGreenUtilsTest {
@@ -83,6 +89,141 @@ public void testPrepareFlinkDeploymentWithoutNameReplacement() {
assertEquals(parentDeploymentName + ".jm", resultFlinkConfig.get("metrics.scope.jm"));
}
+ @Test
+ public void testSavepointRequiredBasedOnUpgradeMode() {
+ // SAVEPOINT mode requires savepoint
+ FlinkBlueGreenDeployment bgDeployment =
+ buildBlueGreenDeployment("test-app", TEST_NAMESPACE);
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setUpgradeMode(UpgradeMode.SAVEPOINT);
+ BlueGreenContext context = createContext(bgDeployment);
+ assertTrue(BlueGreenUtils.isSavepointRequired(context));
+
+ // LAST_STATE mode requires savepoint
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setUpgradeMode(UpgradeMode.LAST_STATE);
+ assertTrue(BlueGreenUtils.isSavepointRequired(context));
+
+ // STATELESS mode does not require savepoint
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setUpgradeMode(UpgradeMode.STATELESS);
+ assertFalse(BlueGreenUtils.isSavepointRequired(context));
+ }
+
+ @Test
+ public void testPrepareFlinkDeploymentStatelessInitialSavepointPath() {
+ // Setup: STATELESS mode with initialSavepointPath set
+ FlinkBlueGreenDeployment bgDeployment =
+ buildBlueGreenDeployment("test-app", TEST_NAMESPACE);
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setUpgradeMode(UpgradeMode.STATELESS);
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setInitialSavepointPath("s3://bucket/savepoint-xyz");
+
+ BlueGreenContext context = createContext(bgDeployment);
+
+ // Act: Prepare deployment with null lastCheckpoint (STATELESS transition)
+ FlinkDeployment result =
+ BlueGreenUtils.prepareFlinkDeployment(
+ context,
+ BlueGreenDeploymentType.GREEN,
+ null, // No lastCheckpoint
+ false, // Not first deployment
+ bgDeployment.getMetadata());
+
+ // Assert: initialSavepointPath should be used for STATELESS
+ assertNotNull(result.getSpec().getJob().getInitialSavepointPath());
+ }
+
+ @Test
+ public void testNullLastCheckpointUsesInitialSavepointPath() {
+ // lastCheckpoint=null -> use initialSavepointPath from spec
+ FlinkBlueGreenDeployment bgDeployment =
+ buildBlueGreenDeployment("test-app", TEST_NAMESPACE);
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setUpgradeMode(UpgradeMode.SAVEPOINT);
+ String initialPath = "s3://bucket/user-specified-savepoint";
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setInitialSavepointPath(initialPath);
+
+ BlueGreenContext context = createContext(bgDeployment);
+
+ FlinkDeployment result =
+ BlueGreenUtils.prepareFlinkDeployment(
+ context,
+ BlueGreenDeploymentType.GREEN,
+ null, // null = nonce changed, no new savepoint taken
+ false,
+ bgDeployment.getMetadata());
+
+ assertEquals(initialPath, result.getSpec().getJob().getInitialSavepointPath());
+ }
+
+ @Test
+ public void testNormalTransitionUsesFreshSavepoint() {
+ // Normal transition → take fresh savepoint from running job → use that, not
+ // initialSavepointPath
+ FlinkBlueGreenDeployment bgDeployment =
+ buildBlueGreenDeployment("test-app", TEST_NAMESPACE);
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setUpgradeMode(UpgradeMode.SAVEPOINT);
+ bgDeployment
+ .getSpec()
+ .getTemplate()
+ .getSpec()
+ .getJob()
+ .setInitialSavepointPath("s3://bucket/ignored");
+
+ BlueGreenContext context = createContext(bgDeployment);
+
+ String freshSavepoint = "s3://bucket/fresh-savepoint-from-running-job";
+ Savepoint triggered =
+ Savepoint.of(
+ freshSavepoint, SnapshotTriggerType.UPGRADE, SavepointFormatType.CANONICAL);
+
+ FlinkDeployment result =
+ BlueGreenUtils.prepareFlinkDeployment(
+ context,
+ BlueGreenDeploymentType.GREEN,
+ triggered, // Fresh savepoint provided
+ false,
+ bgDeployment.getMetadata());
+
+ assertEquals(freshSavepoint, result.getSpec().getJob().getInitialSavepointPath());
+ }
+
private static FlinkBlueGreenDeployment buildBlueGreenDeployment(
String name, String namespace) {
var deployment = new FlinkBlueGreenDeployment();
diff --git a/pom.xml b/pom.xml
index 44a1fffd26..c209b27591 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,7 +75,7 @@ under the License.