This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/dev/pr-12843-93cd2407a32de85833f91fab88416ebce5d6a348 in repository https://gitbox.apache.org/repos/asf/seatunnel.git
commit 924810d8730a3619cf478bb96ddc6217ed0be7ec Author: Goutam Adwant <[email protected]> AuthorDate: Sun Oct 18 05:47:11 2026 +0000 [Fix][Connector-V2] Apply Qdrant API key to source and sink clients (#12843) --- docs/en/connectors/sink/Qdrant.md | 1 + docs/en/connectors/source/Qdrant.md | 1 + docs/zh/connectors/sink/Qdrant.md | 1 + docs/zh/connectors/source/Qdrant.md | 1 + .../seatunnel/qdrant/config/QdrantParameters.java | 6 ++- .../e2e/connector/v2/qdrant/QdrantIT.java | 42 +++++++++++++---- .../src/test/resources/qdrant-auth-to-qdrant.conf | 53 ++++++++++++++++++++++ 7 files changed, 95 insertions(+), 10 deletions(-) diff --git a/docs/en/connectors/sink/Qdrant.md b/docs/en/connectors/sink/Qdrant.md index 9f4b36d2fa..67ac73a637 100644 --- a/docs/en/connectors/sink/Qdrant.md +++ b/docs/en/connectors/sink/Qdrant.md @@ -50,6 +50,7 @@ The gRPC port of the Qdrant instance. ### api_key [string] The API key used to connect to authenticated Qdrant deployments. Leave it empty when the Qdrant service does not require authentication. +Set `use_tls = true` when sending an API key over an untrusted network; otherwise the key is sent over an unencrypted gRPC connection. ### use_tls [bool] diff --git a/docs/en/connectors/source/Qdrant.md b/docs/en/connectors/source/Qdrant.md index 5599cd801e..20a469340f 100644 --- a/docs/en/connectors/source/Qdrant.md +++ b/docs/en/connectors/source/Qdrant.md @@ -86,6 +86,7 @@ The gRPC port of the Qdrant instance. ### api_key [string] The API key used to connect to authenticated Qdrant deployments. Leave it empty when the Qdrant service does not require authentication. +Set `use_tls = true` when sending an API key over an untrusted network; otherwise the key is sent over an unencrypted gRPC connection. ### use_tls [bool] diff --git a/docs/zh/connectors/sink/Qdrant.md b/docs/zh/connectors/sink/Qdrant.md index 37eb4affc2..047e088582 100644 --- a/docs/zh/connectors/sink/Qdrant.md +++ b/docs/zh/connectors/sink/Qdrant.md @@ -50,6 +50,7 @@ Qdrant 实例的 gRPC 端口。 ### api_key [string] 连接需要认证的 Qdrant 部署时使用的 API key。如果 Qdrant 服务不需要认证,可以保持为空。 +通过不可信网络传输 API key 时,请设置 `use_tls = true`;否则密钥会通过未加密的 gRPC 连接发送。 ### use_tls [bool] diff --git a/docs/zh/connectors/source/Qdrant.md b/docs/zh/connectors/source/Qdrant.md index 5bfdb02c1d..7c60422be6 100644 --- a/docs/zh/connectors/source/Qdrant.md +++ b/docs/zh/connectors/source/Qdrant.md @@ -86,6 +86,7 @@ Qdrant 实例的 gRPC 端口。 ### api_key [string] 连接需要认证的 Qdrant 部署时使用的 API key。如果 Qdrant 服务不需要认证,可以保持为空。 +通过不可信网络传输 API key 时,请设置 `use_tls = true`;否则密钥会通过未加密的 gRPC 连接发送。 ### use_tls [bool] diff --git a/seatunnel-connectors-v2/connector-qdrant/src/main/java/org/apache/seatunnel/connectors/seatunnel/qdrant/config/QdrantParameters.java b/seatunnel-connectors-v2/connector-qdrant/src/main/java/org/apache/seatunnel/connectors/seatunnel/qdrant/config/QdrantParameters.java index a4b8940cec..4095e5dc4c 100644 --- a/seatunnel-connectors-v2/connector-qdrant/src/main/java/org/apache/seatunnel/connectors/seatunnel/qdrant/config/QdrantParameters.java +++ b/seatunnel-connectors-v2/connector-qdrant/src/main/java/org/apache/seatunnel/connectors/seatunnel/qdrant/config/QdrantParameters.java @@ -42,6 +42,10 @@ public class QdrantParameters implements Serializable { } public QdrantClient buildQdrantClient() { - return new QdrantClient(QdrantGrpcClient.newBuilder(host, port, useTls).build()); + QdrantGrpcClient.Builder builder = QdrantGrpcClient.newBuilder(host, port, useTls); + if (apiKey != null && !apiKey.isEmpty()) { + builder.withApiKey(apiKey); + } + return new QdrantClient(builder.build()); } } diff --git a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-qdrant-e2e/src/test/java/org/apache/seatunnel/e2e/connector/v2/qdrant/QdrantIT.java b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-qdrant-e2e/src/test/java/org/apache/seatunnel/e2e/connector/v2/qdrant/QdrantIT.java index ae883b2ab2..879038a247 100644 --- a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-qdrant-e2e/src/test/java/org/apache/seatunnel/e2e/connector/v2/qdrant/QdrantIT.java +++ b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-qdrant-e2e/src/test/java/org/apache/seatunnel/e2e/connector/v2/qdrant/QdrantIT.java @@ -60,6 +60,8 @@ import static io.qdrant.client.VectorsFactory.namedVectors; public class QdrantIT extends TestSuiteBase implements TestResource { private static final String ALIAS = "qdrante2e"; + private static final String AUTH_ALIAS = "qdrantauthe2e"; + private static final String AUTH_API_KEY = "seatunnel-test-key"; private static final String SOURCE_COLLECTION = "source_collection"; private static final String SINK_COLLECTION = "sink_collection"; /** @@ -69,16 +71,24 @@ public class QdrantIT extends TestSuiteBase implements TestResource { private static final String IMAGE = "qdrant/qdrant:v1.15.0"; private QdrantContainer container; + private QdrantContainer authenticatedContainer; private QdrantClient qdrantClient; + private QdrantClient authenticatedClient; @BeforeAll @Override public void startUp() throws Exception { this.container = new QdrantContainer(IMAGE).withNetwork(NETWORK).withNetworkAliases(ALIAS); - Startables.deepStart(Stream.of(this.container)).join(); + this.authenticatedContainer = + new QdrantContainer(IMAGE) + .withNetwork(NETWORK) + .withNetworkAliases(AUTH_ALIAS) + .withEnv("QDRANT__SERVICE__API_KEY", AUTH_API_KEY); + Startables.deepStart(Stream.of(this.container, this.authenticatedContainer)).join(); Awaitility.given().ignoreExceptions().await().atMost(10L, TimeUnit.SECONDS); this.initQdrant(); - this.initSourceData(); + this.initSourceData(qdrantClient); + this.initSourceData(authenticatedClient); } private void initQdrant() { @@ -87,11 +97,18 @@ public class QdrantIT extends TestSuiteBase implements TestResource { QdrantGrpcClient.newBuilder( container.getHost(), container.getGrpcPort(), false) .build()); + authenticatedClient = + new QdrantClient( + QdrantGrpcClient.newBuilder( + authenticatedContainer.getHost(), + authenticatedContainer.getGrpcPort(), + false) + .withApiKey(AUTH_API_KEY) + .build()); } - private void initSourceData() throws Exception { - qdrantClient - .createCollectionAsync( + private void initSourceData(QdrantClient client) throws Exception { + client.createCollectionAsync( SOURCE_COLLECTION, ImmutableMap.of( "my_vector", @@ -101,8 +118,7 @@ public class QdrantIT extends TestSuiteBase implements TestResource { .build())) .get(); - qdrantClient - .createCollectionAsync( + client.createCollectionAsync( SINK_COLLECTION, ImmutableMap.of( "my_vector", @@ -125,8 +141,7 @@ public class QdrantIT extends TestSuiteBase implements TestResource { points.add(pointStruct.build()); } - qdrantClient - .upsertAsync( + client.upsertAsync( Points.UpsertPoints.newBuilder() .setCollectionName(SOURCE_COLLECTION) .addAllPoints(points) @@ -138,6 +153,7 @@ public class QdrantIT extends TestSuiteBase implements TestResource { @Override public void tearDown() { this.qdrantClient.close(); + this.authenticatedClient.close(); } @TestTemplate @@ -147,4 +163,12 @@ public class QdrantIT extends TestSuiteBase implements TestResource { Assertions.assertEquals(0, execResult.getExitCode()); Assertions.assertEquals(10, qdrantClient.countAsync(SINK_COLLECTION).get()); } + + @TestTemplate + public void testQdrantWithApiKey(TestContainer container) + throws IOException, InterruptedException, ExecutionException { + Container.ExecResult execResult = container.executeJob("/qdrant-auth-to-qdrant.conf"); + Assertions.assertEquals(0, execResult.getExitCode()); + Assertions.assertEquals(10, authenticatedClient.countAsync(SINK_COLLECTION).get()); + } } diff --git a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-qdrant-e2e/src/test/resources/qdrant-auth-to-qdrant.conf b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-qdrant-e2e/src/test/resources/qdrant-auth-to-qdrant.conf new file mode 100644 index 0000000000..f0a623bfc6 --- /dev/null +++ b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-qdrant-e2e/src/test/resources/qdrant-auth-to-qdrant.conf @@ -0,0 +1,53 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +env { + parallelism = 1 + job.mode = "BATCH" +} + +source { + Qdrant { + collection_name = "source_collection" + host = "qdrantauthe2e" + api_key = "seatunnel-test-key" + schema = { + columns = [ + { + name = file_name + type = string + } + { + name = file_size + type = int + } + { + name = my_vector + type = float_vector + } + ] + } + } +} + +sink { + Qdrant { + collection_name = "sink_collection" + host = "qdrantauthe2e" + api_key = "seatunnel-test-key" + } +}
