Amazon ElasticSearch Java 통합 테스트

Amazon은 ElasticSearch Cloud 서비스를 제공합니다. 아시다시피 Amazon은 원래 제품에 몇 가지 문제 가 발생한 후 Opendistro라는 포크를 기반으로합니다 . 이 Amazon 제품에는 원래 ElasticSearch 제품과 다른 버전 관리 태그가있는 자체 수명주기가 있습니다. 따라서 통합을 확인하기 위해 서버에 대해 Java 클라이언트 코드를 테스트 할 때 실제 Amazon 제품에서 일어나는 일을 밀접하게 시뮬레이션하는 유효한 환경을 찾는 것은 쉽지 않습니다.

따라서이 기사에서는 프로젝트 빌드의 일부로 Java 클라이언트에 대한 통합 테스트를 수행하고 Amazon 환경으로 이동하기 전에 통합 문제를 감지하는 방법에 대한 작업 예제를 제공합니다. 이를 통해 개발 팀은 프로세스에서 곧 문제를 식별하고 빠른 수정을 위해 개발 환경에서 프로덕션 문제를 재현 할 수 있습니다.

의존성

종속성 목록부터 시작하겠습니다.

<dependency>
   <groupId>org.junit.jupiter</groupId>
   <artifactId>junit-jupiter-engine</artifactId>
   <version>5.4.0</version>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.testcontainers</groupId>
   <artifactId>testcontainers</artifactId>
   <version>1.15.1</version>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.elasticsearch.client</groupId>
   <artifactId>elasticsearch-rest-high-level-client</artifactId>
   <version>7.10.2</version>
</dependency>

그런 다음 Docker 이미지로 작업하고 테스트를 위해 Amazon ElasticSearch 서버를 부트 스트랩 할 수있는 기본 테스트 도구 인 Testcontainer가 필요합니다. 추가 테스트 컨테이너 요구 사항 확인 링크.

서버에 쿼리를 보내는 데 사용되는 Java ElastiSearch 클라이언트 종속성입니다.

Awaitility 도구는 서버가 요청을 수신 할 준비가되는 시간 조건을 정의하는 데 사용됩니다.

스켈레톤 테스트

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.ClassRule;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import static org.junit.Assert.assertTrue;
public class OpendistroCompTest {

    public static final DockerImageName OPENDISTRO_IMAGE = DockerImageName.parse("amazon/opendistro-for-elasticsearch:1.11.0");

    private static final String ELASTICSEARCH_USERNAME = "admin";
    private static final String ELASTICSEARCH_PASSWORD = "admin";

    @ClassRule
    public static GenericContainer<?> container;

    private static RestHighLevelClient esClient;
    private static RestClient restClient;

    @Test
    public void checkServerHealth() throws Exception {

        Response response = restClient.performRequest(new Request("GET", "/_cluster/health"));
    }

    private static class DefaultTrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

    @BeforeAll
    public static void prepareESContainer() throws Exception {
        // Create the elasticsearch container.
        // configure the SSLContext with a TrustManager
        //this is because local ES certificate is not bound to the proper ip
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
        SSLContext.setDefault(ctx);

        // Start the container. This step might take some time...
        container =
        new GenericContainer<>(OPENDISTRO_IMAGE)
                .withExposedPorts(9200, 9600).withEnv("discovery.type", "single-node").waitingFor(Wait.forLogMessage(".*Hot-reloading of audit configuration is enabled.*\\n", 1));
        container.start();
        String hostAddr = container.getContainerInfo().getNetworkSettings().getIpAddress();
        HttpHost httpHost1 = new HttpHost(hostAddr, 9200, "https");


        // Create the secured client.
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD));

        RestClientBuilder builder = RestClient.builder(httpHost1).setHttpClientConfigCallback(
                httpClientBuilder -> {
                    httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        esClient = new RestHighLevelClient(builder);
        restClient = RestClient.builder(httpHost1).setHttpClientConfigCallback(
                httpClientBuilder -> {
                    httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                }).build();

    }


    

   @AfterAll
    public static void cleanUp() throws Exception {
       // stop the client
        esClient.close();
        restClient.close();

        // Stop the container.
        container.stop();
        container.close();
    }
}

