This is an automated email from the ASF dual-hosted git repository.
ferenc-csaky 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 249dc9c67 [FLINK-38795] Support Flink 2.0+ HA in standalone mode
249dc9c67 is described below
commit 249dc9c6717b3ad67a07aa3cc18549ffa5c9efb2
Author: Marc Demierre <[email protected]>
AuthorDate: Fri Jul 10 15:59:54 2026 +0200
[FLINK-38795] Support Flink 2.0+ HA in standalone mode
---
flink-kubernetes-standalone/pom.xml | 14 ++
.../CmdStandaloneJobManagerDecorator.java | 10 +-
.../CmdStandaloneJobManagerDecoratorTest.java | 142 ++++++++++++++++-----
3 files changed, 131 insertions(+), 35 deletions(-)
diff --git a/flink-kubernetes-standalone/pom.xml
b/flink-kubernetes-standalone/pom.xml
index 5d8da1de3..895187b87 100644
--- a/flink-kubernetes-standalone/pom.xml
+++ b/flink-kubernetes-standalone/pom.xml
@@ -47,12 +47,26 @@ under the License.
</dependency>
<!-- Test -->
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-container</artifactId>
+ <version>${flink.version}</version>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj.version}</version>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
diff --git
a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecorator.java
b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecorator.java
index 2d063eaf7..ebff11c3b 100644
---
a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecorator.java
+++
b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecorator.java
@@ -17,6 +17,7 @@
package org.apache.flink.kubernetes.operator.kubeclient.decorators;
+import org.apache.flink.configuration.JobManagerOptions;
import org.apache.flink.kubernetes.kubeclient.FlinkPod;
import
org.apache.flink.kubernetes.kubeclient.decorators.AbstractKubernetesStepDecorator;
import
org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
@@ -38,6 +39,9 @@ public class CmdStandaloneJobManagerDecorator extends
AbstractKubernetesStepDeco
public static final String APPLICATION_MODE_ARG = "standalone-job";
public static final String POD_IP_ARG =
String.format("$(%s)", Constants.ENV_FLINK_POD_IP_ADDRESS);
+ public static final String DYNAMIC_PROPERTY_FLAG = "-D";
+ public static final String JOBMANAGER_RPC_ADDRESS_ARG =
+ String.format("%s=%s", JobManagerOptions.ADDRESS.key(),
POD_IP_ARG);
private final StandaloneKubernetesJobManagerParameters
kubernetesJobManagerParameters;
@@ -64,7 +68,7 @@ public class CmdStandaloneJobManagerDecorator extends
AbstractKubernetesStepDeco
.addToArgs(JOBMANAGER_ENTRYPOINT_ARG);
if (kubernetesJobManagerParameters.isHAEnabled()) {
- containerBuilder.addToArgs(POD_IP_ARG);
+ containerBuilder.addToArgs(DYNAMIC_PROPERTY_FLAG,
JOBMANAGER_RPC_ADDRESS_ARG);
}
return containerBuilder.build();
@@ -99,8 +103,8 @@ public class CmdStandaloneJobManagerDecorator extends
AbstractKubernetesStepDeco
}
if (kubernetesJobManagerParameters.isHAEnabled()) {
- args.add("--host");
- args.add(POD_IP_ARG);
+ args.add(DYNAMIC_PROPERTY_FLAG);
+ args.add(JOBMANAGER_RPC_ADDRESS_ARG);
}
List<String> jobSpecArgs =
kubernetesJobManagerParameters.getJobSpecArgs();
diff --git
a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecoratorTest.java
b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecoratorTest.java
index d40f111c1..dea384566 100644
---
a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecoratorTest.java
+++
b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecoratorTest.java
@@ -20,21 +20,27 @@ package
org.apache.flink.kubernetes.operator.kubeclient.decorators;
import org.apache.flink.client.deployment.ClusterSpecification;
import org.apache.flink.client.deployment.application.ApplicationConfiguration;
import org.apache.flink.configuration.Configuration;
+import org.apache.flink.configuration.ConfigurationUtils;
import org.apache.flink.configuration.HighAvailabilityOptions;
+import org.apache.flink.configuration.JobManagerOptions;
+import
org.apache.flink.container.entrypoint.StandaloneApplicationClusterConfigurationParserFactory;
import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
import org.apache.flink.kubernetes.kubeclient.FlinkPod;
import
org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
import
org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
+import org.apache.flink.runtime.entrypoint.ClusterConfiguration;
+import
org.apache.flink.runtime.entrypoint.EntrypointClusterConfigurationParserFactory;
+import org.apache.flink.runtime.entrypoint.parser.CommandLineParser;
+import org.apache.flink.runtime.entrypoint.parser.ParserResultFactory;
import org.apache.flink.runtime.jobgraph.SavepointConfigOptions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.util.ArrayList;
import java.util.List;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.assertj.core.api.Assertions.assertThat;
/**
* @link CmdStandaloneJobManagerDecorator unit tests
@@ -67,15 +73,15 @@ public class CmdStandaloneJobManagerDecoratorTest {
StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION);
FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
- assertThat(
- decoratedPod.getMainContainer().getCommand(),
containsInAnyOrder(MOCK_ENTRYPATH));
- assertThat(
- decoratedPod.getMainContainer().getArgs(),
-
containsInAnyOrder(CmdStandaloneJobManagerDecorator.JOBMANAGER_ENTRYPOINT_ARG));
+ assertThat(decoratedPod.getMainContainer().getCommand())
+ .containsExactlyInAnyOrder(MOCK_ENTRYPATH);
+ assertThat(decoratedPod.getMainContainer().getArgs())
+ .containsExactlyInAnyOrder(
+
CmdStandaloneJobManagerDecorator.JOBMANAGER_ENTRYPOINT_ARG);
}
@Test
- public void testApplicationCommandAndArgsAdded() {
+ public void testApplicationCommandAndArgsAdded() throws Exception {
final String testMainClass = "org.main.class";
configuration.set(
StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
@@ -85,17 +91,20 @@ public class CmdStandaloneJobManagerDecoratorTest {
configuration.set(SavepointConfigOptions.SAVEPOINT_PATH,
"/tmp/savepoint/path");
FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
- assertThat(
- decoratedPod.getMainContainer().getCommand(),
containsInAnyOrder(MOCK_ENTRYPATH));
- assertThat(
- decoratedPod.getMainContainer().getArgs(),
- containsInAnyOrder(
+ assertThat(decoratedPod.getMainContainer().getCommand())
+ .containsExactlyInAnyOrder(MOCK_ENTRYPATH);
+ assertThat(decoratedPod.getMainContainer().getArgs())
+ .containsExactlyInAnyOrder(
CmdStandaloneJobManagerDecorator.APPLICATION_MODE_ARG,
"--allowNonRestoredState",
"--fromSavepoint",
"/tmp/savepoint/path",
"--job-classname",
- testMainClass));
+ testMainClass);
+
+ parseEntrypointConfig(
+ new StandaloneApplicationClusterConfigurationParserFactory(),
+ decoratedPod.getMainContainer().getArgs());
}
@Test
@@ -105,11 +114,10 @@ public class CmdStandaloneJobManagerDecoratorTest {
StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
- assertThat(
- decoratedPod.getMainContainer().getCommand(),
containsInAnyOrder(MOCK_ENTRYPATH));
- assertThat(
- decoratedPod.getMainContainer().getArgs(),
-
containsInAnyOrder(CmdStandaloneJobManagerDecorator.APPLICATION_MODE_ARG));
+ assertThat(decoratedPod.getMainContainer().getCommand())
+ .containsExactlyInAnyOrder(MOCK_ENTRYPATH);
+ assertThat(decoratedPod.getMainContainer().getArgs())
+
.containsExactlyInAnyOrder(CmdStandaloneJobManagerDecorator.APPLICATION_MODE_ARG);
}
@Test
@@ -124,13 +132,13 @@ public class CmdStandaloneJobManagerDecoratorTest {
FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
- assertThat(
- decoratedPod.getMainContainer().getCommand(),
containsInAnyOrder(MOCK_ENTRYPATH));
- assertThat(
- decoratedPod.getMainContainer().getArgs(),
- containsInAnyOrder(
+ assertThat(decoratedPod.getMainContainer().getCommand())
+ .containsExactlyInAnyOrder(MOCK_ENTRYPATH);
+ assertThat(decoratedPod.getMainContainer().getArgs())
+ .containsExactly(
CmdStandaloneJobManagerDecorator.JOBMANAGER_ENTRYPOINT_ARG,
- CmdStandaloneJobManagerDecorator.POD_IP_ARG));
+ CmdStandaloneJobManagerDecorator.DYNAMIC_PROPERTY_FLAG,
+
CmdStandaloneJobManagerDecorator.JOBMANAGER_RPC_ADDRESS_ARG);
}
@Test
@@ -144,13 +152,83 @@ public class CmdStandaloneJobManagerDecoratorTest {
FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
- assertThat(
- decoratedPod.getMainContainer().getArgs(),
- contains(
+ assertThat(decoratedPod.getMainContainer().getArgs())
+ .containsExactly(
CmdStandaloneJobManagerDecorator.APPLICATION_MODE_ARG,
- "--host",
- CmdStandaloneJobManagerDecorator.POD_IP_ARG,
+ CmdStandaloneJobManagerDecorator.DYNAMIC_PROPERTY_FLAG,
+
CmdStandaloneJobManagerDecorator.JOBMANAGER_RPC_ADDRESS_ARG,
"--test",
- "123"));
+ "123");
+ }
+
+ @Test
+ public void testApplicationHAArgsUnderstoodByFlinkRuntime() throws
Exception {
+ configuration.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+
StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+ configuration.set(HighAvailabilityOptions.HA_MODE, "KUBERNETES");
+
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
+
+ Configuration parsed =
+ parseEntrypointConfig(
+ new
StandaloneApplicationClusterConfigurationParserFactory(),
+ decoratedPod.getMainContainer().getArgs());
+
+ assertThat(parsed.get(JobManagerOptions.ADDRESS))
+ .isEqualTo(CmdStandaloneJobManagerDecorator.POD_IP_ARG);
+ }
+
+ @Test
+ public void testSessionHAArgsUnderstoodByFlinkRuntime() throws Exception {
+ configuration.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION);
+ configuration.set(
+ HighAvailabilityOptions.HA_MODE,
+
"org.apache.flink.kubernetes.highavailability.KubernetesHaServicesFactory");
+
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
+
+ Configuration parsed =
+ parseEntrypointConfig(
+ new EntrypointClusterConfigurationParserFactory(),
+ decoratedPod.getMainContainer().getArgs());
+
+ assertThat(parsed.get(JobManagerOptions.ADDRESS))
+ .isEqualTo(CmdStandaloneJobManagerDecorator.POD_IP_ARG);
+ }
+
+ @Test
+ public void testApplicationWithoutHADoesNotSetRpcAddress() throws
Exception {
+ configuration.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+
StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(new
FlinkPod.Builder().build());
+
+ Configuration parsed =
+ parseEntrypointConfig(
+ new
StandaloneApplicationClusterConfigurationParserFactory(),
+ decoratedPod.getMainContainer().getArgs());
+
+ assertThat(parsed.get(JobManagerOptions.ADDRESS)).isNull();
+ }
+
+ private static Configuration parseEntrypointConfig(
+ ParserResultFactory<? extends ClusterConfiguration> factory,
List<String> containerArgs)
+ throws Exception {
+ // Dropping the leading entrypoint token (standalone-job /
jobmanager); not seen by the JVM.
+ List<String> entrypointArgs = containerArgs.subList(1,
containerArgs.size());
+
+ // Adding configDir as it's required by the parser factory
+ List<String> argsWithConfigDir = new ArrayList<>();
+ argsWithConfigDir.add("--configDir");
+ argsWithConfigDir.add("unused");
+ argsWithConfigDir.addAll(entrypointArgs);
+
+ ClusterConfiguration parsed =
+ new
CommandLineParser<>(factory).parse(argsWithConfigDir.toArray(new String[0]));
+ return
ConfigurationUtils.createConfiguration(parsed.getDynamicProperties());
}
}