Skip to content

Commit 49455a9

Browse files
Replace serverOnly with audience and immutable on ConfigEntry
1 parent 1f5ebbf commit 49455a9

25 files changed

Lines changed: 999 additions & 236 deletions

AGENTS.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,11 @@ Runtime dependency changes may also require `LICENSE-binary` and `NOTICE` update
130130

131131
Configs are `ConfigEntry` instances in `KyuubiConf` or per-engine config classes.
132132

133-
- Server-only entries call `.serverOnly` on the builder; `KyuubiConf` tracks them in `serverOnlyConfEntries` so they are stripped before engine-side propagation.
133+
- Each config entry has two independent access-control properties:
134+
- **`audience`** — which consumers should receive the config. Set via `.audience(SERVER)`, `.audience(SPARK)`, etc. on the builder. Entries without explicit audience use prefix-based inference (`inferAudience`).
135+
- **`immutable`** — whether users can override the config at session time. Set via `.immutable` on the builder. Admin-controlled sources (user defaults, session conf advisors) bypass immutable checks.
136+
- Server-only entries use `.audience(SERVER)` and `.immutable` on the builder.
137+
- Engine process builders must use `conf.getEngineConf(EngineType.X)` (not `conf.getAll`) to get configs filtered by audience.
134138
- `version()` on `ConfigEntry` is the release the key first ships in.
135139
- Namespace matches scope: `kyuubi.operation.*`, `kyuubi.session.*`, `kyuubi.<engine>.*`.
136140
- Provide a sensible default; if none is safe, leave it undefined and document the requirement.

