This is an automated email from the ASF dual-hosted git repository.
ulyssesyou 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 a4622301c [KYUUBI #2765][SUB-TASK][KPIP-4] Refactor current kyuubi-ctl
a4622301c is described below
commit a4622301c432702c44897b400c302d00c4549af8
Author: Tianlin Liao <[email protected]>
AuthorDate: Tue May 31 18:41:26 2022 +0800
[KYUUBI #2765][SUB-TASK][KPIP-4] Refactor current kyuubi-ctl
### _Why are the changes needed?_
To close #2765
### _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
- [ ] [Run
test](https://kyuubi.apache.org/docs/latest/develop_tools/testing.html#running-tests)
locally before make a pull request
Closes #2766 from lightning-L/kyuubi-2628.
Closes #2765
749eebd7 [Tianlin Liao] rename ServiceControlXXX to ControlXXX
842265fb [Tianlin Liao] [KYUUBI #2765] refactor current kyuubi-ctl
Authored-by: Tianlin Liao <[email protected]>
Signed-off-by: ulysses-you <[email protected]>
---
.../scala/org/apache/kyuubi/ctl/CliConfig.scala | 50 ++++
.../scala/org/apache/kyuubi/ctl/CommandLine.scala | 140 +++++++++
.../scala/org/apache/kyuubi/ctl/ControlCli.scala | 97 +++++++
.../apache/kyuubi/ctl/ControlCliArguments.scala | 97 +++++++
...arser.scala => ControlCliArgumentsParser.scala} | 24 +-
...liException.scala => ControlCliException.scala} | 2 +-
...rviceControlCliException.scala => Render.scala} | 18 +-
.../org/apache/kyuubi/ctl/ServiceControlCli.scala | 256 -----------------
.../kyuubi/ctl/ServiceControlCliArguments.scala | 312 ---------------------
.../scala/org/apache/kyuubi/ctl/cmd/Command.scala | 164 +++++++++++
.../org/apache/kyuubi/ctl/cmd/CreateCommand.scala | 94 +++++++
.../org/apache/kyuubi/ctl/cmd/DeleteCommand.scala | 64 +++++
.../org/apache/kyuubi/ctl/cmd/GetCommand.scala | 47 ++++
.../org/apache/kyuubi/ctl/cmd/ListCommand.scala | 53 ++++
...sSuite.scala => ControlCliArgumentsSuite.scala} | 62 ++--
...ControlCliSuite.scala => ControlCliSuite.scala} | 47 ++--
16 files changed, 883 insertions(+), 644 deletions(-)
diff --git a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/CliConfig.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/CliConfig.scala
new file mode 100644
index 000000000..1fc04b33b
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/CliConfig.scala
@@ -0,0 +1,50 @@
+/*
+ * 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.ctl
+
+import org.apache.kyuubi.ctl.ServiceControlAction.ControlAction
+import org.apache.kyuubi.ctl.ServiceControlObject.ControlObject
+
+private[ctl] object ServiceControlAction extends Enumeration {
+ type ControlAction = Value
+ val CREATE, GET, DELETE, LIST = Value
+}
+
+private[ctl] object ServiceControlObject extends Enumeration {
+ type ControlObject = Value
+ val SERVER, ENGINE = Value
+}
+
+case class CliConfig(
+ action: ControlAction = null,
+ service: ControlObject = ServiceControlObject.SERVER,
+ commonOpts: CommonOpts = CommonOpts(),
+ engineOpts: EngineOpts = EngineOpts())
+
+case class CommonOpts(
+ zkQuorum: String = null,
+ namespace: String = null,
+ host: String = null,
+ port: String = null,
+ version: String = null,
+ verbose: Boolean = false)
+
+case class EngineOpts(
+ user: String = null,
+ engineType: String = null,
+ engineSubdomain: String = null,
+ engineShareLevel: String = null)
diff --git a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/CommandLine.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/CommandLine.scala
new file mode 100644
index 000000000..903c6cd48
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/CommandLine.scala
@@ -0,0 +1,140 @@
+/*
+ * 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.ctl
+
+import scopt.{OParser, OParserBuilder}
+
+import org.apache.kyuubi.KYUUBI_VERSION
+
+object CommandLine {
+
+ def getOptionParser(builder: OParserBuilder[CliConfig]): OParser[Unit,
CliConfig] = {
+ import builder._
+ OParser.sequence(
+ programName("kyuubi-ctl"),
+ head("kyuubi", KYUUBI_VERSION),
+ common(builder),
+ create(builder),
+ get(builder),
+ delete(builder),
+ list(builder),
+ checkConfig(f => {
+ if (f.action == null) failure("Must specify action command:
[create|get|delete|list].")
+ else success
+ }),
+ note(""),
+ help('h', "help").text("Show help message and exit."))
+ }
+
+ private def common(builder: OParserBuilder[CliConfig]): OParser[_,
CliConfig] = {
+ import builder._
+ OParser.sequence(
+ opt[String]("zk-quorum").abbr("zk")
+ .action((v, c) => c.copy(commonOpts = c.commonOpts.copy(zkQuorum = v)))
+ .text("The connection string for the zookeeper ensemble," +
+ " using zk quorum manually."),
+ opt[String]('n', "namespace")
+ .action((v, c) => c.copy(commonOpts = c.commonOpts.copy(namespace =
v)))
+ .text("The namespace, using kyuubi-defaults/conf if absent."),
+ opt[String]('s', "host")
+ .action((v, c) => c.copy(commonOpts = c.commonOpts.copy(host = v)))
+ .text("Hostname or IP address of a service."),
+ opt[String]('p', "port")
+ .action((v, c) => c.copy(commonOpts = c.commonOpts.copy(port = v)))
+ .text("Listening port of a service."),
+ opt[String]('v', "version")
+ .action((v, c) => c.copy(commonOpts = c.commonOpts.copy(version = v)))
+ .text("Using the compiled KYUUBI_VERSION default," +
+ " change it if the active service is running in another."),
+ opt[Unit]('b', "verbose")
+ .action((_, c) => c.copy(commonOpts = c.commonOpts.copy(verbose =
true)))
+ .text("Print additional debug output."))
+ }
+
+ private def create(builder: OParserBuilder[CliConfig]): OParser[_,
CliConfig] = {
+ import builder._
+ OParser.sequence(
+ note(""),
+ cmd("create")
+ .action((_, c) => c.copy(action = ServiceControlAction.CREATE))
+ .children(
+ serverCmd(builder).text("\tExpose Kyuubi server instance to another
domain.")))
+ }
+
+ private def get(builder: OParserBuilder[CliConfig]): OParser[_, CliConfig] =
{
+ import builder._
+ OParser.sequence(
+ note(""),
+ cmd("get")
+ .text("\tGet the service/engine node info, host and port needed.")
+ .action((_, c) => c.copy(action = ServiceControlAction.GET))
+ .children(
+ serverCmd(builder).text("\tGet Kyuubi server info of domain"),
+ engineCmd(builder).text("\tGet Kyuubi engine info belong to a
user.")))
+
+ }
+
+ private def delete(builder: OParserBuilder[CliConfig]): OParser[_,
CliConfig] = {
+ import builder._
+ OParser.sequence(
+ note(""),
+ cmd("delete")
+ .text("\tDelete the specified service/engine node, host and port
needed.")
+ .action((_, c) => c.copy(action = ServiceControlAction.DELETE))
+ .children(
+ serverCmd(builder).text("\tDelete the specified service node for a
domain"),
+ engineCmd(builder).text("\tDelete the specified engine node for
user.")))
+
+ }
+
+ private def list(builder: OParserBuilder[CliConfig]): OParser[_, CliConfig]
= {
+ import builder._
+ OParser.sequence(
+ note(""),
+ cmd("list")
+ .text("\tList all the service/engine nodes for a particular domain.")
+ .action((_, c) => c.copy(action = ServiceControlAction.LIST))
+ .children(
+ serverCmd(builder).text("\tList all the service nodes for a
particular domain"),
+ engineCmd(builder).text("\tList all the engine nodes for a user")))
+
+ }
+
+ private def serverCmd(builder: OParserBuilder[CliConfig]): OParser[_,
CliConfig] = {
+ import builder._
+ cmd("server").action((_, c) => c.copy(service =
ServiceControlObject.SERVER))
+ }
+
+ private def engineCmd(builder: OParserBuilder[CliConfig]): OParser[_,
CliConfig] = {
+ import builder._
+ cmd("engine").action((_, c) => c.copy(service =
ServiceControlObject.ENGINE))
+ .children(
+ opt[String]('u', "user")
+ .action((v, c) => c.copy(engineOpts = c.engineOpts.copy(user = v)))
+ .text("The user name this engine belong to."),
+ opt[String]("engine-type").abbr("et")
+ .action((v, c) => c.copy(engineOpts = c.engineOpts.copy(engineType =
v)))
+ .text("The engine type this engine belong to."),
+ opt[String]("engine-subdomain").abbr("es")
+ .action((v, c) => c.copy(engineOpts =
c.engineOpts.copy(engineSubdomain = v)))
+ .text("The engine subdomain this engine belong to."),
+ opt[String]("engine-share-level").abbr("esl")
+ .action((v, c) => c.copy(engineOpts =
c.engineOpts.copy(engineShareLevel = v)))
+ .text("The engine share level this engine belong to."))
+ }
+
+}
diff --git a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCli.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCli.scala
new file mode 100644
index 000000000..ab87568b4
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCli.scala
@@ -0,0 +1,97 @@
+/*
+ * 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.ctl
+
+import org.apache.kyuubi.Logging
+
+/**
+ * Main gateway of launching a Kyuubi Ctl action.
+ */
+private[kyuubi] class ControlCli extends Logging {
+
+ def doAction(args: Array[String]): Unit = {
+ // Initialize logging if it hasn't been done yet.
+ // Set log level ERROR
+ initializeLoggerIfNecessary(true)
+
+ val ctlArgs = parseArguments(args)
+
+ // when parse failed, exit
+ if (ctlArgs.cliArgs == null) {
+ sys.exit(1)
+ }
+
+ val verbose = ctlArgs.cliArgs.commonOpts.verbose
+ if (verbose) {
+ super.info(ctlArgs.toString)
+ }
+
+ ctlArgs.command.run()
+ }
+
+ protected def parseArguments(args: Array[String]): ControlCliArguments = {
+ new ControlCliArguments(args)
+ }
+
+}
+
+object ControlCli extends CommandLineUtils with Logging {
+ override def main(args: Array[String]): Unit = {
+ val ctl = new ControlCli() {
+ self =>
+ override protected def parseArguments(args: Array[String]):
ControlCliArguments = {
+ new ControlCliArguments(args) {
+ override def info(msg: => Any): Unit = self.info(msg)
+
+ override def warn(msg: => Any): Unit = self.warn(msg)
+
+ override def error(msg: => Any): Unit = self.error(msg)
+
+ override private[kyuubi] lazy val effectSetup = new
KyuubiOEffectSetup {
+ override def displayToOut(msg: String): Unit = self.info(msg)
+
+ override def displayToErr(msg: String): Unit = self.info(msg)
+
+ override def reportError(msg: String): Unit = self.info(msg)
+
+ override def reportWarning(msg: String): Unit = self.warn(msg)
+ }
+ }
+ }
+
+ override def info(msg: => Any): Unit = printMessage(msg)
+
+ override def warn(msg: => Any): Unit = printMessage(s"Warning: $msg")
+
+ override def error(msg: => Any): Unit = printMessage(s"Error: $msg")
+
+ override def doAction(args: Array[String]): Unit = {
+ try {
+ super.doAction(args)
+ exitFn(0)
+ } catch {
+ case e: ControlCliException =>
+ exitFn(e.exitCode)
+ }
+ }
+ }
+
+ ctl.doAction(args)
+ }
+
+}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliArguments.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliArguments.scala
new file mode 100644
index 000000000..29d4724d5
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliArguments.scala
@@ -0,0 +1,97 @@
+/*
+ * 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.ctl
+
+import scopt.OParser
+
+import org.apache.kyuubi.Logging
+import org.apache.kyuubi.ctl.cmd._
+
+class ControlCliArguments(args: Seq[String], env: Map[String, String] =
sys.env)
+ extends ControlCliArgumentsParser with Logging {
+
+ var cliArgs: CliConfig = null
+
+ var command: Command = null
+
+ // Set parameters from command line arguments
+ parse(args)
+
+ lazy val cliParser = parser()
+
+ override def parser(): OParser[Unit, CliConfig] = {
+ val builder = OParser.builder[CliConfig]
+ CommandLine.getOptionParser(builder)
+ }
+
+ private[kyuubi] lazy val effectSetup = new KyuubiOEffectSetup
+
+ override def parse(args: Seq[String]): Unit = {
+ OParser.runParser(cliParser, args, CliConfig()) match {
+ case (result, effects) =>
+ OParser.runEffects(effects, effectSetup)
+ result match {
+ case Some(arguments) =>
+ command = getCommand(arguments)
+ command.preProcess()
+ cliArgs = command.cliArgs
+ case _ =>
+ // arguments are bad, exit
+ }
+ }
+ }
+
+ private def getCommand(cliArgs: CliConfig): Command = {
+ cliArgs.action match {
+ case ServiceControlAction.CREATE => new CreateCommand(cliArgs)
+ case ServiceControlAction.GET => new GetCommand(cliArgs)
+ case ServiceControlAction.DELETE => new DeleteCommand(cliArgs)
+ case ServiceControlAction.LIST => new ListCommand(cliArgs)
+ case _ => null
+ }
+ }
+
+ override def toString: String = {
+ cliArgs.service match {
+ case ServiceControlObject.SERVER =>
+ s"""Parsed arguments:
+ | action ${cliArgs.action}
+ | service ${cliArgs.service}
+ | zkQuorum ${cliArgs.commonOpts.zkQuorum}
+ | namespace ${cliArgs.commonOpts.namespace}
+ | host ${cliArgs.commonOpts.host}
+ | port ${cliArgs.commonOpts.port}
+ | version ${cliArgs.commonOpts.version}
+ | verbose ${cliArgs.commonOpts.verbose}
+ """.stripMargin
+ case ServiceControlObject.ENGINE =>
+ s"""Parsed arguments:
+ | action ${cliArgs.action}
+ | service ${cliArgs.service}
+ | zkQuorum ${cliArgs.commonOpts.zkQuorum}
+ | namespace ${cliArgs.commonOpts.namespace}
+ | user ${cliArgs.engineOpts.user}
+ | host ${cliArgs.commonOpts.host}
+ | port ${cliArgs.commonOpts.port}
+ | version ${cliArgs.commonOpts.version}
+ | verbose ${cliArgs.commonOpts.verbose}
+ """.stripMargin
+ case _ => ""
+ }
+ }
+}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliArgumentsParser.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliArgumentsParser.scala
similarity index 58%
rename from
kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliArgumentsParser.scala
rename to
kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliArgumentsParser.scala
index 9c031e0cd..4a6707f39 100644
---
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliArgumentsParser.scala
+++
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliArgumentsParser.scala
@@ -19,32 +19,12 @@ package org.apache.kyuubi.ctl
import scopt.OParser
-import org.apache.kyuubi.ctl.ServiceControlAction.ServiceControlAction
-import org.apache.kyuubi.ctl.ServiceControlObject.ServiceControlObject
-
-abstract private[kyuubi] class ServiceControlCliArgumentsParser {
-
- /**
- * Description of available options
- */
- case class CliArguments(
- action: ServiceControlAction = null,
- service: ServiceControlObject = ServiceControlObject.SERVER,
- zkQuorum: String = null,
- namespace: String = null,
- user: String = null,
- host: String = null,
- port: String = null,
- version: String = null,
- verbose: Boolean = false,
- engineType: String = null,
- engineSubdomain: String = null,
- engineShareLevel: String = null)
+abstract private[kyuubi] class ControlCliArgumentsParser {
/**
* Cli arguments parse rules.
*/
- def parser(): OParser[Unit, CliArguments]
+ def parser(): OParser[Unit, CliConfig]
/**
* Parse a list of kyuubi-ctl command line options.
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliException.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliException.scala
similarity index 93%
copy from
kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliException.scala
copy to
kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliException.scala
index b38a7f754..86414f5c0 100644
---
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliException.scala
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ControlCliException.scala
@@ -19,5 +19,5 @@ package org.apache.kyuubi.ctl
import org.apache.kyuubi.KyuubiException
-private[kyuubi] case class ServiceControlCliException(exitCode: Int)
+private[kyuubi] case class ControlCliException(exitCode: Int)
extends KyuubiException(s"Kyuubi service control cli exited with $exitCode")
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliException.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/Render.scala
similarity index 63%
rename from
kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliException.scala
rename to kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/Render.scala
index b38a7f754..39dd618d3 100644
---
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliException.scala
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/Render.scala
@@ -14,10 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.kyuubi.ctl
-import org.apache.kyuubi.KyuubiException
+import org.apache.kyuubi.ha.client.ServiceNodeInfo
+
+object Render {
-private[kyuubi] case class ServiceControlCliException(exitCode: Int)
- extends KyuubiException(s"Kyuubi service control cli exited with $exitCode")
+ private[ctl] def renderServiceNodesInfo(
+ title: String,
+ serviceNodeInfo: Seq[ServiceNodeInfo],
+ verbose: Boolean): String = {
+ val header = Seq("Namespace", "Host", "Port", "Version")
+ val rows = serviceNodeInfo.sortBy(_.nodeName).map { sn =>
+ Seq(sn.namespace, sn.host, sn.port.toString, sn.version.getOrElse(""))
+ }
+ Tabulator.format(title, header, rows, verbose)
+ }
+}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCli.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCli.scala
deleted file mode 100644
index 0c8010740..000000000
--- a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCli.scala
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * 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.ctl
-
-import scala.collection.mutable.ListBuffer
-
-import org.apache.kyuubi.Logging
-import org.apache.kyuubi.config.KyuubiConf.{ENGINE_SHARE_LEVEL,
ENGINE_SHARE_LEVEL_SUBDOMAIN, ENGINE_TYPE}
-import org.apache.kyuubi.ha.HighAvailabilityConf._
-import org.apache.kyuubi.ha.client.{DiscoveryClientProvider, ServiceNodeInfo}
-import org.apache.kyuubi.ha.client.DiscoveryClient
-import org.apache.kyuubi.ha.client.DiscoveryPaths
-
-private[ctl] object ServiceControlAction extends Enumeration {
- type ServiceControlAction = Value
- val CREATE, GET, DELETE, LIST = Value
-}
-
-private[ctl] object ServiceControlObject extends Enumeration {
- type ServiceControlObject = Value
- val SERVER, ENGINE = Value
-}
-
-/**
- * Main gateway of launching a Kyuubi Ctl action.
- */
-private[kyuubi] class ServiceControlCli extends Logging {
- import DiscoveryClientProvider._
- import ServiceControlCli._
-
- private var verbose: Boolean = false
-
- def doAction(args: Array[String]): Unit = {
- // Initialize logging if it hasn't been done yet.
- // Set log level ERROR
- initializeLoggerIfNecessary(true)
-
- val ctlArgs = parseArguments(args)
-
- // when parse failed, exit
- if (ctlArgs.cliArgs == null) {
- sys.exit(1)
- }
-
- verbose = ctlArgs.cliArgs.verbose
- if (verbose) {
- super.info(ctlArgs.toString)
- }
- ctlArgs.cliArgs.action match {
- case ServiceControlAction.CREATE => create(ctlArgs)
- case ServiceControlAction.LIST => list(ctlArgs, filterHostPort = false)
- case ServiceControlAction.GET => list(ctlArgs, filterHostPort = true)
- case ServiceControlAction.DELETE => delete(ctlArgs)
- }
- }
-
- protected def parseArguments(args: Array[String]):
ServiceControlCliArguments = {
- new ServiceControlCliArguments(args)
- }
-
- /**
- * Expose Kyuubi server instance to another domain.
- */
- private def create(args: ServiceControlCliArguments): Unit = {
- val kyuubiConf = args.conf
-
- kyuubiConf.setIfMissing(HA_ADDRESSES, args.cliArgs.zkQuorum)
- withDiscoveryClient(kyuubiConf) { discoveryClient =>
- val fromNamespace =
- DiscoveryPaths.makePath(null, kyuubiConf.get(HA_NAMESPACE))
- val toNamespace = getZkNamespace(args)
-
- val currentServerNodes =
discoveryClient.getServiceNodesInfo(fromNamespace)
- val exposedServiceNodes = ListBuffer[ServiceNodeInfo]()
-
- if (currentServerNodes.nonEmpty) {
- def doCreate(zc: DiscoveryClient): Unit = {
- currentServerNodes.foreach { sn =>
- info(s"Exposing server instance:${sn.instance} with
version:${sn.version}" +
- s" from $fromNamespace to $toNamespace")
- val newNodePath = zc.createAndGetServiceNode(
- kyuubiConf,
- args.cliArgs.namespace,
- sn.instance,
- sn.version,
- true)
- exposedServiceNodes += sn.copy(
- namespace = toNamespace,
- nodeName = newNodePath.split("/").last)
- }
- }
-
- if (kyuubiConf.get(HA_ADDRESSES) == args.cliArgs.zkQuorum) {
- doCreate(discoveryClient)
- } else {
- kyuubiConf.set(HA_ADDRESSES, args.cliArgs.zkQuorum)
- withDiscoveryClient(kyuubiConf)(doCreate)
- }
- }
-
- val title = "Created zookeeper service nodes"
- info(renderServiceNodesInfo(title, exposedServiceNodes, verbose))
- }
- }
-
- /**
- * List Kyuubi server nodes info.
- */
- private def list(args: ServiceControlCliArguments, filterHostPort: Boolean):
Unit = {
- withDiscoveryClient(args.conf) { discoveryClient =>
- val znodeRoot = getZkNamespace(args)
- val hostPortOpt =
- if (filterHostPort) {
- Some((args.cliArgs.host, args.cliArgs.port.toInt))
- } else None
- val nodes = getServiceNodes(discoveryClient, znodeRoot, hostPortOpt)
-
- val title = "Zookeeper service nodes"
- info(renderServiceNodesInfo(title, nodes, verbose))
- }
- }
-
- private def getServiceNodes(
- discoveryClient: DiscoveryClient,
- znodeRoot: String,
- hostPortOpt: Option[(String, Int)]): Seq[ServiceNodeInfo] = {
- val serviceNodes = discoveryClient.getServiceNodesInfo(znodeRoot)
- hostPortOpt match {
- case Some((host, port)) => serviceNodes.filter { sn =>
- sn.host == host && sn.port == port
- }
- case _ => serviceNodes
- }
- }
-
- /**
- * Delete zookeeper service node with specified host port.
- */
- private def delete(args: ServiceControlCliArguments): Unit = {
- withDiscoveryClient(args.conf) { discoveryClient =>
- val znodeRoot = getZkNamespace(args)
- val hostPortOpt = Some((args.cliArgs.host, args.cliArgs.port.toInt))
- val nodesToDelete = getServiceNodes(discoveryClient, znodeRoot,
hostPortOpt)
-
- val deletedNodes = ListBuffer[ServiceNodeInfo]()
- nodesToDelete.foreach { node =>
- val nodePath = s"$znodeRoot/${node.nodeName}"
- info(s"Deleting zookeeper service node:$nodePath")
- try {
- discoveryClient.delete(nodePath)
- deletedNodes += node
- } catch {
- case e: Exception =>
- error(s"Failed to delete zookeeper service node:$nodePath", e)
- }
- }
-
- val title = "Deleted zookeeper service nodes"
- info(renderServiceNodesInfo(title, deletedNodes, verbose))
- }
- }
-}
-
-object ServiceControlCli extends CommandLineUtils with Logging {
- override def main(args: Array[String]): Unit = {
- val ctl = new ServiceControlCli() {
- self =>
- override protected def parseArguments(args: Array[String]):
ServiceControlCliArguments = {
- new ServiceControlCliArguments(args) {
- override def info(msg: => Any): Unit = self.info(msg)
-
- override def warn(msg: => Any): Unit = self.warn(msg)
-
- override def error(msg: => Any): Unit = self.error(msg)
-
- override private[kyuubi] lazy val effectSetup = new
KyuubiOEffectSetup {
- override def displayToOut(msg: String): Unit = self.info(msg)
-
- override def displayToErr(msg: String): Unit = self.info(msg)
-
- override def reportError(msg: String): Unit = self.info(msg)
-
- override def reportWarning(msg: String): Unit = self.warn(msg)
- }
- }
- }
-
- override def info(msg: => Any): Unit = printMessage(msg)
-
- override def warn(msg: => Any): Unit = printMessage(s"Warning: $msg")
-
- override def error(msg: => Any): Unit = printMessage(s"Error: $msg")
-
- override def doAction(args: Array[String]): Unit = {
- try {
- super.doAction(args)
- exitFn(0)
- } catch {
- case e: ServiceControlCliException =>
- exitFn(e.exitCode)
- }
- }
- }
-
- ctl.doAction(args)
- }
-
- private[ctl] def getZkNamespace(args: ServiceControlCliArguments): String = {
- args.cliArgs.service match {
- case ServiceControlObject.SERVER =>
- DiscoveryPaths.makePath(null, args.cliArgs.namespace)
- case ServiceControlObject.ENGINE =>
- val engineType = Some(args.cliArgs.engineType)
- .filter(_ != null).filter(_.nonEmpty)
- .getOrElse(args.conf.get(ENGINE_TYPE))
- val engineSubdomain = Some(args.cliArgs.engineSubdomain)
- .filter(_ != null).filter(_.nonEmpty)
-
.getOrElse(args.conf.get(ENGINE_SHARE_LEVEL_SUBDOMAIN).getOrElse("default"))
- val engineShareLevel = Some(args.cliArgs.engineShareLevel)
- .filter(_ != null).filter(_.nonEmpty)
- .getOrElse(args.conf.get(ENGINE_SHARE_LEVEL))
- // The path of the engine defined in zookeeper comes from
- // org.apache.kyuubi.engine.EngineRef#engineSpace
- DiscoveryPaths.makePath(
-
s"${args.cliArgs.namespace}_${args.cliArgs.version}_${engineShareLevel}_${engineType}",
- args.cliArgs.user,
- Array(engineSubdomain))
- }
- }
-
- private[ctl] def renderServiceNodesInfo(
- title: String,
- serviceNodeInfo: Seq[ServiceNodeInfo],
- verbose: Boolean): String = {
- val header = Seq("Namespace", "Host", "Port", "Version")
- val rows = serviceNodeInfo.sortBy(_.nodeName).map { sn =>
- Seq(sn.namespace, sn.host, sn.port.toString, sn.version.getOrElse(""))
- }
- Tabulator.format(title, header, rows, verbose)
- }
-}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliArguments.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliArguments.scala
deleted file mode 100644
index efa22cb58..000000000
---
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/ServiceControlCliArguments.scala
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * 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.ctl
-
-import java.net.InetAddress
-
-import scopt.OParser
-
-import org.apache.kyuubi.{KYUUBI_VERSION, KyuubiException, Logging}
-import org.apache.kyuubi.config.KyuubiConf
-import org.apache.kyuubi.ha.HighAvailabilityConf._
-
-class ServiceControlCliArguments(args: Seq[String], env: Map[String, String] =
sys.env)
- extends ServiceControlCliArgumentsParser with Logging {
-
- var cliArgs: CliArguments = null
-
- val conf = KyuubiConf().loadFileDefaults()
-
- // Set parameters from command line arguments
- parse(args)
-
- lazy val cliParser = parser()
-
- override def parser(): OParser[Unit, CliArguments] = {
- val builder = OParser.builder[CliArguments]
- import builder._
-
- // Options after action and service
- val ops = OParser.sequence(
- opt[String]("zk-quorum").abbr("zk")
- .action((v, c) => c.copy(zkQuorum = v))
- .text("The connection string for the zookeeper ensemble," +
- " using zk quorum manually."),
- opt[String]('n', "namespace")
- .action((v, c) => c.copy(namespace = v))
- .text("The namespace, using kyuubi-defaults/conf if absent."),
- opt[String]('s', "host")
- .action((v, c) => c.copy(host = v))
- .text("Hostname or IP address of a service."),
- opt[String]('p', "port")
- .action((v, c) => c.copy(port = v))
- .text("Listening port of a service."),
- opt[String]('v', "version")
- .action((v, c) => c.copy(version = v))
- .text("Using the compiled KYUUBI_VERSION default," +
- " change it if the active service is running in another."),
- opt[Unit]('b', "verbose")
- .action((_, c) => c.copy(verbose = true))
- .text("Print additional debug output."))
-
- // for engine service only
- val userOps = opt[String]('u', "user")
- .action((v, c) => c.copy(user = v))
- .text("The user name this engine belong to.")
-
- val engineTypeOps = opt[String]("engine-type").abbr("et")
- .action((v, c) => c.copy(engineType = v))
- .text("The engine type this engine belong to.")
-
- val engineSubdomainOps = opt[String]("engine-subdomain").abbr("es")
- .action((v, c) => c.copy(engineSubdomain = v))
- .text("The engine subdomain this engine belong to.")
-
- val engineShareLevelOps = opt[String]("engine-share-level").abbr("esl")
- .action((v, c) => c.copy(engineShareLevel = v))
- .text("The engine share level this engine belong to.")
-
- val serverCmd =
- cmd("server").action((_, c) => c.copy(service =
ServiceControlObject.SERVER))
- val engineCmd =
- cmd("engine").action((_, c) => c.copy(service =
ServiceControlObject.ENGINE))
-
- val CtlParser = {
- OParser.sequence(
- programName("kyuubi-ctl"),
- head("kyuubi", KYUUBI_VERSION),
- ops,
- note(""),
- cmd("create")
- .action((_, c) => c.copy(action = ServiceControlAction.CREATE))
- .children(
- serverCmd.text("\tExpose Kyuubi server instance to another
domain.")),
- note(""),
- cmd("get")
- .action((_, c) => c.copy(action = ServiceControlAction.GET))
- .text("\tGet the service/engine node info, host and port needed.")
- .children(
- serverCmd.text("\tGet Kyuubi server info of domain"),
- engineCmd
- .children(userOps)
- .children(engineTypeOps)
- .children(engineSubdomainOps)
- .children(engineShareLevelOps)
- .text("\tGet Kyuubi engine info belong to a user.")),
- note(""),
- cmd("delete")
- .action((_, c) => c.copy(action = ServiceControlAction.DELETE))
- .text("\tDelete the specified service/engine node, host and port
needed.")
- .children(
- serverCmd.text("\tDelete the specified service node for a domain"),
- engineCmd
- .children(userOps)
- .children(engineTypeOps)
- .children(engineSubdomainOps)
- .children(engineShareLevelOps)
- .text("\tDelete the specified engine node for user.")),
- note(""),
- cmd("list")
- .action((_, c) => c.copy(action = ServiceControlAction.LIST))
- .text("\tList all the service/engine nodes for a particular domain.")
- .children(
- serverCmd.text("\tList all the service nodes for a particular
domain"),
- engineCmd
- .children(userOps)
- .children(engineTypeOps)
- .children(engineSubdomainOps)
- .children(engineShareLevelOps)
- .text("\tList all the engine nodes for a user")),
- checkConfig(f => {
- if (f.action == null) failure("Must specify action command:
[create|get|delete|list].")
- else success
- }),
- note(""),
- help('h', "help").text("Show help message and exit."))
- }
- CtlParser
- }
-
- private[kyuubi] lazy val effectSetup = new KyuubiOEffectSetup
-
- override def parse(args: Seq[String]): Unit = {
- OParser.runParser(cliParser, args, CliArguments()) match {
- case (result, effects) =>
- OParser.runEffects(effects, effectSetup)
- result match {
- case Some(arguments) =>
- // use default property value if not set
- cliArgs = useDefaultPropertyValueIfMissing(arguments).copy()
-
- // validate arguments
- validateArguments()
- case _ =>
- // arguments are bad, exit
- }
- }
- }
-
- private def useDefaultPropertyValueIfMissing(value: CliArguments):
CliArguments = {
- var arguments: CliArguments = value.copy()
- if (value.zkQuorum == null) {
- conf.getOption(HA_ADDRESSES.key).foreach { v =>
- if (arguments.verbose) {
- super.info(s"Zookeeper quorum is not specified, use value from
default conf:$v")
- }
- arguments = arguments.copy(zkQuorum = v)
- }
- }
-
- if (arguments.namespace == null) {
- arguments = arguments.copy(namespace = conf.get(HA_NAMESPACE))
- if (arguments.verbose) {
- super.info(s"Zookeeper namespace is not specified, use value from
default conf:" +
- s"${arguments.namespace}")
- }
- }
-
- if (arguments.version == null) {
- if (arguments.verbose) {
- super.info(s"version is not specified, use built-in
KYUUBI_VERSION:$KYUUBI_VERSION")
- }
- arguments = arguments.copy(version = KYUUBI_VERSION)
- }
- arguments
- }
-
- /** Ensure that required fields exists. Call this only once all defaults are
loaded. */
- private def validateArguments(): Unit = {
- cliArgs.action match {
- case ServiceControlAction.CREATE => validateCreateActionArguments()
- case ServiceControlAction.GET => validateGetDeleteActionArguments()
- case ServiceControlAction.DELETE => validateGetDeleteActionArguments()
- case ServiceControlAction.LIST => validateListActionArguments()
- case _ => // do nothing
- }
- }
-
- private def validateCreateActionArguments(): Unit = {
- if (cliArgs.service != ServiceControlObject.SERVER) {
- fail("Only support expose Kyuubi server instance to another domain")
- }
- validateZkArguments()
-
- val defaultNamespace = conf.getOption(HA_NAMESPACE.key)
- .getOrElse(HA_NAMESPACE.defaultValStr)
- if (defaultNamespace.equals(cliArgs.namespace)) {
- fail(
- s"""
- |Only support expose Kyuubi server instance to another domain, a
different namespace
- |than the default namespace [$defaultNamespace] should be specified.
- """.stripMargin)
- }
- }
-
- private def validateGetDeleteActionArguments(): Unit = {
- validateZkArguments()
- validateHostAndPort()
- validateUser()
- mergeArgsIntoKyuubiConf()
- }
-
- private def validateListActionArguments(): Unit = {
- validateZkArguments()
- cliArgs.service match {
- case ServiceControlObject.ENGINE => validateUser()
- case _ =>
- }
- mergeArgsIntoKyuubiConf()
- }
-
- private def mergeArgsIntoKyuubiConf(): Unit = {
- conf.set(HA_ADDRESSES.key, cliArgs.zkQuorum)
- conf.set(HA_NAMESPACE.key, cliArgs.namespace)
- }
-
- private def validateZkArguments(): Unit = {
- if (cliArgs.zkQuorum == null) {
- fail("Zookeeper quorum is not specified and no default value to load")
- }
- if (cliArgs.namespace == null) {
- fail("Zookeeper namespace is not specified and no default value to load")
- }
- }
-
- private def validateHostAndPort(): Unit = {
- if (cliArgs.host == null) {
- fail("Must specify host for service")
- }
- if (cliArgs.port == null) {
- fail("Must specify port for service")
- }
-
- try {
- InetAddress.getByName(cliArgs.host)
- cliArgs = cliArgs.copy(host = cliArgs.host)
- } catch {
- case _: Exception =>
- fail(s"Unknown host: ${cliArgs.host}")
- }
-
- try {
- if (cliArgs.port.toInt <= 0) {
- fail(s"Specified port should be a positive number")
- }
- } catch {
- case _: NumberFormatException =>
- fail(s"Specified port is not a valid integer number: ${cliArgs.port}")
- }
- }
-
- private def validateUser(): Unit = {
- if (cliArgs.service == ServiceControlObject.ENGINE && cliArgs.user ==
null) {
- fail("Must specify user name for engine, please use -u or --user.")
- }
- }
-
- override def toString: String = {
- cliArgs.service match {
- case ServiceControlObject.SERVER =>
- s"""Parsed arguments:
- | action ${cliArgs.action}
- | service ${cliArgs.service}
- | zkQuorum ${cliArgs.zkQuorum}
- | namespace ${cliArgs.namespace}
- | host ${cliArgs.host}
- | port ${cliArgs.port}
- | version ${cliArgs.version}
- | verbose ${cliArgs.verbose}
- """.stripMargin
- case ServiceControlObject.ENGINE =>
- s"""Parsed arguments:
- | action ${cliArgs.action}
- | service ${cliArgs.service}
- | zkQuorum ${cliArgs.zkQuorum}
- | namespace ${cliArgs.namespace}
- | user ${cliArgs.user}
- | host ${cliArgs.host}
- | port ${cliArgs.port}
- | version ${cliArgs.version}
- | verbose ${cliArgs.verbose}
- """.stripMargin
- case _ => ""
- }
- }
-
- private def fail(msg: String): Unit = throw new KyuubiException(msg)
-}
diff --git a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/Command.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/Command.scala
new file mode 100644
index 000000000..0850ff400
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/Command.scala
@@ -0,0 +1,164 @@
+/*
+ * 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.ctl.cmd
+
+import java.net.InetAddress
+
+import org.apache.kyuubi.{KYUUBI_VERSION, KyuubiException, Logging}
+import org.apache.kyuubi.config.KyuubiConf
+import org.apache.kyuubi.config.KyuubiConf.{ENGINE_SHARE_LEVEL,
ENGINE_SHARE_LEVEL_SUBDOMAIN, ENGINE_TYPE}
+import org.apache.kyuubi.ctl.{CliConfig, ServiceControlObject}
+import org.apache.kyuubi.ctl.ControlCli.printMessage
+import org.apache.kyuubi.ha.HighAvailabilityConf._
+import org.apache.kyuubi.ha.client.{DiscoveryClient, DiscoveryPaths,
ServiceNodeInfo}
+
+abstract class Command(var cliArgs: CliConfig) extends Logging {
+
+ val conf = KyuubiConf().loadFileDefaults()
+
+ val verbose = cliArgs.commonOpts.verbose
+
+ def preProcess(): Unit = {
+ this.cliArgs = useDefaultPropertyValueIfMissing()
+ validateArguments()
+ }
+
+ /** Ensure that required fields exists. Call this only once all defaults are
loaded. */
+ def validateArguments(): Unit
+
+ def run(): Unit
+
+ def fail(msg: String): Unit = throw new KyuubiException(msg)
+
+ protected def validateZkArguments(): Unit = {
+ if (cliArgs.commonOpts.zkQuorum == null) {
+ fail("Zookeeper quorum is not specified and no default value to load")
+ }
+ if (cliArgs.commonOpts.namespace == null) {
+ fail("Zookeeper namespace is not specified and no default value to load")
+ }
+ }
+
+ protected def validateHostAndPort(): Unit = {
+ if (cliArgs.commonOpts.host == null) {
+ fail("Must specify host for service")
+ }
+ if (cliArgs.commonOpts.port == null) {
+ fail("Must specify port for service")
+ }
+
+ try {
+ InetAddress.getByName(cliArgs.commonOpts.host)
+ } catch {
+ case _: Exception =>
+ fail(s"Unknown host: ${cliArgs.commonOpts.host}")
+ }
+
+ try {
+ if (cliArgs.commonOpts.port.toInt <= 0) {
+ fail(s"Specified port should be a positive number")
+ }
+ } catch {
+ case _: NumberFormatException =>
+ fail(s"Specified port is not a valid integer number:
${cliArgs.commonOpts.port}")
+ }
+ }
+
+ protected def validateUser(): Unit = {
+ if (cliArgs.service == ServiceControlObject.ENGINE &&
cliArgs.engineOpts.user == null) {
+ fail("Must specify user name for engine, please use -u or --user.")
+ }
+ }
+
+ protected def mergeArgsIntoKyuubiConf(): Unit = {
+ conf.set(HA_ADDRESSES.key, cliArgs.commonOpts.zkQuorum)
+ conf.set(HA_NAMESPACE.key, cliArgs.commonOpts.namespace)
+ }
+
+ private def useDefaultPropertyValueIfMissing(): CliConfig = {
+ var arguments: CliConfig = cliArgs.copy()
+ if (cliArgs.commonOpts.zkQuorum == null) {
+ conf.getOption(HA_ADDRESSES.key).foreach { v =>
+ if (verbose) {
+ super.info(s"Zookeeper quorum is not specified, use value from
default conf:$v")
+ }
+ arguments = arguments.copy(commonOpts =
arguments.commonOpts.copy(zkQuorum = v))
+ }
+ }
+
+ if (arguments.commonOpts.namespace == null) {
+ arguments = arguments.copy(commonOpts =
+ arguments.commonOpts.copy(namespace = conf.get(HA_NAMESPACE)))
+ if (verbose) {
+ super.info(s"Zookeeper namespace is not specified, use value from
default conf:" +
+ s"${arguments.commonOpts.namespace}")
+ }
+ }
+
+ if (arguments.commonOpts.version == null) {
+ if (verbose) {
+ super.info(s"version is not specified, use built-in
KYUUBI_VERSION:$KYUUBI_VERSION")
+ }
+ arguments = arguments.copy(commonOpts =
arguments.commonOpts.copy(version = KYUUBI_VERSION))
+ }
+ arguments
+ }
+
+ private[ctl] def getZkNamespace(): String = {
+ cliArgs.service match {
+ case ServiceControlObject.SERVER =>
+ DiscoveryPaths.makePath(null, cliArgs.commonOpts.namespace)
+ case ServiceControlObject.ENGINE =>
+ val engineType = Some(cliArgs.engineOpts.engineType)
+ .filter(_ != null).filter(_.nonEmpty)
+ .getOrElse(conf.get(ENGINE_TYPE))
+ val engineSubdomain = Some(cliArgs.engineOpts.engineSubdomain)
+ .filter(_ != null).filter(_.nonEmpty)
+
.getOrElse(conf.get(ENGINE_SHARE_LEVEL_SUBDOMAIN).getOrElse("default"))
+ val engineShareLevel = Some(cliArgs.engineOpts.engineShareLevel)
+ .filter(_ != null).filter(_.nonEmpty)
+ .getOrElse(conf.get(ENGINE_SHARE_LEVEL))
+ // The path of the engine defined in zookeeper comes from
+ // org.apache.kyuubi.engine.EngineRef#engineSpace
+ DiscoveryPaths.makePath(
+ s"${cliArgs.commonOpts.namespace}_${cliArgs.commonOpts.version}_" +
+ s"${engineShareLevel}_${engineType}",
+ cliArgs.engineOpts.user,
+ Array(engineSubdomain))
+ }
+ }
+
+ private[ctl] def getServiceNodes(
+ discoveryClient: DiscoveryClient,
+ znodeRoot: String,
+ hostPortOpt: Option[(String, Int)]): Seq[ServiceNodeInfo] = {
+ val serviceNodes = discoveryClient.getServiceNodesInfo(znodeRoot)
+ hostPortOpt match {
+ case Some((host, port)) => serviceNodes.filter { sn =>
+ sn.host == host && sn.port == port
+ }
+ case _ => serviceNodes
+ }
+ }
+
+ override def info(msg: => Any): Unit = printMessage(msg)
+
+ override def warn(msg: => Any): Unit = printMessage(s"Warning: $msg")
+
+ override def error(msg: => Any): Unit = printMessage(s"Error: $msg")
+
+}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/CreateCommand.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/CreateCommand.scala
new file mode 100644
index 000000000..63bf7d092
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/CreateCommand.scala
@@ -0,0 +1,94 @@
+/*
+ * 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.ctl.cmd
+
+import scala.collection.mutable.ListBuffer
+
+import org.apache.kyuubi.ctl.{CliConfig, Render, ServiceControlObject}
+import org.apache.kyuubi.ha.HighAvailabilityConf._
+import org.apache.kyuubi.ha.client.{DiscoveryClient, DiscoveryPaths,
ServiceNodeInfo}
+import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
+
+class CreateCommand(cliConfig: CliConfig) extends Command(cliConfig) {
+
+ def validateArguments(): Unit = {
+ if (cliArgs.service != ServiceControlObject.SERVER) {
+ fail("Only support expose Kyuubi server instance to another domain")
+ }
+ validateZkArguments()
+
+ val defaultNamespace = conf.getOption(HA_NAMESPACE.key)
+ .getOrElse(HA_NAMESPACE.defaultValStr)
+ if (defaultNamespace.equals(cliArgs.commonOpts.namespace)) {
+ fail(
+ s"""
+ |Only support expose Kyuubi server instance to another domain, a
different namespace
+ |than the default namespace [$defaultNamespace] should be specified.
+ """.stripMargin)
+ }
+
+ }
+
+ def run(): Unit = {
+ create()
+ }
+
+ /**
+ * Expose Kyuubi server instance to another domain.
+ */
+ private def create(): Unit = {
+ val kyuubiConf = conf
+
+ kyuubiConf.setIfMissing(HA_ADDRESSES, cliArgs.commonOpts.zkQuorum)
+ withDiscoveryClient(kyuubiConf) { discoveryClient =>
+ val fromNamespace =
+ DiscoveryPaths.makePath(null, kyuubiConf.get(HA_NAMESPACE))
+ val toNamespace = getZkNamespace()
+
+ val currentServerNodes =
discoveryClient.getServiceNodesInfo(fromNamespace)
+ val exposedServiceNodes = ListBuffer[ServiceNodeInfo]()
+
+ if (currentServerNodes.nonEmpty) {
+ def doCreate(zc: DiscoveryClient): Unit = {
+ currentServerNodes.foreach { sn =>
+ info(s"Exposing server instance:${sn.instance} with
version:${sn.version}" +
+ s" from $fromNamespace to $toNamespace")
+ val newNodePath = zc.createAndGetServiceNode(
+ kyuubiConf,
+ cliArgs.commonOpts.namespace,
+ sn.instance,
+ sn.version,
+ true)
+ exposedServiceNodes += sn.copy(
+ namespace = toNamespace,
+ nodeName = newNodePath.split("/").last)
+ }
+ }
+
+ if (kyuubiConf.get(HA_ADDRESSES) == cliArgs.commonOpts.zkQuorum) {
+ doCreate(discoveryClient)
+ } else {
+ kyuubiConf.set(HA_ADDRESSES, cliArgs.commonOpts.zkQuorum)
+ withDiscoveryClient(kyuubiConf)(doCreate)
+ }
+ }
+
+ val title = "Created zookeeper service nodes"
+ info(Render.renderServiceNodesInfo(title, exposedServiceNodes, verbose))
+ }
+ }
+}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/DeleteCommand.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/DeleteCommand.scala
new file mode 100644
index 000000000..d76f65bc3
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/DeleteCommand.scala
@@ -0,0 +1,64 @@
+/*
+ * 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.ctl.cmd
+
+import scala.collection.mutable.ListBuffer
+
+import org.apache.kyuubi.ctl.{CliConfig, Render}
+import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
+import org.apache.kyuubi.ha.client.ServiceNodeInfo
+
+class DeleteCommand(cliConfig: CliConfig) extends Command(cliConfig) {
+
+ def validateArguments(): Unit = {
+ validateZkArguments()
+ validateHostAndPort()
+ validateUser()
+ mergeArgsIntoKyuubiConf()
+ }
+
+ override def run(): Unit = {
+ delete()
+ }
+
+ /**
+ * Delete zookeeper service node with specified host port.
+ */
+ private def delete(): Unit = {
+ withDiscoveryClient(conf) { discoveryClient =>
+ val znodeRoot = getZkNamespace()
+ val hostPortOpt = Some((cliArgs.commonOpts.host,
cliArgs.commonOpts.port.toInt))
+ val nodesToDelete = getServiceNodes(discoveryClient, znodeRoot,
hostPortOpt)
+
+ val deletedNodes = ListBuffer[ServiceNodeInfo]()
+ nodesToDelete.foreach { node =>
+ val nodePath = s"$znodeRoot/${node.nodeName}"
+ info(s"Deleting zookeeper service node:$nodePath")
+ try {
+ discoveryClient.delete(nodePath)
+ deletedNodes += node
+ } catch {
+ case e: Exception =>
+ error(s"Failed to delete zookeeper service node:$nodePath", e)
+ }
+ }
+
+ val title = "Deleted zookeeper service nodes"
+ info(Render.renderServiceNodesInfo(title, deletedNodes, verbose))
+ }
+ }
+}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/GetCommand.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/GetCommand.scala
new file mode 100644
index 000000000..745495504
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/GetCommand.scala
@@ -0,0 +1,47 @@
+/*
+ * 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.ctl.cmd
+
+import org.apache.kyuubi.ctl.{CliConfig, Render}
+import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
+
+class GetCommand(cliConfig: CliConfig) extends Command(cliConfig) {
+
+ override def validateArguments(): Unit = {
+ validateZkArguments()
+ validateHostAndPort()
+ validateUser()
+ mergeArgsIntoKyuubiConf()
+ }
+
+ override def run(): Unit = list(filterHostPort = true)
+
+ private def list(filterHostPort: Boolean): Unit = {
+ withDiscoveryClient(conf) { discoveryClient =>
+ val znodeRoot = getZkNamespace()
+ val hostPortOpt =
+ if (filterHostPort) {
+ Some((cliArgs.commonOpts.host, cliArgs.commonOpts.port.toInt))
+ } else None
+ val nodes = getServiceNodes(discoveryClient, znodeRoot, hostPortOpt)
+
+ val title = "Zookeeper service nodes"
+ info(Render.renderServiceNodesInfo(title, nodes, verbose))
+ }
+ }
+
+}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/ListCommand.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/ListCommand.scala
new file mode 100644
index 000000000..6c539dc22
--- /dev/null
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/ListCommand.scala
@@ -0,0 +1,53 @@
+/*
+ * 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.ctl.cmd
+
+import org.apache.kyuubi.ctl.{CliConfig, Render, ServiceControlObject}
+import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
+
+class ListCommand(cliConfig: CliConfig) extends Command(cliConfig) {
+
+ override def validateArguments(): Unit = {
+ validateZkArguments()
+ cliArgs.service match {
+ case ServiceControlObject.ENGINE => validateUser()
+ case _ =>
+ }
+ mergeArgsIntoKyuubiConf()
+ }
+
+ override def run(): Unit = {
+ list(filterHostPort = false)
+ }
+
+ /**
+ * List Kyuubi server nodes info.
+ */
+ private def list(filterHostPort: Boolean): Unit = {
+ withDiscoveryClient(conf) { discoveryClient =>
+ val znodeRoot = getZkNamespace()
+ val hostPortOpt =
+ if (filterHostPort) {
+ Some((cliArgs.commonOpts.host, cliArgs.commonOpts.port.toInt))
+ } else None
+ val nodes = getServiceNodes(discoveryClient, znodeRoot, hostPortOpt)
+
+ val title = "Zookeeper service nodes"
+ info(Render.renderServiceNodesInfo(title, nodes, verbose))
+ }
+ }
+}
diff --git
a/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ServiceControlCliArgumentsSuite.scala
b/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ControlCliArgumentsSuite.scala
similarity index 87%
rename from
kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ServiceControlCliArgumentsSuite.scala
rename to
kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ControlCliArgumentsSuite.scala
index 4e02d22ab..9f424a742 100644
---
a/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ServiceControlCliArgumentsSuite.scala
+++
b/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ControlCliArgumentsSuite.scala
@@ -20,7 +20,7 @@ package org.apache.kyuubi.ctl
import org.apache.kyuubi.{KYUUBI_VERSION, KyuubiFunSuite}
import org.apache.kyuubi.ha.HighAvailabilityConf.HA_NAMESPACE
-class ServiceControlCliArgumentsSuite extends KyuubiFunSuite {
+class ControlCliArgumentsSuite extends KyuubiFunSuite {
val zkQuorum = "localhost:2181"
val namespace = "kyuubi"
val user = "kyuubi"
@@ -34,7 +34,7 @@ class ServiceControlCliArgumentsSuite extends KyuubiFunSuite {
val thread = new Thread {
override def run(): Unit =
try {
- new ServiceControlCliArguments(args)
+ new ControlCliArguments(args)
} catch {
case e: Exception =>
error(e)
@@ -54,7 +54,7 @@ class ServiceControlCliArgumentsSuite extends KyuubiFunSuite {
val thread = new Thread {
override def run(): Unit =
try {
- new ServiceControlCliArguments(args) {
+ new ControlCliArguments(args) {
override private[kyuubi] lazy val effectSetup = new
KyuubiOEffectSetup {
// nothing to do, to handle out stream.
override def terminate(exitState: Either[String, Unit]): Unit
= ()
@@ -90,15 +90,15 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
port,
"--version",
KYUUBI_VERSION)
- val opArgs = new ServiceControlCliArguments(args)
+ val opArgs = new ControlCliArguments(args)
assert(opArgs.cliArgs.action.toString.equalsIgnoreCase(op))
assert(opArgs.cliArgs.service.toString.equalsIgnoreCase(service))
- assert(opArgs.cliArgs.zkQuorum == zkQuorum)
- assert(opArgs.cliArgs.namespace == namespace)
- assert(opArgs.cliArgs.user == user)
- assert(opArgs.cliArgs.host == host)
- assert(opArgs.cliArgs.port == port)
- assert(opArgs.cliArgs.version == KYUUBI_VERSION)
+ assert(opArgs.cliArgs.commonOpts.zkQuorum == zkQuorum)
+ assert(opArgs.cliArgs.commonOpts.namespace == namespace)
+ assert(opArgs.cliArgs.engineOpts.user == user)
+ assert(opArgs.cliArgs.commonOpts.host == host)
+ assert(opArgs.cliArgs.commonOpts.port == port)
+ assert(opArgs.cliArgs.commonOpts.version == KYUUBI_VERSION)
}
}
@@ -119,14 +119,14 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
port,
"--version",
KYUUBI_VERSION)
- val opArgs = new ServiceControlCliArguments(args)
+ val opArgs = new ControlCliArguments(args)
assert(opArgs.cliArgs.action.toString.equalsIgnoreCase(op))
assert(opArgs.cliArgs.service.toString.equalsIgnoreCase(service))
- assert(opArgs.cliArgs.zkQuorum == zkQuorum)
- assert(opArgs.cliArgs.namespace == newNamespace)
- assert(opArgs.cliArgs.host == host)
- assert(opArgs.cliArgs.port == port)
- assert(opArgs.cliArgs.version == KYUUBI_VERSION)
+ assert(opArgs.cliArgs.commonOpts.zkQuorum == zkQuorum)
+ assert(opArgs.cliArgs.commonOpts.namespace == newNamespace)
+ assert(opArgs.cliArgs.commonOpts.host == host)
+ assert(opArgs.cliArgs.commonOpts.port == port)
+ assert(opArgs.cliArgs.commonOpts.version == KYUUBI_VERSION)
}
}
@@ -166,7 +166,7 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
zkQuorum,
"--namespace",
namespace)
- val opArgs = new ServiceControlCliArguments(args2)
+ val opArgs = new ControlCliArguments(args2)
assert(opArgs.cliArgs.action == ServiceControlAction.LIST)
}
@@ -218,7 +218,7 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
host,
"--port",
port)
- val opArgs6 = new ServiceControlCliArguments(args5)
+ val opArgs6 = new ControlCliArguments(args5)
assert(opArgs6.cliArgs.action.toString.equalsIgnoreCase(op))
}
}
@@ -281,7 +281,7 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
zkQuorum,
"--namespace",
newNamespace)
- val opArgs2 = new ServiceControlCliArguments(args2)
+ val opArgs2 = new ControlCliArguments(args2)
assert(opArgs2.cliArgs.action.toString.equalsIgnoreCase(op))
val args4 = Array(
@@ -301,9 +301,9 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
"list",
"--zk-quorum",
zkQuorum)
- val opArgs = new ServiceControlCliArguments(args)
- assert(opArgs.cliArgs.namespace == namespace)
- assert(opArgs.cliArgs.version == KYUUBI_VERSION)
+ val opArgs = new ControlCliArguments(args)
+ assert(opArgs.cliArgs.commonOpts.namespace == namespace)
+ assert(opArgs.cliArgs.commonOpts.version == KYUUBI_VERSION)
}
test("test use short options") {
@@ -324,15 +324,15 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
port,
"-v",
KYUUBI_VERSION)
- val opArgs = new ServiceControlCliArguments(args)
+ val opArgs = new ControlCliArguments(args)
assert(opArgs.cliArgs.action.toString.equalsIgnoreCase(op))
assert(opArgs.cliArgs.service.toString.equalsIgnoreCase(service))
- assert(opArgs.cliArgs.zkQuorum == zkQuorum)
- assert(opArgs.cliArgs.namespace == namespace)
- assert(opArgs.cliArgs.user == user)
- assert(opArgs.cliArgs.host == host)
- assert(opArgs.cliArgs.port == port)
- assert(opArgs.cliArgs.version == KYUUBI_VERSION)
+ assert(opArgs.cliArgs.commonOpts.zkQuorum == zkQuorum)
+ assert(opArgs.cliArgs.commonOpts.namespace == namespace)
+ assert(opArgs.cliArgs.engineOpts.user == user)
+ assert(opArgs.cliArgs.commonOpts.host == host)
+ assert(opArgs.cliArgs.commonOpts.port == port)
+ assert(opArgs.cliArgs.commonOpts.version == KYUUBI_VERSION)
}
}
@@ -342,8 +342,8 @@ class ServiceControlCliArgumentsSuite extends
KyuubiFunSuite {
"-zk",
zkQuorum,
"-b")
- val opArgs3 = new ServiceControlCliArguments(args2)
- assert(opArgs3.cliArgs.verbose)
+ val opArgs3 = new ControlCliArguments(args2)
+ assert(opArgs3.cliArgs.commonOpts.verbose)
}
test("test --help") {
diff --git
a/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ServiceControlCliSuite.scala
b/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ControlCliSuite.scala
similarity index 91%
rename from
kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ServiceControlCliSuite.scala
rename to kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ControlCliSuite.scala
index 64e231095..f1a9984b9 100644
---
a/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ServiceControlCliSuite.scala
+++ b/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/ControlCliSuite.scala
@@ -49,7 +49,7 @@ trait TestPrematureExit {
private[kyuubi] def testPrematureExit(
input: Array[String],
searchString: String,
- mainObject: CommandLineUtils = ServiceControlCli): Unit = {
+ mainObject: CommandLineUtils = ControlCli): Unit = {
val printStream = new BufferPrintStream()
mainObject.printStream = printStream
@@ -84,9 +84,8 @@ trait TestPrematureExit {
}
}
-class ServiceControlCliSuite extends KyuubiFunSuite with TestPrematureExit {
+class ControlCliSuite extends KyuubiFunSuite with TestPrematureExit {
import DiscoveryClientProvider._
- import ServiceControlCli._
val zkServer = new EmbeddedZookeeper()
val conf: KyuubiConf = KyuubiConf()
@@ -95,7 +94,7 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
val host = "localhost"
val port = "10000"
val user = "kyuubi"
- val ctl = new ServiceControlCli()
+ val ctl = new ControlCli()
val counter = new AtomicInteger(0)
override def beforeAll(): Unit = {
@@ -123,7 +122,7 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
private def getRenderedNodesInfoWithoutTitle(
nodesInfo: Seq[ServiceNodeInfo],
verbose: Boolean): String = {
- val renderedInfo = renderServiceNodesInfo("", nodesInfo, verbose)
+ val renderedInfo = Render.renderServiceNodesInfo("", nodesInfo, verbose)
if (verbose) {
renderedInfo.substring(renderedInfo.indexOf("|"))
} else {
@@ -198,7 +197,7 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
val title = "test render"
val nodes = Seq(
ServiceNodeInfo("/kyuubi", "serviceNode", "localhost", 10000,
Some("version"), None))
- val renderedInfo = renderServiceNodesInfo(title, nodes, true)
+ val renderedInfo = Render.renderServiceNodesInfo(title, nodes, true)
val expected = {
s"\n $title " +
"""
@@ -264,7 +263,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
zkServer.getConnectString,
"--namespace",
namespace)
- assert(getZkNamespace(new ServiceControlCliArguments(arg1)) ==
s"/$namespace")
+ val scArgs1 = new ControlCliArguments(arg1)
+ assert(scArgs1.command.getZkNamespace() == s"/$namespace")
val arg2 = Array(
"list",
@@ -275,7 +275,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
namespace,
"--user",
user)
- assert(getZkNamespace(new ServiceControlCliArguments(arg2)) ==
+ val scArgs2 = new ControlCliArguments(arg2)
+ assert(scArgs2.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_USER_SPARK_SQL/$user/default")
}
@@ -414,7 +415,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
namespace,
"--user",
user)
- assert(getZkNamespace(new ServiceControlCliArguments(arg1)) ==
+ val scArgs1 = new ControlCliArguments(arg1)
+ assert(scArgs1.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_USER_SPARK_SQL/$user/default")
val arg2 = Array(
@@ -428,7 +430,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
user,
"--engine-type",
"FLINK_SQL")
- assert(getZkNamespace(new ServiceControlCliArguments(arg2)) ==
+ val scArgs2 = new ControlCliArguments(arg2)
+ assert(scArgs2.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_USER_FLINK_SQL/$user/default")
val arg3 = Array(
@@ -442,7 +445,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
user,
"--engine-type",
"TRINO")
- assert(getZkNamespace(new ServiceControlCliArguments(arg3)) ==
+ val scArgs3 = new ControlCliArguments(arg3)
+ assert(scArgs3.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_USER_TRINO/$user/default")
val arg4 = Array(
@@ -458,7 +462,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
"SPARK_SQL",
"--engine-subdomain",
"sub_1")
- assert(getZkNamespace(new ServiceControlCliArguments(arg4)) ==
+ val scArgs4 = new ControlCliArguments(arg4)
+ assert(scArgs4.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_USER_SPARK_SQL/$user/sub_1")
val arg5 = Array(
@@ -476,7 +481,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
"SPARK_SQL",
"--engine-subdomain",
"sub_1")
- assert(getZkNamespace(new ServiceControlCliArguments(arg5)) ==
+ val scArgs5 = new ControlCliArguments(arg5)
+ assert(scArgs5.command.getZkNamespace() ==
s"/${namespace}_1.5.0_USER_SPARK_SQL/$user/sub_1")
}
@@ -490,7 +496,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
namespace,
"--user",
user)
- assert(getZkNamespace(new ServiceControlCliArguments(arg1)) ==
+ val scArgs1 = new ControlCliArguments(arg1)
+ assert(scArgs1.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_USER_SPARK_SQL/$user/default")
val arg2 = Array(
@@ -504,7 +511,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
user,
"--engine-share-level",
"CONNECTION")
- assert(getZkNamespace(new ServiceControlCliArguments(arg2)) ==
+ val scArgs2 = new ControlCliArguments(arg2)
+ assert(scArgs2.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_CONNECTION_SPARK_SQL/$user/default")
val arg3 = Array(
@@ -518,7 +526,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
user,
"--engine-share-level",
"USER")
- assert(getZkNamespace(new ServiceControlCliArguments(arg3)) ==
+ val scArgs3 = new ControlCliArguments(arg3)
+ assert(scArgs3.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_USER_SPARK_SQL/$user/default")
val arg4 = Array(
@@ -532,7 +541,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
user,
"--engine-share-level",
"GROUP")
- assert(getZkNamespace(new ServiceControlCliArguments(arg4)) ==
+ val scArgs4 = new ControlCliArguments(arg4)
+ assert(scArgs4.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_GROUP_SPARK_SQL/$user/default")
val arg5 = Array(
@@ -546,7 +556,8 @@ class ServiceControlCliSuite extends KyuubiFunSuite with
TestPrematureExit {
user,
"--engine-share-level",
"SERVER")
- assert(getZkNamespace(new ServiceControlCliArguments(arg5)) ==
+ val scArgs5 = new ControlCliArguments(arg5)
+ assert(scArgs5.command.getZkNamespace() ==
s"/${namespace}_${KYUUBI_VERSION}_SERVER_SPARK_SQL/$user/default")
}
}