Skip to content

Commit 5a3743e

Browse files
authored
[K8s] Migrate streampark-flink-kubernetes from Scala to Java (#4465)
* [Migrate] #4451 Migrate streampark-flink-kubernetes from Scala to Java Convert K8s integration module to Java with Jackson REST DTOs replacing json4s, preserve watcher/cache behavior, and update console enum bridges. * [CI] Fix Sonar reliability issues for #4465 k8s migration Handle InterruptedException in watcher CompletableFuture joins, remove redundant Optional null checks, and replace backtracking-prone path regex. * [CI] Fix Sonar code smell issues for #4465 k8s migration Address 32 Sonar CODE_SMELL findings: extract string constants (S1192), refactor nested try blocks (S1141), simplify complex methods (S3776/S135), use @Builder.Default static constants (S1170), and fix comment/path issues. * [CI] Fix Scala/Java interop for ClusterKey in session client Update KubernetesNativeSessionClient to use Java ClusterKey builder and Optional.orElseThrow after k8s module migration to Java. * [CI] Harden E2E against flaky Docker Hub ryuk pulls Use GHCR ryuk mirror with retry pre-pull for testcontainers images to avoid transient registry-1.docker.io timeouts in CI. * [CI] Fix E2E ryuk pull by removing invalid GHCR mirror ghcr.io/testcontainers/ryuk:0.7.0 does not exist (manifest unknown), which broke all E2E jobs. Keep Docker Hub pre-pull with retry only. * [CI] Fix Sonar S1170 @Builder.Default issues in k8s models Use inline literal defaults instead of static constants referenced by instance fields, which triggered "Make this final field static too".
1 parent 60768b8 commit 5a3743e

97 files changed

Lines changed: 5797 additions & 4213 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/e2e.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,22 @@ jobs:
198198
run: |
199199
docker load -i /tmp/standalone-image.tar \
200200
&& rm -f /tmp/standalone-image.tar
201+
- name: Pre-pull Testcontainers images
202+
run: |
203+
pull_with_retry() {
204+
local image="$1"
205+
for attempt in $(seq 1 8); do
206+
if docker pull "$image"; then
207+
return 0
208+
fi
209+
echo "Failed to pull $image (attempt $attempt), retrying in ${attempt}0s..."
210+
sleep $((attempt * 10))
211+
done
212+
echo "Failed to pull $image after 8 attempts"
213+
return 1
214+
}
215+
pull_with_retry testcontainers/ryuk:0.7.0
216+
pull_with_retry selenium/standalone-chrome:4.13.0 || true
201217
- name: Run Test
202218
run: |
203219
./mvnw -B -f streampark-e2e/pom.xml -am \

streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/enums/FlinkAppStateEnum.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121

2222
import lombok.Getter;
2323

24-
import scala.Enumeration;
25-
2624
/** Describe the status of Flink Application */
2725
@Getter
2826
public enum FlinkAppStateEnum {
@@ -149,16 +147,16 @@ public static boolean isLost(Integer appState) {
149147
@Deprecated
150148
public static class Bridge {
151149

152-
/** covert from org.apache.streampark.flink.k8s.enums.FlinkJobState */
153-
public static FlinkAppStateEnum fromK8sFlinkJobState(Enumeration.Value flinkJobState) {
154-
if (FlinkJobState.K8S_INITIALIZING() == flinkJobState) {
150+
/** covert from org.apache.streampark.flink.kubernetes.enums.FlinkJobState */
151+
public static FlinkAppStateEnum fromK8sFlinkJobState(FlinkJobState flinkJobState) {
152+
if (FlinkJobState.K8S_INITIALIZING == flinkJobState) {
155153
return INITIALIZING;
156154
}
157-
return getState(flinkJobState.toString());
155+
return getState(flinkJobState.name());
158156
}
159157

160-
/** covert to org.apache.streampark.flink.k8s.enums.FlinkJobState */
161-
public static Enumeration.Value toK8sFlinkJobState(FlinkAppStateEnum flinkAppStateEnum) {
158+
/** covert to org.apache.streampark.flink.kubernetes.enums.FlinkJobState */
159+
public static FlinkJobState toK8sFlinkJobState(FlinkAppStateEnum flinkAppStateEnum) {
162160
return FlinkJobState.of(flinkAppStateEnum.name());
163161
}
164162
}

streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/application/impl/FlinkApplicationInfoServiceImpl.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,10 +370,16 @@ public String k8sStartLog(Long id, Integer offset, Integer limit) throws Excepti
370370
"Job deployMode must be kubernetes-session|kubernetes-application.");
371371

372372
CompletableFuture<String> future = CompletableFuture.supplyAsync(
373-
() -> KubernetesDeploymentHelper.watchDeploymentLog(
374-
application.getK8sNamespace(),
375-
application.getJobName(),
376-
application.getJobId()));
373+
() -> {
374+
try {
375+
return KubernetesDeploymentHelper.watchDeploymentLog(
376+
application.getK8sNamespace(),
377+
application.getJobName(),
378+
application.getJobId());
379+
} catch (Exception e) {
380+
throw new RuntimeException(e);
381+
}
382+
});
377383

378384
return future
379385
.exceptionally(

streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/watcher/FlinkK8sChangeEventListener.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@
4747
import java.util.Date;
4848
import java.util.concurrent.Executor;
4949

50-
import scala.Enumeration;
51-
5250
import static org.apache.streampark.console.core.enums.FlinkAppStateEnum.Bridge.fromK8sFlinkJobState;
5351
import static org.apache.streampark.console.core.enums.FlinkAppStateEnum.Bridge.toK8sFlinkJobState;
5452

@@ -161,9 +159,9 @@ public void subscribeCheckpointChange(FlinkJobCheckpointChangeEvent event) {
161159
}
162160

163161
private void setByJobStatusCV(FlinkApplication app, JobStatusCV jobStatus) {
164-
// infer the final flink job state
165-
Enumeration.Value state = FlinkJobStatusWatcher.inferFlinkJobStateFromPersist(
166-
jobStatus.jobState(), toK8sFlinkJobState(app.getStateEnum()));
162+
FlinkJobState state =
163+
FlinkJobStatusWatcher.inferFlinkJobStateFromPersist(
164+
jobStatus.jobState(), toK8sFlinkJobState(app.getStateEnum()));
167165

168166
// corrective start-time / end-time / duration
169167
long preStartTime = app.getStartTime() != null ? app.getStartTime().getTime() : 0;

streampark-flink/streampark-flink-client/streampark-flink-client-core/src/main/scala/org/apache/streampark/flink/client/impl/KubernetesNativeSessionClient.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,20 @@ object KubernetesNativeSessionClient extends KubernetesNativeClientTrait with Lo
6363
fatJar: File): SubmitResponse = {
6464

6565
// get jm rest url of flink session cluster
66-
val clusterKey = ClusterKey(
67-
FlinkK8sDeployMode.SESSION,
68-
submitRequest.kubernetesNamespace,
69-
submitRequest.clusterId)
66+
val clusterKey = ClusterKey.builder()
67+
.executeMode(FlinkK8sDeployMode.SESSION)
68+
.namespace(submitRequest.kubernetesNamespace)
69+
.clusterId(submitRequest.clusterId)
70+
.build()
7071
val jmRestUrl = KubernetesRetriever
7172
.retrieveFlinkRestUrl(clusterKey)
72-
.getOrElse(
73-
throw new Exception(
73+
.orElseThrow(() =>
74+
new Exception(
7475
s"[flink-submit] retrieve flink session rest url failed, clusterKey=$clusterKey"))
7576
// submit job via rest api
7677
val jobId =
7778
FlinkSessionSubmitHelper.submitViaRestApi(jmRestUrl, fatJar, flinkConfig)
78-
SubmitResponse(clusterKey.clusterId, flinkConfig.toMap, jobId, jmRestUrl)
79+
SubmitResponse(clusterKey.clusterId(), flinkConfig.toMap, jobId, jmRestUrl)
7980
}
8081

8182
/** Submit flink session job with building JobGraph via ClusterClient api. */

streampark-flink/streampark-flink-kubernetes/pom.xml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
<properties>
3232
<apache.httpclient5.version>5.1</apache.httpclient5.version>
3333
<snakeyaml.version>2.0</snakeyaml.version>
34-
<scalatest.skiptests>true</scalatest.skiptests>
3534
</properties>
3635

3736
<dependencies>
@@ -123,9 +122,15 @@
123122
</dependency>
124123

125124
<dependency>
126-
<groupId>org.json4s</groupId>
127-
<artifactId>json4s-jackson_${scala.binary.version}</artifactId>
128-
<scope>provided</scope>
125+
<groupId>com.fasterxml.jackson.core</groupId>
126+
<artifactId>jackson-annotations</artifactId>
127+
<version>${jackson.version}</version>
128+
</dependency>
129+
130+
<dependency>
131+
<groupId>com.fasterxml.jackson.core</groupId>
132+
<artifactId>jackson-databind</artifactId>
133+
<version>${jackson.version}</version>
129134
</dependency>
130135

131136
<!-- snake yaml -->
@@ -157,6 +162,12 @@
157162
<artifactId>lombok</artifactId>
158163
</dependency>
159164

165+
<dependency>
166+
<groupId>org.junit.jupiter</groupId>
167+
<artifactId>junit-jupiter-engine</artifactId>
168+
<scope>test</scope>
169+
</dependency>
170+
160171
</dependencies>
161172

162173
<build>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.streampark.flink.kubernetes;
19+
20+
import org.apache.streampark.common.util.Utils;
21+
22+
import java.io.Serializable;
23+
import java.util.Objects;
24+
25+
public final class CacheKey implements Serializable {
26+
27+
private final Long key;
28+
29+
public CacheKey(Long key) {
30+
this.key = key;
31+
}
32+
33+
public Long key() {
34+
return key;
35+
}
36+
37+
@Override
38+
public int hashCode() {
39+
return Utils.hashCode(key);
40+
}
41+
42+
@Override
43+
public boolean equals(Object obj) {
44+
if (!(obj instanceof CacheKey)) {
45+
return false;
46+
}
47+
CacheKey that = (CacheKey) obj;
48+
return Objects.equals(key, that.key);
49+
}
50+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.streampark.flink.kubernetes;
19+
20+
import org.apache.streampark.common.util.ThreadUtils;
21+
22+
import com.google.common.eventbus.AsyncEventBus;
23+
import com.google.common.eventbus.EventBus;
24+
25+
import java.util.concurrent.LinkedBlockingQueue;
26+
import java.util.concurrent.ThreadPoolExecutor;
27+
import java.util.concurrent.TimeUnit;
28+
29+
public class ChangeEventBus {
30+
31+
private static final int CPU_NUM = Math.max(4, Runtime.getRuntime().availableProcessors() * 2);
32+
33+
private final ThreadPoolExecutor execPool =
34+
new ThreadPoolExecutor(
35+
CPU_NUM,
36+
CPU_NUM * 5,
37+
60L,
38+
TimeUnit.SECONDS,
39+
new LinkedBlockingQueue<>(),
40+
ThreadUtils.threadFactory("streampark-k8s-watching-thread"));
41+
42+
final AsyncEventBus asyncEventBus =
43+
new AsyncEventBus("[StreamPark][flink-k8s]AsyncEventBus", execPool);
44+
45+
final EventBus syncEventBus = new EventBus("[StreamPark][flink-k8s]SyncEventBus");
46+
47+
public void postAsync(Object event) {
48+
asyncEventBus.post(event);
49+
}
50+
51+
public void postSync(Object event) {
52+
syncEventBus.post(event);
53+
}
54+
55+
public void registerListener(Object listener) {
56+
asyncEventBus.register(listener);
57+
syncEventBus.register(listener);
58+
}
59+
}

0 commit comments

Comments
 (0)