You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polaris.apache.org by sn...@apache.org on 2026/08/20 09:30:10 UTC
(polaris) branch main updated: Remove MinIO based tests and minio-testcontainer (#5192)
This is an automated email from the ASF dual-hosted git repository.
snazy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git
The following commit(s) were added to refs/heads/main by this push:
new f0a1907718 Remove MinIO based tests and minio-testcontainer (#5192)
f0a1907718 is described below
commit f0a1907718a826586b0481e05bb28c9b6ee11d37
Author: Robert Stupp <sn...@snazy.de>
AuthorDate: Thu Aug 20 11:30:05 2026 +0200
Remove MinIO based tests and minio-testcontainer (#5192)
Minio is no longer maintained. The independently added Floci based tests exercise the same test cases, so removing Minio based tests and the related testcontainer appears appropriate.
---
LICENSE | 4 -
README.md | 2 +-
bom/build.gradle.kts | 1 -
gradle/projects.main.properties | 1 -
integration-tests/build.gradle.kts | 1 -
runtime/service/build.gradle.kts | 1 -
.../service/it/PolarisRestCatalogMinIOIT.java | 94 ----
.../service/it/RestCatalogMinIOSpecialIT.java | 594 ---------------------
.../minio-testcontainer/build.gradle.kts | 42 --
.../java/org/apache/polaris/test/minio/Minio.java | 46 --
.../org/apache/polaris/test/minio/MinioAccess.java | 68 ---
.../test/minio/MinioConditionExtension.java | 46 --
.../apache/polaris/test/minio/MinioContainer.java | 276 ----------
.../apache/polaris/test/minio/MinioExtension.java | 120 -----
.../polaris/test/minio/MinioTestResource.java | 65 ---
.../polaris/test/minio/Dockerfile-minio-version | 22 -
16 files changed, 1 insertion(+), 1382 deletions(-)
diff --git a/LICENSE b/LICENSE
index f876e29e58..239dc6dadb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -326,10 +326,6 @@ This product includes code from Project Nessie.
* tools/config-docs/generator/src/test/java/tests/smallrye/SomeEnum.java
* tools/config-docs/generator/src/test/java/tests/smallrye/VeryNested.java
* tools/container-spec-helper/src/main/java/org/apache/polaris/containerspec/ContainerSpecHelper.java
-* tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/Minio.java
-* tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioAccess.java
-* tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioContainer.java
-* tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioExtension.java
* tools/testcontainers/rustfs-testcontainer/src/main/java/org/apache/polaris/test/rustfs/Rustfs.java
* tools/testcontainers/rustfs-testcontainer/src/main/java/org/apache/polaris/test/rustfs/RustfsAccess.java
* tools/testcontainers/rustfs-testcontainer/src/main/java/org/apache/polaris/test/rustfs/RustfsContainer.java
diff --git a/README.md b/README.md
index 2adc791807..d4fd14f3d7 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,7 @@ The repository is organized into the following groups of Gradle modules. Each le
- `polaris-bom`, `polaris-build-logic`, `polaris-version` - BOM, shared build logic, versioning
- `polaris-immutables`, `polaris-misc-types`, `polaris-container-spec-helper` - shared utilities
- `polaris-floci-aws-testcontainer`, `polaris-floci-az-testcontainer`, `polaris-floci-gcp-testcontainer`,
- `polaris-minio-testcontainer`, `polaris-rustfs-testcontainer`, `polaris-hms-testcontainer` - test containers
+ `polaris-rustfs-testcontainer`, `polaris-hms-testcontainer` - test containers
- `polaris-config-docs-{annotations,generator,site}` - reference-doc generation
- `aggregated-license-report` - aggregated license report
- **Tests**
diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts
index ee9b2a4bda..05808f668b 100644
--- a/bom/build.gradle.kts
+++ b/bom/build.gradle.kts
@@ -36,7 +36,6 @@ dependencies {
api(project(":polaris-floci-az-testcontainer"))
api(project(":polaris-floci-gcp-testcontainer"))
api(project(":polaris-keycloak-testcontainer"))
- api(project(":polaris-minio-testcontainer"))
api(project(":polaris-rustfs-testcontainer"))
api(project(":polaris-immutables"))
api(project(":polaris-misc-types"))
diff --git a/gradle/projects.main.properties b/gradle/projects.main.properties
index f691d5782a..8d62143cbc 100644
--- a/gradle/projects.main.properties
+++ b/gradle/projects.main.properties
@@ -41,7 +41,6 @@ polaris-floci-aws-testcontainer=tools/testcontainers/floci-aws
polaris-floci-az-testcontainer=tools/testcontainers/floci-az
polaris-floci-gcp-testcontainer=tools/testcontainers/floci-gcp
polaris-keycloak-testcontainer=tools/testcontainers/keycloak
-polaris-minio-testcontainer=tools/testcontainers/minio-testcontainer
polaris-rustfs-testcontainer=tools/testcontainers/rustfs-testcontainer
polaris-hms-testcontainer=tools/testcontainers/hms-testcontainer
polaris-version=tools/version
diff --git a/integration-tests/build.gradle.kts b/integration-tests/build.gradle.kts
index 7d8e2c3d89..451884c81d 100644
--- a/integration-tests/build.gradle.kts
+++ b/integration-tests/build.gradle.kts
@@ -81,7 +81,6 @@ dependencies {
implementation(libs.hawkular.agent.prometheus.scraper)
implementation(libs.s3mock.testcontainers)
implementation(project(":polaris-runtime-test-common"))
- implementation(project(":polaris-minio-testcontainer"))
implementation(project(":polaris-rustfs-testcontainer"))
implementation(project(":polaris-hms-testcontainer"))
}
diff --git a/runtime/service/build.gradle.kts b/runtime/service/build.gradle.kts
index e85068a982..f9d588399e 100644
--- a/runtime/service/build.gradle.kts
+++ b/runtime/service/build.gradle.kts
@@ -134,7 +134,6 @@ dependencies {
testImplementation(project(":polaris-api-management-model"))
testImplementation(project(":polaris-relational-jdbc"))
- testImplementation(project(":polaris-minio-testcontainer"))
testImplementation(project(":polaris-rustfs-testcontainer"))
testImplementation("org.apache.iceberg:iceberg-api:${libs.versions.iceberg.get()}:tests")
diff --git a/runtime/service/src/intTest/java/org/apache/polaris/service/it/PolarisRestCatalogMinIOIT.java b/runtime/service/src/intTest/java/org/apache/polaris/service/it/PolarisRestCatalogMinIOIT.java
deleted file mode 100644
index 50d7b8e811..0000000000
--- a/runtime/service/src/intTest/java/org/apache/polaris/service/it/PolarisRestCatalogMinIOIT.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-package org.apache.polaris.service.it;
-
-import static org.apache.polaris.test.commons.MinioRustProfile.ACCESS_KEY;
-import static org.apache.polaris.test.commons.MinioRustProfile.SECRET_KEY;
-
-import com.google.common.collect.ImmutableMap;
-import io.quarkus.test.common.QuarkusTestResource;
-import io.quarkus.test.common.ResourceArg;
-import io.quarkus.test.junit.QuarkusIntegrationTest;
-import io.quarkus.test.junit.TestProfile;
-import java.util.List;
-import java.util.Map;
-import org.apache.iceberg.aws.s3.S3FileIOProperties;
-import org.apache.polaris.core.admin.model.AwsStorageConfigInfo;
-import org.apache.polaris.core.admin.model.StorageConfigInfo;
-import org.apache.polaris.service.it.env.RestCatalogConfig;
-import org.apache.polaris.service.it.ext.PolarisIntegrationTestExtension;
-import org.apache.polaris.service.it.test.PolarisRestCatalogIntegrationBase;
-import org.apache.polaris.test.commons.MinioRustProfile;
-import org.apache.polaris.test.minio.Minio;
-import org.apache.polaris.test.minio.MinioAccess;
-import org.apache.polaris.test.minio.MinioConditionExtension;
-import org.apache.polaris.test.minio.MinioTestResource;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-@QuarkusIntegrationTest
-@TestProfile(MinioRustProfile.class)
-@QuarkusTestResource(
- value = MinioTestResource.class,
- initArgs = {
- @ResourceArg(name = "accessKey", value = ACCESS_KEY),
- @ResourceArg(name = "secretKey", value = SECRET_KEY)
- })
-@ExtendWith(MinioConditionExtension.class)
-@ExtendWith(PolarisIntegrationTestExtension.class)
-@RestCatalogConfig({"header.X-Iceberg-Access-Delegation", "vended-credentials"})
-public class PolarisRestCatalogMinIOIT extends PolarisRestCatalogIntegrationBase {
-
- protected static final String BUCKET_URI_PREFIX = "/minio-test-polaris";
-
- @Minio static MinioAccess minioAccess;
-
- private static Map<String, String> s3Properties;
-
- @BeforeEach
- void setup() {
- s3Properties =
- Map.of(
- S3FileIOProperties.ENDPOINT,
- minioAccess.s3endpoint(),
- S3FileIOProperties.PATH_STYLE_ACCESS,
- "true",
- S3FileIOProperties.ACCESS_KEY_ID,
- ACCESS_KEY,
- S3FileIOProperties.SECRET_ACCESS_KEY,
- SECRET_KEY);
- }
-
- @Override
- protected ImmutableMap.Builder<String, String> clientFileIOProperties() {
- return super.clientFileIOProperties().putAll(s3Properties);
- }
-
- @Override
- protected StorageConfigInfo getStorageConfigInfo() {
- AwsStorageConfigInfo.Builder storageConfig =
- AwsStorageConfigInfo.builder()
- .setStorageType(StorageConfigInfo.StorageTypeEnum.S3)
- .setPathStyleAccess(true)
- .setEndpoint(minioAccess.s3endpoint())
- .setAllowedLocations(List.of(minioAccess.s3BucketUri(BUCKET_URI_PREFIX).toString()));
-
- return storageConfig.build();
- }
-}
diff --git a/runtime/service/src/intTest/java/org/apache/polaris/service/it/RestCatalogMinIOSpecialIT.java b/runtime/service/src/intTest/java/org/apache/polaris/service/it/RestCatalogMinIOSpecialIT.java
deleted file mode 100644
index 7dda278b3a..0000000000
--- a/runtime/service/src/intTest/java/org/apache/polaris/service/it/RestCatalogMinIOSpecialIT.java
+++ /dev/null
@@ -1,594 +0,0 @@
-/*
- * 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.
- */
-package org.apache.polaris.service.it;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.iceberg.CatalogProperties.TABLE_DEFAULT_PREFIX;
-import static org.apache.iceberg.aws.AwsClientProperties.REFRESH_CREDENTIALS_ENDPOINT;
-import static org.apache.iceberg.aws.s3.S3FileIOProperties.ACCESS_KEY_ID;
-import static org.apache.iceberg.aws.s3.S3FileIOProperties.ENDPOINT;
-import static org.apache.iceberg.aws.s3.S3FileIOProperties.SECRET_ACCESS_KEY;
-import static org.apache.iceberg.types.Types.NestedField.optional;
-import static org.apache.iceberg.types.Types.NestedField.required;
-import static org.apache.polaris.core.storage.StorageAccessProperty.AWS_KEY_ID;
-import static org.apache.polaris.core.storage.StorageAccessProperty.AWS_SECRET_KEY;
-import static org.apache.polaris.service.catalog.AccessDelegationMode.VENDED_CREDENTIALS;
-import static org.apache.polaris.service.it.env.PolarisClient.polarisClient;
-import static org.apache.polaris.test.commons.MinioRustProfile.ACCESS_KEY;
-import static org.apache.polaris.test.commons.MinioRustProfile.SECRET_KEY;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import com.google.common.collect.ImmutableMap;
-import io.quarkus.test.common.QuarkusTestResource;
-import io.quarkus.test.common.ResourceArg;
-import io.quarkus.test.junit.QuarkusIntegrationTest;
-import io.quarkus.test.junit.TestProfile;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import org.apache.iceberg.DataFile;
-import org.apache.iceberg.DataFiles;
-import org.apache.iceberg.FileFormat;
-import org.apache.iceberg.HasTableOperations;
-import org.apache.iceberg.PartitionSpec;
-import org.apache.iceberg.Schema;
-import org.apache.iceberg.Table;
-import org.apache.iceberg.TableOperations;
-import org.apache.iceberg.catalog.TableIdentifier;
-import org.apache.iceberg.io.FileIO;
-import org.apache.iceberg.io.OutputFile;
-import org.apache.iceberg.io.PositionOutputStream;
-import org.apache.iceberg.rest.RESTCatalog;
-import org.apache.iceberg.rest.auth.OAuth2Properties;
-import org.apache.iceberg.rest.responses.LoadTableResponse;
-import org.apache.iceberg.types.Types;
-import org.apache.polaris.core.admin.model.AwsStorageConfigInfo;
-import org.apache.polaris.core.admin.model.Catalog;
-import org.apache.polaris.core.admin.model.CatalogProperties;
-import org.apache.polaris.core.admin.model.PolarisCatalog;
-import org.apache.polaris.core.admin.model.PrincipalWithCredentials;
-import org.apache.polaris.core.admin.model.StorageConfigInfo;
-import org.apache.polaris.service.catalog.AccessDelegationMode;
-import org.apache.polaris.service.it.env.CatalogApi;
-import org.apache.polaris.service.it.env.ClientCredentials;
-import org.apache.polaris.service.it.env.ManagementApi;
-import org.apache.polaris.service.it.env.PolarisApiEndpoints;
-import org.apache.polaris.service.it.env.PolarisClient;
-import org.apache.polaris.service.it.ext.PolarisIntegrationTestExtension;
-import org.apache.polaris.test.commons.MinioRustProfile;
-import org.apache.polaris.test.minio.Minio;
-import org.apache.polaris.test.minio.MinioAccess;
-import org.apache.polaris.test.minio.MinioConditionExtension;
-import org.apache.polaris.test.minio.MinioTestResource;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInfo;
-import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.CsvSource;
-import org.junit.jupiter.params.provider.ValueSource;
-import software.amazon.awssdk.services.s3.S3Client;
-import software.amazon.awssdk.services.s3.model.GetObjectRequest;
-import software.amazon.awssdk.services.s3.model.GetObjectResponse;
-
-/**
- * These tests complement {@link PolarisRestCatalogMinIOIT} to validate client-side access to MinIO
- * storage via {@code FileIO} instances configured from catalog's {@code loadTable} responses with
- * some S3-specific options.
- */
-@QuarkusIntegrationTest
-@TestProfile(MinioRustProfile.class)
-@QuarkusTestResource(
- value = MinioTestResource.class,
- initArgs = {
- @ResourceArg(name = "accessKey", value = ACCESS_KEY),
- @ResourceArg(name = "secretKey", value = SECRET_KEY)
- })
-@ExtendWith(MinioConditionExtension.class)
-@ExtendWith(PolarisIntegrationTestExtension.class)
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class RestCatalogMinIOSpecialIT {
-
- private static final String BUCKET_URI_PREFIX = "/minio-test";
- private static final String TEST_REGION = "us-west-2";
- private static final String TEST_ROLE_ARN = "arn:aws:iam::000000000000:role/polaris-access-role";
- private static String adminToken;
-
- private static final Schema SCHEMA =
- new Schema(
- required(1, "id", Types.IntegerType.get(), "doc"),
- optional(2, "data", Types.StringType.get()));
-
- @Minio static MinioAccess minioAccess;
-
- private static PolarisApiEndpoints endpoints;
- private static PolarisClient client;
- private static ManagementApi managementApi;
- private static URI storageBase;
- private static String endpoint;
- private static S3Client s3Client;
-
- private CatalogApi catalogApi;
- private String principalRoleName;
- private PrincipalWithCredentials principalCredentials;
- private String catalogName;
-
- @BeforeAll
- void setup(PolarisApiEndpoints apiEndpoints, ClientCredentials credentials) {
- endpoints = apiEndpoints;
- s3Client = minioAccess.s3Client();
- client = polarisClient(endpoints);
- adminToken = client.obtainToken(credentials);
- managementApi = client.managementApi(adminToken);
- storageBase = minioAccess.s3BucketUri(BUCKET_URI_PREFIX);
- endpoint = minioAccess.s3endpoint();
- }
-
- @AfterAll
- void close() throws Exception {
- client.close();
- }
-
- @BeforeEach
- public void before(TestInfo testInfo) {
- String principalName = client.newEntityName("test-user");
- principalRoleName = client.newEntityName("test-admin");
- principalCredentials = managementApi.createPrincipalWithRole(principalName, principalRoleName);
-
- String principalToken = client.obtainToken(principalCredentials);
- catalogApi = client.catalogApi(principalToken);
-
- catalogName = client.newEntityName(testInfo.getTestMethod().orElseThrow().getName());
- }
-
- private RESTCatalog createCatalog(
- Optional<String> endpoint,
- Optional<String> stsEndpoint,
- boolean pathStyleAccess,
- Optional<AccessDelegationMode> delegationMode,
- boolean stsEnabled) {
- return createCatalog(
- endpoint,
- stsEndpoint,
- pathStyleAccess,
- Optional.empty(),
- delegationMode,
- stsEnabled,
- Optional.empty(),
- Optional.empty(),
- Optional.empty());
- }
-
- private RESTCatalog createCatalog(
- Optional<String> endpoint,
- Optional<String> stsEndpoint,
- boolean pathStyleAccess,
- Optional<String> endpointInternal,
- Optional<AccessDelegationMode> delegationMode,
- boolean stsEnabled) {
- return createCatalog(
- endpoint,
- stsEndpoint,
- pathStyleAccess,
- endpointInternal,
- delegationMode,
- stsEnabled,
- Optional.empty(),
- Optional.empty(),
- Optional.empty());
- }
-
- private RESTCatalog createCatalog(
- Optional<String> endpoint,
- Optional<String> stsEndpoint,
- boolean pathStyleAccess,
- Optional<String> endpointInternal,
- Optional<AccessDelegationMode> delegationMode,
- boolean stsEnabled,
- Optional<String> region,
- Optional<String> roleArn,
- Optional<Boolean> kmsUnavailable) {
- AwsStorageConfigInfo.Builder storageConfig =
- AwsStorageConfigInfo.builder()
- .setStorageType(StorageConfigInfo.StorageTypeEnum.S3)
- .setPathStyleAccess(pathStyleAccess)
- .setStsUnavailable(!stsEnabled)
- .setAllowedLocations(List.of(storageBase.toString() + "/" + catalogName));
-
- endpoint.ifPresent(storageConfig::setEndpoint);
- stsEndpoint.ifPresent(storageConfig::setStsEndpoint);
- endpointInternal.ifPresent(storageConfig::setEndpointInternal);
- region.ifPresent(storageConfig::setRegion);
- roleArn.ifPresent(storageConfig::setRoleArn);
- kmsUnavailable.ifPresent(storageConfig::setKmsUnavailable);
-
- CatalogProperties.Builder catalogProps =
- CatalogProperties.builder(storageBase.toString() + "/" + catalogName);
- if (!stsEnabled) {
- catalogProps.addProperty(TABLE_DEFAULT_PREFIX + AWS_KEY_ID.getPropertyName(), ACCESS_KEY);
- catalogProps.addProperty(TABLE_DEFAULT_PREFIX + AWS_SECRET_KEY.getPropertyName(), SECRET_KEY);
- }
- Catalog catalog =
- PolarisCatalog.builder()
- .setType(Catalog.TypeEnum.INTERNAL)
- .setName(catalogName)
- .setStorageConfigInfo(storageConfig.build())
- .setProperties(catalogProps.build())
- .build();
-
- managementApi.createCatalog(principalRoleName, catalog);
-
- String authToken = client.obtainToken(principalCredentials);
- RESTCatalog restCatalog = new RESTCatalog();
-
- ImmutableMap.Builder<String, String> propertiesBuilder =
- ImmutableMap.<String, String>builder()
- .put(
- org.apache.iceberg.CatalogProperties.URI, endpoints.catalogApiEndpoint().toString())
- .put(OAuth2Properties.TOKEN, authToken)
- .put("warehouse", catalogName)
- .putAll(endpoints.extraHeaders("header."));
-
- delegationMode.ifPresent(
- dm -> propertiesBuilder.put("header.X-Iceberg-Access-Delegation", dm.protocolValue()));
-
- if (delegationMode.isEmpty()) {
- // Use local credentials on the client side
- propertiesBuilder.put("s3.access-key-id", ACCESS_KEY);
- propertiesBuilder.put("s3.secret-access-key", SECRET_KEY);
- }
-
- restCatalog.initialize("polaris", propertiesBuilder.buildKeepingLast());
- return restCatalog;
- }
-
- @AfterEach
- public void cleanUp() {
- client.cleanUp(adminToken);
- }
-
- @ParameterizedTest
- @CsvSource("true, true,")
- @CsvSource("false, true,")
- @CsvSource("true, false,")
- @CsvSource("false, false,")
- public void testCreateTable(boolean pathStyle, boolean stsEnabled) throws IOException {
- LoadTableResponse response = doTestCreateTable(pathStyle, Optional.empty(), stsEnabled);
- assertThat(response.config()).doesNotContainKey(SECRET_ACCESS_KEY);
- assertThat(response.config()).doesNotContainKey(ACCESS_KEY_ID);
- assertThat(response.config()).doesNotContainKey(REFRESH_CREDENTIALS_ENDPOINT);
- assertThat(response.credentials()).isEmpty();
- }
-
- @ParameterizedTest
- @ValueSource(booleans = {true, false})
- public void testCreateTableVendedCredentials(boolean pathStyle) throws IOException {
- LoadTableResponse response =
- doTestCreateTable(pathStyle, Optional.of(VENDED_CREDENTIALS), true);
- assertThat(response.config())
- .containsEntry(
- REFRESH_CREDENTIALS_ENDPOINT,
- "v1/" + catalogName + "/namespaces/test-ns/tables/t1/credentials");
- assertThat(response.credentials()).hasSize(1);
- }
-
- @ParameterizedTest
- @CsvSource({
- "false, false",
- "true, false",
- "false, true",
- })
- public void testCreateTableVendedCredentialsWithPartialAwsShapePasses(
- boolean includeRegion, boolean includeRoleArn) throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(
- Optional.of(endpoint),
- Optional.of(endpoint),
- true,
- Optional.empty(),
- Optional.of(VENDED_CREDENTIALS),
- true,
- includeRegion ? Optional.of(TEST_REGION) : Optional.empty(),
- includeRoleArn ? Optional.of(TEST_ROLE_ARN) : Optional.empty(),
- Optional.of(false))) {
- TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
- try {
- assertLoadTableWithVendedCredentialsSucceeds(id);
- } finally {
- catalogApi.dropTable(catalogName, id);
- }
- }
- }
-
- @Test
- public void testCreateTableVendedCredentialsWithFullAwsShapeAndKmsEnabledFails()
- throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(
- Optional.of(endpoint),
- Optional.of(endpoint),
- true,
- Optional.empty(),
- Optional.of(VENDED_CREDENTIALS),
- true,
- Optional.of(TEST_REGION),
- Optional.of(TEST_ROLE_ARN),
- Optional.of(false))) {
- TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
- try {
- assertLoadTableWithVendedCredentialsFailsWithKmsError(id);
- } finally {
- catalogApi.dropTable(catalogName, id);
- }
- }
- }
-
- @Test
- public void testCreateTableVendedCredentialsWithAwsShapeAndKmsUnavailablePasses()
- throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(
- Optional.of(endpoint),
- Optional.of(endpoint),
- true,
- Optional.empty(),
- Optional.of(VENDED_CREDENTIALS),
- true,
- Optional.of(TEST_REGION),
- Optional.of(TEST_ROLE_ARN),
- Optional.of(true))) {
- TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
- try {
- assertLoadTableWithVendedCredentialsSucceeds(id);
- } finally {
- catalogApi.dropTable(catalogName, id);
- }
- }
- }
-
- private void assertLoadTableWithVendedCredentialsFailsWithKmsError(TableIdentifier id) {
- assertThatThrownBy(
- () ->
- catalogApi.loadTable(
- catalogName,
- id,
- "ALL",
- Map.of("X-Iceberg-Access-Delegation", VENDED_CREDENTIALS.protocolValue())))
- .hasMessageContaining("invalid resource")
- .hasMessageContaining("arn:aws:kms:us-west-2:000000000000:key/*");
- }
-
- private void assertLoadTableWithVendedCredentialsSucceeds(TableIdentifier id) {
- LoadTableResponse response =
- catalogApi.loadTable(
- catalogName,
- id,
- "ALL",
- Map.of("X-Iceberg-Access-Delegation", VENDED_CREDENTIALS.protocolValue()));
- assertThat(response.config())
- .containsEntry(
- REFRESH_CREDENTIALS_ENDPOINT,
- "v1/" + catalogName + "/namespaces/test-ns/tables/t1/credentials");
- assertThat(response.credentials()).hasSize(1);
- }
-
- private LoadTableResponse doTestCreateTable(
- boolean pathStyle, Optional<AccessDelegationMode> dm, boolean stsEnabled) throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(Optional.of(endpoint), Optional.empty(), pathStyle, dm, stsEnabled)) {
- LoadTableResponse loadTableResponse = doTestCreateTable(restCatalog, dm);
- if (pathStyle) {
- assertThat(loadTableResponse.config())
- .containsEntry("s3.path-style-access", Boolean.TRUE.toString());
- }
- return loadTableResponse;
- }
- }
-
- @Test
- public void testInternalEndpoints() throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(
- Optional.of("http://s3.example.com"),
- Optional.of(endpoint),
- false,
- Optional.of(endpoint),
- Optional.empty(),
- true)) {
- StorageConfigInfo storageConfig =
- managementApi.getCatalog(catalogName).getStorageConfigInfo();
- assertThat((AwsStorageConfigInfo) storageConfig)
- .extracting(
- AwsStorageConfigInfo::getEndpoint,
- AwsStorageConfigInfo::getStsEndpoint,
- AwsStorageConfigInfo::getEndpointInternal,
- AwsStorageConfigInfo::getPathStyleAccess)
- .containsExactly("http://s3.example.com", endpoint, endpoint, false);
- LoadTableResponse loadTableResponse = doTestCreateTable(restCatalog, Optional.empty());
- assertThat(loadTableResponse.config()).containsEntry(ENDPOINT, "http://s3.example.com");
- }
- }
-
- @Test
- public void testCreateTableFailureWithCredentialVendingWithoutSts() throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(
- Optional.of(endpoint),
- Optional.of("http://sts.example.com"), // not called
- false,
- Optional.of(VENDED_CREDENTIALS),
- false)) {
- StorageConfigInfo storageConfig =
- managementApi.getCatalog(catalogName).getStorageConfigInfo();
- assertThat((AwsStorageConfigInfo) storageConfig)
- .extracting(
- AwsStorageConfigInfo::getEndpoint,
- AwsStorageConfigInfo::getStsEndpoint,
- AwsStorageConfigInfo::getEndpointInternal,
- AwsStorageConfigInfo::getPathStyleAccess,
- AwsStorageConfigInfo::getStsUnavailable)
- .containsExactly(endpoint, "http://sts.example.com", null, false, true);
-
- catalogApi.createNamespace(catalogName, "test-ns");
- TableIdentifier id = TableIdentifier.of("test-ns", "t2");
- // Credential vending is not supported without STS
- assertThatThrownBy(() -> restCatalog.createTable(id, SCHEMA))
- .hasMessageContaining("but no credentials are available")
- .hasMessageContaining(id.toString());
- }
- }
-
- @Test
- public void testLoadTableFailureWithCredentialVendingWithoutSts() throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(
- Optional.of(endpoint),
- Optional.of("http://sts.example.com"), // not called
- false,
- Optional.empty(),
- false)) {
-
- catalogApi.createNamespace(catalogName, "test-ns");
- TableIdentifier id = TableIdentifier.of("test-ns", "t3");
- restCatalog.createTable(id, SCHEMA);
-
- // Credential vending is not supported without STS
- assertThatThrownBy(
- () ->
- catalogApi.loadTable(
- catalogName,
- id,
- "ALL",
- Map.of("X-Iceberg-Access-Delegation", VENDED_CREDENTIALS.protocolValue())))
- .hasMessageContaining("but no credentials are available")
- .hasMessageContaining(id.toString());
- }
- }
-
- public LoadTableResponse doTestCreateTable(
- RESTCatalog restCatalog, Optional<AccessDelegationMode> dm) {
- TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
- assertThat(restCatalog.tableExists(id)).isTrue();
-
- try {
- LoadTableResponse loadTableResponse =
- catalogApi.loadTable(
- catalogName,
- id,
- "ALL",
- dm.map(v -> Map.of("X-Iceberg-Access-Delegation", v.protocolValue()))
- .orElse(Map.of()));
-
- assertThat(loadTableResponse.config()).containsKey(ENDPOINT);
- return loadTableResponse;
- } finally {
- if (restCatalog.tableExists(id)) {
- restCatalog.dropTable(id);
- assertThat(restCatalog.tableExists(id)).isFalse();
- }
- }
- }
-
- private TableIdentifier createTableAndVerifyMetadata(RESTCatalog restCatalog) {
- catalogApi.createNamespace(catalogName, "test-ns");
- TableIdentifier id = TableIdentifier.of("test-ns", "t1");
- Table table = restCatalog.createTable(id, SCHEMA);
- assertThat(table).isNotNull();
-
- TableOperations ops = ((HasTableOperations) table).operations();
- URI location = URI.create(ops.current().metadataFileLocation());
-
- GetObjectResponse response =
- s3Client
- .getObject(
- GetObjectRequest.builder()
- .bucket(location.getAuthority())
- .key(location.getPath().substring(1)) // drop leading slash
- .build())
- .response();
- assertThat(response.contentLength()).isGreaterThan(0);
- return id;
- }
-
- @ParameterizedTest
- @CsvSource("true, true,")
- @CsvSource("false, true,")
- @CsvSource("true, false,")
- @CsvSource("false, false,")
- @CsvSource("true, true, VENDED_CREDENTIALS")
- @CsvSource("false, true, VENDED_CREDENTIALS")
- public void testAppendFiles(
- boolean pathStyle, boolean stsEnabled, AccessDelegationMode delegationMode)
- throws IOException {
- try (RESTCatalog restCatalog =
- createCatalog(
- Optional.of(endpoint),
- Optional.of(endpoint),
- pathStyle,
- Optional.ofNullable(delegationMode),
- stsEnabled)) {
- catalogApi.createNamespace(catalogName, "test-ns");
- TableIdentifier id = TableIdentifier.of("test-ns", "t1");
- Table table = restCatalog.createTable(id, SCHEMA);
- assertThat(table).isNotNull();
-
- @SuppressWarnings("resource")
- FileIO io = table.io();
-
- URI loc =
- URI.create(
- table
- .locationProvider()
- .newDataLocation(
- String.format(
- "test-file-%s-%s-%s.txt", pathStyle, delegationMode, stsEnabled)));
- OutputFile f1 = io.newOutputFile(loc.toString());
- try (PositionOutputStream os = f1.create()) {
- os.write("Hello World".getBytes(UTF_8));
- }
-
- DataFile df =
- DataFiles.builder(PartitionSpec.unpartitioned())
- .withPath(f1.location())
- .withFormat(FileFormat.PARQUET) // bogus value
- .withFileSizeInBytes(4)
- .withRecordCount(1)
- .build();
-
- table.newAppend().appendFile(df).commit();
-
- try (InputStream is =
- s3Client.getObject(
- GetObjectRequest.builder()
- .bucket(loc.getAuthority())
- .key(loc.getPath().substring(1)) // drop leading slash
- .build())) {
- assertThat(new String(is.readAllBytes(), UTF_8)).isEqualTo("Hello World");
- }
- }
- }
-}
diff --git a/tools/testcontainers/minio-testcontainer/build.gradle.kts b/tools/testcontainers/minio-testcontainer/build.gradle.kts
deleted file mode 100644
index 8a558aa891..0000000000
--- a/tools/testcontainers/minio-testcontainer/build.gradle.kts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- */
-
-plugins {
- id("org.kordamp.gradle.jandex")
- id("polaris-server")
-}
-
-dependencies {
- api(platform(libs.testcontainers.bom))
- api("org.testcontainers:testcontainers")
-
- api(platform(libs.awssdk.bom))
- api("software.amazon.awssdk:s3")
- api("software.amazon.awssdk:kms")
-
- implementation(project(":polaris-container-spec-helper"))
- implementation("software.amazon.awssdk:url-connection-client")
- implementation(libs.guava)
-
- compileOnly(platform(libs.junit.bom))
- compileOnly("org.junit.jupiter:junit-jupiter-api")
-
- compileOnly(platform(libs.quarkus.bom))
- compileOnly("io.quarkus:quarkus-test-common")
-}
diff --git a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/Minio.java b/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/Minio.java
deleted file mode 100644
index f5ad582ad7..0000000000
--- a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/Minio.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.polaris.test.minio;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-// CODE_COPIED_TO_POLARIS from Project Nessie 0.104.2
-@Target({ElementType.FIELD, ElementType.PARAMETER})
-@Retention(RetentionPolicy.RUNTIME)
-@Inherited
-public @interface Minio {
- /** Optional, use this access key instead of a random one. */
- String accessKey() default DEFAULT;
-
- /** Optional, use this secret key instead of a random one. */
- String secretKey() default DEFAULT;
-
- /** Optional, use this bucket instead of a random one. */
- String bucket() default DEFAULT;
-
- /** Optional, use this region. */
- String region() default DEFAULT;
-
- String DEFAULT = "minio_default_value__";
-}
diff --git a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioAccess.java b/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioAccess.java
deleted file mode 100644
index 86e0a68f44..0000000000
--- a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioAccess.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.polaris.test.minio;
-
-import java.net.URI;
-import java.util.Map;
-import java.util.Optional;
-import software.amazon.awssdk.core.sync.RequestBody;
-import software.amazon.awssdk.services.s3.S3Client;
-
-/**
- * Provides access to Minio via a preconfigured S3 client and providing the by default randomized
- * bucket and access/secret keys.
- *
- * <p>Annotate JUnit test instance or static fields or method parameters of this type with {@link
- * Minio}.
- */
-// CODE_COPIED_TO_POLARIS from Project Nessie 0.104.2
-public interface MinioAccess {
-
- /** Host and port, separated by '{@code :}'. */
- String hostPort();
-
- String accessKey();
-
- String secretKey();
-
- String bucket();
-
- Optional<String> region();
-
- /** HTTP protocol endpoint. */
- String s3endpoint();
-
- S3Client s3Client();
-
- /** Properties needed by Apache Iceberg to access this instance. */
- Map<String, String> icebergProperties();
-
- /** Properties needed by Apache Hadoop to access this instance. */
- Map<String, String> hadoopConfig();
-
- /** S3 scheme URI including the bucket to access the given path. */
- URI s3BucketUri(String path);
-
- /** Convenience method to put an object into S3. */
- @SuppressWarnings("resource")
- default void s3put(String key, RequestBody body) {
- s3Client().putObject(b -> b.bucket(bucket()).key(key), body);
- }
-}
diff --git a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioConditionExtension.java b/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioConditionExtension.java
deleted file mode 100644
index fbb96539dd..0000000000
--- a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioConditionExtension.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.polaris.test.minio;
-
-import static java.lang.String.format;
-import static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;
-import static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;
-
-import org.junit.jupiter.api.condition.OS;
-import org.junit.jupiter.api.extension.ConditionEvaluationResult;
-import org.junit.jupiter.api.extension.ExecutionCondition;
-import org.junit.jupiter.api.extension.ExtensionContext;
-
-/** JUnit extension that gates whether the Minio container can be run on the current platform. */
-public class MinioConditionExtension implements ExecutionCondition {
- @Override
- public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
- if (OS.current() == OS.LINUX) {
- return enabled("Running on Linux");
- }
- if (OS.current() == OS.MAC
- && System.getenv("CI_MAC") == null
- && MinioContainer.canRunOnMacOs()) {
- // Disable tests on GitHub Actions
- return enabled("Running on macOS locally");
- }
- return disabled(format("Disabled on %s", OS.current().name()));
- }
-}
diff --git a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioContainer.java b/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioContainer.java
deleted file mode 100644
index 9da81d008d..0000000000
--- a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioContainer.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.polaris.test.minio;
-
-import com.google.common.base.Preconditions;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.UnknownHostException;
-import java.time.Duration;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
-import org.apache.polaris.containerspec.ContainerSpecHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.containers.output.Slf4jLogConsumer;
-import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
-import org.testcontainers.utility.Base58;
-import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
-import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
-import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient;
-import software.amazon.awssdk.regions.Region;
-import software.amazon.awssdk.services.s3.S3Client;
-import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
-
-// CODE_COPIED_TO_POLARIS from Project Nessie 0.104.2
-public final class MinioContainer extends GenericContainer<MinioContainer>
- implements MinioAccess, AutoCloseable {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MinioContainer.class);
-
- private static final int DEFAULT_PORT = 9000;
-
- private static final String MINIO_ACCESS_KEY = "MINIO_ROOT_USER";
- private static final String MINIO_SECRET_KEY = "MINIO_ROOT_PASSWORD";
- private static final String MINIO_DOMAIN = "MINIO_DOMAIN";
-
- private static final String DEFAULT_STORAGE_DIRECTORY = "/data";
- private static final String HEALTH_ENDPOINT = "/minio/health/ready";
- private static final String MINIO_DOMAIN_NAME;
-
- /**
- * Domain must start with "s3" in order to be recognized as an S3 endpoint by the AWS SDK with
- * virtual-host-style addressing. The bucket name is expected to be the first part of the domain
- * name, e.g. "bucket.s3.127-0-0-1.nip.io".
- */
- private static final String MINIO_DOMAIN_NIP = "s3.127-0-0-1.nip.io";
-
- /**
- * Whether random bucket names cannot be used. Randomized bucket names can only be used when
- * either `*.localhost` (on Linux) or `*.s3.127-0-0-1.nip.io` (on macOS, if DNS rebind protection
- * is not active) can be resolved. Otherwise we have to use a fixed bucket name and users have to
- * configure that in `/etc/hosts`.
- */
- private static final String FIXED_BUCKET_NAME;
-
- static boolean canRunOnMacOs() {
- return MINIO_DOMAIN_NAME.equals(MINIO_DOMAIN_NIP);
- }
-
- static {
- String name;
- String fixedBucketName = null;
- if (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("linux")) {
- name = "localhost";
- } else {
- try {
- InetAddress ignored = InetAddress.getByName(MINIO_DOMAIN_NIP);
- name = MINIO_DOMAIN_NIP;
- } catch (UnknownHostException x) {
- LOGGER.warn(
- "Could not resolve '{}', falling back to 'localhost'. "
- + "This usually happens when your router or DNS provider is unable to resolve the nip.io addresses.",
- MINIO_DOMAIN_NIP);
- name = "localhost";
- fixedBucketName = "miniobucket";
- validateBucketHost(fixedBucketName);
- }
- }
- MINIO_DOMAIN_NAME = name;
- FIXED_BUCKET_NAME = fixedBucketName;
- }
-
- /** Validates the bucket host name, on non-Linux, if necessary. */
- private static String validateBucketHost(String bucketName) {
- if (FIXED_BUCKET_NAME != null) {
- String test = bucketName + ".localhost";
- try {
- InetAddress ignored = InetAddress.getByName(test);
- } catch (UnknownHostException e) {
- LOGGER.warn(
- "Could not resolve '{}',\n Please add the line \n '127.0.0.1 {}'\n to your local '/etc/hosts' file.\n Tests are expected to fail unless name resolution works.",
- test,
- test);
- }
- }
- return bucketName;
- }
-
- private final String accessKey;
- private final String secretKey;
- private final String bucket;
-
- private String hostPort;
- private String s3endpoint;
- private S3Client s3;
- private Optional<String> region;
-
- @SuppressWarnings("unused")
- public MinioContainer() {
- this(null, null, null, null, null);
- }
-
- @SuppressWarnings("resource")
- public MinioContainer(
- String image, String accessKey, String secretKey, String bucket, String region) {
- super(
- ContainerSpecHelper.containerSpecHelper("minio", MinioContainer.class)
- .dockerImageName(image));
- withNetworkAliases(randomString("minio"));
- withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(MinioContainer.class)));
- addExposedPort(DEFAULT_PORT);
- this.accessKey = accessKey != null ? accessKey : randomString("access");
- this.secretKey = secretKey != null ? secretKey : randomString("secret");
- this.bucket =
- bucket != null
- ? validateBucketHost(bucket)
- : (FIXED_BUCKET_NAME != null ? FIXED_BUCKET_NAME : randomString("bucket"));
- this.region = Optional.ofNullable(region);
- withEnv(MINIO_ACCESS_KEY, this.accessKey);
- withEnv(MINIO_SECRET_KEY, this.secretKey);
- // S3 SDK encodes bucket names in host names - need to tell Minio which domain to use
- withEnv(MINIO_DOMAIN, MINIO_DOMAIN_NAME);
- withCommand("server", DEFAULT_STORAGE_DIRECTORY);
- setWaitStrategy(
- new HttpWaitStrategy()
- .forPort(DEFAULT_PORT)
- .forPath(HEALTH_ENDPOINT)
- .withStartupTimeout(Duration.ofMinutes(2)));
- }
-
- public MinioContainer withRegion(String region) {
- this.region = Optional.of(region);
- return this;
- }
-
- private static String randomString(String prefix) {
- return prefix + "-" + Base58.randomString(6).toLowerCase(Locale.ROOT);
- }
-
- @Override
- public String hostPort() {
- Preconditions.checkState(hostPort != null, "Container not yet started");
- return hostPort;
- }
-
- @Override
- public String accessKey() {
- return accessKey;
- }
-
- @Override
- public String secretKey() {
- return secretKey;
- }
-
- @Override
- public String bucket() {
- return bucket;
- }
-
- @Override
- public Optional<String> region() {
- return region;
- }
-
- @Override
- public String s3endpoint() {
- Preconditions.checkState(s3endpoint != null, "Container not yet started");
- return s3endpoint;
- }
-
- @Override
- public S3Client s3Client() {
- Preconditions.checkState(s3 != null, "Container not yet started");
- return s3;
- }
-
- @Override
- public Map<String, String> icebergProperties() {
- Map<String, String> props = new HashMap<>();
- props.put("s3.access-key-id", accessKey());
- props.put("s3.secret-access-key", secretKey());
- props.put("s3.endpoint", s3endpoint());
- props.put("http-client.type", "urlconnection");
- region().ifPresent(r -> props.put("client.region", r));
- return props;
- }
-
- @Override
- public Map<String, String> hadoopConfig() {
- Map<String, String> r = new HashMap<>();
- r.put("fs.s3.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem");
- r.put("fs.s3a.access.key", accessKey());
- r.put("fs.s3a.secret.key", secretKey());
- r.put("fs.s3a.endpoint", s3endpoint());
- return r;
- }
-
- @Override
- public URI s3BucketUri(String path) {
- return s3BucketUri("s3", path);
- }
-
- public URI s3BucketUri(String scheme, String path) {
- Preconditions.checkState(bucket != null, "Container not yet started");
- return URI.create(String.format("%s://%s/", scheme, bucket)).resolve(path);
- }
-
- @Override
- public void start() {
- super.start();
-
- this.hostPort = MINIO_DOMAIN_NAME + ":" + getMappedPort(DEFAULT_PORT);
- this.s3endpoint = String.format("http://%s/", hostPort);
-
- this.s3 = createS3Client();
- this.s3.createBucket(CreateBucketRequest.builder().bucket(bucket()).build());
- }
-
- @Override
- public void close() {
- stop();
- }
-
- @Override
- public void stop() {
- try {
- if (s3 != null) {
- s3.close();
- }
- } finally {
- s3 = null;
- super.stop();
- }
- }
-
- private S3Client createS3Client() {
- return S3Client.builder()
- .httpClientBuilder(UrlConnectionHttpClient.builder())
- .applyMutation(builder -> builder.endpointOverride(URI.create(s3endpoint())))
- .applyMutation(builder -> region.ifPresent(r -> builder.region(Region.of(r))))
- .credentialsProvider(
- StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey(), secretKey())))
- .build();
- }
-}
diff --git a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioExtension.java b/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioExtension.java
deleted file mode 100644
index e30e60685f..0000000000
--- a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioExtension.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.polaris.test.minio;
-
-import static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields;
-import static org.junit.platform.commons.util.ReflectionUtils.makeAccessible;
-
-import java.lang.reflect.Field;
-import org.junit.jupiter.api.extension.BeforeAllCallback;
-import org.junit.jupiter.api.extension.BeforeEachCallback;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.api.extension.ParameterContext;
-import org.junit.jupiter.api.extension.ParameterResolutionException;
-import org.junit.jupiter.api.extension.ParameterResolver;
-import org.junit.platform.commons.util.AnnotationUtils;
-import org.junit.platform.commons.util.ExceptionUtils;
-import org.junit.platform.commons.util.ReflectionUtils;
-
-/**
- * JUnit extension that provides a Minio container configured with a single bucket.
- *
- * <p>Provides instances of {@link MinioAccess} via instance or static fields or parameters
- * annotated with {@link Minio}.
- */
-// CODE_COPIED_TO_POLARIS from Project Nessie 0.104.2
-public class MinioExtension extends MinioConditionExtension
- implements BeforeAllCallback, BeforeEachCallback, ParameterResolver {
- private static final ExtensionContext.Namespace NAMESPACE =
- ExtensionContext.Namespace.create(MinioExtension.class);
-
- @Override
- public void beforeAll(ExtensionContext context) {
- Class<?> testClass = context.getRequiredTestClass();
-
- findAnnotatedFields(testClass, Minio.class, ReflectionUtils::isStatic)
- .forEach(field -> injectField(context, field));
- }
-
- @Override
- public void beforeEach(ExtensionContext context) {
- Class<?> testClass = context.getRequiredTestClass();
-
- findAnnotatedFields(testClass, Minio.class, ReflectionUtils::isNotStatic)
- .forEach(field -> injectField(context, field));
- }
-
- private void injectField(ExtensionContext context, Field field) {
- try {
- Minio minio =
- AnnotationUtils.findAnnotation(field, Minio.class)
- .orElseThrow(IllegalStateException::new);
-
- MinioAccess container =
- context
- .getStore(NAMESPACE)
- .computeIfAbsent(field.toString(), x -> createContainer(minio), MinioAccess.class);
-
- makeAccessible(field).set(context.getTestInstance().orElse(null), container);
- } catch (Throwable t) {
- ExceptionUtils.throwAsUncheckedException(t);
- }
- }
-
- @Override
- public boolean supportsParameter(
- ParameterContext parameterContext, ExtensionContext extensionContext)
- throws ParameterResolutionException {
- if (parameterContext.findAnnotation(Minio.class).isEmpty()) {
- return false;
- }
- return parameterContext.getParameter().getType().isAssignableFrom(MinioAccess.class);
- }
-
- @Override
- public Object resolveParameter(
- ParameterContext parameterContext, ExtensionContext extensionContext)
- throws ParameterResolutionException {
- return extensionContext
- .getStore(NAMESPACE)
- .computeIfAbsent(
- MinioExtension.class.getName() + '#' + parameterContext.getParameter().getName(),
- k -> {
- Minio minio = parameterContext.findAnnotation(Minio.class).get();
- return createContainer(minio);
- },
- MinioAccess.class);
- }
-
- private MinioAccess createContainer(Minio minio) {
- String accessKey = nonDefault(minio.accessKey());
- String secretKey = nonDefault(minio.secretKey());
- String bucket = nonDefault(minio.bucket());
- String region = nonDefault(minio.region());
- MinioContainer container =
- new MinioContainer(null, accessKey, secretKey, bucket, region).withStartupAttempts(5);
- container.start();
- return container;
- }
-
- private static String nonDefault(String s) {
- return s.equals(Minio.DEFAULT) ? null : s;
- }
-}
diff --git a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioTestResource.java b/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioTestResource.java
deleted file mode 100644
index df2d26d683..0000000000
--- a/tools/testcontainers/minio-testcontainer/src/main/java/org/apache/polaris/test/minio/MinioTestResource.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.polaris.test.minio;
-
-import static java.util.Objects.requireNonNull;
-
-import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
-import java.util.Map;
-
-public class MinioTestResource implements QuarkusTestResourceLifecycleManager {
-
- private Map<String, String> initArgs = Map.of();
- private MinioContainer container;
-
- @Override
- public void init(Map<String, String> initArgs) {
- this.initArgs = Map.copyOf(initArgs);
- }
-
- @Override
- public Map<String, String> start() {
- var accessKey = initArgs.get("accessKey");
- var secretKey = initArgs.get("secretKey");
- var bucket = initArgs.get("bucket");
- var region = initArgs.get("region");
- this.container =
- new MinioContainer(null, accessKey, secretKey, bucket, region).withStartupAttempts(5);
- this.container.start();
- return Map.of();
- }
-
- @Override
- public void stop() {
- if (container != null) {
- try {
- container.close();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- @Override
- public void inject(TestInjector testInjector) {
- MinioAccess minioAccess = requireNonNull(container, "Minio not started");
- testInjector.injectIntoFields(minioAccess, field -> field.isAnnotationPresent(Minio.class));
- }
-}
diff --git a/tools/testcontainers/minio-testcontainer/src/main/resources/org/apache/polaris/test/minio/Dockerfile-minio-version b/tools/testcontainers/minio-testcontainer/src/main/resources/org/apache/polaris/test/minio/Dockerfile-minio-version
deleted file mode 100644
index 69552d3ab9..0000000000
--- a/tools/testcontainers/minio-testcontainer/src/main/resources/org/apache/polaris/test/minio/Dockerfile-minio-version
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# 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.
-#
-
-# Dockerfile to provide the image name and tag to a test.
-# Version is managed by Renovate - do not edit.
-FROM quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z