Skip to content

Commit

Permalink
Merge branch 'main' into oceanbase-ce
Browse files Browse the repository at this point in the history
  • Loading branch information
whhe committed Sep 7, 2023
2 parents d35fb61 + ba9e3cc commit ee387fa
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 6 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ body:
- Kafka
- LocalStack
- MariaDB
- MinIO
- MockServer
- MongoDB
- MSSQLServer
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/enhancement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ body:
- Kafka
- LocalStack
- MariaDB
- MinIO
- MockServer
- MongoDB
- MSSQLServer
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ body:
- Kafka
- LocalStack
- MariaDB
- MinIO
- MockServer
- MongoDB
- MSSQLServer
Expand Down
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ updates:
ignore:
- dependency-name: "org.mariadb:r2dbc-mariadb"
update-types: [ "version-update:semver-minor" ]
- package-ecosystem: "gradle"
directory: "/modules/minio"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/mockserver"
schedule:
Expand Down
2 changes: 2 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
- modules/localstack/**/*
"modules/mariadb":
- modules/mariadb/**/*
"modules/minio":
- modules/minio/**/*
"modules/mockserver":
- modules/mockserver/**/*
"modules/mongodb":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,6 @@ protected void doStart() {
try {
configure();

Instant startedAt = Instant.now();

logger().debug("Starting container: {}", getDockerImageName());

AtomicInteger attempt = new AtomicInteger(0);
Expand All @@ -353,7 +351,7 @@ protected void doStart() {
attempt.incrementAndGet(),
startupAttempts
);
tryStart(startedAt);
tryStart();
return true;
}
);
Expand All @@ -380,11 +378,12 @@ protected boolean canBeReused() {
return true;
}

