This is an automated email from the ASF dual-hosted git repository.
feiwang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 94177b0dc [KYUUBI #3410] Support to delete the engine node from engine
discovery space with AdminResource
94177b0dc is described below
commit 94177b0dcfa0a747bc2e17a43c91fcb88c9c2219
Author: Tianlin Liao <[email protected]>
AuthorDate: Fri Oct 14 13:50:17 2022 +0800
[KYUUBI #3410] Support to delete the engine node from engine discovery
space with AdminResource
### _Why are the changes needed?_
To close #3410
### _How was this patch tested?_
- [x] 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.apache.org/docs/latest/develop_tools/testing.html#running-tests)
locally before make a pull request
Closes #3609 from lightning-L/kyuubi-3410.
Closes #3410
83acd702 [Tianlin Liao] refactor
31773937 [Tianlin Liao] minor fix
d5b50c1b [Tianlin Liao] remove unnecessary parameters; get username from
authentication
590e993a [Tianlin Liao] Support to delete the engine node from engine
discovery space with AdminResource
Authored-by: Tianlin Liao <[email protected]>
Signed-off-by: Fei Wang <[email protected]>
---
.../kyuubi/server/api/v1/AdminResource.scala | 69 +++++++++++++++++++++-
.../kyuubi/server/api/v1/AdminResourceSuite.scala | 53 ++++++++++++++++-
2 files changed, 118 insertions(+), 4 deletions(-)
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 723d7b161..81cbfa667 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
@@ -17,16 +17,22 @@
package org.apache.kyuubi.server.api.v1
-import javax.ws.rs.{NotAllowedException, Path, POST, Produces}
+import javax.ws.rs._
import javax.ws.rs.core.{MediaType, Response}
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.tags.Tag
-import org.apache.kyuubi.{Logging, Utils}
+import org.apache.kyuubi.{KYUUBI_VERSION, Logging, Utils}
+import org.apache.kyuubi.config.KyuubiConf
+import org.apache.kyuubi.config.KyuubiConf._
+import org.apache.kyuubi.ha.HighAvailabilityConf.HA_NAMESPACE
+import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
+import org.apache.kyuubi.ha.client.DiscoveryPaths
import org.apache.kyuubi.server.KyuubiServer
import org.apache.kyuubi.server.api.ApiRequestContext
+import org.apache.kyuubi.service.authentication.KyuubiAuthenticationFactory
@Tag(name = "Admin")
@Produces(Array(MediaType.APPLICATION_JSON))
@@ -53,4 +59,63 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
KyuubiServer.reloadHadoopConf()
Response.ok(s"Refresh the hadoop conf for ${fe.connectionUrl}
successfully.").build()
}
+
+ @ApiResponse(
+ responseCode = "200",
+ content = Array(new Content(
+ mediaType = MediaType.APPLICATION_JSON)),
+ description = "delete kyuubi engine")
+ @DELETE
+ @Path("engine")
+ def deleteEngine(
+ @QueryParam("type") engineType: String,
+ @QueryParam("sharelevel") shareLevel: String,
+ @QueryParam("subdomain") subdomain: String,
+ @QueryParam("hive.server2.proxy.user") hs2ProxyUser: String): Response =
{
+ val sessionConf = Option(hs2ProxyUser).filter(_.nonEmpty).map(proxyUser =>
+ Map(KyuubiAuthenticationFactory.HS2_PROXY_USER ->
proxyUser)).getOrElse(Map())
+
+ var userName: String = null
+ try {
+ userName = fe.getUserName(sessionConf)
+ } catch {
+ case t: Throwable =>
+ throw new NotAllowedException(t.getMessage)
+ }
+
+ // use default value from kyuubi conf when param is not provided
+ val clonedConf: KyuubiConf = fe.getConf.clone
+ Option(engineType).foreach(clonedConf.set(ENGINE_TYPE, _))
+ Option(subdomain).filter(_.nonEmpty)
+ .foreach(_ => clonedConf.set(ENGINE_SHARE_LEVEL_SUBDOMAIN,
Option(subdomain)))
+
Option(shareLevel).filter(_.nonEmpty).foreach(clonedConf.set(ENGINE_SHARE_LEVEL,
_))
+
+ val normalizedEngineType = clonedConf.get(ENGINE_TYPE)
+ val engineSubdomain =
clonedConf.get(ENGINE_SHARE_LEVEL_SUBDOMAIN).getOrElse("default")
+ val engineShareLevel = clonedConf.get(ENGINE_SHARE_LEVEL)
+
+ val engineSpace = DiscoveryPaths.makePath(
+ s"${fe.getConf.get(HA_NAMESPACE)}_${KYUUBI_VERSION}_" +
+ s"${engineShareLevel}_$normalizedEngineType",
+ userName,
+ Array(engineSubdomain))
+
+ withDiscoveryClient(fe.getConf) { discoveryClient =>
+ val engineNodes = discoveryClient.getChildren(engineSpace)
+ engineNodes.foreach { node =>
+ val nodePath = s"$engineSpace/$node"
+ info(s"Deleting engine node:$nodePath")
+ try {
+ discoveryClient.delete(nodePath)
+ } catch {
+ case e: Exception =>
+ error(s"Failed to delete engine node:$nodePath", e)
+ throw new NotFoundException(s"Failed to delete engine
node:$nodePath," +
+ s"${e.getMessage}")
+ }
+ }
+ }
+
+ Response.ok(s"Engine $engineSpace is deleted successfully.").build()
+ }
}
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 6924c445a..5a2f8579f 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
@@ -17,9 +17,17 @@
package org.apache.kyuubi.server.api.v1
-import java.util.Base64
+import java.util.{Base64, UUID}
+import javax.ws.rs.core.MediaType
-import org.apache.kyuubi.{KyuubiFunSuite, RestFrontendTestHelper, Utils}
+import org.apache.kyuubi.{KYUUBI_VERSION, KyuubiFunSuite,
RestFrontendTestHelper, Utils}
+import org.apache.kyuubi.config.KyuubiConf
+import org.apache.kyuubi.engine.EngineRef
+import org.apache.kyuubi.engine.EngineType.SPARK_SQL
+import org.apache.kyuubi.engine.ShareLevel.USER
+import org.apache.kyuubi.ha.HighAvailabilityConf
+import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
+import org.apache.kyuubi.ha.client.DiscoveryPaths
import
org.apache.kyuubi.server.http.authentication.AuthenticationHandler.AUTHORIZATION_HEADER
class AdminResourceSuite extends KyuubiFunSuite with RestFrontendTestHelper {
@@ -40,4 +48,45 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
.post(null)
assert(200 == response.getStatus)
}
+
+ test("delete engine") {
+ val id = UUID.randomUUID().toString
+ conf.set(KyuubiConf.ENGINE_SHARE_LEVEL, USER.toString)
+ conf.set(KyuubiConf.ENGINE_TYPE, SPARK_SQL.toString)
+ conf.set(KyuubiConf.FRONTEND_THRIFT_BINARY_BIND_PORT, 0)
+ conf.set(HighAvailabilityConf.HA_NAMESPACE, "kyuubi_test")
+ conf.set(KyuubiConf.ENGINE_IDLE_TIMEOUT, 180000L)
+ val engine = new EngineRef(conf.clone, Utils.currentUser, id, null)
+
+ val engineSpace = DiscoveryPaths.makePath(
+ s"kyuubi_test_${KYUUBI_VERSION}_USER_SPARK_SQL",
+ Utils.currentUser,
+ Array("default"))
+
+ withDiscoveryClient(conf) { client =>
+ engine.getOrCreate(client)
+
+ assert(client.pathExists(engineSpace))
+ var child = client.getChildren(engineSpace)
+ assert(child.size == 1)
+
+ val adminUser = Utils.currentUser
+ val encodeAuthorization = new String(
+ Base64.getEncoder.encode(
+ s"$adminUser:".getBytes()),
+ "UTF-8")
+ val response = webTarget.path("api/v1/admin/engine")
+ .queryParam("sharelevel", "USER")
+ .queryParam("type", "spark_sql")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .delete()
+
+ assert(200 == response.getStatus)
+ assert(client.pathExists(engineSpace))
+ child = client.getChildren(engineSpace)
+ assert(child.size == 0)
+ }
+ }
+
}