tech.beshu.ror.utils.containers.ESWithReadonlyRestContainer.java Source code

Java tutorial

Introduction

Here is the source code for tech.beshu.ror.utils.containers.ESWithReadonlyRestContainer.java

Source

/*
 *    This file is part of ReadonlyREST.
 *
 *    ReadonlyREST is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    ReadonlyREST is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with ReadonlyREST.  If not, see http://www.gnu.org/licenses/
 */
package tech.beshu.ror.utils.containers;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import tech.beshu.ror.utils.Tuple;
import tech.beshu.ror.utils.containers.exceptions.ContainerCreationException;
import tech.beshu.ror.utils.gradle.RorPluginGradleProject;
import tech.beshu.ror.utils.httpclient.RestClient;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.WaitStrategy;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.shaded.com.fasterxml.jackson.core.type.TypeReference;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;

import static tech.beshu.ror.utils.containers.ContainerUtils.checkTimeout;

public class ESWithReadonlyRestContainer extends GenericContainer<ESWithReadonlyRestContainer> {

    private static Logger logger = LogManager.getLogger(ESWithReadonlyRestContainer.class);

    private static int ES_PORT = 9200;
    private static Duration WAIT_BETWEEN_RETRIES = Duration.ofSeconds(1);
    private static Duration CONTAINER_STARTUP_TIMEOUT = Duration.ofSeconds(240);
    private static String ADMIN_LOGIN = "admin";
    private static String ADMIN_PASSWORD = "container";

    private final String esVersion;

    private ESWithReadonlyRestContainer(String esVersion, ImageFromDockerfile imageFromDockerfile) {
        super(imageFromDockerfile);
        this.esVersion = esVersion;
    }

    public static ESWithReadonlyRestContainer create(RorPluginGradleProject project, String elasticsearchConfig,
            Optional<ESWithReadonlyRestContainer.ESInitalizer> initalizer) {
        return create(project, ContainerUtils.getResourceFile(elasticsearchConfig), initalizer);
    }

    public static ESWithReadonlyRestContainer create(RorPluginGradleProject project, File elasticsearchConfigFile,
            Optional<ESWithReadonlyRestContainer.ESInitalizer> initalizer) {
        File pluginFile = project.assemble()
                .orElseThrow(() -> new ContainerCreationException("Plugin file assembly failed"));
        logger.info("Creating ES container ...");
        String elasticsearchConfigName = "elasticsearch.yml";
        String log4j2FileName = "log4j2.properties";
        String keystoreFileName = "keystore.jks";

        ESWithReadonlyRestContainer container = new ESWithReadonlyRestContainer(project.getESVersion(),
                new ImageFromDockerfile().withFileFromFile(pluginFile.getAbsolutePath(), pluginFile)
                        .withFileFromFile(elasticsearchConfigName, elasticsearchConfigFile)
                        .withFileFromFile(log4j2FileName, ContainerUtils.getResourceFile("/" + log4j2FileName))
                        .withFileFromFile(keystoreFileName, ContainerUtils.getResourceFile("/" + keystoreFileName))
                        .withDockerfileFromBuilder(builder -> logger.info(builder
                                .from("docker.elastic.co/elasticsearch/elasticsearch:" + project.getESVersion())
                                .env("TEST_VAR", "dev").copy(pluginFile.getAbsolutePath(), "/tmp/")
                                .copy(log4j2FileName, "/usr/share/elasticsearch/config/")
                                .copy(keystoreFileName, "/usr/share/elasticsearch/config/")
                                .copy(elasticsearchConfigName, "/usr/share/elasticsearch/config/readonlyrest.yml")
                                .run("grep -v xpack /usr/share/elasticsearch/config/elasticsearch.yml > /tmp/xxx.yml && mv /tmp/xxx.yml /usr/share/elasticsearch/config/elasticsearch.yml")
                                .run("echo 'http.type: ssl_netty4' >> /usr/share/elasticsearch/config/elasticsearch.yml")
                                .run("sed -i \"s|debug|info|g\" /usr/share/elasticsearch/config/log4j2.properties")
                                .run("/usr/share/elasticsearch/bin/elasticsearch-plugin remove x-pack --purge || rm -rf /usr/share/elasticsearch/plugins/*")
                                .user("root").run("chown elasticsearch:elasticsearch config/*")
                                .user("elasticsearch").env("ES_JAVA_OPTS", "-Xms512m -Xmx512m")
                                .run("yes | /usr/share/elasticsearch/bin/elasticsearch-plugin install file:///tmp/"
                                        + pluginFile.getName())
                                .build())));
        return container.withLogConsumer((l) -> System.out.print(l.getUtf8String())).withExposedPorts(ES_PORT)
                .waitingFor(container.waitStrategy(initalizer).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT));

    }

    public String getEsVersion() {
        return esVersion;
    }

    public String getESHost() {
        return this.getContainerIpAddress();
    }

    public Integer getESPort() {
        return this.getMappedPort(ES_PORT);
    }

    public RestClient getClient(Header... headers) {
        return new RestClient(true, getESHost(), getESPort(), Optional.empty(), headers);
    }

    public RestClient getBasicAuthClient(String name, String password) {
        return new RestClient(true, getESHost(), getESPort(), Optional.of(Tuple.from(name, password)));
    }

    public RestClient getAdminClient() {
        return new RestClient(true, getESHost(), getESPort(), Optional.of(Tuple.from(ADMIN_LOGIN, ADMIN_PASSWORD)));
    }

    private WaitStrategy waitStrategy(Optional<ESWithReadonlyRestContainer.ESInitalizer> initalizer) {
        final ObjectMapper mapper = new ObjectMapper();
        return new GenericContainer.AbstractWaitStrategy() {
            @Override
            protected void waitUntilReady() {
                logger.info("Waiting for ES container ...");
                final RestClient client = getAdminClient();
                final Instant startTime = Instant.now();
                while (!isReady(client) && !checkTimeout(startTime, startupTimeout)) {
                    try {
                        Thread.sleep(WAIT_BETWEEN_RETRIES.toMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                initalizer.ifPresent(i -> i.initialize(client));
                logger.info("ES container stated");
            }

            private boolean isReady(RestClient client) {
                try {
                    HttpResponse result = client.execute(new HttpGet(client.from("_cluster/health")));
                    if (result.getStatusLine().getStatusCode() != 200)
                        return false;
                    Map<String, String> healthJson = mapper.readValue(result.getEntity().getContent(),
                            new TypeReference<Map<String, String>>() {
                            });
                    return "green".equals(healthJson.get("status"));
                } catch (IOException | URISyntaxException e) {
                    return false;
                }
            }
        };
    }

    public interface ESInitalizer {
        void initialize(RestClient adminClient);
    }

}