From e2c27a7b113f02bf22ed5e4acc5cf9cea04debaa Mon Sep 17 00:00:00 2001 From: "Wang, Fei" Date: Sun, 26 Oct 2025 14:14:01 -0700 Subject: [PATCH 1/3] Support to get kyuubi server event with RESTful api --- .../apache/kyuubi/client/AdminRestApi.java | 7 + .../client/api/v1/dto/KyuubiServerEvent.java | 146 ++++++++++++++++++ .../apache/kyuubi/server/KyuubiServer.scala | 4 + .../apache/kyuubi/server/api/ApiUtils.scala | 15 +- .../kyuubi/server/api/v1/AdminResource.scala | 15 +- .../server/api/v1/AdminResourceSuite.scala | 11 ++ .../rest/client/AdminRestApiSuite.scala | 10 ++ 7 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java diff --git a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java index c616352142f..b06dbbc7f29 100644 --- a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java +++ b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java @@ -20,6 +20,7 @@ import java.util.*; import org.apache.commons.lang3.StringUtils; import org.apache.kyuubi.client.api.v1.dto.Engine; +import org.apache.kyuubi.client.api.v1.dto.KyuubiServerEvent; import org.apache.kyuubi.client.api.v1.dto.OperationData; import org.apache.kyuubi.client.api.v1.dto.ServerData; import org.apache.kyuubi.client.api.v1.dto.SessionData; @@ -165,6 +166,12 @@ public List listServers() { return Arrays.asList(result); } + public KyuubiServerEvent getServerEvent() { + return this.getClient() + .get( + API_BASE_PATH + "/server/event", null, KyuubiServerEvent.class, client.getAuthHeader()); + } + private IRestClient getClient() { return this.client.getHttpClient(); } diff --git a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java new file mode 100644 index 00000000000..e542642ceb1 --- /dev/null +++ b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java @@ -0,0 +1,146 @@ +/* + * 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.kyuubi.client.api.v1.dto; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class KyuubiServerEvent { + private String serverName; + private Long startTime; + private Long eventTime; + private String state; + private String serverIp; + private Map serverConf; + private Map serverEnv; + + public KyuubiServerEvent() {} + + public KyuubiServerEvent( + String serverName, + Long startTime, + Long eventTime, + String state, + String serverIp, + Map serverConf, + Map serverEnv) { + this.serverName = serverName; + this.startTime = startTime; + this.eventTime = eventTime; + this.state = state; + this.serverIp = serverIp; + this.serverConf = serverConf; + this.serverEnv = serverEnv; + } + + public String getServerName() { + return serverName; + } + + public void setServerName(String serverName) { + this.serverName = serverName; + } + + public Long getStartTime() { + return startTime; + } + + public void setStartTime(Long startTime) { + this.startTime = startTime; + } + + public Long getEventTime() { + return eventTime; + } + + public void setEventTime(Long eventTime) { + this.eventTime = eventTime; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getServerIp() { + return serverIp; + } + + public void setServerIp(String serverIp) { + this.serverIp = serverIp; + } + + public Map getServerConf() { + if (null == serverConf) { + return Collections.emptyMap(); + } + return serverConf; + } + + public void setServerConf(Map serverConf) { + this.serverConf = serverConf; + } + + public Map getServerEnv() { + if (null == serverEnv) { + return Collections.emptyMap(); + } + return serverEnv; + } + + public void setServerEnv(Map serverEnv) { + this.serverEnv = serverEnv; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + KyuubiServerEvent that = (KyuubiServerEvent) o; + return Objects.equals(getServerName(), that.getServerName()) + && Objects.equals(getStartTime(), that.getStartTime()) + && Objects.equals(getEventTime(), that.getEventTime()) + && Objects.equals(getState(), that.getState()) + && Objects.equals(getServerIp(), that.getServerIp()) + && Objects.equals(getServerConf(), that.getServerConf()) + && Objects.equals(getServerEnv(), that.getServerEnv()); + } + + @Override + public int hashCode() { + return Objects.hash( + getServerName(), + getStartTime(), + getEventTime(), + getState(), + getServerIp(), + getServerConf(), + getServerEnv()); + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); + } +} diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala index 0996472653b..ee4c16b6ca4 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala @@ -231,6 +231,10 @@ class KyuubiServer(name: String) extends Serverable(name) { super.stop() } + def getServerEvent(): Option[KyuubiServerInfoEvent] = { + KyuubiServerInfoEvent(this, state) + } + private def initLoggerEventHandler(conf: KyuubiConf): Unit = { ServerEventHandlerRegister.registerEventLoggers(conf) } diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala index 1676ba15759..a1154498309 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala @@ -21,7 +21,8 @@ import scala.collection.JavaConverters._ import org.apache.kyuubi.{Logging, Utils} import org.apache.kyuubi.client.api.v1.dto -import org.apache.kyuubi.client.api.v1.dto.{OperationData, OperationProgress, ServerData, SessionData} +import org.apache.kyuubi.client.api.v1.dto.{KyuubiServerEvent, OperationData, OperationProgress, ServerData, SessionData} +import org.apache.kyuubi.events.KyuubiServerInfoEvent import org.apache.kyuubi.ha.client.ServiceNodeInfo import org.apache.kyuubi.operation.KyuubiOperation import org.apache.kyuubi.session.KyuubiSession @@ -136,6 +137,18 @@ object ApiUtils extends Logging { "Running") } + def serverEvent(serverEvent: KyuubiServerInfoEvent): KyuubiServerEvent = { + if (serverEvent == null) return new KyuubiServerEvent() + new KyuubiServerEvent( + serverEvent.serverName, + serverEvent.startTime, + serverEvent.eventTime, + serverEvent.state, + serverEvent.serverIP, + serverEvent.serverConf.asJava, + serverEvent.serverEnv.asJava) + } + def logAndRefineErrorMsg(errorMsg: String, throwable: Throwable): String = { error(errorMsg, throwable) s"$errorMsg: ${Utils.prettyPrint(throwable)}" diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala index 6b354820ac9..ad6283d61cd 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala @@ -378,7 +378,7 @@ private[v1] class AdminResource extends ApiRequestContext with Logging { new Content( mediaType = MediaType.APPLICATION_JSON, array = new ArraySchema(schema = new Schema(implementation = - classOf[OperationData])))), + classOf[ServerData])))), description = "list all live kyuubi servers") @GET @Path("server") @@ -401,6 +401,19 @@ private[v1] class AdminResource extends ApiRequestContext with Logging { servers.toSeq } + @ApiResponse( + responseCode = "200", + content = Array( + new Content( + mediaType = MediaType.APPLICATION_JSON, + schema = new Schema(implementation = classOf[KyuubiServerEvent]))), + description = "Get the server event") + @GET + @Path("server/event") + def getServerEvent(): KyuubiServerEvent = { + ApiUtils.serverEvent(KyuubiServer.kyuubiServer.getServerEvent().orNull) + } + private def normalizeEngineInfo( userName: String, engineType: String, diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala index 669a0af0f80..5fb20d73ba2 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala @@ -841,4 +841,15 @@ class AdminResourceSuite extends KyuubiFunSuite with RestFrontendTestHelper { assert("Running".equals(testServer.getStatus)) } } + + test("get server event") { + val response = webTarget.path("api/v1/admin/server/event") + .request() + .header(AUTHORIZATION_HEADER, HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser)) + .get + + assert(response.getStatus === 200) + val serverEvent = response.readEntity(classOf[KyuubiServerEvent]) + assert(serverEvent.getStartTime > 0) + } } diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala index 9aaefd2735c..0c5a707625e 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala @@ -173,4 +173,14 @@ class AdminRestApiSuite extends RestClientTestHelper { assert(servers.map(s => s.getInstance()).contains(server.frontendServices.last.connectionUrl)) } } + + test("get server event") { + val spnegoKyuubiRestClient: KyuubiRestClient = + KyuubiRestClient.builder(baseUri.toString) + .authHeaderMethod(KyuubiRestClient.AuthHeaderMethod.SPNEGO) + .spnegoHost("localhost") + .build() + val adminRestApi = new AdminRestApi(spnegoKyuubiRestClient) + assert(adminRestApi.getServerEvent.getStartTime > 0) + } } From 329aa0b4b47a5e69603e831de3238eaeede55980 Mon Sep 17 00:00:00 2001 From: "Wang, Fei" Date: Sun, 26 Oct 2025 15:40:27 -0700 Subject: [PATCH 2/3] return build info --- .../client/api/v1/dto/KyuubiServerEvent.java | 22 ++++++++++++++++--- .../apache/kyuubi/server/api/ApiUtils.scala | 6 ++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java index e542642ceb1..b164baf55cf 100644 --- a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java +++ b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java @@ -31,6 +31,7 @@ public class KyuubiServerEvent { private String serverIp; private Map serverConf; private Map serverEnv; + private Map buildInfo; public KyuubiServerEvent() {} @@ -41,7 +42,8 @@ public KyuubiServerEvent( String state, String serverIp, Map serverConf, - Map serverEnv) { + Map serverEnv, + Map buildInfo) { this.serverName = serverName; this.startTime = startTime; this.eventTime = eventTime; @@ -49,6 +51,7 @@ public KyuubiServerEvent( this.serverIp = serverIp; this.serverConf = serverConf; this.serverEnv = serverEnv; + this.buildInfo = buildInfo; } public String getServerName() { @@ -113,6 +116,17 @@ public void setServerEnv(Map serverEnv) { this.serverEnv = serverEnv; } + public Map getBuildInfo() { + if (null == buildInfo) { + return Collections.emptyMap(); + } + return buildInfo; + } + + public void setBuildInfo(Map buildInfo) { + this.buildInfo = buildInfo; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -124,7 +138,8 @@ public boolean equals(Object o) { && Objects.equals(getState(), that.getState()) && Objects.equals(getServerIp(), that.getServerIp()) && Objects.equals(getServerConf(), that.getServerConf()) - && Objects.equals(getServerEnv(), that.getServerEnv()); + && Objects.equals(getServerEnv(), that.getServerEnv()) + && Objects.equals(getBuildInfo(), that.getBuildInfo()); } @Override @@ -136,7 +151,8 @@ public int hashCode() { getState(), getServerIp(), getServerConf(), - getServerEnv()); + getServerEnv(), + getBuildInfo()); } @Override diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala index a1154498309..25566b28dff 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala @@ -146,7 +146,11 @@ object ApiUtils extends Logging { serverEvent.state, serverEvent.serverIP, serverEvent.serverConf.asJava, - serverEvent.serverEnv.asJava) + serverEvent.serverEnv.asJava, + (Map( + "BUILD_USER" -> serverEvent.BUILD_USER, + "BUILD_DATE" -> serverEvent.BUILD_DATE, + "REPO_URL" -> serverEvent.REPO_URL) ++ serverEvent.VERSION_INFO).asJava) } def logAndRefineErrorMsg(errorMsg: String, throwable: Throwable): String = { From aabf80e95813445b78d6764ec781c3c077201fad Mon Sep 17 00:00:00 2001 From: "Wang, Fei" Date: Sun, 26 Oct 2025 20:52:29 -0700 Subject: [PATCH 3/3] serverIP --- .../client/api/v1/dto/KyuubiServerEvent.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java index b164baf55cf..7b508970caf 100644 --- a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java +++ b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/api/v1/dto/KyuubiServerEvent.java @@ -28,7 +28,7 @@ public class KyuubiServerEvent { private Long startTime; private Long eventTime; private String state; - private String serverIp; + private String serverIP; private Map serverConf; private Map serverEnv; private Map buildInfo; @@ -40,7 +40,7 @@ public KyuubiServerEvent( Long startTime, Long eventTime, String state, - String serverIp, + String serverIP, Map serverConf, Map serverEnv, Map buildInfo) { @@ -48,7 +48,7 @@ public KyuubiServerEvent( this.startTime = startTime; this.eventTime = eventTime; this.state = state; - this.serverIp = serverIp; + this.serverIP = serverIP; this.serverConf = serverConf; this.serverEnv = serverEnv; this.buildInfo = buildInfo; @@ -86,12 +86,12 @@ public void setState(String state) { this.state = state; } - public String getServerIp() { - return serverIp; + public String getServerIP() { + return serverIP; } - public void setServerIp(String serverIp) { - this.serverIp = serverIp; + public void setServerIP(String serverIP) { + this.serverIP = serverIP; } public Map getServerConf() { @@ -136,7 +136,7 @@ public boolean equals(Object o) { && Objects.equals(getStartTime(), that.getStartTime()) && Objects.equals(getEventTime(), that.getEventTime()) && Objects.equals(getState(), that.getState()) - && Objects.equals(getServerIp(), that.getServerIp()) + && Objects.equals(getServerIP(), that.getServerIP()) && Objects.equals(getServerConf(), that.getServerConf()) && Objects.equals(getServerEnv(), that.getServerEnv()) && Objects.equals(getBuildInfo(), that.getBuildInfo()); @@ -149,7 +149,7 @@ public int hashCode() { getStartTime(), getEventTime(), getState(), - getServerIp(), + getServerIP(), getServerConf(), getServerEnv(), getBuildInfo());