kyuubi-common/src/main/scala/org/apache/kyuubi/Utils.scala

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -325,17 +325,10 @@ object Utils extends Logging {
325325
def redactCommandLineArgs(conf: KyuubiConf, commands: Iterable[String]): Iterable[String] = {
326326
conf.get(SERVER_SECRET_REDACTION_PATTERN) match {
327327
case Some(redactionPattern) =>
328-
var nextKV = false
329328
commands.map {
330-
case PATTERN_FOR_KEY_VALUE_ARG(key, value) if nextKV =>
329+
case PATTERN_FOR_KEY_VALUE_ARG(key, value) =>
331330
val (_, newValue) = redact(redactionPattern, Seq((key, value))).head
332-
nextKV = false
333331
genKeyValuePair(key, newValue)
334-
335-
case cmd if cmd == CONF =>
336-
nextKV = true
337-
cmd
338-
339332
case cmd =>
340333
cmd
341334
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.kyuubi.config
19+
20+
import org.apache.kyuubi.engine.EngineType
21+
22+
object ConfigAudience extends Enumeration {
23+
type ConfigAudience = Value
24+
25+
val SERVER, SPARK, FLINK, HIVE, TRINO, JDBC, DATA_AGENT, ALL_ENGINES, ANY = Value
26+
27+
private val ENGINE_VALUES: Set[Value] = Set(SPARK, FLINK, HIVE, TRINO, JDBC, DATA_AGENT)
28+
29+
def fromEngineType(engineType: EngineType.EngineType): ConfigAudience = engineType match {
30+
case EngineType.SPARK_SQL => SPARK
31+
case EngineType.FLINK_SQL => FLINK
32+
case EngineType.HIVE_SQL => HIVE
33+
case EngineType.TRINO => TRINO
34+
case EngineType.JDBC => JDBC
35+
case EngineType.DATA_AGENT => DATA_AGENT
36+
case _ => throw new IllegalArgumentException(s"Unsupported engine type: $engineType")
37+
}
38+
39+
def isAudienceMatch(
40+
configAudience: Set[ConfigAudience],
41+
target: ConfigAudience): Boolean = {
42+
configAudience.contains(ANY) ||
43+
(configAudience.contains(ALL_ENGINES) && ENGINE_VALUES.contains(target)) ||
44+
configAudience.contains(target)
45+
}
46+
47+
def inferAudience(key: String): Set[ConfigAudience] = {
48+
if (key.startsWith("kyuubi.server.") || key.startsWith("kyuubi.frontend.") ||
49+
key.startsWith("kyuubi.backend.server.")) {
50+
Set(SERVER)
51+
} else if (key.startsWith("kyuubi.engine.spark.") ||
52+
key.startsWith("kyuubi.session.engine.spark.") || key.startsWith("spark.")) {
53+
Set(SPARK)
54+
} else if (key.startsWith("kyuubi.engine.flink.") ||
55+
key.startsWith("kyuubi.session.engine.flink.") || key.startsWith("flink.")) {
56+
Set(FLINK)
57+
} else if (key.startsWith("kyuubi.engine.hive.") ||
58+
key.startsWith("kyuubi.session.engine.hive.") || key.startsWith("hive.")) {
59+
Set(HIVE)
60+
} else if (key.startsWith("kyuubi.engine.trino.") ||
61+
key.startsWith("kyuubi.session.engine.trino.") || key.startsWith("trino.")) {
62+
Set(TRINO)
63+
} else if (key.startsWith("kyuubi.engine.jdbc.") ||
64+
key.startsWith("kyuubi.session.engine.jdbc.")) {
65+
Set(JDBC)
66+
} else if (key.startsWith("kyuubi.engine.data.agent.") ||
67+
key.startsWith("kyuubi.session.engine.data-agent.")) {
68+
Set(DATA_AGENT)
69+
} else {
70+
Set(ANY)
71+
}
72+
}
73+
}

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,22 @@ private[kyuubi] case class ConfigBuilder(key: String) {
3333
private[config] var _onCreate: Option[ConfigEntry[_] => Unit] = None
3434
private[config] var _type = ""
3535
private[config] var _internal = false
36-
private[config] var _serverOnly = false
36+
private[config] var _audience: Option[Set[ConfigAudience.Value]] = None
37+
private[config] var _immutable = false
3738
private[config] var _alternatives = List.empty[String]
3839

3940
def internal: ConfigBuilder = {
4041
_internal = true
4142
this
4243
}
4344

44-
def serverOnly: ConfigBuilder = {
45-
_serverOnly = true
45+
def audience(audiences: ConfigAudience.Value*): ConfigBuilder = {
46+
_audience = Some(audiences.toSet)
47+
this
48+
}
49+
50+
def immutable: ConfigBuilder = {
51+
_immutable = true
4652
this
4753
}
4854

@@ -138,7 +144,8 @@ private[kyuubi] case class ConfigBuilder(key: String) {
138144
_doc,
139145
_version,
140146
_internal,
141-
_serverOnly,
147+
_audience,
148+
_immutable,
142149
fallback)
143150
_onCreate.foreach(_(entry))
144151
entry
@@ -228,12 +235,12 @@ private[kyuubi] case class TypedConfigBuilder[T](
228235
/** Turns the config entry into a sequence of values of the underlying type. */
229236
def toSequence(sp: String = ","): TypedConfigBuilder[Seq[T]] = {
230237
parent._type = "seq"
231-
TypedConfigBuilder(parent, strToSeq(_, fromStr, sp), iterableToStr(_, toStr))
238+
TypedConfigBuilder(parent, strToSeq(_, fromStr, sp), iterableToStr(_, toStr, sp))
232239
}
233240

234241
def toSet(sp: String = ",", skipBlank: Boolean = true): TypedConfigBuilder[Set[T]] = {
235242
parent._type = "set"
236-
TypedConfigBuilder(parent, strToSet(_, fromStr, sp, skipBlank), iterableToStr(_, toStr))
243+
TypedConfigBuilder(parent, strToSet(_, fromStr, sp, skipBlank), iterableToStr(_, toStr, sp))
237244
}
238245

239246
def createOptional: OptionalConfigEntry[T] = {
@@ -246,7 +253,8 @@ private[kyuubi] case class TypedConfigBuilder[T](
246253
parent._version,
247254
parent._type,
248255
parent._internal,
249-
parent._serverOnly)
256+
parent._audience,
257+
parent._immutable)
250258
parent._onCreate.foreach(_(entry))
251259
entry
252260
}
@@ -265,7 +273,8 @@ private[kyuubi] case class TypedConfigBuilder[T](
265273
parent._version,
266274
parent._type,
267275
parent._internal,
268-
parent._serverOnly)
276+
parent._audience,
277+
parent._immutable)
269278
parent._onCreate.foreach(_(entry))
270279
entry
271280
}
@@ -281,7 +290,8 @@ private[kyuubi] case class TypedConfigBuilder[T](
281290
parent._version,
282291
parent._type,
283292
parent._internal,
284-
parent._serverOnly)
293+
parent._audience,
294+
parent._immutable)
285295
parent._onCreate.foreach(_(entry))
286296
entry
287297
}

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ trait ConfigEntry[T] {
2626
def version: String
2727
def typ: String
2828
def internal: Boolean
29-
def serverOnly: Boolean
29+
def audience: Option[Set[ConfigAudience.Value]]
30+
def immutable: Boolean
3031

3132
def defaultValStr: String
3233
def defaultVal: Option[T]
@@ -53,7 +54,8 @@ class OptionalConfigEntry[T](
5354
_version: String,
5455
_type: String,
5556
_internal: Boolean,
56-
_serverOnly: Boolean) extends ConfigEntry[Option[T]] {
57+
_audience: Option[Set[ConfigAudience.Value]],
58+
_immutable: Boolean) extends ConfigEntry[Option[T]] {
5759
override def valueConverter: String => Option[T] = {
5860
s => Option(rawValueConverter(s))
5961
}
@@ -84,7 +86,9 @@ class OptionalConfigEntry[T](
8486

8587
override def internal: Boolean = _internal
8688

87-
override def serverOnly: Boolean = _serverOnly
89+
override def audience: Option[Set[ConfigAudience.Value]] = _audience
90+
91+
override def immutable: Boolean = _immutable
8892
}
8993

9094
class ConfigEntryWithDefault[T](
@@ -97,7 +101,8 @@ class ConfigEntryWithDefault[T](
97101
_version: String,
98102
_type: String,
99103
_internal: Boolean,
100-
_serverOnly: Boolean) extends ConfigEntry[T] {
104+
_audience: Option[Set[ConfigAudience.Value]],
105+
_immutable: Boolean) extends ConfigEntry[T] {
101106
override def defaultValStr: String = strConverter(_defaultVal)
102107

103108
override def defaultVal: Option[T] = Option(_defaultVal)
@@ -122,7 +127,9 @@ class ConfigEntryWithDefault[T](
122127

123128
override def internal: Boolean = _internal
124129

125-
override def serverOnly: Boolean = _serverOnly
130+
override def audience: Option[Set[ConfigAudience.Value]] = _audience
131+
132+
override def immutable: Boolean = _immutable
126133
}
127134

128135
class ConfigEntryWithDefaultString[T](
@@ -135,7 +142,8 @@ class ConfigEntryWithDefaultString[T](
135142
_version: String,
136143
_type: String,
137144
_internal: Boolean,
138-
_serverOnly: Boolean) extends ConfigEntry[T] {
145+
_audience: Option[Set[ConfigAudience.Value]],
146+
_immutable: Boolean) extends ConfigEntry[T] {
139147
override def defaultValStr: String = _defaultVal
140148

141149
override def defaultVal: Option[T] = Some(valueConverter(_defaultVal))
@@ -161,7 +169,9 @@ class ConfigEntryWithDefaultString[T](
161169

162170
override def internal: Boolean = _internal
163171

164-
override def serverOnly: Boolean = _serverOnly
172+
override def audience: Option[Set[ConfigAudience.Value]] = _audience
173+
174+
override def immutable: Boolean = _immutable
165175
}
166176

167177
class ConfigEntryFallback[T](
@@ -170,7 +180,8 @@ class ConfigEntryFallback[T](
170180
_doc: String,
171181
_version: String,
172182
_internal: Boolean,
173-
_serverOnly: Boolean,
183+
_audience: Option[Set[ConfigAudience.Value]],
184+
_immutable: Boolean,
174185
fallback: ConfigEntry[T]) extends ConfigEntry[T] {
175186
override def defaultValStr: String = fallback.defaultValStr
176187

@@ -182,6 +193,8 @@ class ConfigEntryFallback[T](
182193

183194
override def key: String = _key
184195

196+
def fallbackKey: String = fallback.key
197+
185198
override def alternatives: List[String] = _alternatives
186199

187200
override def valueConverter: String => T = fallback.valueConverter
@@ -196,7 +209,9 @@ class ConfigEntryFallback[T](
196209

197210
override def internal: Boolean = _internal
198211

199-
override def serverOnly: Boolean = _serverOnly
212+
override def audience: Option[Set[ConfigAudience.Value]] = _audience
213+
214+
override def immutable: Boolean = _immutable
200215
}
201216

202217
object ConfigEntry {

0 commit comments

Comments
 (0)