컬 -XGET https://172.17.0.3:9200-u admin : admin — 안전하지 않은
{
"이름": "4c3f689ddd76",
"cluster_name": "docker-cluster",
"cluster_uuid": "n96mfPN3QYOx3kPb2NdcWg",
"version": {
"number": "7.9.1",
" build_flavor”:“oss”,
“build_type”:“tar”,
“build_hash”:“083627f112ba94dffc1232e8b42b73492789ef91”,
“build_date”:“2020–09–01T21 : 22 : 21.964974Z”,
“build_snapshot”: false,
“lucene_version”: "8.6.2",
"minimum_wire_compatibility_version": "6.8.0",
"minimum_index_compatibility_version": "6.0.0-beta1"
},
"tagline": "알고있는 검색 용"
}

언급 할만한 속성은 다음과 같습니다.

  • 번호 : 아마존 제품 버전입니다. ElasticSearch 도메인에 대해 Amazon Console에서 설정 한 것과 상관 관계가 있어야합니다.
  • Lucene_version : 서버에서 사용하는 내부 Lucene 검색 엔진의 버전입니다.
  • 최소 와이어 호환성 버전 : 클러스터의 다른 노드가 적절히 조정하기위한 최소 버전입니다.
  • 최소 색인 호환성 : 이전과 동일하지만 이전 노드에서 파일을 읽는 것과 관련이 있습니다.

container =
                new GenericContainer<>(OPENDISTRO_IMAGE)
                        .withExposedPorts(9200, 9600).withEnv("discovery.type", "single-node").waitingFor(Wait.forLogMessage(".*Hot-reloading of audit configuration is enabled.*\\n", 1));;
        container.start();

그런 다음 Java 클라이언트를 생성합니다. 로컬 서버에 액세스 할 수 있도록 몇 가지 보안 조정이 있음을 알 수 있습니다. 이것이 테스트에 중요한 경우 서버 인증서에 정의 된 적절한 localhost를 사용하여 로컬 인스턴스에 액세스하는 방법을 파악하여 올바르게 일치하도록해야합니다.

RestClientBuilder builder = RestClient.builder....

public void checkServerHealth() throws Exception {

        Response response = restClient.performRequest(new Request("GET", "/_cluster/health"));
    }

@AfterAll
    public static void cleanUp() throws Exception {
       // stop the client
        esClient.close();
        restClient.close();

        // Stop the container.
        container.stop();
        container.close();
    }

인덱스 관리 및 데이터 채우기

테스트 스위트에는 인덱스 관리 또는 쿼리 만 포함되거나 둘 다 포함될 수 있습니다. 우리의 경우 Java Lambda 코드 쿼리를 확인하고 싶었지만 쿼리를 실행하려면 서버에 실제 인덱스와 데이터가 있어야합니다. 색인, 템플릿을 만들고 일부 데이터를로드하는 데 사용하는 도구를 공유하고 있습니다.

private static String loadResource(String name) throws IOException {
    final InputStream data = CdrCompTest.class.getResourceAsStream(name);
    String template = IOUtils.toString(data, StandardCharsets.UTF_8);
    return template;
}

private static void createIndex(String name) throws IOException {
    CreateIndexRequest request = new CreateIndexRequest(name);
    esClient.indices().create(request, RequestOptions.DEFAULT);
}

private static void addDoc(String index, String dataFile) throws Exception {
    String jsonString = loadResource(dataFile);
    String[] ss = jsonString.split(";");

    BulkRequest request = new BulkRequest();
    for (String s : ss) {
        IndexRequest iRequest = new IndexRequest(index, "_doc");
        iRequest.source(s, XContentType.JSON);
        request.add(iRequest);
    }

    BulkResponse bulkResponse = esClient.bulk(request, RequestOptions.DEFAULT);
}

private static void prepareIndexes(GenericContainer container) throws Exception {
    // creating of index templates
    for (String s : templateNames) {
        String template = loadResource("/" + s + ".txt");

        Request request = new Request("PUT", "/_template/" + s);
        NStringEntity entity = new NStringEntity(template, ContentType.APPLICATION_JSON);
        request.setEntity(entity);
        Response response = restClient.performRequest(request);

        GetIndexTemplatesRequest p = new GetIndexTemplatesRequest(s);
        GetIndexTemplatesResponse resp = esClient.indices().getIndexTemplate(p, RequestOptions.DEFAULT);
        int i1 = 0;
    }

    // creating of indeces
    for (String name : indexNames) {
        createIndex(name);
    }
}

