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)
+    }
+  }
+
 }

Reply via email to