You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by gy...@apache.org on 2022/07/20 15:59:06 UTC
[flink-kubernetes-operator] branch main updated (bf85a9d3 -> 79b16996)
This is an automated email from the ASF dual-hosted git repository.
gyfora pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/flink-kubernetes-operator.git
from bf85a9d3 [doc] Doc config update
new 446961d0 [CI] Make overlapping class check more robust
new ca848f9d [FLINK-27443] Add k8s parameters and decorators for standalone JM and TM pods
new 261fed20 [FLINK-27444] Add KubernetesStandaloneClusterDescriptor and FlinkStandaloneKubeClient
new 0d05d0ab [FLINK-27445] Create Flink Standalone Service
new 79b16996 [FLINK-27446] Add standalone mode validation and config building
The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.github/workflows/ci.yml | 7 +-
docs/content/docs/custom-resource/reference.md | 11 +
e2e-tests/data/flinkdep-cr.yaml | 1 +
e2e-tests/data/multi-sessionjob.yaml | 2 +
e2e-tests/data/sessionjob-cr.yaml | 1 +
.../flink/kubernetes/operator/FlinkOperator.java | 16 +-
.../operator/config/FlinkConfigBuilder.java | 74 ++-
.../operator/crd/spec/FlinkDeploymentSpec.java | 3 +
...radeMode.java => KubernetesDeploymentMode.java} | 29 +-
.../observer/deployment/ObserverFactory.java | 35 +-
.../observer/sessionjob/SessionJobObserver.java | 85 +--
.../AbstractFlinkResourceReconciler.java | 34 +-
.../deployment/AbstractJobReconciler.java | 28 +-
.../deployment/ApplicationReconciler.java | 37 +-
.../reconciler/deployment/ReconcilerFactory.java | 27 +-
.../reconciler/deployment/SessionReconciler.java | 31 +-
.../sessionjob/SessionJobReconciler.java | 27 +-
...FlinkService.java => AbstractFlinkService.java} | 510 +++++++-------
.../kubernetes/operator/service/FlinkService.java | 729 +--------------------
.../operator/service/FlinkServiceFactory.java | 73 +++
.../operator/service/NativeFlinkService.java | 161 +++++
.../operator/service/StandaloneFlinkService.java | 183 ++++++
.../operator/utils/EventSourceUtils.java | 6 +-
.../kubernetes/operator/utils/FlinkUtils.java | 139 +---
.../operator/validation/DefaultValidator.java | 36 +-
.../kubernetes/operator/TestingFlinkService.java | 34 +-
.../operator/TestingFlinkServiceFactory.java | 52 ++
.../operator/config/FlinkConfigBuilderTest.java | 85 +++
.../TestingFlinkDeploymentController.java | 8 +-
.../sessionjob/SessionJobObserverTest.java | 8 +-
.../deployment/ApplicationReconcilerTest.java | 6 +-
.../sessionjob/SessionJobReconcilerTest.java | 13 +-
...erviceTest.java => NativeFlinkServiceTest.java} | 12 +-
.../service/StandaloneFlinkServiceTest.java | 138 ++++
.../operator/validation/DefaultValidatorTest.java | 69 ++
flink-kubernetes-shaded/pom.xml | 22 +
.../pom.xml | 77 +--
.../Fabric8FlinkStandaloneKubeClient.java | 71 ++
.../kubeclient/FlinkStandaloneKubeClient.java | 12 +-
.../CmdStandaloneJobManagerDecorator.java | 90 +++
.../CmdStandaloneTaskManagerDecorator.java | 51 ++
.../InitStandaloneTaskManagerDecorator.java | 145 ++++
.../decorators/UserLibMountDecorator.java | 94 +++
.../StandaloneKubernetesJobManagerFactory.java | 124 ++++
.../StandaloneKubernetesTaskManagerFactory.java | 90 +++
.../StandaloneKubernetesJobManagerParameters.java | 91 +++
.../StandaloneKubernetesTaskManagerParameters.java | 138 ++++
.../KubernetesStandaloneClusterDescriptor.java | 257 ++++++++
.../StandaloneKubernetesConfigOptionsInternal.java | 48 ++
.../operator/utils/StandaloneKubernetesUtils.java | 64 ++
.../src/main/resources/META-INF/NOTICE | 5 +
.../Fabric8FlinkStandaloneKubeClientTest.java | 104 +++
.../CmdStandaloneJobManagerDecoratorTest.java | 106 +++
.../CmdStandaloneTaskManagerDecoratorTest.java | 64 ++
.../InitStandaloneTaskManagerDecoratorTest.java | 176 +++++
.../decorators/UserLibMountDecoratorTest.java | 126 ++++
.../StandaloneKubernetesJobManagerFactoryTest.java | 273 ++++++++
...StandaloneKubernetesTaskManagerFactoryTest.java | 150 +++++
.../kubeclient/parameters/ParametersTestBase.java | 145 ++++
...andaloneKubernetesJobManagerParametersTest.java | 105 +++
...ndaloneKubernetesTaskManagerParametersTest.java | 159 +++++
.../operator/kubeclient/utils/TestUtils.java | 92 +++
.../KubernetesStandaloneClusterDescriptorTest.java | 182 +++++
.../crds/flinkdeployments.flink.apache.org-v1.yml | 5 +
pom.xml | 6 +-
65 files changed, 4477 insertions(+), 1305 deletions(-)
copy flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/{UpgradeMode.java => KubernetesDeploymentMode.java} (56%)
copy flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/{FlinkService.java => AbstractFlinkService.java} (90%)
create mode 100644 flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java
create mode 100644 flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java
create mode 100644 flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java
create mode 100644 flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkServiceFactory.java
rename flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/{FlinkServiceTest.java => NativeFlinkServiceTest.java} (98%)
create mode 100644 flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkServiceTest.java
copy {flink-kubernetes-webhook => flink-kubernetes-standalone}/pom.xml (55%)
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClient.java
copy flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/metrics/CustomResourceMetrics.java => flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/FlinkStandaloneKubeClient.java (67%)
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecorator.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecorator.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecorator.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactory.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactory.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParameters.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptor.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/StandaloneKubernetesConfigOptionsInternal.java
create mode 100644 flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/utils/StandaloneKubernetesUtils.java
create mode 100644 flink-kubernetes-standalone/src/main/resources/META-INF/NOTICE
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClientTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecoratorTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecoratorTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecoratorTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactoryTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactoryTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/ParametersTestBase.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParametersTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParametersTest.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/utils/TestUtils.java
create mode 100644 flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptorTest.java
[flink-kubernetes-operator] 03/05: [FLINK-27444] Add KubernetesStandaloneClusterDescriptor and FlinkStandaloneKubeClient
Posted by gy...@apache.org.
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
commit 261fed2076efe385ede148152c946eb7c5f1f48d
Author: Usamah Jassat <us...@amazon.com>
AuthorDate: Mon Jun 13 15:00:12 2022 +0100
[FLINK-27444] Add KubernetesStandaloneClusterDescriptor and FlinkStandaloneKubeClient
---
flink-kubernetes-standalone/pom.xml | 1 -
.../Fabric8FlinkStandaloneKubeClient.java | 71 ++++++
.../kubeclient/FlinkStandaloneKubeClient.java | 27 ++
.../CmdStandaloneJobManagerDecorator.java | 6 +
.../decorators/UserLibMountDecorator.java | 13 +
.../StandaloneKubernetesJobManagerFactory.java | 124 ++++++++++
.../StandaloneKubernetesTaskManagerFactory.java | 90 +++++++
.../StandaloneKubernetesJobManagerParameters.java | 8 +
.../KubernetesStandaloneClusterDescriptor.java | 257 +++++++++++++++++++
.../src/main/resources/META-INF/NOTICE | 5 +
.../Fabric8FlinkStandaloneKubeClientTest.java | 104 ++++++++
.../CmdStandaloneJobManagerDecoratorTest.java | 26 +-
.../decorators/UserLibMountDecoratorTest.java | 50 +++-
.../StandaloneKubernetesJobManagerFactoryTest.java | 273 +++++++++++++++++++++
...StandaloneKubernetesTaskManagerFactoryTest.java | 150 +++++++++++
.../KubernetesStandaloneClusterDescriptorTest.java | 182 ++++++++++++++
16 files changed, 1383 insertions(+), 4 deletions(-)
diff --git a/flink-kubernetes-standalone/pom.xml b/flink-kubernetes-standalone/pom.xml
index f2a1c2f6..6125efe3 100644
--- a/flink-kubernetes-standalone/pom.xml
+++ b/flink-kubernetes-standalone/pom.xml
@@ -29,7 +29,6 @@ under the License.
</parent>
-
<artifactId>flink-kubernetes-standalone</artifactId>
<name>Flink Kubernetes Standalone</name>
<packaging>jar</packaging>
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClient.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClient.java
new file mode 100644
index 00000000..45454dd7
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClient.java
@@ -0,0 +1,71 @@
+/*
+ * 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.kubeclient;
+
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.kubernetes.kubeclient.Fabric8FlinkKubeClient;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+
+import java.util.concurrent.ExecutorService;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/** The Implementation of {@link FlinkStandaloneKubeClient}. */
+public class Fabric8FlinkStandaloneKubeClient extends Fabric8FlinkKubeClient
+ implements FlinkStandaloneKubeClient {
+
+ private final NamespacedKubernetesClient internalClient;
+
+ public Fabric8FlinkStandaloneKubeClient(
+ Configuration flinkConfig,
+ NamespacedKubernetesClient client,
+ ExecutorService executorService) {
+ super(flinkConfig, client, executorService);
+ internalClient = checkNotNull(client);
+ }
+
+ @Override
+ public void createTaskManagerDeployment(Deployment tmDeployment) {
+ this.internalClient.apps().deployments().create(tmDeployment);
+ }
+
+ @Override
+ public void stopAndCleanupCluster(String clusterId) {
+ this.internalClient
+ .apps()
+ .deployments()
+ .withName(StandaloneKubernetesUtils.getJobManagerDeploymentName(clusterId))
+ .cascading(true)
+ .delete();
+
+ this.internalClient
+ .apps()
+ .deployments()
+ .withName(StandaloneKubernetesUtils.getTaskManagerDeploymentName(clusterId))
+ .cascading(true)
+ .delete();
+ }
+
+ public static NamespacedKubernetesClient createNamespacedKubeClient(String namespace) {
+ return new DefaultKubernetesClient().inNamespace(namespace);
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/FlinkStandaloneKubeClient.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/FlinkStandaloneKubeClient.java
new file mode 100644
index 00000000..9a7f8207
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/FlinkStandaloneKubeClient.java
@@ -0,0 +1,27 @@
+/*
+ * 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.kubeclient;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkKubeClient;
+
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+
+/** Extension of the FlinkKubeClient that is used for Flink standalone deployments. */
+public interface FlinkStandaloneKubeClient extends FlinkKubeClient {
+ void createTaskManagerDeployment(Deployment tmDeployment);
+}
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 430abe2e..9bfcc866 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
@@ -79,6 +79,12 @@ public class CmdStandaloneJobManagerDecorator extends AbstractKubernetesStepDeco
args.add(mainClass);
}
+ Boolean allowNonRestoredState = kubernetesJobManagerParameters.getAllowNonRestoredState();
+ if (allowNonRestoredState != null) {
+ args.add("--allowNonRestoredState");
+ args.add(allowNonRestoredState.toString());
+ }
+
return args;
}
}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java
index a66026b2..207637ac 100644
--- a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java
@@ -27,6 +27,9 @@ import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeBuilder;
+import io.fabric8.kubernetes.api.model.VolumeMount;
+
+import java.util.List;
import static org.apache.flink.util.Preconditions.checkNotNull;
@@ -51,6 +54,10 @@ public class UserLibMountDecorator extends AbstractKubernetesStepDecorator {
return flinkPod;
}
+ if (mainContainerHasUserLibPath(flinkPod)) {
+ return flinkPod;
+ }
+
final Volume userLibVolume =
new VolumeBuilder()
.withName(USER_LIB_VOLUME)
@@ -78,4 +85,10 @@ public class UserLibMountDecorator extends AbstractKubernetesStepDecorator {
.withMainContainer(mountedMainContainer)
.build();
}
+
+ private boolean mainContainerHasUserLibPath(FlinkPod flinkPod) {
+ List<VolumeMount> volumeMounts = flinkPod.getMainContainer().getVolumeMounts();
+ return volumeMounts.stream()
+ .anyMatch(volumeMount -> volumeMount.getMountPath().startsWith(USER_LIB_PATH));
+ }
}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactory.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactory.java
new file mode 100644
index 00000000..f789db93
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactory.java
@@ -0,0 +1,124 @@
+/*
+ * 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.kubeclient.factory;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.KubernetesJobManagerSpecification;
+import org.apache.flink.kubernetes.kubeclient.decorators.EnvSecretsDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.ExternalServiceDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.FlinkConfMountDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.HadoopConfMountDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.InitJobManagerDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.InternalServiceDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.KerberosMountDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.KubernetesStepDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.MountSecretsDecorator;
+import org.apache.flink.kubernetes.kubeclient.parameters.KubernetesJobManagerParameters;
+import org.apache.flink.kubernetes.kubeclient.resources.KubernetesOwnerReference;
+import org.apache.flink.kubernetes.operator.kubeclient.decorators.CmdStandaloneJobManagerDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.decorators.UserLibMountDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.Constants;
+import org.apache.flink.util.Preconditions;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Utility class for constructing all the Kubernetes for the JobManager deploying in standalone
+ * mode. This can include the Deployment, the ConfigMap(s), and the Service(s).
+ */
+public class StandaloneKubernetesJobManagerFactory {
+ public static KubernetesJobManagerSpecification buildKubernetesJobManagerSpecification(
+ FlinkPod podTemplate,
+ StandaloneKubernetesJobManagerParameters kubernetesJobManagerParameters)
+ throws IOException {
+ FlinkPod flinkPod = Preconditions.checkNotNull(podTemplate).copy();
+ List<HasMetadata> accompanyingResources = new ArrayList<>();
+
+ final KubernetesStepDecorator[] stepDecorators =
+ new KubernetesStepDecorator[] {
+ new InitJobManagerDecorator(kubernetesJobManagerParameters),
+ new EnvSecretsDecorator(kubernetesJobManagerParameters),
+ new MountSecretsDecorator(kubernetesJobManagerParameters),
+ new CmdStandaloneJobManagerDecorator(kubernetesJobManagerParameters),
+ new InternalServiceDecorator(kubernetesJobManagerParameters),
+ new ExternalServiceDecorator(kubernetesJobManagerParameters),
+ new HadoopConfMountDecorator(kubernetesJobManagerParameters),
+ new KerberosMountDecorator(kubernetesJobManagerParameters),
+ new FlinkConfMountDecorator(kubernetesJobManagerParameters),
+ new UserLibMountDecorator(kubernetesJobManagerParameters),
+ };
+
+ for (KubernetesStepDecorator stepDecorator : stepDecorators) {
+ flinkPod = stepDecorator.decorateFlinkPod(flinkPod);
+ accompanyingResources.addAll(stepDecorator.buildAccompanyingKubernetesResources());
+ }
+
+ final Deployment deployment =
+ createJobManagerDeployment(flinkPod, kubernetesJobManagerParameters);
+
+ return new KubernetesJobManagerSpecification(deployment, accompanyingResources);
+ }
+
+ private static Deployment createJobManagerDeployment(
+ FlinkPod flinkPod, KubernetesJobManagerParameters kubernetesJobManagerParameters) {
+ final Container resolvedMainContainer = flinkPod.getMainContainer();
+
+ final Pod resolvedPod =
+ new PodBuilder(flinkPod.getPodWithoutMainContainer())
+ .editOrNewSpec()
+ .addToContainers(resolvedMainContainer)
+ .endSpec()
+ .build();
+ return new DeploymentBuilder()
+ .withApiVersion(Constants.APPS_API_VERSION)
+ .editOrNewMetadata()
+ .withName(
+ StandaloneKubernetesUtils.getJobManagerDeploymentName(
+ kubernetesJobManagerParameters.getClusterId()))
+ .withAnnotations(kubernetesJobManagerParameters.getAnnotations())
+ .withLabels(kubernetesJobManagerParameters.getLabels())
+ .withOwnerReferences(
+ kubernetesJobManagerParameters.getOwnerReference().stream()
+ .map(e -> KubernetesOwnerReference.fromMap(e).getInternalResource())
+ .collect(Collectors.toList()))
+ .endMetadata()
+ .editOrNewSpec()
+ .withReplicas(kubernetesJobManagerParameters.getReplicas())
+ .editOrNewTemplate()
+ .withMetadata(resolvedPod.getMetadata())
+ .withSpec(resolvedPod.getSpec())
+ .endTemplate()
+ .editOrNewSelector()
+ .addToMatchLabels(kubernetesJobManagerParameters.getSelectors())
+ .endSelector()
+ .endSpec()
+ .build();
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactory.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactory.java
new file mode 100644
index 00000000..8bcd9990
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactory.java
@@ -0,0 +1,90 @@
+/*
+ * 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.kubeclient.factory;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.decorators.EnvSecretsDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.FlinkConfMountDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.HadoopConfMountDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.KerberosMountDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.KubernetesStepDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.MountSecretsDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.decorators.CmdStandaloneTaskManagerDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.decorators.InitStandaloneTaskManagerDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.Constants;
+import org.apache.flink.util.Preconditions;
+
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
+
+/** Utility class for constructing the TaskManager Deployment when deploying in standalone mode. */
+public class StandaloneKubernetesTaskManagerFactory {
+
+ public static Deployment buildKubernetesTaskManagerDeployment(
+ FlinkPod podTemplate,
+ StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters) {
+ FlinkPod flinkPod = Preconditions.checkNotNull(podTemplate).copy();
+
+ final KubernetesStepDecorator[] stepDecorators =
+ new KubernetesStepDecorator[] {
+ new InitStandaloneTaskManagerDecorator(kubernetesTaskManagerParameters),
+ new EnvSecretsDecorator(kubernetesTaskManagerParameters),
+ new MountSecretsDecorator(kubernetesTaskManagerParameters),
+ new CmdStandaloneTaskManagerDecorator(kubernetesTaskManagerParameters),
+ new HadoopConfMountDecorator(kubernetesTaskManagerParameters),
+ new KerberosMountDecorator(kubernetesTaskManagerParameters),
+ new FlinkConfMountDecorator(kubernetesTaskManagerParameters)
+ };
+
+ for (KubernetesStepDecorator stepDecorator : stepDecorators) {
+ flinkPod = stepDecorator.decorateFlinkPod(flinkPod);
+ }
+
+ final Pod resolvedPod =
+ new PodBuilder(flinkPod.getPodWithoutMainContainer())
+ .editOrNewSpec()
+ .addToContainers(flinkPod.getMainContainer())
+ .endSpec()
+ .build();
+
+ return new DeploymentBuilder()
+ .withApiVersion(Constants.APPS_API_VERSION)
+ .editOrNewMetadata()
+ .withName(
+ StandaloneKubernetesUtils.getTaskManagerDeploymentName(
+ kubernetesTaskManagerParameters.getClusterId()))
+ .withAnnotations(kubernetesTaskManagerParameters.getAnnotations())
+ .withLabels(kubernetesTaskManagerParameters.getLabels())
+ .endMetadata()
+ .editOrNewSpec()
+ .withReplicas(kubernetesTaskManagerParameters.getReplicas())
+ .editOrNewTemplate()
+ .withMetadata(resolvedPod.getMetadata())
+ .withSpec(resolvedPod.getSpec())
+ .endTemplate()
+ .editOrNewSelector()
+ .addToMatchLabels(kubernetesTaskManagerParameters.getSelectors())
+ .endSelector()
+ .endSpec()
+ .build();
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java
index bdc28d48..fbfcbdeb 100644
--- a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java
@@ -24,6 +24,7 @@ import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
import org.apache.flink.kubernetes.kubeclient.parameters.KubernetesJobManagerParameters;
import org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.runtime.jobgraph.SavepointConfigOptions;
import java.util.Collections;
import java.util.HashMap;
@@ -80,4 +81,11 @@ public class StandaloneKubernetesJobManagerParameters extends KubernetesJobManag
}
return flinkConfig.getString(ApplicationConfiguration.APPLICATION_MAIN_CLASS);
}
+
+ public Boolean getAllowNonRestoredState() {
+ if (flinkConfig.contains(SavepointConfigOptions.SAVEPOINT_IGNORE_UNCLAIMED_STATE)) {
+ return flinkConfig.get(SavepointConfigOptions.SAVEPOINT_IGNORE_UNCLAIMED_STATE);
+ }
+ return null;
+ }
}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptor.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptor.java
new file mode 100644
index 00000000..cfed561a
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptor.java
@@ -0,0 +1,257 @@
+/*
+ * 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.standalone;
+
+import org.apache.flink.client.deployment.ClusterDeploymentException;
+import org.apache.flink.client.deployment.ClusterDescriptor;
+import org.apache.flink.client.deployment.ClusterRetrieveException;
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.client.deployment.application.ApplicationConfiguration;
+import org.apache.flink.client.program.ClusterClient;
+import org.apache.flink.client.program.ClusterClientProvider;
+import org.apache.flink.client.program.rest.RestClusterClient;
+import org.apache.flink.configuration.BlobServerOptions;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.configuration.HighAvailabilityOptions;
+import org.apache.flink.configuration.JobManagerOptions;
+import org.apache.flink.configuration.RestOptions;
+import org.apache.flink.configuration.TaskManagerOptions;
+import org.apache.flink.kubernetes.KubernetesClusterDescriptor;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.kubeclient.Endpoint;
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.KubernetesJobManagerSpecification;
+import org.apache.flink.kubernetes.operator.kubeclient.FlinkStandaloneKubeClient;
+import org.apache.flink.kubernetes.operator.kubeclient.factory.StandaloneKubernetesJobManagerFactory;
+import org.apache.flink.kubernetes.operator.kubeclient.factory.StandaloneKubernetesTaskManagerFactory;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+import org.apache.flink.kubernetes.utils.Constants;
+import org.apache.flink.kubernetes.utils.KubernetesUtils;
+import org.apache.flink.runtime.highavailability.HighAvailabilityServicesUtils;
+import org.apache.flink.runtime.highavailability.nonha.standalone.StandaloneClientHAServices;
+import org.apache.flink.runtime.jobmanager.HighAvailabilityMode;
+import org.apache.flink.runtime.rpc.AddressResolution;
+
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/** Standalone Kubernetes specific {@link ClusterDescriptor} implementation. */
+public class KubernetesStandaloneClusterDescriptor extends KubernetesClusterDescriptor {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(KubernetesStandaloneClusterDescriptor.class);
+
+ private static final String CLUSTER_DESCRIPTION = "Standalone Kubernetes cluster";
+
+ private final Configuration flinkConfig;
+
+ private final FlinkStandaloneKubeClient client;
+
+ private final String clusterId;
+
+ public KubernetesStandaloneClusterDescriptor(
+ Configuration flinkConfig, FlinkStandaloneKubeClient client) {
+ super(flinkConfig, client);
+ this.flinkConfig = checkNotNull(flinkConfig);
+ this.client = checkNotNull(client);
+ this.clusterId =
+ checkNotNull(
+ flinkConfig.getString(KubernetesConfigOptions.CLUSTER_ID),
+ "ClusterId must be specified!");
+ }
+
+ @Override
+ public String getClusterDescription() {
+ return CLUSTER_DESCRIPTION;
+ }
+
+ @Override
+ public ClusterClientProvider<String> deploySessionCluster(
+ ClusterSpecification clusterSpecification) throws ClusterDeploymentException {
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION);
+
+ final ClusterClientProvider clusterClientProvider =
+ deployClusterInternal(clusterSpecification);
+
+ try (ClusterClient<String> clusterClient = clusterClientProvider.getClusterClient()) {
+ LOG.info(
+ "Created flink session cluster {} successfully, JobManager Web Interface: {}",
+ clusterId,
+ clusterClient.getWebInterfaceURL());
+ }
+ return clusterClientProvider;
+ }
+
+ @Override
+ public ClusterClientProvider<String> deployApplicationCluster(
+ ClusterSpecification clusterSpecification,
+ ApplicationConfiguration applicationConfiguration)
+ throws ClusterDeploymentException {
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+ applicationConfiguration.applyToConfiguration(flinkConfig);
+ final ClusterClientProvider clusterClientProvider =
+ deployClusterInternal(clusterSpecification);
+
+ try (ClusterClient<String> clusterClient = clusterClientProvider.getClusterClient()) {
+ LOG.info(
+ "Created flink application cluster {} successfully, JobManager Web Interface: {}",
+ clusterId,
+ clusterClient.getWebInterfaceURL());
+ }
+ return clusterClientProvider;
+ }
+
+ private ClusterClientProvider<String> deployClusterInternal(
+ ClusterSpecification clusterSpecification) throws ClusterDeploymentException {
+
+ // Rpc, blob, rest, taskManagerRpc ports need to be exposed, so update them to fixed values.
+ KubernetesUtils.checkAndUpdatePortConfigOption(
+ flinkConfig, BlobServerOptions.PORT, Constants.BLOB_SERVER_PORT);
+ KubernetesUtils.checkAndUpdatePortConfigOption(
+ flinkConfig, TaskManagerOptions.RPC_PORT, Constants.TASK_MANAGER_RPC_PORT);
+ KubernetesUtils.checkAndUpdatePortConfigOption(
+ flinkConfig, RestOptions.BIND_PORT, Constants.REST_PORT);
+
+ if (HighAvailabilityMode.isHighAvailabilityModeActivated(flinkConfig)) {
+ flinkConfig.setString(HighAvailabilityOptions.HA_CLUSTER_ID, clusterId);
+ KubernetesUtils.checkAndUpdatePortConfigOption(
+ flinkConfig,
+ HighAvailabilityOptions.HA_JOB_MANAGER_PORT_RANGE,
+ flinkConfig.get(JobManagerOptions.PORT));
+ }
+
+ // Deploy JM + resources
+ try {
+ KubernetesJobManagerSpecification jmSpec = getJobManagerSpec(clusterSpecification);
+ Deployment tmDeployment = getTaskManagerDeployment(clusterSpecification);
+
+ client.createJobManagerComponent(jmSpec);
+ client.createTaskManagerDeployment(tmDeployment);
+
+ return createClusterClientProvider(clusterId);
+ } catch (Exception e) {
+ try {
+ LOG.warn(
+ "Failed to create the Kubernetes cluster \"{}\", try to clean up the residual resources.",
+ clusterId);
+ client.stopAndCleanupCluster(clusterId);
+ } catch (Exception e1) {
+ LOG.info(
+ "Failed to stop and clean up the Kubernetes cluster \"{}\".",
+ clusterId,
+ e1);
+ }
+ throw new ClusterDeploymentException(
+ "Could not create Kubernetes cluster \"" + clusterId + "\".", e);
+ }
+ }
+
+ private KubernetesJobManagerSpecification getJobManagerSpec(
+ ClusterSpecification clusterSpecification) throws IOException {
+ final StandaloneKubernetesJobManagerParameters kubernetesJobManagerParameters =
+ new StandaloneKubernetesJobManagerParameters(flinkConfig, clusterSpecification);
+
+ final FlinkPod podTemplate =
+ kubernetesJobManagerParameters
+ .getPodTemplateFilePath()
+ .map(
+ file ->
+ KubernetesUtils.loadPodFromTemplateFile(
+ client, file, Constants.MAIN_CONTAINER_NAME))
+ .orElse(new FlinkPod.Builder().build());
+
+ return StandaloneKubernetesJobManagerFactory.buildKubernetesJobManagerSpecification(
+ podTemplate, kubernetesJobManagerParameters);
+ }
+
+ private Deployment getTaskManagerDeployment(ClusterSpecification clusterSpecification) {
+ final StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters =
+ new StandaloneKubernetesTaskManagerParameters(flinkConfig, clusterSpecification);
+
+ final FlinkPod podTemplate =
+ kubernetesTaskManagerParameters
+ .getPodTemplateFilePath()
+ .map(
+ file ->
+ KubernetesUtils.loadPodFromTemplateFile(
+ client, file, Constants.MAIN_CONTAINER_NAME))
+ .orElse(new FlinkPod.Builder().build());
+
+ return StandaloneKubernetesTaskManagerFactory.buildKubernetesTaskManagerDeployment(
+ podTemplate, kubernetesTaskManagerParameters);
+ }
+
+ private ClusterClientProvider<String> createClusterClientProvider(String clusterId) {
+ return () -> {
+ final Configuration configuration = new Configuration(flinkConfig);
+
+ final Optional<Endpoint> restEndpoint = client.getRestEndpoint(clusterId);
+
+ if (restEndpoint.isPresent()) {
+ configuration.setString(RestOptions.ADDRESS, restEndpoint.get().getAddress());
+ configuration.setInteger(RestOptions.PORT, restEndpoint.get().getPort());
+ } else {
+ throw new RuntimeException(
+ new ClusterRetrieveException(
+ "Could not get the rest endpoint of " + clusterId));
+ }
+
+ try {
+ // Flink client will always use Kubernetes service to contact with jobmanager. So we
+ // have a pre-configured web monitor address. Using StandaloneClientHAServices to
+ // create RestClusterClient is reasonable.
+ return new RestClusterClient<>(
+ configuration,
+ clusterId,
+ (effectiveConfiguration, fatalErrorHandler) ->
+ new StandaloneClientHAServices(
+ getWebMonitorAddress(effectiveConfiguration)));
+ } catch (Exception e) {
+ throw new RuntimeException(
+ new ClusterRetrieveException("Could not create the RestClusterClient.", e));
+ }
+ };
+ }
+
+ private String getWebMonitorAddress(Configuration configuration) throws Exception {
+ AddressResolution resolution = AddressResolution.TRY_ADDRESS_RESOLUTION;
+ final KubernetesConfigOptions.ServiceExposedType serviceType =
+ configuration.get(KubernetesConfigOptions.REST_SERVICE_EXPOSED_TYPE);
+ if (serviceType.isClusterIP()) {
+ resolution = AddressResolution.NO_ADDRESS_RESOLUTION;
+ LOG.warn(
+ "Please note that Flink client operations(e.g. cancel, list, stop,"
+ + " savepoint, etc.) won't work from outside the Kubernetes cluster"
+ + " since '{}' has been set to {}.",
+ KubernetesConfigOptions.REST_SERVICE_EXPOSED_TYPE.key(),
+ serviceType);
+ }
+ return HighAvailabilityServicesUtils.getWebMonitorAddress(configuration, resolution);
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/resources/META-INF/NOTICE b/flink-kubernetes-standalone/src/main/resources/META-INF/NOTICE
new file mode 100644
index 00000000..993ca343
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,5 @@
+flink-kubernetes-operator-standalone
+Copyright 2014-2022 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
\ No newline at end of file
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClientTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClientTest.java
new file mode 100644
index 00000000..9c52eb32
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/Fabric8FlinkStandaloneKubeClientTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.kubeclient;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.KubernetesJobManagerSpecification;
+import org.apache.flink.kubernetes.operator.kubeclient.factory.StandaloneKubernetesJobManagerFactory;
+import org.apache.flink.kubernetes.operator.kubeclient.factory.StandaloneKubernetesTaskManagerFactory;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.util.concurrent.Executors;
+
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
+import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/** @link Fabric8FlinkStandaloneKubeClient unit tests */
+@EnableKubernetesMockClient(crud = true)
+public class Fabric8FlinkStandaloneKubeClientTest {
+ private static final String NAMESPACE = "test";
+
+ KubernetesMockServer mockServer;
+ protected NamespacedKubernetesClient kubernetesClient;
+ private FlinkStandaloneKubeClient flinkKubeClient;
+ private StandaloneKubernetesTaskManagerParameters taskManagerParameters;
+ private Deployment tmDeployment;
+ private ClusterSpecification clusterSpecification;
+ private Configuration flinkConfig = new Configuration();
+
+ @BeforeEach
+ public final void setup() {
+ flinkConfig = TestUtils.createTestFlinkConfig();
+ kubernetesClient = mockServer.createClient();
+
+ flinkKubeClient =
+ new Fabric8FlinkStandaloneKubeClient(
+ flinkConfig, kubernetesClient, Executors.newDirectExecutorService());
+ clusterSpecification = TestUtils.createClusterSpecification();
+
+ taskManagerParameters =
+ new StandaloneKubernetesTaskManagerParameters(flinkConfig, clusterSpecification);
+
+ tmDeployment =
+ StandaloneKubernetesTaskManagerFactory.buildKubernetesTaskManagerDeployment(
+ new FlinkPod.Builder().build(), taskManagerParameters);
+ }
+
+ @Test
+ public void testCreateTaskManagerDeployment() {
+ flinkKubeClient.createTaskManagerDeployment(tmDeployment);
+
+ final List<Deployment> resultedDeployments =
+ kubernetesClient.apps().deployments().inNamespace(NAMESPACE).list().getItems();
+ assertEquals(1, resultedDeployments.size());
+ }
+
+ @Test
+ public void testStopAndCleanupCluster() throws Exception {
+ ClusterSpecification clusterSpecification = TestUtils.createClusterSpecification();
+ StandaloneKubernetesJobManagerParameters jmParameters =
+ new StandaloneKubernetesJobManagerParameters(flinkConfig, clusterSpecification);
+ KubernetesJobManagerSpecification jmSpec =
+ StandaloneKubernetesJobManagerFactory.buildKubernetesJobManagerSpecification(
+ new FlinkPod.Builder().build(), jmParameters);
+
+ flinkKubeClient.createJobManagerComponent(jmSpec);
+ flinkKubeClient.createTaskManagerDeployment(tmDeployment);
+
+ List<Deployment> resultedDeployments =
+ kubernetesClient.apps().deployments().inNamespace(NAMESPACE).list().getItems();
+ assertEquals(2, resultedDeployments.size());
+
+ flinkKubeClient.stopAndCleanupCluster(taskManagerParameters.getClusterId());
+
+ resultedDeployments =
+ kubernetesClient.apps().deployments().inNamespace(NAMESPACE).list().getItems();
+ assertEquals(0, resultedDeployments.size());
+ }
+}
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 49355574..8bdb0f33 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
@@ -18,11 +18,13 @@
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.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.jobgraph.SavepointConfigOptions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -67,7 +69,29 @@ public class CmdStandaloneJobManagerDecoratorTest {
}
@Test
- public void testApplicationCommandAdded() {
+ public void testApplicationCommandAndArgsAdded() {
+ final String testMainClass = "org.main.class";
+ configuration.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+ configuration.set(ApplicationConfiguration.APPLICATION_MAIN_CLASS, testMainClass);
+ configuration.set(SavepointConfigOptions.SAVEPOINT_IGNORE_UNCLAIMED_STATE, false);
+
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(new FlinkPod.Builder().build());
+ assertThat(
+ decoratedPod.getMainContainer().getCommand(), containsInAnyOrder(MOCK_ENTRYPATH));
+ assertThat(
+ decoratedPod.getMainContainer().getArgs(),
+ containsInAnyOrder(
+ CmdStandaloneJobManagerDecorator.APPLICATION_MODE_ARG,
+ "--allowNonRestoredState",
+ "false",
+ "--job-classname",
+ testMainClass));
+ }
+
+ @Test
+ public void testApplicationOptionalArgsNotAdded() {
configuration.set(
StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java
index 07c8af20..1a867521 100644
--- a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java
@@ -23,6 +23,10 @@ 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 io.fabric8.kubernetes.api.model.ContainerBuilder;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.PodSpecBuilder;
+import io.fabric8.kubernetes.api.model.VolumeBuilder;
import io.fabric8.kubernetes.api.model.VolumeMount;
import org.junit.jupiter.api.Test;
@@ -32,7 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class UserLibMountDecoratorTest {
@Test
- public void testVolumeAdded() {
+ public void testVolumeAddedApplicationMode() {
StandaloneKubernetesJobManagerParameters jmParameters =
createJmParamsWithClusterMode(
StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
@@ -54,7 +58,7 @@ public class UserLibMountDecoratorTest {
}
@Test
- public void testVolumeNotAdded() {
+ public void testVolumeNotAddedSessionMode() {
StandaloneKubernetesJobManagerParameters jmParameters =
createJmParamsWithClusterMode(
StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION);
@@ -69,6 +73,48 @@ public class UserLibMountDecoratorTest {
assertEquals(0, decoratedPod.getPodWithoutMainContainer().getSpec().getVolumes().size());
}
+ @Test
+ public void testVolumeNotAddedExistingVolumeMount() {
+ StandaloneKubernetesJobManagerParameters jmParameters =
+ createJmParamsWithClusterMode(
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+ UserLibMountDecorator decorator = new UserLibMountDecorator(jmParameters);
+
+ final String volName = "flink-artifact";
+ final String userLibPath = "/opt/flink/usrlib";
+
+ FlinkPod baseFlinkPod =
+ new FlinkPod.Builder()
+ .withMainContainer(
+ new ContainerBuilder()
+ .addNewVolumeMount()
+ .withName(volName)
+ .withMountPath(userLibPath)
+ .endVolumeMount()
+ .build())
+ .withPod(
+ new PodBuilder()
+ .withSpec(
+ new PodSpecBuilder()
+ .addNewVolumeLike(
+ new VolumeBuilder()
+ .withName(volName)
+ .withNewEmptyDir()
+ .endEmptyDir()
+ .build())
+ .endVolume()
+ .build())
+ .build())
+ .build();
+
+ assertEquals(1, baseFlinkPod.getMainContainer().getVolumeMounts().size());
+ assertEquals(1, baseFlinkPod.getPodWithoutMainContainer().getSpec().getVolumes().size());
+
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(baseFlinkPod);
+ assertEquals(1, decoratedPod.getMainContainer().getVolumeMounts().size());
+ assertEquals(1, decoratedPod.getPodWithoutMainContainer().getSpec().getVolumes().size());
+ }
+
private StandaloneKubernetesJobManagerParameters createJmParamsWithClusterMode(
StandaloneKubernetesConfigOptionsInternal.ClusterMode clusterMode) {
return new StandaloneKubernetesJobManagerParameters(
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactoryTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactoryTest.java
new file mode 100644
index 00000000..832ce97a
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesJobManagerFactoryTest.java
@@ -0,0 +1,273 @@
+/*
+ * 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.kubeclient.factory;
+
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.KubernetesJobManagerSpecification;
+import org.apache.flink.kubernetes.kubeclient.decorators.ExternalServiceDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.FlinkConfMountDecorator;
+import org.apache.flink.kubernetes.kubeclient.decorators.InternalServiceDecorator;
+import org.apache.flink.kubernetes.kubeclient.services.HeadlessClusterIPService;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.ParametersTestBase;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.Constants;
+
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerPort;
+import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.PodSpec;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirements;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.apps.DeploymentSpec;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.apache.flink.kubernetes.configuration.KubernetesConfigOptions.REST_SERVICE_EXPOSED_TYPE;
+import static org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils.CLUSTER_ID;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/** @link StandaloneKubernetesJobManagerFactory unit tests */
+public class StandaloneKubernetesJobManagerFactoryTest extends ParametersTestBase {
+
+ KubernetesJobManagerSpecification jmSpec;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ setupFlinkConfig();
+ flinkConfig.set(KubernetesConfigOptions.FLINK_CONF_DIR, "/missing/dir");
+ FlinkPod podTemplate = createPodTemplate();
+ StandaloneKubernetesJobManagerParameters tmParameters =
+ new StandaloneKubernetesJobManagerParameters(
+ flinkConfig, TestUtils.createClusterSpecification());
+ jmSpec =
+ StandaloneKubernetesJobManagerFactory.buildKubernetesJobManagerSpecification(
+ podTemplate, tmParameters);
+ }
+
+ @Test
+ public void testDeploymentMetadata() {
+ ObjectMeta deploymentMetadata = jmSpec.getDeployment().getMetadata();
+ assertEquals(
+ StandaloneKubernetesUtils.getJobManagerDeploymentName(CLUSTER_ID),
+ deploymentMetadata.getName());
+
+ final Map<String, String> expectedLabels =
+ new HashMap<>(StandaloneKubernetesUtils.getJobManagerSelectors(CLUSTER_ID));
+ expectedLabels.putAll(userLabels);
+ assertEquals(expectedLabels, deploymentMetadata.getLabels());
+
+ assertEquals(userAnnotations, deploymentMetadata.getAnnotations());
+ }
+
+ @Test
+ public void testDeploymentSpec() {
+ DeploymentSpec deploymentSpec = jmSpec.getDeployment().getSpec();
+
+ assertEquals(1, deploymentSpec.getReplicas());
+ assertEquals(
+ StandaloneKubernetesUtils.getJobManagerSelectors(CLUSTER_ID),
+ deploymentSpec.getSelector().getMatchLabels());
+ }
+
+ @Test
+ public void testTemplateMetadata() {
+ final ObjectMeta podMetadata = jmSpec.getDeployment().getSpec().getTemplate().getMetadata();
+
+ final Map<String, String> expectedLabels =
+ new HashMap<>(StandaloneKubernetesUtils.getJobManagerSelectors(CLUSTER_ID));
+ expectedLabels.putAll(userLabels);
+ expectedLabels.putAll(templateLabels);
+ assertEquals(expectedLabels, podMetadata.getLabels());
+
+ final Map<String, String> expectedAnnotations = new HashMap<>(userAnnotations);
+ expectedAnnotations.putAll(templateAnnotations);
+ assertEquals(expectedAnnotations, podMetadata.getAnnotations());
+ }
+
+ @Test
+ public void testTemplateSpec() {
+ final PodSpec podSpec = jmSpec.getDeployment().getSpec().getTemplate().getSpec();
+
+ assertEquals(1, podSpec.getContainers().size());
+ assertEquals(TestUtils.SERVICE_ACCOUNT, podSpec.getServiceAccountName());
+ // Config and secret volumes
+ assertEquals(3, podSpec.getVolumes().size());
+
+ final Container mainContainer = podSpec.getContainers().get(0);
+ assertEquals(Constants.MAIN_CONTAINER_NAME, mainContainer.getName());
+ assertEquals(TestUtils.IMAGE, mainContainer.getImage());
+ assertEquals(TestUtils.IMAGE_POLICY, mainContainer.getImagePullPolicy());
+
+ final Map<String, String> envs = new HashMap<>();
+ mainContainer.getEnv().forEach(env -> envs.put(env.getName(), env.getValue()));
+
+ Map<String, String> expectedEnvs = new HashMap<>(templateEnvs);
+ expectedEnvs.put(Constants.ENV_FLINK_POD_IP_ADDRESS, null);
+ assertEquals(expectedEnvs, envs);
+
+ final List<ContainerPort> expectedContainerPorts =
+ Arrays.asList(
+ new ContainerPortBuilder()
+ .withName(Constants.JOB_MANAGER_RPC_PORT_NAME)
+ .withContainerPort(6123)
+ .build(),
+ new ContainerPortBuilder()
+ .withName(Constants.BLOB_SERVER_PORT_NAME)
+ .withContainerPort(Constants.BLOB_SERVER_PORT)
+ .build(),
+ new ContainerPortBuilder()
+ .withName(Constants.REST_PORT_NAME)
+ .withContainerPort(Constants.REST_PORT)
+ .build(),
+ new ContainerPortBuilder()
+ .withName(TEMPLATE_PORT_NAME)
+ .withContainerPort(TEMPLATE_PORT)
+ .build());
+
+ assertThat(mainContainer.getPorts(), containsInAnyOrder(expectedContainerPorts.toArray()));
+
+ final ResourceRequirements resourceRequirements = mainContainer.getResources();
+
+ final Map<String, Quantity> requests = resourceRequirements.getRequests();
+ assertEquals(Double.toString(TestUtils.JOB_MANAGER_CPU), requests.get("cpu").getAmount());
+ assertEquals(
+ String.valueOf(TestUtils.JOB_MANAGER_MEMORY_MB),
+ requests.get("memory").getAmount());
+
+ final Map<String, Quantity> limits = resourceRequirements.getLimits();
+ assertEquals(Double.toString(TestUtils.JOB_MANAGER_CPU), limits.get("cpu").getAmount());
+ assertEquals(
+ String.valueOf(TestUtils.JOB_MANAGER_MEMORY_MB), limits.get("memory").getAmount());
+
+ assertEquals(3, mainContainer.getVolumeMounts().size());
+ }
+
+ @Test
+ public void testAdditionalResourcesSize() throws IOException {
+ final List<HasMetadata> resultAdditionalResources = this.jmSpec.getAccompanyingResources();
+ assertEquals(3, resultAdditionalResources.size());
+
+ final List<HasMetadata> resultServices =
+ resultAdditionalResources.stream()
+ .filter(x -> x instanceof Service)
+ .collect(Collectors.toList());
+ assertEquals(2, resultServices.size());
+
+ final List<HasMetadata> resultConfigMaps =
+ resultAdditionalResources.stream()
+ .filter(x -> x instanceof ConfigMap)
+ .collect(Collectors.toList());
+ assertEquals(1, resultConfigMaps.size());
+ }
+
+ @Test
+ public void testServices() throws IOException {
+ final List<Service> resultServices =
+ this.jmSpec.getAccompanyingResources().stream()
+ .filter(x -> x instanceof Service)
+ .map(x -> (Service) x)
+ .collect(Collectors.toList());
+
+ assertEquals(2, resultServices.size());
+
+ final List<Service> internalServiceCandidates =
+ resultServices.stream()
+ .filter(
+ x ->
+ x.getMetadata()
+ .getName()
+ .equals(
+ InternalServiceDecorator
+ .getInternalServiceName(
+ CLUSTER_ID)))
+ .collect(Collectors.toList());
+ assertEquals(1, internalServiceCandidates.size());
+
+ final List<Service> restServiceCandidates =
+ resultServices.stream()
+ .filter(
+ x ->
+ x.getMetadata()
+ .getName()
+ .equals(
+ ExternalServiceDecorator
+ .getExternalServiceName(
+ CLUSTER_ID)))
+ .collect(Collectors.toList());
+ assertEquals(1, restServiceCandidates.size());
+
+ final Service resultInternalService = internalServiceCandidates.get(0);
+ assertEquals(2, resultInternalService.getMetadata().getLabels().size());
+
+ assertNull(resultInternalService.getSpec().getType());
+ assertEquals(
+ HeadlessClusterIPService.HEADLESS_CLUSTER_IP,
+ resultInternalService.getSpec().getClusterIP());
+ assertEquals(2, resultInternalService.getSpec().getPorts().size());
+ assertEquals(3, resultInternalService.getSpec().getSelector().size());
+
+ final Service resultRestService = restServiceCandidates.get(0);
+ assertEquals(2, resultRestService.getMetadata().getLabels().size());
+
+ assertEquals(
+ REST_SERVICE_EXPOSED_TYPE.defaultValue().toString(),
+ resultRestService.getSpec().getType());
+ assertEquals(1, resultRestService.getSpec().getPorts().size());
+ assertEquals(3, resultRestService.getSpec().getSelector().size());
+ }
+
+ @Test
+ public void testFlinkConfConfigMap() throws IOException {
+ final ConfigMap resultConfigMap =
+ (ConfigMap)
+ jmSpec.getAccompanyingResources().stream()
+ .filter(
+ x ->
+ x instanceof ConfigMap
+ && x.getMetadata()
+ .getName()
+ .equals(
+ FlinkConfMountDecorator
+ .getFlinkConfConfigMapName(
+ CLUSTER_ID)))
+ .collect(Collectors.toList())
+ .get(0);
+
+ assertEquals(2, resultConfigMap.getMetadata().getLabels().size());
+
+ final Map<String, String> resultData = resultConfigMap.getData();
+ assertEquals(1, resultData.size());
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactoryTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactoryTest.java
new file mode 100644
index 00000000..8c8d4e62
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/factory/StandaloneKubernetesTaskManagerFactoryTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.kubeclient.factory;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.ParametersTestBase;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.Constants;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerPort;
+import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.PodSpec;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirements;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/** @link StandaloneKubernetesJobManagerFactory unit tests */
+public class StandaloneKubernetesTaskManagerFactoryTest extends ParametersTestBase {
+
+ private Deployment deployment;
+
+ @BeforeEach
+ public void setup() {
+ setupFlinkConfig();
+ FlinkPod podTemplate = createPodTemplate();
+ StandaloneKubernetesTaskManagerParameters tmParameters =
+ new StandaloneKubernetesTaskManagerParameters(
+ flinkConfig, TestUtils.createClusterSpecification());
+ deployment =
+ StandaloneKubernetesTaskManagerFactory.buildKubernetesTaskManagerDeployment(
+ podTemplate, tmParameters);
+ }
+
+ @Test
+ public void testDeploymentMetadata() {
+ assertEquals(
+ StandaloneKubernetesUtils.getTaskManagerDeploymentName(TestUtils.CLUSTER_ID),
+ deployment.getMetadata().getName());
+
+ final Map<String, String> expectedLabels =
+ new HashMap<>(
+ StandaloneKubernetesUtils.getTaskManagerSelectors(TestUtils.CLUSTER_ID));
+ expectedLabels.putAll(userLabels);
+ assertEquals(expectedLabels, deployment.getMetadata().getLabels());
+
+ assertEquals(userAnnotations, deployment.getMetadata().getAnnotations());
+ }
+
+ @Test
+ public void testDeploymentSpec() {
+ assertEquals(1, deployment.getSpec().getReplicas());
+ assertEquals(
+ StandaloneKubernetesUtils.getTaskManagerSelectors(TestUtils.CLUSTER_ID),
+ deployment.getSpec().getSelector().getMatchLabels());
+ }
+
+ @Test
+ public void testTemplateMetadata() {
+ final ObjectMeta podMetadata = deployment.getSpec().getTemplate().getMetadata();
+
+ final Map<String, String> expectedLabels =
+ new HashMap<>(
+ StandaloneKubernetesUtils.getTaskManagerSelectors(TestUtils.CLUSTER_ID));
+ expectedLabels.putAll(userLabels);
+ expectedLabels.putAll(templateLabels);
+ assertEquals(expectedLabels, podMetadata.getLabels());
+
+ final Map<String, String> expectedAnnotations = new HashMap<>(userAnnotations);
+ expectedAnnotations.putAll(templateAnnotations);
+ assertEquals(expectedAnnotations, podMetadata.getAnnotations());
+ }
+
+ @Test
+ public void testTemplateSpec() {
+ final PodSpec podSpec = deployment.getSpec().getTemplate().getSpec();
+
+ assertEquals(1, podSpec.getContainers().size());
+ assertEquals(TestUtils.SERVICE_ACCOUNT, podSpec.getServiceAccountName());
+ // Config and secret volumes
+ assertEquals(2, podSpec.getVolumes().size());
+
+ final Container mainContainer = podSpec.getContainers().get(0);
+ assertEquals(Constants.MAIN_CONTAINER_NAME, mainContainer.getName());
+ assertEquals(TestUtils.IMAGE, mainContainer.getImage());
+ assertEquals(TestUtils.IMAGE_POLICY, mainContainer.getImagePullPolicy());
+
+ final Map<String, String> envs = new HashMap<>();
+ mainContainer.getEnv().forEach(env -> envs.put(env.getName(), env.getValue()));
+
+ assertEquals(templateEnvs, envs);
+
+ final List<ContainerPort> expectedContainerPorts =
+ Arrays.asList(
+ new ContainerPortBuilder()
+ .withName(Constants.TASK_MANAGER_RPC_PORT_NAME)
+ .withContainerPort(Constants.TASK_MANAGER_RPC_PORT)
+ .build(),
+ new ContainerPortBuilder()
+ .withName(TEMPLATE_PORT_NAME)
+ .withContainerPort(TEMPLATE_PORT)
+ .build());
+
+ assertThat(mainContainer.getPorts(), containsInAnyOrder(expectedContainerPorts.toArray()));
+
+ final ResourceRequirements resourceRequirements = mainContainer.getResources();
+
+ final Map<String, Quantity> requests = resourceRequirements.getRequests();
+ assertEquals(Double.toString(TestUtils.TASK_MANAGER_CPU), requests.get("cpu").getAmount());
+ assertEquals(
+ String.valueOf(TestUtils.TASK_MANAGER_MEMORY_MB),
+ requests.get("memory").getAmount());
+
+ final Map<String, Quantity> limits = resourceRequirements.getLimits();
+ assertEquals(Double.toString(TestUtils.TASK_MANAGER_CPU), limits.get("cpu").getAmount());
+ assertEquals(
+ String.valueOf(TestUtils.TASK_MANAGER_MEMORY_MB), limits.get("memory").getAmount());
+
+ assertEquals(2, mainContainer.getVolumeMounts().size());
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptorTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptorTest.java
new file mode 100644
index 00000000..8aae921e
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/standalone/KubernetesStandaloneClusterDescriptorTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.standalone;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.client.deployment.application.ApplicationConfiguration;
+import org.apache.flink.client.program.ClusterClient;
+import org.apache.flink.client.program.ClusterClientProvider;
+import org.apache.flink.configuration.BlobServerOptions;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.configuration.RestOptions;
+import org.apache.flink.configuration.TaskManagerOptions;
+import org.apache.flink.kubernetes.kubeclient.decorators.ExternalServiceDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.Fabric8FlinkStandaloneKubeClient;
+import org.apache.flink.kubernetes.operator.kubeclient.FlinkStandaloneKubeClient;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.kubernetes.utils.Constants;
+import org.apache.flink.util.concurrent.Executors;
+
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
+import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** @link KubernetesStandaloneClusterDescriptor unit tests */
+@EnableKubernetesMockClient(crud = true)
+public class KubernetesStandaloneClusterDescriptorTest {
+
+ private KubernetesStandaloneClusterDescriptor clusterDescriptor;
+ KubernetesMockServer mockServer;
+ private NamespacedKubernetesClient kubernetesClient;
+ private FlinkStandaloneKubeClient flinkKubeClient;
+ private Configuration flinkConfig = new Configuration();
+
+ @BeforeEach
+ public void setup() {
+ flinkConfig = TestUtils.createTestFlinkConfig();
+ kubernetesClient = mockServer.createClient().inNamespace(TestUtils.TEST_NAMESPACE);
+ flinkKubeClient =
+ new Fabric8FlinkStandaloneKubeClient(
+ flinkConfig, kubernetesClient, Executors.newDirectExecutorService());
+
+ clusterDescriptor = new KubernetesStandaloneClusterDescriptor(flinkConfig, flinkKubeClient);
+ }
+
+ @Test
+ public void testDeploySessionCluster() throws Exception {
+ ClusterSpecification clusterSpecification = TestUtils.createClusterSpecification();
+
+ flinkConfig.setString(BlobServerOptions.PORT, String.valueOf(0));
+ flinkConfig.setString(TaskManagerOptions.RPC_PORT, String.valueOf(0));
+ flinkConfig.setString(RestOptions.BIND_PORT, String.valueOf(0));
+
+ ClusterClientProvider clusterClientProvider =
+ clusterDescriptor.deploySessionCluster(clusterSpecification);
+
+ List<Deployment> deployments =
+ kubernetesClient
+ .apps()
+ .deployments()
+ .inNamespace(TestUtils.TEST_NAMESPACE)
+ .list()
+ .getItems();
+ String expectedJMDeploymentName = TestUtils.CLUSTER_ID;
+ String expectedTMDeploymentName = TestUtils.CLUSTER_ID + "-taskmanager";
+
+ assertEquals(2, deployments.size());
+ assertThat(
+ deployments.stream()
+ .map(d -> d.getMetadata().getName())
+ .collect(Collectors.toList()),
+ containsInAnyOrder(expectedJMDeploymentName, expectedTMDeploymentName));
+ assertEquals(
+ flinkConfig.get(BlobServerOptions.PORT),
+ String.valueOf(Constants.BLOB_SERVER_PORT));
+ assertEquals(
+ flinkConfig.get(TaskManagerOptions.RPC_PORT),
+ String.valueOf(Constants.TASK_MANAGER_RPC_PORT));
+ assertEquals(flinkConfig.get(RestOptions.BIND_PORT), String.valueOf(Constants.REST_PORT));
+
+ Deployment jmDeployment =
+ deployments.stream()
+ .filter(d -> d.getMetadata().getName().equals(expectedJMDeploymentName))
+ .findFirst()
+ .orElse(null);
+ assertTrue(
+ jmDeployment.getSpec().getTemplate().getSpec().getContainers().stream()
+ .anyMatch(c -> c.getArgs().contains("jobmanager")));
+
+ ClusterClient clusterClient = clusterClientProvider.getClusterClient();
+
+ String expectedWebUrl =
+ String.format(
+ "http://%s:%d",
+ ExternalServiceDecorator.getNamespacedExternalServiceName(
+ TestUtils.CLUSTER_ID, TestUtils.TEST_NAMESPACE),
+ Constants.REST_PORT);
+ assertEquals(expectedWebUrl, clusterClient.getWebInterfaceURL());
+ }
+
+ @Test
+ public void testDeployApplicationCluster() throws Exception {
+ ClusterSpecification clusterSpecification = TestUtils.createClusterSpecification();
+
+ flinkConfig.setString(BlobServerOptions.PORT, String.valueOf(0));
+ flinkConfig.setString(TaskManagerOptions.RPC_PORT, String.valueOf(0));
+ flinkConfig.setString(RestOptions.BIND_PORT, String.valueOf(0));
+
+ ClusterClientProvider clusterClientProvider =
+ clusterDescriptor.deployApplicationCluster(
+ clusterSpecification,
+ ApplicationConfiguration.fromConfiguration(flinkConfig));
+
+ List<Deployment> deployments =
+ kubernetesClient
+ .apps()
+ .deployments()
+ .inNamespace(TestUtils.TEST_NAMESPACE)
+ .list()
+ .getItems();
+ String expectedJMDeploymentName = TestUtils.CLUSTER_ID;
+ String expectedTMDeploymentName = TestUtils.CLUSTER_ID + "-taskmanager";
+
+ assertEquals(2, deployments.size());
+ assertThat(
+ deployments.stream()
+ .map(d -> d.getMetadata().getName())
+ .collect(Collectors.toList()),
+ containsInAnyOrder(expectedJMDeploymentName, expectedTMDeploymentName));
+ assertEquals(
+ flinkConfig.get(BlobServerOptions.PORT),
+ String.valueOf(Constants.BLOB_SERVER_PORT));
+ assertEquals(
+ flinkConfig.get(TaskManagerOptions.RPC_PORT),
+ String.valueOf(Constants.TASK_MANAGER_RPC_PORT));
+ assertEquals(flinkConfig.get(RestOptions.BIND_PORT), String.valueOf(Constants.REST_PORT));
+
+ Deployment jmDeployment =
+ deployments.stream()
+ .filter(d -> d.getMetadata().getName().equals(expectedJMDeploymentName))
+ .findFirst()
+ .orElse(null);
+ assertTrue(
+ jmDeployment.getSpec().getTemplate().getSpec().getContainers().stream()
+ .anyMatch(c -> c.getArgs().contains("standalone-job")));
+
+ ClusterClient clusterClient = clusterClientProvider.getClusterClient();
+
+ String expectedWebUrl =
+ String.format(
+ "http://%s:%d",
+ ExternalServiceDecorator.getNamespacedExternalServiceName(
+ TestUtils.CLUSTER_ID, TestUtils.TEST_NAMESPACE),
+ Constants.REST_PORT);
+ assertEquals(expectedWebUrl, clusterClient.getWebInterfaceURL());
+ }
+}
[flink-kubernetes-operator] 02/05: [FLINK-27443] Add k8s parameters and decorators for standalone JM and TM pods
Posted by gy...@apache.org.
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
commit ca848f9da9de9f075b0e12e00b057d6c8a85b771
Author: Usamah Jassat <us...@amazon.com>
AuthorDate: Tue Apr 26 18:41:41 2022 +0100
[FLINK-27443] Add k8s parameters and decorators for standalone JM and TM pods
---
flink-kubernetes-shaded/pom.xml | 22 +++
.../pom.xml | 80 +++++-----
.../CmdStandaloneJobManagerDecorator.java | 84 ++++++++++
.../CmdStandaloneTaskManagerDecorator.java | 51 ++++++
.../InitStandaloneTaskManagerDecorator.java | 145 +++++++++++++++++
.../decorators/UserLibMountDecorator.java | 81 ++++++++++
.../StandaloneKubernetesJobManagerParameters.java | 83 ++++++++++
.../StandaloneKubernetesTaskManagerParameters.java | 138 ++++++++++++++++
.../StandaloneKubernetesConfigOptionsInternal.java | 48 ++++++
.../operator/utils/StandaloneKubernetesUtils.java | 64 ++++++++
.../CmdStandaloneJobManagerDecoratorTest.java | 82 ++++++++++
.../CmdStandaloneTaskManagerDecoratorTest.java | 64 ++++++++
.../InitStandaloneTaskManagerDecoratorTest.java | 176 +++++++++++++++++++++
.../decorators/UserLibMountDecoratorTest.java | 80 ++++++++++
.../kubeclient/parameters/ParametersTestBase.java | 145 +++++++++++++++++
...andaloneKubernetesJobManagerParametersTest.java | 105 ++++++++++++
...ndaloneKubernetesTaskManagerParametersTest.java | 159 +++++++++++++++++++
.../operator/kubeclient/utils/TestUtils.java | 92 +++++++++++
pom.xml | 6 +-
19 files changed, 1667 insertions(+), 38 deletions(-)
diff --git a/flink-kubernetes-shaded/pom.xml b/flink-kubernetes-shaded/pom.xml
index 7fb94ef1..543043b3 100644
--- a/flink-kubernetes-shaded/pom.xml
+++ b/flink-kubernetes-shaded/pom.xml
@@ -43,6 +43,17 @@ under the License.
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-kubernetes-standalone</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
</dependencies>
<build>
@@ -50,6 +61,7 @@ under the License.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
+ <version>3.3.0</version>
<executions>
<execution>
<id>shade-flink-operator</id>
@@ -69,6 +81,16 @@ under the License.
<shadedPattern>org.apache.flink.kubernetes.shaded.io.fabric8</shadedPattern>
</relocation>
</relocations>
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/DEPENDENCIES</exclude>
+ <exclude>META-INF/LICENSE</exclude>
+ <exclude>META-INF/MANIFEST.MF</exclude>
+ </excludes>
+ </filter>
+ </filters>
</configuration>
</execution>
</executions>
diff --git a/flink-kubernetes-shaded/pom.xml b/flink-kubernetes-standalone/pom.xml
similarity index 50%
copy from flink-kubernetes-shaded/pom.xml
copy to flink-kubernetes-standalone/pom.xml
index 7fb94ef1..f2a1c2f6 100644
--- a/flink-kubernetes-shaded/pom.xml
+++ b/flink-kubernetes-standalone/pom.xml
@@ -20,18 +20,27 @@ under the License.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
+
<parent>
- <groupId>org.apache.flink</groupId>
- <artifactId>flink-kubernetes-operator-parent</artifactId>
- <version>1.2-SNAPSHOT</version>
- <relativePath>..</relativePath>
+ <artifactId>flink-kubernetes-operator-parent</artifactId>
+ <groupId>org.apache.flink</groupId>
+ <version>1.2-SNAPSHOT</version>
+ <relativePath>..</relativePath>
</parent>
- <artifactId>flink-kubernetes-shaded</artifactId>
- <name>Flink Kubernetes Shaded</name>
+
+
+ <artifactId>flink-kubernetes-standalone</artifactId>
+ <name>Flink Kubernetes Standalone</name>
<packaging>jar</packaging>
<dependencies>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-client</artifactId>
+ <version>${fabric8.version}</version>
+ </dependency>
+
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-kubernetes</artifactId>
@@ -43,36 +52,33 @@ under the License.
</exclusion>
</exclusions>
</dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-shade-plugin</artifactId>
- <executions>
- <execution>
- <id>shade-flink-operator</id>
- <phase>package</phase>
- <goals>
- <goal>shade</goal>
- </goals>
- <configuration>
- <artifactSet>
- <includes combine.children="append">
- <include>*:*</include>
- </includes>
- </artifactSet>
- <relocations>
- <relocation>
- <pattern>io.fabric8</pattern>
- <shadedPattern>org.apache.flink.kubernetes.shaded.io.fabric8</shadedPattern>
- </relocation>
- </relocations>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-clients</artifactId>
+ <version>${flink.version}</version>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>${junit.jupiter.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <version>${hamcrest.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-server-mock</artifactId>
+ <version>${fabric8.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
</project>
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
new file mode 100644
index 00000000..430abe2e
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecorator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.decorators.AbstractKubernetesStepDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/**
+ * Attach the command and args to the main container for running the JobManager in standalone mode.
+ */
+public class CmdStandaloneJobManagerDecorator extends AbstractKubernetesStepDecorator {
+
+ public static final String JOBMANAGER_ENTRYPOINT_ARG = "jobmanager";
+ public static final String APPLICATION_MODE_ARG = "standalone-job";
+
+ private final StandaloneKubernetesJobManagerParameters kubernetesJobManagerParameters;
+
+ public CmdStandaloneJobManagerDecorator(
+ StandaloneKubernetesJobManagerParameters kubernetesJobManagerParameters) {
+ this.kubernetesJobManagerParameters = checkNotNull(kubernetesJobManagerParameters);
+ }
+
+ @Override
+ public FlinkPod decorateFlinkPod(FlinkPod flinkPod) {
+ final Container mainContainerWithStartCmd;
+ if (kubernetesJobManagerParameters.isApplicationCluster()) {
+ mainContainerWithStartCmd = decorateApplicationContainer(flinkPod.getMainContainer());
+ } else {
+ mainContainerWithStartCmd = decorateSessionContainer(flinkPod.getMainContainer());
+ }
+ return new FlinkPod.Builder(flinkPod).withMainContainer(mainContainerWithStartCmd).build();
+ }
+
+ private Container decorateSessionContainer(Container mainContainer) {
+ return new ContainerBuilder(mainContainer)
+ .withCommand(kubernetesJobManagerParameters.getContainerEntrypoint())
+ .withArgs(JOBMANAGER_ENTRYPOINT_ARG)
+ .build();
+ }
+
+ private Container decorateApplicationContainer(Container mainContainer) {
+ return new ContainerBuilder(mainContainer)
+ .withCommand(kubernetesJobManagerParameters.getContainerEntrypoint())
+ .withArgs(getApplicationClusterArgs())
+ .build();
+ }
+
+ private List<String> getApplicationClusterArgs() {
+ List<String> args = new ArrayList<>();
+ args.add(APPLICATION_MODE_ARG);
+
+ String mainClass = kubernetesJobManagerParameters.getMainClass();
+ if (mainClass != null) {
+ args.add("--job-classname");
+ args.add(mainClass);
+ }
+
+ return args;
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecorator.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecorator.java
new file mode 100644
index 00000000..1533e0b2
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecorator.java
@@ -0,0 +1,51 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.decorators.AbstractKubernetesStepDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerBuilder;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/**
+ * Attach the command and args to the main container for running the TaskManager in standalone mode.
+ */
+public class CmdStandaloneTaskManagerDecorator extends AbstractKubernetesStepDecorator {
+ public static final String TASKMANAGER_ENTRYPOINT_ARG = "taskmanager";
+
+ private final StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters;
+
+ public CmdStandaloneTaskManagerDecorator(
+ StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters) {
+ this.kubernetesTaskManagerParameters = checkNotNull(kubernetesTaskManagerParameters);
+ }
+
+ @Override
+ public FlinkPod decorateFlinkPod(FlinkPod flinkPod) {
+ final Container mainContainerWithStartCmd =
+ new ContainerBuilder(flinkPod.getMainContainer())
+ .withCommand(kubernetesTaskManagerParameters.getContainerEntrypoint())
+ .withArgs(TASKMANAGER_ENTRYPOINT_ARG)
+ .build();
+ return new FlinkPod.Builder(flinkPod).withMainContainer(mainContainerWithStartCmd).build();
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecorator.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecorator.java
new file mode 100644
index 00000000..adbc1a95
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecorator.java
@@ -0,0 +1,145 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.decorators.AbstractKubernetesStepDecorator;
+import org.apache.flink.kubernetes.kubeclient.resources.KubernetesToleration;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+import org.apache.flink.kubernetes.utils.Constants;
+import org.apache.flink.kubernetes.utils.KubernetesUtils;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerBuilder;
+import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
+import io.fabric8.kubernetes.api.model.EnvVar;
+import io.fabric8.kubernetes.api.model.EnvVarBuilder;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.ResourceRequirements;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/**
+ * An initializer for the TaskManager {@link org.apache.flink.kubernetes.kubeclient.FlinkPod} in
+ * standalone mode.
+ */
+public class InitStandaloneTaskManagerDecorator extends AbstractKubernetesStepDecorator {
+ private final StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters;
+
+ public InitStandaloneTaskManagerDecorator(
+ StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters) {
+ this.kubernetesTaskManagerParameters = checkNotNull(kubernetesTaskManagerParameters);
+ }
+
+ @Override
+ public FlinkPod decorateFlinkPod(FlinkPod flinkPod) {
+ final PodBuilder basicPodBuilder = new PodBuilder(flinkPod.getPodWithoutMainContainer());
+
+ // Overwrite fields
+ final String serviceAccountName = kubernetesTaskManagerParameters.getServiceAccount();
+
+ basicPodBuilder
+ .withApiVersion(Constants.API_VERSION)
+ .editOrNewSpec()
+ .withServiceAccount(serviceAccountName)
+ .withServiceAccountName(serviceAccountName)
+ .endSpec();
+
+ // Merge fields
+ basicPodBuilder
+ .editOrNewMetadata()
+ .addToLabels(kubernetesTaskManagerParameters.getLabels())
+ .addToAnnotations(kubernetesTaskManagerParameters.getAnnotations())
+ .endMetadata()
+ .editOrNewSpec()
+ .addToImagePullSecrets(kubernetesTaskManagerParameters.getImagePullSecrets())
+ .addToNodeSelector(kubernetesTaskManagerParameters.getNodeSelector())
+ .addAllToTolerations(
+ kubernetesTaskManagerParameters.getTolerations().stream()
+ .map(e -> KubernetesToleration.fromMap(e).getInternalResource())
+ .collect(Collectors.toList()))
+ .endSpec();
+
+ final Container basicMainContainer = decorateMainContainer(flinkPod.getMainContainer());
+
+ return new FlinkPod.Builder(flinkPod)
+ .withPod(basicPodBuilder.build())
+ .withMainContainer(basicMainContainer)
+ .build();
+ }
+
+ private Container decorateMainContainer(Container container) {
+ final ContainerBuilder mainContainerBuilder = new ContainerBuilder(container);
+
+ // Overwrite fields
+ final ResourceRequirements requirementsInPodTemplate =
+ container.getResources() == null
+ ? new ResourceRequirements()
+ : container.getResources();
+ final ResourceRequirements resourceRequirements =
+ KubernetesUtils.getResourceRequirements(
+ requirementsInPodTemplate,
+ kubernetesTaskManagerParameters.getTaskManagerMemoryMB(),
+ kubernetesTaskManagerParameters.getMemoryLimitFactor(),
+ kubernetesTaskManagerParameters.getTaskManagerCPU(),
+ kubernetesTaskManagerParameters.getCpuLimitFactor(),
+ Collections.emptyMap(),
+ Collections.emptyMap());
+ final String image = kubernetesTaskManagerParameters.getImage();
+ final String imagePullPolicy = kubernetesTaskManagerParameters.getImagePullPolicy().name();
+ mainContainerBuilder
+ .withName(Constants.MAIN_CONTAINER_NAME)
+ .withImage(image)
+ .withImagePullPolicy(imagePullPolicy)
+ .withResources(resourceRequirements);
+
+ // Merge fields
+ mainContainerBuilder
+ .addToPorts(
+ new ContainerPortBuilder()
+ .withName(Constants.TASK_MANAGER_RPC_PORT_NAME)
+ .withContainerPort(kubernetesTaskManagerParameters.getRPCPort())
+ .build())
+ .addAllToEnv(getCustomizedEnvs());
+ getFlinkLogDirEnv().ifPresent(mainContainerBuilder::addToEnv);
+
+ return mainContainerBuilder.build();
+ }
+
+ private List<EnvVar> getCustomizedEnvs() {
+ return kubernetesTaskManagerParameters.getEnvironments().entrySet().stream()
+ .map(
+ kv ->
+ new EnvVarBuilder()
+ .withName(kv.getKey())
+ .withValue(kv.getValue())
+ .build())
+ .collect(Collectors.toList());
+ }
+
+ private Optional<EnvVar> getFlinkLogDirEnv() {
+ return kubernetesTaskManagerParameters
+ .getFlinkLogDirInPod()
+ .map(logDir -> new EnvVar(Constants.ENV_FLINK_LOG_DIR, logDir, null));
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java
new file mode 100644
index 00000000..a66026b2
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecorator.java
@@ -0,0 +1,81 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.kubeclient.decorators.AbstractKubernetesStepDecorator;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesJobManagerParameters;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerBuilder;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Volume;
+import io.fabric8.kubernetes.api.model.VolumeBuilder;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/**
+ * Mount the Flink User Lib directory to enable Flink to pick up a Jars defined in
+ * pipeline.classpaths. Used for starting standalone application clusters
+ */
+public class UserLibMountDecorator extends AbstractKubernetesStepDecorator {
+ private static final String USER_LIB_VOLUME = "user-lib-dir";
+ private static final String USER_LIB_PATH = "/opt/flink/usrlib";
+
+ private final StandaloneKubernetesJobManagerParameters kubernetesJobManagerParameters;
+
+ public UserLibMountDecorator(
+ StandaloneKubernetesJobManagerParameters kubernetesJobManagerParameters) {
+ this.kubernetesJobManagerParameters = checkNotNull(kubernetesJobManagerParameters);
+ }
+
+ @Override
+ public FlinkPod decorateFlinkPod(FlinkPod flinkPod) {
+ if (!kubernetesJobManagerParameters.isApplicationCluster()) {
+ return flinkPod;
+ }
+
+ final Volume userLibVolume =
+ new VolumeBuilder()
+ .withName(USER_LIB_VOLUME)
+ .withNewEmptyDir()
+ .endEmptyDir()
+ .build();
+
+ final Pod pod =
+ new PodBuilder(flinkPod.getPodWithoutMainContainer())
+ .editSpec()
+ .addNewVolumeLike(userLibVolume)
+ .endVolume()
+ .endSpec()
+ .build();
+
+ final Container mountedMainContainer =
+ new ContainerBuilder(flinkPod.getMainContainer())
+ .addNewVolumeMount()
+ .withName(USER_LIB_VOLUME)
+ .withMountPath(USER_LIB_PATH)
+ .endVolumeMount()
+ .build();
+ return new FlinkPod.Builder(flinkPod)
+ .withPod(pod)
+ .withMainContainer(mountedMainContainer)
+ .build();
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java
new file mode 100644
index 00000000..bdc28d48
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParameters.java
@@ -0,0 +1,83 @@
+/*
+ * 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.kubeclient.parameters;
+
+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.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.kubeclient.parameters.KubernetesJobManagerParameters;
+import org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A Utility class that helps to parse, verify and manage the Kubernetes parameters that are used
+ * for constructing the JobManager deployment used for standalone cluster deployments.
+ */
+public class StandaloneKubernetesJobManagerParameters extends KubernetesJobManagerParameters {
+
+ public StandaloneKubernetesJobManagerParameters(
+ Configuration flinkConfig, ClusterSpecification clusterSpecification) {
+ super(flinkConfig, clusterSpecification);
+ }
+
+ @Override
+ public Map<String, String> getLabels() {
+ final Map<String, String> labels = new HashMap<>();
+ labels.putAll(getSelectors());
+ labels.putAll(
+ flinkConfig
+ .getOptional(KubernetesConfigOptions.JOB_MANAGER_LABELS)
+ .orElse(Collections.emptyMap()));
+ return Collections.unmodifiableMap(labels);
+ }
+
+ @Override
+ public Map<String, String> getSelectors() {
+ return Collections.unmodifiableMap(
+ StandaloneKubernetesUtils.getJobManagerSelectors(getClusterId()));
+ }
+
+ @Override
+ public Map<String, String> getCommonLabels() {
+ return Collections.unmodifiableMap(
+ StandaloneKubernetesUtils.getCommonLabels(getClusterId()));
+ }
+
+ @Override
+ public boolean isInternalServiceEnabled() {
+ return true;
+ }
+
+ public boolean isApplicationCluster() {
+ return flinkConfig
+ .get(StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE)
+ .equals(StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+ }
+
+ public String getMainClass() {
+ if (!isApplicationCluster()) {
+ return null;
+ }
+ return flinkConfig.getString(ApplicationConfiguration.APPLICATION_MAIN_CLASS);
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParameters.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParameters.java
new file mode 100644
index 00000000..692614bf
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParameters.java
@@ -0,0 +1,138 @@
+/*
+ * 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.kubeclient.parameters;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.configuration.TaskManagerOptions;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.kubeclient.parameters.AbstractKubernetesParameters;
+import org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.KubernetesUtils;
+import org.apache.flink.util.Preconditions;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * A utility class that helps to parse, verify and manage the Kubernetes parameters that are used
+ * for constructing the TaskManager deployment used for standalone deployments.
+ */
+public class StandaloneKubernetesTaskManagerParameters extends AbstractKubernetesParameters {
+ private final ClusterSpecification clusterSpecification;
+
+ public StandaloneKubernetesTaskManagerParameters(
+ Configuration flinkConfig, ClusterSpecification clusterSpecification) {
+ super(flinkConfig);
+ this.clusterSpecification = clusterSpecification;
+ }
+
+ @Override
+ public Map<String, String> getLabels() {
+ final Map<String, String> labels = new HashMap<>();
+ labels.putAll(
+ flinkConfig
+ .getOptional(KubernetesConfigOptions.TASK_MANAGER_LABELS)
+ .orElse(Collections.emptyMap()));
+ labels.putAll(getSelectors());
+ return Collections.unmodifiableMap(labels);
+ }
+
+ @Override
+ public Map<String, String> getSelectors() {
+ return StandaloneKubernetesUtils.getTaskManagerSelectors(getClusterId());
+ }
+
+ @Override
+ public Map<String, String> getNodeSelector() {
+ return Collections.unmodifiableMap(
+ flinkConfig
+ .getOptional(KubernetesConfigOptions.TASK_MANAGER_NODE_SELECTOR)
+ .orElse(Collections.emptyMap()));
+ }
+
+ @Override
+ public Map<String, String> getEnvironments() {
+ // TMs have environment set using the pod template.
+ return new HashMap<>();
+ }
+
+ @Override
+ public Map<String, String> getAnnotations() {
+ return flinkConfig
+ .getOptional(KubernetesConfigOptions.TASK_MANAGER_ANNOTATIONS)
+ .orElse(Collections.emptyMap());
+ }
+
+ @Override
+ public List<Map<String, String>> getTolerations() {
+ return flinkConfig
+ .getOptional(KubernetesConfigOptions.TASK_MANAGER_TOLERATIONS)
+ .orElse(Collections.emptyList());
+ }
+
+ public int getReplicas() {
+ int replicas =
+ flinkConfig.get(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS);
+ Preconditions.checkArgument(
+ replicas > 0,
+ "'%s' should not be configured less than 1.",
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS.key());
+ return replicas;
+ }
+
+ public String getServiceAccount() {
+ return flinkConfig.get(KubernetesConfigOptions.TASK_MANAGER_SERVICE_ACCOUNT);
+ }
+
+ public int getTaskManagerMemoryMB() {
+ return clusterSpecification.getTaskManagerMemoryMB();
+ }
+
+ public double getTaskManagerCPU() {
+ return flinkConfig.getDouble(KubernetesConfigOptions.TASK_MANAGER_CPU);
+ }
+
+ public int getRPCPort() {
+ final int taskManagerRpcPort =
+ KubernetesUtils.parsePort(flinkConfig, TaskManagerOptions.RPC_PORT);
+ Preconditions.checkArgument(
+ taskManagerRpcPort > 0, "%s should not be 0.", TaskManagerOptions.RPC_PORT.key());
+ return taskManagerRpcPort;
+ }
+
+ public Optional<File> getPodTemplateFilePath() {
+ return flinkConfig
+ .getOptional(KubernetesConfigOptions.TASK_MANAGER_POD_TEMPLATE)
+ .map(File::new);
+ }
+
+ public double getMemoryLimitFactor() {
+ return flinkConfig.get(KubernetesConfigOptions.TASK_MANAGER_MEMORY_LIMIT_FACTOR);
+ }
+
+ public double getCpuLimitFactor() {
+ return flinkConfig.get(KubernetesConfigOptions.TASK_MANAGER_CPU_LIMIT_FACTOR);
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/StandaloneKubernetesConfigOptionsInternal.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/StandaloneKubernetesConfigOptionsInternal.java
new file mode 100644
index 00000000..bb1af0e9
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/standalone/StandaloneKubernetesConfigOptionsInternal.java
@@ -0,0 +1,48 @@
+/*
+ * 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.standalone;
+
+import org.apache.flink.configuration.ConfigOption;
+
+import static org.apache.flink.configuration.ConfigOptions.key;
+
+/**
+ * This class holds internal configuration constants used by flink operator when deploying flink
+ * clusters in standalone mode.
+ */
+public class StandaloneKubernetesConfigOptionsInternal {
+ public static final ConfigOption<Integer> KUBERNETES_TASKMANAGER_REPLICAS =
+ key("kubernetes.internal.taskmanager.replicas")
+ .intType()
+ .defaultValue(1)
+ .withDescription(
+ "Specify how many pods will be in the TaskManager pool. For "
+ + "standalone kubernetes Flink sessions clusters.");
+
+ public static final ConfigOption<ClusterMode> CLUSTER_MODE =
+ key("kubernetes.internal.cluster-mode")
+ .enumType(ClusterMode.class)
+ .defaultValue(ClusterMode.APPLICATION)
+ .withDescription("Specify what mode the cluster will be deployed in.");
+
+ /** The different modes that a Flink cluster can be deployed in. */
+ public enum ClusterMode {
+ APPLICATION,
+ SESSION
+ }
+}
diff --git a/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/utils/StandaloneKubernetesUtils.java b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/utils/StandaloneKubernetesUtils.java
new file mode 100644
index 00000000..ca801e4c
--- /dev/null
+++ b/flink-kubernetes-standalone/src/main/java/org/apache/flink/kubernetes/operator/utils/StandaloneKubernetesUtils.java
@@ -0,0 +1,64 @@
+/*
+ * 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.utils;
+
+import org.apache.flink.kubernetes.utils.Constants;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Standalone Kubernetes Utils. */
+public class StandaloneKubernetesUtils {
+
+ public static final String LABEL_TYPE_STANDALONE_TYPE = "flink-standalone-kubernetes";
+ private static final String TM_DEPLOYMENT_POSTFIX = "-taskmanager";
+
+ public static String getTaskManagerDeploymentName(String clusterId) {
+ return clusterId + TM_DEPLOYMENT_POSTFIX;
+ }
+
+ public static String getJobManagerDeploymentName(String clusterId) {
+ return clusterId;
+ }
+
+ public static Map<String, String> getCommonLabels(String clusterId) {
+ Map<String, String> commonLabels = new HashMap();
+ commonLabels.put(Constants.LABEL_TYPE_KEY, LABEL_TYPE_STANDALONE_TYPE);
+ commonLabels.put(Constants.LABEL_APP_KEY, clusterId);
+ return commonLabels;
+ }
+
+ public static Map<String, String> getTaskManagerSelectors(String clusterId) {
+ Map<String, String> labels = getCommonLabels(clusterId);
+ labels.put(Constants.LABEL_COMPONENT_KEY, Constants.LABEL_COMPONENT_TASK_MANAGER);
+ return Collections.unmodifiableMap(labels);
+ }
+
+ public static Map<String, String> getJobManagerSelectors(String clusterId) {
+ final Map<String, String> labels = getCommonLabels(clusterId);
+ labels.put(Constants.LABEL_COMPONENT_KEY, Constants.LABEL_COMPONENT_JOB_MANAGER);
+ return Collections.unmodifiableMap(labels);
+ }
+
+ public static Map<String, String> getConfigMapLabels(String clusterId, String type) {
+ Map<String, String> labels = new HashMap(getCommonLabels(clusterId));
+ labels.put("configmap-type", type);
+ return Collections.unmodifiableMap(labels);
+ }
+}
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
new file mode 100644
index 00000000..49355574
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneJobManagerDecoratorTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.Configuration;
+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.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+
+/** @link CmdStandaloneJobManagerDecorator unit tests */
+public class CmdStandaloneJobManagerDecoratorTest {
+
+ private static final String MOCK_ENTRYPATH = "./docker-entrypath";
+
+ private StandaloneKubernetesJobManagerParameters jmParameters;
+ private Configuration configuration;
+ private CmdStandaloneJobManagerDecorator decorator;
+
+ @BeforeEach
+ public void setup() {
+ configuration = new Configuration();
+ configuration.setString(KubernetesConfigOptions.KUBERNETES_ENTRY_PATH, MOCK_ENTRYPATH);
+ jmParameters =
+ new StandaloneKubernetesJobManagerParameters(
+ configuration,
+ new ClusterSpecification.ClusterSpecificationBuilder()
+ .createClusterSpecification());
+
+ decorator = new CmdStandaloneJobManagerDecorator(jmParameters);
+ }
+
+ @Test
+ public void testSessionCommandAdded() {
+ configuration.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ 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));
+ }
+
+ @Test
+ public void testApplicationCommandAdded() {
+ configuration.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ 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));
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecoratorTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecoratorTest.java
new file mode 100644
index 00000000..09d623c0
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/CmdStandaloneTaskManagerDecoratorTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+
+/** @link CmdStandaloneTaskManagerDecorator unit tests */
+public class CmdStandaloneTaskManagerDecoratorTest {
+
+ private static final String MOCK_ENTRYPATH = "./docker-entrypath";
+
+ private StandaloneKubernetesTaskManagerParameters tmParameters;
+ private Configuration configuration;
+ private CmdStandaloneTaskManagerDecorator decorator;
+
+ @BeforeEach
+ public void setup() {
+ configuration = new Configuration();
+ configuration.setString(KubernetesConfigOptions.KUBERNETES_ENTRY_PATH, MOCK_ENTRYPATH);
+ tmParameters =
+ new StandaloneKubernetesTaskManagerParameters(
+ configuration,
+ new ClusterSpecification.ClusterSpecificationBuilder()
+ .createClusterSpecification());
+
+ decorator = new CmdStandaloneTaskManagerDecorator(tmParameters);
+ }
+
+ @Test
+ public void testCommandAdded() {
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(new FlinkPod.Builder().build());
+
+ assertThat(
+ decoratedPod.getMainContainer().getCommand(), containsInAnyOrder(MOCK_ENTRYPATH));
+ assertThat(
+ decoratedPod.getMainContainer().getArgs(),
+ containsInAnyOrder(CmdStandaloneTaskManagerDecorator.TASKMANAGER_ENTRYPOINT_ARG));
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecoratorTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecoratorTest.java
new file mode 100644
index 00000000..36483adf
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/InitStandaloneTaskManagerDecoratorTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.ParametersTestBase;
+import org.apache.flink.kubernetes.operator.kubeclient.parameters.StandaloneKubernetesTaskManagerParameters;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.Constants;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerPort;
+import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
+import io.fabric8.kubernetes.api.model.EnvVar;
+import io.fabric8.kubernetes.api.model.LocalObjectReference;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirements;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/** @link InitStandaloneTaskManagerDecorator unit tests */
+public class InitStandaloneTaskManagerDecoratorTest extends ParametersTestBase {
+
+ private Pod resultPod;
+ private Container resultMainContainer;
+
+ @BeforeEach
+ public void setup() {
+ setupFlinkConfig();
+
+ ClusterSpecification clusterSpecification = TestUtils.createClusterSpecification();
+ StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters =
+ new StandaloneKubernetesTaskManagerParameters(flinkConfig, clusterSpecification);
+ InitStandaloneTaskManagerDecorator decorator =
+ new InitStandaloneTaskManagerDecorator(kubernetesTaskManagerParameters);
+
+ final FlinkPod resultFlinkPod = decorator.decorateFlinkPod(createPodTemplate());
+ resultPod = resultFlinkPod.getPodWithoutMainContainer();
+ resultMainContainer = resultFlinkPod.getMainContainer();
+ }
+
+ @Test
+ public void testOverrideApiVersion() {
+ assertEquals(Constants.API_VERSION, resultPod.getApiVersion());
+ }
+
+ @Test
+ public void testMainContainerName() {
+ assertEquals(Constants.MAIN_CONTAINER_NAME, resultMainContainer.getName());
+ }
+
+ @Test
+ public void testOverrideMainContainerImage() {
+ assertEquals(TestUtils.IMAGE, resultMainContainer.getImage());
+ }
+
+ @Test
+ public void testOverrideMainContainerImagePullPolicy() {
+ assertEquals(TestUtils.IMAGE_POLICY, resultMainContainer.getImagePullPolicy());
+ }
+
+ @Test
+ public void testOverrideMainContainerResourceRequirements() {
+ final ResourceRequirements resourceRequirements = resultMainContainer.getResources();
+
+ final Map<String, Quantity> requests = resourceRequirements.getRequests();
+ assertEquals(Double.toString(TestUtils.TASK_MANAGER_CPU), requests.get("cpu").getAmount());
+ assertEquals(
+ String.valueOf(TestUtils.TASK_MANAGER_MEMORY_MB),
+ requests.get("memory").getAmount());
+
+ final Map<String, Quantity> limits = resourceRequirements.getLimits();
+ assertEquals(Double.toString(TestUtils.TASK_MANAGER_CPU), limits.get("cpu").getAmount());
+ assertEquals(
+ String.valueOf(TestUtils.TASK_MANAGER_MEMORY_MB), limits.get("memory").getAmount());
+ }
+
+ @Test
+ public void testMergeMainContainerPorts() {
+ final List<ContainerPort> expectedContainerPorts =
+ Arrays.asList(
+ new ContainerPortBuilder()
+ .withName(Constants.TASK_MANAGER_RPC_PORT_NAME)
+ .withContainerPort(Constants.TASK_MANAGER_RPC_PORT)
+ .build(),
+ new ContainerPortBuilder()
+ .withName(TEMPLATE_PORT_NAME)
+ .withContainerPort(TEMPLATE_PORT)
+ .build());
+
+ assertThat(
+ resultMainContainer.getPorts(),
+ containsInAnyOrder(expectedContainerPorts.toArray()));
+ }
+
+ @Test
+ public void testMergePodLabels() {
+ final Map<String, String> expectedLabels =
+ new HashMap<>(
+ StandaloneKubernetesUtils.getTaskManagerSelectors(TestUtils.CLUSTER_ID));
+ expectedLabels.putAll(userLabels);
+ expectedLabels.putAll(templateLabels);
+
+ assertEquals(expectedLabels, resultPod.getMetadata().getLabels());
+ }
+
+ @Test
+ public void testMergePodAnnotations() {
+ final Map<String, String> expectedAnnotations = new HashMap<>(userAnnotations);
+ expectedAnnotations.putAll(templateAnnotations);
+ assertEquals(expectedAnnotations, resultPod.getMetadata().getAnnotations());
+ }
+
+ @Test
+ public void testOverridePodServiceAccountName() {
+ assertEquals(TestUtils.SERVICE_ACCOUNT, resultPod.getSpec().getServiceAccountName());
+ }
+
+ @Test
+ public void testMergeImagePullSecrets() {
+ final List<String> resultSecrets =
+ resultPod.getSpec().getImagePullSecrets().stream()
+ .map(LocalObjectReference::getName)
+ .collect(Collectors.toList());
+ final List<String> expectedImagePullSecrets = new ArrayList<>(userImagePullSecrets);
+ expectedImagePullSecrets.addAll(templateImagePullSecrets);
+
+ assertThat(resultSecrets, containsInAnyOrder(expectedImagePullSecrets.toArray()));
+ }
+
+ @Test
+ public void testMergeNodeSelector() {
+ final Map<String, String> expectedNodeSelectors = new HashMap<>(userNodeSelectors);
+ expectedNodeSelectors.putAll(templateNodeSelector);
+ assertEquals(expectedNodeSelectors, resultPod.getSpec().getNodeSelector());
+ }
+
+ @Test
+ public void testEnvs() {
+ final List<EnvVar> envVars = resultMainContainer.getEnv();
+
+ final Map<String, String> envs = new HashMap<>();
+ envVars.forEach(env -> envs.put(env.getName(), env.getValue()));
+
+ assertEquals(templateEnvs, envs);
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java
new file mode 100644
index 00000000..07c8af20
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/decorators/UserLibMountDecoratorTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.kubeclient.decorators;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.Configuration;
+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 io.fabric8.kubernetes.api.model.VolumeMount;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/** @link UserLibMountDecorator unit tests */
+public class UserLibMountDecoratorTest {
+
+ @Test
+ public void testVolumeAdded() {
+ StandaloneKubernetesJobManagerParameters jmParameters =
+ createJmParamsWithClusterMode(
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+
+ UserLibMountDecorator decorator = new UserLibMountDecorator(jmParameters);
+
+ FlinkPod baseFlinkPod = new FlinkPod.Builder().build();
+
+ assertEquals(0, baseFlinkPod.getMainContainer().getVolumeMounts().size());
+ assertEquals(0, baseFlinkPod.getPodWithoutMainContainer().getSpec().getVolumes().size());
+
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(baseFlinkPod);
+ assertEquals(1, decoratedPod.getMainContainer().getVolumeMounts().size());
+ assertEquals(1, decoratedPod.getPodWithoutMainContainer().getSpec().getVolumes().size());
+
+ VolumeMount volumeMount = decoratedPod.getMainContainer().getVolumeMounts().get(0);
+
+ assertEquals("/opt/flink/usrlib", volumeMount.getMountPath());
+ }
+
+ @Test
+ public void testVolumeNotAdded() {
+ StandaloneKubernetesJobManagerParameters jmParameters =
+ createJmParamsWithClusterMode(
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION);
+ UserLibMountDecorator decorator = new UserLibMountDecorator(jmParameters);
+
+ FlinkPod baseFlinkPod = new FlinkPod.Builder().build();
+ assertEquals(0, baseFlinkPod.getMainContainer().getVolumeMounts().size());
+ assertEquals(0, baseFlinkPod.getPodWithoutMainContainer().getSpec().getVolumes().size());
+
+ FlinkPod decoratedPod = decorator.decorateFlinkPod(baseFlinkPod);
+ assertEquals(0, decoratedPod.getMainContainer().getVolumeMounts().size());
+ assertEquals(0, decoratedPod.getPodWithoutMainContainer().getSpec().getVolumes().size());
+ }
+
+ private StandaloneKubernetesJobManagerParameters createJmParamsWithClusterMode(
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode clusterMode) {
+ return new StandaloneKubernetesJobManagerParameters(
+ new Configuration()
+ .set(StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE, clusterMode),
+ new ClusterSpecification.ClusterSpecificationBuilder()
+ .createClusterSpecification());
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/ParametersTestBase.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/ParametersTestBase.java
new file mode 100644
index 00000000..c3ffbafb
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/ParametersTestBase.java
@@ -0,0 +1,145 @@
+/*
+ * 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.kubeclient.parameters;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.kubeclient.FlinkPod;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.kubernetes.utils.KubernetesUtils;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerBuilder;
+import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
+import io.fabric8.kubernetes.api.model.EnvVar;
+import io.fabric8.kubernetes.api.model.EnvVarBuilder;
+import io.fabric8.kubernetes.api.model.LocalObjectReference;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.ResourceRequirements;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/** Base class for Kubernetes tests. */
+public class ParametersTestBase {
+
+ protected Configuration flinkConfig;
+
+ protected ClusterSpecification clusterSpecification = TestUtils.createClusterSpecification();
+
+ protected final Map<String, String> userLabels =
+ TestUtils.generateTestStringStringMap("label", "value", 2);
+
+ protected final Map<String, String> userAnnotations =
+ TestUtils.generateTestStringStringMap("annotation", "value", 2);
+
+ protected final Map<String, String> userNodeSelectors =
+ TestUtils.generateTestStringStringMap("selector", "val", 2);
+
+ protected final List<String> userImagePullSecrets = Arrays.asList("s1", "s2", "s3");
+
+ protected static final String TEMPLATE_PORT_NAME = "user-port";
+ protected static final int TEMPLATE_PORT = 9458;
+
+ protected final Map<String, String> templateEnvs =
+ TestUtils.generateTestStringStringMap("TEMPLATE_KEY", "VAL", 2);
+
+ protected final Map<String, String> templateLabels =
+ TestUtils.generateTestStringStringMap("template-label", "value", 2);
+
+ protected final Map<String, String> templateAnnotations =
+ TestUtils.generateTestStringStringMap("template-annotation", "value", 2);
+
+ protected final Map<String, String> templateNodeSelector =
+ TestUtils.generateTestStringStringMap("template-node-selector", "value", 2);
+
+ protected final List<String> templateImagePullSecrets = Arrays.asList("ts1", "ts2", "ts3");
+
+ private static final String SECRETS = "ssl-cert:/etc/ssl";
+
+ protected FlinkPod createPodTemplate() {
+ List<EnvVar> envVars = new ArrayList<>();
+ templateEnvs.forEach(
+ (k, v) -> envVars.add(new EnvVarBuilder().withName(k).withValue(v).build()));
+
+ Container mainContainer =
+ new ContainerBuilder()
+ .withImagePullPolicy("templatePullPolicy")
+ .withImage("templateImage")
+ .withResources(
+ KubernetesUtils.getResourceRequirements(
+ new ResourceRequirements(),
+ 1234,
+ 1,
+ 102,
+ 1,
+ Collections.emptyMap(),
+ Collections.emptyMap()))
+ .withPorts(
+ new ContainerPortBuilder()
+ .withName(TEMPLATE_PORT_NAME)
+ .withContainerPort(TEMPLATE_PORT)
+ .build())
+ .withEnv(envVars)
+ .build();
+
+ return new FlinkPod.Builder()
+ .withMainContainer(mainContainer)
+ .withPod(
+ new PodBuilder()
+ .withApiVersion("templateAPIVersion")
+ .editOrNewSpec()
+ .withServiceAccountName("templateServiceAccountName")
+ .withServiceAccount("templateServiceAccount")
+ .endSpec()
+ .editOrNewMetadata()
+ .addToLabels(templateLabels)
+ .addToAnnotations(templateAnnotations)
+ .endMetadata()
+ .editOrNewSpec()
+ .addToImagePullSecrets(getImagePullSecrets())
+ .addToNodeSelector(templateNodeSelector)
+ .endSpec()
+ .build())
+ .build();
+ }
+
+ private LocalObjectReference[] getImagePullSecrets() {
+ return templateImagePullSecrets.stream()
+ .map(String::trim)
+ .filter(secret -> !secret.isEmpty())
+ .map(LocalObjectReference::new)
+ .toArray(LocalObjectReference[]::new);
+ }
+
+ protected void setupFlinkConfig() {
+ flinkConfig = TestUtils.createTestFlinkConfig();
+ flinkConfig.set(KubernetesConfigOptions.TASK_MANAGER_ANNOTATIONS, userAnnotations);
+ flinkConfig.set(KubernetesConfigOptions.JOB_MANAGER_ANNOTATIONS, userAnnotations);
+ flinkConfig.set(KubernetesConfigOptions.JOB_MANAGER_LABELS, userLabels);
+ flinkConfig.set(KubernetesConfigOptions.TASK_MANAGER_LABELS, userLabels);
+ flinkConfig.set(KubernetesConfigOptions.TASK_MANAGER_NODE_SELECTOR, userNodeSelectors);
+ flinkConfig.set(KubernetesConfigOptions.JOB_MANAGER_NODE_SELECTOR, userNodeSelectors);
+ flinkConfig.set(KubernetesConfigOptions.CONTAINER_IMAGE_PULL_SECRETS, userImagePullSecrets);
+ flinkConfig.setString(KubernetesConfigOptions.KUBERNETES_SECRETS.key(), SECRETS);
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParametersTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParametersTest.java
new file mode 100644
index 00000000..cf6c0250
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesJobManagerParametersTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.kubeclient.parameters;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.client.deployment.application.ApplicationConfiguration;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** @link StandaloneKubernetesJobManagerParameters unit tests */
+public class StandaloneKubernetesJobManagerParametersTest extends ParametersTestBase {
+ private StandaloneKubernetesJobManagerParameters kubernetesJobManagerParameters;
+
+ @BeforeEach
+ public void setup() {
+ setupFlinkConfig();
+ ClusterSpecification clusterSpecification = TestUtils.createClusterSpecification();
+ kubernetesJobManagerParameters =
+ new StandaloneKubernetesJobManagerParameters(flinkConfig, clusterSpecification);
+ }
+
+ @Test
+ public void testGetLabels() {
+ Map<String, String> expectedLabels = new HashMap<>();
+ expectedLabels.putAll(userLabels);
+ expectedLabels.putAll(
+ StandaloneKubernetesUtils.getJobManagerSelectors(TestUtils.CLUSTER_ID));
+ assertEquals(expectedLabels, kubernetesJobManagerParameters.getLabels());
+ }
+
+ @Test
+ public void testGetSelectors() {
+ assertEquals(
+ StandaloneKubernetesUtils.getJobManagerSelectors(TestUtils.CLUSTER_ID),
+ kubernetesJobManagerParameters.getSelectors());
+ }
+
+ @Test
+ public void testGetCommonLabels() {
+ assertEquals(
+ StandaloneKubernetesUtils.getCommonLabels(TestUtils.CLUSTER_ID),
+ kubernetesJobManagerParameters.getCommonLabels());
+ }
+
+ @Test
+ public void testIsInternalServiceEnabled() {
+ assertTrue(kubernetesJobManagerParameters.isInternalServiceEnabled());
+ }
+
+ @Test
+ public void testIsApplicationCluster() {
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+ assertTrue(kubernetesJobManagerParameters.isApplicationCluster());
+
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION);
+ assertFalse(kubernetesJobManagerParameters.isApplicationCluster());
+ }
+
+ @Test
+ public void testGetMainClass() {
+ String entryClass = "my.main.test.class";
+ flinkConfig.set(ApplicationConfiguration.APPLICATION_MAIN_CLASS, entryClass);
+
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION);
+ assertNull(kubernetesJobManagerParameters.getMainClass());
+
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION);
+ assertEquals(entryClass, kubernetesJobManagerParameters.getMainClass());
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParametersTest.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParametersTest.java
new file mode 100644
index 00000000..c794a630
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/parameters/StandaloneKubernetesTaskManagerParametersTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.kubeclient.parameters;
+
+import org.apache.flink.configuration.TaskManagerOptions;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.operator.kubeclient.utils.TestUtils;
+import org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.Constants;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** @link StandaloneKubernetesTaskManagerParameters unit tests */
+public class StandaloneKubernetesTaskManagerParametersTest extends ParametersTestBase {
+
+ private StandaloneKubernetesTaskManagerParameters kubernetesTaskManagerParameters;
+
+ @BeforeEach
+ public void setup() {
+ setupFlinkConfig();
+ kubernetesTaskManagerParameters =
+ new StandaloneKubernetesTaskManagerParameters(flinkConfig, clusterSpecification);
+ }
+
+ @Test
+ public void testGetLabels() {
+ Map<String, String> expectedLabels = new HashMap<>();
+ expectedLabels.putAll(userLabels);
+ expectedLabels.putAll(
+ StandaloneKubernetesUtils.getTaskManagerSelectors(TestUtils.CLUSTER_ID));
+ assertEquals(expectedLabels, kubernetesTaskManagerParameters.getLabels());
+ }
+
+ @Test
+ public void testGetSelectors() {
+ Map<String, String> expectedSelectors =
+ StandaloneKubernetesUtils.getTaskManagerSelectors(TestUtils.CLUSTER_ID);
+
+ assertEquals(expectedSelectors, kubernetesTaskManagerParameters.getSelectors());
+ }
+
+ @Test
+ public void testGetNodeSelector() {
+ assertEquals(userNodeSelectors, kubernetesTaskManagerParameters.getNodeSelector());
+ }
+
+ @Test
+ public void testGetEnvironments() {
+ assertTrue(kubernetesTaskManagerParameters.getEnvironments().isEmpty());
+ }
+
+ @Test
+ public void testGetAnnotations() {
+ assertEquals(userAnnotations, kubernetesTaskManagerParameters.getAnnotations());
+ }
+
+ @Test
+ public void testGetReplicas() {
+ int tmReplicas = 11;
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS,
+ tmReplicas);
+ assertEquals(tmReplicas, kubernetesTaskManagerParameters.getReplicas());
+ }
+
+ @Test
+ public void testInvalidReplicas() {
+ flinkConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS, -1);
+ kubernetesTaskManagerParameters =
+ new StandaloneKubernetesTaskManagerParameters(flinkConfig, clusterSpecification);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ kubernetesTaskManagerParameters.getReplicas();
+ });
+ }
+
+ @Test
+ public void testGetServiceAccount() {
+ assertEquals(
+ TestUtils.SERVICE_ACCOUNT, kubernetesTaskManagerParameters.getServiceAccount());
+ }
+
+ @Test
+ public void testGetTaskManagerMemoryMB() {
+ assertEquals(
+ TestUtils.TASK_MANAGER_MEMORY_MB,
+ kubernetesTaskManagerParameters.getTaskManagerMemoryMB());
+ }
+
+ @Test
+ public void testGetTaskManagerCPU() {
+ assertEquals(
+ TestUtils.TASK_MANAGER_CPU,
+ kubernetesTaskManagerParameters.getTaskManagerCPU(),
+ 0.00001);
+ }
+
+ @Test
+ public void testGetGetRPCPort() {
+ assertEquals(Constants.TASK_MANAGER_RPC_PORT, kubernetesTaskManagerParameters.getRPCPort());
+ }
+
+ @Test
+ public void testInvalidRPCPort() {
+ flinkConfig.set(TaskManagerOptions.RPC_PORT, String.valueOf(0));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ kubernetesTaskManagerParameters.getRPCPort();
+ });
+ }
+
+ @Test
+ public void testGetPodTemplateFilePath() {
+ String templateFilePath = "/tmp/tst.yml";
+ flinkConfig.setString(KubernetesConfigOptions.TASK_MANAGER_POD_TEMPLATE, templateFilePath);
+ Optional<File> templateFile = kubernetesTaskManagerParameters.getPodTemplateFilePath();
+ assertTrue(templateFile.isPresent());
+ String filePath = templateFile.get().getAbsolutePath();
+ assertEquals(templateFilePath, filePath);
+ }
+
+ @Test
+ public void testGetNoPodTemplateFilePath() {
+ flinkConfig.removeConfig(KubernetesConfigOptions.TASK_MANAGER_POD_TEMPLATE);
+ Optional<File> templateFile = kubernetesTaskManagerParameters.getPodTemplateFilePath();
+ assertFalse(templateFile.isPresent());
+ }
+}
diff --git a/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/utils/TestUtils.java b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/utils/TestUtils.java
new file mode 100644
index 00000000..5a651930
--- /dev/null
+++ b/flink-kubernetes-standalone/src/test/java/org/apache/flink/kubernetes/operator/kubeclient/utils/TestUtils.java
@@ -0,0 +1,92 @@
+/*
+ * 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.kubeclient.utils;
+
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.BlobServerOptions;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.configuration.MemorySize;
+import org.apache.flink.configuration.RestOptions;
+import org.apache.flink.configuration.TaskManagerOptions;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.utils.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Testing utilities. */
+public class TestUtils {
+
+ public static final String CLUSTER_ID = "test-cluster";
+ public static final String SERVICE_ACCOUNT = "flink-operator";
+ public static final String TEST_NAMESPACE = "flink-operator-test";
+
+ public static final String TASK_MANAGER_MEMORY = "2048m";
+ public static final String JOB_MANAGER_MEMORY = "1024m";
+
+ public static final String FLINK_VERSION = "latest";
+ public static final String IMAGE = String.format("flink:%s", FLINK_VERSION);
+ public static final String IMAGE_POLICY = "IfNotPresent";
+
+ public static final int TASK_MANAGER_MEMORY_MB =
+ MemorySize.parse(TASK_MANAGER_MEMORY).getMebiBytes();
+ public static final int JOB_MANAGER_MEMORY_MB =
+ MemorySize.parse(JOB_MANAGER_MEMORY).getMebiBytes();
+
+ public static final int SLOTS_PER_TASK_MANAGER = 2;
+
+ public static final double TASK_MANAGER_CPU = 4;
+ public static final double JOB_MANAGER_CPU = 2;
+
+ public static final Map<String, String> generateTestStringStringMap(
+ String keyPrefix, String valuePrefix, int entries) {
+ Map<String, String> map = new HashMap<>();
+ for (int i = 1; i <= entries; i++) {
+ map.put(keyPrefix + i, valuePrefix + i);
+ }
+ return map;
+ }
+
+ public static ClusterSpecification createClusterSpecification() {
+ return new ClusterSpecification.ClusterSpecificationBuilder()
+ .setMasterMemoryMB(JOB_MANAGER_MEMORY_MB)
+ .setTaskManagerMemoryMB(TASK_MANAGER_MEMORY_MB)
+ .setSlotsPerTaskManager(SLOTS_PER_TASK_MANAGER)
+ .createClusterSpecification();
+ }
+
+ public static Configuration createTestFlinkConfig() {
+ Configuration flinkConf = new Configuration();
+ flinkConf.set(KubernetesConfigOptions.CLUSTER_ID, CLUSTER_ID);
+ flinkConf.set(KubernetesConfigOptions.NAMESPACE, TEST_NAMESPACE);
+ flinkConf.set(KubernetesConfigOptions.KUBERNETES_SERVICE_ACCOUNT, SERVICE_ACCOUNT);
+ flinkConf.set(KubernetesConfigOptions.CONTAINER_IMAGE, IMAGE);
+ flinkConf.set(
+ KubernetesConfigOptions.CONTAINER_IMAGE_PULL_POLICY,
+ KubernetesConfigOptions.ImagePullPolicy.valueOf(IMAGE_POLICY));
+
+ flinkConf.set(KubernetesConfigOptions.JOB_MANAGER_CPU, JOB_MANAGER_CPU);
+ flinkConf.set(KubernetesConfigOptions.TASK_MANAGER_CPU, TASK_MANAGER_CPU);
+
+ flinkConf.setString(
+ TaskManagerOptions.RPC_PORT, String.valueOf(Constants.TASK_MANAGER_RPC_PORT));
+ flinkConf.setString(BlobServerOptions.PORT, String.valueOf(Constants.BLOB_SERVER_PORT));
+ flinkConf.setString(RestOptions.BIND_PORT, String.valueOf(Constants.REST_PORT));
+ return flinkConf;
+ }
+}
diff --git a/pom.xml b/pom.xml
index e548bfdd..6eddd541 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,6 +51,7 @@ under the License.
</scm>
<modules>
+ <module>flink-kubernetes-standalone</module>
<module>flink-kubernetes-shaded</module>
<module>flink-kubernetes-operator</module>
<module>flink-kubernetes-webhook</module>
@@ -62,7 +63,7 @@ under the License.
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
- <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
+ <maven-surefire-plugin.version>3.0.0-M4</maven-surefire-plugin.version>
<maven-failsafe-plugin.version>3.0.0-M5</maven-failsafe-plugin.version>
<maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
<git-commit-id-maven-plugin.version>5.0.0</git-commit-id-maven-plugin.version>
@@ -81,6 +82,9 @@ under the License.
<spotless.version>2.4.2</spotless.version>
<it.skip>true</it.skip>
+
+ <hamcrest.version>1.3</hamcrest.version>
+
</properties>
<dependencyManagement>
[flink-kubernetes-operator] 04/05: [FLINK-27445] Create Flink Standalone Service
Posted by gy...@apache.org.
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
commit 0d05d0ab514387a314179785bb850d41c7dd1e85
Author: Usamah Jassat <us...@amazon.com>
AuthorDate: Fri Jun 17 18:14:38 2022 +0100
[FLINK-27445] Create Flink Standalone Service
---
.../flink/kubernetes/operator/FlinkOperator.java | 16 +-
.../operator/config/FlinkConfigBuilder.java | 2 +-
.../operator/crd/spec/FlinkDeploymentSpec.java | 3 +
.../crd/spec/KubernetesDeploymentMode.java | 47 ++
.../observer/deployment/ObserverFactory.java | 20 +-
.../observer/sessionjob/SessionJobObserver.java | 85 +--
.../AbstractFlinkResourceReconciler.java | 34 +-
.../deployment/AbstractJobReconciler.java | 28 +-
.../deployment/ApplicationReconciler.java | 38 +-
.../reconciler/deployment/ReconcilerFactory.java | 12 +-
.../reconciler/deployment/SessionReconciler.java | 31 +-
.../sessionjob/SessionJobReconciler.java | 27 +-
...FlinkService.java => AbstractFlinkService.java} | 507 +++++++-------
.../kubernetes/operator/service/FlinkService.java | 729 +--------------------
.../operator/service/FlinkServiceFactory.java | 66 ++
.../operator/service/NativeFlinkService.java | 178 +++++
.../operator/service/StandaloneFlinkService.java | 163 +++++
.../kubernetes/operator/utils/FlinkUtils.java | 135 ----
.../kubernetes/operator/TestingFlinkService.java | 22 +-
.../operator/TestingFlinkServiceFactory.java | 52 ++
.../TestingFlinkDeploymentController.java | 7 +-
.../sessionjob/SessionJobObserverTest.java | 9 +-
.../deployment/ApplicationReconcilerTest.java | 6 +-
.../sessionjob/SessionJobReconcilerTest.java | 13 +-
...erviceTest.java => NativeFlinkServiceTest.java} | 12 +-
.../service/StandaloneFlinkServiceTest.java | 138 ++++
.../crds/flinkdeployments.flink.apache.org-v1.yml | 5 +
27 files changed, 1157 insertions(+), 1228 deletions(-)
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/FlinkOperator.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/FlinkOperator.java
index 8933a6c7..4d2f4674 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/FlinkOperator.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/FlinkOperator.java
@@ -37,7 +37,7 @@ import org.apache.flink.kubernetes.operator.observer.deployment.ObserverFactory;
import org.apache.flink.kubernetes.operator.observer.sessionjob.SessionJobObserver;
import org.apache.flink.kubernetes.operator.reconciler.deployment.ReconcilerFactory;
import org.apache.flink.kubernetes.operator.reconciler.sessionjob.SessionJobReconciler;
-import org.apache.flink.kubernetes.operator.service.FlinkService;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EnvUtils;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.KubernetesClientUtils;
@@ -68,7 +68,7 @@ public class FlinkOperator {
private final Operator operator;
private final KubernetesClient client;
- private final FlinkService flinkService;
+ private final FlinkServiceFactory flinkServiceFactory;
private final FlinkConfigManager configManager;
private final Set<FlinkResourceValidator> validators;
@VisibleForTesting final Set<RegisteredController> registeredControllers = new HashSet<>();
@@ -86,7 +86,7 @@ public class FlinkOperator {
KubernetesClientUtils.getKubernetesClient(
configManager.getOperatorConfiguration(), this.metricGroup);
this.operator = new Operator(client, this::overrideOperatorConfigs);
- this.flinkService = new FlinkService(client, configManager);
+ this.flinkServiceFactory = new FlinkServiceFactory(client, configManager);
this.validators = ValidatorUtils.discoverValidators(configManager);
this.listeners = ListenerUtils.discoverListeners(configManager);
PluginManager pluginManager =
@@ -126,9 +126,10 @@ public class FlinkOperator {
var eventRecorder = EventRecorder.create(client, listeners);
var reconcilerFactory =
new ReconcilerFactory(
- client, flinkService, configManager, eventRecorder, statusRecorder);
+ client, flinkServiceFactory, configManager, eventRecorder, statusRecorder);
var observerFactory =
- new ObserverFactory(flinkService, configManager, statusRecorder, eventRecorder);
+ new ObserverFactory(
+ flinkServiceFactory, configManager, statusRecorder, eventRecorder);
var controller =
new FlinkDeploymentController(
@@ -149,9 +150,10 @@ public class FlinkOperator {
client, new MetricManager<>(metricGroup, configManager), listeners);
var reconciler =
new SessionJobReconciler(
- client, flinkService, configManager, eventRecorder, statusRecorder);
+ client, flinkServiceFactory, configManager, eventRecorder, statusRecorder);
var observer =
- new SessionJobObserver(flinkService, configManager, statusRecorder, eventRecorder);
+ new SessionJobObserver(
+ flinkServiceFactory, configManager, statusRecorder, eventRecorder);
var controller =
new FlinkSessionJobController(
configManager, validators, reconciler, observer, statusRecorder);
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java
index 2dfd9fd9..8941dd51 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java
@@ -260,7 +260,7 @@ public class FlinkConfigBuilder {
return effectiveConfig;
}
- protected static Configuration buildFrom(
+ public static Configuration buildFrom(
String namespace, String clusterId, FlinkDeploymentSpec spec, Configuration flinkConfig)
throws IOException, URISyntaxException {
return new FlinkConfigBuilder(namespace, clusterId, spec, flinkConfig)
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/FlinkDeploymentSpec.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/FlinkDeploymentSpec.java
index 14f50afa..4b81f1b8 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/FlinkDeploymentSpec.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/FlinkDeploymentSpec.java
@@ -70,4 +70,7 @@ public class FlinkDeploymentSpec extends AbstractFlinkSpec {
* configContent.
*/
private Map<String, String> logConfiguration;
+
+ /** Deployment mode of the Flink cluster, native or standalone. */
+ private KubernetesDeploymentMode mode;
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/KubernetesDeploymentMode.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/KubernetesDeploymentMode.java
new file mode 100644
index 00000000..5537be61
--- /dev/null
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/crd/spec/KubernetesDeploymentMode.java
@@ -0,0 +1,47 @@
+/*
+ * 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.crd.spec;
+
+import org.apache.flink.annotation.Experimental;
+import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/** Enum to control Flink deployment mode on Kubernetes. */
+@Experimental
+public enum KubernetesDeploymentMode {
+
+ /**
+ * Deploys Flink using Flinks native Kubernetes support. Only supported for newer versions of
+ * Flink
+ */
+ @JsonProperty("native")
+ NATIVE,
+
+ /** Deploys Flink on-top of kubernetes in standalone mode. */
+ @JsonProperty("standalone")
+ STANDALONE;
+
+ public static KubernetesDeploymentMode getDeploymentMode(FlinkDeployment flinkDeployment) {
+ return getDeploymentMode(flinkDeployment.getSpec());
+ }
+
+ public static KubernetesDeploymentMode getDeploymentMode(FlinkDeploymentSpec spec) {
+ return spec.getMode() == null ? KubernetesDeploymentMode.NATIVE : spec.getMode();
+ }
+}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java
index 605c2769..6c2c203d 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java
@@ -22,28 +22,28 @@ import org.apache.flink.kubernetes.operator.config.Mode;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.observer.Observer;
-import org.apache.flink.kubernetes.operator.service.FlinkService;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-/** The factory to create the observer based ob the {@link FlinkDeployment} mode. */
+/** The factory to create the observer based on the {@link FlinkDeployment} mode. */
public class ObserverFactory {
- private final FlinkService flinkService;
+ private final FlinkServiceFactory flinkServiceFactory;
private final FlinkConfigManager configManager;
private final StatusRecorder<FlinkDeploymentStatus> statusRecorder;
private final EventRecorder eventRecorder;
private final Map<Mode, Observer<FlinkDeployment>> observerMap;
public ObserverFactory(
- FlinkService flinkService,
+ FlinkServiceFactory flinkServiceFactory,
FlinkConfigManager configManager,
StatusRecorder<FlinkDeploymentStatus> statusRecorder,
EventRecorder eventRecorder) {
- this.flinkService = flinkService;
+ this.flinkServiceFactory = flinkServiceFactory;
this.configManager = configManager;
this.statusRecorder = statusRecorder;
this.eventRecorder = eventRecorder;
@@ -56,10 +56,16 @@ public class ObserverFactory {
mode -> {
switch (mode) {
case SESSION:
- return new SessionObserver(flinkService, configManager, eventRecorder);
+ return new SessionObserver(
+ flinkServiceFactory.getOrCreate(flinkApp),
+ configManager,
+ eventRecorder);
case APPLICATION:
return new ApplicationObserver(
- flinkService, configManager, statusRecorder, eventRecorder);
+ flinkServiceFactory.getOrCreate(flinkApp),
+ configManager,
+ statusRecorder,
+ eventRecorder);
default:
throw new UnsupportedOperationException(
String.format("Unsupported running mode: %s", mode));
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserver.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserver.java
index 5ba397c4..c3aeec46 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserver.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/sessionjob/SessionJobObserver.java
@@ -32,6 +32,7 @@ import org.apache.flink.kubernetes.operator.observer.context.VoidObserverContext
import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
import org.apache.flink.kubernetes.operator.reconciler.sessionjob.SessionJobReconciler;
import org.apache.flink.kubernetes.operator.service.FlinkService;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.SavepointUtils;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
@@ -52,52 +53,51 @@ import java.util.stream.Collectors;
public class SessionJobObserver implements Observer<FlinkSessionJob> {
private static final Logger LOG = LoggerFactory.getLogger(SessionJobObserver.class);
+ private final FlinkServiceFactory flinkServiceFactory;
private final FlinkConfigManager configManager;
private final EventRecorder eventRecorder;
- private final SavepointObserver<FlinkSessionJobStatus> savepointObserver;
- private final JobStatusObserver<VoidObserverContext> jobStatusObserver;
- private final FlinkService flinkService;
+ private final StatusRecorder<FlinkSessionJobStatus> statusRecorder;
public SessionJobObserver(
- FlinkService flinkService,
+ FlinkServiceFactory flinkServiceFactory,
FlinkConfigManager configManager,
StatusRecorder<FlinkSessionJobStatus> statusRecorder,
EventRecorder eventRecorder) {
+ this.flinkServiceFactory = flinkServiceFactory;
this.configManager = configManager;
this.eventRecorder = eventRecorder;
- this.flinkService = flinkService;
- this.savepointObserver =
- new SavepointObserver<>(flinkService, configManager, statusRecorder, eventRecorder);
- this.jobStatusObserver =
- new JobStatusObserver<>(flinkService, eventRecorder) {
- @Override
- protected void onTimeout(VoidObserverContext sessionJobObserverContext) {}
-
- @Override
- protected Optional<JobStatusMessage> filterTargetJob(
- JobStatus status, List<JobStatusMessage> clusterJobStatuses) {
- var jobId =
- Preconditions.checkNotNull(
- status.getJobId(),
- "The jobID to be observed should not be null");
- var matchedList =
- clusterJobStatuses.stream()
- .filter(job -> job.getJobId().toHexString().equals(jobId))
- .collect(Collectors.toList());
- Preconditions.checkArgument(
- matchedList.size() <= 1,
- String.format(
- "Expected one job for JobID: %s, but %d founded",
- status.getJobId(), matchedList.size()));
-
- if (matchedList.size() == 0) {
- LOG.info("No job found for JobID: {}", jobId);
- return Optional.empty();
- } else {
- return Optional.of(matchedList.get(0));
- }
- }
- };
+ this.statusRecorder = statusRecorder;
+ }
+
+ private JobStatusObserver<VoidObserverContext> getJobStatusObserver(FlinkService flinkService) {
+ return new JobStatusObserver<>(flinkService, eventRecorder) {
+ @Override
+ protected void onTimeout(VoidObserverContext sessionJobObserverContext) {}
+
+ @Override
+ protected Optional<JobStatusMessage> filterTargetJob(
+ JobStatus status, List<JobStatusMessage> clusterJobStatuses) {
+ var jobId =
+ Preconditions.checkNotNull(
+ status.getJobId(), "The jobID to be observed should not be null");
+ var matchedList =
+ clusterJobStatuses.stream()
+ .filter(job -> job.getJobId().toHexString().equals(jobId))
+ .collect(Collectors.toList());
+ Preconditions.checkArgument(
+ matchedList.size() <= 1,
+ String.format(
+ "Expected one job for JobID: %s, but %d founded",
+ status.getJobId(), matchedList.size()));
+
+ if (matchedList.size() == 0) {
+ LOG.info("No job found for JobID: {}", jobId);
+ return Optional.empty();
+ } else {
+ return Optional.of(matchedList.get(0));
+ }
+ }
+ };
}
@Override
@@ -112,11 +112,13 @@ public class SessionJobObserver implements Observer<FlinkSessionJob> {
return;
}
+ FlinkService flinkService = flinkServiceFactory.getOrCreate(flinkDepOpt.get());
+ var jobStatusObserver = getJobStatusObserver(flinkService);
var deployedConfig =
configManager.getSessionJobConfig(flinkDepOpt.get(), flinkSessionJob.getSpec());
var reconciliationStatus = flinkSessionJob.getStatus().getReconciliationStatus();
if (reconciliationStatus.getState() == ReconciliationState.UPGRADING) {
- checkIfAlreadyUpgraded(flinkSessionJob, deployedConfig);
+ checkIfAlreadyUpgraded(flinkSessionJob, deployedConfig, flinkService);
if (reconciliationStatus.getState() == ReconciliationState.UPGRADING) {
return;
}
@@ -127,13 +129,18 @@ public class SessionJobObserver implements Observer<FlinkSessionJob> {
flinkSessionJob, deployedConfig, VoidObserverContext.INSTANCE);
if (jobFound) {
+ SavepointObserver savepointObserver =
+ new SavepointObserver<>(
+ flinkService, configManager, statusRecorder, eventRecorder);
savepointObserver.observeSavepointStatus(flinkSessionJob, deployedConfig);
}
SavepointUtils.resetTriggerIfJobNotRunning(flinkSessionJob, eventRecorder);
}
private void checkIfAlreadyUpgraded(
- FlinkSessionJob flinkSessionJob, Configuration deployedConfig) {
+ FlinkSessionJob flinkSessionJob,
+ Configuration deployedConfig,
+ FlinkService flinkService) {
var uid = flinkSessionJob.getMetadata().getUid();
Collection<JobStatusMessage> jobStatusMessages;
try {
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractFlinkResourceReconciler.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractFlinkResourceReconciler.java
index 7758ac6f..3d458432 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractFlinkResourceReconciler.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractFlinkResourceReconciler.java
@@ -65,7 +65,6 @@ public abstract class AbstractFlinkResourceReconciler<
protected final EventRecorder eventRecorder;
protected final StatusRecorder<STATUS> statusRecorder;
protected final KubernetesClient kubernetesClient;
- protected final FlinkService flinkService;
public static final String MSG_SUSPENDED = "Suspending existing deployment.";
public static final String MSG_SPEC_CHANGED = "Detected spec change, starting reconciliation.";
@@ -74,12 +73,10 @@ public abstract class AbstractFlinkResourceReconciler<
public AbstractFlinkResourceReconciler(
KubernetesClient kubernetesClient,
- FlinkService flinkService,
FlinkConfigManager configManager,
EventRecorder eventRecorder,
StatusRecorder<STATUS> statusRecorder) {
this.kubernetesClient = kubernetesClient;
- this.flinkService = flinkService;
this.configManager = configManager;
this.eventRecorder = eventRecorder;
this.statusRecorder = statusRecorder;
@@ -112,6 +109,7 @@ public abstract class AbstractFlinkResourceReconciler<
cr,
spec,
status,
+ ctx,
deployConfig,
Optional.ofNullable(spec.getJob()).map(JobSpec::getInitialSavepointPath),
false);
@@ -128,7 +126,7 @@ public abstract class AbstractFlinkResourceReconciler<
reconciliationStatus.getState() == ReconciliationState.UPGRADING
|| !currentDeploySpec.equals(lastReconciledSpec);
var observeConfig = getObserveConfig(cr, ctx);
-
+ var flinkService = getFlinkService(cr, ctx);
if (specChanged) {
if (checkNewSpecAlreadyDeployed(cr, deployConfig)) {
return;
@@ -142,8 +140,8 @@ public abstract class AbstractFlinkResourceReconciler<
EventRecorder.Component.JobManagerDeployment,
MSG_SPEC_CHANGED);
}
- reconcileSpecChange(cr, observeConfig, deployConfig);
- } else if (shouldRollBack(cr, observeConfig)) {
+ reconcileSpecChange(cr, ctx, observeConfig, deployConfig);
+ } else if (shouldRollBack(cr, observeConfig, flinkService)) {
// Rollbacks are executed in two steps, we initiate it first then return
if (initiateRollBack(status)) {
return;
@@ -156,7 +154,7 @@ public abstract class AbstractFlinkResourceReconciler<
EventRecorder.Component.JobManagerDeployment,
MSG_ROLLBACK);
rollback(cr, ctx, observeConfig);
- } else if (!reconcileOtherChanges(cr, observeConfig)) {
+ } else if (!reconcileOtherChanges(cr, ctx, observeConfig)) {
LOG.info("Resource fully reconciled, nothing to do...");
}
}
@@ -202,7 +200,8 @@ public abstract class AbstractFlinkResourceReconciler<
* @throws Exception Error during spec upgrade.
*/
protected abstract void reconcileSpecChange(
- CR cr, Configuration observeConfig, Configuration deployConfig) throws Exception;
+ CR cr, Context ctx, Configuration observeConfig, Configuration deployConfig)
+ throws Exception;
/**
* Rollback deployed resource to the last stable spec.
@@ -224,8 +223,8 @@ public abstract class AbstractFlinkResourceReconciler<
* @return True if any further reconciliation action was taken.
* @throws Exception Error during reconciliation.
*/
- protected abstract boolean reconcileOtherChanges(CR cr, Configuration observeConfig)
- throws Exception;
+ protected abstract boolean reconcileOtherChanges(
+ CR cr, Context context, Configuration observeConfig) throws Exception;
@Override
public final DeleteControl cleanup(CR resource, Context context) {
@@ -239,6 +238,7 @@ public abstract class AbstractFlinkResourceReconciler<
* @param spec Spec that should be deployed to Kubernetes.
* @param status Status object of the resource
* @param deployConfig Flink conf for the deployment.
+ * @param ctx Reconciliation context.
* @param savepoint Optional savepoint path for applications and session jobs.
* @param requireHaMetadata Flag used by application deployments to validate HA metadata
* @throws Exception Error during deployment.
@@ -247,6 +247,7 @@ public abstract class AbstractFlinkResourceReconciler<
CR relatedResource,
SPEC spec,
STATUS status,
+ Context ctx,
Configuration deployConfig,
Optional<String> savepoint,
boolean requireHaMetadata)
@@ -261,6 +262,15 @@ public abstract class AbstractFlinkResourceReconciler<
*/
protected abstract DeleteControl cleanupInternal(CR resource, Context context);
+ /**
+ * Get the Flink service related to the resource and context.
+ *
+ * @param resource
+ * @param context
+ * @return
+ */
+ protected abstract FlinkService getFlinkService(CR resource, Context context);
+
/**
* Checks whether the desired spec already matches the currently deployed spec. If they match
* the resource status is updated to reflect successful reconciliation.
@@ -296,7 +306,9 @@ public abstract class AbstractFlinkResourceReconciler<
* @return True if the resource should be rolled back.
*/
private boolean shouldRollBack(
- AbstractFlinkResource<SPEC, STATUS> resource, Configuration configuration) {
+ AbstractFlinkResource<SPEC, STATUS> resource,
+ Configuration configuration,
+ FlinkService flinkService) {
var reconciliationStatus = resource.getStatus().getReconciliationStatus();
if (reconciliationStatus.getState() == ReconciliationState.ROLLING_BACK) {
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractJobReconciler.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractJobReconciler.java
index 7f4cbdf1..e3a72a05 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractJobReconciler.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/AbstractJobReconciler.java
@@ -28,7 +28,6 @@ import org.apache.flink.kubernetes.operator.crd.status.CommonStatus;
import org.apache.flink.kubernetes.operator.crd.status.JobStatus;
import org.apache.flink.kubernetes.operator.crd.status.ReconciliationState;
import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
-import org.apache.flink.kubernetes.operator.service.FlinkService;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.SavepointUtils;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
@@ -54,11 +53,10 @@ public abstract class AbstractJobReconciler<
public AbstractJobReconciler(
KubernetesClient kubernetesClient,
- FlinkService flinkService,
FlinkConfigManager configManager,
EventRecorder eventRecorder,
StatusRecorder<STATUS> statusRecorder) {
- super(kubernetesClient, flinkService, configManager, eventRecorder, statusRecorder);
+ super(kubernetesClient, configManager, eventRecorder, statusRecorder);
}
@Override
@@ -80,8 +78,8 @@ public abstract class AbstractJobReconciler<
@Override
protected void reconcileSpecChange(
- CR resource, Configuration observeConfig, Configuration deployConfig) throws Exception {
- var deployMeta = resource.getMetadata();
+ CR resource, Context ctx, Configuration observeConfig, Configuration deployConfig)
+ throws Exception {
STATUS status = resource.getStatus();
var reconciliationStatus = status.getReconciliationStatus();
SPEC lastReconciledSpec = reconciliationStatus.deserializeLastReconciledSpec();
@@ -107,7 +105,7 @@ public abstract class AbstractJobReconciler<
MSG_SUSPENDED);
// We must record the upgrade mode used to the status later
currentDeploySpec.getJob().setUpgradeMode(availableUpgradeMode.get());
- cancelJob(resource, availableUpgradeMode.get(), observeConfig);
+ cancelJob(resource, ctx, availableUpgradeMode.get(), observeConfig);
if (desiredJobState == JobState.RUNNING) {
ReconciliationUtils.updateStatusBeforeDeploymentAttempt(resource, deployConfig);
} else {
@@ -123,6 +121,7 @@ public abstract class AbstractJobReconciler<
resource,
currentDeploySpec,
status,
+ ctx,
deployConfig,
// We decide to enforce HA based on how job was previously suspended
lastReconciledSpec.getJob().getUpgradeMode() == UpgradeMode.LAST_STATE);
@@ -174,6 +173,7 @@ public abstract class AbstractJobReconciler<
CR resource,
SPEC spec,
STATUS status,
+ Context ctx,
Configuration deployConfig,
boolean requireHaMetadata)
throws Exception {
@@ -185,11 +185,11 @@ public abstract class AbstractJobReconciler<
.flatMap(s -> Optional.ofNullable(s.getLocation()));
}
- deploy(resource, spec, status, deployConfig, savepointOpt, requireHaMetadata);
+ deploy(resource, spec, status, ctx, deployConfig, savepointOpt, requireHaMetadata);
}
@Override
- protected void rollback(CR resource, Context context, Configuration observeConfig)
+ protected void rollback(CR resource, Context ctx, Configuration observeConfig)
throws Exception {
var reconciliationStatus = resource.getStatus().getReconciliationStatus();
var rollbackSpec = reconciliationStatus.deserializeLastStableSpec();
@@ -198,6 +198,7 @@ public abstract class AbstractJobReconciler<
cancelJob(
resource,
+ ctx,
upgradeMode == UpgradeMode.STATELESS
? UpgradeMode.STATELESS
: UpgradeMode.LAST_STATE,
@@ -207,16 +208,18 @@ public abstract class AbstractJobReconciler<
resource,
rollbackSpec,
resource.getStatus(),
- getDeployConfig(resource.getMetadata(), rollbackSpec, context),
+ ctx,
+ getDeployConfig(resource.getMetadata(), rollbackSpec, ctx),
upgradeMode != UpgradeMode.STATELESS);
reconciliationStatus.setState(ReconciliationState.ROLLED_BACK);
}
@Override
- public boolean reconcileOtherChanges(CR resource, Configuration observeConfig)
+ public boolean reconcileOtherChanges(CR resource, Context context, Configuration observeConfig)
throws Exception {
- return SavepointUtils.triggerSavepointIfNeeded(flinkService, resource, observeConfig);
+ return SavepointUtils.triggerSavepointIfNeeded(
+ getFlinkService(resource, context), resource, observeConfig);
}
/**
@@ -228,5 +231,6 @@ public abstract class AbstractJobReconciler<
* @throws Exception Error during cancellation.
*/
protected abstract void cancelJob(
- CR resource, UpgradeMode upgradeMode, Configuration observeConfig) throws Exception;
+ CR resource, Context ctx, UpgradeMode upgradeMode, Configuration observeConfig)
+ throws Exception;
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java
index df0efe37..c03ddc98 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java
@@ -33,6 +33,7 @@ import org.apache.flink.kubernetes.operator.service.FlinkService;
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.kubernetes.operator.utils.SavepointUtils;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
import org.apache.flink.runtime.highavailability.JobResultStoreOptions;
import org.apache.flink.runtime.jobgraph.SavepointConfigOptions;
@@ -53,6 +54,7 @@ public class ApplicationReconciler
extends AbstractJobReconciler<FlinkDeployment, FlinkDeploymentSpec, FlinkDeploymentStatus> {
private static final Logger LOG = LoggerFactory.getLogger(ApplicationReconciler.class);
+ protected final FlinkService flinkService;
public ApplicationReconciler(
KubernetesClient kubernetesClient,
@@ -60,7 +62,13 @@ public class ApplicationReconciler
FlinkConfigManager configManager,
EventRecorder eventRecorder,
StatusRecorder<FlinkDeploymentStatus> statusRecorder) {
- super(kubernetesClient, flinkService, configManager, eventRecorder, statusRecorder);
+ super(kubernetesClient, configManager, eventRecorder, statusRecorder);
+ this.flinkService = flinkService;
+ }
+
+ @Override
+ protected FlinkService getFlinkService(FlinkDeployment resource, Context context) {
+ return flinkService;
}
@Override
@@ -118,6 +126,7 @@ public class ApplicationReconciler
FlinkDeployment relatedResource,
FlinkDeploymentSpec spec,
FlinkDeploymentStatus status,
+ Context ctx,
Configuration deployConfig,
Optional<String> savepoint,
boolean requireHaMetadata)
@@ -138,13 +147,7 @@ public class ApplicationReconciler
}
LOG.info("Deleting deployment with terminated application before new deployment");
flinkService.deleteClusterDeployment(relatedResource.getMetadata(), status, true);
- FlinkUtils.waitForClusterShutdown(
- kubernetesClient,
- deployConfig,
- configManager
- .getOperatorConfiguration()
- .getFlinkShutdownClusterTimeout()
- .toSeconds());
+ flinkService.waitForClusterShutdown(deployConfig);
}
eventRecorder.triggerEvent(
relatedResource,
@@ -162,7 +165,10 @@ public class ApplicationReconciler
@Override
protected void cancelJob(
- FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration observeConfig)
+ FlinkDeployment deployment,
+ Context ctx,
+ UpgradeMode upgradeMode,
+ Configuration observeConfig)
throws Exception {
flinkService.cancelJob(deployment, upgradeMode, observeConfig);
}
@@ -186,24 +192,24 @@ public class ApplicationReconciler
}
@Override
- public boolean reconcileOtherChanges(FlinkDeployment deployment, Configuration observeConfig)
- throws Exception {
- if (super.reconcileOtherChanges(deployment, observeConfig)) {
+ public boolean reconcileOtherChanges(
+ FlinkDeployment deployment, Context ctx, Configuration observeConfig) throws Exception {
+ if (SavepointUtils.triggerSavepointIfNeeded(flinkService, deployment, observeConfig)) {
return true;
}
if (shouldRecoverDeployment(observeConfig, deployment)) {
- recoverJmDeployment(deployment, observeConfig);
+ recoverJmDeployment(deployment, ctx, observeConfig);
return true;
}
return false;
}
- private void recoverJmDeployment(FlinkDeployment deployment, Configuration observeConfig)
- throws Exception {
+ private void recoverJmDeployment(
+ FlinkDeployment deployment, Context ctx, Configuration observeConfig) throws Exception {
LOG.info("Missing Flink Cluster deployment, trying to recover...");
FlinkDeploymentSpec specToRecover = ReconciliationUtils.getDeployedSpec(deployment);
- restoreJob(deployment, specToRecover, deployment.getStatus(), observeConfig, true);
+ restoreJob(deployment, specToRecover, deployment.getStatus(), ctx, observeConfig, true);
}
@Override
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java
index 2e556fd1..419e3e41 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java
@@ -22,7 +22,7 @@ import org.apache.flink.kubernetes.operator.config.Mode;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.reconciler.Reconciler;
-import org.apache.flink.kubernetes.operator.service.FlinkService;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
@@ -35,7 +35,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class ReconcilerFactory {
private final KubernetesClient kubernetesClient;
- private final FlinkService flinkService;
+ private final FlinkServiceFactory flinkServiceFactory;
private final FlinkConfigManager configManager;
private final EventRecorder eventRecorder;
private final StatusRecorder<FlinkDeploymentStatus> deploymentStatusRecorder;
@@ -43,12 +43,12 @@ public class ReconcilerFactory {
public ReconcilerFactory(
KubernetesClient kubernetesClient,
- FlinkService flinkService,
+ FlinkServiceFactory flinkServiceFactory,
FlinkConfigManager configManager,
EventRecorder eventRecorder,
StatusRecorder<FlinkDeploymentStatus> deploymentStatusRecorder) {
this.kubernetesClient = kubernetesClient;
- this.flinkService = flinkService;
+ this.flinkServiceFactory = flinkServiceFactory;
this.configManager = configManager;
this.eventRecorder = eventRecorder;
this.deploymentStatusRecorder = deploymentStatusRecorder;
@@ -63,14 +63,14 @@ public class ReconcilerFactory {
case SESSION:
return new SessionReconciler(
kubernetesClient,
- flinkService,
+ flinkServiceFactory.getOrCreate(flinkApp),
configManager,
eventRecorder,
deploymentStatusRecorder);
case APPLICATION:
return new ApplicationReconciler(
kubernetesClient,
- flinkService,
+ flinkServiceFactory.getOrCreate(flinkApp),
configManager,
eventRecorder,
deploymentStatusRecorder);
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/SessionReconciler.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/SessionReconciler.java
index 791db2fe..527cb8b5 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/SessionReconciler.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/SessionReconciler.java
@@ -29,7 +29,6 @@ import org.apache.flink.kubernetes.operator.crd.status.ReconciliationStatus;
import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
import org.apache.flink.kubernetes.operator.service.FlinkService;
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.kubernetes.operator.utils.StatusRecorder;
@@ -52,6 +51,8 @@ public class SessionReconciler
extends AbstractFlinkResourceReconciler<
FlinkDeployment, FlinkDeploymentSpec, FlinkDeploymentStatus> {
+ protected final FlinkService flinkService;
+
private static final Logger LOG = LoggerFactory.getLogger(SessionReconciler.class);
public SessionReconciler(
@@ -60,7 +61,13 @@ public class SessionReconciler
FlinkConfigManager configManager,
EventRecorder eventRecorder,
StatusRecorder<FlinkDeploymentStatus> statusRecorder) {
- super(kubernetesClient, flinkService, configManager, eventRecorder, statusRecorder);
+ super(kubernetesClient, configManager, eventRecorder, statusRecorder);
+ this.flinkService = flinkService;
+ }
+
+ @Override
+ protected FlinkService getFlinkService(FlinkDeployment resource, Context context) {
+ return flinkService;
}
@Override
@@ -82,7 +89,10 @@ public class SessionReconciler
@Override
protected void reconcileSpecChange(
- FlinkDeployment deployment, Configuration observeConfig, Configuration deployConfig)
+ FlinkDeployment deployment,
+ Context ctx,
+ Configuration observeConfig,
+ Configuration deployConfig)
throws Exception {
deleteSessionCluster(deployment, observeConfig);
@@ -94,6 +104,7 @@ public class SessionReconciler
deployment,
deployment.getSpec(),
deployment.getStatus(),
+ ctx,
deployConfig,
Optional.empty(),
false);
@@ -103,13 +114,7 @@ public class SessionReconciler
private void deleteSessionCluster(FlinkDeployment deployment, Configuration effectiveConfig) {
flinkService.deleteClusterDeployment(
deployment.getMetadata(), deployment.getStatus(), false);
- FlinkUtils.waitForClusterShutdown(
- kubernetesClient,
- effectiveConfig,
- configManager
- .getOperatorConfiguration()
- .getFlinkShutdownClusterTimeout()
- .toSeconds());
+ flinkService.waitForClusterShutdown(effectiveConfig);
}
@Override
@@ -117,6 +122,7 @@ public class SessionReconciler
FlinkDeployment cr,
FlinkDeploymentSpec spec,
FlinkDeploymentStatus status,
+ Context ctx,
Configuration deployConfig,
Optional<String> savepoint,
boolean requireHaMetadata)
@@ -141,6 +147,7 @@ public class SessionReconciler
deployment,
rollbackSpec,
deployment.getStatus(),
+ ctx,
rollbackConfig,
Optional.empty(),
false);
@@ -149,8 +156,8 @@ public class SessionReconciler
}
@Override
- public boolean reconcileOtherChanges(FlinkDeployment flinkApp, Configuration observeConfig)
- throws Exception {
+ public boolean reconcileOtherChanges(
+ FlinkDeployment flinkApp, Context ctx, Configuration observeConfig) throws Exception {
if (shouldRecoverDeployment(observeConfig, flinkApp)) {
recoverSession(flinkApp, observeConfig);
return true;
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconciler.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconciler.java
index e04d3bd1..f0dd2520 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconciler.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/sessionjob/SessionJobReconciler.java
@@ -28,6 +28,7 @@ import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatu
import org.apache.flink.kubernetes.operator.crd.status.JobStatus;
import org.apache.flink.kubernetes.operator.reconciler.deployment.AbstractJobReconciler;
import org.apache.flink.kubernetes.operator.service.FlinkService;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
@@ -45,14 +46,27 @@ public class SessionJobReconciler
extends AbstractJobReconciler<FlinkSessionJob, FlinkSessionJobSpec, FlinkSessionJobStatus> {
private static final Logger LOG = LoggerFactory.getLogger(SessionJobReconciler.class);
+ private final FlinkServiceFactory flinkServiceFactory;
public SessionJobReconciler(
KubernetesClient kubernetesClient,
- FlinkService flinkService,
+ FlinkServiceFactory flinkServiceFactory,
FlinkConfigManager configManager,
EventRecorder eventRecorder,
StatusRecorder<FlinkSessionJobStatus> statusRecorder) {
- super(kubernetesClient, flinkService, configManager, eventRecorder, statusRecorder);
+ super(kubernetesClient, configManager, eventRecorder, statusRecorder);
+ this.flinkServiceFactory = flinkServiceFactory;
+ }
+
+ @Override
+ protected FlinkService getFlinkService(FlinkSessionJob resource, Context context) {
+ Optional<FlinkDeployment> deploymentOpt =
+ context.getSecondaryResource(FlinkDeployment.class);
+
+ if (sessionClusterReady(deploymentOpt)) {
+ return flinkServiceFactory.getOrCreate(deploymentOpt.get());
+ }
+ return null;
}
@Override
@@ -84,10 +98,12 @@ public class SessionJobReconciler
FlinkSessionJob cr,
FlinkSessionJobSpec sessionJobSpec,
FlinkSessionJobStatus status,
+ Context ctx,
Configuration deployConfig,
Optional<String> savepoint,
boolean requireHaMetadata)
throws Exception {
+ FlinkService flinkService = getFlinkService(cr, ctx);
var jobID =
flinkService.submitJobToSessionCluster(
cr.getMetadata(), sessionJobSpec, deployConfig, savepoint.orElse(null));
@@ -101,8 +117,12 @@ public class SessionJobReconciler
@Override
protected void cancelJob(
- FlinkSessionJob resource, UpgradeMode upgradeMode, Configuration observeConfig)
+ FlinkSessionJob resource,
+ Context ctx,
+ UpgradeMode upgradeMode,
+ Configuration observeConfig)
throws Exception {
+ FlinkService flinkService = getFlinkService(resource, ctx);
flinkService.cancelSessionJob(resource, upgradeMode, observeConfig);
}
@@ -117,6 +137,7 @@ public class SessionJobReconciler
try {
cancelJob(
sessionJob,
+ context,
UpgradeMode.STATELESS,
getObserveConfig(sessionJob, context));
} catch (Exception e) {
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/AbstractFlinkService.java
similarity index 87%
copy from flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkService.java
copy to flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/AbstractFlinkService.java
index 2deccaee..afb7bf27 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/AbstractFlinkService.java
@@ -20,13 +20,6 @@ package org.apache.flink.kubernetes.operator.service;
import org.apache.flink.annotation.VisibleForTesting;
import org.apache.flink.api.common.JobID;
import org.apache.flink.api.common.JobStatus;
-import org.apache.flink.client.cli.ApplicationDeployer;
-import org.apache.flink.client.deployment.ClusterClientFactory;
-import org.apache.flink.client.deployment.ClusterClientServiceLoader;
-import org.apache.flink.client.deployment.ClusterDescriptor;
-import org.apache.flink.client.deployment.DefaultClusterClientServiceLoader;
-import org.apache.flink.client.deployment.application.ApplicationConfiguration;
-import org.apache.flink.client.deployment.application.cli.ApplicationClusterDeployer;
import org.apache.flink.client.program.ClusterClient;
import org.apache.flink.client.program.rest.RestClusterClient;
import org.apache.flink.configuration.CheckpointingOptions;
@@ -43,7 +36,6 @@ import org.apache.flink.kubernetes.operator.crd.spec.FlinkSessionJobSpec;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkVersion;
import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
-import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatus;
import org.apache.flink.kubernetes.operator.crd.status.Savepoint;
import org.apache.flink.kubernetes.operator.crd.status.SavepointTriggerType;
@@ -94,6 +86,7 @@ import org.apache.flink.util.concurrent.ExecutorThreadFactory;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.PodList;
+import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
@@ -106,6 +99,7 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
@@ -123,112 +117,42 @@ import java.util.stream.Collectors;
import static org.apache.flink.kubernetes.operator.config.FlinkConfigBuilder.FLINK_VERSION;
-/** Service for submitting and interacting with Flink clusters and jobs. */
-public class FlinkService {
+/**
+ * An abstract {@link FlinkService} containing some common implementations for the native and
+ * standalone Flink Services.
+ */
+public abstract class AbstractFlinkService implements FlinkService {
- private static final Logger LOG = LoggerFactory.getLogger(FlinkService.class);
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractFlinkService.class);
- private final KubernetesClient kubernetesClient;
- private final ArtifactManager artifactManager;
- private final FlinkConfigManager configManager;
+ protected final KubernetesClient kubernetesClient;
+ protected final FlinkConfigManager configManager;
private final ExecutorService executorService;
+ protected final ArtifactManager artifactManager;
- public FlinkService(KubernetesClient kubernetesClient, FlinkConfigManager configManager) {
+ public AbstractFlinkService(
+ KubernetesClient kubernetesClient, FlinkConfigManager configManager) {
this.kubernetesClient = kubernetesClient;
- this.artifactManager = new ArtifactManager(configManager);
this.configManager = configManager;
+ this.artifactManager = new ArtifactManager(configManager);
this.executorService =
Executors.newFixedThreadPool(
4, new ExecutorThreadFactory("Flink-RestClusterClient-IO"));
}
- private static List<JobStatusMessage> toJobStatusMessage(
- MultipleJobsDetails multipleJobsDetails) {
- return multipleJobsDetails.getJobs().stream()
- .map(
- details ->
- new JobStatusMessage(
- details.getJobId(),
- details.getJobName(),
- getEffectiveStatus(details),
- details.getStartTime()))
- .collect(Collectors.toList());
- }
-
- @VisibleForTesting
- protected static JobStatus getEffectiveStatus(JobDetails details) {
- int numRunning = details.getTasksPerState()[ExecutionState.RUNNING.ordinal()];
- int numFinished = details.getTasksPerState()[ExecutionState.FINISHED.ordinal()];
- boolean allRunningOrFinished = details.getNumTasks() == (numRunning + numFinished);
- JobStatus effectiveStatus = details.getStatus();
- if (JobStatus.RUNNING.equals(effectiveStatus) && !allRunningOrFinished) {
- effectiveStatus = JobStatus.CREATED;
- LOG.debug("Adjusting job state from {} to {}", JobStatus.RUNNING, effectiveStatus);
- }
- return effectiveStatus;
- }
+ protected abstract PodList getJmPodList(String namespace, String clusterId);
+ @Override
public KubernetesClient getKubernetesClient() {
return kubernetesClient;
}
- public void submitApplicationCluster(
- JobSpec jobSpec, Configuration conf, boolean requireHaMetadata) throws Exception {
- LOG.info(
- "Deploying application cluster{}",
- requireHaMetadata ? " requiring last-state from HA metadata" : "");
- if (FlinkUtils.isKubernetesHAActivated(conf)) {
- final String clusterId = conf.get(KubernetesConfigOptions.CLUSTER_ID);
- final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
- // Delete the job graph in the HA ConfigMaps so that the newly changed job config(e.g.
- // parallelism) could take effect
- FlinkUtils.deleteJobGraphInKubernetesHA(clusterId, namespace, kubernetesClient);
- }
- if (requireHaMetadata) {
- validateHaMetadataExists(conf);
- }
- final ClusterClientServiceLoader clusterClientServiceLoader =
- new DefaultClusterClientServiceLoader();
- final ApplicationDeployer deployer =
- new ApplicationClusterDeployer(clusterClientServiceLoader);
-
- final ApplicationConfiguration applicationConfiguration =
- new ApplicationConfiguration(
- jobSpec.getArgs() != null ? jobSpec.getArgs() : new String[0],
- jobSpec.getEntryClass());
-
- deployer.run(conf, applicationConfiguration);
- LOG.info("Application cluster successfully deployed");
- }
-
+ @Override
public boolean isHaMetadataAvailable(Configuration conf) {
return FlinkUtils.isHaMetadataAvailable(conf, kubernetesClient);
}
- protected void validateHaMetadataExists(Configuration conf) {
- if (!isHaMetadataAvailable(conf)) {
- throw new DeploymentFailedException(
- "HA metadata not available to restore from last state. "
- + "It is possible that the job has finished or terminally failed, or the configmaps have been deleted. "
- + "Manual restore required.",
- "RestoreFailed");
- }
- }
-
- public void submitSessionCluster(Configuration conf) throws Exception {
- LOG.info("Deploying session cluster");
- final ClusterClientServiceLoader clusterClientServiceLoader =
- new DefaultClusterClientServiceLoader();
- final ClusterClientFactory<String> kubernetesClusterClientFactory =
- clusterClientServiceLoader.getClusterClientFactory(conf);
- try (final ClusterDescriptor<String> kubernetesClusterDescriptor =
- kubernetesClusterClientFactory.createClusterDescriptor(conf)) {
- kubernetesClusterDescriptor.deploySessionCluster(
- kubernetesClusterClientFactory.getClusterSpecification(conf));
- }
- LOG.info("Session cluster successfully deployed");
- }
-
+ @Override
public JobID submitJobToSessionCluster(
ObjectMeta meta,
FlinkSessionJobSpec spec,
@@ -242,108 +166,7 @@ public class FlinkService {
return jobID;
}
- private JarRunResponseBody runJar(
- JobSpec job,
- JobID jobID,
- JarUploadResponseBody response,
- Configuration conf,
- String savepoint) {
- String jarId =
- response.getFilename().substring(response.getFilename().lastIndexOf("/") + 1);
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- JarRunHeaders headers = JarRunHeaders.getInstance();
- JarRunMessageParameters parameters = headers.getUnresolvedMessageParameters();
- parameters.jarIdPathParameter.resolve(jarId);
- JarRunRequestBody runRequestBody =
- new JarRunRequestBody(
- job.getEntryClass(),
- null,
- job.getArgs() == null ? null : Arrays.asList(job.getArgs()),
- job.getParallelism() > 0 ? job.getParallelism() : null,
- jobID,
- job.getAllowNonRestoredState(),
- savepoint,
- conf.get(FLINK_VERSION).isNewerVersionThan(FlinkVersion.v1_14)
- ? RestoreMode.DEFAULT
- : null);
- LOG.info("Submitting job: {} to session cluster.", jobID.toHexString());
- return clusterClient
- .sendRequest(headers, parameters, runRequestBody)
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- } catch (Exception e) {
- LOG.error("Failed to submit job to session cluster.", e);
- throw new FlinkRuntimeException(e);
- } finally {
- deleteJar(conf, jarId);
- }
- }
-
- private JarUploadResponseBody uploadJar(
- ObjectMeta objectMeta, FlinkSessionJobSpec spec, Configuration conf) throws Exception {
- String targetDir = artifactManager.generateJarDir(objectMeta, spec);
- File jarFile = artifactManager.fetch(spec.getJob().getJarURI(), conf, targetDir);
- Preconditions.checkArgument(
- jarFile.exists(),
- String.format("The jar file %s not exists", jarFile.getAbsolutePath()));
- JarUploadHeaders headers = JarUploadHeaders.getInstance();
- String clusterId = spec.getDeploymentName();
- String namespace = objectMeta.getNamespace();
- int port = conf.getInteger(RestOptions.PORT);
- String host =
- ObjectUtils.firstNonNull(
- configManager.getOperatorConfiguration().getFlinkServiceHostOverride(),
- ExternalServiceDecorator.getNamespacedExternalServiceName(
- clusterId, namespace));
- try (RestClient restClient = new RestClient(conf, executorService)) {
- // TODO add method in flink#RestClusterClient to support upload jar.
- return restClient
- .sendRequest(
- host,
- port,
- headers,
- EmptyMessageParameters.getInstance(),
- EmptyRequestBody.getInstance(),
- Collections.singletonList(
- new FileUpload(
- jarFile.toPath(), RestConstants.CONTENT_TYPE_JAR)))
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- } finally {
- LOG.debug("Deleting the jar file {}", jarFile);
- FileUtils.deleteFileOrDirectory(jarFile);
- }
- }
-
- private void deleteJar(Configuration conf, String jarId) {
- LOG.debug("Deleting the jar: {}", jarId);
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- JarDeleteHeaders headers = JarDeleteHeaders.getInstance();
- JarDeleteMessageParameters parameters = headers.getUnresolvedMessageParameters();
- parameters.jarIdPathParameter.resolve(jarId);
- clusterClient
- .sendRequest(headers, parameters, EmptyRequestBody.getInstance())
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- } catch (Exception e) {
- LOG.error("Failed to delete the jar: {}.", jarId, e);
- }
- }
-
+ @Override
public boolean isJobManagerPortReady(Configuration config) {
final URI uri;
try (ClusterClient<String> clusterClient = getClusterClient(config)) {
@@ -357,11 +180,13 @@ public class FlinkService {
socket.connect(socketAddress, 1000);
socket.close();
return true;
+ } catch (SocketTimeoutException ste) {
} catch (IOException e) {
}
return false;
}
+ @Override
public Collection<JobStatusMessage> listJobs(Configuration conf) throws Exception {
try (RestClusterClient<String> clusterClient =
(RestClusterClient<String>) getClusterClient(conf)) {
@@ -370,7 +195,7 @@ public class FlinkService {
JobsOverviewHeaders.getInstance(),
EmptyMessageParameters.getInstance(),
EmptyRequestBody.getInstance())
- .thenApply(FlinkService::toJobStatusMessage)
+ .thenApply(AbstractFlinkService::toJobStatusMessage)
.get(
configManager
.getOperatorConfiguration()
@@ -380,6 +205,7 @@ public class FlinkService {
}
}
+ @Override
public JobResult requestJobResult(Configuration conf, JobID jobID) throws Exception {
try (ClusterClient<String> clusterClient = getClusterClient(conf)) {
return clusterClient
@@ -393,22 +219,7 @@ public class FlinkService {
}
}
- @VisibleForTesting
- protected ClusterClient<String> getClusterClient(Configuration config) throws Exception {
- final String clusterId = config.get(KubernetesConfigOptions.CLUSTER_ID);
- final String namespace = config.get(KubernetesConfigOptions.NAMESPACE);
- final int port = config.getInteger(RestOptions.PORT);
- final String host =
- ObjectUtils.firstNonNull(
- configManager.getOperatorConfiguration().getFlinkServiceHostOverride(),
- ExternalServiceDecorator.getNamespacedExternalServiceName(
- clusterId, namespace));
- final String restServerAddress = String.format("http://%s:%s", host, port);
- LOG.debug("Creating RestClusterClient({})", restServerAddress);
- return new RestClusterClient<>(
- config, clusterId, (c, e) -> new StandaloneClientHAServices(restServerAddress));
- }
-
+ @Override
public void cancelJob(FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration conf)
throws Exception {
var deploymentStatus = deployment.getStatus();
@@ -504,30 +315,7 @@ public class FlinkService {
}
}
- @VisibleForTesting
- protected void waitForClusterShutdown(Configuration conf) {
- FlinkUtils.waitForClusterShutdown(
- kubernetesClient,
- conf,
- configManager
- .getOperatorConfiguration()
- .getFlinkShutdownClusterTimeout()
- .toSeconds());
- }
-
- public void deleteClusterDeployment(
- ObjectMeta meta, FlinkDeploymentStatus status, boolean deleteHaData) {
- FlinkUtils.deleteCluster(
- status,
- meta,
- kubernetesClient,
- deleteHaData,
- configManager
- .getOperatorConfiguration()
- .getFlinkShutdownClusterTimeout()
- .toSeconds());
- }
-
+ @Override
public void cancelSessionJob(
FlinkSessionJob sessionJob, UpgradeMode upgradeMode, Configuration conf)
throws Exception {
@@ -536,7 +324,6 @@ public class FlinkService {
var jobIdString = jobStatus.getJobId();
Preconditions.checkNotNull(jobIdString, "The job to be suspend should not be null");
var jobId = JobID.fromHexString(jobIdString);
-
Optional<String> savepointOpt = Optional.empty();
try (ClusterClient<String> clusterClient = getClusterClient(conf)) {
final String clusterId = clusterClient.getClusterId();
@@ -592,7 +379,6 @@ public class FlinkService {
throw new RuntimeException("Unsupported upgrade mode " + upgradeMode);
}
}
-
jobStatus.setState(JobStatus.FINISHED.name());
savepointOpt.ifPresent(
location -> {
@@ -601,6 +387,7 @@ public class FlinkService {
});
}
+ @Override
public void triggerSavepoint(
String jobId,
SavepointTriggerType triggerType,
@@ -639,6 +426,7 @@ public class FlinkService {
}
}
+ @Override
public Optional<Savepoint> getLastCheckpoint(JobID jobId, Configuration conf) throws Exception {
try (RestClusterClient<String> clusterClient =
(RestClusterClient<String>) getClusterClient(conf)) {
@@ -675,6 +463,25 @@ public class FlinkService {
}
}
+ @Override
+ public void disposeSavepoint(String savepointPath, Configuration conf) throws Exception {
+ try (RestClusterClient<String> clusterClient =
+ (RestClusterClient<String>) getClusterClient(conf)) {
+ clusterClient
+ .sendRequest(
+ SavepointDisposalTriggerHeaders.getInstance(),
+ EmptyMessageParameters.getInstance(),
+ new SavepointDisposalRequest(savepointPath))
+ .get(
+ configManager
+ .getOperatorConfiguration()
+ .getFlinkClientTimeout()
+ .getSeconds(),
+ TimeUnit.SECONDS);
+ }
+ }
+
+ @Override
public SavepointFetchResult fetchSavepointInfo(
String triggerId, String jobId, Configuration conf) {
LOG.info("Fetching savepoint result with triggerId: " + triggerId);
@@ -716,23 +523,7 @@ public class FlinkService {
}
}
- public void disposeSavepoint(String savepointPath, Configuration conf) throws Exception {
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- clusterClient
- .sendRequest(
- SavepointDisposalTriggerHeaders.getInstance(),
- EmptyMessageParameters.getInstance(),
- new SavepointDisposalRequest(savepointPath))
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .getSeconds(),
- TimeUnit.SECONDS);
- }
- }
-
+ @Override
public Map<String, String> getClusterInfo(Configuration conf) throws Exception {
Map<String, String> runtimeVersion = new HashMap<>();
@@ -762,6 +553,7 @@ public class FlinkService {
return runtimeVersion;
}
+ @Override
public PodList getJmPodList(FlinkDeployment deployment, Configuration conf) {
final String namespace = conf.getString(KubernetesConfigOptions.NAMESPACE);
final String clusterId;
@@ -770,6 +562,207 @@ public class FlinkService {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
- return FlinkUtils.getJmPodList(kubernetesClient, namespace, clusterId);
+ return getJmPodList(namespace, clusterId);
+ }
+
+ @Override
+ public void waitForClusterShutdown(Configuration conf) {
+ waitForClusterShutdown(
+ conf.getString(KubernetesConfigOptions.NAMESPACE),
+ conf.getString(KubernetesConfigOptions.CLUSTER_ID),
+ configManager
+ .getOperatorConfiguration()
+ .getFlinkShutdownClusterTimeout()
+ .toSeconds());
+ }
+
+ @VisibleForTesting
+ protected ClusterClient<String> getClusterClient(Configuration conf) throws Exception {
+ final String clusterId = conf.get(KubernetesConfigOptions.CLUSTER_ID);
+ final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
+ final int port = conf.getInteger(RestOptions.PORT);
+ final String host =
+ ObjectUtils.firstNonNull(
+ configManager.getOperatorConfiguration().getFlinkServiceHostOverride(),
+ ExternalServiceDecorator.getNamespacedExternalServiceName(
+ clusterId, namespace));
+ final String restServerAddress = String.format("http://%s:%s", host, port);
+ LOG.debug("Creating RestClusterClient({})", restServerAddress);
+ return new RestClusterClient<>(
+ conf, clusterId, (c, e) -> new StandaloneClientHAServices(restServerAddress));
+ }
+
+ private JarRunResponseBody runJar(
+ JobSpec job,
+ JobID jobID,
+ JarUploadResponseBody response,
+ Configuration conf,
+ String savepoint) {
+ String jarId =
+ response.getFilename().substring(response.getFilename().lastIndexOf("/") + 1);
+ try (RestClusterClient<String> clusterClient =
+ (RestClusterClient<String>) getClusterClient(conf)) {
+ JarRunHeaders headers = JarRunHeaders.getInstance();
+ JarRunMessageParameters parameters = headers.getUnresolvedMessageParameters();
+ parameters.jarIdPathParameter.resolve(jarId);
+ JarRunRequestBody runRequestBody =
+ new JarRunRequestBody(
+ job.getEntryClass(),
+ null,
+ job.getArgs() == null ? null : Arrays.asList(job.getArgs()),
+ job.getParallelism() > 0 ? job.getParallelism() : null,
+ jobID,
+ job.getAllowNonRestoredState(),
+ savepoint,
+ conf.get(FLINK_VERSION).isNewerVersionThan(FlinkVersion.v1_14)
+ ? RestoreMode.DEFAULT
+ : null);
+ LOG.info("Submitting job: {} to session cluster.", jobID.toHexString());
+ return clusterClient
+ .sendRequest(headers, parameters, runRequestBody)
+ .get(
+ configManager
+ .getOperatorConfiguration()
+ .getFlinkClientTimeout()
+ .toSeconds(),
+ TimeUnit.SECONDS);
+ } catch (Exception e) {
+ LOG.error("Failed to submit job to session cluster.", e);
+ throw new FlinkRuntimeException(e);
+ } finally {
+ deleteJar(conf, jarId);
+ }
+ }
+
+ private JarUploadResponseBody uploadJar(
+ ObjectMeta objectMeta, FlinkSessionJobSpec spec, Configuration conf) throws Exception {
+ String targetDir = artifactManager.generateJarDir(objectMeta, spec);
+ File jarFile = artifactManager.fetch(spec.getJob().getJarURI(), conf, targetDir);
+ Preconditions.checkArgument(
+ jarFile.exists(),
+ String.format("The jar file %s not exists", jarFile.getAbsolutePath()));
+ JarUploadHeaders headers = JarUploadHeaders.getInstance();
+ String clusterId = spec.getDeploymentName();
+ String namespace = objectMeta.getNamespace();
+ int port = conf.getInteger(RestOptions.PORT);
+ String host =
+ ObjectUtils.firstNonNull(
+ configManager.getOperatorConfiguration().getFlinkServiceHostOverride(),
+ ExternalServiceDecorator.getNamespacedExternalServiceName(
+ clusterId, namespace));
+ try (RestClient restClient = new RestClient(conf, executorService)) {
+ // TODO add method in flink#RestClusterClient to support upload jar.
+ return restClient
+ .sendRequest(
+ host,
+ port,
+ headers,
+ EmptyMessageParameters.getInstance(),
+ EmptyRequestBody.getInstance(),
+ Collections.singletonList(
+ new FileUpload(
+ jarFile.toPath(), RestConstants.CONTENT_TYPE_JAR)))
+ .get(
+ configManager
+ .getOperatorConfiguration()
+ .getFlinkClientTimeout()
+ .toSeconds(),
+ TimeUnit.SECONDS);
+ } finally {
+ LOG.debug("Deleting the jar file {}", jarFile);
+ FileUtils.deleteFileOrDirectory(jarFile);
+ }
+ }
+
+ private void deleteJar(Configuration conf, String jarId) {
+ LOG.debug("Deleting the jar: {}", jarId);
+ try (RestClusterClient<String> clusterClient =
+ (RestClusterClient<String>) getClusterClient(conf)) {
+ JarDeleteHeaders headers = JarDeleteHeaders.getInstance();
+ JarDeleteMessageParameters parameters = headers.getUnresolvedMessageParameters();
+ parameters.jarIdPathParameter.resolve(jarId);
+ clusterClient
+ .sendRequest(headers, parameters, EmptyRequestBody.getInstance())
+ .get(
+ configManager
+ .getOperatorConfiguration()
+ .getFlinkClientTimeout()
+ .toSeconds(),
+ TimeUnit.SECONDS);
+ } catch (Exception e) {
+ LOG.error("Failed to delete the jar: {}.", jarId, e);
+ }
+ }
+
+ /** Wait until the FLink cluster has completely shut down. */
+ @VisibleForTesting
+ void waitForClusterShutdown(String namespace, String clusterId, long shutdownTimeout) {
+
+ boolean jobManagerRunning = true;
+ boolean serviceRunning = true;
+
+ for (int i = 0; i < shutdownTimeout; i++) {
+ if (jobManagerRunning) {
+ PodList jmPodList = getJmPodList(namespace, clusterId);
+
+ if (jmPodList == null || jmPodList.getItems().isEmpty()) {
+ jobManagerRunning = false;
+ }
+ }
+
+ if (serviceRunning) {
+ Service service =
+ kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .withName(
+ ExternalServiceDecorator.getExternalServiceName(clusterId))
+ .fromServer()
+ .get();
+ if (service == null) {
+ serviceRunning = false;
+ }
+ }
+
+ if (!jobManagerRunning && !serviceRunning) {
+ break;
+ }
+ // log a message waiting to shutdown Flink cluster every 5 seconds.
+ if ((i + 1) % 5 == 0) {
+ LOG.info("Waiting for cluster shutdown... ({}s)", i + 1);
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ LOG.info("Cluster shutdown completed.");
+ }
+
+ private static List<JobStatusMessage> toJobStatusMessage(
+ MultipleJobsDetails multipleJobsDetails) {
+ return multipleJobsDetails.getJobs().stream()
+ .map(
+ details ->
+ new JobStatusMessage(
+ details.getJobId(),
+ details.getJobName(),
+ getEffectiveStatus(details),
+ details.getStartTime()))
+ .collect(Collectors.toList());
+ }
+
+ @VisibleForTesting
+ protected static JobStatus getEffectiveStatus(JobDetails details) {
+ int numRunning = details.getTasksPerState()[ExecutionState.RUNNING.ordinal()];
+ int numFinished = details.getTasksPerState()[ExecutionState.FINISHED.ordinal()];
+ boolean allRunningOrFinished = details.getNumTasks() == (numRunning + numFinished);
+ JobStatus effectiveStatus = details.getStatus();
+ if (JobStatus.RUNNING.equals(effectiveStatus) && !allRunningOrFinished) {
+ effectiveStatus = JobStatus.CREATED;
+ LOG.debug("Adjusting job state from {} to {}", JobStatus.RUNNING, effectiveStatus);
+ }
+ return effectiveStatus;
}
}
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 2deccaee..d7aa1b98 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
@@ -17,759 +17,80 @@
package org.apache.flink.kubernetes.operator.service;
-import org.apache.flink.annotation.VisibleForTesting;
import org.apache.flink.api.common.JobID;
-import org.apache.flink.api.common.JobStatus;
-import org.apache.flink.client.cli.ApplicationDeployer;
-import org.apache.flink.client.deployment.ClusterClientFactory;
-import org.apache.flink.client.deployment.ClusterClientServiceLoader;
-import org.apache.flink.client.deployment.ClusterDescriptor;
-import org.apache.flink.client.deployment.DefaultClusterClientServiceLoader;
-import org.apache.flink.client.deployment.application.ApplicationConfiguration;
-import org.apache.flink.client.deployment.application.cli.ApplicationClusterDeployer;
-import org.apache.flink.client.program.ClusterClient;
-import org.apache.flink.client.program.rest.RestClusterClient;
-import org.apache.flink.configuration.CheckpointingOptions;
import org.apache.flink.configuration.Configuration;
-import org.apache.flink.configuration.RestOptions;
-import org.apache.flink.core.execution.SavepointFormatType;
-import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
-import org.apache.flink.kubernetes.kubeclient.decorators.ExternalServiceDecorator;
-import org.apache.flink.kubernetes.operator.artifact.ArtifactManager;
-import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.FlinkSessionJob;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkSessionJobSpec;
-import org.apache.flink.kubernetes.operator.crd.spec.FlinkVersion;
import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
-import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatus;
import org.apache.flink.kubernetes.operator.crd.status.Savepoint;
import org.apache.flink.kubernetes.operator.crd.status.SavepointTriggerType;
-import org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
import org.apache.flink.kubernetes.operator.observer.SavepointFetchResult;
-import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
-import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
import org.apache.flink.runtime.client.JobStatusMessage;
-import org.apache.flink.runtime.execution.ExecutionState;
-import org.apache.flink.runtime.highavailability.nonha.standalone.StandaloneClientHAServices;
-import org.apache.flink.runtime.jobgraph.RestoreMode;
import org.apache.flink.runtime.jobmaster.JobResult;
-import org.apache.flink.runtime.messages.webmonitor.JobDetails;
-import org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails;
-import org.apache.flink.runtime.rest.FileUpload;
-import org.apache.flink.runtime.rest.RestClient;
-import org.apache.flink.runtime.rest.handler.async.AsynchronousOperationResult;
-import org.apache.flink.runtime.rest.handler.async.TriggerResponse;
-import org.apache.flink.runtime.rest.messages.DashboardConfiguration;
-import org.apache.flink.runtime.rest.messages.EmptyMessageParameters;
-import org.apache.flink.runtime.rest.messages.EmptyRequestBody;
-import org.apache.flink.runtime.rest.messages.JobsOverviewHeaders;
-import org.apache.flink.runtime.rest.messages.TriggerId;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointDisposalRequest;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointDisposalTriggerHeaders;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointInfo;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointStatusHeaders;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointStatusMessageParameters;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointTriggerHeaders;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointTriggerMessageParameters;
-import org.apache.flink.runtime.rest.messages.job.savepoints.SavepointTriggerRequestBody;
-import org.apache.flink.runtime.rest.util.RestConstants;
-import org.apache.flink.runtime.state.memory.NonPersistentMetadataCheckpointStorageLocation;
-import org.apache.flink.runtime.webmonitor.handlers.JarDeleteHeaders;
-import org.apache.flink.runtime.webmonitor.handlers.JarDeleteMessageParameters;
-import org.apache.flink.runtime.webmonitor.handlers.JarRunHeaders;
-import org.apache.flink.runtime.webmonitor.handlers.JarRunMessageParameters;
-import org.apache.flink.runtime.webmonitor.handlers.JarRunRequestBody;
-import org.apache.flink.runtime.webmonitor.handlers.JarRunResponseBody;
-import org.apache.flink.runtime.webmonitor.handlers.JarUploadHeaders;
-import org.apache.flink.runtime.webmonitor.handlers.JarUploadResponseBody;
-import org.apache.flink.streaming.api.environment.ExecutionCheckpointingOptions;
-import org.apache.flink.util.FileUtils;
-import org.apache.flink.util.FlinkException;
-import org.apache.flink.util.FlinkRuntimeException;
-import org.apache.flink.util.Preconditions;
-import org.apache.flink.util.concurrent.ExecutorThreadFactory;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.client.KubernetesClient;
-import org.apache.commons.lang3.ObjectUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
-import java.io.File;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.URI;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
-
-import static org.apache.flink.kubernetes.operator.config.FlinkConfigBuilder.FLINK_VERSION;
/** Service for submitting and interacting with Flink clusters and jobs. */
-public class FlinkService {
-
- private static final Logger LOG = LoggerFactory.getLogger(FlinkService.class);
-
- private final KubernetesClient kubernetesClient;
- private final ArtifactManager artifactManager;
- private final FlinkConfigManager configManager;
- private final ExecutorService executorService;
-
- public FlinkService(KubernetesClient kubernetesClient, FlinkConfigManager configManager) {
- this.kubernetesClient = kubernetesClient;
- this.artifactManager = new ArtifactManager(configManager);
- this.configManager = configManager;
- this.executorService =
- Executors.newFixedThreadPool(
- 4, new ExecutorThreadFactory("Flink-RestClusterClient-IO"));
- }
-
- private static List<JobStatusMessage> toJobStatusMessage(
- MultipleJobsDetails multipleJobsDetails) {
- return multipleJobsDetails.getJobs().stream()
- .map(
- details ->
- new JobStatusMessage(
- details.getJobId(),
- details.getJobName(),
- getEffectiveStatus(details),
- details.getStartTime()))
- .collect(Collectors.toList());
- }
-
- @VisibleForTesting
- protected static JobStatus getEffectiveStatus(JobDetails details) {
- int numRunning = details.getTasksPerState()[ExecutionState.RUNNING.ordinal()];
- int numFinished = details.getTasksPerState()[ExecutionState.FINISHED.ordinal()];
- boolean allRunningOrFinished = details.getNumTasks() == (numRunning + numFinished);
- JobStatus effectiveStatus = details.getStatus();
- if (JobStatus.RUNNING.equals(effectiveStatus) && !allRunningOrFinished) {
- effectiveStatus = JobStatus.CREATED;
- LOG.debug("Adjusting job state from {} to {}", JobStatus.RUNNING, effectiveStatus);
- }
- return effectiveStatus;
- }
-
- public KubernetesClient getKubernetesClient() {
- return kubernetesClient;
- }
-
- public void submitApplicationCluster(
- JobSpec jobSpec, Configuration conf, boolean requireHaMetadata) throws Exception {
- LOG.info(
- "Deploying application cluster{}",
- requireHaMetadata ? " requiring last-state from HA metadata" : "");
- if (FlinkUtils.isKubernetesHAActivated(conf)) {
- final String clusterId = conf.get(KubernetesConfigOptions.CLUSTER_ID);
- final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
- // Delete the job graph in the HA ConfigMaps so that the newly changed job config(e.g.
- // parallelism) could take effect
- FlinkUtils.deleteJobGraphInKubernetesHA(clusterId, namespace, kubernetesClient);
- }
- if (requireHaMetadata) {
- validateHaMetadataExists(conf);
- }
- final ClusterClientServiceLoader clusterClientServiceLoader =
- new DefaultClusterClientServiceLoader();
- final ApplicationDeployer deployer =
- new ApplicationClusterDeployer(clusterClientServiceLoader);
+public interface FlinkService {
- final ApplicationConfiguration applicationConfiguration =
- new ApplicationConfiguration(
- jobSpec.getArgs() != null ? jobSpec.getArgs() : new String[0],
- jobSpec.getEntryClass());
+ KubernetesClient getKubernetesClient();
- deployer.run(conf, applicationConfiguration);
- LOG.info("Application cluster successfully deployed");
- }
+ void submitApplicationCluster(JobSpec jobSpec, Configuration conf, boolean requireHaMetadata)
+ throws Exception;
- public boolean isHaMetadataAvailable(Configuration conf) {
- return FlinkUtils.isHaMetadataAvailable(conf, kubernetesClient);
- }
+ boolean isHaMetadataAvailable(Configuration conf);
- protected void validateHaMetadataExists(Configuration conf) {
- if (!isHaMetadataAvailable(conf)) {
- throw new DeploymentFailedException(
- "HA metadata not available to restore from last state. "
- + "It is possible that the job has finished or terminally failed, or the configmaps have been deleted. "
- + "Manual restore required.",
- "RestoreFailed");
- }
- }
+ void submitSessionCluster(Configuration conf) throws Exception;
- public void submitSessionCluster(Configuration conf) throws Exception {
- LOG.info("Deploying session cluster");
- final ClusterClientServiceLoader clusterClientServiceLoader =
- new DefaultClusterClientServiceLoader();
- final ClusterClientFactory<String> kubernetesClusterClientFactory =
- clusterClientServiceLoader.getClusterClientFactory(conf);
- try (final ClusterDescriptor<String> kubernetesClusterDescriptor =
- kubernetesClusterClientFactory.createClusterDescriptor(conf)) {
- kubernetesClusterDescriptor.deploySessionCluster(
- kubernetesClusterClientFactory.getClusterSpecification(conf));
- }
- LOG.info("Session cluster successfully deployed");
- }
-
- public JobID submitJobToSessionCluster(
+ JobID submitJobToSessionCluster(
ObjectMeta meta,
FlinkSessionJobSpec spec,
Configuration conf,
@Nullable String savepoint)
- throws Exception {
- // we generate jobID in advance to help deduplicate job submission.
- var jobID = FlinkUtils.generateSessionJobFixedJobID(meta);
- runJar(spec.getJob(), jobID, uploadJar(meta, spec, conf), conf, savepoint);
- LOG.info("Submitted job: {} to session cluster.", jobID);
- return jobID;
- }
-
- private JarRunResponseBody runJar(
- JobSpec job,
- JobID jobID,
- JarUploadResponseBody response,
- Configuration conf,
- String savepoint) {
- String jarId =
- response.getFilename().substring(response.getFilename().lastIndexOf("/") + 1);
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- JarRunHeaders headers = JarRunHeaders.getInstance();
- JarRunMessageParameters parameters = headers.getUnresolvedMessageParameters();
- parameters.jarIdPathParameter.resolve(jarId);
- JarRunRequestBody runRequestBody =
- new JarRunRequestBody(
- job.getEntryClass(),
- null,
- job.getArgs() == null ? null : Arrays.asList(job.getArgs()),
- job.getParallelism() > 0 ? job.getParallelism() : null,
- jobID,
- job.getAllowNonRestoredState(),
- savepoint,
- conf.get(FLINK_VERSION).isNewerVersionThan(FlinkVersion.v1_14)
- ? RestoreMode.DEFAULT
- : null);
- LOG.info("Submitting job: {} to session cluster.", jobID.toHexString());
- return clusterClient
- .sendRequest(headers, parameters, runRequestBody)
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- } catch (Exception e) {
- LOG.error("Failed to submit job to session cluster.", e);
- throw new FlinkRuntimeException(e);
- } finally {
- deleteJar(conf, jarId);
- }
- }
-
- private JarUploadResponseBody uploadJar(
- ObjectMeta objectMeta, FlinkSessionJobSpec spec, Configuration conf) throws Exception {
- String targetDir = artifactManager.generateJarDir(objectMeta, spec);
- File jarFile = artifactManager.fetch(spec.getJob().getJarURI(), conf, targetDir);
- Preconditions.checkArgument(
- jarFile.exists(),
- String.format("The jar file %s not exists", jarFile.getAbsolutePath()));
- JarUploadHeaders headers = JarUploadHeaders.getInstance();
- String clusterId = spec.getDeploymentName();
- String namespace = objectMeta.getNamespace();
- int port = conf.getInteger(RestOptions.PORT);
- String host =
- ObjectUtils.firstNonNull(
- configManager.getOperatorConfiguration().getFlinkServiceHostOverride(),
- ExternalServiceDecorator.getNamespacedExternalServiceName(
- clusterId, namespace));
- try (RestClient restClient = new RestClient(conf, executorService)) {
- // TODO add method in flink#RestClusterClient to support upload jar.
- return restClient
- .sendRequest(
- host,
- port,
- headers,
- EmptyMessageParameters.getInstance(),
- EmptyRequestBody.getInstance(),
- Collections.singletonList(
- new FileUpload(
- jarFile.toPath(), RestConstants.CONTENT_TYPE_JAR)))
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- } finally {
- LOG.debug("Deleting the jar file {}", jarFile);
- FileUtils.deleteFileOrDirectory(jarFile);
- }
- }
-
- private void deleteJar(Configuration conf, String jarId) {
- LOG.debug("Deleting the jar: {}", jarId);
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- JarDeleteHeaders headers = JarDeleteHeaders.getInstance();
- JarDeleteMessageParameters parameters = headers.getUnresolvedMessageParameters();
- parameters.jarIdPathParameter.resolve(jarId);
- clusterClient
- .sendRequest(headers, parameters, EmptyRequestBody.getInstance())
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- } catch (Exception e) {
- LOG.error("Failed to delete the jar: {}.", jarId, e);
- }
- }
-
- public boolean isJobManagerPortReady(Configuration config) {
- final URI uri;
- try (ClusterClient<String> clusterClient = getClusterClient(config)) {
- uri = URI.create(clusterClient.getWebInterfaceURL());
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- SocketAddress socketAddress = new InetSocketAddress(uri.getHost(), uri.getPort());
- Socket socket = new Socket();
- try {
- socket.connect(socketAddress, 1000);
- socket.close();
- return true;
- } catch (IOException e) {
- }
- return false;
- }
-
- public Collection<JobStatusMessage> listJobs(Configuration conf) throws Exception {
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- return clusterClient
- .sendRequest(
- JobsOverviewHeaders.getInstance(),
- EmptyMessageParameters.getInstance(),
- EmptyRequestBody.getInstance())
- .thenApply(FlinkService::toJobStatusMessage)
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- }
- }
-
- public JobResult requestJobResult(Configuration conf, JobID jobID) throws Exception {
- try (ClusterClient<String> clusterClient = getClusterClient(conf)) {
- return clusterClient
- .requestJobResult(jobID)
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .getSeconds(),
- TimeUnit.SECONDS);
- }
- }
+ throws Exception;
- @VisibleForTesting
- protected ClusterClient<String> getClusterClient(Configuration config) throws Exception {
- final String clusterId = config.get(KubernetesConfigOptions.CLUSTER_ID);
- final String namespace = config.get(KubernetesConfigOptions.NAMESPACE);
- final int port = config.getInteger(RestOptions.PORT);
- final String host =
- ObjectUtils.firstNonNull(
- configManager.getOperatorConfiguration().getFlinkServiceHostOverride(),
- ExternalServiceDecorator.getNamespacedExternalServiceName(
- clusterId, namespace));
- final String restServerAddress = String.format("http://%s:%s", host, port);
- LOG.debug("Creating RestClusterClient({})", restServerAddress);
- return new RestClusterClient<>(
- config, clusterId, (c, e) -> new StandaloneClientHAServices(restServerAddress));
- }
+ boolean isJobManagerPortReady(Configuration config);
- public void cancelJob(FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration conf)
- throws Exception {
- var deploymentStatus = deployment.getStatus();
- var jobIdString = deploymentStatus.getJobStatus().getJobId();
- var jobId = jobIdString != null ? JobID.fromHexString(jobIdString) : null;
+ Collection<JobStatusMessage> listJobs(Configuration conf) throws Exception;
- Optional<String> savepointOpt = Optional.empty();
- try (ClusterClient<String> clusterClient = getClusterClient(conf)) {
- var clusterId = clusterClient.getClusterId();
- switch (upgradeMode) {
- case STATELESS:
- if (ReconciliationUtils.isJobRunning(deployment.getStatus())) {
- LOG.info("Job is running, cancelling job.");
- try {
- clusterClient
- .cancel(Preconditions.checkNotNull(jobId))
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkCancelJobTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- LOG.info("Job successfully cancelled.");
- } catch (Exception e) {
- LOG.error("Could not shut down cluster gracefully, deleting...", e);
- }
- }
- deleteClusterDeployment(deployment.getMetadata(), deploymentStatus, true);
- break;
- case SAVEPOINT:
- final String savepointDirectory =
- Preconditions.checkNotNull(
- conf.get(CheckpointingOptions.SAVEPOINT_DIRECTORY));
- final long timeout =
- conf.get(ExecutionCheckpointingOptions.CHECKPOINTING_TIMEOUT)
- .getSeconds();
- try {
- if (ReconciliationUtils.isJobRunning(deploymentStatus)) {
- LOG.info("Suspending job with savepoint.");
- String savepoint =
- clusterClient
- .stopWithSavepoint(
- Preconditions.checkNotNull(jobId),
- false,
- savepointDirectory,
- conf.get(FLINK_VERSION)
- .isNewerVersionThan(
- FlinkVersion.v1_14)
- ? SavepointFormatType.DEFAULT
- : null)
- .get(timeout, TimeUnit.SECONDS);
- savepointOpt = Optional.of(savepoint);
- LOG.info("Job successfully suspended with savepoint {}.", savepoint);
- } else if (ReconciliationUtils.isJobInTerminalState(deploymentStatus)) {
- LOG.info(
- "Job is already in terminal state skipping cancel-with-savepoint operation.");
- } else {
- throw new RuntimeException(
- "Unexpected non-terminal status: " + deploymentStatus);
- }
- } catch (TimeoutException exception) {
- throw new FlinkException(
- String.format(
- "Timed out stopping the job %s in Flink cluster %s with savepoint, "
- + "please configure a larger timeout via '%s'",
- jobId,
- clusterId,
- ExecutionCheckpointingOptions.CHECKPOINTING_TIMEOUT.key()),
- exception);
- }
- break;
- case LAST_STATE:
- deleteClusterDeployment(deployment.getMetadata(), deploymentStatus, false);
- break;
- default:
- throw new RuntimeException("Unsupported upgrade mode " + upgradeMode);
- }
- }
- deploymentStatus.getJobStatus().setState(JobStatus.FINISHED.name());
- savepointOpt.ifPresent(
- location -> {
- Savepoint sp = Savepoint.of(location, SavepointTriggerType.UPGRADE);
- deploymentStatus.getJobStatus().getSavepointInfo().updateLastSavepoint(sp);
- });
+ JobResult requestJobResult(Configuration conf, JobID jobID) throws Exception;
- var shutdownDisabled =
- upgradeMode != UpgradeMode.LAST_STATE
- && FlinkUtils.clusterShutdownDisabled(
- ReconciliationUtils.getDeployedSpec(deployment));
- if (!shutdownDisabled) {
- waitForClusterShutdown(conf);
- deploymentStatus.setJobManagerDeploymentStatus(JobManagerDeploymentStatus.MISSING);
- }
- }
+ void cancelJob(FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration conf)
+ throws Exception;
- @VisibleForTesting
- protected void waitForClusterShutdown(Configuration conf) {
- FlinkUtils.waitForClusterShutdown(
- kubernetesClient,
- conf,
- configManager
- .getOperatorConfiguration()
- .getFlinkShutdownClusterTimeout()
- .toSeconds());
- }
+ void deleteClusterDeployment(
+ ObjectMeta meta, FlinkDeploymentStatus status, boolean deleteHaData);
- public void deleteClusterDeployment(
- ObjectMeta meta, FlinkDeploymentStatus status, boolean deleteHaData) {
- FlinkUtils.deleteCluster(
- status,
- meta,
- kubernetesClient,
- deleteHaData,
- configManager
- .getOperatorConfiguration()
- .getFlinkShutdownClusterTimeout()
- .toSeconds());
- }
+ void cancelSessionJob(FlinkSessionJob sessionJob, UpgradeMode upgradeMode, Configuration conf)
+ throws Exception;
- public void cancelSessionJob(
- FlinkSessionJob sessionJob, UpgradeMode upgradeMode, Configuration conf)
- throws Exception {
-
- var jobStatus = sessionJob.getStatus().getJobStatus();
- var jobIdString = jobStatus.getJobId();
- Preconditions.checkNotNull(jobIdString, "The job to be suspend should not be null");
- var jobId = JobID.fromHexString(jobIdString);
-
- Optional<String> savepointOpt = Optional.empty();
- try (ClusterClient<String> clusterClient = getClusterClient(conf)) {
- final String clusterId = clusterClient.getClusterId();
- switch (upgradeMode) {
- case STATELESS:
- LOG.info("Cancelling job.");
- clusterClient
- .cancel(jobId)
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkCancelJobTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
- LOG.info("Job successfully cancelled.");
- break;
- case SAVEPOINT:
- LOG.info("Suspending job with savepoint.");
- final String savepointDirectory =
- Preconditions.checkNotNull(
- conf.get(CheckpointingOptions.SAVEPOINT_DIRECTORY));
- final long timeout =
- conf.get(ExecutionCheckpointingOptions.CHECKPOINTING_TIMEOUT)
- .getSeconds();
- try {
- String savepoint =
- clusterClient
- .stopWithSavepoint(
- jobId,
- false,
- savepointDirectory,
- conf.get(FLINK_VERSION)
- .isNewerVersionThan(
- FlinkVersion.v1_14)
- ? SavepointFormatType.DEFAULT
- : null)
- .get(timeout, TimeUnit.SECONDS);
- savepointOpt = Optional.of(savepoint);
- LOG.info("Job successfully suspended with savepoint {}.", savepoint);
- } catch (TimeoutException exception) {
- throw new FlinkException(
- String.format(
- "Timed out stopping the job %s in Flink cluster %s with savepoint, "
- + "please configure a larger timeout via '%s'",
- jobId,
- clusterId,
- ExecutionCheckpointingOptions.CHECKPOINTING_TIMEOUT.key()),
- exception);
- }
- break;
- case LAST_STATE:
- default:
- throw new RuntimeException("Unsupported upgrade mode " + upgradeMode);
- }
- }
-
- jobStatus.setState(JobStatus.FINISHED.name());
- savepointOpt.ifPresent(
- location -> {
- Savepoint sp = Savepoint.of(location, SavepointTriggerType.UPGRADE);
- jobStatus.getSavepointInfo().updateLastSavepoint(sp);
- });
- }
-
- public void triggerSavepoint(
+ void triggerSavepoint(
String jobId,
SavepointTriggerType triggerType,
org.apache.flink.kubernetes.operator.crd.status.SavepointInfo savepointInfo,
Configuration conf)
- throws Exception {
- LOG.info("Triggering new savepoint");
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- SavepointTriggerHeaders savepointTriggerHeaders = SavepointTriggerHeaders.getInstance();
- SavepointTriggerMessageParameters savepointTriggerMessageParameters =
- savepointTriggerHeaders.getUnresolvedMessageParameters();
- savepointTriggerMessageParameters.jobID.resolve(JobID.fromHexString(jobId));
-
- final String savepointDirectory =
- Preconditions.checkNotNull(conf.get(CheckpointingOptions.SAVEPOINT_DIRECTORY));
- final long timeout =
- configManager.getOperatorConfiguration().getFlinkClientTimeout().getSeconds();
- TriggerResponse response =
- clusterClient
- .sendRequest(
- savepointTriggerHeaders,
- savepointTriggerMessageParameters,
- new SavepointTriggerRequestBody(
- savepointDirectory,
- false,
- conf.get(FLINK_VERSION)
- .isNewerVersionThan(FlinkVersion.v1_14)
- ? SavepointFormatType.DEFAULT
- : null,
- null))
- .get(timeout, TimeUnit.SECONDS);
- LOG.info("Savepoint successfully triggered: " + response.getTriggerId().toHexString());
-
- savepointInfo.setTrigger(response.getTriggerId().toHexString(), triggerType);
- }
- }
-
- public Optional<Savepoint> getLastCheckpoint(JobID jobId, Configuration conf) throws Exception {
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
-
- var headers = CustomCheckpointingStatisticsHeaders.getInstance();
- var params = headers.getUnresolvedMessageParameters();
- params.jobPathParameter.resolve(jobId);
-
- CompletableFuture<CheckpointHistoryWrapper> response =
- clusterClient.sendRequest(headers, params, EmptyRequestBody.getInstance());
-
- var checkpoints =
- response.get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .getSeconds(),
- TimeUnit.SECONDS);
-
- var latestCheckpointOpt = checkpoints.getLatestCheckpointPath();
-
- if (latestCheckpointOpt.isPresent()
- && latestCheckpointOpt
- .get()
- .equals(
- NonPersistentMetadataCheckpointStorageLocation
- .EXTERNAL_POINTER)) {
- throw new DeploymentFailedException(
- "Latest checkpoint not externally addressable, manual recovery required.",
- "CheckpointNotFound");
- }
- return latestCheckpointOpt.map(
- pointer -> Savepoint.of(pointer, SavepointTriggerType.UNKNOWN));
- }
- }
-
- public SavepointFetchResult fetchSavepointInfo(
- String triggerId, String jobId, Configuration conf) {
- LOG.info("Fetching savepoint result with triggerId: " + triggerId);
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- SavepointStatusHeaders savepointStatusHeaders = SavepointStatusHeaders.getInstance();
- SavepointStatusMessageParameters savepointStatusMessageParameters =
- savepointStatusHeaders.getUnresolvedMessageParameters();
- savepointStatusMessageParameters.jobIdPathParameter.resolve(JobID.fromHexString(jobId));
- savepointStatusMessageParameters.triggerIdPathParameter.resolve(
- TriggerId.fromHexString(triggerId));
- CompletableFuture<AsynchronousOperationResult<SavepointInfo>> response =
- clusterClient.sendRequest(
- savepointStatusHeaders,
- savepointStatusMessageParameters,
- EmptyRequestBody.getInstance());
-
- if (response.get() == null || response.get().resource() == null) {
- return SavepointFetchResult.pending();
- }
-
- if (response.get().resource().getLocation() == null) {
- if (response.get().resource().getFailureCause() != null) {
- LOG.error(
- "Failure occurred while fetching the savepoint result",
- response.get().resource().getFailureCause());
- return SavepointFetchResult.error(
- response.get().resource().getFailureCause().toString());
- } else {
- return SavepointFetchResult.pending();
- }
- }
- String location = response.get().resource().getLocation();
- LOG.info("Savepoint result: {}", location);
- return SavepointFetchResult.completed(location);
- } catch (Exception e) {
- LOG.error("Exception while fetching the savepoint result", e);
- return SavepointFetchResult.error(e.getMessage());
- }
- }
+ throws Exception;
- public void disposeSavepoint(String savepointPath, Configuration conf) throws Exception {
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
- clusterClient
- .sendRequest(
- SavepointDisposalTriggerHeaders.getInstance(),
- EmptyMessageParameters.getInstance(),
- new SavepointDisposalRequest(savepointPath))
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .getSeconds(),
- TimeUnit.SECONDS);
- }
- }
+ Optional<Savepoint> getLastCheckpoint(JobID jobId, Configuration conf) throws Exception;
- public Map<String, String> getClusterInfo(Configuration conf) throws Exception {
- Map<String, String> runtimeVersion = new HashMap<>();
+ SavepointFetchResult fetchSavepointInfo(String triggerId, String jobId, Configuration conf);
- try (RestClusterClient<String> clusterClient =
- (RestClusterClient<String>) getClusterClient(conf)) {
+ void disposeSavepoint(String savepointPath, Configuration conf) throws Exception;
- CustomDashboardConfiguration dashboardConfiguration =
- clusterClient
- .sendRequest(
- CustomDashboardConfigurationHeaders.getInstance(),
- EmptyMessageParameters.getInstance(),
- EmptyRequestBody.getInstance())
- .get(
- configManager
- .getOperatorConfiguration()
- .getFlinkClientTimeout()
- .toSeconds(),
- TimeUnit.SECONDS);
+ Map<String, String> getClusterInfo(Configuration conf) throws Exception;
- runtimeVersion.put(
- DashboardConfiguration.FIELD_NAME_FLINK_VERSION,
- dashboardConfiguration.getFlinkVersion());
- runtimeVersion.put(
- DashboardConfiguration.FIELD_NAME_FLINK_REVISION,
- dashboardConfiguration.getFlinkRevision());
- }
- return runtimeVersion;
- }
+ PodList getJmPodList(FlinkDeployment deployment, Configuration conf);
- public PodList getJmPodList(FlinkDeployment deployment, Configuration conf) {
- final String namespace = conf.getString(KubernetesConfigOptions.NAMESPACE);
- final String clusterId;
- try (ClusterClient<String> clusterClient = getClusterClient(conf)) {
- clusterId = clusterClient.getClusterId();
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- return FlinkUtils.getJmPodList(kubernetesClient, namespace, clusterId);
- }
+ void waitForClusterShutdown(Configuration conf);
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java
new file mode 100644
index 00000000..761f1e68
--- /dev/null
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
+import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
+
+import io.fabric8.kubernetes.client.KubernetesClient;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** The factory to create the FlinkService based on the {@link FlinkDeployment} mode. */
+public class FlinkServiceFactory {
+
+ private final KubernetesClient kubernetesClient;
+ private final FlinkConfigManager configManager;
+ private final Map<KubernetesDeploymentMode, FlinkService> serviceMap;
+
+ public FlinkServiceFactory(
+ KubernetesClient kubernetesClient, FlinkConfigManager configManager) {
+ this.kubernetesClient = kubernetesClient;
+ this.configManager = configManager;
+ this.serviceMap = new ConcurrentHashMap<>();
+ }
+
+ public FlinkService getOrCreate(KubernetesDeploymentMode deploymentMode) {
+ return serviceMap.computeIfAbsent(
+ deploymentMode,
+ mode -> {
+ switch (mode) {
+ case NATIVE:
+ return new NativeFlinkService(kubernetesClient, configManager);
+ case STANDALONE:
+ return new StandaloneFlinkService(kubernetesClient, configManager);
+ default:
+ throw new UnsupportedOperationException(
+ String.format("Unsupported deployment mode: %s", mode));
+ }
+ });
+ }
+
+ public FlinkService getOrCreate(FlinkDeployment deployment) {
+ return getOrCreate(getDeploymentMode(deployment));
+ }
+
+ private KubernetesDeploymentMode getDeploymentMode(FlinkDeployment deployment) {
+ return KubernetesDeploymentMode.getDeploymentMode(deployment);
+ }
+}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java
new file mode 100644
index 00000000..60b5ee3b
--- /dev/null
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java
@@ -0,0 +1,178 @@
+/*
+ * 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 org.apache.flink.api.common.JobStatus;
+import org.apache.flink.client.cli.ApplicationDeployer;
+import org.apache.flink.client.deployment.ClusterClientFactory;
+import org.apache.flink.client.deployment.ClusterClientServiceLoader;
+import org.apache.flink.client.deployment.ClusterDescriptor;
+import org.apache.flink.client.deployment.DefaultClusterClientServiceLoader;
+import org.apache.flink.client.deployment.application.ApplicationConfiguration;
+import org.apache.flink.client.deployment.application.cli.ApplicationClusterDeployer;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
+import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
+import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
+import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatus;
+import org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
+import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
+import org.apache.flink.kubernetes.utils.KubernetesUtils;
+
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.PodList;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.flink.kubernetes.utils.Constants.LABEL_CONFIGMAP_TYPE_HIGH_AVAILABILITY;
+
+/**
+ * Implementation of {@link FlinkService} submitting and interacting with Native Kubernetes Flink
+ * clusters and jobs.
+ */
+public class NativeFlinkService extends AbstractFlinkService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NativeFlinkService.class);
+
+ public NativeFlinkService(KubernetesClient kubernetesClient, FlinkConfigManager configManager) {
+ super(kubernetesClient, configManager);
+ }
+
+ @Override
+ public void submitApplicationCluster(
+ JobSpec jobSpec, Configuration conf, boolean requireHaMetadata) throws Exception {
+ LOG.info(
+ "Deploying application cluster{}",
+ requireHaMetadata ? " requiring last-state from HA metadata" : "");
+ if (FlinkUtils.isKubernetesHAActivated(conf)) {
+ final String clusterId = conf.get(KubernetesConfigOptions.CLUSTER_ID);
+ final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
+ // Delete the job graph in the HA ConfigMaps so that the newly changed job config(e.g.
+ // parallelism) could take effect
+ FlinkUtils.deleteJobGraphInKubernetesHA(clusterId, namespace, kubernetesClient);
+ }
+ if (requireHaMetadata) {
+ validateHaMetadataExists(conf);
+ }
+ final ClusterClientServiceLoader clusterClientServiceLoader =
+ new DefaultClusterClientServiceLoader();
+ final ApplicationDeployer deployer =
+ new ApplicationClusterDeployer(clusterClientServiceLoader);
+
+ final ApplicationConfiguration applicationConfiguration =
+ new ApplicationConfiguration(
+ jobSpec.getArgs() != null ? jobSpec.getArgs() : new String[0],
+ jobSpec.getEntryClass());
+
+ deployer.run(conf, applicationConfiguration);
+ LOG.info("Application cluster successfully deployed");
+ }
+
+ private void validateHaMetadataExists(Configuration conf) {
+ if (!isHaMetadataAvailable(conf)) {
+ throw new DeploymentFailedException(
+ "HA metadata not available to restore from last state. "
+ + "It is possible that the job has finished or terminally failed, or the configmaps have been deleted. "
+ + "Manual restore required.",
+ "RestoreFailed");
+ }
+ }
+
+ @Override
+ public void submitSessionCluster(Configuration conf) throws Exception {
+ LOG.info("Deploying session cluster");
+ final ClusterClientServiceLoader clusterClientServiceLoader =
+ new DefaultClusterClientServiceLoader();
+ final ClusterClientFactory<String> kubernetesClusterClientFactory =
+ clusterClientServiceLoader.getClusterClientFactory(conf);
+ try (final ClusterDescriptor<String> kubernetesClusterDescriptor =
+ kubernetesClusterClientFactory.createClusterDescriptor(conf)) {
+ kubernetesClusterDescriptor.deploySessionCluster(
+ kubernetesClusterClientFactory.getClusterSpecification(conf));
+ }
+ LOG.info("Session cluster successfully deployed");
+ }
+
+ @Override
+ public void deleteClusterDeployment(
+ ObjectMeta meta, FlinkDeploymentStatus status, boolean deleteHaData) {
+ deleteCluster(
+ status,
+ meta,
+ deleteHaData,
+ configManager
+ .getOperatorConfiguration()
+ .getFlinkShutdownClusterTimeout()
+ .toSeconds());
+ }
+
+ @Override
+ protected PodList getJmPodList(String namespace, String clusterId) {
+ return kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(KubernetesUtils.getJobManagerSelectors(clusterId))
+ .list();
+ }
+
+ /**
+ * Delete Flink kubernetes cluster by deleting the kubernetes resources directly. Optionally
+ * allows deleting the native kubernetes HA resources as well.
+ *
+ * @param status Deployment status object
+ * @param meta ObjectMeta of the deployment
+ * @param deleteHaConfigmaps Flag to indicate whether k8s HA metadata should be removed as well
+ * @param shutdownTimeout maximum time allowed for cluster shutdown
+ */
+ private void deleteCluster(
+ FlinkDeploymentStatus status,
+ ObjectMeta meta,
+ boolean deleteHaConfigmaps,
+ long shutdownTimeout) {
+
+ String namespace = meta.getNamespace();
+ String clusterId = meta.getName();
+
+ LOG.info(
+ "Deleting JobManager deployment {}.",
+ deleteHaConfigmaps ? "and HA metadata" : "while preserving HA metadata");
+ kubernetesClient
+ .apps()
+ .deployments()
+ .inNamespace(namespace)
+ .withName(KubernetesUtils.getDeploymentName(clusterId))
+ .cascading(true)
+ .delete();
+
+ if (deleteHaConfigmaps) {
+ // We need to wait for cluster shutdown otherwise HA configmaps might be recreated
+ waitForClusterShutdown(namespace, clusterId, shutdownTimeout);
+ kubernetesClient
+ .configMaps()
+ .inNamespace(namespace)
+ .withLabels(
+ KubernetesUtils.getConfigMapLabels(
+ clusterId, LABEL_CONFIGMAP_TYPE_HIGH_AVAILABILITY))
+ .delete();
+ }
+ status.setJobManagerDeploymentStatus(JobManagerDeploymentStatus.MISSING);
+ status.getJobStatus().setState(JobStatus.FINISHED.name());
+ }
+}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java
new file mode 100644
index 00000000..5a0b85eb
--- /dev/null
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java
@@ -0,0 +1,163 @@
+/*
+ * 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 org.apache.flink.annotation.VisibleForTesting;
+import org.apache.flink.client.deployment.ClusterDeploymentException;
+import org.apache.flink.client.deployment.ClusterSpecification;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.kubernetes.KubernetesClusterClientFactory;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
+import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
+import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
+import org.apache.flink.kubernetes.operator.kubeclient.Fabric8FlinkStandaloneKubeClient;
+import org.apache.flink.kubernetes.operator.kubeclient.FlinkStandaloneKubeClient;
+import org.apache.flink.kubernetes.operator.standalone.KubernetesStandaloneClusterDescriptor;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.kubernetes.utils.KubernetesUtils;
+import org.apache.flink.util.concurrent.ExecutorThreadFactory;
+
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.PodList;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.apache.flink.kubernetes.utils.Constants.LABEL_CONFIGMAP_TYPE_HIGH_AVAILABILITY;
+
+/**
+ * Implementation of {@link FlinkService} submitting and interacting with Standalone Kubernetes
+ * Flink clusters and jobs.
+ */
+public class StandaloneFlinkService extends AbstractFlinkService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(StandaloneFlinkService.class);
+
+ public StandaloneFlinkService(
+ KubernetesClient kubernetesClient, FlinkConfigManager configManager) {
+ super(kubernetesClient, configManager);
+ }
+
+ @Override
+ public void submitApplicationCluster(
+ JobSpec jobSpec, Configuration conf, boolean requireHaMetadata) throws Exception {
+ LOG.info("Deploying application cluster");
+ // TODO some HA stuff?
+ submitClusterInternal(conf);
+ LOG.info("Application cluster successfully deployed");
+ }
+
+ @Override
+ public void submitSessionCluster(Configuration conf) throws Exception {
+ LOG.info("Deploying session cluster");
+ // TODO some HA stuff?
+ submitClusterInternal(conf);
+ LOG.info("Session cluster successfully deployed");
+ }
+
+ @Override
+ public void deleteClusterDeployment(
+ ObjectMeta meta, FlinkDeploymentStatus status, boolean deleteHaData) {
+ deleteClusterInternal(meta, deleteHaData);
+ }
+
+ @Override
+ protected PodList getJmPodList(String namespace, String clusterId) {
+ return kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(StandaloneKubernetesUtils.getJobManagerSelectors(clusterId))
+ .list();
+ }
+
+ @VisibleForTesting
+ protected FlinkStandaloneKubeClient createNamespacedKubeClient(
+ Configuration configuration, String namespace) {
+ final int poolSize =
+ configuration.get(KubernetesConfigOptions.KUBERNETES_CLIENT_IO_EXECUTOR_POOL_SIZE);
+
+ ExecutorService executorService =
+ Executors.newFixedThreadPool(
+ poolSize,
+ new ExecutorThreadFactory("flink-kubeclient-io-for-standalone-service"));
+
+ return new Fabric8FlinkStandaloneKubeClient(
+ configuration,
+ Fabric8FlinkStandaloneKubeClient.createNamespacedKubeClient(namespace),
+ executorService);
+ }
+
+ private void submitClusterInternal(Configuration conf) throws ClusterDeploymentException {
+ final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
+
+ FlinkStandaloneKubeClient client = createNamespacedKubeClient(conf, namespace);
+ try (final KubernetesStandaloneClusterDescriptor kubernetesClusterDescriptor =
+ new KubernetesStandaloneClusterDescriptor(conf, client)) {
+ kubernetesClusterDescriptor.deploySessionCluster(getClusterSpecification(conf));
+ }
+ }
+
+ private ClusterSpecification getClusterSpecification(Configuration conf) {
+ return new KubernetesClusterClientFactory().getClusterSpecification(conf);
+ }
+
+ private void deleteClusterInternal(ObjectMeta meta, boolean deleteHaConfigmaps) {
+ final String clusterId = meta.getName();
+ final String namespace = meta.getNamespace();
+
+ LOG.info("Deleting Flink Standalone cluster TM resources");
+ kubernetesClient
+ .apps()
+ .deployments()
+ .inNamespace(namespace)
+ .withName(StandaloneKubernetesUtils.getTaskManagerDeploymentName(clusterId))
+ .cascading(true)
+ .delete();
+
+ LOG.info("Deleting Flink Standalone cluster JM resources");
+ kubernetesClient
+ .apps()
+ .deployments()
+ .inNamespace(namespace)
+ .withName(StandaloneKubernetesUtils.getJobManagerDeploymentName(clusterId))
+ .cascading(true)
+ .delete();
+
+ if (deleteHaConfigmaps) {
+ // We need to wait for cluster shutdown otherwise HA configmaps might be recreated
+ waitForClusterShutdown(
+ namespace,
+ clusterId,
+ configManager
+ .getOperatorConfiguration()
+ .getFlinkShutdownClusterTimeout()
+ .toSeconds());
+ kubernetesClient
+ .configMaps()
+ .inNamespace(namespace)
+ .withLabels(
+ KubernetesUtils.getConfigMapLabels(
+ clusterId, LABEL_CONFIGMAP_TYPE_HIGH_AVAILABILITY))
+ .delete();
+ }
+ }
+}
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 c1de80e6..05cb7e03 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
@@ -18,18 +18,14 @@
package org.apache.flink.kubernetes.operator.utils;
import org.apache.flink.api.common.JobID;
-import org.apache.flink.api.common.JobStatus;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.CoreOptions;
import org.apache.flink.configuration.HighAvailabilityOptions;
import org.apache.flink.configuration.TaskManagerOptions;
import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
import org.apache.flink.kubernetes.highavailability.KubernetesHaServicesFactory;
-import org.apache.flink.kubernetes.kubeclient.decorators.ExternalServiceDecorator;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkDeploymentSpec;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkVersion;
-import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
-import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatus;
import org.apache.flink.kubernetes.utils.Constants;
import org.apache.flink.kubernetes.utils.KubernetesUtils;
import org.apache.flink.util.Preconditions;
@@ -42,8 +38,6 @@ import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapList;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.PodList;
-import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,8 +48,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.function.Predicate;
-import static org.apache.flink.kubernetes.utils.Constants.LABEL_CONFIGMAP_TYPE_HIGH_AVAILABILITY;
-
/** Flink Utility methods used by the operator. */
public class FlinkUtils {
@@ -107,133 +99,6 @@ public class FlinkUtils {
}
}
- /**
- * Delete Flink kubernetes cluster by deleting the kubernetes resources directly. Optionally
- * allows deleting the native kubernetes HA resources as well.
- *
- * @param status Deployment status object
- * @param meta ObjectMeta of the deployment
- * @param kubernetesClient Kubernetes client
- * @param deleteHaConfigmaps Flag to indicate whether k8s HA metadata should be removed as well
- * @param shutdownTimeout maximum time allowed for cluster shutdown
- */
- public static void deleteCluster(
- FlinkDeploymentStatus status,
- ObjectMeta meta,
- KubernetesClient kubernetesClient,
- boolean deleteHaConfigmaps,
- long shutdownTimeout) {
-
- String namespace = meta.getNamespace();
- String clusterId = meta.getName();
-
- LOG.info(
- "Deleting JobManager deployment {}.",
- deleteHaConfigmaps ? "and HA metadata" : "while preserving HA metadata");
- kubernetesClient
- .apps()
- .deployments()
- .inNamespace(namespace)
- .withName(KubernetesUtils.getDeploymentName(clusterId))
- .cascading(true)
- .delete();
-
- if (deleteHaConfigmaps) {
- // We need to wait for cluster shutdown otherwise HA configmaps might be recreated
- waitForClusterShutdown(kubernetesClient, namespace, clusterId, shutdownTimeout);
- kubernetesClient
- .configMaps()
- .inNamespace(namespace)
- .withLabels(
- KubernetesUtils.getConfigMapLabels(
- clusterId, LABEL_CONFIGMAP_TYPE_HIGH_AVAILABILITY))
- .delete();
- }
- status.setJobManagerDeploymentStatus(JobManagerDeploymentStatus.MISSING);
- status.getJobStatus().setState(JobStatus.FINISHED.name());
- }
-
- /**
- * Wait until the FLink cluster has completely shut down.
- *
- * @param kubernetesClient Kubernetes client.
- * @param namespace Resource namespace.
- * @param clusterId Flink clusterId.
- * @param shutdownTimeout Max time allowed for shutdown.
- */
- public static void waitForClusterShutdown(
- KubernetesClient kubernetesClient,
- String namespace,
- String clusterId,
- long shutdownTimeout) {
-
- boolean jobManagerRunning = true;
- boolean serviceRunning = true;
-
- for (int i = 0; i < shutdownTimeout; i++) {
- if (jobManagerRunning) {
- PodList jmPodList = getJmPodList(kubernetesClient, namespace, clusterId);
-
- if (jmPodList == null || jmPodList.getItems().isEmpty()) {
- jobManagerRunning = false;
- }
- }
-
- if (serviceRunning) {
- Service service =
- kubernetesClient
- .services()
- .inNamespace(namespace)
- .withName(
- ExternalServiceDecorator.getExternalServiceName(clusterId))
- .fromServer()
- .get();
- if (service == null) {
- serviceRunning = false;
- }
- }
-
- if (!jobManagerRunning && !serviceRunning) {
- break;
- }
- // log a message waiting to shutdown Flink cluster every 5 seconds.
- if ((i + 1) % 5 == 0) {
- LOG.info("Waiting for cluster shutdown... ({}s)", i + 1);
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- LOG.info("Cluster shutdown completed.");
- }
-
- /**
- * Wait until the FLink cluster has completely shut down.
- *
- * @param kubernetesClient Kubernetes client.
- * @param conf Flink configuration.
- * @param shutdownTimeout Max time allowed for shutdown.
- */
- public static void waitForClusterShutdown(
- KubernetesClient kubernetesClient, Configuration conf, long shutdownTimeout) {
- FlinkUtils.waitForClusterShutdown(
- kubernetesClient,
- conf.getString(KubernetesConfigOptions.NAMESPACE),
- conf.getString(KubernetesConfigOptions.CLUSTER_ID),
- shutdownTimeout);
- }
-
- public static PodList getJmPodList(
- KubernetesClient kubernetesClient, String namespace, String clusterId) {
- return kubernetesClient
- .pods()
- .inNamespace(namespace)
- .withLabels(KubernetesUtils.getJobManagerSelectors(clusterId))
- .list();
- }
-
public static void deleteJobGraphInKubernetesHA(
String clusterId, String namespace, KubernetesClient kubernetesClient) {
// The HA ConfigMap names have been changed from 1.15, so we use the labels to filter out
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 64a0b6de..2e99c53f 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
@@ -36,8 +36,9 @@ import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatu
import org.apache.flink.kubernetes.operator.crd.status.Savepoint;
import org.apache.flink.kubernetes.operator.crd.status.SavepointInfo;
import org.apache.flink.kubernetes.operator.crd.status.SavepointTriggerType;
+import org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
import org.apache.flink.kubernetes.operator.observer.SavepointFetchResult;
-import org.apache.flink.kubernetes.operator.service.FlinkService;
+import org.apache.flink.kubernetes.operator.service.AbstractFlinkService;
import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
import org.apache.flink.runtime.client.JobStatusMessage;
import org.apache.flink.runtime.execution.ExecutionState;
@@ -75,7 +76,7 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
/** Flink service mock for tests. */
-public class TestingFlinkService extends FlinkService {
+public class TestingFlinkService extends AbstractFlinkService {
public static final Map<String, String> CLUSTER_INFO =
Map.of(
@@ -172,6 +173,16 @@ public class TestingFlinkService extends FlinkService {
jobs.add(Tuple2.of(conf.get(SavepointConfigOptions.SAVEPOINT_PATH), jobStatusMessage));
}
+ protected void validateHaMetadataExists(Configuration conf) {
+ if (!isHaMetadataAvailable(conf)) {
+ throw new DeploymentFailedException(
+ "HA metadata not available to restore from last state. "
+ + "It is possible that the job has finished or terminally failed, or the configmaps have been deleted. "
+ + "Manual restore required.",
+ "RestoreFailed");
+ }
+ }
+
@Override
public boolean isHaMetadataAvailable(Configuration conf) {
return FlinkUtils.isKubernetesHAActivated(conf) && haDataAvailable;
@@ -384,7 +395,7 @@ public class TestingFlinkService extends FlinkService {
}
@Override
- protected void waitForClusterShutdown(Configuration conf) {}
+ public void waitForClusterShutdown(Configuration conf) {}
@Override
public void disposeSavepoint(String savepointPath, Configuration conf) {
@@ -430,6 +441,11 @@ public class TestingFlinkService extends FlinkService {
return podList;
}
+ @Override
+ protected PodList getJmPodList(String namespace, String clusterId) {
+ return podList;
+ }
+
public void setJmPodList(PodList podList) {
this.podList = podList;
}
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkServiceFactory.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkServiceFactory.java
new file mode 100644
index 00000000..e4e4d5e0
--- /dev/null
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/TestingFlinkServiceFactory.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
+import org.apache.flink.kubernetes.operator.service.FlinkService;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
+
+import java.lang.module.Configuration;
+
+/** Flink service factory mock for tests. */
+public class TestingFlinkServiceFactory extends FlinkServiceFactory {
+ private final FlinkService flinkService;
+
+ public TestingFlinkServiceFactory() {
+ super(null, null);
+ flinkService = new TestingFlinkService();
+ }
+
+ public TestingFlinkServiceFactory(FlinkService flinkService) {
+ super(null, null);
+ this.flinkService = flinkService;
+ }
+
+ public FlinkService getOrCreate(KubernetesDeploymentMode deploymentMode) {
+ return flinkService;
+ }
+
+ public FlinkService getOrCreate(FlinkDeployment deployment) {
+ return flinkService;
+ }
+
+ public FlinkService getOrCreate(Configuration configuration) {
+ return flinkService;
+ }
+}
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java
index 2def2b4a..c169e1d9 100644
--- a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java
@@ -19,6 +19,7 @@ package org.apache.flink.kubernetes.operator.controller;
import org.apache.flink.kubernetes.operator.TestUtils;
import org.apache.flink.kubernetes.operator.TestingFlinkService;
+import org.apache.flink.kubernetes.operator.TestingFlinkServiceFactory;
import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
import org.apache.flink.kubernetes.operator.crd.AbstractFlinkResource;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
@@ -26,6 +27,7 @@ import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.observer.deployment.ObserverFactory;
import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
import org.apache.flink.kubernetes.operator.reconciler.deployment.ReconcilerFactory;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
import org.apache.flink.kubernetes.operator.utils.ValidatorUtils;
@@ -67,6 +69,7 @@ public class TestingFlinkDeploymentController
FlinkConfigManager configManager,
KubernetesClient kubernetesClient,
TestingFlinkService flinkService) {
+ FlinkServiceFactory flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
eventRecorder = new EventRecorder(kubernetesClient, eventCollector);
statusRecorder =
new StatusRecorder<>(
@@ -79,12 +82,12 @@ public class TestingFlinkDeploymentController
ValidatorUtils.discoverValidators(configManager),
new ReconcilerFactory(
kubernetesClient,
- flinkService,
+ flinkServiceFactory,
configManager,
eventRecorder,
statusRecorder),
new ObserverFactory(
- flinkService, configManager, statusRecorder, eventRecorder),
+ flinkServiceFactory, configManager, statusRecorder, eventRecorder),
statusRecorder,
eventRecorder);
}
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 e7270bd7..f801bfbc 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
@@ -24,12 +24,14 @@ import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.RestOptions;
import org.apache.flink.kubernetes.operator.TestUtils;
import org.apache.flink.kubernetes.operator.TestingFlinkService;
+import org.apache.flink.kubernetes.operator.TestingFlinkServiceFactory;
import org.apache.flink.kubernetes.operator.TestingStatusRecorder;
import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
import org.apache.flink.kubernetes.operator.crd.status.FlinkSessionJobStatus;
import org.apache.flink.kubernetes.operator.crd.status.ReconciliationState;
import org.apache.flink.kubernetes.operator.crd.status.SavepointTriggerType;
import org.apache.flink.kubernetes.operator.reconciler.sessionjob.SessionJobReconciler;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
import org.apache.flink.kubernetes.operator.utils.SavepointUtils;
@@ -52,6 +54,7 @@ public class SessionJobObserverTest {
private TestingFlinkService flinkService;
private SessionJobObserver observer;
private SessionJobReconciler reconciler;
+ private FlinkServiceFactory flinkServiceFactory;
@BeforeEach
public void before() {
@@ -59,12 +62,14 @@ public class SessionJobObserverTest {
var eventRecorder = new EventRecorder(kubernetesClient, (r, e) -> {});
var statusRecorder = new TestingStatusRecorder<FlinkSessionJobStatus>();
flinkService = new TestingFlinkService();
+ flinkServiceFactory = flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
observer =
- new SessionJobObserver(flinkService, configManager, statusRecorder, eventRecorder);
+ new SessionJobObserver(
+ flinkServiceFactory, configManager, statusRecorder, eventRecorder);
reconciler =
new SessionJobReconciler(
kubernetesClient,
- flinkService,
+ flinkServiceFactory,
configManager,
eventRecorder,
statusRecorder);
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 b7122159..2bb1f089 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
@@ -488,6 +488,8 @@ public class ApplicationReconcilerTest {
@Test
public void testRandomJobResultStorePath() throws Exception {
FlinkDeployment flinkApp = TestUtils.buildApplicationCluster();
+ Context context = flinkService.getContext();
+
final String haStoragePath = "file:///flink-data/ha";
flinkApp.getSpec()
.getFlinkConfiguration()
@@ -500,14 +502,14 @@ public class ApplicationReconcilerTest {
status.getJobStatus().setState(org.apache.flink.api.common.JobStatus.FINISHED.name());
status.setJobManagerDeploymentStatus(JobManagerDeploymentStatus.READY);
- reconciler.deploy(flinkApp, spec, status, deployConfig, Optional.empty(), false);
+ reconciler.deploy(flinkApp, spec, status, context, deployConfig, Optional.empty(), false);
String path1 = deployConfig.get(JobResultStoreOptions.STORAGE_PATH);
Assertions.assertTrue(path1.startsWith(haStoragePath));
status.getJobStatus().setState(org.apache.flink.api.common.JobStatus.FINISHED.name());
status.setJobManagerDeploymentStatus(JobManagerDeploymentStatus.READY);
- reconciler.deploy(flinkApp, spec, status, deployConfig, Optional.empty(), false);
+ reconciler.deploy(flinkApp, spec, status, context, deployConfig, Optional.empty(), false);
String path2 = deployConfig.get(JobResultStoreOptions.STORAGE_PATH);
Assertions.assertTrue(path2.startsWith(haStoragePath));
assertNotEquals(path1, path2);
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 a28e2f19..998fa34d 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
@@ -22,6 +22,7 @@ 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.TestingFlinkServiceFactory;
import org.apache.flink.kubernetes.operator.TestingStatusRecorder;
import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
import org.apache.flink.kubernetes.operator.config.KubernetesOperatorConfigOptions;
@@ -33,6 +34,7 @@ import org.apache.flink.kubernetes.operator.crd.status.FlinkSessionJobStatus;
import org.apache.flink.kubernetes.operator.crd.status.JobStatus;
import org.apache.flink.kubernetes.operator.crd.status.SavepointTriggerType;
import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
+import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
import org.apache.flink.kubernetes.operator.utils.EventRecorder;
import org.apache.flink.kubernetes.operator.utils.SavepointUtils;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
@@ -69,6 +71,8 @@ public class SessionJobReconcilerTest {
@BeforeEach
public void before() {
+ flinkService = new TestingFlinkService();
+ flinkServiceFactory = flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
eventRecorder =
new EventRecorder(null, (r, e) -> {}) {
@Override
@@ -83,14 +87,17 @@ public class SessionJobReconcilerTest {
};
reconciler =
new SessionJobReconciler(
- null, flinkService, configManager, eventRecorder, statusRecoder);
+ null, flinkServiceFactory, configManager, eventRecorder, statusRecoder);
kubernetesClient.resource(TestUtils.buildSessionJob()).createOrReplace();
statusRecoder = new TestingStatusRecorder<>();
reconciler =
new SessionJobReconciler(
- null, flinkService, configManager, eventRecorder, statusRecoder);
+ null, flinkServiceFactory, configManager, eventRecorder, statusRecoder);
}
+ private FlinkServiceFactory flinkServiceFactory;
+ private FlinkSessionJob sessionJob;
+
@Test
public void testSubmitAndCleanUp() throws Exception {
FlinkSessionJob sessionJob = TestUtils.buildSessionJob();
@@ -460,7 +467,7 @@ public class SessionJobReconcilerTest {
// Force upgrade when savepoint is in progress.
reconciler =
new SessionJobReconciler(
- null, flinkService, configManager, eventRecorder, statusRecoder);
+ null, flinkServiceFactory, configManager, eventRecorder, statusRecoder);
spSessionJob.getSpec().getJob().setParallelism(100);
reconciler.reconcile(spSessionJob, readyContext);
assertEquals(
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/FlinkServiceTest.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/NativeFlinkServiceTest.java
similarity index 98%
rename from flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/FlinkServiceTest.java
rename to flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/NativeFlinkServiceTest.java
index aebd5749..8e151f04 100644
--- a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/FlinkServiceTest.java
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/NativeFlinkServiceTest.java
@@ -68,7 +68,7 @@ import static org.junit.jupiter.api.Assertions.fail;
/** @link FlinkService unit tests */
@EnableKubernetesMockClient(crud = true)
-public class FlinkServiceTest {
+public class NativeFlinkServiceTest {
KubernetesClient client;
private final Configuration configuration = new Configuration();
private final FlinkConfigManager configManager = new FlinkConfigManager(configuration);
@@ -308,7 +308,7 @@ public class FlinkServiceTest {
Tuple2.of(ExecutionState.RUNNING, 4));
assertEquals(
org.apache.flink.api.common.JobStatus.RUNNING,
- FlinkService.getEffectiveStatus(allRunning));
+ AbstractFlinkService.getEffectiveStatus(allRunning));
JobDetails allRunningOrFinished =
getJobDetails(
@@ -317,7 +317,7 @@ public class FlinkServiceTest {
Tuple2.of(ExecutionState.FINISHED, 2));
assertEquals(
org.apache.flink.api.common.JobStatus.RUNNING,
- FlinkService.getEffectiveStatus(allRunningOrFinished));
+ AbstractFlinkService.getEffectiveStatus(allRunningOrFinished));
JobDetails allRunningOrScheduled =
getJobDetails(
@@ -326,7 +326,7 @@ public class FlinkServiceTest {
Tuple2.of(ExecutionState.SCHEDULED, 2));
assertEquals(
org.apache.flink.api.common.JobStatus.CREATED,
- FlinkService.getEffectiveStatus(allRunningOrScheduled));
+ AbstractFlinkService.getEffectiveStatus(allRunningOrScheduled));
JobDetails allFinished =
getJobDetails(
@@ -334,7 +334,7 @@ public class FlinkServiceTest {
Tuple2.of(ExecutionState.FINISHED, 4));
assertEquals(
org.apache.flink.api.common.JobStatus.FINISHED,
- FlinkService.getEffectiveStatus(allFinished));
+ AbstractFlinkService.getEffectiveStatus(allFinished));
}
private JobDetails getJobDetails(
@@ -358,7 +358,7 @@ public class FlinkServiceTest {
}
private FlinkService createFlinkService(ClusterClient<String> clusterClient) {
- return new FlinkService(client, new FlinkConfigManager(configuration)) {
+ return new NativeFlinkService(client, new FlinkConfigManager(configuration)) {
@Override
protected ClusterClient<String> getClusterClient(Configuration config) {
return clusterClient;
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkServiceTest.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkServiceTest.java
new file mode 100644
index 00000000..bbffabbd
--- /dev/null
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkServiceTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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 org.apache.flink.configuration.Configuration;
+import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
+import org.apache.flink.kubernetes.operator.TestUtils;
+import org.apache.flink.kubernetes.operator.config.FlinkConfigBuilder;
+import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
+import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
+import org.apache.flink.kubernetes.operator.utils.StandaloneKubernetesUtils;
+import org.apache.flink.util.concurrent.ExecutorThreadFactory;
+
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
+import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.junit.Assert.assertEquals;
+
+/** @link StandaloneFlinkService unit tests */
+@EnableKubernetesMockClient(crud = true)
+public class StandaloneFlinkServiceTest {
+ KubernetesMockServer mockServer;
+
+ private NamespacedKubernetesClient kubernetesClient;
+ StandaloneFlinkService flinkStandaloneService;
+ Configuration configuration = new Configuration();
+
+ @BeforeEach
+ public void setup() {
+ configuration.set(KubernetesConfigOptions.CLUSTER_ID, TestUtils.TEST_DEPLOYMENT_NAME);
+ configuration.set(KubernetesConfigOptions.NAMESPACE, TestUtils.TEST_NAMESPACE);
+
+ kubernetesClient = mockServer.createClient().inAnyNamespace();
+ flinkStandaloneService =
+ new StandaloneFlinkService(kubernetesClient, new FlinkConfigManager(configuration));
+
+ ExecutorService executorService =
+ Executors.newFixedThreadPool(
+ 1, new ExecutorThreadFactory("flink-kubeclient-io-for-standalone-service"));
+ }
+
+ @Test
+ public void testDeleteClusterDeployment() throws Exception {
+ FlinkDeployment flinkDeployment = TestUtils.buildSessionCluster();
+ configuration = buildConfig(flinkDeployment, configuration);
+
+ createDeployments();
+
+ List<Deployment> deployments = kubernetesClient.apps().deployments().list().getItems();
+
+ assertEquals(2, deployments.size());
+
+ flinkStandaloneService.deleteClusterDeployment(
+ flinkDeployment.getMetadata(), flinkDeployment.getStatus(), false);
+
+ deployments = kubernetesClient.apps().deployments().list().getItems();
+
+ assertEquals(0, deployments.size());
+ }
+
+ @Test
+ public void testDeleteClusterDeploymentWithHADelete() throws Exception {
+ FlinkDeployment flinkDeployment = TestUtils.buildSessionCluster();
+ configuration = buildConfig(flinkDeployment, configuration);
+
+ createDeployments();
+
+ List<Deployment> deployments = kubernetesClient.apps().deployments().list().getItems();
+ assertEquals(2, deployments.size());
+
+ flinkStandaloneService.deleteClusterDeployment(
+ flinkDeployment.getMetadata(), flinkDeployment.getStatus(), true);
+
+ deployments = kubernetesClient.apps().deployments().list().getItems();
+
+ assertEquals(0, deployments.size());
+ }
+
+ private Configuration buildConfig(FlinkDeployment flinkDeployment, Configuration configuration)
+ throws Exception {
+ return configuration =
+ FlinkConfigBuilder.buildFrom(
+ flinkDeployment.getMetadata().getNamespace(),
+ flinkDeployment.getMetadata().getName(),
+ flinkDeployment.getSpec(),
+ configuration);
+ }
+
+ private void createDeployments() {
+ Deployment jmDeployment = new Deployment();
+ ObjectMeta jmMetadata = new ObjectMeta();
+ jmMetadata.setName(
+ StandaloneKubernetesUtils.getJobManagerDeploymentName(
+ TestUtils.TEST_DEPLOYMENT_NAME));
+ jmDeployment.setMetadata(jmMetadata);
+ kubernetesClient
+ .apps()
+ .deployments()
+ .inNamespace(TestUtils.TEST_NAMESPACE)
+ .create(jmDeployment);
+
+ Deployment tmDeployment = new Deployment();
+ ObjectMeta tmMetadata = new ObjectMeta();
+ tmMetadata.setName(
+ StandaloneKubernetesUtils.getTaskManagerDeploymentName(
+ TestUtils.TEST_DEPLOYMENT_NAME));
+ tmDeployment.setMetadata(tmMetadata);
+ kubernetesClient
+ .apps()
+ .deployments()
+ .inNamespace(TestUtils.TEST_NAMESPACE)
+ .create(tmDeployment);
+ }
+}
diff --git a/helm/flink-kubernetes-operator/crds/flinkdeployments.flink.apache.org-v1.yml b/helm/flink-kubernetes-operator/crds/flinkdeployments.flink.apache.org-v1.yml
index 83f3b9e3..86d3a97a 100644
--- a/helm/flink-kubernetes-operator/crds/flinkdeployments.flink.apache.org-v1.yml
+++ b/helm/flink-kubernetes-operator/crds/flinkdeployments.flink.apache.org-v1.yml
@@ -9047,6 +9047,11 @@ spec:
additionalProperties:
type: string
type: object
+ mode:
+ enum:
+ - native
+ - standalone
+ type: string
job:
properties:
jarURI:
[flink-kubernetes-operator] 05/05: [FLINK-27446] Add standalone mode validation and config building
Posted by gy...@apache.org.
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
commit 79b169964cd3aa1ea3383b3180a601939fbd3a04
Author: Usamah Jassat <us...@amazon.com>
AuthorDate: Tue Jun 21 17:35:08 2022 +0100
[FLINK-27446] Add standalone mode validation and config building
---
.github/workflows/ci.yml | 2 +
docs/content/docs/custom-resource/reference.md | 11 +++
e2e-tests/data/flinkdep-cr.yaml | 1 +
e2e-tests/data/multi-sessionjob.yaml | 2 +
e2e-tests/data/sessionjob-cr.yaml | 1 +
.../operator/config/FlinkConfigBuilder.java | 72 +++++++++++++++++-
.../observer/deployment/ObserverFactory.java | 15 ++--
.../deployment/ApplicationReconciler.java | 3 +-
.../reconciler/deployment/ReconcilerFactory.java | 15 ++--
.../operator/service/AbstractFlinkService.java | 43 ++++++++++-
.../operator/service/FlinkServiceFactory.java | 7 ++
.../operator/service/NativeFlinkService.java | 39 +++-------
.../operator/service/StandaloneFlinkService.java | 36 +++++++--
.../operator/utils/EventSourceUtils.java | 6 +-
.../kubernetes/operator/utils/FlinkUtils.java | 4 +
.../operator/validation/DefaultValidator.java | 36 ++++++++-
.../kubernetes/operator/TestingFlinkService.java | 12 +++
.../operator/config/FlinkConfigBuilderTest.java | 85 ++++++++++++++++++++++
.../TestingFlinkDeploymentController.java | 1 +
.../sessionjob/SessionJobObserverTest.java | 3 +-
.../sessionjob/SessionJobReconcilerTest.java | 2 +-
.../operator/validation/DefaultValidatorTest.java | 69 ++++++++++++++++++
22 files changed, 400 insertions(+), 65 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1dde3421..b34a0d28 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -97,6 +97,7 @@ jobs:
matrix:
version: ["v1_15","v1_14","v1_13"]
namespace: ["default","flink"]
+ mode: ["native", "standalone"]
test:
- test_application_kubernetes_ha.sh
- test_application_operations.sh
@@ -155,6 +156,7 @@ jobs:
run: |
sed -i "s/image: flink:.*/image: ${{ matrix.image }}/" e2e-tests/data/*.yaml
sed -i "s/flinkVersion: .*/flinkVersion: ${{ matrix.version }}/" e2e-tests/data/*.yaml
+ sed -i "s/mode: .*/mode: ${{ matrix.mode }}/" e2e-tests/data/*.yaml
git diff HEAD
echo "Running e2e-tests/$test"
bash e2e-tests/${{ matrix.test }} || exit 1
diff --git a/docs/content/docs/custom-resource/reference.md b/docs/content/docs/custom-resource/reference.md
index 100db9d4..b2399505 100644
--- a/docs/content/docs/custom-resource/reference.md
+++ b/docs/content/docs/custom-resource/reference.md
@@ -57,6 +57,7 @@ This page serves as a full reference for FlinkDeployment custom resource definit
| jobManager | org.apache.flink.kubernetes.operator.crd.spec.JobManagerSpec | JobManager specs. |
| taskManager | org.apache.flink.kubernetes.operator.crd.spec.TaskManagerSpec | TaskManager specs. |
| logConfiguration | java.util.Map<java.lang.String,java.lang.String> | Log configuration overrides for the Flink deployment. Format logConfigFileName -> configContent. |
+| mode | org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode | Deployment mode of the Flink cluster, native or standalone. |
### FlinkSessionJobSpec
**Class**: org.apache.flink.kubernetes.operator.crd.spec.FlinkSessionJobSpec
@@ -131,6 +132,16 @@ This page serves as a full reference for FlinkDeployment custom resource definit
| RUNNING | Job is expected to be processing data. |
| SUSPENDED | Processing is suspended with the intention of continuing later. |
+### KubernetesDeploymentMode
+**Class**: org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode
+
+**Description**: Enum to control Flink deployment mode on Kubernetes.
+
+| Value | Docs |
+| ----- | ---- |
+| NATIVE | Deploys Flink using Flinks native Kubernetes support. Only supported for newer versions of Flink |
+| STANDALONE | Deploys Flink on-top of kubernetes in standalone mode. |
+
### Resource
**Class**: org.apache.flink.kubernetes.operator.crd.spec.Resource
diff --git a/e2e-tests/data/flinkdep-cr.yaml b/e2e-tests/data/flinkdep-cr.yaml
index d48af2be..18e73b27 100644
--- a/e2e-tests/data/flinkdep-cr.yaml
+++ b/e2e-tests/data/flinkdep-cr.yaml
@@ -83,6 +83,7 @@ spec:
entryClass: org.apache.flink.streaming.examples.statemachine.StateMachineExample
parallelism: 2
upgradeMode: last-state
+ mode: native
---
apiVersion: v1
diff --git a/e2e-tests/data/multi-sessionjob.yaml b/e2e-tests/data/multi-sessionjob.yaml
index ed5c892f..77991953 100644
--- a/e2e-tests/data/multi-sessionjob.yaml
+++ b/e2e-tests/data/multi-sessionjob.yaml
@@ -65,6 +65,7 @@ spec:
resource:
memory: "1024m"
cpu: 0.25
+ mode: native
---
apiVersion: flink.apache.org/v1beta1
@@ -116,6 +117,7 @@ spec:
resource:
memory: "1024m"
cpu: 0.25
+ mode: native
---
apiVersion: flink.apache.org/v1beta1
diff --git a/e2e-tests/data/sessionjob-cr.yaml b/e2e-tests/data/sessionjob-cr.yaml
index 53b49f0a..1953cdec 100644
--- a/e2e-tests/data/sessionjob-cr.yaml
+++ b/e2e-tests/data/sessionjob-cr.yaml
@@ -65,6 +65,7 @@ spec:
resource:
memory: "1024m"
cpu: 0.5
+ mode: native
---
apiVersion: flink.apache.org/v1beta1
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java
index 8941dd51..706d1108 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilder.java
@@ -17,6 +17,7 @@
package org.apache.flink.kubernetes.operator.config;
+import org.apache.flink.client.deployment.application.ApplicationConfiguration;
import org.apache.flink.configuration.ConfigOption;
import org.apache.flink.configuration.ConfigOptions;
import org.apache.flink.configuration.Configuration;
@@ -32,8 +33,12 @@ import org.apache.flink.kubernetes.configuration.KubernetesDeploymentTarget;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkDeploymentSpec;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkVersion;
+import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
import org.apache.flink.kubernetes.operator.crd.spec.Resource;
import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
+import org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
+import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
import org.apache.flink.runtime.jobgraph.SavepointConfigOptions;
import org.apache.flink.runtime.jobmanager.HighAvailabilityMode;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
@@ -59,6 +64,8 @@ import static org.apache.flink.configuration.DeploymentOptions.SUBMIT_FAILED_JOB
import static org.apache.flink.configuration.DeploymentOptionsInternal.CONF_DIR;
import static org.apache.flink.configuration.WebOptions.CANCEL_ENABLE;
import static org.apache.flink.kubernetes.configuration.KubernetesConfigOptions.REST_SERVICE_EXPOSED_TYPE;
+import static org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION;
+import static org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION;
import static org.apache.flink.kubernetes.operator.utils.FlinkUtils.mergePodTemplates;
import static org.apache.flink.kubernetes.utils.Constants.CONFIG_FILE_LOG4J_NAME;
import static org.apache.flink.kubernetes.utils.Constants.CONFIG_FILE_LOGBACK_NAME;
@@ -216,30 +223,89 @@ public class FlinkConfigBuilder {
spec.getTaskManager().getPodTemplate(),
effectiveConfig,
false);
+
+ if (spec.getTaskManager().getReplicas() != null
+ && spec.getTaskManager().getReplicas() > 0) {
+ effectiveConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS,
+ spec.getTaskManager().getReplicas());
+ }
+ }
+
+ if (spec.getJob() != null
+ && KubernetesDeploymentMode.getDeploymentMode(spec)
+ == KubernetesDeploymentMode.STANDALONE) {
+ if (!effectiveConfig.contains(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS)) {
+ effectiveConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS,
+ FlinkUtils.getNumTaskManagers(effectiveConfig, getParallelism()));
+ }
}
return this;
}
protected FlinkConfigBuilder applyJobOrSessionSpec() throws URISyntaxException {
+ KubernetesDeploymentMode deploymentMode = KubernetesDeploymentMode.getDeploymentMode(spec);
+
if (spec.getJob() != null) {
+ JobSpec jobSpec = spec.getJob();
effectiveConfig.set(
DeploymentOptions.TARGET, KubernetesDeploymentTarget.APPLICATION.getName());
- final URI uri = new URI(spec.getJob().getJarURI());
+ final URI uri = new URI(jobSpec.getJarURI());
effectiveConfig.set(PipelineOptions.JARS, Collections.singletonList(uri.toString()));
effectiveConfig.set(CoreOptions.DEFAULT_PARALLELISM, getParallelism());
- if (spec.getJob().getAllowNonRestoredState() != null) {
+ if (jobSpec.getAllowNonRestoredState() != null) {
effectiveConfig.set(
SavepointConfigOptions.SAVEPOINT_IGNORE_UNCLAIMED_STATE,
- spec.getJob().getAllowNonRestoredState());
+ jobSpec.getAllowNonRestoredState());
+ }
+
+ if (jobSpec.getEntryClass() != null) {
+ effectiveConfig.set(
+ ApplicationConfiguration.APPLICATION_MAIN_CLASS, jobSpec.getEntryClass());
}
} else {
effectiveConfig.set(
DeploymentOptions.TARGET, KubernetesDeploymentTarget.SESSION.getName());
}
+
+ if (deploymentMode == KubernetesDeploymentMode.STANDALONE) {
+ effectiveConfig.set(DeploymentOptions.TARGET, "remote");
+ effectiveConfig.set(
+ StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE,
+ spec.getJob() == null ? SESSION : APPLICATION);
+
+ if (spec.getJob() != null) {
+ effectiveConfig.set(
+ PipelineOptions.CLASSPATHS,
+ Collections.singletonList(getStandaloneJarURI(spec.getJob())));
+ }
+ }
return this;
}
+ private String getStandaloneJarURI(JobSpec jobSpec) throws URISyntaxException {
+ URI uri = new URI(jobSpec.getJarURI());
+
+ // Running an application job through standalone mode doesn't requires file uri scheme and
+ // doesn't accept
+ // local scheme which is used for native so convert here to improve compatibilty at the
+ // operator layer
+ if (uri.getScheme().equals("local")) {
+ uri =
+ new URI(
+ "file",
+ uri.getAuthority() == null ? "" : uri.getAuthority(),
+ uri.getPath(),
+ uri.getQuery(),
+ uri.getFragment());
+ }
+
+ return uri.toASCIIString();
+ }
+
private int getParallelism() {
if (spec.getTaskManager() != null && spec.getTaskManager().getReplicas() != null) {
if (spec.getJob().getParallelism() > 0) {
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java
index 6c2c203d..b41d34b8 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/ObserverFactory.java
@@ -17,9 +17,11 @@
package org.apache.flink.kubernetes.operator.observer.deployment;
+import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
import org.apache.flink.kubernetes.operator.config.Mode;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.observer.Observer;
import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
@@ -36,7 +38,8 @@ public class ObserverFactory {
private final FlinkConfigManager configManager;
private final StatusRecorder<FlinkDeploymentStatus> statusRecorder;
private final EventRecorder eventRecorder;
- private final Map<Mode, Observer<FlinkDeployment>> observerMap;
+ private final Map<Tuple2<Mode, KubernetesDeploymentMode>, Observer<FlinkDeployment>>
+ observerMap;
public ObserverFactory(
FlinkServiceFactory flinkServiceFactory,
@@ -52,9 +55,11 @@ public class ObserverFactory {
public Observer<FlinkDeployment> getOrCreate(FlinkDeployment flinkApp) {
return observerMap.computeIfAbsent(
- Mode.getMode(flinkApp),
- mode -> {
- switch (mode) {
+ Tuple2.of(
+ Mode.getMode(flinkApp),
+ KubernetesDeploymentMode.getDeploymentMode(flinkApp)),
+ modes -> {
+ switch (modes.f0) {
case SESSION:
return new SessionObserver(
flinkServiceFactory.getOrCreate(flinkApp),
@@ -68,7 +73,7 @@ public class ObserverFactory {
eventRecorder);
default:
throw new UnsupportedOperationException(
- String.format("Unsupported running mode: %s", mode));
+ String.format("Unsupported running mode: %s", modes.f0));
}
});
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java
index c03ddc98..ec0389e2 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ApplicationReconciler.java
@@ -33,7 +33,6 @@ import org.apache.flink.kubernetes.operator.service.FlinkService;
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.kubernetes.operator.utils.SavepointUtils;
import org.apache.flink.kubernetes.operator.utils.StatusRecorder;
import org.apache.flink.runtime.highavailability.JobResultStoreOptions;
import org.apache.flink.runtime.jobgraph.SavepointConfigOptions;
@@ -194,7 +193,7 @@ public class ApplicationReconciler
@Override
public boolean reconcileOtherChanges(
FlinkDeployment deployment, Context ctx, Configuration observeConfig) throws Exception {
- if (SavepointUtils.triggerSavepointIfNeeded(flinkService, deployment, observeConfig)) {
+ if (super.reconcileOtherChanges(deployment, ctx, observeConfig)) {
return true;
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java
index 419e3e41..2d11f495 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/reconciler/deployment/ReconcilerFactory.java
@@ -17,9 +17,11 @@
package org.apache.flink.kubernetes.operator.reconciler.deployment;
+import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
import org.apache.flink.kubernetes.operator.config.Mode;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.reconciler.Reconciler;
import org.apache.flink.kubernetes.operator.service.FlinkServiceFactory;
@@ -39,7 +41,8 @@ public class ReconcilerFactory {
private final FlinkConfigManager configManager;
private final EventRecorder eventRecorder;
private final StatusRecorder<FlinkDeploymentStatus> deploymentStatusRecorder;
- private final Map<Mode, Reconciler<FlinkDeployment>> reconcilerMap;
+ private final Map<Tuple2<Mode, KubernetesDeploymentMode>, Reconciler<FlinkDeployment>>
+ reconcilerMap;
public ReconcilerFactory(
KubernetesClient kubernetesClient,
@@ -57,9 +60,11 @@ public class ReconcilerFactory {
public Reconciler<FlinkDeployment> getOrCreate(FlinkDeployment flinkApp) {
return reconcilerMap.computeIfAbsent(
- Mode.getMode(flinkApp),
- mode -> {
- switch (mode) {
+ Tuple2.of(
+ Mode.getMode(flinkApp),
+ KubernetesDeploymentMode.getDeploymentMode(flinkApp)),
+ modes -> {
+ switch (modes.f0) {
case SESSION:
return new SessionReconciler(
kubernetesClient,
@@ -76,7 +81,7 @@ public class ReconcilerFactory {
deploymentStatusRecorder);
default:
throw new UnsupportedOperationException(
- String.format("Unsupported running mode: %s", mode));
+ String.format("Unsupported running mode: %s", modes.f0));
}
});
}
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 afb7bf27..c4b32529 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
@@ -142,11 +142,33 @@ public abstract class AbstractFlinkService implements FlinkService {
protected abstract PodList getJmPodList(String namespace, String clusterId);
+ protected abstract void deployApplicationCluster(JobSpec jobSpec, Configuration conf)
+ throws Exception;
+
@Override
public KubernetesClient getKubernetesClient() {
return kubernetesClient;
}
+ @Override
+ public void submitApplicationCluster(
+ JobSpec jobSpec, Configuration conf, boolean requireHaMetadata) throws Exception {
+ LOG.info(
+ "Deploying application cluster{}",
+ requireHaMetadata ? " requiring last-state from HA metadata" : "");
+ if (FlinkUtils.isKubernetesHAActivated(conf)) {
+ final String clusterId = conf.get(KubernetesConfigOptions.CLUSTER_ID);
+ final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
+ // Delete the job graph in the HA ConfigMaps so that the newly changed job config(e.g.
+ // parallelism) could take effect
+ FlinkUtils.deleteJobGraphInKubernetesHA(clusterId, namespace, kubernetesClient);
+ }
+ if (requireHaMetadata) {
+ validateHaMetadataExists(conf);
+ }
+ deployApplicationCluster(jobSpec, conf);
+ }
+
@Override
public boolean isHaMetadataAvailable(Configuration conf) {
return FlinkUtils.isHaMetadataAvailable(conf, kubernetesClient);
@@ -219,8 +241,11 @@ public abstract class AbstractFlinkService implements FlinkService {
}
}
- @Override
- public void cancelJob(FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration conf)
+ protected void cancelJob(
+ FlinkDeployment deployment,
+ UpgradeMode upgradeMode,
+ Configuration conf,
+ boolean deleteClusterAfterSavepoint)
throws Exception {
var deploymentStatus = deployment.getStatus();
var jobIdString = deploymentStatus.getJobStatus().getJobId();
@@ -290,6 +315,9 @@ public abstract class AbstractFlinkService implements FlinkService {
ExecutionCheckpointingOptions.CHECKPOINTING_TIMEOUT.key()),
exception);
}
+ if (deleteClusterAfterSavepoint) {
+ deleteClusterDeployment(deployment.getMetadata(), deploymentStatus, true);
+ }
break;
case LAST_STATE:
deleteClusterDeployment(deployment.getMetadata(), deploymentStatus, false);
@@ -697,7 +725,6 @@ public abstract class AbstractFlinkService implements FlinkService {
/** Wait until the FLink cluster has completely shut down. */
@VisibleForTesting
void waitForClusterShutdown(String namespace, String clusterId, long shutdownTimeout) {
-
boolean jobManagerRunning = true;
boolean serviceRunning = true;
@@ -765,4 +792,14 @@ public abstract class AbstractFlinkService implements FlinkService {
}
return effectiveStatus;
}
+
+ private void validateHaMetadataExists(Configuration conf) {
+ if (!isHaMetadataAvailable(conf)) {
+ throw new DeploymentFailedException(
+ "HA metadata not available to restore from last state. "
+ + "It is possible that the job has finished or terminally failed, or the configmaps have been deleted. "
+ + "Manual restore required.",
+ "RestoreFailed");
+ }
+ }
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java
index 761f1e68..9fce6eec 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/FlinkServiceFactory.java
@@ -22,6 +22,8 @@ import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
import io.fabric8.kubernetes.client.KubernetesClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -33,6 +35,8 @@ public class FlinkServiceFactory {
private final FlinkConfigManager configManager;
private final Map<KubernetesDeploymentMode, FlinkService> serviceMap;
+ private static final Logger LOG = LoggerFactory.getLogger(FlinkServiceFactory.class);
+
public FlinkServiceFactory(
KubernetesClient kubernetesClient, FlinkConfigManager configManager) {
this.kubernetesClient = kubernetesClient;
@@ -46,8 +50,10 @@ public class FlinkServiceFactory {
mode -> {
switch (mode) {
case NATIVE:
+ LOG.info("Using NativeFlinkService");
return new NativeFlinkService(kubernetesClient, configManager);
case STANDALONE:
+ LOG.info("Using StandaloneFlinkService");
return new StandaloneFlinkService(kubernetesClient, configManager);
default:
throw new UnsupportedOperationException(
@@ -57,6 +63,7 @@ public class FlinkServiceFactory {
}
public FlinkService getOrCreate(FlinkDeployment deployment) {
+ LOG.info("Getting service for {}", deployment.getMetadata().getName());
return getOrCreate(getDeploymentMode(deployment));
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java
index 60b5ee3b..47d3fc01 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/NativeFlinkService.java
@@ -26,13 +26,12 @@ import org.apache.flink.client.deployment.DefaultClusterClientServiceLoader;
import org.apache.flink.client.deployment.application.ApplicationConfiguration;
import org.apache.flink.client.deployment.application.cli.ApplicationClusterDeployer;
import org.apache.flink.configuration.Configuration;
-import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
+import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
+import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatus;
-import org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
-import org.apache.flink.kubernetes.operator.utils.FlinkUtils;
import org.apache.flink.kubernetes.utils.KubernetesUtils;
import io.fabric8.kubernetes.api.model.ObjectMeta;
@@ -56,21 +55,8 @@ public class NativeFlinkService extends AbstractFlinkService {
}
@Override
- public void submitApplicationCluster(
- JobSpec jobSpec, Configuration conf, boolean requireHaMetadata) throws Exception {
- LOG.info(
- "Deploying application cluster{}",
- requireHaMetadata ? " requiring last-state from HA metadata" : "");
- if (FlinkUtils.isKubernetesHAActivated(conf)) {
- final String clusterId = conf.get(KubernetesConfigOptions.CLUSTER_ID);
- final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
- // Delete the job graph in the HA ConfigMaps so that the newly changed job config(e.g.
- // parallelism) could take effect
- FlinkUtils.deleteJobGraphInKubernetesHA(clusterId, namespace, kubernetesClient);
- }
- if (requireHaMetadata) {
- validateHaMetadataExists(conf);
- }
+ protected void deployApplicationCluster(JobSpec jobSpec, Configuration conf) throws Exception {
+ LOG.info("Deploying application cluster");
final ClusterClientServiceLoader clusterClientServiceLoader =
new DefaultClusterClientServiceLoader();
final ApplicationDeployer deployer =
@@ -85,16 +71,6 @@ public class NativeFlinkService extends AbstractFlinkService {
LOG.info("Application cluster successfully deployed");
}
- private void validateHaMetadataExists(Configuration conf) {
- if (!isHaMetadataAvailable(conf)) {
- throw new DeploymentFailedException(
- "HA metadata not available to restore from last state. "
- + "It is possible that the job has finished or terminally failed, or the configmaps have been deleted. "
- + "Manual restore required.",
- "RestoreFailed");
- }
- }
-
@Override
public void submitSessionCluster(Configuration conf) throws Exception {
LOG.info("Deploying session cluster");
@@ -110,6 +86,13 @@ public class NativeFlinkService extends AbstractFlinkService {
LOG.info("Session cluster successfully deployed");
}
+ @Override
+ public void cancelJob(
+ FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration configuration)
+ throws Exception {
+ cancelJob(deployment, upgradeMode, configuration, false);
+ }
+
@Override
public void deleteClusterDeployment(
ObjectMeta meta, FlinkDeploymentStatus status, boolean deleteHaData) {
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java
index 5a0b85eb..08c2ef4e 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/service/StandaloneFlinkService.java
@@ -20,11 +20,15 @@ package org.apache.flink.kubernetes.operator.service;
import org.apache.flink.annotation.VisibleForTesting;
import org.apache.flink.client.deployment.ClusterDeploymentException;
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.kubernetes.KubernetesClusterClientFactory;
import org.apache.flink.kubernetes.configuration.KubernetesConfigOptions;
import org.apache.flink.kubernetes.operator.config.FlinkConfigManager;
+import org.apache.flink.kubernetes.operator.config.Mode;
+import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
+import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.kubeclient.Fabric8FlinkStandaloneKubeClient;
import org.apache.flink.kubernetes.operator.kubeclient.FlinkStandaloneKubeClient;
@@ -58,22 +62,25 @@ public class StandaloneFlinkService extends AbstractFlinkService {
}
@Override
- public void submitApplicationCluster(
- JobSpec jobSpec, Configuration conf, boolean requireHaMetadata) throws Exception {
+ protected void deployApplicationCluster(JobSpec jobSpec, Configuration conf) throws Exception {
LOG.info("Deploying application cluster");
- // TODO some HA stuff?
- submitClusterInternal(conf);
+ submitClusterInternal(conf, Mode.APPLICATION);
LOG.info("Application cluster successfully deployed");
}
@Override
public void submitSessionCluster(Configuration conf) throws Exception {
LOG.info("Deploying session cluster");
- // TODO some HA stuff?
- submitClusterInternal(conf);
+ submitClusterInternal(conf, Mode.SESSION);
LOG.info("Session cluster successfully deployed");
}
+ @Override
+ public void cancelJob(FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration conf)
+ throws Exception {
+ cancelJob(deployment, upgradeMode, conf, true);
+ }
+
@Override
public void deleteClusterDeployment(
ObjectMeta meta, FlinkDeploymentStatus status, boolean deleteHaData) {
@@ -106,13 +113,26 @@ public class StandaloneFlinkService extends AbstractFlinkService {
executorService);
}
- private void submitClusterInternal(Configuration conf) throws ClusterDeploymentException {
+ private void submitClusterInternal(Configuration conf, Mode mode)
+ throws ClusterDeploymentException {
final String namespace = conf.get(KubernetesConfigOptions.NAMESPACE);
FlinkStandaloneKubeClient client = createNamespacedKubeClient(conf, namespace);
try (final KubernetesStandaloneClusterDescriptor kubernetesClusterDescriptor =
new KubernetesStandaloneClusterDescriptor(conf, client)) {
- kubernetesClusterDescriptor.deploySessionCluster(getClusterSpecification(conf));
+ switch (mode) {
+ case APPLICATION:
+ kubernetesClusterDescriptor.deployApplicationCluster(
+ getClusterSpecification(conf),
+ ApplicationConfiguration.fromConfiguration(conf));
+ break;
+ case SESSION:
+ kubernetesClusterDescriptor.deploySessionCluster(getClusterSpecification(conf));
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ String.format("Unsupported running mode: %s", mode));
+ }
}
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/EventSourceUtils.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/EventSourceUtils.java
index 62bea6dd..db238f28 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/EventSourceUtils.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/EventSourceUtils.java
@@ -45,11 +45,7 @@ public class EventSourceUtils {
public static InformerEventSource<Deployment, FlinkDeployment> getDeploymentInformerEventSource(
EventSourceContext<FlinkDeployment> context) {
final String labelSelector =
- Map.of(
- Constants.LABEL_TYPE_KEY,
- Constants.LABEL_TYPE_NATIVE_TYPE,
- Constants.LABEL_COMPONENT_KEY,
- Constants.LABEL_COMPONENT_JOB_MANAGER)
+ Map.of(Constants.LABEL_COMPONENT_KEY, Constants.LABEL_COMPONENT_JOB_MANAGER)
.entrySet().stream()
.map(Object::toString)
.collect(Collectors.joining(","));
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 05cb7e03..d2f4c359 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
@@ -167,6 +167,10 @@ public class FlinkUtils {
public static int getNumTaskManagers(Configuration conf) {
int parallelism = conf.get(CoreOptions.DEFAULT_PARALLELISM);
+ return getNumTaskManagers(conf, parallelism);
+ }
+
+ public static int getNumTaskManagers(Configuration conf, int parallelism) {
int taskSlots = conf.get(TaskManagerOptions.NUM_TASK_SLOTS);
return (parallelism + taskSlots - 1) / taskSlots;
}
diff --git a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/validation/DefaultValidator.java b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/validation/DefaultValidator.java
index de3c7b68..9e92dc93 100644
--- a/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/validation/DefaultValidator.java
+++ b/flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/validation/DefaultValidator.java
@@ -32,6 +32,7 @@ import org.apache.flink.kubernetes.operator.crd.spec.IngressSpec;
import org.apache.flink.kubernetes.operator.crd.spec.JobManagerSpec;
import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
import org.apache.flink.kubernetes.operator.crd.spec.JobState;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
import org.apache.flink.kubernetes.operator.crd.spec.Resource;
import org.apache.flink.kubernetes.operator.crd.spec.TaskManagerSpec;
import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
@@ -88,7 +89,11 @@ public class DefaultValidator implements FlinkResourceValidator {
deployment.getMetadata().getName(),
deployment.getMetadata().getNamespace()),
validateLogConfig(spec.getLogConfiguration()),
- validateJobSpec(spec.getJob(), spec.getTaskManager(), effectiveConfig),
+ validateJobSpec(
+ spec.getJob(),
+ spec.getTaskManager(),
+ effectiveConfig,
+ KubernetesDeploymentMode.getDeploymentMode(deployment)),
validateJmSpec(spec.getJobManager(), effectiveConfig),
validateTmSpec(spec.getTaskManager()),
validateSpecChange(deployment, effectiveConfig),
@@ -173,7 +178,10 @@ public class DefaultValidator implements FlinkResourceValidator {
}
private Optional<String> validateJobSpec(
- JobSpec job, @Nullable TaskManagerSpec tm, Map<String, String> confMap) {
+ JobSpec job,
+ @Nullable TaskManagerSpec tm,
+ Map<String, String> confMap,
+ KubernetesDeploymentMode mode) {
if (job == null) {
return Optional.empty();
}
@@ -306,6 +314,24 @@ public class DefaultValidator implements FlinkResourceValidator {
return Optional.of("Cannot switch from job to session cluster");
}
+ KubernetesDeploymentMode oldDeploymentMode =
+ oldSpec.getMode() == null ? KubernetesDeploymentMode.NATIVE : oldSpec.getMode();
+
+ KubernetesDeploymentMode newDeploymentMode =
+ newSpec.getMode() == null ? KubernetesDeploymentMode.NATIVE : newSpec.getMode();
+
+ if (oldDeploymentMode == KubernetesDeploymentMode.NATIVE
+ && newDeploymentMode != KubernetesDeploymentMode.NATIVE) {
+ return Optional.of(
+ "Cannot switch from native kubernetes to standalone kubernetes cluster");
+ }
+
+ if (oldDeploymentMode == KubernetesDeploymentMode.STANDALONE
+ && newDeploymentMode != KubernetesDeploymentMode.STANDALONE) {
+ return Optional.of(
+ "Cannot switch from standalone kubernetes to native kubernetes cluster");
+ }
+
JobSpec oldJob = oldSpec.getJob();
JobSpec newJob = newSpec.getJob();
if (oldJob != null && newJob != null) {
@@ -358,7 +384,11 @@ public class DefaultValidator implements FlinkResourceValidator {
return firstPresent(
validateNotApplicationCluster(sessionCluster),
validateSessionClusterId(sessionJob, sessionCluster),
- validateJobSpec(sessionJob.getSpec().getJob(), null, effectiveConfig));
+ validateJobSpec(
+ sessionJob.getSpec().getJob(),
+ null,
+ effectiveConfig,
+ KubernetesDeploymentMode.getDeploymentMode(sessionCluster)));
}
private Optional<String> validateJobNotEmpty(FlinkSessionJob sessionJob) {
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 2e99c53f..4ff0a33f 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
@@ -31,6 +31,7 @@ import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkSessionJobSpec;
import org.apache.flink.kubernetes.operator.crd.spec.FlinkVersion;
import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
+import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentStatus;
import org.apache.flink.kubernetes.operator.crd.status.JobManagerDeploymentStatus;
import org.apache.flink.kubernetes.operator.crd.status.Savepoint;
@@ -156,6 +157,10 @@ public class TestingFlinkService extends AbstractFlinkService {
if (requireHaMetadata) {
validateHaMetadataExists(conf);
}
+ deployApplicationCluster(jobSpec, conf);
+ }
+
+ protected void deployApplicationCluster(JobSpec jobSpec, Configuration conf) throws Exception {
if (deployFailure) {
throw new Exception("Deployment failure");
}
@@ -358,6 +363,13 @@ public class TestingFlinkService extends AbstractFlinkService {
0);
}
+ @Override
+ public void cancelJob(
+ FlinkDeployment deployment, UpgradeMode upgradeMode, Configuration configuration)
+ throws Exception {
+ cancelJob(deployment, upgradeMode, configuration, false);
+ }
+
private String cancelJob(FlinkVersion flinkVersion, JobID jobID, boolean savepoint)
throws Exception {
Optional<Tuple2<String, JobStatusMessage>> jobOpt =
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilderTest.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilderTest.java
index df5bf415..f33a9986 100644
--- a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilderTest.java
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/config/FlinkConfigBuilderTest.java
@@ -17,6 +17,8 @@
package org.apache.flink.kubernetes.operator.config;
+import org.apache.flink.client.deployment.application.ApplicationConfiguration;
+import org.apache.flink.configuration.ConfigUtils;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.CoreOptions;
import org.apache.flink.configuration.DeploymentOptions;
@@ -33,9 +35,11 @@ import org.apache.flink.kubernetes.highavailability.KubernetesHaServicesFactory;
import org.apache.flink.kubernetes.operator.TestUtils;
import org.apache.flink.kubernetes.operator.crd.FlinkDeployment;
import org.apache.flink.kubernetes.operator.crd.spec.IngressSpec;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
import org.apache.flink.kubernetes.operator.crd.spec.TaskManagerSpec;
import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
import org.apache.flink.kubernetes.operator.reconciler.ReconciliationUtils;
+import org.apache.flink.kubernetes.operator.standalone.StandaloneKubernetesConfigOptionsInternal;
import org.apache.flink.kubernetes.utils.Constants;
import org.apache.flink.runtime.jobgraph.SavepointConfigOptions;
import org.apache.flink.streaming.api.environment.ExecutionCheckpointingOptions;
@@ -54,6 +58,7 @@ import java.net.URISyntaxException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import static org.apache.flink.kubernetes.operator.TestUtils.IMAGE;
@@ -62,6 +67,8 @@ import static org.apache.flink.kubernetes.operator.TestUtils.SAMPLE_JAR;
import static org.apache.flink.kubernetes.operator.TestUtils.SERVICE_ACCOUNT;
import static org.apache.flink.kubernetes.operator.config.FlinkConfigBuilder.DEFAULT_CHECKPOINTING_INTERVAL;
import static org.apache.flink.kubernetes.utils.Constants.CONFIG_FILE_LOG4J_NAME;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
/** FlinkConfigBuilderTest. */
public class FlinkConfigBuilderTest {
@@ -347,6 +354,84 @@ public class FlinkConfigBuilderTest {
configuration.getBoolean(SavepointConfigOptions.SAVEPOINT_IGNORE_UNCLAIMED_STATE));
}
+ @Test
+ public void testApplyStandaloneApplicationSpec() throws URISyntaxException, IOException {
+ FlinkDeployment dep = ReconciliationUtils.clone(flinkDeployment);
+ final String entryClass = "entry.class";
+ final String jarUri = "local:///flink/opt/StateMachine.jar";
+ final String correctedJarUri = "file:///flink/opt/StateMachine.jar";
+ dep.getSpec().setMode(KubernetesDeploymentMode.STANDALONE);
+ dep.getSpec().getJob().setEntryClass(entryClass);
+ dep.getSpec().getJob().setJarURI(jarUri);
+ dep.getSpec().setTaskManager(new TaskManagerSpec());
+ dep.getSpec().getTaskManager().setReplicas(3);
+ dep.getSpec().getFlinkConfiguration().put(TaskManagerOptions.NUM_TASK_SLOTS.key(), "2");
+
+ Configuration configuration =
+ new FlinkConfigBuilder(dep, new Configuration())
+ .applyFlinkConfiguration()
+ .applyTaskManagerSpec()
+ .applyJobOrSessionSpec()
+ .build();
+
+ Assertions.assertEquals("remote", configuration.getString(DeploymentOptions.TARGET));
+ Assertions.assertEquals(
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.APPLICATION,
+ configuration.get(StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE));
+ Assertions.assertEquals(6, configuration.getInteger(CoreOptions.DEFAULT_PARALLELISM));
+ Assertions.assertEquals(
+ entryClass,
+ configuration.getString(ApplicationConfiguration.APPLICATION_MAIN_CLASS));
+ Assertions.assertEquals(
+ 3,
+ configuration.get(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS));
+ List<String> classpaths =
+ ConfigUtils.decodeListFromConfig(
+ configuration, PipelineOptions.CLASSPATHS, String::toString);
+ assertThat(classpaths, containsInAnyOrder(correctedJarUri));
+
+ dep.getSpec().getTaskManager().setReplicas(null);
+ dep.getSpec().getJob().setParallelism(10);
+
+ configuration =
+ new FlinkConfigBuilder(dep, new Configuration())
+ .applyFlinkConfiguration()
+ .applyTaskManagerSpec()
+ .applyJobOrSessionSpec()
+ .build();
+ Assertions.assertEquals(
+ 5,
+ configuration.get(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS));
+ }
+
+ @Test
+ public void testApplyStandaloneSessionSpec() throws URISyntaxException, IOException {
+ FlinkDeployment dep = ReconciliationUtils.clone(flinkDeployment);
+ dep.getSpec().setMode(KubernetesDeploymentMode.STANDALONE);
+ dep.getSpec().setJob(null);
+ dep.getSpec().setTaskManager(new TaskManagerSpec());
+ dep.getSpec().getTaskManager().setReplicas(5);
+ dep.getSpec().getFlinkConfiguration().put(TaskManagerOptions.NUM_TASK_SLOTS.key(), "2");
+
+ Configuration configuration =
+ new FlinkConfigBuilder(dep, new Configuration())
+ .applyFlinkConfiguration()
+ .applyTaskManagerSpec()
+ .applyJobOrSessionSpec()
+ .build();
+
+ Assertions.assertEquals("remote", configuration.getString(DeploymentOptions.TARGET));
+ Assertions.assertEquals(
+ StandaloneKubernetesConfigOptionsInternal.ClusterMode.SESSION,
+ configuration.get(StandaloneKubernetesConfigOptionsInternal.CLUSTER_MODE));
+ Assertions.assertEquals(
+ 5,
+ configuration.get(
+ StandaloneKubernetesConfigOptionsInternal.KUBERNETES_TASKMANAGER_REPLICAS));
+ }
+
@Test
public void testBuildFrom() throws Exception {
final Configuration configuration =
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java
index c169e1d9..088d46bf 100644
--- a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/controller/TestingFlinkDeploymentController.java
@@ -70,6 +70,7 @@ public class TestingFlinkDeploymentController
KubernetesClient kubernetesClient,
TestingFlinkService flinkService) {
FlinkServiceFactory flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
+
eventRecorder = new EventRecorder(kubernetesClient, eventCollector);
statusRecorder =
new StatusRecorder<>(
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 f801bfbc..5dddb7b9 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
@@ -54,7 +54,6 @@ public class SessionJobObserverTest {
private TestingFlinkService flinkService;
private SessionJobObserver observer;
private SessionJobReconciler reconciler;
- private FlinkServiceFactory flinkServiceFactory;
@BeforeEach
public void before() {
@@ -62,7 +61,7 @@ public class SessionJobObserverTest {
var eventRecorder = new EventRecorder(kubernetesClient, (r, e) -> {});
var statusRecorder = new TestingStatusRecorder<FlinkSessionJobStatus>();
flinkService = new TestingFlinkService();
- flinkServiceFactory = flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
+ FlinkServiceFactory flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
observer =
new SessionJobObserver(
flinkServiceFactory, configManager, statusRecorder, eventRecorder);
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 998fa34d..d6f342c9 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
@@ -72,7 +72,7 @@ public class SessionJobReconcilerTest {
@BeforeEach
public void before() {
flinkService = new TestingFlinkService();
- flinkServiceFactory = flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
+ flinkServiceFactory = new TestingFlinkServiceFactory(flinkService);
eventRecorder =
new EventRecorder(null, (r, e) -> {}) {
@Override
diff --git a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/validation/DefaultValidatorTest.java b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/validation/DefaultValidatorTest.java
index 5189ab47..23e75bc1 100644
--- a/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/validation/DefaultValidatorTest.java
+++ b/flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/validation/DefaultValidatorTest.java
@@ -32,6 +32,7 @@ import org.apache.flink.kubernetes.operator.crd.spec.FlinkVersion;
import org.apache.flink.kubernetes.operator.crd.spec.IngressSpec;
import org.apache.flink.kubernetes.operator.crd.spec.JobSpec;
import org.apache.flink.kubernetes.operator.crd.spec.JobState;
+import org.apache.flink.kubernetes.operator.crd.spec.KubernetesDeploymentMode;
import org.apache.flink.kubernetes.operator.crd.spec.TaskManagerSpec;
import org.apache.flink.kubernetes.operator.crd.spec.UpgradeMode;
import org.apache.flink.kubernetes.operator.crd.status.FlinkDeploymentReconciliationStatus;
@@ -301,6 +302,74 @@ public class DefaultValidatorTest {
},
"Cannot switch from session to job cluster");
+ testError(
+ dep -> {
+ dep.setStatus(new FlinkDeploymentStatus());
+ dep.getStatus().setJobStatus(new JobStatus());
+
+ dep.getStatus()
+ .setReconciliationStatus(new FlinkDeploymentReconciliationStatus());
+ dep.getSpec().setMode(KubernetesDeploymentMode.STANDALONE);
+ FlinkDeploymentSpec spec = ReconciliationUtils.clone(dep.getSpec());
+
+ spec.setMode(KubernetesDeploymentMode.NATIVE);
+ dep.getStatus()
+ .getReconciliationStatus()
+ .serializeAndSetLastReconciledSpec(spec, dep);
+ },
+ "Cannot switch from native kubernetes to standalone kubernetes cluster");
+
+ testError(
+ dep -> {
+ dep.setStatus(new FlinkDeploymentStatus());
+ dep.getStatus().setJobStatus(new JobStatus());
+
+ dep.getStatus()
+ .setReconciliationStatus(new FlinkDeploymentReconciliationStatus());
+ dep.getSpec().setMode(KubernetesDeploymentMode.STANDALONE);
+ FlinkDeploymentSpec spec = ReconciliationUtils.clone(dep.getSpec());
+
+ spec.setMode(null);
+ dep.getStatus()
+ .getReconciliationStatus()
+ .serializeAndSetLastReconciledSpec(spec, dep);
+ },
+ "Cannot switch from native kubernetes to standalone kubernetes cluster");
+
+ testError(
+ dep -> {
+ dep.setStatus(new FlinkDeploymentStatus());
+ dep.getStatus().setJobStatus(new JobStatus());
+
+ dep.getStatus()
+ .setReconciliationStatus(new FlinkDeploymentReconciliationStatus());
+ dep.getSpec().setMode(null);
+ FlinkDeploymentSpec spec = ReconciliationUtils.clone(dep.getSpec());
+
+ spec.setMode(KubernetesDeploymentMode.STANDALONE);
+ dep.getStatus()
+ .getReconciliationStatus()
+ .serializeAndSetLastReconciledSpec(spec, dep);
+ },
+ "Cannot switch from standalone kubernetes to native kubernetes cluster");
+
+ testError(
+ dep -> {
+ dep.setStatus(new FlinkDeploymentStatus());
+ dep.getStatus().setJobStatus(new JobStatus());
+
+ dep.getStatus()
+ .setReconciliationStatus(new FlinkDeploymentReconciliationStatus());
+ dep.getSpec().setMode(KubernetesDeploymentMode.NATIVE);
+ FlinkDeploymentSpec spec = ReconciliationUtils.clone(dep.getSpec());
+
+ spec.setMode(KubernetesDeploymentMode.STANDALONE);
+ dep.getStatus()
+ .getReconciliationStatus()
+ .serializeAndSetLastReconciledSpec(spec, dep);
+ },
+ "Cannot switch from standalone kubernetes to native kubernetes cluster");
+
// Test upgrade mode change validation
testError(
dep -> {
[flink-kubernetes-operator] 01/05: [CI] Make overlapping class check more robust
Posted by gy...@apache.org.
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
commit 446961d0252bbdac5cf64179b641d90632c7eb92
Author: Gyula Fora <g_...@apple.com>
AuthorDate: Wed Jul 20 15:26:00 2022 +0200
[CI] Make overlapping class check more robust
---
.github/workflows/ci.yml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 35c669ed..1dde3421 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -41,8 +41,9 @@ jobs:
- name: Build with Maven
run: |
set -o pipefail; mvn clean install javadoc:javadoc -Pgenerate-docs | tee ./mvn.log; set +o pipefail
- if [[ $(cat ./mvn.log | grep -E -v 'flink-runtime-.*.jar, flink-kubernetes-operator-.*.jar define 2 overlapping classes' | grep -c "overlapping classes" -) -gt 0 ]];then
- echo "Found overlapping classes, please fix"
+ if [[ $(cat ./mvn.log | grep -E -v '(flink-runtime-.*.jar, flink-kubernetes-operator-.*.jar)|(flink-kubernetes-operator-.*.jar, flink-runtime-.*.jar) define 2 overlapping classes' | grep -c "overlapping classes" -) -gt 0 ]];then
+ echo "Found overlapping classes: "
+ cat ./mvn.log | grep "overlapping classes"
exit 1
fi
if [[ $(git diff HEAD | wc -l) -gt 0 ]];then