Настройка сервера Minio для использования с Testcontainers - PullRequest
0 голосов
/ 28 марта 2019

Мое приложение использует Minio для хранения объектов, совместимых с S3, и я хотел бы использовать образ док-станции Minio в моих интеграционных тестах через Testcontainers .

Для некоторых очень простых тестов я запускаю GenericContainer, используя образ докера minio/minio и без конфигурации, кроме MINIO_ACCESS_KEY и MINIO_SECRET_KEY. Мои тесты затем используют Minio Java Client SDK . Они работают нормально и ведут себя так, как ожидалось.

Но для других интеграционных тестов мне нужно настроить отдельных пользователей в Mino. Насколько я вижу, пользователи могут быть добавлены в Minio только с помощью Admin API , для которого нет клиента Java, только образ докера minio/mc (CLI mc недоступен в minio/minio образ докера, используемый для сервера).

В командной строке я могу использовать Admin API следующим образом:

$ docker run --interactive --tty --detach --entrypoint=/bin/sh --name minio_admin minio/mc

* * * * * * * * * * * * * * * * * * * * --interactive --tty * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1024* * * * * * * * * * * * * * * * * * * * * * * * * * * * *) * * * * * * * * * * * * * * * * * * * * * * * * * * будет "1010" * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1023* * * * * * * * * * * * * * * * * * * * * * * * * * * * *1024* - это всего лишь взлом, чтобы поддерживать работу контейнера, так что я позже смогу выполнить такие команды:

$ docker exec --interactive --tty minio_admin mc admin user add ...

Используя Testcontainers, я пытаюсь сделать то же самое, как это:

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());
}

Журналы показывают, что контейнер запускается, но выполнение команды для него возвращает код выхода 126 и утверждает, что он остановлен:

[minio/mc:latest] - Starting container with ID: 4f96fc7583fe62290925472c4c6b329fbeb7a55b38a3c0ad41ee797db1431841
[minio/mc:latest] - Container minio/mc:latest is starting: 4f96fc7583fe62290925472c4c6b329fbeb7a55b38a3c0ad41ee797db1431841
[minio/mc:latest] - Container minio/mc:latest started
minio.MinioAdminTests - mc is running: true
org.testcontainers.containers.ExecInContainerPattern - /kind_volhard: Running "exec" command: mc
minio.MinioAdminTests - Executing command 'mc' returned exit code '126'
  and stdout 'cannot exec in a stopped state: unknown'

java.lang.AssertionError: Expected: 0, Actual: 126

После долгих раздумий с этим у меня заканчиваются идеи. Кто-нибудь может помочь?

Ответы [ 3 ]

1 голос
/ 29 марта 2019

Как предложено @bsideup, вы можете использовать однократную стратегию, то есть как здесь .UPD: добавлен рабочий тест.Здесь важно знать, что

Когда контейнер запускается, он выполняет команду entrypoint + (это вообще Docker и не имеет никакого отношения к Testcontainers). Источник из TC github

public class TempTest {
    @Rule
    public Network network = Network.newNetwork();

    private String runMcCommand(String cmd) throws TimeoutException {
        GenericContainer container = new GenericContainer<>("minio/mc")
                .withCommand(cmd)
                .withNetwork(network)
                .withStartupCheckStrategy(new OneShotStartupCheckStrategy())
                .withCreateContainerCmdModifier(command -> command.withTty(true));
        container.start();
        WaitingConsumer waitingConsumer = new WaitingConsumer();
        ToStringConsumer toStringConsumer = new ToStringConsumer();
        Consumer<OutputFrame> composedConsumer = toStringConsumer.andThen(waitingConsumer);
        container.followOutput(composedConsumer);
        waitingConsumer.waitUntilEnd(4, TimeUnit.SECONDS);
        return toStringConsumer.toUtf8String();
    }

    private void showCommandOutput(String cmd) throws TimeoutException {
        String res = runMcCommand(cmd);
        System.out.printf("Cmd '%s' result:\n----\n%s\n----%n", cmd, res);
    }

    @Test
    public void testAdminApi() throws Exception {
        showCommandOutput("ls");
        showCommandOutput("version");
    }
}

Другой вариант - использовать содержимое dockerfile файла minio / mc, которое мало, изменить выполненную команду (одну-off «mc» по умолчанию) и запускать собственный контейнер один раз за тест, что, по сравнению с одноразовым контейнером, сэкономит некоторое время, если вам нужно выполнить несколько команд:

@Rule
public Network network = Network.newNetwork();

@Rule
public GenericContainer mc = new GenericContainer(new ImageFromDockerfile()
  .withDockerfileFromBuilder(builder ->
    builder
      .from("alpine:3.7")
      .run("apk add --no-cache ca-certificates && apk add --no-cache --virtual .build-deps curl && curl https://dl.minio.io/client/mc/release/linux-amd64/mc > /usr/bin/mc && chmod +x /usr/bin/mc && apk del .build-deps")
      .cmd("/bin/sh", "-c", "while sleep 3600; do :; done")
      .build())
    )
  .withNetwork(network);

public void myTest() {
  mc.execInContainer("mc blah");
  mc.execInContainer("mc foo");
}

По сути, этозапускает образ с установленным mc и спит в течение 1 часа, что достаточно для ваших тестов.Пока он работает, вы можете выполнять команды и т. Д. После завершения он уничтожается.Ваш мини-контейнер может быть в той же сети.

1 голос
/ 29 марта 2019

Вы можете запустить одноразовый контейнер (используйте OneShotStartupCheckStrategy) с mc и withCommand("your command"), подключенными к той же сети, что и сервер minio, на котором вы работаете (см. Сеть ).

0 голосов
/ 02 апреля 2019

Благодаря @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();
        }
    }
}
...