From 32ffdedb1b0a7a497e0614bd333a23e6117eee06 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 10 Mar 2026 00:48:55 -0700 Subject: [PATCH 1/8] Replace MinIO with SeaweedFS in quickstart and docker-compose files MinIO is under maintenance mode and its Docker images have unpatched CVE vulnerabilities. Replace it with SeaweedFS mini mode, which provides an S3-compatible API in a lightweight single-binary deployment. Closes #14638 --- .../docker-compose.yml | 57 ++++++++++++------- .../docker/docker-compose.yml | 47 ++++++++++----- .../apache/iceberg/connect/TestContext.java | 10 ++-- site/docs/spark-quickstart.md | 56 +++++++++++------- 4 files changed, 108 insertions(+), 62 deletions(-) diff --git a/docker/iceberg-flink-quickstart/docker-compose.yml b/docker/iceberg-flink-quickstart/docker-compose.yml index dd5d711f67c4..acbe00ff3076 100644 --- a/docker/iceberg-flink-quickstart/docker-compose.yml +++ b/docker/iceberg-flink-quickstart/docker-compose.yml @@ -50,7 +50,7 @@ services: AWS_REGION: us-east-1 AWS_ACCESS_KEY_ID: admin AWS_SECRET_ACCESS_KEY: password - S3_ENDPOINT: http://minio:9000 + S3_ENDPOINT: http://seaweedfs:8333 # Flink TaskManager taskmanager: @@ -72,7 +72,7 @@ services: AWS_REGION: us-east-1 AWS_ACCESS_KEY_ID: admin AWS_SECRET_ACCESS_KEY: password - S3_ENDPOINT: http://minio:9000 + S3_ENDPOINT: http://seaweedfs:8333 # Iceberg REST Catalog iceberg-rest: @@ -92,43 +92,56 @@ services: AWS_REGION: us-east-1 CATALOG_WAREHOUSE: s3://warehouse/ CATALOG_IO__IMPL: org.apache.iceberg.aws.s3.S3FileIO - CATALOG_S3_ENDPOINT: http://minio:9000 + CATALOG_S3_ENDPOINT: http://seaweedfs:8333 CATALOG_S3_ACCESS__KEY__ID: admin CATALOG_S3_SECRET__ACCESS__KEY: password - # MinIO for S3-compatible object storage - minio: - image: minio/minio - hostname: minio + # SeaweedFS for S3-compatible object storage (mini mode) + seaweedfs: + image: chrislusf/seaweedfs:latest + hostname: seaweedfs environment: - MINIO_ROOT_USER: admin - MINIO_ROOT_PASSWORD: password - MINIO_DOMAIN: minio + AWS_ACCESS_KEY_ID: admin + AWS_SECRET_ACCESS_KEY: password networks: iceberg_net: aliases: - - warehouse.minio - command: server /data --console-address ":9001" + - warehouse.seaweedfs + command: "mini -dir=/data" healthcheck: - test: ["CMD", "mc", "ready", "local"] - interval: 5s - timeout: 5s - retries: 5 + test: ["CMD", "curl", "-sf", "http://localhost:8333/status"] + interval: 3s + timeout: 2s + retries: 10 + start_period: 5s # Create the warehouse bucket create-bucket: - image: minio/mc + image: amazon/aws-cli:latest depends_on: - minio: + seaweedfs: condition: service_healthy networks: iceberg_net: + environment: + AWS_ACCESS_KEY_ID: admin + AWS_SECRET_ACCESS_KEY: password + AWS_REGION: us-east-1 entrypoint: | /bin/sh -c " - until (/usr/bin/mc alias set minio http://minio:9000 admin password) do echo '...waiting...' && sleep 1; done; - /usr/bin/mc rm -r --force minio/warehouse; - /usr/bin/mc mb minio/warehouse; - /usr/bin/mc policy set public minio/warehouse; + aws --endpoint-url http://seaweedfs:8333 s3 mb s3://warehouse; + aws --endpoint-url http://seaweedfs:8333 s3api put-bucket-policy \ + --bucket warehouse \ + --policy '{ + \"Version\": \"2012-10-17\", + \"Statement\": [{ + \"Sid\": \"public\", + \"Effect\": \"Allow\", + \"Principal\": \"*\", + \"Action\": \"s3:*\", + \"Resource\": [\"arn:aws:s3:::warehouse\", \"arn:aws:s3:::warehouse/*\"] + }] + }'; " networks: diff --git a/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml b/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml index 4b46028eafda..f41c05f5f671 100644 --- a/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml +++ b/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml @@ -19,26 +19,45 @@ volumes: data: {} services: - minio: - image: minio/minio - hostname: minio + seaweedfs: + image: chrislusf/seaweedfs:latest + hostname: seaweedfs environment: - - MINIO_ROOT_USER=minioadmin - - MINIO_ROOT_PASSWORD=minioadmin + - AWS_ACCESS_KEY_ID=admin + - AWS_SECRET_ACCESS_KEY=password ports: - - 9000:9000 - - 9001:9001 + - 8333:8333 # S3 API + - 23646:23646 # Admin UI volumes: - data:/data - command: server /data --console-address ":9001" + command: "mini -dir=/data" create-bucket: - image: minio/mc + image: amazon/aws-cli:latest depends_on: - - minio + - seaweedfs volumes: - data:/data - entrypoint: mc mb /data/bucket + environment: + - AWS_ACCESS_KEY_ID=admin + - AWS_SECRET_ACCESS_KEY=password + - AWS_REGION=us-east-1 + entrypoint: | + /bin/sh -c " + until aws --endpoint-url http://seaweedfs:8333 s3 mb s3://bucket 2>/dev/null; do echo '...waiting for SeaweedFS...' && sleep 1; done; + aws --endpoint-url http://seaweedfs:8333 s3api put-bucket-policy \ + --bucket bucket \ + --policy '{ + \"Version\": \"2012-10-17\", + \"Statement\": [{ + \"Sid\": \"public\", + \"Effect\": \"Allow\", + \"Principal\": \"*\", + \"Action\": \"s3:*\", + \"Resource\": [\"arn:aws:s3:::bucket\", \"arn:aws:s3:::bucket/*\"] + }] + }'; + " iceberg: image: apache/iceberg-rest-fixture @@ -52,10 +71,10 @@ services: - CATALOG_WAREHOUSE=s3://bucket/warehouse/ - CATALOG_URI=jdbc:sqlite:file:/tmp/iceberg_rest_mode=memory - CATALOG_IO__IMPL=org.apache.iceberg.aws.s3.S3FileIO - - CATALOG_S3_ENDPOINT=http://minio:9000 + - CATALOG_S3_ENDPOINT=http://seaweedfs:8333 - CATALOG_S3_PATH__STYLE__ACCESS=true - - CATALOG_S3_ACCESS__KEY__ID=minioadmin - - CATALOG_S3_SECRET__ACCESS__KEY=minioadmin + - CATALOG_S3_ACCESS__KEY__ID=admin + - CATALOG_S3_SECRET__ACCESS__KEY=password kafka: image: confluentinc/cp-kafka:7.8.1 diff --git a/kafka-connect/kafka-connect-runtime/src/integration/java/org/apache/iceberg/connect/TestContext.java b/kafka-connect/kafka-connect-runtime/src/integration/java/org/apache/iceberg/connect/TestContext.java index 2a1ded6cd8a1..4a691f91fe43 100644 --- a/kafka-connect/kafka-connect-runtime/src/integration/java/org/apache/iceberg/connect/TestContext.java +++ b/kafka-connect/kafka-connect-runtime/src/integration/java/org/apache/iceberg/connect/TestContext.java @@ -43,11 +43,11 @@ public class TestContext { public static final ObjectMapper MAPPER = new ObjectMapper(); public static final int CONNECT_PORT = 8083; - private static final int MINIO_PORT = 9000; + private static final int S3_PORT = 8333; private static final int CATALOG_PORT = 8181; private static final String BOOTSTRAP_SERVERS = "localhost:29092"; - private static final String AWS_ACCESS_KEY = "minioadmin"; - private static final String AWS_SECRET_KEY = "minioadmin"; + private static final String AWS_ACCESS_KEY = "admin"; + private static final String AWS_SECRET_KEY = "password"; private static final String AWS_REGION = "us-east-1"; public static synchronized TestContext instance() { @@ -82,7 +82,7 @@ public Catalog initLocalCatalog() { ImmutableMap.builder() .put(CatalogProperties.URI, localCatalogUri) .put(CatalogProperties.FILE_IO_IMPL, "org.apache.iceberg.aws.s3.S3FileIO") - .put("s3.endpoint", "http://localhost:" + MINIO_PORT) + .put("s3.endpoint", "http://localhost:" + S3_PORT) .put("s3.access-key-id", AWS_ACCESS_KEY) .put("s3.secret-access-key", AWS_SECRET_KEY) .put("s3.path-style-access", "true") @@ -100,7 +100,7 @@ public Map connectorCatalogProperties() { .put( "iceberg.catalog." + CatalogProperties.FILE_IO_IMPL, "org.apache.iceberg.aws.s3.S3FileIO") - .put("iceberg.catalog.s3.endpoint", "http://minio:" + MINIO_PORT) + .put("iceberg.catalog.s3.endpoint", "http://seaweedfs:" + S3_PORT) .put("iceberg.catalog.s3.access-key-id", AWS_ACCESS_KEY) .put("iceberg.catalog.s3.secret-access-key", AWS_SECRET_KEY) .put("iceberg.catalog.s3.path-style-access", true) diff --git a/site/docs/spark-quickstart.md b/site/docs/spark-quickstart.md index 38149ad35038..daa54f1d9658 100644 --- a/site/docs/spark-quickstart.md +++ b/site/docs/spark-quickstart.md @@ -46,7 +46,7 @@ services: iceberg_net: depends_on: - rest - - minio + - seaweedfs volumes: - ./warehouse:/home/iceberg/warehouse - ./notebooks:/home/iceberg/notebooks/notebooks @@ -72,27 +72,33 @@ services: - AWS_REGION=us-east-1 - CATALOG_WAREHOUSE=s3://warehouse/ - CATALOG_IO__IMPL=org.apache.iceberg.aws.s3.S3FileIO - - CATALOG_S3_ENDPOINT=http://minio:9000 - minio: - image: minio/minio - container_name: minio + - CATALOG_S3_ENDPOINT=http://seaweedfs:8333 + seaweedfs: + image: chrislusf/seaweedfs:latest + container_name: seaweedfs environment: - - MINIO_ROOT_USER=admin - - MINIO_ROOT_PASSWORD=password - - MINIO_DOMAIN=minio + - AWS_ACCESS_KEY_ID=admin + - AWS_SECRET_ACCESS_KEY=password networks: iceberg_net: aliases: - - warehouse.minio + - warehouse.seaweedfs ports: - - 9001:9001 - - 9000:9000 - command: ["server", "/data", "--console-address", ":9001"] - mc: + - 8333:8333 # S3 API + - 23646:23646 # Admin UI + command: "mini -dir=/data" + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8333/status"] + interval: 3s + timeout: 2s + retries: 10 + start_period: 5s + create-bucket: + image: amazon/aws-cli:latest + container_name: create-bucket depends_on: - - minio - image: minio/mc - container_name: mc + seaweedfs: + condition: service_healthy networks: iceberg_net: environment: @@ -101,11 +107,19 @@ services: - AWS_REGION=us-east-1 entrypoint: | /bin/sh -c " - until (/usr/bin/mc alias set minio http://minio:9000 admin password) do echo '...waiting...' && sleep 1; done; - /usr/bin/mc rm -r --force minio/warehouse; - /usr/bin/mc mb minio/warehouse; - /usr/bin/mc policy set public minio/warehouse; - tail -f /dev/null + aws --endpoint-url http://seaweedfs:8333 s3 mb s3://warehouse; + aws --endpoint-url http://seaweedfs:8333 s3api put-bucket-policy \ + --bucket warehouse \ + --policy '{ + \"Version\": \"2012-10-17\", + \"Statement\": [{ + \"Sid\": \"public\", + \"Effect\": \"Allow\", + \"Principal\": \"*\", + \"Action\": \"s3:*\", + \"Resource\": [\"arn:aws:s3:::warehouse\", \"arn:aws:s3:::warehouse/*\"] + }] + }'; " networks: iceberg_net: From 27b33d3a94d0dd803d30cce0af8b10702f51658e Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 00:07:07 -0700 Subject: [PATCH 2/8] remove latest tag from documentation, use tag 4.16 for testing addressing https://github.com/apache/iceberg/pull/15577/changes#r2916164516 --- docker/iceberg-flink-quickstart/docker-compose.yml | 4 ++-- kafka-connect/kafka-connect-runtime/docker/docker-compose.yml | 4 ++-- site/docs/spark-quickstart.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/iceberg-flink-quickstart/docker-compose.yml b/docker/iceberg-flink-quickstart/docker-compose.yml index acbe00ff3076..bc3f90453914 100644 --- a/docker/iceberg-flink-quickstart/docker-compose.yml +++ b/docker/iceberg-flink-quickstart/docker-compose.yml @@ -98,7 +98,7 @@ services: # SeaweedFS for S3-compatible object storage (mini mode) seaweedfs: - image: chrislusf/seaweedfs:latest + image: chrislusf/seaweedfs hostname: seaweedfs environment: AWS_ACCESS_KEY_ID: admin @@ -117,7 +117,7 @@ services: # Create the warehouse bucket create-bucket: - image: amazon/aws-cli:latest + image: amazon/aws-cli depends_on: seaweedfs: condition: service_healthy diff --git a/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml b/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml index f41c05f5f671..54fdc933fd04 100644 --- a/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml +++ b/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml @@ -20,7 +20,7 @@ volumes: services: seaweedfs: - image: chrislusf/seaweedfs:latest + image: chrislusf/seaweedfs:4.16 hostname: seaweedfs environment: - AWS_ACCESS_KEY_ID=admin @@ -33,7 +33,7 @@ services: command: "mini -dir=/data" create-bucket: - image: amazon/aws-cli:latest + image: amazon/aws-cli depends_on: - seaweedfs volumes: diff --git a/site/docs/spark-quickstart.md b/site/docs/spark-quickstart.md index daa54f1d9658..bfcd1e3f567a 100644 --- a/site/docs/spark-quickstart.md +++ b/site/docs/spark-quickstart.md @@ -74,7 +74,7 @@ services: - CATALOG_IO__IMPL=org.apache.iceberg.aws.s3.S3FileIO - CATALOG_S3_ENDPOINT=http://seaweedfs:8333 seaweedfs: - image: chrislusf/seaweedfs:latest + image: chrislusf/seaweedfs container_name: seaweedfs environment: - AWS_ACCESS_KEY_ID=admin @@ -94,7 +94,7 @@ services: retries: 10 start_period: 5s create-bucket: - image: amazon/aws-cli:latest + image: amazon/aws-cli container_name: create-bucket depends_on: seaweedfs: From dadd7c0aa2d39ddec75f0ac1f373fe9c1bd19435 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 01:10:57 -0700 Subject: [PATCH 3/8] Replace MinIO with SeaweedFS in Flink quickstart docs and Daft integration guide Update S3 endpoint references from minio:9000 to seaweedfs:8333 to match the docker-compose configuration. Update Daft integration guide to reference SeaweedFS instead of MinIO. --- docker/iceberg-flink-quickstart/test.sql | 2 +- site/docs/flink-quickstart.md | 6 +++--- site/docs/integrations/daft.md | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docker/iceberg-flink-quickstart/test.sql b/docker/iceberg-flink-quickstart/test.sql index a7de5a89581f..40d6784e5448 100644 --- a/docker/iceberg-flink-quickstart/test.sql +++ b/docker/iceberg-flink-quickstart/test.sql @@ -18,7 +18,7 @@ CREATE CATALOG iceberg_catalog WITH ( 'uri' = 'http://iceberg-rest:8181', 'warehouse' = 's3://warehouse/', 'io-impl' = 'org.apache.iceberg.aws.s3.S3FileIO', - 's3.endpoint' = 'http://minio:9000', + 's3.endpoint' = 'http://seaweedfs:8333', 's3.access-key-id' = 'admin', 's3.secret-access-key' = 'password', 's3.path-style-access' = 'true' diff --git a/site/docs/flink-quickstart.md b/site/docs/flink-quickstart.md index 657d13d80681..8a8c3d484e98 100644 --- a/site/docs/flink-quickstart.md +++ b/site/docs/flink-quickstart.md @@ -31,7 +31,7 @@ The quickstart includes: * A local Flink cluster (Job Manager and Task Manager) * Iceberg REST Catalog -* MinIO (local S3 storage) +* SeaweedFS (local S3 storage) ![An overview of the Flink quickstart containers](/assets/images/flink-quickstart.excalidraw.png) @@ -65,7 +65,7 @@ CREATE CATALOG iceberg_catalog WITH ( 'uri' = 'http://iceberg-rest:8181', 'warehouse' = 's3://warehouse/', 'io-impl' = 'org.apache.iceberg.aws.s3.S3FileIO', - 's3.endpoint' = 'http://minio:9000', + 's3.endpoint' = 'http://seaweedfs:8333', 's3.access-key-id' = 'admin', 's3.secret-access-key' = 'password', 's3.path-style-access' = 'true' @@ -150,7 +150,7 @@ CREATE TABLE taxis_inline_config ( 'uri' = 'http://iceberg-rest:8181', 'warehouse' = 's3://warehouse/', 'io-impl' = 'org.apache.iceberg.aws.s3.S3FileIO', - 's3.endpoint' = 'http://minio:9000', + 's3.endpoint' = 'http://seaweedfs:8333', 's3.access-key-id' = 'admin', 's3.secret-access-key' = 'password', 's3.path-style-access' = 'true' diff --git a/site/docs/integrations/daft.md b/site/docs/integrations/daft.md index 01ce305aff1b..434c08a8da19 100644 --- a/site/docs/integrations/daft.md +++ b/site/docs/integrations/daft.md @@ -51,8 +51,8 @@ catalog: default: # URL to the Iceberg REST server Docker container uri: http://localhost:8181 - # URL and credentials for the MinIO Docker container - s3.endpoint: http://localhost:9000 + # URL and credentials for the SeaweedFS Docker container + s3.endpoint: http://localhost:8333 s3.access-key-id: admin s3.secret-access-key: password ``` @@ -63,10 +63,10 @@ Here is how the Iceberg table `demo.nyc.taxis` can be loaded into Daft: import daft from pyiceberg.catalog import load_catalog -# Configure Daft to use the local MinIO Docker container for any S3 operations +# Configure Daft to use the local SeaweedFS Docker container for any S3 operations daft.set_planning_config( default_io_config=daft.io.IOConfig( - s3=daft.io.S3Config(endpoint_url="http://localhost:9000"), + s3=daft.io.S3Config(endpoint_url="http://localhost:8333"), ) ) From 368537ae1f306b7cf2be259c76fc536c660467ae Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 01:11:04 -0700 Subject: [PATCH 4/8] Remove testcontainers-minio dependency SeaweedFS integration tests use GenericContainer from the core testcontainers library, so the dedicated MinIO module is no longer needed. --- build.gradle | 1 - gradle/libs.versions.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index 765a7fe8203e..5769ab61cf7a 100644 --- a/build.gradle +++ b/build.gradle @@ -528,7 +528,6 @@ project(':iceberg-aws') { testImplementation libs.sqlite.jdbc testImplementation libs.testcontainers testImplementation libs.testcontainers.junit.jupiter - testImplementation libs.testcontainers.minio testImplementation libs.httpcomponents.httpclient5 testImplementation libs.mockserver.netty testImplementation libs.mockserver.client.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8cd9e566b367..6f41bd2cf4c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -224,6 +224,5 @@ orc-tools = { module = "org.apache.orc:orc-tools", version.ref = "orc" } sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite-jdbc" } testcontainers = { module = "org.testcontainers:testcontainers", version.ref = "testcontainers" } testcontainers-junit-jupiter = { module = "org.testcontainers:testcontainers-junit-jupiter", version.ref = "testcontainers" } -testcontainers-minio = { module = "org.testcontainers:testcontainers-minio", version.ref = "testcontainers" } tez08-dag = { module = "org.apache.tez:tez-dag", version.ref = "tez08" } tez08-mapreduce = { module = "org.apache.tez:tez-mapreduce", version.ref = "tez08" } From 30403dbefbe1ca0ac75c30b3b3ab93fca84a69e3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 01:11:21 -0700 Subject: [PATCH 5/8] Replace MinIO testcontainers with SeaweedFS in S3 integration tests Replace MinioUtil with SeaweedFSUtil backed by GenericContainer running chrislusf/seaweedfs in mini mode. Update all S3 integration tests (TestS3FileIO, TestS3InputStream, TestS3OutputStream, TestS3RestSigner) to use the new utility. Rename TestS3FileIOWithLegacyMinIO to TestS3FileIOWithLegacyMd5. --- .../org/apache/iceberg/aws/s3/MinioUtil.java | 76 --------------- .../apache/iceberg/aws/s3/SeaweedFSUtil.java | 96 +++++++++++++++++++ .../apache/iceberg/aws/s3/TestS3FileIO.java | 10 +- ...IO.java => TestS3FileIOWithLegacyMd5.java} | 10 +- .../iceberg/aws/s3/TestS3InputStream.java | 6 +- .../iceberg/aws/s3/TestS3OutputStream.java | 6 +- ...tMinioUtil.java => TestSeaweedFSUtil.java} | 9 +- .../aws/s3/signer/TestS3RestSigner.java | 19 ++-- 8 files changed, 123 insertions(+), 109 deletions(-) delete mode 100644 aws/src/integration/java/org/apache/iceberg/aws/s3/MinioUtil.java create mode 100644 aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java rename aws/src/integration/java/org/apache/iceberg/aws/s3/{TestS3FileIOWithLegacyMinIO.java => TestS3FileIOWithLegacyMd5.java} (75%) rename aws/src/integration/java/org/apache/iceberg/aws/s3/{TestMinioUtil.java => TestSeaweedFSUtil.java} (92%) diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/MinioUtil.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/MinioUtil.java deleted file mode 100644 index 90557720fa0e..000000000000 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/MinioUtil.java +++ /dev/null @@ -1,76 +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.iceberg.aws.s3; - -import java.net.URI; -import org.testcontainers.containers.MinIOContainer; -import org.testcontainers.utility.DockerImageName; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.AwsCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.LegacyMd5Plugin; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.S3ClientBuilder; - -public class MinioUtil { - public static final String LATEST_TAG = "latest"; - // This version doesn't support strong integrity checks - static final String LEGACY_TAG = "RELEASE.2024-12-18T13-15-44Z"; - - private MinioUtil() {} - - public static MinIOContainer createContainer() { - return createContainer(LATEST_TAG, null); - } - - public static MinIOContainer createContainer(String tag, AwsCredentials credentials) { - var container = new MinIOContainer(DockerImageName.parse("minio/minio").withTag(tag)); - - // this enables virtual-host-style requests. see - // https://github.com/minio/minio/tree/master/docs/config#domain - container.withEnv("MINIO_DOMAIN", "localhost"); - - if (credentials != null) { - container.withUserName(credentials.accessKeyId()); - container.withPassword(credentials.secretAccessKey()); - } - - return container; - } - - public static S3Client createS3Client(MinIOContainer container) { - return createS3Client(container, false); - } - - public static S3Client createS3Client(MinIOContainer container, boolean legacyMd5PluginEnabled) { - URI uri = URI.create(container.getS3URL()); - S3ClientBuilder builder = S3Client.builder(); - if (legacyMd5PluginEnabled) { - builder.addPlugin(LegacyMd5Plugin.create()); - } - builder.credentialsProvider( - StaticCredentialsProvider.create( - AwsBasicCredentials.create(container.getUserName(), container.getPassword()))); - builder.applyMutation(mutator -> mutator.endpointOverride(uri)); - builder.region(Region.US_EAST_1); - builder.forcePathStyle(true); // OSX won't resolve subdomains - return builder.build(); - } -} diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java new file mode 100644 index 000000000000..d4b68cace0e6 --- /dev/null +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java @@ -0,0 +1,96 @@ +/* + * 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.iceberg.aws.s3; + +import java.net.URI; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.LegacyMd5Plugin; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; + +public class SeaweedFSUtil { + public static final String IMAGE = "chrislusf/seaweedfs"; + public static final String LATEST_TAG = "latest"; + public static final int S3_PORT = 8333; + private static final String DEFAULT_ACCESS_KEY = "admin"; + private static final String DEFAULT_SECRET_KEY = "password"; + + private SeaweedFSUtil() {} + + @SuppressWarnings("resource") + public static GenericContainer createContainer() { + return createContainer(LATEST_TAG, null); + } + + @SuppressWarnings("resource") + public static GenericContainer createContainer(String tag, AwsCredentials credentials) { + String accessKey = + credentials != null ? credentials.accessKeyId() : DEFAULT_ACCESS_KEY; + String secretKey = + credentials != null ? credentials.secretAccessKey() : DEFAULT_SECRET_KEY; + + GenericContainer container = + new GenericContainer<>(IMAGE + ":" + tag) + .withExposedPorts(S3_PORT) + .withCommand("mini -dir=/data") + .withEnv("AWS_ACCESS_KEY_ID", accessKey) + .withEnv("AWS_SECRET_ACCESS_KEY", secretKey) + .waitingFor( + new HttpWaitStrategy() + .forPort(S3_PORT) + .forPath("/status") + .forStatusCode(200)); + + return container; + } + + public static String getS3URL(GenericContainer container) { + return String.format( + "http://%s:%d", container.getHost(), container.getMappedPort(S3_PORT)); + } + + public static S3Client createS3Client(GenericContainer container) { + return createS3Client(container, false); + } + + public static S3Client createS3Client( + GenericContainer container, boolean legacyMd5PluginEnabled) { + URI uri = URI.create(getS3URL(container)); + String accessKey = + container.getEnvMap().getOrDefault("AWS_ACCESS_KEY_ID", DEFAULT_ACCESS_KEY); + String secretKey = + container.getEnvMap().getOrDefault("AWS_SECRET_ACCESS_KEY", DEFAULT_SECRET_KEY); + + S3ClientBuilder builder = S3Client.builder(); + if (legacyMd5PluginEnabled) { + builder.addPlugin(LegacyMd5Plugin.create()); + } + builder.credentialsProvider( + StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey))); + builder.applyMutation(mutator -> mutator.endpointOverride(uri)); + builder.region(Region.US_EAST_1); + builder.forcePathStyle(true); // OSX won't resolve subdomains + return builder.build(); + } +} diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java index ebee07e53e4c..2d5aba3a08b5 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java @@ -88,7 +88,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mockito; -import org.testcontainers.containers.MinIOContainer; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import software.amazon.awssdk.awscore.AwsServiceClientConfiguration; @@ -115,10 +115,10 @@ @Testcontainers public class TestS3FileIO { - @Container private final MinIOContainer minio = createMinIOContainer(); + @Container private final GenericContainer seaweedfs = createSeaweedFSContainer(); private final SerializableSupplier s3 = - () -> MinioUtil.createS3Client(minio, legacyMd5PluginEnabled()); + () -> SeaweedFSUtil.createS3Client(seaweedfs, legacyMd5PluginEnabled()); private final S3Client s3mock = mock(S3Client.class, delegatesTo(s3.get())); private final Random random = new Random(1); private final int numBucketsForBatchDeletion = 3; @@ -136,8 +136,8 @@ public class TestS3FileIO { "s3.delete.batch-size", Integer.toString(batchDeletionSize)); - protected MinIOContainer createMinIOContainer() { - MinIOContainer container = MinioUtil.createContainer(); + protected GenericContainer createSeaweedFSContainer() { + GenericContainer container = SeaweedFSUtil.createContainer(); container.start(); return container; } diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIOWithLegacyMinIO.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIOWithLegacyMd5.java similarity index 75% rename from aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIOWithLegacyMinIO.java rename to aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIOWithLegacyMd5.java index f3c0efd706d9..c414a2409159 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIOWithLegacyMinIO.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIOWithLegacyMd5.java @@ -18,18 +18,10 @@ */ package org.apache.iceberg.aws.s3; -import org.testcontainers.containers.MinIOContainer; import org.testcontainers.junit.jupiter.Testcontainers; @Testcontainers -public class TestS3FileIOWithLegacyMinIO extends TestS3FileIO { - @Override - protected MinIOContainer createMinIOContainer() { - MinIOContainer container = MinioUtil.createContainer(MinioUtil.LEGACY_TAG, null); - container.start(); - return container; - } - +public class TestS3FileIOWithLegacyMd5 extends TestS3FileIO { @Override protected boolean legacyMd5PluginEnabled() { return true; diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3InputStream.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3InputStream.java index f8903842df37..a9d57f5e1dbd 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3InputStream.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3InputStream.java @@ -29,7 +29,7 @@ import org.apache.iceberg.io.SeekableInputStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.MinIOContainer; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import software.amazon.awssdk.core.sync.RequestBody; @@ -41,9 +41,9 @@ @Testcontainers public class TestS3InputStream { - @Container private static final MinIOContainer MINIO = MinioUtil.createContainer(); + @Container private static final GenericContainer SEAWEEDFS = SeaweedFSUtil.createContainer(); - private final S3Client s3 = MinioUtil.createS3Client(MINIO); + private final S3Client s3 = SeaweedFSUtil.createS3Client(SEAWEEDFS); private final Random random = new Random(1); @BeforeEach diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3OutputStream.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3OutputStream.java index e59bb182cb18..913285c9669a 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3OutputStream.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3OutputStream.java @@ -52,7 +52,7 @@ import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.testcontainers.containers.MinIOContainer; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import software.amazon.awssdk.core.ResponseBytes; @@ -77,10 +77,10 @@ public class TestS3OutputStream { private static final String BUCKET = "test-bucket"; private static final int FIVE_MBS = 5 * 1024 * 1024; - @Container private static final MinIOContainer MINIO = MinioUtil.createContainer(); + @Container private static final GenericContainer SEAWEEDFS = SeaweedFSUtil.createContainer(); @TempDir private static Path tmpDir = null; - private final S3Client s3 = MinioUtil.createS3Client(MINIO); + private final S3Client s3 = SeaweedFSUtil.createS3Client(SEAWEEDFS); private final S3Client s3mock = mock(S3Client.class, delegatesTo(s3)); private final Random random = new Random(1); @TempDir private Path newTmpDirectory; diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestMinioUtil.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestSeaweedFSUtil.java similarity index 92% rename from aws/src/integration/java/org/apache/iceberg/aws/s3/TestMinioUtil.java rename to aws/src/integration/java/org/apache/iceberg/aws/s3/TestSeaweedFSUtil.java index 9955aa7f8459..03d6e4797d21 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestMinioUtil.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestSeaweedFSUtil.java @@ -23,7 +23,7 @@ import java.util.UUID; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.MinIOContainer; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import software.amazon.awssdk.core.sync.RequestBody; @@ -36,12 +36,13 @@ import software.amazon.awssdk.services.s3.model.S3Exception; @Testcontainers -public class TestMinioUtil { - @Container private static final MinIOContainer MINIO = MinioUtil.createContainer(); +public class TestSeaweedFSUtil { + @Container + private static final GenericContainer SEAWEEDFS = SeaweedFSUtil.createContainer(); @Test void validateS3ConditionalWrites() { - S3Client s3Client = MinioUtil.createS3Client(MINIO); + S3Client s3Client = SeaweedFSUtil.createS3Client(SEAWEEDFS); String bucket = "test-bucket-" + UUID.randomUUID(); diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java index b51d97cc611a..d8d7f91812f1 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java @@ -32,7 +32,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import org.apache.iceberg.aws.s3.MinioUtil; +import org.apache.iceberg.aws.s3.SeaweedFSUtil; import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList; import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap; import org.apache.iceberg.rest.auth.OAuth2Properties; @@ -46,7 +46,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.MinIOContainer; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; @@ -87,8 +87,9 @@ public class TestS3RestSigner { AwsBasicCredentials.create("accessKeyId", "secretAccessKey")); @Container - private static final MinIOContainer MINIO_CONTAINER = - MinioUtil.createContainer(MinioUtil.LATEST_TAG, CREDENTIALS_PROVIDER.resolveCredentials()); + private static final GenericContainer SEAWEEDFS_CONTAINER = + SeaweedFSUtil.createContainer( + SeaweedFSUtil.LATEST_TAG, CREDENTIALS_PROVIDER.resolveCredentials()); private static Server httpServer; private static ValidatingSigner validatingSigner; @@ -96,7 +97,7 @@ public class TestS3RestSigner { @BeforeAll public static void beforeClass() throws Exception { - assertThat(MINIO_CONTAINER.isRunning()).isTrue(); + assertThat(SEAWEEDFS_CONTAINER.isRunning()).isTrue(); if (null == httpServer) { httpServer = initHttpServer(); @@ -154,7 +155,7 @@ public static void afterClass() throws Exception { @BeforeEach public void before() throws Exception { - MINIO_CONTAINER.start(); + SEAWEEDFS_CONTAINER.start(); s3 = S3Client.builder() .region(REGION) @@ -163,7 +164,7 @@ public void before() throws Exception { s3ClientBuilder -> s3ClientBuilder.httpClientBuilder( software.amazon.awssdk.http.apache.ApacheHttpClient.builder())) - .endpointOverride(URI.create(MINIO_CONTAINER.getS3URL())) + .endpointOverride(URI.create(SeaweedFSUtil.getS3URL(SEAWEEDFS_CONTAINER))) .forcePathStyle(true) // OSX won't resolve subdomains .overrideConfiguration( c -> c.putAdvancedOption(SdkAdvancedClientOption.SIGNER, validatingSigner)) @@ -251,7 +252,7 @@ public void validatedCreateMultiPartUpload() { @AfterEach public void after() { - MINIO_CONTAINER.stop(); + SEAWEEDFS_CONTAINER.stop(); } @Test @@ -381,7 +382,7 @@ private SdkHttpFullRequest signWithAwsSigner( * payload signing here. * - *

