You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by mg...@apache.org on 2026/09/25 10:48:26 UTC
(kudu) branch master updated: [spark] KuduSink: honor auth and encryption opts
This is an automated email from the ASF dual-hosted git repository.
martongreber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new 102683f65 [spark] KuduSink: honor auth and encryption opts
102683f65 is described below
commit 102683f65af844d269565345d7d8c6ebc55c92c5
Author: Marton Greber <gr...@gmail.com>
AuthorDate: Fri Sep 4 12:11:19 2026 +0200
[spark] KuduSink: honor auth and encryption opts
KuduSink captured requireAuthentication and encryptionPolicy in its
constructor but never passed them to the KuduContext it created, so the
context always fell back to the defaults (no authentication required,
OPTIONAL encryption). As a result streaming writes silently ignored the
kudu.requireAuthentication and kudu.encryptionPolicy options, unlike the
batch read path in KuduRelation which forwards them correctly.
Forward both options into the KuduContext, matching KuduRelation.
Add StreamingTest coverage against an insecure cluster: requiring
authentication or encryption must now fail the write instead of
silently proceeding; both new tests fail without the fix.
Co-Authored-By: Claude Opus 4.8
Change-Id: I661a45fe327d2d8adca5898f211b14233d5d813f
Reviewed-on: http://gerrit.cloudera.org:8080/24791
Tested-by: Kudu Jenkins
Reviewed-by: Gabriella Lotz <lo...@gmail.com>
Reviewed-by: Zoltan Martonka <zm...@cloudera.com>
---
.../org/apache/kudu/spark/kudu/DefaultSource.scala | 8 ++-
.../org/apache/kudu/spark/kudu/StreamingTest.scala | 75 ++++++++++++++++++++++
2 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/DefaultSource.scala b/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/DefaultSource.scala
index 6d9c0c996..86a51c56b 100644
--- a/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/DefaultSource.scala
+++ b/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/DefaultSource.scala
@@ -547,7 +547,13 @@ class KuduSink(
extends Sink {
private val context: KuduContext =
- new KuduContext(masterAddrs, sqlContext.sparkContext, None, Some(saslProtocolName))
+ new KuduContext(
+ masterAddrs,
+ sqlContext.sparkContext,
+ None,
+ Some(saslProtocolName),
+ requireAuthentication,
+ encryptionPolicy)
override def addBatch(batchId: Long, data: DataFrame): Unit = {
context.writeRows(data, tableName, operationType, writeOptions)
diff --git a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/StreamingTest.scala b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/StreamingTest.scala
index 1e2eaab2e..ff79d0cae 100644
--- a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/StreamingTest.scala
+++ b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/StreamingTest.scala
@@ -17,10 +17,14 @@
package org.apache.kudu.spark.kudu
+import org.apache.kudu.test.KuduTestHarness.MasterServerConfig
+import org.apache.kudu.test.KuduTestHarness.TabletServerConfig
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.execution.streaming._
import org.apache.spark.sql.streaming.OutputMode
+import org.apache.spark.sql.streaming.StreamingQuery
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -69,4 +73,75 @@ class StreamingTest extends KuduTestSuite {
verifyOutput(expectedData = Seq((2, "1"), (3, "2"), (4, "3")))
query.stop()
}
+
+ /**
+ * Runs a streaming write to the Kudu table with the given extra options and
+ * asserts that it fails with an error whose (possibly nested) message contains
+ * `expectedMessage`.
+ */
+ private def assertStreamingWriteFails(
+ extraOptions: Map[String, String],
+ expectedMessage: String): Unit = {
+ KuduClientCache.clearCacheForTests()
+ val spark = ss
+ import spark.implicits._
+ val checkpointDir = java.nio.file.Files.createTempDirectory("spark_kudu")
+ val input = MemoryStream[Int]
+ input.addData(1, 2, 3)
+ var query: StreamingQuery = null
+ val exception =
+ try {
+ query = input
+ .toDS()
+ .map(v => (v + 1, v.toString))
+ .toDF("key", "val")
+ .writeStream
+ .format("kudu")
+ .option("kudu.master", harness.getMasterAddressesAsString)
+ .option("kudu.table", simpleTableName)
+ .option("checkpointLocation", checkpointDir.toFile.getCanonicalPath)
+ .options(extraOptions)
+ .outputMode(OutputMode.Update)
+ .start()
+ query.processAllAvailable()
+ None
+ } catch {
+ case e: Throwable => Some(e)
+ } finally {
+ if (query != null) query.stop()
+ }
+ assertTrue("expected the streaming write to fail", exception.isDefined)
+ val messages = Iterator
+ .iterate(exception.get)(_.getCause)
+ .takeWhile(_ != null)
+ .flatMap(e => Option(e.getMessage))
+ .mkString(" | ")
+ assertTrue(s"unexpected exception: $messages", messages.contains(expectedMessage))
+ }
+
+ /**
+ * Verifies that the KuduSink honors the "kudu.requireAuthentication" option.
+ * Against an insecure cluster, requiring authentication must fail the write
+ * rather than silently falling back to the default (no authentication).
+ */
+ @Test
+ def testKuduRequireAuthenticationInsecureClusterStreaming() {
+ assertStreamingWriteFails(
+ Map("kudu.requireAuthentication" -> "true"),
+ "client requires authentication, but server does not have Kerberos enabled")
+ }
+
+ /**
+ * Verifies that the KuduSink honors the "kudu.encryptionPolicy" option.
+ * Against a cluster with RPC encryption disabled, requiring encryption must
+ * fail the write rather than silently falling back to the default (OPTIONAL).
+ */
+ @Test
+ @MasterServerConfig(flags = Array("--rpc_encryption=disabled", "--rpc_authentication=disabled"))
+ @TabletServerConfig(flags = Array("--rpc_encryption=disabled", "--rpc_authentication=disabled"))
+ def testKuduRequireEncryptionInsecureClusterStreaming() {
+ assertStreamingWriteFails(
+ Map("kudu.encryptionPolicy" -> "required_remote"),
+ "server does not support required TLS encryption")
+ }
}