This is an automated email from the ASF dual-hosted git repository.

milenkovicm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-ballista.git


The following commit(s) were added to refs/heads/main by this push:
     new 2da1bf6ed ci: run the S3 integration tests against RustFS instead of 
MinIO (#2488)
2da1bf6ed is described below

commit 2da1bf6ed1119c925c7fe568147b21cfb211bfea
Author: NoahKusaba <[email protected]>
AuthorDate: Thu Sep 24 11:05:08 2026 -0400

    ci: run the S3 integration tests against RustFS instead of MinIO (#2488)
    
    MinIO withdrew its minio/minio image from Docker Hub on 2026-09-11 and from
    quay.io on 2026-09-24, so the object_store tests can no longer pull it and
    fail on every run. Use RustFS, an S3-compatible server published on Docker
    Hub, pinned to 1.0.0.
    
    testcontainers-modules was only here for its MinIO module, so depend on
    testcontainers directly and start a GenericImage, waiting on RustFS's
    /health endpoint since it logs only warnings by default. The bucket is still
    created with mkdir under the data volume, which RustFS serves as a bucket.
    
    custom-client's instructions start RustFS too, and now create the bucket the
    example writes to.
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 Cargo.lock                         | 12 ++-------
 examples/Cargo.toml                |  2 +-
 examples/examples/custom-client.rs | 13 +++++-----
 examples/tests/common/mod.rs       | 53 +++++++++++++++++++++-----------------
 examples/tests/object_store.rs     | 53 ++++++++++++++++++++++++--------------
 5 files changed, 73 insertions(+), 60 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 74fc48ba6..e6790ad16 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1167,7 +1167,7 @@ dependencies = [
  "object_store",
  "rustls",
  "tempfile",
- "testcontainers-modules",
+ "testcontainers",
  "tokio",
  "tonic",
  "url",
@@ -7105,6 +7105,7 @@ dependencies = [
  "memchr",
  "parse-display",
  "pin-project-lite",
+ "reqwest 0.13.5",
  "serde",
  "serde_json",
  "serde_with",
@@ -7115,15 +7116,6 @@ dependencies = [
  "url",
 ]
 
-[[package]]
-name = "testcontainers-modules"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e5985fde5befe4ffa77a052e035e16c2da86e8bae301baa9f9904ad3c494d357"
-dependencies = [
- "testcontainers",
-]
-
 [[package]]
 name = "textwrap"
 version = "0.11.0"
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index 447a26a41..0e573c4ce 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -70,7 +70,7 @@ futures = { workspace = true }
 log = { workspace = true }
 object_store = { workspace = true, features = ["aws"] }
 tempfile = { workspace = true }
-testcontainers-modules = { version = "0.15", features = ["minio"] }
+testcontainers = { version = "0.27", features = ["http_wait_plain"] }
 tokio = { workspace = true, features = [
     "macros",
     "rt",
diff --git a/examples/examples/custom-client.rs 
b/examples/examples/custom-client.rs
index 430d20ae2..619e5a83e 100644
--- a/examples/examples/custom-client.rs
+++ b/examples/examples/custom-client.rs
@@ -23,23 +23,24 @@ use datafusion::{assert_batches_eq, 
prelude::SessionContext};
 /// bucket name to be used for this example
 const S3_BUCKET: &str = "ballista";
 /// S3 access key
-const S3_ACCESS_KEY_ID: &str = "MINIO";
+const S3_ACCESS_KEY_ID: &str = "BALLISTA";
 /// S3 secret key
-const S3_SECRET_KEY: &str = "MINIOSECRET";
+const S3_SECRET_KEY: &str = "BALLISTASECRET";
 ///
 /// # Extending Ballista
 ///
 /// This example demonstrates how to extend ballista scheduler and executor 
registering new object store registry.
-/// It uses local [minio](https://min.io) to act as S3 object store.
+/// It uses a local [RustFS](https://github.com/rustfs/rustfs) server to act 
as S3 object store.
 ///
 /// Ballista will be extended providing custom session configuration, runtime 
environment and session state.
 ///
-/// Minio can be started:
+/// RustFS can be started, along with the bucket this example writes to:
 ///
 /// ```bash
-/// docker run --rm -p 9000:9000  -p 9001:9001 -e "MINIO_ACCESS_KEY=MINIO"  -e 
"MINIO_SECRET_KEY=MINIOSECRET"   quay.io/minio/minio server /data 
--console-address ":9001"
+/// docker run --rm -d --name rustfs -p 9000:9000 -e 
RUSTFS_ACCESS_KEY=BALLISTA -e RUSTFS_SECRET_KEY=BALLISTASECRET 
rustfs/rustfs:1.0.0
+/// docker exec rustfs mkdir /data/ballista
 /// ```
-/// After minio, we need to start `custom-scheduler`
+/// After RustFS, we need to start `custom-scheduler`
 ///
 /// ```bash
 /// cargo run --example custom-scheduler
diff --git a/examples/tests/common/mod.rs b/examples/tests/common/mod.rs
index 32c4b5d0c..48704cb34 100644
--- a/examples/tests/common/mod.rs
+++ b/examples/tests/common/mod.rs
@@ -24,23 +24,25 @@ use ballista_scheduler::SessionBuilder;
 use datafusion::execution::SessionState;
 use datafusion::prelude::SessionConfig;
 use object_store::aws::AmazonS3Builder;
-use testcontainers_modules::minio::MinIO;
-use testcontainers_modules::testcontainers::ContainerRequest;
-use testcontainers_modules::testcontainers::core::{CmdWaitFor, ExecCommand};
-use testcontainers_modules::{minio, testcontainers::ImageExt};
+use testcontainers::core::wait::HttpWaitStrategy;
+use testcontainers::core::{CmdWaitFor, ContainerPort, ExecCommand, WaitFor};
+use testcontainers::{ContainerRequest, GenericImage, ImageExt};
 
 pub const REGION: &str = "eu-west-1";
 pub const BUCKET: &str = "ballista";
-pub const ACCESS_KEY_ID: &str = "MINIO";
-pub const SECRET_KEY: &str = "MINIOMINIO";
+pub const ACCESS_KEY_ID: &str = "BALLISTA";
+pub const SECRET_KEY: &str = "BALLISTASECRET";
 
-/// Registry override for the image pinned by `testcontainers-modules`.
+/// S3-compatible server the integration tests run against.
 ///
-/// MinIO withdrew `minio/minio` from Docker Hub on 2026-09-11, so the image 
the
-/// crate pins no longer resolves and every test that starts a container 
panics.
-/// quay.io still serves the same tag, so only the registry changes here.
-/// See <https://github.com/apache/datafusion/issues/25215>.
-const MINIO_IMAGE_NAME: &str = "quay.io/minio/minio";
+/// These tests used MinIO until it withdrew its `minio/minio` image from 
Docker Hub
+/// (2026-09-11) and then quay.io (2026-09-24). RustFS is an S3-compatible 
server
+/// published on Docker Hub; see <https://github.com/rustfs/rustfs>.
+const RUSTFS_IMAGE: &str = "rustfs/rustfs";
+const RUSTFS_TAG: &str = "1.0.0";
+
+/// Port the S3 API listens on inside the container.
+pub const S3_PORT: u16 = 9000;
 
 #[allow(dead_code)]
 pub fn create_s3_store(
@@ -59,26 +61,29 @@ pub fn create_s3_store(
 }
 
 #[allow(dead_code)]
-pub fn create_minio_container() -> ContainerRequest<minio::MinIO> {
-    MinIO::default()
-        .with_name(MINIO_IMAGE_NAME)
-        .with_env_var("MINIO_ACCESS_KEY", ACCESS_KEY_ID)
-        .with_env_var("MINIO_SECRET_KEY", SECRET_KEY)
+pub fn create_s3_container() -> ContainerRequest<GenericImage> {
+    // RustFS logs only warnings by default, so wait on its health endpoint
+    // rather than a startup message.
+    let ready = HttpWaitStrategy::new("/health")
+        .with_port(ContainerPort::Tcp(S3_PORT))
+        .with_expected_status_code(200u16);
+
+    GenericImage::new(RUSTFS_IMAGE, RUSTFS_TAG)
+        .with_exposed_port(ContainerPort::Tcp(S3_PORT))
+        .with_wait_for(WaitFor::http(ready))
+        .with_env_var("RUSTFS_ACCESS_KEY", ACCESS_KEY_ID)
+        .with_env_var("RUSTFS_SECRET_KEY", SECRET_KEY)
 }
 
 #[allow(dead_code)]
 pub fn create_bucket_command() -> ExecCommand {
-    // this is hack to create a bucket without creating s3 client.
-    // this works with current testcontainer (and image) version 
'RELEASE.2022-02-07T08-17-33Z'.
-    // (testcontainer  does not await properly on latest image version)
-    //
-    // if testcontainer image version change to something newer we should use 
"mc mb /data/ballista"
-    // to crate a bucket.
+    // A top-level directory under the data volume is a bucket, which avoids
+    // pulling an S3 client in just to create one.
     ExecCommand::new(vec![
         "mkdir".to_string(),
         format!("/data/{}", crate::common::BUCKET),
     ])
-    .with_cmd_ready_condition(CmdWaitFor::seconds(1))
+    .with_cmd_ready_condition(CmdWaitFor::exit_code(0))
 }
 
 /// starts a ballista cluster for integration tests
diff --git a/examples/tests/object_store.rs b/examples/tests/object_store.rs
index 6202bb8f9..6db234fba 100644
--- a/examples/tests/object_store.rs
+++ b/examples/tests/object_store.rs
@@ -19,7 +19,7 @@
 //!
 //! Tests demonstrate how to setup object stores with ballista.
 //!
-//! Test depend on Minio testcontainer acting as S3 object
+//! Test depend on a RustFS testcontainer acting as S3 object
 //! store.
 //!
 //! Tesctoncainers require docker to run.
@@ -36,11 +36,11 @@ mod standalone {
     use datafusion::{assert_batches_eq, prelude::SessionContext};
     use datafusion::{error::DataFusionError, execution::SessionStateBuilder};
     use std::sync::Arc;
-    use testcontainers_modules::testcontainers::runners::AsyncRunner;
+    use testcontainers::runners::AsyncRunner;
 
     #[tokio::test]
     async fn should_execute_sql_write() -> datafusion::error::Result<()> {
-        let container = crate::common::create_minio_container();
+        let container = crate::common::create_s3_container();
         let node = container.start().await.unwrap();
 
         node.exec(crate::common::create_bucket_command())
@@ -48,7 +48,10 @@ mod standalone {
             .unwrap();
 
         let host = node.get_host().await.unwrap();
-        let port = node.get_host_port_ipv4(9000).await.unwrap();
+        let port = node
+            .get_host_port_ipv4(crate::common::S3_PORT)
+            .await
+            .unwrap();
 
         let object_store = crate::common::create_s3_store(&host.to_string(), 
port)
             .map_err(|e| DataFusionError::External(e.into()))?;
@@ -133,13 +136,13 @@ mod remote {
     use datafusion::{assert_batches_eq, prelude::SessionContext};
     use datafusion::{error::DataFusionError, execution::SessionStateBuilder};
     use std::sync::Arc;
-    use testcontainers_modules::testcontainers::runners::AsyncRunner;
+    use testcontainers::runners::AsyncRunner;
 
     #[tokio::test]
     async fn should_execute_sql_write() -> datafusion::error::Result<()> {
         let test_data = examples_test_data();
 
-        let container = crate::common::create_minio_container();
+        let container = crate::common::create_s3_container();
         let node = container.start().await.unwrap();
 
         node.exec(crate::common::create_bucket_command())
@@ -147,7 +150,10 @@ mod remote {
             .unwrap();
 
         let host = node.get_host().await.unwrap();
-        let port = node.get_host_port_ipv4(9000).await.unwrap();
+        let port = node
+            .get_host_port_ipv4(crate::common::S3_PORT)
+            .await
+            .unwrap();
 
         let object_store = crate::common::create_s3_store(&host.to_string(), 
port)
             .map_err(|e| DataFusionError::External(e.into()))?;
@@ -230,7 +236,7 @@ mod custom_s3_config {
     use datafusion::{assert_batches_eq, prelude::SessionContext};
     use datafusion::{error::DataFusionError, execution::SessionStateBuilder};
     use std::sync::Arc;
-    use testcontainers_modules::testcontainers::runners::AsyncRunner;
+    use testcontainers::runners::AsyncRunner;
 
     #[tokio::test]
     async fn should_configure_s3_execute_sql_write_remote()
@@ -238,9 +244,9 @@ mod custom_s3_config {
         let test_data = examples_test_data();
 
         //
-        // Minio cluster setup
+        // S3 (RustFS) container setup
         //
-        let container = crate::common::create_minio_container();
+        let container = crate::common::create_s3_container();
         let node = container.start().await.unwrap();
 
         node.exec(crate::common::create_bucket_command())
@@ -248,10 +254,13 @@ mod custom_s3_config {
             .unwrap();
 
         let endpoint_host = node.get_host().await.unwrap();
-        let endpoint_port = node.get_host_port_ipv4(9000).await.unwrap();
+        let endpoint_port = node
+            .get_host_port_ipv4(crate::common::S3_PORT)
+            .await
+            .unwrap();
 
         log::info!(
-            "MINIO testcontainers host: {}, port: {}",
+            "S3 testcontainers host: {}, port: {}",
             endpoint_host,
             endpoint_port
         );
@@ -377,9 +386,9 @@ mod custom_s3_config {
         let test_data = examples_test_data();
 
         //
-        // Minio cluster setup
+        // S3 (RustFS) container setup
         //
-        let container = crate::common::create_minio_container();
+        let container = crate::common::create_s3_container();
         let node = container.start().await.unwrap();
 
         node.exec(crate::common::create_bucket_command())
@@ -387,10 +396,13 @@ mod custom_s3_config {
             .unwrap();
 
         let endpoint_host = node.get_host().await.unwrap();
-        let endpoint_port = node.get_host_port_ipv4(9000).await.unwrap();
+        let endpoint_port = node
+            .get_host_port_ipv4(crate::common::S3_PORT)
+            .await
+            .unwrap();
 
         log::info!(
-            "MINIO testcontainers host: {}, port: {}",
+            "S3 testcontainers host: {}, port: {}",
             endpoint_host,
             endpoint_port
         );
@@ -526,9 +538,9 @@ mod custom_s3_config {
         let test_data = examples_test_data();
 
         //
-        // Minio cluster setup
+        // S3 (RustFS) container setup
         //
-        let container = crate::common::create_minio_container();
+        let container = crate::common::create_s3_container();
         let node = container.start().await.unwrap();
 
         node.exec(crate::common::create_bucket_command())
@@ -536,7 +548,10 @@ mod custom_s3_config {
             .unwrap();
 
         let endpoint_host = node.get_host().await.unwrap();
-        let endpoint_port = node.get_host_port_ipv4(9000).await.unwrap();
+        let endpoint_port = node
+            .get_host_port_ipv4(crate::common::S3_PORT)
+            .await
+            .unwrap();
 
         //
         // Session Context and Ballista cluster setup


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to