This is an automated email from the ASF dual-hosted git repository.
kfaraz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new b8e11706e61 test: Fix flaky `BasicAuthMSQTest` (#19593)
b8e11706e61 is described below
commit b8e11706e61054889b5f2061ac7f67e0dbf2a236
Author: Andreas Maechler <[email protected]>
AuthorDate: Tue Jun 23 00:52:00 2026 -0600
test: Fix flaky `BasicAuthMSQTest` (#19593)
Retry the task submission while it fails with these transient auth
errors so the assertions only run once the Broker's auth cache reflects
the test setup. Other failures are not retried, so real errors still
fail fast.
---
.../testing/embedded/auth/BasicAuthMSQTest.java | 122 ++++++++++++++-------
1 file changed, 81 insertions(+), 41 deletions(-)
diff --git
a/embedded-tests/src/test/java/org/apache/druid/testing/embedded/auth/BasicAuthMSQTest.java
b/embedded-tests/src/test/java/org/apache/druid/testing/embedded/auth/BasicAuthMSQTest.java
index 87012ce2c7b..46210caec44 100644
---
a/embedded-tests/src/test/java/org/apache/druid/testing/embedded/auth/BasicAuthMSQTest.java
+++
b/embedded-tests/src/test/java/org/apache/druid/testing/embedded/auth/BasicAuthMSQTest.java
@@ -21,6 +21,7 @@ package org.apache.druid.testing.embedded.auth;
import com.google.common.collect.ImmutableList;
import org.apache.druid.error.ExceptionMatcher;
+import org.apache.druid.java.util.common.RetryUtils;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.metadata.DefaultPasswordProvider;
import org.apache.druid.query.http.ClientSqlQuery;
@@ -58,6 +59,12 @@ public class BasicAuthMSQTest extends EmbeddedClusterTestBase
public static final String ROLE_1 = "role1";
public static final String USER_1_PASSWORD = "password1";
+ /**
+ * Attempts allowed for a request while basic-auth changes reach the broker,
which the
+ * coordinator then propagates asynchronously.
+ */
+ private static final int AUTH_PROPAGATION_ATTEMPTS = 5;
+
private SecurityClient securityClient;
private EmbeddedServiceClient userClient;
@@ -134,7 +141,7 @@ public class BasicAuthMSQTest extends
EmbeddedClusterTestBase
}
@Test
- public void testIngestionWithPermissions()
+ public void testIngestionWithPermissions() throws Exception
{
List<ResourceAction> permissions = ImmutableList.of(
new ResourceAction(new Resource(".*", "DATASOURCE"), Action.READ),
@@ -150,11 +157,7 @@ public class BasicAuthMSQTest extends
EmbeddedClusterTestBase
Resources.DataFile.tinyWiki1Json()
);
- final SqlTaskStatus taskStatus = userClient.onAnyBroker(
- b -> b.submitSqlTask(
- new ClientSqlQuery(queryLocal, null, false, false, false,
Map.of(), List.of())
- )
- );
+ final SqlTaskStatus taskStatus = submitSqlTaskWhenAuthorized(queryLocal);
cluster.callApi().waitForTaskToSucceed(taskStatus.getTaskId(), overlord);
}
@@ -173,16 +176,18 @@ public class BasicAuthMSQTest extends
EmbeddedClusterTestBase
String exportQuery =
StringUtils.format(
- "INSERT INTO extern(%s(exportPath => '%s'))\n"
- + "AS CSV\n"
- + "SELECT page, added, delta\n"
- + "FROM TABLE(\n"
- + " EXTERN(\n"
- + " '{\"type\":\"local\",\"files\":[\"%s\"]}',\n"
- + " '{\"type\":\"json\"}',\n"
- + "
'[{\"type\":\"string\",\"name\":\"timestamp\"},{\"type\":\"string\",\"name\":\"isRobot\"},{\"type\":\"string\",\"name\":\"diffUrl\"},{\"type\":\"long\",\"name\":\"added\"},{\"type\":\"string\",\"name\":\"countryIsoCode\"},{\"type\":\"string\",\"name\":\"regionName\"},{\"type\":\"string\",\"name\":\"channel\"},{\"type\":\"string\",\"name\":\"flags\"},{\"type\":\"long\",\"name\":\"delta\"},{\"type\":\"string\",\"name\":\"isUnpatrolled\"},{\"type\":\"string\",\"name\":\"i
[...]
- + " )\n"
- + ")\n",
+ """
+ INSERT INTO extern(%s(exportPath => '%s'))
+ AS CSV
+ SELECT page, added, delta
+ FROM TABLE(
+ EXTERN(
+ '{"type":"local","files":["%s"]}',
+ '{"type":"json"}',
+
'[{"type":"string","name":"timestamp"},{"type":"string","name":"isRobot"},{"type":"string","name":"diffUrl"},{"type":"long","name":"added"},{"type":"string","name":"countryIsoCode"},{"type":"string","name":"regionName"},{"type":"string","name":"channel"},{"type":"string","name":"flags"},{"type":"long","name":"delta"},{"type":"string","name":"isUnpatrolled"},{"type":"string","name":"isNew"},{"type":"double","name":"deltaBucket"},{"type":"string","name":"isMinor"},{"typ
[...]
+ )
+ )
+ """,
LocalFileExportStorageProvider.TYPE_NAME,
cluster.getTestFolder().getOrCreateFolder("msq-export").getAbsolutePath(),
Resources.DataFile.tinyWiki1Json().getAbsolutePath()
@@ -192,7 +197,7 @@ public class BasicAuthMSQTest extends
EmbeddedClusterTestBase
}
@Test
- public void testExportWithPermissions()
+ public void testExportWithPermissions() throws Exception
{
// No external write permissions for s3
List<ResourceAction> permissions = ImmutableList.of(
@@ -206,41 +211,76 @@ public class BasicAuthMSQTest extends
EmbeddedClusterTestBase
String exportQuery =
StringUtils.format(
- "INSERT INTO extern(%s(exportPath => '%s'))\n"
- + "AS CSV\n"
- + "SELECT page, added, delta\n"
- + "FROM TABLE(\n"
- + " EXTERN(\n"
- + " '{\"type\":\"local\",\"files\":[\"%s\"]}',\n"
- + " '{\"type\":\"json\"}',\n"
- + "
'[{\"type\":\"string\",\"name\":\"timestamp\"},{\"type\":\"string\",\"name\":\"isRobot\"},{\"type\":\"string\",\"name\":\"diffUrl\"},{\"type\":\"long\",\"name\":\"added\"},{\"type\":\"string\",\"name\":\"countryIsoCode\"},{\"type\":\"string\",\"name\":\"regionName\"},{\"type\":\"string\",\"name\":\"channel\"},{\"type\":\"string\",\"name\":\"flags\"},{\"type\":\"long\",\"name\":\"delta\"},{\"type\":\"string\",\"name\":\"isUnpatrolled\"},{\"type\":\"string\",\"name\":\"i
[...]
- + " )\n"
- + ")\n",
+ """
+ INSERT INTO extern(%s(exportPath => '%s'))
+ AS CSV
+ SELECT page, added, delta
+ FROM TABLE(
+ EXTERN(
+ '{"type":"local","files":["%s"]}',
+ '{"type":"json"}',
+
'[{"type":"string","name":"timestamp"},{"type":"string","name":"isRobot"},{"type":"string","name":"diffUrl"},{"type":"long","name":"added"},{"type":"string","name":"countryIsoCode"},{"type":"string","name":"regionName"},{"type":"string","name":"channel"},{"type":"string","name":"flags"},{"type":"long","name":"delta"},{"type":"string","name":"isUnpatrolled"},{"type":"string","name":"isNew"},{"type":"double","name":"deltaBucket"},{"type":"string","name":"isMinor"},{"typ
[...]
+ )
+ )
+ """,
LocalFileExportStorageProvider.TYPE_NAME,
new File(exportDirectory.get(), dataSource).getAbsolutePath(),
Resources.DataFile.tinyWiki1Json()
);
- final SqlTaskStatus taskStatus = userClient.onAnyBroker(
+ final SqlTaskStatus taskStatus = submitSqlTaskWhenAuthorized(exportQuery);
+ cluster.callApi().waitForTaskToSucceed(taskStatus.getTaskId(), overlord);
+ }
+
+ /**
+ * Submits an MSQ task to an arbitrary Broker as {@link #USER_1}.
+ */
+ private SqlTaskStatus submitSqlTaskAsUser(String sql)
+ {
+ return userClient.onAnyBroker(
b -> b.submitSqlTask(
- new ClientSqlQuery(exportQuery, null, false, false, false,
Map.of(), List.of())
+ new ClientSqlQuery(sql, null, false, false, false, Map.of(),
List.of())
)
);
- cluster.callApi().waitForTaskToSucceed(taskStatus.getTaskId(), overlord);
}
- private void verifySqlSubmitFailsWith403Forbidden(String sql)
+ /**
+ * Submits an MSQ task as {@link #USER_1}.
+ */
+ private SqlTaskStatus submitSqlTaskWhenAuthorized(String sql) throws
Exception
{
- MatcherAssert.assertThat(
- Assertions.assertThrows(
- Exception.class,
- () -> userClient.onAnyBroker(
- b -> b.submitSqlTask(
- new ClientSqlQuery(sql, null, false, false, false,
Map.of(), List.of())
- )
- )
- ),
- ExceptionMatcher.of(Exception.class).expectMessageContains("403
Forbidden")
+ return RetryUtils.retry(
+ () -> submitSqlTaskAsUser(sql),
+ e -> unauthorizedExceptionMatcher().matches(e) ||
forbiddenExceptionMatcher().matches(e),
+ AUTH_PROPAGATION_ATTEMPTS
);
}
+
+ /**
+ * Asserts that submitting SQL as an unauthorized user fails with 403
Forbidden.
+ */
+ private void verifySqlSubmitFailsWith403Forbidden(String sql)
+ {
+ try {
+ RetryUtils.retry(
+ () -> submitSqlTaskAsUser(sql),
+ e -> unauthorizedExceptionMatcher().matches(e),
+ AUTH_PROPAGATION_ATTEMPTS
+ );
+ Assertions.fail("Expected submit to fail with 403 Forbidden");
+ }
+ catch (Exception e) {
+ MatcherAssert.assertThat(e, forbiddenExceptionMatcher());
+ }
+ }
+
+ private static ExceptionMatcher unauthorizedExceptionMatcher()
+ {
+ return ExceptionMatcher.of(Exception.class).expectMessageContains("401
Unauthorized");
+ }
+
+ private static ExceptionMatcher forbiddenExceptionMatcher()
+ {
+ return ExceptionMatcher.of(Exception.class).expectMessageContains("403
Forbidden");
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]