diff --git a/README.md b/README.md index 7270e7c..610d5e6 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,15 @@ Dropwizard Elasticsearch [![Coverage Status](https://img.shields.io/coveralls/dropwizard/dropwizard-elasticsearch.svg)](https://coveralls.io/r/dropwizard/dropwizard-elasticsearch) [![Maven Central](https://img.shields.io/maven-central/v/io.dropwizard.modules/dropwizard-elasticsearch.svg)](http://mvnrepository.com/artifact/io.dropwizard.modules/dropwizard-elasticsearch) -A set of classes for using [Elasticsearch][1] (version 2.3.0 and higher) in a [Dropwizard][2] application. +A set of classes for using [Elasticsearch][1] (version 7.1.0 and higher) in a [Dropwizard][2] application. The package provides a [lifecycle-managed][3] client class (`ManagedEsClient`), a configuration class with the most common options (`EsConfiguration`), and some [health checks][4] which can instantly be used in any Dropwizard application. [1]: http://www.elasticsearch.org/ -[2]: http://dropwizard.io/1.2.0/docs -[3]: http://dropwizard.io/1.2.0/docs/manual/core.html#managed-objects -[4]: http://dropwizard.io/1.2.0/docs/manual/core.html#health-checks +[2]: http://dropwizard.io/1.3.0/docs +[3]: http://dropwizard.io/1.3.0/docs/manual/core.html#managed-objects +[4]: http://dropwizard.io/1.3.0/docs/manual/core.html#health-checks Usage @@ -43,33 +43,14 @@ Configuration The following configuration settings are supported by `EsConfiguration`: -* `nodeClient`: When `true`, `ManagedEsClient` will create a `NodeClient`, otherwise a `TransportClient`; default: `true` * `servers`: A list of servers for usage with the created TransportClient if `nodeClient` is `false` -* `clusterName`: The name of the Elasticsearch cluster; default: "elasticsearch" -* `settings`: Any additional settings for Elasticsearch, see [Configuration](https://www.elastic.co/guide/en/elasticsearch/reference/2.4/setup-configuration.html) -* `settingsFile`: Any additional settings file for Elasticsearch, see [Configuration](https://www.elastic.co/guide/en/elasticsearch/reference/2.4/setup-configuration.html) -An example configuration file for creating a Node Client could like this: +An example configuration file for creating a High Level Rest Client could like this: - clusterName: MyClusterName - settings: - node.name: MyCustomNodeName - -The order of precedence is: `nodeClient`/`servers`/`clusterName` > `settings` > `settingsFile`, meaning that -any setting in `settingsFile` can be overwritten with `settings` which in turn get overwritten by the specific settings -like `clusterName`. - -Maven Artifacts ---------------- - -This project is available on Maven Central. To add it to your project simply add the following dependencies to your -`pom.xml`: - - - io.dropwizard.modules - dropwizard-elasticsearch - 1.2.0-1 - + servers: + - http://127.0.0.1:9200 + - http://127.0.0.1:9201 + - http://127.0.0.1:9202 Support diff --git a/pom.xml b/pom.xml index fd0738f..1bebedb 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ io.dropwizard.modules dropwizard-elasticsearch - 1.2.0-2-SNAPSHOT + 1.3.0-1-SNAPSHOT jar Dropwizard Elasticsearch Bundle @@ -69,7 +69,7 @@ 1.8 1.8 1.3.12 - 2.4.6 + 7.1.0 @@ -90,8 +90,13 @@ dropwizard-core - org.elasticsearch - elasticsearch + org.elasticsearch.client + elasticsearch-rest-high-level-client + ${elasticsearch.version} + + + org.elasticsearch.client + elasticsearch-rest-client-sniffer ${elasticsearch.version} diff --git a/src/main/java/io/dropwizard/elasticsearch/config/BasicAuthenticationConfiguration.java b/src/main/java/io/dropwizard/elasticsearch/config/BasicAuthenticationConfiguration.java new file mode 100644 index 0000000..87a7c78 --- /dev/null +++ b/src/main/java/io/dropwizard/elasticsearch/config/BasicAuthenticationConfiguration.java @@ -0,0 +1,23 @@ +package io.dropwizard.elasticsearch.config; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.NotNull; + +public class BasicAuthenticationConfiguration { + @JsonProperty + @NotNull + private String user=""; + + @JsonProperty + @NotNull + private String password=""; + + public String getUser() { + return user; + } + + public String getPassword() { + return password; + } +} diff --git a/src/main/java/io/dropwizard/elasticsearch/config/EsConfiguration.java b/src/main/java/io/dropwizard/elasticsearch/config/EsConfiguration.java index cdf0a3d..e269a27 100644 --- a/src/main/java/io/dropwizard/elasticsearch/config/EsConfiguration.java +++ b/src/main/java/io/dropwizard/elasticsearch/config/EsConfiguration.java @@ -1,61 +1,111 @@ package io.dropwizard.elasticsearch.config; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.net.HostAndPort; -import io.dropwizard.validation.ValidationMethod; -import org.hibernate.validator.constraints.NotEmpty; -import javax.validation.constraints.NotNull; +import org.apache.http.HttpHost; +import org.apache.http.message.BasicHeader; + +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import javax.validation.constraints.NotNull; + /** * Configuration class for Elasticsearch related settings. */ public class EsConfiguration { @JsonProperty @NotNull - private List servers = Collections.emptyList(); + private List servers = Collections.emptyList(); @JsonProperty - @NotEmpty - private String clusterName = "elasticsearch"; + @NotNull + private int connectTimeOut = 1000; @JsonProperty - private boolean nodeClient = true; + @NotNull + private int socketTimeOut = 30000; + + @JsonProperty + private int numberOfThreads = 0; + + @JsonProperty + private String node = ""; + + @JsonProperty + private BasicAuthenticationConfiguration basicAuthentication = null; + + @JsonProperty + private KeyStoreConfiguration keystore = null; + + @JsonProperty + private SnifferConfiguration sniffer = null; @JsonProperty - @NotNull private Map settings = Collections.emptyMap(); @JsonProperty - private String settingsFile = null; + private Map headers = Collections.emptyMap(); - public List getServers() { + public List getServers() { return servers; } - public String getClusterName() { - return clusterName; + public List getServersAsHttpHosts() { + ArrayList httpHosts=new ArrayList<>(); + getServers().forEach(hostAndPort -> { + HttpHost httpHost = HttpHost.create(hostAndPort); + if (httpHost.getPort() < 0) { + httpHost = new HttpHost(httpHost.getHostName(), 9200); + } + httpHosts.add(httpHost); + }); + return httpHosts; } - public boolean isNodeClient() { - return nodeClient; + public Map getHeaders() { + return headers; } - public Map getSettings() { - return settings; + public List getHeadersAsHeaders() { + ArrayList basicHeaders = new ArrayList<>(); + for (Map.Entry entry: getHeaders().entrySet()) { + basicHeaders.add(new BasicHeader(entry.getKey(), entry.getValue())); + } + return basicHeaders; + } + + public int getConnectTimeOut() { + return connectTimeOut; + } + + public int getSocketTimeOut() { + return socketTimeOut; + } + + public int getNumberOfThreads() { + return numberOfThreads; } - public String getSettingsFile() { - return settingsFile; + public String getNode() { + return node; } - @ValidationMethod - @JsonIgnore - public boolean isValidConfig() { - return nodeClient || !servers.isEmpty(); + public BasicAuthenticationConfiguration getBasicAuthentication() { + return basicAuthentication; + } + + public KeyStoreConfiguration getKeystore() { + return keystore; + } + + public SnifferConfiguration getSniffer() { + return sniffer; + } + + public Map getSettings() { + return settings; } } diff --git a/src/main/java/io/dropwizard/elasticsearch/config/KeyStoreConfiguration.java b/src/main/java/io/dropwizard/elasticsearch/config/KeyStoreConfiguration.java new file mode 100644 index 0000000..e1581e2 --- /dev/null +++ b/src/main/java/io/dropwizard/elasticsearch/config/KeyStoreConfiguration.java @@ -0,0 +1,33 @@ +package io.dropwizard.elasticsearch.config; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import javax.validation.constraints.NotNull; + +public class KeyStoreConfiguration { + @JsonProperty + private String type = "jks"; + + @JsonProperty + @NotNull + private String keyStorePath=""; + + @JsonProperty + @NotNull + private String keyStorePass=""; + + public String getType() { + return type; + } + + public Path getKeyStorePath() { + return Paths.get(keyStorePath); + } + + public String getKeyStorePass() { + return keyStorePass; + } +} diff --git a/src/main/java/io/dropwizard/elasticsearch/config/SnifferConfiguration.java b/src/main/java/io/dropwizard/elasticsearch/config/SnifferConfiguration.java new file mode 100644 index 0000000..55d47a2 --- /dev/null +++ b/src/main/java/io/dropwizard/elasticsearch/config/SnifferConfiguration.java @@ -0,0 +1,27 @@ +package io.dropwizard.elasticsearch.config; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class SnifferConfiguration { + + @JsonProperty + private Boolean sniffOnFailure = true; + + @JsonProperty + private int sniffIntervalMillis = 5000; + + @JsonProperty + private int sniffAfterFailureDelayMillis = 30000; + + public Boolean getSniffOnFailure() { + return sniffOnFailure; + } + + public int getSniffIntervalMillis() { + return sniffIntervalMillis; + } + + public int getSniffAfterFailureDelayMillis() { + return sniffAfterFailureDelayMillis; + } +} diff --git a/src/main/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheck.java b/src/main/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheck.java index dbdcad3..7764400 100644 --- a/src/main/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheck.java +++ b/src/main/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheck.java @@ -1,7 +1,12 @@ package io.dropwizard.elasticsearch.health; import com.codahale.metrics.health.HealthCheck; + +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.client.Client; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.cluster.health.ClusterHealthStatus; import static com.google.common.base.Preconditions.checkNotNull; @@ -12,7 +17,7 @@ * @see Admin Cluster Health */ public class EsClusterHealthCheck extends HealthCheck { - private final Client client; + private final RestHighLevelClient client; private final boolean failOnYellow; /** @@ -21,7 +26,7 @@ public class EsClusterHealthCheck extends HealthCheck { * @param client an Elasticsearch {@link Client} instance connected to the cluster * @param failOnYellow whether the health check should fail if the cluster health state is yellow */ - public EsClusterHealthCheck(Client client, boolean failOnYellow) { + public EsClusterHealthCheck(RestHighLevelClient client, boolean failOnYellow) { this.client = checkNotNull(client); this.failOnYellow = failOnYellow; } @@ -30,9 +35,9 @@ public EsClusterHealthCheck(Client client, boolean failOnYellow) { * Construct a new Elasticsearch cluster health check which will fail if the cluster health state is * {@link ClusterHealthStatus#RED}. * - * @param client an Elasticsearch {@link Client} instance connected to the cluster + * @param client an Elasticsearch {@link RestHighLevelClient} instance connected to the cluster */ - public EsClusterHealthCheck(Client client) { + public EsClusterHealthCheck(RestHighLevelClient client) { this(client, false); } @@ -47,7 +52,9 @@ public EsClusterHealthCheck(Client client) { */ @Override protected Result check() throws Exception { - final ClusterHealthStatus status = client.admin().cluster().prepareHealth().get().getStatus(); + ClusterHealthRequest request = new ClusterHealthRequest(); + ClusterHealthResponse response = client.cluster().health(request, RequestOptions.DEFAULT); + final ClusterHealthStatus status = response.getStatus(); if (status == ClusterHealthStatus.RED || (failOnYellow && status == ClusterHealthStatus.YELLOW)) { return Result.unhealthy("Last status: %s", status.name()); diff --git a/src/main/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheck.java b/src/main/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheck.java index dc561a7..3977a9f 100644 --- a/src/main/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheck.java +++ b/src/main/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheck.java @@ -1,10 +1,16 @@ package io.dropwizard.elasticsearch.health; -import com.codahale.metrics.health.HealthCheck; import com.google.common.collect.ImmutableList; -import org.elasticsearch.action.admin.indices.stats.IndexStats; -import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; + +import com.codahale.metrics.health.HealthCheck; + import org.elasticsearch.client.Client; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.core.CountRequest; +import org.elasticsearch.client.core.CountResponse; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.builder.SearchSourceBuilder; import java.util.ArrayList; import java.util.List; @@ -21,7 +27,7 @@ public class EsIndexDocsHealthCheck extends HealthCheck { private static final String HEALTH_CHECK_NAME = "elasticsearch-index-documents"; private static final long DEFAULT_DOCUMENT_THRESHOLD = 1L; - private final Client client; + private final RestHighLevelClient client; private final String[] indices; private final long documentThreshold; @@ -34,7 +40,7 @@ public class EsIndexDocsHealthCheck extends HealthCheck { * @throws IllegalArgumentException if {@code indices} was {@literal null} or empty, * or {@code documentThreshold} was less than 1 */ - public EsIndexDocsHealthCheck(Client client, List indices, long documentThreshold) { + public EsIndexDocsHealthCheck(RestHighLevelClient client, List indices, long documentThreshold) { checkArgument(!indices.isEmpty(), "At least one index must be given"); checkArgument(documentThreshold > 0L, "The document threshold must at least be 1"); @@ -50,7 +56,7 @@ public EsIndexDocsHealthCheck(Client client, List indices, long document * @param client an Elasticsearch {@link Client} instance connected to the cluster * @param indices a {@link List} of indices in Elasticsearch which should be checked */ - public EsIndexDocsHealthCheck(Client client, List indices) { + public EsIndexDocsHealthCheck(RestHighLevelClient client, List indices) { this(client, indices, DEFAULT_DOCUMENT_THRESHOLD); } @@ -61,7 +67,7 @@ public EsIndexDocsHealthCheck(Client client, List indices) { * @param indexName the index in Elasticsearch which should be checked * @param documentThreshold the minimal number of documents in an index */ - public EsIndexDocsHealthCheck(Client client, String indexName, long documentThreshold) { + public EsIndexDocsHealthCheck(RestHighLevelClient client, String indexName, long documentThreshold) { this(client, ImmutableList.of(indexName), documentThreshold); } @@ -71,7 +77,7 @@ public EsIndexDocsHealthCheck(Client client, String indexName, long documentThre * @param client an Elasticsearch {@link Client} instance connected to the cluster * @param indexName the index in Elasticsearch which should be checked */ - public EsIndexDocsHealthCheck(Client client, String indexName) { + public EsIndexDocsHealthCheck(RestHighLevelClient client, String indexName) { this(client, indexName, DEFAULT_DOCUMENT_THRESHOLD); } @@ -86,28 +92,30 @@ public EsIndexDocsHealthCheck(Client client, String indexName) { */ @Override protected Result check() throws Exception { - final IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats(indices).get(); - final List indexDetails = new ArrayList(indices.length); boolean healthy = true; - for (IndexStats indexStats : indicesStatsResponse.getIndices().values()) { - long documentCount = indexStats.getPrimaries().getDocs().getCount(); - + for (String index: indices) { + long documentCount = count(index); if (documentCount < documentThreshold) { healthy = false; - indexDetails.add(String.format("%s (%d)", indexStats.getIndex(), documentCount)); + indexDetails.add(String.format("%s (%d)", index, documentCount)); } else { - indexDetails.add(String.format("%s (%d!)", indexStats.getIndex(), documentCount)); + indexDetails.add(String.format("%s (%d!)", index, documentCount)); } } - final String resultDetails = String.format("Last stats: %s", indexDetails); - if (healthy) { return Result.healthy(resultDetails); } else { return Result.unhealthy(resultDetails); } } -} \ No newline at end of file + + private long count(String index) throws Exception { + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()); + CountRequest countRequest = new CountRequest().source(searchSourceBuilder); + CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT); + return countResponse.getCount(); + } +} diff --git a/src/main/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheck.java b/src/main/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheck.java index 56b2f0c..1dbd79c 100644 --- a/src/main/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheck.java +++ b/src/main/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheck.java @@ -1,9 +1,12 @@ package io.dropwizard.elasticsearch.health; -import com.codahale.metrics.health.HealthCheck; import com.google.common.collect.ImmutableList; -import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; -import org.elasticsearch.client.Client; + +import com.codahale.metrics.health.HealthCheck; + +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.indices.GetIndexRequest; import java.util.List; @@ -16,17 +19,17 @@ * @see Admin Indices Indices Exists */ public class EsIndexExistsHealthCheck extends HealthCheck { - private final Client client; + private final RestHighLevelClient client; private final String[] indices; /** * Construct a new Elasticsearch index exists health check. * - * @param client an Elasticsearch {@link Client} instance connected to the cluster + * @param client an Elasticsearch {@link RestHighLevelClient} instance connected to the cluster * @param indices a {@link List} of indices in Elasticsearch which should be checked * @throws IllegalArgumentException if {@code indices} was {@literal null} or empty */ - public EsIndexExistsHealthCheck(Client client, List indices) { + public EsIndexExistsHealthCheck(RestHighLevelClient client, List indices) { checkArgument(!indices.isEmpty(), "At least one index must be given"); this.client = checkNotNull(client); @@ -39,7 +42,7 @@ public EsIndexExistsHealthCheck(Client client, List indices) { * @param client an Elasticsearch {@link org.elasticsearch.client.Client} instance connected to the cluster * @param indexName the index in Elasticsearch which should be checked */ - public EsIndexExistsHealthCheck(Client client, String indexName) { + public EsIndexExistsHealthCheck(RestHighLevelClient client, String indexName) { this(client, ImmutableList.of(indexName)); } @@ -54,12 +57,12 @@ public EsIndexExistsHealthCheck(Client client, String indexName) { */ @Override protected Result check() throws Exception { - final IndicesExistsResponse indicesExistsResponse = client.admin().indices().prepareExists(indices).get(); - - if (indicesExistsResponse.isExists()) { + GetIndexRequest request = new GetIndexRequest(indices); + boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); + if (exists) { return Result.healthy(); } else { return Result.unhealthy("One or more indices do not exist."); } } -} \ No newline at end of file +} diff --git a/src/main/java/io/dropwizard/elasticsearch/managed/ManagedEsClient.java b/src/main/java/io/dropwizard/elasticsearch/managed/ManagedEsClient.java index a91c039..877d857 100644 --- a/src/main/java/io/dropwizard/elasticsearch/managed/ManagedEsClient.java +++ b/src/main/java/io/dropwizard/elasticsearch/managed/ManagedEsClient.java @@ -1,96 +1,113 @@ package io.dropwizard.elasticsearch.managed; -import com.google.common.io.Resources; -import io.dropwizard.elasticsearch.config.EsConfiguration; -import io.dropwizard.elasticsearch.util.TransportAddressHelper; -import io.dropwizard.lifecycle.Managed; +import org.apache.http.Header; +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.client.config.RequestConfig; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.nio.reactor.IOReactorConfig; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; import org.elasticsearch.client.Client; -import org.elasticsearch.client.transport.TransportClient; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.transport.TransportAddress; -import org.elasticsearch.node.Node; +import org.elasticsearch.client.Node; +import org.elasticsearch.client.NodeSelector; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.sniff.SniffOnFailureListener; +import org.elasticsearch.client.sniff.Sniffer; + +import java.io.InputStream; +import java.nio.file.Files; +import java.security.KeyStore; +import java.util.Iterator; -import java.io.File; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; +import javax.net.ssl.SSLContext; + +import io.dropwizard.elasticsearch.config.EsConfiguration; +import io.dropwizard.lifecycle.Managed; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.isNullOrEmpty; -import static org.elasticsearch.node.NodeBuilder.nodeBuilder; + /** - * A Dropwizard managed Elasticsearch {@link Client}. Depending on the {@link EsConfiguration} a Node Client or - * a {@link TransportClient} a is being created and its lifecycle is managed by Dropwizard. + * A Dropwizard managed Elasticsearch {@link RestHighLevelClient}. + * Depending on the {@link EsConfiguration} a High Level Rest Client + * a {@link RestHighLevelClient} a is being created and its lifecycle is managed by Dropwizard. * - * @see Node Client - * @see Transport Client */ public class ManagedEsClient implements Managed { - private Node node = null; - private Client client = null; - - /** - * Create a new managed Elasticsearch {@link Client}. If {@link EsConfiguration#nodeClient} is {@literal true}, a - * Node Client is being created, otherwise a {@link TransportClient} is being created with {@link EsConfiguration#servers} - * as transport addresses. - * - * @param config a valid {@link EsConfiguration} instance - */ - public ManagedEsClient(final EsConfiguration config) { + private RestHighLevelClient client = null; + private Sniffer sniffer = null; + public ManagedEsClient(final EsConfiguration config) throws Exception { checkNotNull(config, "EsConfiguration must not be null"); - final Settings.Builder settingsBuilder = Settings.builder(); - if (!isNullOrEmpty(config.getSettingsFile())) { - Path path = Paths.get(config.getSettingsFile()); - if (!path.toFile().exists()) { - try { - final URL url = Resources.getResource(config.getSettingsFile()); - path = new File(url.toURI()).toPath(); - } catch (URISyntaxException | NullPointerException e) { - throw new IllegalArgumentException("settings file cannot be found", e); - } - } - settingsBuilder.loadFromPath(path); + RestClientBuilder restClientBuilder = RestClient.builder(config.getServersAsHttpHosts().toArray(new HttpHost[0])); + setRequest(restClientBuilder, config); + + if (!config.getHeadersAsHeaders().isEmpty()) { + restClientBuilder.setDefaultHeaders(config.getHeadersAsHeaders().toArray(new Header[0])); + } + + if (config.getNumberOfThreads()>0) { + setThreads(restClientBuilder, config); + } + + if (config.getNode()!= null && !config.getNode().isEmpty()) { + setNodeSelector(restClientBuilder, config); + } + + if (config.getBasicAuthentication() != null) { + setCredential(restClientBuilder, config); + } + + if (config.getKeystore()!= null) { + setKeyStore(restClientBuilder, config); } - final Settings settings = settingsBuilder - .put(config.getSettings()) - .put("cluster.name", config.getClusterName()) - .build(); - - if (config.isNodeClient()) { - this.node = nodeBuilder() - .client(true) - .data(false) - .settings(settings) - .build(); - this.client = this.node.client(); + if (config.getSniffer()!=null) { + if (config.getSniffer().getSniffOnFailure()) { + SniffOnFailureListener sniffOnFailureListener = + new SniffOnFailureListener(); + restClientBuilder.setFailureListener(sniffOnFailureListener); + this.client = new RestHighLevelClient(restClientBuilder); + this.sniffer = Sniffer.builder(this.client.getLowLevelClient()) + .setSniffAfterFailureDelayMillis(config.getSniffer().getSniffAfterFailureDelayMillis()) + .build(); + sniffOnFailureListener.setSniffer(this.sniffer); + + } else { + this.client = new RestHighLevelClient(restClientBuilder); + this.sniffer = Sniffer.builder(this.client.getLowLevelClient()) + .setSniffIntervalMillis(config.getSniffer().getSniffIntervalMillis()) + .build(); + } + } else { - final TransportAddress[] addresses = TransportAddressHelper.fromHostAndPorts(config.getServers()); - this.client = TransportClient.builder().settings(settings).build().addTransportAddresses(addresses); + this.client = new RestHighLevelClient(restClientBuilder); } } /** - * Create a new managed Elasticsearch {@link Client} from the provided {@link Node}. + * Create a new managed Elasticsearch {@link Client} from the provided {@link Client}. * - * @param node a valid {@link Node} instance + * @param client an initialized {@link Client} instance */ - public ManagedEsClient(final Node node) { - this.node = checkNotNull(node, "Elasticsearch node must not be null"); - this.client = node.client(); + public ManagedEsClient(RestHighLevelClient client) { + this.client = checkNotNull(client, "Elasticsearch client must not be null"); } - /** * Create a new managed Elasticsearch {@link Client} from the provided {@link Client}. * * @param client an initialized {@link Client} instance */ - public ManagedEsClient(Client client) { + public ManagedEsClient(RestHighLevelClient client, Sniffer sniffer) { this.client = checkNotNull(client, "Elasticsearch client must not be null"); + this.sniffer = checkNotNull(sniffer, "Sniffer must not be null"); } /** @@ -100,7 +117,6 @@ public ManagedEsClient(Client client) { */ @Override public void start() throws Exception { - startNode(); } /** @@ -112,7 +128,6 @@ public void start() throws Exception { @Override public void stop() throws Exception { closeClient(); - closeNode(); } /** @@ -120,27 +135,109 @@ public void stop() throws Exception { * * @return a valid Elasticsearch {@link Client} instance */ - public Client getClient() { + public RestHighLevelClient getClient() { return client; } - private Node startNode() { - if (null != node) { - return node.start(); + + private void closeClient() throws Exception { + if (null != client) { + client.close(); } + if (null != sniffer) { + sniffer.close(); + } + } - return null; + private void setRequest(RestClientBuilder restClientBuilder, EsConfiguration config) { + restClientBuilder.setRequestConfigCallback( + new RestClientBuilder.RequestConfigCallback() { + @Override + public RequestConfig.Builder customizeRequestConfig( + RequestConfig.Builder requestConfigBuilder) { + return requestConfigBuilder + .setConnectTimeout(config.getConnectTimeOut()) + .setSocketTimeout(config.getSocketTimeOut()); + } + }); } - private void closeNode() { - if (null != node && !node.isClosed()) { - node.close(); - } + private void setThreads(RestClientBuilder restClientBuilder, EsConfiguration config) { + restClientBuilder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient( + HttpAsyncClientBuilder httpClientBuilder) { + return httpClientBuilder.setDefaultIOReactorConfig( + IOReactorConfig.custom() + .setIoThreadCount(config.getNumberOfThreads()) + .build()); + } + }); } - private void closeClient() { - if (null != client) { - client.close(); + private void setCredential(RestClientBuilder restClientBuilder, EsConfiguration config) { + final CredentialsProvider credentialsProvider = + new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(config.getBasicAuthentication().getUser(), config.getBasicAuthentication().getPassword())); + restClientBuilder + .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient( + HttpAsyncClientBuilder httpClientBuilder) { + httpClientBuilder.disableAuthCaching(); + return httpClientBuilder + .setDefaultCredentialsProvider(credentialsProvider); + } + }); + } + + private void setKeyStore(RestClientBuilder restClientBuilder, EsConfiguration config) throws Exception { + KeyStore truststore = KeyStore.getInstance(config.getKeystore().getType()); + try (InputStream is = Files.newInputStream(config.getKeystore().getKeyStorePath())) { + truststore.load(is, config.getKeystore().getKeyStorePass().toCharArray()); } + SSLContextBuilder sslBuilder = SSLContexts.custom() + .loadTrustMaterial(truststore, null); + final SSLContext sslContext = sslBuilder.build(); + restClientBuilder + .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient( + HttpAsyncClientBuilder httpClientBuilder) { + return httpClientBuilder.setSSLContext(sslContext); + } + }); + } + + private void setNodeSelector(RestClientBuilder restClientBuilder, EsConfiguration config) { + restClientBuilder.setNodeSelector(new NodeSelector() { + @Override + public void select(Iterable nodes) { + /* + * Prefer any node that belongs to rack_one. If none is around + * we will go to another rack till it's time to try and revive + * some of the nodes that belong to rack_one. + */ + boolean foundOne = false; + for (Node node : nodes) { + String rackId = node.getAttributes().get("rack_id").get(0); + if (config.getNode().equals(rackId)) { + foundOne = true; + break; + } + } + if (foundOne) { + Iterator nodesIt = nodes.iterator(); + while (nodesIt.hasNext()) { + Node node = nodesIt.next(); + String rackId = node.getAttributes().get("rack_id").get(0); + if (config.getNode().equals(rackId) == false) { + nodesIt.remove(); + } + } + } + } + }); } } diff --git a/src/main/java/io/dropwizard/elasticsearch/util/TransportAddressHelper.java b/src/main/java/io/dropwizard/elasticsearch/util/TransportAddressHelper.java deleted file mode 100644 index 615d30a..0000000 --- a/src/main/java/io/dropwizard/elasticsearch/util/TransportAddressHelper.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.dropwizard.elasticsearch.util; - -import com.google.common.net.HostAndPort; -import org.elasticsearch.common.transport.InetSocketTransportAddress; -import org.elasticsearch.common.transport.TransportAddress; - -import java.net.InetSocketAddress; -import java.util.List; - -/** - * Helper class for converting Guava {@link HostAndPort} objects to Elasticsearch {@link TransportAddress}. - */ -public class TransportAddressHelper { - private static final int DEFAULT_PORT = 9300; - - /** - * Convert a {@link HostAndPort} instance to {@link TransportAddress}. If the {@link HostAndPort} instance doesn't - * contain a port the resulting {@link TransportAddress} will have {@link #DEFAULT_PORT} as port. - * - * @param hostAndPort a valid {@link HostAndPort} instance - * @return a {@link TransportAddress} equivalent to the provided {@link HostAndPort} instance - */ - public static TransportAddress fromHostAndPort(final HostAndPort hostAndPort) { - InetSocketAddress address = new InetSocketAddress(hostAndPort.getHost(), hostAndPort.getPortOrDefault(DEFAULT_PORT)); - return new InetSocketTransportAddress(address); - } - - /** - * Convert a list of {@link HostAndPort} instances to an array of {@link TransportAddress} instances. - * - * @param hostAndPorts a {@link List} of valid {@link HostAndPort} instances - * @return an array of {@link TransportAddress} instances - * @see #fromHostAndPort(com.google.common.net.HostAndPort) - */ - public static TransportAddress[] fromHostAndPorts(final List hostAndPorts) { - if (hostAndPorts == null) { - return new TransportAddress[0]; - } else { - TransportAddress[] transportAddresses = new TransportAddress[hostAndPorts.size()]; - - for (int i = 0; i < hostAndPorts.size(); i++) { - transportAddresses[i] = fromHostAndPort(hostAndPorts.get(i)); - } - - return transportAddresses; - } - } -} diff --git a/src/test/java/io/dropwizard/elasticsearch/config/CredentialConfigurationTest.java b/src/test/java/io/dropwizard/elasticsearch/config/CredentialConfigurationTest.java new file mode 100644 index 0000000..636c7ff --- /dev/null +++ b/src/test/java/io/dropwizard/elasticsearch/config/CredentialConfigurationTest.java @@ -0,0 +1,39 @@ +package io.dropwizard.elasticsearch.config; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +import javax.validation.Validation; +import javax.validation.Validator; + +import io.dropwizard.configuration.ConfigurationException; +import io.dropwizard.configuration.ConfigurationFactory; +import io.dropwizard.configuration.DefaultConfigurationFactoryFactory; +import io.dropwizard.jackson.Jackson; + + +/** + * Unit tests for {@link BasicAuthenticationConfiguration}. + */ +public class CredentialConfigurationTest { + private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + private final ConfigurationFactory configFactory = + new DefaultConfigurationFactoryFactory() + .create(BasicAuthenticationConfiguration.class, validator, Jackson.newObjectMapper(), "dw"); + + @Test + public void defaultConfigShouldBeValid() throws IOException, ConfigurationException { + configFactory.build(); + } + + @Test(expected = ConfigurationException.class) + public void userAndPasswordMustBeSet() throws IOException, ConfigurationException, URISyntaxException { + URL configFileUrl = this.getClass().getResource("/invalid.yml"); + File configFile = new File(configFileUrl.toURI()); + configFactory.build(configFile); + } +} diff --git a/src/test/java/io/dropwizard/elasticsearch/config/EsConfigurationTest.java b/src/test/java/io/dropwizard/elasticsearch/config/EsConfigurationTest.java index 7e607ba..841c9ef 100644 --- a/src/test/java/io/dropwizard/elasticsearch/config/EsConfigurationTest.java +++ b/src/test/java/io/dropwizard/elasticsearch/config/EsConfigurationTest.java @@ -28,7 +28,7 @@ public void defaultConfigShouldBeValid() throws IOException, ConfigurationExcept } @Test(expected = ConfigurationException.class) - public void eitherNodeClientOrServerListMustBeSet() throws IOException, ConfigurationException, URISyntaxException { + public void serverListMustBeSet() throws IOException, ConfigurationException, URISyntaxException { URL configFileUrl = this.getClass().getResource("/invalid.yml"); File configFile = new File(configFileUrl.toURI()); configFactory.build(configFile); diff --git a/src/test/java/io/dropwizard/elasticsearch/config/KeyStoreConfigurationTest.java b/src/test/java/io/dropwizard/elasticsearch/config/KeyStoreConfigurationTest.java new file mode 100644 index 0000000..0463532 --- /dev/null +++ b/src/test/java/io/dropwizard/elasticsearch/config/KeyStoreConfigurationTest.java @@ -0,0 +1,38 @@ +package io.dropwizard.elasticsearch.config; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +import javax.validation.Validation; +import javax.validation.Validator; + +import io.dropwizard.configuration.ConfigurationException; +import io.dropwizard.configuration.ConfigurationFactory; +import io.dropwizard.configuration.DefaultConfigurationFactoryFactory; +import io.dropwizard.jackson.Jackson; + +/** + * Unit tests for {@link KeyStoreConfiguration}. + */ +public class KeyStoreConfigurationTest { + private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + private final ConfigurationFactory configFactory = + new DefaultConfigurationFactoryFactory() + .create(KeyStoreConfiguration.class, validator, Jackson.newObjectMapper(), "dw"); + + @Test + public void defaultConfigShouldBeValid() throws IOException, ConfigurationException { + configFactory.build(); + } + + @Test(expected = ConfigurationException.class) + public void keyStorePathAndPassMustBeSet() throws IOException, ConfigurationException, URISyntaxException { + URL configFileUrl = this.getClass().getResource("/invalid.yml"); + File configFile = new File(configFileUrl.toURI()); + configFactory.build(configFile); + } +} diff --git a/src/test/java/io/dropwizard/elasticsearch/config/SnifferConfigurationTest.java b/src/test/java/io/dropwizard/elasticsearch/config/SnifferConfigurationTest.java new file mode 100644 index 0000000..9a35195 --- /dev/null +++ b/src/test/java/io/dropwizard/elasticsearch/config/SnifferConfigurationTest.java @@ -0,0 +1,29 @@ +package io.dropwizard.elasticsearch.config; + + +import org.junit.Test; + +import java.io.IOException; + +import javax.validation.Validation; +import javax.validation.Validator; + +import io.dropwizard.configuration.ConfigurationException; +import io.dropwizard.configuration.ConfigurationFactory; +import io.dropwizard.configuration.DefaultConfigurationFactoryFactory; +import io.dropwizard.jackson.Jackson; + +/** + * Unit tests for {@link SnifferConfiguration}. + */ +public class SnifferConfigurationTest { + private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + private final ConfigurationFactory configFactory = + new DefaultConfigurationFactoryFactory() + .create(SnifferConfiguration.class, validator, Jackson.newObjectMapper(), "dw"); + + @Test + public void defaultConfigShouldBeValid() throws IOException, ConfigurationException { + configFactory.build(); + } +} diff --git a/src/test/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheckTest.java b/src/test/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheckTest.java index b70b03b..f1eebaf 100644 --- a/src/test/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheckTest.java +++ b/src/test/java/io/dropwizard/elasticsearch/health/EsClusterHealthCheckTest.java @@ -1,9 +1,21 @@ package io.dropwizard.elasticsearch.health; -import org.elasticsearch.client.Client; +import com.codahale.metrics.health.HealthCheck; + +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; +import org.elasticsearch.client.ClusterClient; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Unit tests for {@link EsClusterHealthCheck} @@ -16,6 +28,54 @@ public void initializationWithNullClientShouldFail() { @Test public void initializationWithClientShouldSucceed() { - new EsClusterHealthCheck(mock(Client.class)); + new EsClusterHealthCheck(mock(RestHighLevelClient.class)); + } + + @Test + public void canHaveHealthyResultsWithFormattedMessage() throws Exception { + RestHighLevelClient client = mock(RestHighLevelClient.class); + ClusterClient clusterClient = mock(ClusterClient.class); + EsClusterHealthCheck healthCheck = new EsClusterHealthCheck(client); + ClusterHealthResponse response = mock(ClusterHealthResponse.class); + + when(client.cluster()).thenReturn(clusterClient); + when(clusterClient.health(any(ClusterHealthRequest.class), any(RequestOptions.class))).thenReturn(response); + when(response.getStatus()).thenReturn(ClusterHealthStatus.GREEN); + HealthCheck.Result result = healthCheck.check(); + + assertTrue(result.isHealthy()); + assertEquals(result.getMessage(), "Last status: GREEN"); + } + + @Test + public void canHaveUnHealthyResultsWithFormattedMessage() throws Exception { + RestHighLevelClient client = mock(RestHighLevelClient.class); + ClusterClient clusterClient = mock(ClusterClient.class); + EsClusterHealthCheck healthCheck = new EsClusterHealthCheck(client); + ClusterHealthResponse response = mock(ClusterHealthResponse.class); + + when(client.cluster()).thenReturn(clusterClient); + when(clusterClient.health(any(ClusterHealthRequest.class), any(RequestOptions.class))).thenReturn(response); + when(response.getStatus()).thenReturn(ClusterHealthStatus.RED); + HealthCheck.Result result = healthCheck.check(); + + assertFalse(result.isHealthy()); + assertEquals(result.getMessage(), "Last status: RED"); + } + + @Test + public void canHaveUnHealthyResultsOnYellowWithFormattedMessage() throws Exception { + RestHighLevelClient client = mock(RestHighLevelClient.class); + ClusterClient clusterClient = mock(ClusterClient.class); + EsClusterHealthCheck healthCheck = new EsClusterHealthCheck(client, true); + ClusterHealthResponse response = mock(ClusterHealthResponse.class); + + when(client.cluster()).thenReturn(clusterClient); + when(clusterClient.health(any(ClusterHealthRequest.class), any(RequestOptions.class))).thenReturn(response); + when(response.getStatus()).thenReturn(ClusterHealthStatus.YELLOW); + HealthCheck.Result result = healthCheck.check(); + + assertFalse(result.isHealthy()); + assertEquals(result.getMessage(), "Last status: YELLOW"); } } diff --git a/src/test/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheckTest.java b/src/test/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheckTest.java index a3c0882..d2faa72 100644 --- a/src/test/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheckTest.java +++ b/src/test/java/io/dropwizard/elasticsearch/health/EsIndexDocsHealthCheckTest.java @@ -1,12 +1,23 @@ package io.dropwizard.elasticsearch.health; import com.google.common.collect.ImmutableList; -import org.elasticsearch.client.Client; + +import com.codahale.metrics.health.HealthCheck; + +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.core.CountRequest; +import org.elasticsearch.client.core.CountResponse; import org.junit.Test; import java.util.Collections; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Unit tests for {@link EsIndexDocsHealthCheck}. @@ -19,31 +30,56 @@ public void initializationWithNullClientShouldFail() { @Test(expected = IllegalArgumentException.class) public void initializationWithoutIndicesShouldFail() { - new EsIndexDocsHealthCheck(mock(Client.class), Collections.emptyList()); + new EsIndexDocsHealthCheck(mock(RestHighLevelClient.class), Collections.emptyList()); } @Test(expected = NullPointerException.class) public void initializationWithoutIndexShouldFail() { - new EsIndexDocsHealthCheck(mock(Client.class), (String) null); + new EsIndexDocsHealthCheck(mock(RestHighLevelClient.class), (String) null); } @Test public void initializationWithClientAndIndicesShouldSucceed() { - new EsIndexDocsHealthCheck(mock(Client.class), ImmutableList.of("index", "foobar")); + new EsIndexDocsHealthCheck(mock(RestHighLevelClient.class), ImmutableList.of("index", "foobar")); } @Test public void initializationWithClientAndIndexShouldSucceed() { - new EsIndexDocsHealthCheck(mock(Client.class), "index"); + new EsIndexDocsHealthCheck(mock(RestHighLevelClient.class), "index"); } @Test(expected = IllegalArgumentException.class) public void initializationWithDocumentThresholdTooLowShouldFail() { - new EsIndexDocsHealthCheck(mock(Client.class), "index", 0L); + new EsIndexDocsHealthCheck(mock(RestHighLevelClient.class), "index", 0L); } @Test public void initializationWithValidParametersShouldSucceedl() { - new EsIndexDocsHealthCheck(mock(Client.class), "index", 10L); + new EsIndexDocsHealthCheck(mock(RestHighLevelClient.class), "index", 10L); + } + + + @Test + public void canHaveHealthyResultsWithFormattedMessage() throws Exception { + RestHighLevelClient client = mock(RestHighLevelClient.class); + EsIndexDocsHealthCheck healthCheck = new EsIndexDocsHealthCheck(client, "index"); + CountResponse countResponse = mock(CountResponse.class); + when(client.count(any(CountRequest.class), any(RequestOptions.class))).thenReturn(countResponse); + when(countResponse.getCount()).thenReturn(10L); + HealthCheck.Result result = healthCheck.check(); + assertTrue(result.isHealthy()); + assertEquals(result.getMessage(), "Last stats: [index (10!)]"); + } + + @Test + public void canHaveUnhealthyResultsWithFormattedMessage() throws Exception { + RestHighLevelClient client = mock(RestHighLevelClient.class); + EsIndexDocsHealthCheck healthCheck = new EsIndexDocsHealthCheck(client, "index"); + CountResponse countResponse = mock(CountResponse.class); + when(client.count(any(CountRequest.class), any(RequestOptions.class))).thenReturn(countResponse); + when(countResponse.getCount()).thenReturn(0L); + HealthCheck.Result result = healthCheck.check(); + assertFalse(result.isHealthy()); + assertEquals(result.getMessage(), "Last stats: [index (0)]"); } } diff --git a/src/test/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheckTest.java b/src/test/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheckTest.java index 32191df..65cd8f1 100644 --- a/src/test/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheckTest.java +++ b/src/test/java/io/dropwizard/elasticsearch/health/EsIndexExistsHealthCheckTest.java @@ -1,16 +1,30 @@ package io.dropwizard.elasticsearch.health; import com.google.common.collect.ImmutableList; -import org.elasticsearch.client.Client; + +import com.codahale.metrics.health.HealthCheck; + +import org.elasticsearch.client.IndicesClient; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.indices.GetIndexRequest; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Unit tests for {@link EsIndexExistsHealthCheck}. */ +@RunWith(MockitoJUnitRunner.class) public class EsIndexExistsHealthCheckTest { @Test(expected = NullPointerException.class) public void initializationWithNullClientShouldFail() { @@ -19,21 +33,46 @@ public void initializationWithNullClientShouldFail() { @Test(expected = IllegalArgumentException.class) public void initializationWithoutIndicesShouldFail() { - new EsIndexExistsHealthCheck(mock(Client.class), Collections.emptyList()); + new EsIndexExistsHealthCheck(mock(RestHighLevelClient.class), Collections.emptyList()); } @Test(expected = NullPointerException.class) public void initializationWithoutIndexShouldFail() { - new EsIndexExistsHealthCheck(mock(Client.class), (String) null); + new EsIndexExistsHealthCheck(mock(RestHighLevelClient.class), (String) null); } @Test public void initializationWithClientAndIndexShouldSucceed() { - new EsIndexExistsHealthCheck(mock(Client.class), "index"); + new EsIndexExistsHealthCheck(mock(RestHighLevelClient.class), "index"); } @Test public void initializationWithClientAndIndicesShouldSucceed() { - new EsIndexExistsHealthCheck(mock(Client.class), ImmutableList.of("index", "foobar")); + new EsIndexExistsHealthCheck(mock(RestHighLevelClient.class), ImmutableList.of("index", "foobar")); + } + + @Test + public void canHaveHealthyResults() throws Exception { + RestHighLevelClient client = mock(RestHighLevelClient.class); + IndicesClient indicesClient = mock(IndicesClient.class); + EsIndexExistsHealthCheck healthCheck = new EsIndexExistsHealthCheck(client, "index"); + + when(client.indices()).thenReturn(indicesClient); + when(indicesClient.exists(any(GetIndexRequest.class), any(RequestOptions.class))).thenReturn(true); + HealthCheck.Result result = healthCheck.check(); + assertTrue(result.isHealthy()); + } + + @Test + public void canHaveUnhealthyResultsWithFormattedMessage() throws Exception { + RestHighLevelClient client = mock(RestHighLevelClient.class); + IndicesClient indicesClient = mock(IndicesClient.class); + EsIndexExistsHealthCheck healthCheck = new EsIndexExistsHealthCheck(client, "index"); + + when(client.indices()).thenReturn(indicesClient); + when(indicesClient.exists(any(GetIndexRequest.class), any(RequestOptions.class))).thenReturn(false); + HealthCheck.Result result = healthCheck.check(); + assertFalse(result.isHealthy()); + assertEquals(result.getMessage(), "One or more indices do not exist."); } } diff --git a/src/test/java/io/dropwizard/elasticsearch/managed/ManagedEsClientTest.java b/src/test/java/io/dropwizard/elasticsearch/managed/ManagedEsClientTest.java index af5becb..a04cf36 100644 --- a/src/test/java/io/dropwizard/elasticsearch/managed/ManagedEsClientTest.java +++ b/src/test/java/io/dropwizard/elasticsearch/managed/ManagedEsClientTest.java @@ -1,37 +1,36 @@ package io.dropwizard.elasticsearch.managed; -import com.google.common.net.HostAndPort; -import io.dropwizard.configuration.ConfigurationException; -import io.dropwizard.configuration.ConfigurationFactory; -import io.dropwizard.configuration.DefaultConfigurationFactoryFactory; -import io.dropwizard.elasticsearch.config.EsConfiguration; -import io.dropwizard.elasticsearch.util.TransportAddressHelper; -import io.dropwizard.jackson.Jackson; -import io.dropwizard.lifecycle.Managed; -import org.elasticsearch.client.Client; -import org.elasticsearch.client.node.NodeClient; -import org.elasticsearch.client.transport.TransportClient; -import org.elasticsearch.node.Node; +import org.elasticsearch.client.RestHighLevelClient; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; -import javax.validation.Validation; -import javax.validation.Validator; +import org.elasticsearch.client.sniff.Sniffer; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; +import javax.validation.Validation; +import javax.validation.Validator; + +import io.dropwizard.configuration.ConfigurationException; +import io.dropwizard.configuration.ConfigurationFactory; +import io.dropwizard.configuration.DefaultConfigurationFactoryFactory; +import io.dropwizard.elasticsearch.config.EsConfiguration; +import io.dropwizard.jackson.Jackson; +import io.dropwizard.lifecycle.Managed; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * Unit tests for {@link ManagedEsClient}. */ +@RunWith(MockitoJUnitRunner.class) public class ManagedEsClientTest { private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); private final ConfigurationFactory configFactory = @@ -39,130 +38,115 @@ public class ManagedEsClientTest { .create(EsConfiguration.class, validator, Jackson.newObjectMapper(), "dw"); @Test(expected = NullPointerException.class) - public void ensureEsConfigurationIsNotNull() { + public void ensureEsConfigurationIsNotNull() throws Exception { new ManagedEsClient((EsConfiguration) null); } - @Test(expected = NullPointerException.class) - public void ensureNodeIsNotNull() { - new ManagedEsClient((Node) null); - } - @Test(expected = NullPointerException.class) public void ensureClientIsNotNull() { - new ManagedEsClient((Client) null); + new ManagedEsClient((RestHighLevelClient) null); } @Test public void stopShouldCloseTheClient() throws Exception { - Client client = mock(Client.class); + RestHighLevelClient client = + mock(RestHighLevelClient.class); Managed managed = new ManagedEsClient(client); - + // to stub a final method it is necessary to have the + // /src/test/resources/mockito-extensions/org.mockiot.plugins.MockMaker file + // with `mock-maker-inline` as context + doNothing().when(client).close(); + assertNotNull(client); managed.start(); managed.stop(); - verify(client).close(); + assertNotNull(client); } @Test - public void lifecycleMethodsShouldStartAndCloseTheNode() throws Exception { - Node node = mock(Node.class); - when(node.isClosed()).thenReturn(false); - Managed managed = new ManagedEsClient(node); - + public void stopShouldCloseTheClientWithSniffer() throws Exception { + RestHighLevelClient client = + mock(RestHighLevelClient.class); + Sniffer sniffer = mock(Sniffer.class); + Managed managed = new ManagedEsClient(client, sniffer); + // to stub a final method it is necessary to have the + // /src/test/resources/mockito-extensions/org.mockiot.plugins.MockMaker file + // with `mock-maker-inline` as context + doNothing().when(client).close(); + doNothing().when(sniffer).close(); + assertNotNull(client); managed.start(); - verify(node).start(); - managed.stop(); - verify(node).close(); - } - - @Test - public void managedEsClientWithNodeShouldReturnClient() throws Exception { - Client client = mock(Client.class); - Node node = mock(Node.class); - when(node.client()).thenReturn(client); - - ManagedEsClient managed = new ManagedEsClient(node); - - assertSame(client, managed.getClient()); + verify(client).close(); + verify(sniffer).close(); } @Test - public void nodeClientShouldBeCreatedFromConfig() throws URISyntaxException, IOException, ConfigurationException { - URL configFileUrl = this.getClass().getResource("/node_client.yml"); + public void highLevelRestClientShouldBeCreatedFromConfig() throws Exception { + URL configFileUrl = this.getClass().getResource("/rest_client.yml"); File configFile = new File(configFileUrl.toURI()); EsConfiguration config = configFactory.build(configFile); ManagedEsClient managedEsClient = new ManagedEsClient(config); - Client client = managedEsClient.getClient(); - - assertNotNull(client); - assertTrue(client instanceof NodeClient); + RestHighLevelClient restHighLevelClient = managedEsClient.getClient(); - NodeClient nodeClient = (NodeClient) client; - assertEquals(config.getClusterName(), nodeClient.settings().get("cluster.name")); - assertEquals("true", nodeClient.settings().get("node.client")); - assertEquals("false", nodeClient.settings().get("node.data")); - } + assertNotNull(restHighLevelClient); - @Test - public void transportClientShouldBeCreatedFromConfig() throws URISyntaxException, IOException, ConfigurationException { - URL configFileUrl = this.getClass().getResource("/transport_client.yml"); - File configFile = new File(configFileUrl.toURI()); - EsConfiguration config = configFactory.build(configFile); - - ManagedEsClient managedEsClient = new ManagedEsClient(config); - Client client = managedEsClient.getClient(); + assertEquals(3, restHighLevelClient.getLowLevelClient().getNodes().size()); + assertEquals( + "127.0.0.1", + restHighLevelClient.getLowLevelClient().getNodes().get(0).getHost().getHostName()); - assertNotNull(client); - assertTrue(client instanceof TransportClient); + assertEquals( + 9200, + restHighLevelClient.getLowLevelClient().getNodes().get(0).getHost().getPort()); + assertEquals( + "127.0.0.1", + restHighLevelClient.getLowLevelClient().getNodes().get(1).getHost().getHostName()); - final TransportClient transportClient = (TransportClient) client; - assertEquals(3, transportClient.transportAddresses().size()); assertEquals( - TransportAddressHelper.fromHostAndPort(HostAndPort.fromParts("127.0.0.1", 9300)), - transportClient.transportAddresses().get(0)); + 9201, + restHighLevelClient.getLowLevelClient().getNodes().get(1).getHost().getPort()); assertEquals( - TransportAddressHelper.fromHostAndPort(HostAndPort.fromParts("127.0.0.1", 9301)), - transportClient.transportAddresses().get(1)); + "127.0.0.1", + restHighLevelClient.getLowLevelClient().getNodes().get(2).getHost().getHostName()); + assertEquals( - TransportAddressHelper.fromHostAndPort(HostAndPort.fromParts("127.0.0.1", 9302)), - transportClient.transportAddresses().get(2)); + 9202, + restHighLevelClient.getLowLevelClient().getNodes().get(2).getHost().getPort()); } @Test - public void managedClientShouldUseCustomElasticsearchConfig() throws URISyntaxException, IOException, ConfigurationException { - URL configFileUrl = this.getClass().getResource("/custom_settings_file.yml"); + public void highLevelRestClientShouldBeCreatedFromConfigWithExtendedFields() throws Exception { + URL configFileUrl = this.getClass().getResource("/rest_client_with_sniffer_on_failure.yml"); File configFile = new File(configFileUrl.toURI()); EsConfiguration config = configFactory.build(configFile); ManagedEsClient managedEsClient = new ManagedEsClient(config); - Client client = managedEsClient.getClient(); + RestHighLevelClient restHighLevelClient = managedEsClient.getClient(); - assertNotNull(client); - assertTrue(client instanceof NodeClient); + assertNotNull(restHighLevelClient); - NodeClient nodeClient = (NodeClient) client; - assertEquals(config.getClusterName(), nodeClient.settings().get("cluster.name")); - assertEquals("19300-19400", nodeClient.settings().get("transport.tcp.port")); + assertEquals(1, restHighLevelClient.getLowLevelClient().getNodes().size()); + assertEquals( + "127.0.0.1", + restHighLevelClient.getLowLevelClient().getNodes().get(0).getHost().getHostName()); } @Test - public void managedClientObeysPrecedenceOfSettings() throws URISyntaxException, IOException, ConfigurationException { - URL configFileUrl = this.getClass().getResource("/custom_settings_precedence.yml"); + public void highLevelRestClientShouldBeCreatedWithSniffer() throws Exception { + URL configFileUrl = this.getClass().getResource("/rest_client_sniffer.yml"); File configFile = new File(configFileUrl.toURI()); EsConfiguration config = configFactory.build(configFile); ManagedEsClient managedEsClient = new ManagedEsClient(config); - Client client = managedEsClient.getClient(); + RestHighLevelClient restHighLevelClient = managedEsClient.getClient(); - assertNotNull(client); - assertTrue(client instanceof NodeClient); + assertNotNull(restHighLevelClient); - NodeClient nodeClient = (NodeClient) client; - assertEquals(config.getClusterName(), nodeClient.settings().get("cluster.name")); - assertEquals("29300-29400", nodeClient.settings().get("transport.tcp.port")); - assertEquals("target/data/yaml", nodeClient.settings().get("path.home")); + assertEquals(1, restHighLevelClient.getLowLevelClient().getNodes().size()); + assertEquals( + "127.0.0.1", + restHighLevelClient.getLowLevelClient().getNodes().get(0).getHost().getHostName()); } } diff --git a/src/test/java/io/dropwizard/elasticsearch/util/TransportAddressHelperTest.java b/src/test/java/io/dropwizard/elasticsearch/util/TransportAddressHelperTest.java deleted file mode 100644 index d9699c5..0000000 --- a/src/test/java/io/dropwizard/elasticsearch/util/TransportAddressHelperTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.dropwizard.elasticsearch.util; - -import com.google.common.collect.ImmutableList; -import com.google.common.net.HostAndPort; -import org.elasticsearch.common.transport.InetSocketTransportAddress; -import org.elasticsearch.common.transport.TransportAddress; -import org.junit.Test; - -import java.util.Collections; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -/** - * Unit tests for {@link TransportAddressHelper}. - */ -public class TransportAddressHelperTest { - - private static final int ES_DEFAULT_PORT = 9300; - - @Test(expected = NullPointerException.class) - public void fromHostAndPortWithNullShouldFail() { - TransportAddressHelper.fromHostAndPort(null); - } - - @Test - public void fromHostAndPortsWithNullShouldReturnEmptyArray() { - TransportAddress[] result = TransportAddressHelper.fromHostAndPorts(null); - - assertEquals(0, result.length); - } - - @Test - public void fromHostAndPortsWithEmptyListShouldReturnEmptyArray() { - TransportAddress[] result = TransportAddressHelper.fromHostAndPorts(Collections.emptyList()); - - assertEquals(0, result.length); - } - - @Test - public void fromHostAndPortWithoutPortShouldUseDefaultPort() { - InetSocketTransportAddress result = (InetSocketTransportAddress) TransportAddressHelper - .fromHostAndPort(HostAndPort.fromString("localhost")); - - assertEquals("localhost", result.address().getHostName()); - assertEquals(ES_DEFAULT_PORT, result.address().getPort()); - } - - @Test - public void fromHostAndPortWithCorrectDataShouldSucceed() { - InetSocketTransportAddress result = (InetSocketTransportAddress) TransportAddressHelper - .fromHostAndPort(HostAndPort.fromParts("localhost", 1234)); - - assertEquals("localhost", result.address().getHostName()); - assertEquals(1234, result.address().getPort()); - } - - @Test - public void fromHostAndPostWithCorrectDataShouldSucceed() { - final List hostAndPorts = ImmutableList.of( - HostAndPort.fromParts("example.net", 1234), - HostAndPort.fromParts("example.com", 5678), - HostAndPort.fromString("example.org") - ); - final TransportAddress[] result = TransportAddressHelper.fromHostAndPorts(hostAndPorts); - - assertEquals(3, result.length); - - for (int i = 0; i < result.length; i++) { - final InetSocketTransportAddress transportAddress = (InetSocketTransportAddress) result[i]; - assertEquals(hostAndPorts.get(i).getHost(), transportAddress.address().getHostName()); - assertEquals(hostAndPorts.get(i).getPortOrDefault(ES_DEFAULT_PORT), transportAddress.address().getPort()); - } - } -} diff --git a/src/test/resources/custom_settings_file.yml b/src/test/resources/custom_settings_file.yml deleted file mode 100644 index 8d8a34e..0000000 --- a/src/test/resources/custom_settings_file.yml +++ /dev/null @@ -1,2 +0,0 @@ -clusterName: dropwizard_elasticsearch_test -settingsFile: elasticsearch.yml \ No newline at end of file diff --git a/src/test/resources/custom_settings_precedence.yml b/src/test/resources/custom_settings_precedence.yml deleted file mode 100644 index 7939520..0000000 --- a/src/test/resources/custom_settings_precedence.yml +++ /dev/null @@ -1,6 +0,0 @@ -clusterName: dropwizard_elasticsearch_test -settingsFile: elasticsearch_precedence.yml -settings: - cluster.name: settingsYaml - transport.tcp.port: 29300-29400 - path.home: "target/data/yaml" \ No newline at end of file diff --git a/src/test/resources/elasticsearch.yml b/src/test/resources/elasticsearch.yml deleted file mode 100644 index ce8c5a2..0000000 --- a/src/test/resources/elasticsearch.yml +++ /dev/null @@ -1,2 +0,0 @@ -transport.tcp.port: 19300-19400 -path.home: "target/data" \ No newline at end of file diff --git a/src/test/resources/elasticsearch_precedence.yml b/src/test/resources/elasticsearch_precedence.yml deleted file mode 100644 index 467e0f3..0000000 --- a/src/test/resources/elasticsearch_precedence.yml +++ /dev/null @@ -1,3 +0,0 @@ -cluster.name: settingsFile -transport.tcp.port: 19300-19400 -path.home: "target/data/settings" \ No newline at end of file diff --git a/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000..1f0955d --- /dev/null +++ b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline diff --git a/src/test/resources/node_client.yml b/src/test/resources/node_client.yml deleted file mode 100644 index 7d0395d..0000000 --- a/src/test/resources/node_client.yml +++ /dev/null @@ -1,3 +0,0 @@ -clusterName: dropwizard_elasticsearch_test -settings: - path.home: "target/data" \ No newline at end of file diff --git a/src/test/resources/rest_client.yml b/src/test/resources/rest_client.yml new file mode 100644 index 0000000..8f8f061 --- /dev/null +++ b/src/test/resources/rest_client.yml @@ -0,0 +1,4 @@ +servers: + - http://127.0.0.1 + - http://127.0.0.1:9201 + - http://127.0.0.1:9202 diff --git a/src/test/resources/rest_client_sniffer.yml b/src/test/resources/rest_client_sniffer.yml new file mode 100644 index 0000000..69d6d99 --- /dev/null +++ b/src/test/resources/rest_client_sniffer.yml @@ -0,0 +1,15 @@ +servers: + - http://127.0.0.1 +connectTimeOut: 5000 +socketTimeOut: 60000 +numberOfThreads: 1 +node: "node_one" +headers: + header_one: value +basicAuthentication: + user: one_user + password: mYsEcReT +sniffer: + sniffOnFailure: false + sniffIntervalMillis: 6000 + sniffAfterFailureDelayMillis: 60000 diff --git a/src/test/resources/rest_client_with_sniffer_on_failure.yml b/src/test/resources/rest_client_with_sniffer_on_failure.yml new file mode 100644 index 0000000..b351891 --- /dev/null +++ b/src/test/resources/rest_client_with_sniffer_on_failure.yml @@ -0,0 +1,10 @@ +servers: + - http://127.0.0.1 +connectTimeOut: 5000 +socketTimeOut: 60000 +numberOfThreads: 1 +node: "node_one" +sniffer: + sniffOnFailure: true + sniffIntervalMillis: 6000 + sniffAfterFailureDelayMillis: 60000 diff --git a/src/test/resources/transport_client.yml b/src/test/resources/transport_client.yml deleted file mode 100644 index 1e65b03..0000000 --- a/src/test/resources/transport_client.yml +++ /dev/null @@ -1,6 +0,0 @@ -nodeClient: false -clusterName: dropwizard_elasticsearch_test -servers: - - 127.0.0.1 - - 127.0.0.1:9301 - - 127.0.0.1:9302 \ No newline at end of file diff --git a/src/test/resources/transport_client_with_empty_server_list.yml b/src/test/resources/transport_client_with_empty_server_list.yml deleted file mode 100644 index ef0398c..0000000 --- a/src/test/resources/transport_client_with_empty_server_list.yml +++ /dev/null @@ -1,2 +0,0 @@ -nodeClient: false -clusterName: dropwizard_elasticsearch_test \ No newline at end of file