Docker 配置Minio服务器以与Testcontainers一起使用
我的应用程序用于S3兼容的对象存储,我希望在集成测试中使用Minio docker映像 对于一些非常基本的测试,我使用Docker 配置Minio服务器以与Testcontainers一起使用,docker,minio,testcontainers,Docker,Minio,Testcontainers,我的应用程序用于S3兼容的对象存储,我希望在集成测试中使用Minio docker映像 对于一些非常基本的测试,我使用minio/miniodocker映像运行一个GenericContainer,除了minio\u ACCESS\u KEY和minio\u SECRET\u KEY之外,没有其他配置。然后,我的测试使用Minio。这些工作很好,表现和预期的一样 但是对于其他集成测试,我需要在Mino中设置单独的用户。据我所知,用户只能使用添加到Minio,而没有Java客户端,只有Minio/
minio/minio
docker映像运行一个GenericContainer,除了minio\u ACCESS\u KEY
和minio\u SECRET\u KEY
之外,没有其他配置。然后,我的测试使用Minio。这些工作很好,表现和预期的一样
但是对于其他集成测试,我需要在Mino中设置单独的用户。据我所知,用户只能使用添加到Minio,而没有Java客户端,只有Minio/mc
docker映像(服务器使用的Minio/Minio
docker映像中没有mc
CLI)
在命令行上,我可以像这样使用管理API:
$ docker run --interactive --tty --detach --entrypoint=/bin/sh --name minio_admin minio/mc
public void testAdminApi() throws Exception {
GenericContainer mc = new GenericContainer("minio/mc")
.withCommand("/bin/sh")
.withCreateContainerCmdModifier(new Consumer<CreateContainerCmd>() {
@Override
public void accept(CreateContainerCmd cmd) {
cmd
.withAttachStdin(true)
.withStdinOpen(true)
.withTty(true);
}
});
mc.start();
log.info("mc is running: {}", mc.isRunning());
String command = "mc";
Container.ExecResult result = mc.execInContainer(command);
log.info("Executing command '{}' returned exit code '{}' and stdout '{}'", command, result.getExitCode(), result.getStdout());
assertEquals(0, result.getExitCode());
}
--interactive--tty
是一种让容器运行的技巧,因此我可以在以后运行如下命令:
$ docker exec --interactive --tty minio_admin mc admin user add ...
使用Testcontainers,我尝试执行以下操作:
$ docker run --interactive --tty --detach --entrypoint=/bin/sh --name minio_admin minio/mc
public void testAdminApi() throws Exception {
GenericContainer mc = new GenericContainer("minio/mc")
.withCommand("/bin/sh")
.withCreateContainerCmdModifier(new Consumer<CreateContainerCmd>() {
@Override
public void accept(CreateContainerCmd cmd) {
cmd
.withAttachStdin(true)
.withStdinOpen(true)
.withTty(true);
}
});
mc.start();
log.info("mc is running: {}", mc.isRunning());
String command = "mc";
Container.ExecResult result = mc.execInContainer(command);
log.info("Executing command '{}' returned exit code '{}' and stdout '{}'", command, result.getExitCode(), result.getStdout());
assertEquals(0, result.getExitCode());
}
在花了几个小时摆弄这件事之后,我已经没有主意了。有人能帮忙吗?您可以使用
mc
和with command(“您的命令”)
运行一个一次性容器(使用OneShotStartupCheckStrategy
),该容器与您正在运行的minio服务器连接到同一网络(请参阅)。如@bsideup所建议,您可以使用一次性策略,即,如中所示。
UPD:增加了工作测试。知道这一点很重要
当容器启动时,它执行entrypoint+命令(这通常是Docker,与Testcontainers无关)
基本上,它在安装了mc的情况下运行image,并休眠1小时,这对于您的测试来说已经足够了。当它运行时,您可以执行命令等。完成后,它将被杀死。
您的minio容器可以位于同一网络中。多亏了@glebsts和@bsideup,我才能够让集成测试正常工作。下面是如何添加用户的一个简单示例:
public class MinioIntegrationTest {
private static final String ADMIN_ACCESS_KEY = "admin";
private static final String ADMIN_SECRET_KEY = "12345678";
private static final String USER_ACCESS_KEY = "bob";
private static final String USER_SECRET_KEY = "87654321";
private static GenericContainer minioServer;
private static String minioServerUrl;
@BeforeAll
static void setUp() throws Exception {
int port = 9000;
minioServer = new GenericContainer("minio/minio")
.withEnv("MINIO_ACCESS_KEY", ADMIN_ACCESS_KEY)
.withEnv("MINIO_SECRET_KEY", ADMIN_SECRET_KEY)
.withCommand("server /data")
.withExposedPorts(port)
.waitingFor(new HttpWaitStrategy()
.forPath("/minio/health/ready")
.forPort(port)
.withStartupTimeout(Duration.ofSeconds(10)));
minioServer.start();
Integer mappedPort = minioServer.getFirstMappedPort();
Testcontainers.exposeHostPorts(mappedPort);
minioServerUrl = String.format("http://%s:%s", minioServer.getContainerIpAddress(), mappedPort);
// Minio Java SDK uses s3v4 protocol by default, need to specify explicitly for mc
String cmdTpl = "mc config host add myminio http://host.testcontainers.internal:%s %s %s --api s3v4 && "
+ "mc admin user add myminio %s %s readwrite";
String cmd = String.format(cmdTpl, mappedPort, ADMIN_ACCESS_KEY, ADMIN_SECRET_KEY, USER_ACCESS_KEY, USER_SECRET_KEY);
GenericContainer mcContainer = new GenericContainer<>("minio/mc")
.withStartupCheckStrategy(new OneShotStartupCheckStrategy())
.withCreateContainerCmdModifier(containerCommand -> containerCommand
.withTty(true)
.withEntrypoint("/bin/sh", "-c", cmd));
mcContainer.start();
}
@Test
public void canCreateBucketWithAdminUser() throws Exception {
MinioClient client = new MinioClient(minioServerUrl, ADMIN_ACCESS_KEY, ADMIN_SECRET_KEY);
client.ignoreCertCheck();
String bucketName = "foo";
client.makeBucket(bucketName);
assertTrue(client.bucketExists(bucketName));
}
@Test
public void canCreateBucketWithNonAdminUser() throws Exception {
MinioClient client = new MinioClient(minioServerUrl, USER_ACCESS_KEY, USER_SECRET_KEY);
client.ignoreCertCheck();
String bucketName = "bar";
client.makeBucket(bucketName);
assertTrue(client.bucketExists(bucketName));
}
@AfterAll
static void shutDown() {
if (minioServer.isRunning()) {
minioServer.stop();
}
}
}
公共类小型集成测试{
私有静态最终字符串ADMIN\u ACCESS\u KEY=“ADMIN”;
私有静态最终字符串ADMIN\u SECRET\u KEY=“12345678”;
私有静态最终字符串USER\u ACCESS\u KEY=“bob”;
私有静态最终字符串USER_SECRET_KEY=“87654321”;
专用静态通用容器小型服务器;
私有静态字符串minioServerUrl;
@以前
静态void setUp()引发异常{
int端口=9000;
minioServer=新的通用容器(“minio/minio”)
.withEnv(“MINIO\u访问密钥”,ADMIN\u访问密钥)
.withEnv(“MINIO_SECRET_KEY”,ADMIN_SECRET_KEY)
.withCommand(“服务器/数据”)
.withExposedPorts(端口)
.waitingFor(新的HttpWaitingStrategy()
.forPath(“/minio/health/ready”)
.forPort(港口)
.具有启动超时(持续时间为秒(10));
minioServer.start();
整数mappedPort=minioServer.getFirstMappedPort();
exposeHostPorts(mappedPort);
minioServerUrl=String.format(“http://%s:%s”,minioServer.getContainerIpAddress(),mappedPort);
//Minio Java SDK默认使用s3v4协议,需要为mc显式指定
String cmdTpl=“mc配置主机添加myminio”http://host.testcontainers.internal:%s %s%s--api s3v4&“
+“mc管理员用户添加myminio%s%s读写”;
String cmd=String.format(cmdTpl、mappedPort、ADMIN\u ACCESS\u KEY、ADMIN\u SECRET\u KEY、USER\u ACCESS\u KEY、USER\u SECRET\u KEY);
GenericContainer mcContainer=新的GenericContainer(“minio/mc”)
.withStartupCheckStrategy(新的OneShotStartupCheckStrategy())
.WithCreateContainerCmd修改器(containerCommand->containerCommand)
.withTty(真)
.withEntrypoint(“/bin/sh”,“-c”,cmd));
mcContainer.start();
}
@试验
public void canCreateBucketWithAdminUser()引发异常{
MinioClient=新的MinioClient(minioServerUrl、管理员访问密钥、管理员密钥);
client.ignoreCertCheck();
字符串bucketName=“foo”;
client.makeBucket(bucketName);
assertTrue(client.bucketExists(bucketName));
}
@试验
public void canCreateBucketWithNonAdminUser()引发异常{
MinioClient=新的MinioClient(minioServerUrl、用户访问密钥、用户密钥);
client.ignoreCertCheck();
字符串bucketName=“bar”;
client.makeBucket(bucketName);
assertTrue(client.bucketExists(bucketName));
}
@毕竟
静态无效关机(){
if(minioServer.isRunning()){
minioServer.stop();
}
}
}
基本上,这种“睡眠”与您将容器分离并运行所做的相同。添加了一次性容器的示例。在我的机器上工作(tm)*@BeforeAll/!毕竟没有任何意义,因为testcontainers与@ClassRule配合得很好,您不需要手动清理*覆盖入口点-值得怀疑,所有这些都是因为将巨型超弦粘在一起;*从genericContainer复制硬编码常量是值得怀疑的*为什么实际上需要公开端口?描述了您可能需要它的情况,但在这里,minio和mc都在容器中运行,将它们放在同一个网络中可以更好地隔离它们,并且当您可以拥有内部IP时,您不需要依赖文本url;*您的minio在测试之间没有被清理,env是脏的。这可能会导致测试的相互依赖性,或太多的痛苦,例如添加不需要添加用户的测试
public class MinioIntegrationTest {
private static final String ADMIN_ACCESS_KEY = "admin";
private static final String ADMIN_SECRET_KEY = "12345678";
private static final String USER_ACCESS_KEY = "bob";
private static final String USER_SECRET_KEY = "87654321";
private static GenericContainer minioServer;
private static String minioServerUrl;
@BeforeAll
static void setUp() throws Exception {
int port = 9000;
minioServer = new GenericContainer("minio/minio")
.withEnv("MINIO_ACCESS_KEY", ADMIN_ACCESS_KEY)
.withEnv("MINIO_SECRET_KEY", ADMIN_SECRET_KEY)
.withCommand("server /data")
.withExposedPorts(port)
.waitingFor(new HttpWaitStrategy()
.forPath("/minio/health/ready")
.forPort(port)
.withStartupTimeout(Duration.ofSeconds(10)));
minioServer.start();
Integer mappedPort = minioServer.getFirstMappedPort();
Testcontainers.exposeHostPorts(mappedPort);
minioServerUrl = String.format("http://%s:%s", minioServer.getContainerIpAddress(), mappedPort);
// Minio Java SDK uses s3v4 protocol by default, need to specify explicitly for mc
String cmdTpl = "mc config host add myminio http://host.testcontainers.internal:%s %s %s --api s3v4 && "
+ "mc admin user add myminio %s %s readwrite";
String cmd = String.format(cmdTpl, mappedPort, ADMIN_ACCESS_KEY, ADMIN_SECRET_KEY, USER_ACCESS_KEY, USER_SECRET_KEY);
GenericContainer mcContainer = new GenericContainer<>("minio/mc")
.withStartupCheckStrategy(new OneShotStartupCheckStrategy())
.withCreateContainerCmdModifier(containerCommand -> containerCommand
.withTty(true)
.withEntrypoint("/bin/sh", "-c", cmd));
mcContainer.start();
}
@Test
public void canCreateBucketWithAdminUser() throws Exception {
MinioClient client = new MinioClient(minioServerUrl, ADMIN_ACCESS_KEY, ADMIN_SECRET_KEY);
client.ignoreCertCheck();
String bucketName = "foo";
client.makeBucket(bucketName);
assertTrue(client.bucketExists(bucketName));
}
@Test
public void canCreateBucketWithNonAdminUser() throws Exception {
MinioClient client = new MinioClient(minioServerUrl, USER_ACCESS_KEY, USER_SECRET_KEY);
client.ignoreCertCheck();
String bucketName = "bar";
client.makeBucket(bucketName);
assertTrue(client.bucketExists(bucketName));
}
@AfterAll
static void shutDown() {
if (minioServer.isRunning()) {
minioServer.stop();
}
}
}