[#11501] test(iceberg-rest): Add Trino IT for Iceberg REST authorization in auxMode - #11502
Conversation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a minimal RS256 client-credentials OAuth2 token endpoint for the Iceberg REST Catalog Trino IT, bound to 127.0.0.1 so a deploy-mode Gravitino server subprocess can reach it. Covered by a unit test that asserts the POST returns 200 with a valid signed JWT (subject + audience). Also adjust the module build for the JDK 24 toolchain: disable Error Prone 2.10.0 and JaCoCo 0.8.9 (both incompatible with JDK 24), align JUnit to the 6.0.0 BOM that Trino forces and add the matching platform-launcher, and pull only jjwt api+impl (the 0.13.x runtime uses jjwt-jackson from Trino; the stale jjwt-gson:0.11.x service breaks signing). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run the trino-iceberg-rest deploy-mode IT in its own job: it needs a Java 24 toolchain (Trino 478) and the built distribution, so it does not fit the JDK 17 catch-all backend IT. Gated by paths-filter on push/PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t -> REST The catch-all backend IT runs every module's tests in embedded mode, where this Java 24 / Trino module has no GravitinoServer on its classpath and fails with NoClassDefFoundError. Guard startIntegrationTest with ITUtils.isEmbedded() so the IT self-skips outside deploy mode, keeping it runnable from all deploy workflows. Also rename the new test classes Rest -> REST to match the codebase convention (IcebergRESTServer, IcebergRESTUtils, GravitinoIcebergRESTServer). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The backend and cron integration-test pipelines run every module's tests via the catch-all `./gradlew test`. Exclude :iceberg:iceberg-rest-trino-it:test from both so the Trino IRC IT is not run redundantly; it already has its own dedicated workflow (Java 24 / Trino, deploy only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # .github/workflows/backend-integration-test-action.yml
Code Coverage Report
|
| // incompatible with the 0.13.x runtime and makes signWith().compact() fail. | ||
| testImplementation(libs.jwt.api) | ||
| testRuntimeOnly(libs.jwt.impl) | ||
| testRuntimeOnly("io.jsonwebtoken:jjwt-jackson:0.13.0") |
There was a problem hiding this comment.
Should this be placed in the libs directory?
There was a problem hiding this comment.
Good catch. It turned out the explicit declaration was redundant: jjwt-jackson:0.13.0 is already provided transitively by trino-iceberg/trino-main/trino-testing (verified with dependencyInsight that it stays on the test runtime classpath). So I removed the line entirely — no hardcoded version and no new catalog entry needed. Separately, the remaining version literals in this module (testcontainers, docker-java, and the Trino version) are now centrally managed in gradle/libs.versions.toml.
| @Test | ||
| @Order(1) | ||
| public void testNormalUserDeniedWithoutPrivileges() { | ||
| assertThrows(RuntimeException.class, () -> sql(NORMAL_CATALOG, "SELECT * FROM db1.shared")); |
There was a problem hiding this comment.
Can you check the specific error message instead of throwing a RuntimeException?
There was a problem hiding this comment.
Done. Added assertAuthorizationDenied, which walks the exception cause chain and asserts the IRC 403 message "is not authorized to perform operation" (produced by BaseMetadataAuthorizationMethodInterceptor), applied to both denial tests.
| try { | ||
| super.stopIntegrationTest(); | ||
| } finally { | ||
| containerSuite.close(); |
There was a problem hiding this comment.
Closing ContainerSuite might affect other tests
There was a problem hiding this comment.
Agreed. Removed containerSuite.close() from teardown — it is a process-wide singleton whose PostgreSQL container may be reused by other tests in the same JVM. This matches the Spark analog IcebergAuthorizationIT, which does not close it either.
|
|
||
| // Test against the highest supported Trino version. | ||
| val minTrinoVersion = 473 | ||
| val maxTrinoVersion = 478 |
There was a problem hiding this comment.
Do we need to support multiple versions here?
There was a problem hiding this comment.
No — this is a test-only module that boots an in-process Trino query runner, so it only needs a single version. Removed the min/max range + -PtrinoVersion machinery and pinned the version, now sourced from the catalog (libs.versions.trino-iceberg-it).
| testImplementation(project(":common")) | ||
| testImplementation(project(":clients:client-java")) | ||
| testImplementation(project(":core")) | ||
| testImplementation(project(":server-common")) |
There was a problem hiding this comment.
Some dependencies may not be used.
There was a problem hiding this comment.
Some dependencies are required for BaseIT.
There was a problem hiding this comment.
Removed awaitility and commons-lang3 (no references). I also checked :common, but it is required at compile time (ConfigEntry/MetalakeDTO show up in the signatures of Configs/createMetalake), so it is kept.
There was a problem hiding this comment.
Correction: CI showed awaitility and commons-lang3 are actually not unused — the inherited BaseIT/ITUtils harness (from the integration-test-common testArtifacts dep) uses them at runtime, and testArtifacts does not carry transitive runtime deps, so removing them failed the ITs with NoClassDefFoundError: org/awaitility/Awaitility. I restored both (as testRuntimeOnly, since this module's own sources do not reference them) in 63ac498. :common remains required at compile time. Net result: the declared dependencies are all needed.
| tasks.test { | ||
| useJUnitPlatform() | ||
| // These ITs require the built distribution (deploy mode) and a server subprocess. | ||
| val skipITs = providers.gradleProperty("skipITs").map(String::toBoolean).orElse(false) | ||
| val skipTests = providers.gradleProperty("skipTests").map(String::toBoolean).orElse(false) | ||
| onlyIf { !skipITs.get() && !skipTests.get() } | ||
| } |
There was a problem hiding this comment.
Skipping here is inconsistent with other modules, which use project.hasProperty("skipITs").
There was a problem hiding this comment.
Done. Switched to project.hasProperty("skipITs") + exclude("**/integration/test/**"), consistent with iceberg-rest-server. This also fixes a latent issue: the old onlyIf skipped these ITs under -PskipTests, when they should still run.
- Pin a single Trino version instead of the multi-version range machinery
(this module boots an in-process query runner, unlike trino-connector).
- Drop the redundant explicit jjwt-jackson dependency; Trino already
provides it transitively at 0.13.x.
- Remove unused awaitility and commons-lang3 test dependencies.
- Use project.hasProperty("skipITs") + exclude, consistent with other
modules (also fixes ITs being wrongly skipped under -PskipTests).
- Assert the IRC 403 "not authorized" message in denial tests instead of
a bare RuntimeException.
- Do not close the shared ContainerSuite singleton in teardown, since its
PostgreSQL container may be reused by other tests in the same JVM.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… in the catalog Move the testcontainers, docker-java, and Trino version literals out of the module build file into gradle/libs.versions.toml so they are centrally managed, keeping the resolutionStrategy.force block (which is required to override the newer testcontainers/docker-java that Trino's test stack would otherwise pull). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
These are not unused: the inherited BaseIT/ITUtils harness (pulled via the integration-test-common testArtifacts dependency) uses Awaitility and commons-lang3 at runtime. testArtifacts only carries the compiled classes, not their transitive runtime deps, so the ITs failed in CI with NoClassDefFoundError: org/awaitility/Awaitility. Declared as testRuntimeOnly since this module's own sources do not reference them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ertion Two issues surfaced when the ITs actually ran in CI: - TrinoIcebergRESTSmokeIT failed with "schema db1 already exists": dropping the Gravitino metalake does not remove namespaces from the shared PostgreSQL-backed Iceberg catalog, and removing containerSuite.close() (which previously reset it) left them behind. Now drop the catalog's schemas in teardown instead of tearing down the shared ContainerSuite. - testNormalUserDeniedWithoutPrivileges failed because Trino masks the REST 403 body on the read path and reports "Failed to load view" rather than Gravitino's authorization message (which it does surface for CREATE TABLE). Accept either form as evidence of a denial. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TrinoIcebergRESTSmokeIT and TrinoIcebergRESTAuthorizationIT share one
PostgreSQL-backed Iceberg catalog (the shared ContainerSuite is intentionally
not reset between classes). Both used schema "db1", so the second class to run
failed with "schema db1 already exists". Give the smoke test its own schema
("smoke_db") so the classes cannot collide, and drop the unreliable teardown
schema cleanup in favor of this deterministic isolation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What changes were proposed in this pull request?
Add a new test-only module
iceberg/iceberg-rest-trino-itthat verifies Gravitino's authorization is enforced when an Iceberg REST Catalog served in auxiliary mode (auxMode) is accessed through Trino's nativeicebergREST connector. This is the Trino analog of the Spark-basedIcebergAuthorizationIT.Specifically:
build.gradle.kts+settings.gradle.kts— scaffold the module on a Java 24 toolchain withtrino-iceberg:478/trino-testing:478, mirroringtrino-connector-473-478(Error Prone / JaCoCo disabled for JDK 24, JUnit aligned to Trino's 6.0.0 BOM, jjwt api+impl only).MockOAuthTokenServer(+TestMockOAuthTokenServer) — a minimal RS256 client-credentials OAuth2 token endpoint bound to127.0.0.1, used by the IRC dynamic config provider's service identity. Covered by a unit test asserting a valid signed JWT is returned.TrinoIcebergRestAuthorizationITBase/TrinoIcebergRestAuthorizationIT/TrinoIcebergRestSmokeIT— boot the full Gravitino server in deploy mode with theiceberg-restaux service + authorization + OAuth authenticator, and drive an in-process Trino query runner to assert privilege grant/deny.Why are the changes needed?
The existing
IcebergAuthorizationITonly covers the Spark path. Trino accesses the IRC over a different authentication path (OAuth2 bearer only, no Gravitino Basic auth), which was previously untested. This adds end-to-end coverage that Gravitino authorization is enforced for Trino IRC clients.Fix: #11501
Does this PR introduce any user-facing change?
No. Test-only changes; no new APIs or property keys.
How was this patch tested?
This change is an integration test. Run with the built distribution in deploy mode:
MockOAuthTokenServeris additionally covered byTestMockOAuthTokenServer.🤖 Generated with Claude Code