Skip to content

Commit c77388e

Browse files
[KYUUBI #6402]: engine.share.level=GROUP enable for a list of hadoop groups
1 parent d3520dd commit c77388e

File tree

5 files changed

+65
-6
lines changed

5 files changed

+65
-6
lines changed

docs/configuration/settings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ You can configure the Kyuubi properties in `$KYUUBI_HOME/conf/kyuubi-defaults.co
509509
| kyuubi.session.idle.timeout | PT6H | session idle timeout, it will be closed when it's not accessed for this duration | duration | 1.2.0 |
510510
| kyuubi.session.local.dir.allow.list || The local dir list that are allowed to access by the kyuubi session application. End-users might set some parameters such as `spark.files` and it will upload some local files when launching the kyuubi engine, if the local dir allow list is defined, kyuubi will check whether the path to upload is in the allow list. Note that, if it is empty, there is no limitation for that. And please use absolute paths. | set | 1.6.0 |
511511
| kyuubi.session.name | <undefined> | A human readable name of the session and we use empty string by default. This name will be recorded in the event. Note that, we only apply this value from session conf. | string | 1.4.0 |
512+
| kyuubi.session.preferGroups | <undefined> | Comma separated list of hadoop group name for the group engine launch. The first preferred and valid group from this list will be used for GROUP SHARE LEVEL execution. If this is not configured, the engine will use the first group name from the users list of groups as the primary group. | seq | 1.9.3 |
512513
| kyuubi.session.proxy.user | <undefined> | An alternative to hive.server2.proxy.user. The current behavior is consistent with hive.server2.proxy.user and now only takes effect in RESTFul API. When both parameters are set, kyuubi.session.proxy.user takes precedence. | string | 1.9.0 |
513514
| kyuubi.session.timeout | PT6H | (deprecated)session timeout, it will be closed when it's not accessed for this duration | duration | 1.0.0 |
514515
| kyuubi.session.user.sign.enabled | false | Whether to verify the integrity of session user name on the engine side, e.g. Authz plugin in Spark. | boolean | 1.7.0 |

kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,6 +2779,17 @@ object KyuubiConf {
27792779
}
27802780
.createWithDefault("hadoop")
27812781

2782+
val PREFERRED_GROUPS: OptionalConfigEntry[Seq[String]] =
2783+
buildConf("kyuubi.session.preferGroups")
2784+
.doc("Comma separated list of hadoop group name for the group engine launch. " +
2785+
"The first preferred and valid group from this list will be used for GROUP " +
2786+
"SHARE LEVEL execution. If this is not configured, the engine will use " +
2787+
"the first group name from the users list of groups as the primary group.")
2788+
.version("1.9.3")
2789+
.stringConf
2790+
.toSequence()
2791+
.createOptional
2792+
27822793
val SERVER_NAME: OptionalConfigEntry[String] =
27832794
buildConf("kyuubi.server.name")
27842795
.doc("The name of Kyuubi Server.")

kyuubi-server/src/main/scala/org/apache/kyuubi/engine/EngineRef.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ private[kyuubi] class EngineRef(
9898
}
9999

100100
// user for launching engine
101-
private[kyuubi] val appUser: String = if (doAsEnabled) routingUser else Utils.currentUser
101+
private[kyuubi] val appUser: String = shareLevel match {
102+
case GROUP => if (doAsEnabled) sessionUser else Utils.currentUser
103+
case _ => if (doAsEnabled) routingUser else Utils.currentUser
104+
}
102105

103106
@VisibleForTesting
104107
private[kyuubi] val subdomain: String = conf.get(ENGINE_SHARE_LEVEL_SUBDOMAIN) match {

kyuubi-server/src/main/scala/org/apache/kyuubi/session/HadoopGroupProvider.scala

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,37 @@ import java.util.{Map => JMap}
2222
import org.apache.hadoop.security.UserGroupInformation
2323

2424
import org.apache.kyuubi.Logging
25+
import org.apache.kyuubi.config.KyuubiConf
2526
import org.apache.kyuubi.plugin.GroupProvider
2627

2728
/**
2829
* Hadoop based group provider, see more information at
2930
* https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/GroupsMapping.html
3031
*/
3132
class HadoopGroupProvider extends GroupProvider with Logging {
32-
override def primaryGroup(user: String, sessionConf: JMap[String, String]): String =
33-
groups(user, sessionConf).head
33+
override def primaryGroup(user: String, sessionConf: JMap[String, String]): String = {
34+
val preferredGroups: Option[Seq[String]] = if (sessionConf != null) {
35+
Option(sessionConf.get(KyuubiConf.PREFERRED_GROUPS.key)).map(_.split(",").toSeq)
36+
} else {
37+
None
38+
}
39+
40+
val userGroups: Array[String] = groups(user, sessionConf)
41+
42+
val primaryGroup = preferredGroups match {
43+
case Some(groups) =>
44+
groups.find(userGroups.contains) match {
45+
case Some(group) => group
46+
case None => userGroups.headOption.getOrElse {
47+
throw new NoSuchElementException("No groups available for the user")
48+
}
49+
}
50+
case None => userGroups.headOption.getOrElse {
51+
throw new NoSuchElementException("No groups available for the user")
52+
}
53+
}
54+
primaryGroup
55+
}
3456

3557
override def groups(user: String, sessionConf: JMap[String, String]): Array[String] =
3658
UserGroupInformation.createRemoteUser(user).getGroupNames match {

kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiOperationPerGroupSuite.scala

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,34 @@ class KyuubiOperationPerGroupSuite extends WithKyuubiServer with SparkQueryTests
6868
}
6969

7070
test("kyuubi defined function - system_user/session_user") {
71-
withSessionConf(Map("hive.server2.proxy.user" -> "user1"))(Map.empty)(Map.empty) {
71+
withSessionConf(Map("hive.server2.proxy.user" -> "user2"))(Map.empty)(Map.empty) {
7272
withJdbcStatement() { statement =>
7373
val res = statement.executeQuery("select system_user() as c1, session_user() as c2")
7474
assert(res.next())
75-
assert(res.getString("c1") === "testGG")
76-
assert(res.getString("c2") === "user1")
75+
assert(res.getString("c1") === "user1")
76+
assert(res.getString("c2") === "user2")
77+
}
78+
}
79+
}
80+
81+
test("ensure preferred group is chosen from list of groups") {
82+
withSessionConf(Map("hive.server2.proxy.user" -> "user1"))(Map(
83+
KyuubiConf.PREFERRED_GROUPS.key -> "test,group_tt,testGG"))(Map.empty) {
84+
withJdbcStatement() { statement =>
85+
val res = statement.executeQuery("set spark.app.name")
86+
assert(res.next())
87+
val engineName = res.getString("value")
88+
assert(engineName.startsWith(s"kyuubi_GROUP_${conf.get(KyuubiConf.ENGINE_TYPE)}_group_tt"))
89+
}
90+
}
91+
92+
withSessionConf(Map("hive.server2.proxy.user" -> "user1"))(Map(
93+
KyuubiConf.PREFERRED_GROUPS.key -> "test"))(Map.empty) {
94+
withJdbcStatement() { statement =>
95+
val res = statement.executeQuery("set spark.app.name")
96+
assert(res.next())
97+
val engineName = res.getString("value")
98+
assert(engineName.startsWith(s"kyuubi_GROUP_${conf.get(KyuubiConf.ENGINE_TYPE)}_testGG"))
7799
}
78100
}
79101
}

0 commit comments

Comments
 (0)