However, we run Minio with http and don't have a means to disable payload signing in + *

However, we run SeaweedFS with http and don't have a means to disable payload signing in * order to achieve the same signature in the {@link ValidatingSigner#sign(SdkHttpFullRequest, * ExecutionAttributes)} check above. */ From 416c0f4fa082952e43390c2f229de95e87363a8a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 01:17:35 -0700 Subject: [PATCH 6/8] Fix spotless formatting in SeaweedFS integration tests --- .../apache/iceberg/aws/s3/SeaweedFSUtil.java | 17 +++++------------ .../iceberg/aws/s3/TestSeaweedFSUtil.java | 3 +-- .../iceberg/aws/s3/signer/TestS3RestSigner.java | 4 ++-- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java index d4b68cace0e6..fa475513adb0 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java @@ -45,10 +45,8 @@ public static GenericContainer createContainer() { @SuppressWarnings("resource") public static GenericContainer createContainer(String tag, AwsCredentials credentials) { - String accessKey = - credentials != null ? credentials.accessKeyId() : DEFAULT_ACCESS_KEY; - String secretKey = - credentials != null ? credentials.secretAccessKey() : DEFAULT_SECRET_KEY; + String accessKey = credentials != null ? credentials.accessKeyId() : DEFAULT_ACCESS_KEY; + String secretKey = credentials != null ? credentials.secretAccessKey() : DEFAULT_SECRET_KEY; GenericContainer container = new GenericContainer<>(IMAGE + ":" + tag) @@ -57,17 +55,13 @@ public static GenericContainer createContainer(String tag, AwsCredentials cre .withEnv("AWS_ACCESS_KEY_ID", accessKey) .withEnv("AWS_SECRET_ACCESS_KEY", secretKey) .waitingFor( - new HttpWaitStrategy() - .forPort(S3_PORT) - .forPath("/status") - .forStatusCode(200)); + new HttpWaitStrategy().forPort(S3_PORT).forPath("/status").forStatusCode(200)); return container; } public static String getS3URL(GenericContainer container) { - return String.format( - "http://%s:%d", container.getHost(), container.getMappedPort(S3_PORT)); + return String.format("http://%s:%d", container.getHost(), container.getMappedPort(S3_PORT)); } public static S3Client createS3Client(GenericContainer container) { @@ -77,8 +71,7 @@ public static S3Client createS3Client(GenericContainer container) { public static S3Client createS3Client( GenericContainer container, boolean legacyMd5PluginEnabled) { URI uri = URI.create(getS3URL(container)); - String accessKey = - container.getEnvMap().getOrDefault("AWS_ACCESS_KEY_ID", DEFAULT_ACCESS_KEY); + String accessKey = container.getEnvMap().getOrDefault("AWS_ACCESS_KEY_ID", DEFAULT_ACCESS_KEY); String secretKey = container.getEnvMap().getOrDefault("AWS_SECRET_ACCESS_KEY", DEFAULT_SECRET_KEY); diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestSeaweedFSUtil.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestSeaweedFSUtil.java index 03d6e4797d21..558490628de5 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestSeaweedFSUtil.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestSeaweedFSUtil.java @@ -37,8 +37,7 @@ @Testcontainers public class TestSeaweedFSUtil { - @Container - private static final GenericContainer SEAWEEDFS = SeaweedFSUtil.createContainer(); + @Container private static final GenericContainer SEAWEEDFS = SeaweedFSUtil.createContainer(); @Test void validateS3ConditionalWrites() { diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java index d8d7f91812f1..289024007ba2 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/signer/TestS3RestSigner.java @@ -382,8 +382,8 @@ private SdkHttpFullRequest signWithAwsSigner( * payload signing here. * - *

