Skip to content

Commit c4c3a03

Browse files
committed
[K8s] Handle terminated applications in Spark pod add events
- Handle terminated applications in SparkEnginePodEventHandler.onAdd. - Remove cleanTerminatedAppPodsOnKubernetesClientInitialize and its executor.
1 parent 3df4e50 commit c4c3a03

2 files changed

Lines changed: 56 additions & 47 deletions

File tree

kyuubi-server/src/main/scala/org/apache/kyuubi/engine/KubernetesApplicationOperation.scala

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -83,45 +83,9 @@ class KubernetesApplicationOperation extends ApplicationOperation with Logging {
8383

8484
private var cleanupCanceledAppPodExecutor: ThreadPoolExecutor = _
8585

86-
private var kubernetesClientInitializeCleanupTerminatedPodExecutor: ThreadPoolExecutor = _
87-
8886
private def getOrCreateKubernetesClient(kubernetesInfo: KubernetesInfo): KubernetesClient = {
8987
checkKubernetesInfo(kubernetesInfo)
90-
kubernetesClients.computeIfAbsent(
91-
kubernetesInfo,
92-
kInfo => {
93-
val kubernetesClient = buildKubernetesClient(kInfo)
94-
cleanTerminatedAppPodsOnKubernetesClientInitialize(kInfo, kubernetesClient)
95-
kubernetesClient
96-
})
97-
}
98-
99-
private def cleanTerminatedAppPodsOnKubernetesClientInitialize(
100-
kubernetesInfo: KubernetesInfo,
101-
kubernetesClient: KubernetesClient): Unit = {
102-
if (kubernetesClientInitializeCleanupTerminatedPodExecutor != null) {
103-
kubernetesClientInitializeCleanupTerminatedPodExecutor.submit(new Runnable {
104-
override def run(): Unit = {
105-
val existingPods =
106-
kubernetesClient.pods().withLabel(LABEL_KYUUBI_UNIQUE_KEY).list().getItems
107-
info(s"[$kubernetesInfo] Found ${existingPods.size()} existing pods with label " +
108-
s"$LABEL_KYUUBI_UNIQUE_KEY")
109-
val eventType = KubernetesResourceEventTypes.UPDATE
110-
existingPods.asScala.filter(isSparkEnginePod).foreach { pod =>
111-
val appState = toApplicationState(pod, appStateSource, appStateContainer, eventType)
112-
if (isTerminated(appState)) {
113-
val kyuubiUniqueKey = pod.getMetadata.getLabels.get(LABEL_KYUUBI_UNIQUE_KEY)
114-
info(s"[$kubernetesInfo] Found existing pod ${pod.getMetadata.getName} with " +
115-
s"${toLabel(kyuubiUniqueKey)} in app state $appState, marking it as terminated")
116-
if (appInfoStore.get(kyuubiUniqueKey) == null) {
117-
updateApplicationState(kubernetesInfo, pod, eventType)
118-
}
119-
markApplicationTerminated(kubernetesInfo, pod, eventType)
120-
}
121-
}
122-
}
123-
})
124-
}
88+
kubernetesClients.computeIfAbsent(kubernetesInfo, kInfo => buildKubernetesClient(kInfo))
12589
}
12690

12791
private var metadataManager: Option[MetadataManager] = _
@@ -210,9 +174,6 @@ class KubernetesApplicationOperation extends ApplicationOperation with Logging {
210174
TimeUnit.MILLISECONDS)
211175
cleanupCanceledAppPodExecutor = ThreadUtils.newDaemonCachedThreadPool(
212176
"cleanup-canceled-app-pod-thread")
213-
kubernetesClientInitializeCleanupTerminatedPodExecutor =
214-
ThreadUtils.newDaemonCachedThreadPool(
215-
"kubernetes-client-initialize-cleanup-terminated-pod-thread")
216177
initializeKubernetesClient(kyuubiConf)
217178
}
218179

@@ -373,11 +334,6 @@ class KubernetesApplicationOperation extends ApplicationOperation with Logging {
373334
ThreadUtils.shutdown(cleanupCanceledAppPodExecutor)
374335
cleanupCanceledAppPodExecutor = null
375336
}
376-
377-
if (kubernetesClientInitializeCleanupTerminatedPodExecutor != null) {
378-
ThreadUtils.shutdown(kubernetesClientInitializeCleanupTerminatedPodExecutor)
379-
kubernetesClientInitializeCleanupTerminatedPodExecutor = null
380-
}
381337
}
382338