private static void deleteIndex(String name) throws IOException {
    DeleteIndexRequest request = new DeleteIndexRequest(name);
    esClient.indices().delete(request, RequestOptions.DEFAULT);
}

prepareIndexes(container);

for (String s : dataNames) {
    addDoc(s, "/" + s + ".txt");
}

결론

이 작은 테스트 환경을 통해 서버와의 모든 상호 작용을 테스트하고 클라이언트 코드가 제대로 작동하는지 확인할 수 있습니다. 이 기사를 통해 다른 팀이 Java Amazon ElasticSearch 클라이언트에 통합 테스트를 추가하여 프로세스를 개선 할 수 있기를 바랍니다.

이 솔루션은 Telestax의 Core Network 팀이 함께 노력한 것이기 때문에 성과는 팀에서 나옵니다.

Suggested posts

Spring Boot와 함께 Apache Kafka 구성 및 사용

스프링 부츠를 신은 카프카

Spring Boot와 함께 Apache Kafka 구성 및 사용

소개 Apache Kafka는 대규모, 높은 처리량, 내결함성 실시간 데이터 스트리밍을 처리하는 오픈 소스 분산 스트리밍 플랫폼입니다. 이 시스템은 생산자가 주제에 메시지를 게시하고 소비자가 해당 주제를 구독하여 메시지를 소비하는 게시-구독 모델을 기반으로 합니다.

모바일 개발 마스터하기: 앱에 가장 적합한 프로그래밍 언어를 선택하는 방법

모바일 개발 마스터하기: 앱에 가장 적합한 프로그래밍 언어를 선택하는 방법

모바일 개발은 오늘날의 디지털 세계에서 기업이 성공하기 위한 필수 요소가 되었습니다. 모바일 앱 개발자로서 앱 구축에 적합한 프로그래밍 언어를 선택하는 것이 중요합니다.

Related posts

기다림의 계절에 할 수 있는 것(기술 분야에서 첫 직장을 얻기 위해 기다림)

기다림의 계절에 할 수 있는 것(기술 분야에서 첫 직장을 얻기 위해 기다림)

새로운 직업으로 전환하는 것은 혼자서 하려고 한다면 매우 어려울 수 있습니다. 귀하와 동일한 학습 수준에 있고 유사한 목표, 끈기 및 추진력을 공유하는 책임 파트너를 찾는 것이 중요합니다.

더 이상 암호가 없습니다. 암호가 암호를 대체하는 방법

더 이상 암호가 없습니다. 암호가 암호를 대체하는 방법

모호함을 피하기 위해 다음 용어가 사용됩니다. 인증자; 로그인 웹사이트를 만드는 데 사용되는 휴대폰 사용자가 로그인 요청 서비스를 만들기 위해 사용하는 웹사이트 또는 애플리케이션 주어진 서비스를 제공하는 벤더 예.

"마리아나 해구 공개: 심연의 무서운 숨겨진 진실 공개"

"마리아나 해구 공개: 심연의 무서운 숨겨진 진실 공개"

소개:용감한 독자 여러분, 지구의 가장 어두운 곳으로 우리를 데려다 줄 여행에 오신 것을 환영합니다. 이 블로그에서 우리는 Mariana Trench의 수수께끼 같은 세계를 탐구하여 숨겨진 비밀을 밝히고 그 표면 아래에 있는 뼛속까지 오싹해지는 진실을 밝혀냅니다.

느린 침식: 사람들이 경력을 낭비하는 방식 공개

느린 침식: 사람들이 경력을 낭비하는 방식 공개

오늘날과 같이 급변하고 경쟁이 치열한 세상에서 경력은 개인의 성장, 재정적 안정 및 전반적인 삶의 만족도에 중요한 역할을 합니다. 그러나 많은 사람들은 침체와 불만족의 끊임없는 순환에 휩싸여 직업 생활에서 점차 쇠퇴하고 있습니다.