private void tryStart(Instant startedAt) {
private void tryStart() {
try {
String dockerImageName = getDockerImageName();
logger().debug("Starting container: {}", dockerImageName);

Instant startedAt = Instant.now();
logger().info("Creating container for image: {}", dockerImageName);
CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName);
applyConfiguration(createCommand);
Expand Down Expand Up @@ -1497,7 +1496,7 @@ public SELF withStartupAttempts(int attempts) {
}

/**
* Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart(Instant)}.
* Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart()}.
* Invocation happens eagerly on a moment when container is created.
* Warning: this does expose the underlying docker-java API so might change outside of our control.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ protected final String resolve() {
Exception lastFailure = null;
final Instant lastRetryAllowed = Instant.now().plus(PULL_RETRY_TIME_LIMIT);

Instant startedAt = Instant.now();
while (Instant.now().isBefore(lastRetryAllowed)) {
try {
PullImageCmd pullImageCmd = dockerClient
Expand All @@ -95,10 +96,12 @@ protected final String resolve() {
.exec(new TimeLimitedLoggedPullImageResultCallback(logger))
.awaitCompletion();
}
String dockerImageName = imageName.asCanonicalNameString();
logger.info("Image {} pull took {}", dockerImageName, Duration.between(startedAt, Instant.now()));

LocalImagesCache.INSTANCE.refreshCache(imageName);

return imageName.asCanonicalNameString();
return dockerImageName;
} catch (InterruptedException | InternalServerErrorException e) {
// these classes of exception often relate to timeout/connection errors so should be retried
lastFailure = e;
Expand All @@ -109,6 +112,7 @@ protected final String resolve() {
);
}
}

logger.error(
"Failed to pull image: {}. Please check output of `docker pull {}`",
imageName,
Expand Down
39 changes: 39 additions & 0 deletions docs/modules/minio.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# MinIO Containers

Testcontainers can be used to automatically instantiate and manage [MinIO](https://min.io) containers.

## Usage example

Create a `MinIOContainer` to use it in your tests:
<!--codeinclude-->
[Starting a MinIO container](../../modules/minio/src/test/java/org/testcontainers/containers/MinIOContainerTest.java) inside_block:minioContainer
<!--/codeinclude-->

The [MinIO Java client](https://min.io/docs/minio/linux/developers/java/API.html) can be configured with the container as such:
<!--codeinclude-->
[Configuring a MinIO client](../../modules/minio/src/test/java/org/testcontainers/containers/MinIOContainerTest.java) inside_block:configuringClient
<!--/codeinclude-->

If needed the username and password can be overridden as such:
<!--codeinclude-->
[Overriding a MinIO container](../../modules/minio/src/test/java/org/testcontainers/containers/MinIOContainerTest.java) inside_block:minioOverrides
<!--/codeinclude-->

## Adding this module to your project dependencies

Add the following dependency to your `pom.xml`/`build.gradle` file:

=== "Gradle"
```groovy
testImplementation "org.testcontainers:minio:{{latest_version}}"
```

=== "Maven"
```xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>minio</artifactId>
<version>{{latest_version}}</version>
<scope>test</scope>
</dependency>
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ nav:
- modules/k3s.md
- modules/kafka.md
- modules/localstack.md
- modules/minio.md
- modules/mockserver.md
- modules/nginx.md
- modules/pulsar.md
Expand Down
8 changes: 8 additions & 0 deletions modules/minio/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
description = "Testcontainers :: MinIO"

dependencies {
api project(':testcontainers')

testImplementation("io.minio:minio:8.5.5")
testImplementation 'org.assertj:assertj-core:3.24.2'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.testcontainers.containers;

import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

/**
* Testcontainers implementation for MinIO.
* <p>
* Supported image: {@code minio/minio}
* <p>
* Exposed ports:
* <ul>
* <li>S3: 9000</li>
* <li>Console: 9001</li>
* </ul>
*/
public class MinIOContainer extends GenericContainer<MinIOContainer> {

private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("minio/minio");

private static final int MINIO_S3_PORT = 9000;

private static final int MINIO_UI_PORT = 9001;

private static final String DEFAULT_USER = "minioadmin";

private static final String DEFAULT_PASSWORD = "minioadmin";

private String userName;

private String password;

/**
* Constructs a MinIO container from the dockerImageName
* @param dockerImageName the full image name to use
*/
public MinIOContainer(final String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}

/**
* Constructs a MinIO container from the dockerImageName
* @param dockerImageName the full image name to use
*/
public MinIOContainer(final DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
}

/**
* Overrides the DEFAULT_USER
* @param userName the Root user to override
* @return this
*/
public MinIOContainer withUserName(String userName) {
this.userName = userName;
return this;
}

/**
* Overrides the DEFAULT_PASSWORD
* @param password the Root user's password to override
* @return this
*/
public MinIOContainer withPassword(String password) {
this.password = password;
return this;
}

/**
* Configures the MinIO container
*/
@Override
public void configure() {
withExposedPorts(MinIOContainer.MINIO_S3_PORT, MinIOContainer.MINIO_UI_PORT);

if (this.userName != null) {
addEnv("MINIO_ROOT_USER", this.userName);
} else {
this.userName = DEFAULT_USER;
}
if (this.password != null) {
addEnv("MINIO_ROOT_PASSWORD", this.password);
} else {
this.password = DEFAULT_PASSWORD;
}

withCommand("server", "--console-address", ":" + MINIO_UI_PORT, "/data");

waitingFor(
Wait
.forLogMessage(".*Status: 1 Online, 0 Offline..*", 1)
.withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS))
);
}

/**
* @return the URL to upload/download objects from
*/
public String getS3URL() {
return String.format("http://%s:%s", this.getHost(), getMappedPort(MINIO_S3_PORT));
}

/**
* @return the Username for the Root user
*/
public String getUserName() {
return this.userName;
}

/**
* @return the password for the Root user
*/
public String getPassword() {
return this.password;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.testcontainers.containers;

import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import io.minio.UploadObjectArgs;
import org.junit.Test;

import java.net.URL;

import static org.assertj.core.api.Assertions.assertThat;

public class MinIOContainerTest {

@Test
public void testBasicUsage() throws Exception {
try (
// minioContainer {
MinIOContainer container = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z");
// }
) {
container.start();

// configuringClient {
MinioClient minioClient = MinioClient
.builder()
.endpoint(container.getS3URL())
.credentials(container.getUserName(), container.getPassword())
.build();

// }
minioClient.makeBucket(MakeBucketArgs.builder().bucket("test-bucket").region("us-west-2").build());

BucketExistsArgs existsArgs = BucketExistsArgs.builder().bucket("test-bucket").build();

assertThat(minioClient.bucketExists(existsArgs)).isTrue();

URL file = this.getClass().getResource("/object_to_upload.txt");
assertThat(file).isNotNull();
minioClient.uploadObject(
UploadObjectArgs
.builder()
.bucket("test-bucket")
.object("my-objectname")
.filename(file.getPath())
.build()
);

StatObjectResponse objectStat = minioClient.statObject(
StatObjectArgs.builder().bucket("test-bucket").object("my-objectname").build()
);

assertThat(objectStat.object()).isEqualTo("my-objectname");
}
}

@Test
public void testDefaultUserPassword() {
try (MinIOContainer container = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z")) {
container.start();
assertThat(container.getUserName()).isEqualTo("minioadmin");
assertThat(container.getPassword()).isEqualTo("minioadmin");
}
}

@Test
public void testOverwriteUserPassword() {
try (
// minioOverrides {
MinIOContainer container = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z")
.withUserName("testuser")
.withPassword("testpassword");
// }
) {
container.start();
assertThat(container.getUserName()).isEqualTo("testuser");
assertThat(container.getPassword()).isEqualTo("testpassword");
}
}
}
16 changes: 16 additions & 0 deletions modules/minio/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

<logger name="org.testcontainers" level="INFO"/>
</configuration>
1 change: 1 addition & 0 deletions modules/minio/src/test/resources/object_to_upload.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is a file

0 comments on commit ee387fa

Please sign in to comment.