383339
private class SparkEnginePodEventHandler(kubernetesInfo: KubernetesInfo)
@@ -387,6 +343,10 @@ class KubernetesApplicationOperation extends ApplicationOperation with Logging {
387343
if (isSparkEnginePod(pod)) {
388344
val eventType = KubernetesResourceEventTypes.ADD
389345
updateApplicationState(kubernetesInfo, pod, eventType)
346+
val appState = toApplicationState(pod, appStateSource, appStateContainer, eventType)
347+
if (isTerminated(appState)) {
348+
markApplicationTerminated(kubernetesInfo, pod, eventType)
349+
}
390350
KubernetesApplicationAuditLogger.audit(
391351
eventType,
392352
kubernetesInfo,

kyuubi-server/src/test/scala/org/apache/kyuubi/engine/KubernetesApplicationOperationSuite.scala

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,63 @@
1717

1818
package org.apache.kyuubi.engine
1919

20-
import io.fabric8.kubernetes.api.model.{ContainerState, ContainerStateWaiting}
20+
import com.google.common.cache.Cache
21+
import io.fabric8.kubernetes.api.model.{ContainerState, ContainerStateWaiting, Pod, PodBuilder}
22+
import io.fabric8.kubernetes.client.informers.ResourceEventHandler
2123

2224
import org.apache.kyuubi.{KyuubiException, KyuubiFunSuite}
2325
import org.apache.kyuubi.config.KyuubiConf
24-
import org.apache.kyuubi.engine.ApplicationState.{FAILED, PENDING}
26+
import org.apache.kyuubi.engine.ApplicationState.{ApplicationState, FAILED, FINISHED, PENDING}
27+
import org.apache.kyuubi.engine.KubernetesApplicationOperation.LABEL_KYUUBI_UNIQUE_KEY
2528

2629
class KubernetesApplicationOperationSuite extends KyuubiFunSuite {
2730

31+
private def podEventHandler(
32+
operation: KubernetesApplicationOperation,
33+
kubernetesInfo: KubernetesInfo): ResourceEventHandler[Pod] = {
34+
val handlerClass = classOf[KubernetesApplicationOperation].getDeclaredClasses
35+
.find(_.getSimpleName == "SparkEnginePodEventHandler")
36+
.getOrElse(fail("SparkEnginePodEventHandler not found"))
37+
val constructor = handlerClass.getDeclaredConstructors.head
38+
constructor.setAccessible(true)
39+
constructor
40+
.newInstance(operation, kubernetesInfo)
41+
.asInstanceOf[ResourceEventHandler[Pod]]
42+
}
43+
44+
private def cleanupTrigger(
45+
operation: KubernetesApplicationOperation): Cache[String, ApplicationState] = {
46+
val field = classOf[KubernetesApplicationOperation].getDeclaredFields
47+
.find(_.getName.endsWith("cleanupTerminatedAppInfoTrigger"))
48+
.getOrElse(fail("cleanupTerminatedAppInfoTrigger not found"))
49+
field.setAccessible(true)
50+
field.get(operation).asInstanceOf[Cache[String, ApplicationState]]
51+
}
52+
53+
test("mark terminated application received from pod add event") {
54+
val operation = new KubernetesApplicationOperation()
55+
operation.initialize(KyuubiConf(), None)
56+
val tag = "terminated-app"
57+
val pod = new PodBuilder()
58+
.withNewMetadata()
59+
.withName("terminated-driver")
60+
.addToLabels(LABEL_KYUUBI_UNIQUE_KEY, tag)
61+
.addToLabels("spark-app-selector", "spark-application")
62+
.endMetadata()
63+
.withNewStatus()
64+
.withPhase("Succeeded")
65+
.withContainerStatuses()
66+
.endStatus()
67+
.build()
68+
69+
try {
70+
podEventHandler(operation, KubernetesInfo()).onAdd(pod)
71+
assert(cleanupTrigger(operation).getIfPresent(tag) === FINISHED)
72+
} finally {
73+
operation.stop()
74+
}
75+
}
76+
2877
test("test check kubernetes info") {
2978
val kyuubiConf = KyuubiConf()
3079
kyuubiConf.set(KyuubiConf.KUBERNETES_CONTEXT_ALLOW_LIST.key, "1,2")

0 commit comments

Comments
 (0)