However, we run SeaweedFS with http and don't have a means to disable payload signing in - * order to achieve the same signature in the {@link ValidatingSigner#sign(SdkHttpFullRequest, + *

However, we run SeaweedFS with http and don't have a means to disable payload signing + * in order to achieve the same signature in the {@link ValidatingSigner#sign(SdkHttpFullRequest, * ExecutionAttributes)} check above. */ private static class CustomAwsS3V4Signer extends AbstractAwsS3V4Signer { From 0284990f1fc122c1917025fdb106882fdcade7b6 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 01:31:38 -0700 Subject: [PATCH 7/8] fix to 4.16 for testing 4.17 to resolve v2 object listing --- docker/iceberg-flink-quickstart/docker-compose.yml | 2 +- kafka-connect/kafka-connect-runtime/docker/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/iceberg-flink-quickstart/docker-compose.yml b/docker/iceberg-flink-quickstart/docker-compose.yml index bc3f90453914..84b57b91c5b1 100644 --- a/docker/iceberg-flink-quickstart/docker-compose.yml +++ b/docker/iceberg-flink-quickstart/docker-compose.yml @@ -98,7 +98,7 @@ services: # SeaweedFS for S3-compatible object storage (mini mode) seaweedfs: - image: chrislusf/seaweedfs + image: chrislusf/seaweedfs:4.17 hostname: seaweedfs environment: AWS_ACCESS_KEY_ID: admin diff --git a/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml b/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml index 54fdc933fd04..cefd22b76cba 100644 --- a/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml +++ b/kafka-connect/kafka-connect-runtime/docker/docker-compose.yml @@ -20,7 +20,7 @@ volumes: services: seaweedfs: - image: chrislusf/seaweedfs:4.16 + image: chrislusf/seaweedfs:4.17 hostname: seaweedfs environment: - AWS_ACCESS_KEY_ID=admin From ddcda8d0323d74bd9dcb4f2f911fa40f4d704db2 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 11:57:17 -0700 Subject: [PATCH 8/8] use seaweedfs 4.17 --- .../java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java index fa475513adb0..b260e192d19d 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/SeaweedFSUtil.java @@ -31,7 +31,7 @@ public class SeaweedFSUtil { public static final String IMAGE = "chrislusf/seaweedfs"; - public static final String LATEST_TAG = "latest"; + public static final String LATEST_TAG = "4.17"; public static final int S3_PORT = 8333; private static final String DEFAULT_ACCESS_KEY = "admin"; private static final String DEFAULT_SECRET_KEY = "password";