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/kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 1dd9db749 [KYUUBI #4996] Support to refresh kubernetes configs
dynamically
1dd9db749 is described below
commit 1dd9db7492e7b6fd9cda173dc362235f374f89d6
Author: fwang12 <[email protected]>
AuthorDate: Mon Jun 26 18:06:59 2023 +0800
[KYUUBI #4996] Support to refresh kubernetes configs dynamically
### _Why are the changes needed?_
This is a followup of #4843
To support load kubernetes conf during runtime, so that we can support more
kuberntes contexts without restarting the kyuubi server.
### _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/contributing/code/testing.html#running-tests)
locally before make a pull request
Closes #4996 from turboFei/refresh_kubernetes_conf.
Closes #4996
807fb92e4 [fwang12] comments
d42d25af7 [fwang12] from conf
809a7d3df [fwang12] refresh
22743f9e5 [fwang12] save
dedebbe71 [fwang12] api
d6f58cfc7 [fwang12] refresh kubernetes config
Authored-by: fwang12 <[email protected]>
Signed-off-by: fwang12 <[email protected]>
---
docs/client/rest/rest_api.md | 6 +++++
.../ctl/cmd/refresh/RefreshConfigCommand.scala | 4 +++-
.../apache/kyuubi/ctl/opt/AdminCommandLine.scala | 2 +-
.../kyuubi/ctl/AdminControlCliArgumentsSuite.scala | 11 ++++++++-
.../org/apache/kyuubi/client/AdminRestApi.java | 5 ++++
.../engine/KubernetesApplicationOperation.scala | 10 ++++----
.../org/apache/kyuubi/server/KyuubiServer.scala | 27 +++++++++++++++++-----
.../kyuubi/server/api/v1/AdminResource.scala | 19 +++++++++++++++
8 files changed, 70 insertions(+), 14 deletions(-)
diff --git a/docs/client/rest/rest_api.md b/docs/client/rest/rest_api.md
index fbff59f05..bf4917e88 100644
--- a/docs/client/rest/rest_api.md
+++ b/docs/client/rest/rest_api.md
@@ -451,6 +451,12 @@ Refresh the Hadoop configurations of the Kyuubi server.
Refresh the [user defaults
configs](../../deployment/settings.html#user-defaults) with key in format in
the form of `___{username}___.{config key}` from default property file.
+### POST /admin/refresh/kubernetes_conf
+
+Refresh the kubernetes configs with key prefixed with `kyuubi.kubernetes` from
default property file.
+
+It is helpful if you need to support multiple kubernetes contexts and
namespaces, see [KYUUBI #4843](https://github.com/apache/kyuubi/issues/4843).
+
### DELETE /admin/engine
Delete the specified engine.
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/refresh/RefreshConfigCommand.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/refresh/RefreshConfigCommand.scala
index 69aa0c3d0..571e7eef3 100644
---
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/refresh/RefreshConfigCommand.scala
+++
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/cmd/refresh/RefreshConfigCommand.scala
@@ -21,7 +21,7 @@ import org.apache.kyuubi.KyuubiException
import org.apache.kyuubi.client.AdminRestApi
import org.apache.kyuubi.ctl.RestClientFactory.withKyuubiRestClient
import org.apache.kyuubi.ctl.cmd.AdminCtlCommand
-import
org.apache.kyuubi.ctl.cmd.refresh.RefreshConfigCommandConfigType.{HADOOP_CONF,
UNLIMITED_USERS, USER_DEFAULTS_CONF}
+import
org.apache.kyuubi.ctl.cmd.refresh.RefreshConfigCommandConfigType.{HADOOP_CONF,
KUBERNETES_CONF, UNLIMITED_USERS, USER_DEFAULTS_CONF}
import org.apache.kyuubi.ctl.opt.CliConfig
import org.apache.kyuubi.ctl.util.{Tabulator, Validator}
@@ -36,6 +36,7 @@ class RefreshConfigCommand(cliConfig: CliConfig) extends
AdminCtlCommand[String]
normalizedCliConfig.adminConfigOpts.configType match {
case HADOOP_CONF => adminRestApi.refreshHadoopConf()
case USER_DEFAULTS_CONF => adminRestApi.refreshUserDefaultsConf()
+ case KUBERNETES_CONF => adminRestApi.refreshKubernetesConf()
case UNLIMITED_USERS => adminRestApi.refreshUnlimitedUsers()
case configType => throw new KyuubiException(s"Invalid config
type:$configType")
}
@@ -49,5 +50,6 @@ class RefreshConfigCommand(cliConfig: CliConfig) extends
AdminCtlCommand[String]
object RefreshConfigCommandConfigType {
final val HADOOP_CONF = "hadoopConf"
final val USER_DEFAULTS_CONF = "userDefaultsConf"
+ final val KUBERNETES_CONF = "kubernetesConf"
final val UNLIMITED_USERS = "unlimitedUsers"
}
diff --git
a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/opt/AdminCommandLine.scala
b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/opt/AdminCommandLine.scala
index 71e4068e5..588f3ea37 100644
--- a/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/opt/AdminCommandLine.scala
+++ b/kyuubi-ctl/src/main/scala/org/apache/kyuubi/ctl/opt/AdminCommandLine.scala
@@ -108,6 +108,6 @@ object AdminCommandLine extends CommonCommandLine {
.optional()
.action((v, c) => c.copy(adminConfigOpts =
c.adminConfigOpts.copy(configType = v)))
.text("The valid config type can be one of the following: " +
- s"$HADOOP_CONF, $USER_DEFAULTS_CONF, $UNLIMITED_USERS."))
+ s"$HADOOP_CONF, $USER_DEFAULTS_CONF, $KUBERNETES_CONF,
$UNLIMITED_USERS."))
}
}
diff --git
a/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/AdminControlCliArgumentsSuite.scala
b/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/AdminControlCliArgumentsSuite.scala
index fdadd011b..72b6fd3e8 100644
---
a/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/AdminControlCliArgumentsSuite.scala
+++
b/kyuubi-ctl/src/test/scala/org/apache/kyuubi/ctl/AdminControlCliArgumentsSuite.scala
@@ -83,6 +83,15 @@ class AdminControlCliArgumentsSuite extends KyuubiFunSuite
with TestPrematureExi
assert(opArgs3.cliConfig.resource === ControlObject.CONFIG)
assert(opArgs3.cliConfig.adminConfigOpts.configType === UNLIMITED_USERS)
+ args = Array(
+ "refresh",
+ "config",
+ "kubernetesConf")
+ val opArgs4 = new AdminControlCliArguments(args)
+ assert(opArgs4.cliConfig.action === ControlAction.REFRESH)
+ assert(opArgs4.cliConfig.resource === ControlObject.CONFIG)
+ assert(opArgs4.cliConfig.adminConfigOpts.configType === KUBERNETES_CONF)
+
args = Array(
"refresh",
"config",
@@ -165,7 +174,7 @@ class AdminControlCliArgumentsSuite extends KyuubiFunSuite
with TestPrematureExi
| Refresh the resource.
|Command: refresh config [<configType>]
| Refresh the config with specified type.
- | <configType> The valid config type can be one of the
following: $HADOOP_CONF, $USER_DEFAULTS_CONF, $UNLIMITED_USERS.
+ | <configType> The valid config type can be one of the
following: $HADOOP_CONF, $USER_DEFAULTS_CONF, $KUBERNETES_CONF,
$UNLIMITED_USERS.
|
| -h, --help Show help message and exit.""".stripMargin
// scalastyle:on
diff --git
a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java
b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java
index a983827c8..8287e7368 100644
---
a/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java
+++
b/kyuubi-rest-client/src/main/java/org/apache/kyuubi/client/AdminRestApi.java
@@ -47,6 +47,11 @@ public class AdminRestApi {
return this.getClient().post(path, null, client.getAuthHeader());
}
+ public String refreshKubernetesConf() {
+ String path = String.format("%s/%s", API_BASE_PATH,
"refresh/kubernetes_conf");
+ return this.getClient().post(path, null, client.getAuthHeader());
+ }
+
public String refreshUnlimitedUsers() {
String path = String.format("%s/%s", API_BASE_PATH,
"refresh/unlimited_users");
return this.getClient().post(path, null, client.getAuthHeader());
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/engine/KubernetesApplicationOperation.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/engine/KubernetesApplicationOperation.scala
index d6dfba2fe..984273051 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/engine/KubernetesApplicationOperation.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/engine/KubernetesApplicationOperation.scala
@@ -40,12 +40,14 @@ class KubernetesApplicationOperation extends
ApplicationOperation with Logging {
private val enginePodInformers: ConcurrentHashMap[KubernetesInfo,
SharedIndexInformer[Pod]] =
new ConcurrentHashMap[KubernetesInfo, SharedIndexInformer[Pod]]
- private var allowedContexts: Seq[String] = Seq.empty
- private var allowedNamespaces: Seq[String] = Seq.empty
-
private var submitTimeout: Long = _
private var kyuubiConf: KyuubiConf = _
+ private def allowedContexts: Seq[String] =
+ kyuubiConf.get(KyuubiConf.KUBERNETES_CONTEXT_ALLOW_LIST)
+ private def allowedNamespaces: Seq[String] =
+ kyuubiConf.get(KyuubiConf.KUBERNETES_NAMESPACE_ALLOW_LIST)
+
// key is kyuubi_unique_key
private val appInfoStore: ConcurrentHashMap[String, ApplicationInfo] =
new ConcurrentHashMap[String, ApplicationInfo]
@@ -90,8 +92,6 @@ class KubernetesApplicationOperation extends
ApplicationOperation with Logging {
kyuubiConf = conf
info("Start initializing Kubernetes application operation.")
submitTimeout = conf.get(KyuubiConf.ENGINE_KUBERNETES_SUBMIT_TIMEOUT)
- allowedContexts = conf.get(KyuubiConf.KUBERNETES_CONTEXT_ALLOW_LIST)
- allowedNamespaces = conf.get(KyuubiConf.KUBERNETES_NAMESPACE_ALLOW_LIST)
// Defer cleaning terminated application information
val retainPeriod =
conf.get(KyuubiConf.KUBERNETES_TERMINATED_APPLICATION_RETAIN_PERIOD)
cleanupTerminatedAppInfoTrigger = CacheBuilder.newBuilder()
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala
index 491b11e90..12120ea55 100644
--- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiServer.scala
@@ -25,7 +25,7 @@ import org.apache.hadoop.security.UserGroupInformation
import org.apache.kyuubi._
import org.apache.kyuubi.config.KyuubiConf
-import org.apache.kyuubi.config.KyuubiConf.{FRONTEND_PROTOCOLS,
FrontendProtocols}
+import org.apache.kyuubi.config.KyuubiConf.{FRONTEND_PROTOCOLS,
FrontendProtocols, KYUUBI_KUBERNETES_CONF_PREFIX}
import org.apache.kyuubi.config.KyuubiConf.FrontendProtocols._
import org.apache.kyuubi.events.{EventBus, KyuubiServerInfoEvent,
ServerEventHandlerRegister}
import org.apache.kyuubi.ha.HighAvailabilityConf._
@@ -111,14 +111,29 @@ object KyuubiServer extends Logging {
private[kyuubi] def refreshUserDefaultsConf(): Unit =
kyuubiServer.conf.synchronized {
val existedUserDefaults = kyuubiServer.conf.getAllUserDefaults
val refreshedUserDefaults =
KyuubiConf().loadFileDefaults().getAllUserDefaults
+ refreshConfig("user defaults", existedUserDefaults, refreshedUserDefaults)
+ }
+
+ private[kyuubi] def refreshKubernetesConf(): Unit =
kyuubiServer.conf.synchronized {
+ val existedKubernetesConf =
+
kyuubiServer.conf.getAll.filter(_._1.startsWith(KYUUBI_KUBERNETES_CONF_PREFIX))
+ val refreshedKubernetesConf =
+
KyuubiConf().loadFileDefaults().getAll.filter(_._1.startsWith(KYUUBI_KUBERNETES_CONF_PREFIX))
+ refreshConfig("kubernetes", existedKubernetesConf, refreshedKubernetesConf)
+ }
+
+ private def refreshConfig(
+ configDomain: String,
+ existing: Map[String, String],
+ refreshed: Map[String, String]): Unit = {
var (unsetCount, updatedCount, addedCount) = (0, 0, 0)
- for ((k, _) <- existedUserDefaults if !refreshedUserDefaults.contains(k)) {
+ for ((k, _) <- existing if !refreshed.contains(k)) {
kyuubiServer.conf.unset(k)
unsetCount = unsetCount + 1
}
- for ((k, v) <- refreshedUserDefaults) {
- if (existedUserDefaults.contains(k)) {
- if (!StringUtils.equals(existedUserDefaults.get(k).orNull, v)) {
+ for ((k, v) <- refreshed) {
+ if (existing.contains(k)) {
+ if (!StringUtils.equals(existing.get(k).orNull, v)) {
updatedCount = updatedCount + 1
}
} else {
@@ -126,7 +141,7 @@ object KyuubiServer extends Logging {
}
kyuubiServer.conf.set(k, v)
}
- info(s"Refreshed user defaults configs with changes of " +
+ info(s"Refreshed $configDomain configs with changes of " +
s"unset: $unsetCount, updated: $updatedCount, added: $addedCount")
}
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 735efa71b..a0e136e5e 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
@@ -87,6 +87,25 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
Response.ok(s"Refresh the user defaults conf successfully.").build()
}
+ @ApiResponse(
+ responseCode = "200",
+ content = Array(new Content(mediaType = MediaType.APPLICATION_JSON)),
+ description = "refresh the kubernetes configs")
+ @POST
+ @Path("refresh/kubernetes_conf")
+ def refreshKubernetesConf(): Response = {
+ val userName = fe.getSessionUser(Map.empty[String, String])
+ val ipAddress = fe.getIpAddress
+ info(s"Receive refresh kubernetes conf request from $userName/$ipAddress")
+ if (!isAdministrator(userName)) {
+ throw new NotAllowedException(
+ s"$userName is not allowed to refresh the kubernetes conf")
+ }
+ info(s"Reloading kubernetes conf")
+ KyuubiServer.refreshKubernetesConf()
+ Response.ok(s"Refresh the kubernetes conf successfully.").build()
+ }
+
@ApiResponse(
responseCode = "200",
content = Array(new Content(mediaType = MediaType.APPLICATION_JSON)),