This is an automated email from the ASF dual-hosted git repository.
bowenliang 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 9b932df25 [KYUUBI #5213] [Improvement] Check config value by enum
values
9b932df25 is described below
commit 9b932df25af17c345c22fa4759c5e61ae57c24f4
Author: Bowen Liang <[email protected]>
AuthorDate: Wed Aug 30 22:32:39 2023 +0800
[KYUUBI #5213] [Improvement] Check config value by enum values
### _Why are the changes needed?_
- check config values in the range of targeted enum values
### _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
### _Was this patch authored or co-authored using generative AI tooling?_
No.
Closes #5213 from bowenliang123/config-checkenum.
Closes #5213
857af7c6b [Bowen Liang] is valid enum
2862e5e7c [Bowen Liang] embrace
afe6a5333 [Bowen Liang] assert
00d22f73b [liangbowen] support checking config in the range of enumeration
values
Lead-authored-by: Bowen Liang <[email protected]>
Co-authored-by: liangbowen <[email protected]>
Signed-off-by: liangbowen <[email protected]>
---
.../org/apache/kyuubi/config/ConfigBuilder.scala | 22 +++++++++++++++++
.../org/apache/kyuubi/config/KyuubiConf.scala | 21 ++++++----------
.../apache/kyuubi/config/ConfigBuilderSuite.scala | 18 ++++++++++++++
.../KyuubiAuthenticationFactorySuite.scala | 7 +++---
.../service/authentication/SaslQOPSuite.scala | 2 +-
.../apache/kyuubi/ha/HighAvailabilityConf.scala | 6 ++---
.../org/apache/kyuubi/metrics/MetricsConf.scala | 4 +---
.../scala/org/apache/kyuubi/util/EnumUtils.scala | 28 +++++++---------------
.../org/apache/kyuubi/util/AssertionUtils.scala | 2 ++
9 files changed, 66 insertions(+), 44 deletions(-)
diff --git
a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/ConfigBuilder.scala
b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/ConfigBuilder.scala
index b64e5c5eb..d6de40241 100644
--- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/ConfigBuilder.scala
+++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/ConfigBuilder.scala
@@ -24,6 +24,8 @@ import java.util.regex.PatternSyntaxException
import scala.util.{Failure, Success, Try}
import scala.util.matching.Regex
+import org.apache.kyuubi.util.EnumUtils._
+
private[kyuubi] case class ConfigBuilder(key: String) {
private[config] var _doc = ""
@@ -203,6 +205,26 @@ private[kyuubi] case class TypedConfigBuilder[T](
}
}
+ /** Checks if the user-provided value for the config matches the value set
of the enumeration. */
+ def checkValues(enumeration: Enumeration): TypedConfigBuilder[T] = {
+ transform { v =>
+ val isValid = v match {
+ case iter: Iterable[Any] => isValidEnums(enumeration, iter)
+ case name => isValidEnum(enumeration, name)
+ }
+ if (!isValid) {
+ val actualValueStr = v match {
+ case iter: Iterable[Any] => iter.mkString(",")
+ case value => value.toString
+ }
+ throw new IllegalArgumentException(
+ s"The value of ${parent.key} should be one of
${enumeration.values.mkString(", ")}," +
+ s" but was $actualValueStr")
+ }
+ v
+ }
+ }
+
/** Turns the config entry into a sequence of values of the underlying type.
*/
def toSequence(sp: String = ","): TypedConfigBuilder[Seq[T]] = {
parent._type = "seq"
diff --git
a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
index 2c158b8e8..bec69e95e 100644
--- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
+++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
@@ -415,9 +415,7 @@ object KyuubiConf {
.stringConf
.transformToUpperCase
.toSequence()
- .checkValue(
- _.forall(FrontendProtocols.values.map(_.toString).contains),
- s"the frontend protocol should be one or more of
${FrontendProtocols.values.mkString(",")}")
+ .checkValues(FrontendProtocols)
.createWithDefault(Seq(
FrontendProtocols.THRIFT_BINARY.toString,
FrontendProtocols.REST.toString))
@@ -802,9 +800,7 @@ object KyuubiConf {
.stringConf
.transformToUpperCase
.toSet()
- .checkValue(
- _.forall(AuthTypes.values.map(_.toString).contains),
- s"the authentication type should be one or more of
${AuthTypes.values.mkString(",")}")
+ .checkValues(AuthTypes)
.createWithDefault(Set(AuthTypes.NONE.toString))
val AUTHENTICATION_CUSTOM_CLASS: OptionalConfigEntry[String] =
@@ -1036,7 +1032,7 @@ object KyuubiConf {
.version("1.0.0")
.serverOnly
.stringConf
- .checkValues(SaslQOP.values.map(_.toString))
+ .checkValues(SaslQOP)
.transformToLowerCase
.createWithDefault(SaslQOP.AUTH.toString)
@@ -1926,7 +1922,7 @@ object KyuubiConf {
.version("1.0.0")
.stringConf
.transformToUpperCase
- .checkValues(ShareLevel.values.map(_.toString))
+ .checkValues(ShareLevel)
.createWithDefault(ShareLevel.USER.toString)
// [ZooKeeper Data Model]
@@ -2007,7 +2003,7 @@ object KyuubiConf {
.version("1.4.0")
.stringConf
.transformToUpperCase
- .checkValues(EngineType.values.map(_.toString))
+ .checkValues(EngineType)
.createWithDefault(EngineType.SPARK_SQL.toString)
val ENGINE_POOL_IGNORE_SUBDOMAIN: ConfigEntry[Boolean] =
@@ -2395,10 +2391,7 @@ object KyuubiConf {
.version("1.7.0")
.stringConf
.transformToUpperCase
- .checkValue(
- mode => Set("PLAIN", "JSON").contains(mode),
- "Invalid value for 'kyuubi.operation.plan.only.output.style'. Valid
values are " +
- "'plain', 'json'.")
+ .checkValues(Set("PLAIN", "JSON"))
.createWithDefault(PlainStyle.name)
val OPERATION_PLAN_ONLY_EXCLUDES: ConfigEntry[Set[String]] =
@@ -2446,7 +2439,7 @@ object KyuubiConf {
.version("1.5.0")
.stringConf
.transformToUpperCase
- .checkValues(OperationLanguages.values.map(_.toString))
+ .checkValues(OperationLanguages)
.createWithDefault(OperationLanguages.SQL.toString)
val SESSION_CONF_ADVISOR: OptionalConfigEntry[String] =
diff --git
a/kyuubi-common/src/test/scala/org/apache/kyuubi/config/ConfigBuilderSuite.scala
b/kyuubi-common/src/test/scala/org/apache/kyuubi/config/ConfigBuilderSuite.scala
index 86dc51155..78429d27c 100644
---
a/kyuubi-common/src/test/scala/org/apache/kyuubi/config/ConfigBuilderSuite.scala
+++
b/kyuubi-common/src/test/scala/org/apache/kyuubi/config/ConfigBuilderSuite.scala
@@ -18,6 +18,7 @@
package org.apache.kyuubi.config
import org.apache.kyuubi.KyuubiFunSuite
+import org.apache.kyuubi.util.AssertionUtils._
class ConfigBuilderSuite extends KyuubiFunSuite {
@@ -125,4 +126,21 @@ class ConfigBuilderSuite extends KyuubiFunSuite {
val e = intercept[IllegalArgumentException](kyuubiConf.get(intConf))
assert(e.getMessage equals "'-1' in kyuubi.invalid.config is invalid. must
be positive integer")
}
+
+ test("invalid config for enum") {
+ object TempEnum extends Enumeration {
+ type TempEnum = Value
+ val ValA, ValB = Value
+ }
+ val stringConf = ConfigBuilder("kyuubi.invalid.config.enum")
+ .stringConf
+ .checkValues(TempEnum)
+ .createWithDefault("ValA")
+ assert(stringConf.key === "kyuubi.invalid.config.enum")
+ assert(stringConf.defaultVal.get === "ValA")
+ val kyuubiConf = KyuubiConf().set(stringConf.key, "ValC")
+ KyuubiConf.register(stringConf)
+ interceptEquals[IllegalArgumentException] { kyuubiConf.get(stringConf) }(
+ "The value of kyuubi.invalid.config.enum should be one of ValA, ValB,
but was ValC")
+ }
}
diff --git
a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala
b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala
index 949342629..316c9b2df 100644
---
a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala
+++
b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala
@@ -25,6 +25,7 @@ import org.apache.thrift.transport.TSaslServerTransport
import org.apache.kyuubi.{KyuubiFunSuite, KyuubiSQLException}
import org.apache.kyuubi.config.KyuubiConf
import
org.apache.kyuubi.service.authentication.PlainSASLServer.SaslPlainProvider
+import org.apache.kyuubi.util.AssertionUtils._
import org.apache.kyuubi.util.KyuubiHadoopUtils
class KyuubiAuthenticationFactorySuite extends KyuubiFunSuite {
@@ -57,9 +58,9 @@ class KyuubiAuthenticationFactorySuite extends KyuubiFunSuite
{
test("AuthType Other") {
val conf = KyuubiConf().set(KyuubiConf.AUTHENTICATION_METHOD,
Set("INVALID"))
- val e = intercept[IllegalArgumentException](new
KyuubiAuthenticationFactory(conf))
- assert(e.getMessage contains "the authentication type should be one or
more of" +
- " NOSASL,NONE,LDAP,JDBC,KERBEROS,CUSTOM")
+ interceptEquals[IllegalArgumentException] { new
KyuubiAuthenticationFactory(conf) }(
+ "The value of kyuubi.authentication should be one of" +
+ " NOSASL, NONE, LDAP, JDBC, KERBEROS, CUSTOM, but was INVALID")
}
test("AuthType LDAP") {
diff --git
a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/SaslQOPSuite.scala
b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/SaslQOPSuite.scala
index c48f12aa7..6cf2793d2 100644
---
a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/SaslQOPSuite.scala
+++
b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/SaslQOPSuite.scala
@@ -34,7 +34,7 @@ class SaslQOPSuite extends KyuubiFunSuite {
val e = intercept[IllegalArgumentException](conf.get(SASL_QOP))
assert(e.getMessage ===
"The value of kyuubi.authentication.sasl.qop should be one of" +
- " auth, auth-conf, auth-int, but was abc")
+ " auth, auth-int, auth-conf, but was abc")
}
}
diff --git
a/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala
b/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala
index 28305ac52..626557008 100644
--- a/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala
+++ b/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala
@@ -79,7 +79,7 @@ object HighAvailabilityConf {
s"${AuthTypes.values.mkString("<ul><li>", "</li><li> ",
"</li></ul>")}")
.version("1.3.2")
.stringConf
- .checkValues(AuthTypes.values.map(_.toString))
+ .checkValues(AuthTypes)
.createWithDefault(AuthTypes.NONE.toString)
val HA_ZK_ENGINE_AUTH_TYPE: ConfigEntry[String] =
@@ -88,7 +88,7 @@ object HighAvailabilityConf {
s"${AuthTypes.values.mkString("<ul><li>", "</li><li> ",
"</li></ul>")}")
.version("1.3.2")
.stringConf
- .checkValues(AuthTypes.values.map(_.toString))
+ .checkValues(AuthTypes)
.createWithDefault(AuthTypes.NONE.toString)
val HA_ZK_AUTH_SERVER_PRINCIPAL: OptionalConfigEntry[String] =
@@ -160,7 +160,7 @@ object HighAvailabilityConf {
s" ${RetryPolicies.values.mkString("<ul><li>", "</li><li> ",
"</li></ul>")}")
.version("1.0.0")
.stringConf
- .checkValues(RetryPolicies.values.map(_.toString))
+ .checkValues(RetryPolicies)
.createWithDefault(RetryPolicies.EXPONENTIAL_BACKOFF.toString)
val HA_ZK_NODE_TIMEOUT: ConfigEntry[Long] =
diff --git
a/kyuubi-metrics/src/main/scala/org/apache/kyuubi/metrics/MetricsConf.scala
b/kyuubi-metrics/src/main/scala/org/apache/kyuubi/metrics/MetricsConf.scala
index b22aa8909..fe11f6eb1 100644
--- a/kyuubi-metrics/src/main/scala/org/apache/kyuubi/metrics/MetricsConf.scala
+++ b/kyuubi-metrics/src/main/scala/org/apache/kyuubi/metrics/MetricsConf.scala
@@ -45,9 +45,7 @@ object MetricsConf {
.stringConf
.transformToUpperCase
.toSet()
- .checkValue(
- _.forall(ReporterType.values.map(_.toString).contains),
- s"the reporter type should be one or more of
${ReporterType.values.mkString(",")}")
+ .checkValues(ReporterType)
.createWithDefault(Set(JSON.toString))
val METRICS_CONSOLE_INTERVAL: ConfigEntry[Long] =
buildConf("kyuubi.metrics.console.interval")
diff --git
a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/SaslQOPSuite.scala
b/kyuubi-util-scala/src/main/scala/org/apache/kyuubi/util/EnumUtils.scala
similarity index 52%
copy from
kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/SaslQOPSuite.scala
copy to kyuubi-util-scala/src/main/scala/org/apache/kyuubi/util/EnumUtils.scala
index c48f12aa7..0ab3be381 100644
---
a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/SaslQOPSuite.scala
+++ b/kyuubi-util-scala/src/main/scala/org/apache/kyuubi/util/EnumUtils.scala
@@ -14,27 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.apache.kyuubi.util
-package org.apache.kyuubi.service.authentication
+import scala.util.Try
-import org.apache.kyuubi.KyuubiFunSuite
-import org.apache.kyuubi.config.KyuubiConf
-import org.apache.kyuubi.config.KyuubiConf.SASL_QOP
-
-class SaslQOPSuite extends KyuubiFunSuite {
-
- test("sasl qop") {
- val conf = KyuubiConf(false)
- assert(conf.get(SASL_QOP) === SaslQOP.AUTH.toString)
- SaslQOP.values.foreach { q =>
- conf.set(SASL_QOP, q.toString)
- assert(SaslQOP.withName(conf.get(SASL_QOP)) === q)
- }
- conf.set(SASL_QOP, "abc")
- val e = intercept[IllegalArgumentException](conf.get(SASL_QOP))
- assert(e.getMessage ===
- "The value of kyuubi.authentication.sasl.qop should be one of" +
- " auth, auth-conf, auth-int, but was abc")
- }
+object EnumUtils {
+ def isValidEnum(enumeration: Enumeration, enumName: Any): Boolean = Try {
+ enumeration.withName(enumName.toString)
+ }.isSuccess
+ def isValidEnums(enumeration: Enumeration, enumNames: Iterable[Any]):
Boolean =
+ enumNames.forall(isValidEnum(enumeration, _))
}
diff --git
a/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/AssertionUtils.scala
b/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/AssertionUtils.scala
index a6cd9b862..e12eb1bd4 100644
---
a/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/AssertionUtils.scala
+++
b/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/AssertionUtils.scala
@@ -61,6 +61,7 @@ object AssertionUtils {
def interceptEquals[T <: Exception](f: => Any)(expected: String)(implicit
classTag: ClassTag[T],
pos: source.Position): Unit = {
+ assert(expected != null)
val exception = intercept[T](f)(classTag, pos)
assertResult(expected)(exception.getMessage)
}
@@ -72,6 +73,7 @@ object AssertionUtils {
def interceptContains[T <: Exception](f: => Any)(contained: String)(implicit
classTag: ClassTag[T],
pos: source.Position): Unit = {
+ assert(contained != null)
val exception = intercept[T](f)(classTag, pos)
assert(exception.getMessage.contains(contained))
}