[KYUUBI #4439][FOLLOWUP] Add dto class for operation data
### _Why are the changes needed?_ - add dto for operation data, which is programming friendly - show exception for session data ### _How was this patch tested?_ - [ ] Add some test cases that check the changes thoroughly including negative and positive cases if possible - [ ] Add screenshots for manual tests if appropriate - [x] [Run test](https://kyuubi.readthedocs.io/en/master/develop_tools/testing.html#running-tests) locally before make a pull request Closes #4468 from turboFei/session_op_dto. Closes #4439 fa905e70c [fwang12] fix ut 5c1c7c845 [fwang12] save 2d20215a0 [fwang12] comments 46cd2384e [fwang12] saev Authored-by: fwang12 <fwang12@ebay.com> Signed-off-by: fwang12 <fwang12@ebay.com>
This commit is contained in:
parent
39eac5a780
commit
222a3345f2
@ -387,4 +387,19 @@ object Utils extends Logging {
|
||||
Option(Thread.currentThread().getContextClassLoader).getOrElse(getKyuubiClassLoader)
|
||||
|
||||
def isOnK8s: Boolean = Files.exists(Paths.get("/var/run/secrets/kubernetes.io"))
|
||||
|
||||
/**
|
||||
* Return a nice string representation of the exception. It will call "printStackTrace" to
|
||||
* recursively generate the stack trace including the exception and its causes.
|
||||
*/
|
||||
def prettyPrint(e: Throwable): String = {
|
||||
if (e == null) {
|
||||
""
|
||||
} else {
|
||||
// Use e.printStackTrace here because e.getStackTrace doesn't include the cause
|
||||
val stringWriter = new StringWriter()
|
||||
e.printStackTrace(new PrintWriter(stringWriter))
|
||||
stringWriter.toString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.Objects;
|
||||
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public class OperationData {
|
||||
private String identifier;
|
||||
private String statement;
|
||||
private String state;
|
||||
private Long createTime;
|
||||
private Long startTime;
|
||||
private Long completeTime;
|
||||
private String exception;
|
||||
private String sessionId;
|
||||
private String sessionUser;
|
||||
private String sessionType;
|
||||
|
||||
public OperationData() {}
|
||||
|
||||
public OperationData(
|
||||
String identifier,
|
||||
String statement,
|
||||
String state,
|
||||
Long createTime,
|
||||
Long startTime,
|
||||
Long completeTime,
|
||||
String exception,
|
||||
String sessionId,
|
||||
String sessionUser,
|
||||
String sessionType) {
|
||||
this.identifier = identifier;
|
||||
this.statement = statement;
|
||||
this.state = state;
|
||||
this.createTime = createTime;
|
||||
this.startTime = startTime;
|
||||
this.completeTime = completeTime;
|
||||
this.exception = exception;
|
||||
this.sessionId = sessionId;
|
||||
this.sessionUser = sessionUser;
|
||||
this.sessionType = sessionType;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public void setIdentifier(String identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public String getStatement() {
|
||||
return statement;
|
||||
}
|
||||
|
||||
public void setStatement(String statement) {
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public Long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(Long createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public Long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(Long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public Long getCompleteTime() {
|
||||
return completeTime;
|
||||
}
|
||||
|
||||
public void setCompleteTime(Long completeTime) {
|
||||
this.completeTime = completeTime;
|
||||
}
|
||||
|
||||
public String getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void setException(String exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public String getSessionUser() {
|
||||
return sessionUser;
|
||||
}
|
||||
|
||||
public void setSessionUser(String sessionUser) {
|
||||
this.sessionUser = sessionUser;
|
||||
}
|
||||
|
||||
public String getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
|
||||
public void setSessionType(String sessionType) {
|
||||
this.sessionType = sessionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SessionData that = (SessionData) o;
|
||||
return Objects.equals(getIdentifier(), that.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ public class SessionData {
|
||||
private Long createTime;
|
||||
private Long duration;
|
||||
private Long idleTime;
|
||||
private String exception;
|
||||
|
||||
public SessionData() {}
|
||||
|
||||
@ -41,7 +42,8 @@ public class SessionData {
|
||||
Map<String, String> conf,
|
||||
Long createTime,
|
||||
Long duration,
|
||||
Long idleTime) {
|
||||
Long idleTime,
|
||||
String exception) {
|
||||
this.identifier = identifier;
|
||||
this.user = user;
|
||||
this.ipAddr = ipAddr;
|
||||
@ -49,6 +51,7 @@ public class SessionData {
|
||||
this.createTime = createTime;
|
||||
this.duration = duration;
|
||||
this.idleTime = idleTime;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
@ -110,6 +113,14 @@ public class SessionData {
|
||||
this.idleTime = idleTime;
|
||||
}
|
||||
|
||||
public String getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void setException(String exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.server.api
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
import org.apache.kyuubi.Utils
|
||||
import org.apache.kyuubi.client.api.v1.dto.{OperationData, SessionData}
|
||||
import org.apache.kyuubi.events.KyuubiOperationEvent
|
||||
import org.apache.kyuubi.operation.KyuubiOperation
|
||||
import org.apache.kyuubi.session.KyuubiSession
|
||||
|
||||
object ApiUtils {
|
||||
|
||||
def sessionData(session: KyuubiSession): SessionData = {
|
||||
new SessionData(
|
||||
session.handle.identifier.toString,
|
||||
session.user,
|
||||
session.ipAddress,
|
||||
session.conf.asJava,
|
||||
session.createTime,
|
||||
session.lastAccessTime - session.createTime,
|
||||
session.getNoOperationTime,
|
||||
session.getSessionEvent.flatMap(_.exception).map(Utils.prettyPrint).getOrElse(""))
|
||||
}
|
||||
|
||||
def operationData(operation: KyuubiOperation): OperationData = {
|
||||
val opEvent = KyuubiOperationEvent(operation)
|
||||
new OperationData(
|
||||
opEvent.statementId,
|
||||
opEvent.statement,
|
||||
opEvent.state,
|
||||
opEvent.createTime,
|
||||
opEvent.startTime,
|
||||
opEvent.completeTime,
|
||||
opEvent.exception.map(Utils.prettyPrint).getOrElse(""),
|
||||
opEvent.sessionId,
|
||||
opEvent.sessionUser,
|
||||
opEvent.sessionType)
|
||||
}
|
||||
}
|
||||
@ -30,17 +30,16 @@ import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.apache.zookeeper.KeeperException.NoNodeException
|
||||
|
||||
import org.apache.kyuubi.{KYUUBI_VERSION, Logging, Utils}
|
||||
import org.apache.kyuubi.client.api.v1.dto.{Engine, SessionData}
|
||||
import org.apache.kyuubi.client.api.v1.dto.{Engine, OperationData, SessionData}
|
||||
import org.apache.kyuubi.config.KyuubiConf
|
||||
import org.apache.kyuubi.config.KyuubiConf._
|
||||
import org.apache.kyuubi.events.KyuubiOperationEvent
|
||||
import org.apache.kyuubi.ha.HighAvailabilityConf.HA_NAMESPACE
|
||||
import org.apache.kyuubi.ha.client.{DiscoveryPaths, ServiceNodeInfo}
|
||||
import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
|
||||
import org.apache.kyuubi.operation.{KyuubiOperation, OperationHandle}
|
||||
import org.apache.kyuubi.server.KyuubiServer
|
||||
import org.apache.kyuubi.server.api.ApiRequestContext
|
||||
import org.apache.kyuubi.session.SessionHandle
|
||||
import org.apache.kyuubi.server.api.{ApiRequestContext, ApiUtils}
|
||||
import org.apache.kyuubi.session.{KyuubiSession, SessionHandle}
|
||||
|
||||
@Tag(name = "Admin")
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
@ -122,15 +121,8 @@ private[v1] class AdminResource extends ApiRequestContext with Logging {
|
||||
throw new NotAllowedException(
|
||||
s"$userName is not allowed to list all live sessions")
|
||||
}
|
||||
fe.be.sessionManager.allSessions().map { session =>
|
||||
new SessionData(
|
||||
session.handle.identifier.toString,
|
||||
session.user,
|
||||
session.ipAddress,
|
||||
session.conf.asJava,
|
||||
session.createTime,
|
||||
session.lastAccessTime - session.createTime,
|
||||
session.getNoOperationTime)
|
||||
fe.be.sessionManager.allSessions().map { case session =>
|
||||
ApiUtils.sessionData(session.asInstanceOf[KyuubiSession])
|
||||
}.toSeq
|
||||
}
|
||||
|
||||
@ -157,12 +149,12 @@ private[v1] class AdminResource extends ApiRequestContext with Logging {
|
||||
content = Array(new Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
array = new ArraySchema(schema = new Schema(implementation =
|
||||
classOf[KyuubiOperationEvent])))),
|
||||
classOf[OperationData])))),
|
||||
description =
|
||||
"get the list of all active operation events")
|
||||
"get the list of all active operations")
|
||||
@GET
|
||||
@Path("operations")
|
||||
def listOperations(): Seq[KyuubiOperationEvent] = {
|
||||
def listOperations(): Seq[OperationData] = {
|
||||
val userName = fe.getSessionUser(Map.empty[String, String])
|
||||
val ipAddress = fe.getIpAddress
|
||||
info(s"Received listing all of the active operations request from $userName/$ipAddress")
|
||||
@ -171,7 +163,7 @@ private[v1] class AdminResource extends ApiRequestContext with Logging {
|
||||
s"$userName is not allowed to list all the operations")
|
||||
}
|
||||
fe.be.sessionManager.operationManager.allOperations()
|
||||
.map(operation => KyuubiOperationEvent(operation.asInstanceOf[KyuubiOperation])).toSeq
|
||||
.map(operation => ApiUtils.operationData(operation.asInstanceOf[KyuubiOperation])).toSeq
|
||||
}
|
||||
|
||||
@ApiResponse(
|
||||
|
||||
@ -35,7 +35,7 @@ import org.apache.kyuubi.client.api.v1.dto._
|
||||
import org.apache.kyuubi.config.KyuubiReservedKeys._
|
||||
import org.apache.kyuubi.events.KyuubiEvent
|
||||
import org.apache.kyuubi.operation.OperationHandle
|
||||
import org.apache.kyuubi.server.api.ApiRequestContext
|
||||
import org.apache.kyuubi.server.api.{ApiRequestContext, ApiUtils}
|
||||
import org.apache.kyuubi.session.KyuubiSession
|
||||
import org.apache.kyuubi.session.SessionHandle
|
||||
|
||||
@ -54,15 +54,8 @@ private[v1] class SessionsResource extends ApiRequestContext with Logging {
|
||||
description = "get the list of all live sessions")
|
||||
@GET
|
||||
def sessions(): Seq[SessionData] = {
|
||||
sessionManager.allSessions().map { session =>
|
||||
new SessionData(
|
||||
session.handle.identifier.toString,
|
||||
session.user,
|
||||
session.ipAddress,
|
||||
session.conf.asJava,
|
||||
session.createTime,
|
||||
session.lastAccessTime - session.createTime,
|
||||
session.getNoOperationTime)
|
||||
sessionManager.allSessions().map { case session =>
|
||||
ApiUtils.sessionData(session.asInstanceOf[KyuubiSession])
|
||||
}.toSeq
|
||||
}
|
||||
|
||||
|
||||
@ -27,13 +27,12 @@ import org.apache.hive.service.rpc.thrift.TProtocolVersion.HIVE_CLI_SERVICE_PROT
|
||||
import org.scalatest.time.SpanSugar.convertIntToGrainOfTime
|
||||
|
||||
import org.apache.kyuubi.{KYUUBI_VERSION, KyuubiFunSuite, RestFrontendTestHelper, Utils}
|
||||
import org.apache.kyuubi.client.api.v1.dto.{Engine, SessionData, SessionHandle, SessionOpenRequest}
|
||||
import org.apache.kyuubi.client.api.v1.dto.{Engine, OperationData, SessionData, SessionHandle, SessionOpenRequest}
|
||||
import org.apache.kyuubi.config.KyuubiConf
|
||||
import org.apache.kyuubi.config.KyuubiReservedKeys.KYUUBI_SESSION_CONNECTION_URL_KEY
|
||||
import org.apache.kyuubi.engine.{ApplicationState, EngineRef, KyuubiApplicationManager}
|
||||
import org.apache.kyuubi.engine.EngineType.SPARK_SQL
|
||||
import org.apache.kyuubi.engine.ShareLevel.{CONNECTION, USER}
|
||||
import org.apache.kyuubi.events.KyuubiOperationEvent
|
||||
import org.apache.kyuubi.ha.HighAvailabilityConf
|
||||
import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
|
||||
import org.apache.kyuubi.ha.client.DiscoveryPaths
|
||||
@ -189,9 +188,9 @@ class AdminResourceSuite extends KyuubiFunSuite with RestFrontendTestHelper {
|
||||
.header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
|
||||
.get()
|
||||
assert(200 == response.getStatus)
|
||||
var operations = response.readEntity(new GenericType[Seq[KyuubiOperationEvent]]() {})
|
||||
var operations = response.readEntity(new GenericType[Seq[OperationData]]() {})
|
||||
assert(operations.nonEmpty)
|
||||
assert(operations.map(op => op.statementId).contains(operation.identifier.toString))
|
||||
assert(operations.map(op => op.getIdentifier).contains(operation.identifier.toString))
|
||||
|
||||
// close operation
|
||||
response = webTarget.path(s"api/v1/admin/operations/${operation.identifier}").request()
|
||||
@ -203,8 +202,8 @@ class AdminResourceSuite extends KyuubiFunSuite with RestFrontendTestHelper {
|
||||
response = webTarget.path("api/v1/admin/operations").request()
|
||||
.header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
|
||||
.get()
|
||||
operations = response.readEntity(new GenericType[Seq[KyuubiOperationEvent]]() {})
|
||||
assert(!operations.map(op => op.statementId).contains(operation.identifier.toString))
|
||||
operations = response.readEntity(new GenericType[Seq[OperationData]]() {})
|
||||
assert(!operations.map(op => op.getIdentifier).contains(operation.identifier.toString))
|
||||
}
|
||||
|
||||
test("delete engine - user share level") {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user