Skip to content

Commit ba9e3cc

Browse files
Add MinIO module (#7440)
Co-authored-by: Eddú Meléndez Gonzales <[email protected]>
1 parent fcfe6f2 commit ba9e3cc

File tree

12 files changed

+277
-0
lines changed

12 files changed

+277
-0
lines changed

.github/ISSUE_TEMPLATE/bug_report.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ body:
3131
- Kafka
3232
- LocalStack
3333
- MariaDB
34+
- MinIO
3435
- MockServer
3536
- MongoDB
3637
- MSSQLServer

.github/ISSUE_TEMPLATE/enhancement.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ body:
3131
- Kafka
3232
- LocalStack
3333
- MariaDB
34+
- MinIO
3435
- MockServer
3536
- MongoDB
3637
- MSSQLServer

.github/ISSUE_TEMPLATE/feature.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ body:
3131
- Kafka
3232
- LocalStack
3333
- MariaDB
34+
- MinIO
3435
- MockServer
3536
- MongoDB
3637
- MSSQLServer

.github/dependabot.yml

+5
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ updates:
154154
ignore:
155155
- dependency-name: "org.mariadb:r2dbc-mariadb"
156156
update-types: [ "version-update:semver-minor" ]
157+
- package-ecosystem: "gradle"
158+
directory: "/modules/minio"
159+
schedule:
160+
interval: "monthly"
161+
open-pull-requests-limit: 10
157162
- package-ecosystem: "gradle"
158163
directory: "/modules/mockserver"
159164
schedule:

.github/labeler.yml

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
- modules/localstack/**/*
4848
"modules/mariadb":
4949
- modules/mariadb/**/*
50+
"modules/minio":
51+
- modules/minio/**/*
5052
"modules/mockserver":
5153
- modules/mockserver/**/*
5254
"modules/mongodb":

docs/modules/minio.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# MinIO Containers
2+
3+
Testcontainers can be used to automatically instantiate and manage [MinIO](https://min.io) containers.
4+
5+
## Usage example
6+
7+
Create a `MinIOContainer` to use it in your tests:
8+
<!--codeinclude-->
9+
[Starting a MinIO container](../../modules/minio/src/test/java/org/testcontainers/containers/MinIOContainerTest.java) inside_block:minioContainer
10+
<!--/codeinclude-->
11+
12+
The [MinIO Java client](https://min.io/docs/minio/linux/developers/java/API.html) can be configured with the container as such:
13+
<!--codeinclude-->
14+
[Configuring a MinIO client](../../modules/minio/src/test/java/org/testcontainers/containers/MinIOContainerTest.java) inside_block:configuringClient
15+
<!--/codeinclude-->
16+
17+
If needed the username and password can be overridden as such:
18+
<!--codeinclude-->
19+
[Overriding a MinIO container](../../modules/minio/src/test/java/org/testcontainers/containers/MinIOContainerTest.java) inside_block:minioOverrides
20+
<!--/codeinclude-->
21+
22+
## Adding this module to your project dependencies
23+
24+
Add the following dependency to your `pom.xml`/`build.gradle` file:
25+
26+
=== "Gradle"
27+
```groovy
28+
testImplementation "org.testcontainers:minio:{{latest_version}}"
29+
```
30+
31+
=== "Maven"
32+
```xml
33+
<dependency>
34+
<groupId>org.testcontainers</groupId>
35+
<artifactId>minio</artifactId>
36+
<version>{{latest_version}}</version>
37+
<scope>test</scope>
38+
</dependency>
39+
```

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ nav:
8181
- modules/k3s.md
8282
- modules/kafka.md
8383
- modules/localstack.md
84+
- modules/minio.md
8485
- modules/mockserver.md
8586
- modules/nginx.md
8687
- modules/pulsar.md

modules/minio/build.gradle

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
description = "Testcontainers :: MinIO"
2+
3+
dependencies {
4+
api project(':testcontainers')
5+
6+
testImplementation("io.minio:minio:8.5.5")
7+
testImplementation 'org.assertj:assertj-core:3.24.2'
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package org.testcontainers.containers;
2+
3+
import org.testcontainers.containers.wait.strategy.Wait;
4+
import org.testcontainers.utility.DockerImageName;
5+
6+
import java.time.Duration;
7+
import java.time.temporal.ChronoUnit;
8+
9+
/**
10+
* Testcontainers implementation for MinIO.
11+
* <p>
12+
* Supported image: {@code minio/minio}
13+
* <p>
14+
* Exposed ports:
15+
* <ul>
16+
* <li>S3: 9000</li>
17+
* <li>Console: 9001</li>
18+
* </ul>
19+
*/
20+
public class MinIOContainer extends GenericContainer<MinIOContainer> {
21+
22+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("minio/minio");
23+
24+
private static final int MINIO_S3_PORT = 9000;
25+
26+
private static final int MINIO_UI_PORT = 9001;
27+
28+
private static final String DEFAULT_USER = "minioadmin";
29+
30+
private static final String DEFAULT_PASSWORD = "minioadmin";
31+
32+
private String userName;
33+
34+
private String password;
35+
36+
/**
37+
* Constructs a MinIO container from the dockerImageName
38+
* @param dockerImageName the full image name to use
39+
*/
40+
public MinIOContainer(final String dockerImageName) {
41+
this(DockerImageName.parse(dockerImageName));
42+
}
43+
44+
/**
45+
* Constructs a MinIO container from the dockerImageName
46+
* @param dockerImageName the full image name to use
47+
*/
48+
public MinIOContainer(final DockerImageName dockerImageName) {
49+
super(dockerImageName);
50+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
51+
}
52+
53+
/**
54+
* Overrides the DEFAULT_USER
55+
* @param userName the Root user to override
56+
* @return this
57+
*/
58+
public MinIOContainer withUserName(String userName) {
59+
this.userName = userName;
60+
return this;
61+
}
62+
63+
/**
64+
* Overrides the DEFAULT_PASSWORD
65+
* @param password the Root user's password to override
66+
* @return this
67+
*/
68+
public MinIOContainer withPassword(String password) {
69+
this.password = password;
70+
return this;
71+
}
72+
73+
/**
74+
* Configures the MinIO container
75+
*/
76+
@Override
77+
public void configure() {
78+
withExposedPorts(MinIOContainer.MINIO_S3_PORT, MinIOContainer.MINIO_UI_PORT);
79+
80+
if (this.userName != null) {
81+
addEnv("MINIO_ROOT_USER", this.userName);
82+
} else {
83+
this.userName = DEFAULT_USER;
84+
}
85+
if (this.password != null) {
86+
addEnv("MINIO_ROOT_PASSWORD", this.password);
87+
} else {
88+
this.password = DEFAULT_PASSWORD;
89+
}
90+
91+
withCommand("server", "--console-address", ":" + MINIO_UI_PORT, "/data");
92+
93+
waitingFor(
94+
Wait
95+
.forLogMessage(".*Status: 1 Online, 0 Offline..*", 1)
96+
.withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS))
97+
);
98+
}
99+
100+
/**
101+
* @return the URL to upload/download objects from
102+
*/
103+
public String getS3URL() {
104+
return String.format("http://%s:%s", this.getHost(), getMappedPort(MINIO_S3_PORT));
105+
}
106+
107+
/**
108+
* @return the Username for the Root user
109+
*/
110+
public String getUserName() {
111+
return this.userName;
112+
}
113+
114+
/**
115+
* @return the password for the Root user
116+
*/
117+
public String getPassword() {
118+
return this.password;
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.testcontainers.containers;
2+
3+
import io.minio.BucketExistsArgs;
4+
import io.minio.MakeBucketArgs;
5+
import io.minio.MinioClient;
6+
import io.minio.StatObjectArgs;
7+
import io.minio.StatObjectResponse;
8+
import io.minio.UploadObjectArgs;
9+
import org.junit.Test;
10+
11+
import java.net.URL;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
public class MinIOContainerTest {
16+
17+
@Test
18+
public void testBasicUsage() throws Exception {
19+
try (
20+
// minioContainer {
21+
MinIOContainer container = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z");
22+
// }
23+
) {
24+
container.start();
25+
26+
// configuringClient {
27+
MinioClient minioClient = MinioClient
28+
.builder()
29+
.endpoint(container.getS3URL())
30+
.credentials(container.getUserName(), container.getPassword())
31+
.build();
32+
33+
// }
34+
minioClient.makeBucket(MakeBucketArgs.builder().bucket("test-bucket").region("us-west-2").build());
35+
36+
BucketExistsArgs existsArgs = BucketExistsArgs.builder().bucket("test-bucket").build();
37+
38+
assertThat(minioClient.bucketExists(existsArgs)).isTrue();
39+
40+
URL file = this.getClass().getResource("/object_to_upload.txt");
41+
assertThat(file).isNotNull();
42+
minioClient.uploadObject(
43+
UploadObjectArgs
44+
.builder()
45+
.bucket("test-bucket")
46+
.object("my-objectname")
47+
.filename(file.getPath())
48+
.build()
49+
);
50+
51+
StatObjectResponse objectStat = minioClient.statObject(
52+
StatObjectArgs.builder().bucket("test-bucket").object("my-objectname").build()
53+
);
54+
55+
assertThat(objectStat.object()).isEqualTo("my-objectname");
56+
}
57+
}
58+
59+
@Test
60+
public void testDefaultUserPassword() {
61+
try (MinIOContainer container = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z")) {
62+
container.start();
63+
assertThat(container.getUserName()).isEqualTo("minioadmin");
64+
assertThat(container.getPassword()).isEqualTo("minioadmin");
65+
}
66+
}
67+
68+
@Test
69+
public void testOverwriteUserPassword() {
70+
try (
71+
// minioOverrides {
72+
MinIOContainer container = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z")
73+
.withUserName("testuser")
74+
.withPassword("testpassword");
75+
// }
76+
) {
77+
container.start();
78+
assertThat(container.getUserName()).isEqualTo("testuser");
79+
assertThat(container.getPassword()).isEqualTo("testpassword");
80+
}
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<configuration>
2+
3+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
4+
<!-- encoders are assigned the type
5+
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
6+
<encoder>
7+
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
8+
</encoder>
9+
</appender>
10+
11+
<root level="INFO">
12+
<appender-ref ref="STDOUT"/>
13+
</root>
14+
15+
<logger name="org.testcontainers" level="INFO"/>
16+
</configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is a file

0 commit comments

Comments
 (0)