Skip to content

Commit

Permalink
Elasticsearch: Add test and documentation for secured cluster, bump d…
Browse files Browse the repository at this point in the history
…efault Elasticsearch version (deprecated constructor) from 6.4.1 to 7.9.2 (#2320)

This can be now activated for free with the default basic license.
Also change latest version to 7.9.2.
  • Loading branch information
dadoonet committed Oct 15, 2020
1 parent a33622f commit 85f1e5e
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 20 deletions.
13 changes: 11 additions & 2 deletions docs/modules/elasticsearch.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This module helps running [elasticsearch](https://www.elastic.co/products/elasticsearch) using
Testcontainers.

Note that it's based on the [official Docker image](https://www.elastic.co/guide/en/elasticsearch/reference/6.3/docker.html) provided by elastic.
Note that it's based on the [official Docker image](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html) provided by elastic.

## Usage example

Expand All @@ -15,10 +15,19 @@ You can start an elasticsearch container instance from any Java application by u
<!--/codeinclude-->


Note that if you are still using the [TransportClient](https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.3/transport-client.html)
Note that if you are still using the [TransportClient](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/transport-client.html)
(not recommended as it is deprecated), the default cluster name is set to `docker-cluster` so you need to change `cluster.name` setting
or set `client.transport.ignore_cluster_name` to `true`.

## Secure your Elasticsearch cluster

The default distribution of Elasticsearch comes with the basic license which contains security feature.
You can turn on security by providing some extra environment settings:

<!--codeinclude-->
[HttpClient](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:httpClientSecuredContainer
<!--/codeinclude-->

## Choose your Elasticsearch license

If you prefer to start a Docker image with the pure OSS version (which means with no security in older versions or
Expand Down
2 changes: 1 addition & 1 deletion modules/elasticsearch/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch"
dependencies {
compile project(':testcontainers')
testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.2"
testCompile "org.elasticsearch.client:transport:6.7.1"
testCompile "org.elasticsearch.client:transport:7.9.2"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ public class ElasticsearchContainer extends GenericContainer<ElasticsearchContai

/**
* Elasticsearch Default Transport port
* The TransportClient will be removed in Elasticsearch 8. No need to expose this port anymore in the future.
*/
@Deprecated
private static final int ELASTICSEARCH_DEFAULT_TCP_PORT = 9300;

/**
Expand All @@ -35,7 +37,8 @@ public class ElasticsearchContainer extends GenericContainer<ElasticsearchContai
/**
* Elasticsearch Default version
*/
protected static final String DEFAULT_TAG = "6.4.1";
@Deprecated
protected static final String DEFAULT_TAG = "7.9.2";

/**
* @deprecated use {@link ElasticsearchContainer(DockerImageName)} instead
Expand All @@ -47,15 +50,15 @@ public ElasticsearchContainer() {

/**
* Create an Elasticsearch Container by passing the full docker image name
* @param dockerImageName Full docker image name as a {@link String}, like: docker.elastic.co/elasticsearch/elasticsearch:6.4.1
* @param dockerImageName Full docker image name as a {@link String}, like: docker.elastic.co/elasticsearch/elasticsearch:7.9.2
*/
public ElasticsearchContainer(String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}

/**
* Create an Elasticsearch Container by passing the full docker image name
* @param dockerImageName Full docker image name as a {@link DockerImageName}, like: docker.elastic.co/elasticsearch/elasticsearch:6.4.1
* @param dockerImageName Full docker image name as a {@link DockerImageName}, like: DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch:7.9.2")
*/
public ElasticsearchContainer(final DockerImageName dockerImageName) {
super(dockerImageName);
Expand All @@ -76,6 +79,7 @@ public String getHttpHostAddress() {
return getHost() + ":" + getMappedPort(ELASTICSEARCH_DEFAULT_PORT);
}

@Deprecated // The TransportClient will be removed in Elasticsearch 8. No need to expose this port anymore in the future.
public InetSocketAddress getTcpHost() {
return new InetSocketAddress(getHost(), getMappedPort(ELASTICSEARCH_DEFAULT_TCP_PORT));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows;
import static org.testcontainers.elasticsearch.ElasticsearchContainer.DEFAULT_TAG;

import java.io.IOException;
import org.apache.http.HttpHost;
Expand All @@ -13,7 +12,6 @@
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
Expand All @@ -32,35 +30,41 @@ public class ElasticsearchContainerTest {
/**
* Elasticsearch version which should be used for the Tests
*/
private static final String ELASTICSEARCH_VERSION = Version.CURRENT.toString();
private static final String ELASTICSEARCH_VERSION = "7.9.2";
private static final DockerImageName ELASTICSEARCH_IMAGE =
DockerImageName
.parse("docker.elastic.co/elasticsearch/elasticsearch")
.withTag(ELASTICSEARCH_VERSION);

/**
* Elasticsearch default username, when secured with a license > basic
* Elasticsearch default username, when secured
*/
private static final String ELASTICSEARCH_USERNAME = "elastic";

/**
* Elasticsearch 5.x default password. In 6.x images, there's no security by default as shipped with a basic license.
* From 6.8, we can optionally activate security with a default password.
*/
private static final String ELASTICSEARCH_PASSWORD = "changeme";

private RestClient client = null;
private RestClient anonymousClient = null;

@After
public void stopRestClient() throws IOException {
if (client != null) {
client.close();
client = null;
}
if (anonymousClient != null) {
anonymousClient.close();
anonymousClient = null;
}
}

@SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility
@Test
public void elasticsearchDefaultTest() throws IOException {
@Deprecated // We will remove this test in the future
public void elasticsearchDeprecatedCtorTest() throws IOException {
// Create the elasticsearch container.
try (ElasticsearchContainer container = new ElasticsearchContainer()
.withEnv("foo", "bar") // dummy env for compiler checking correct generics usage
Expand All @@ -71,7 +75,29 @@ public void elasticsearchDefaultTest() throws IOException {
// Do whatever you want with the rest client ...
Response response = getClient(container).performRequest(new Request("GET", "/"));
assertThat(response.getStatusLine().getStatusCode(), is(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString(DEFAULT_TAG));
assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION));

// The default image is running with the features under Elastic License
response = getClient(container).performRequest(new Request("GET", "/_xpack/"));
assertThat(response.getStatusLine().getStatusCode(), is(200));
// For now we test that we have the monitoring feature available
assertThat(EntityUtils.toString(response.getEntity()), containsString("monitoring"));
}
}

@Test
public void elasticsearchDefaultTest() throws IOException {
// Create the elasticsearch container.
try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
.withEnv("foo", "bar") // dummy env for compiler checking correct generics usage
) {
// Start the container. This step might take some time...
container.start();

// Do whatever you want with the rest client ...
Response response = getClient(container).performRequest(new Request("GET", "/"));
assertThat(response.getStatusLine().getStatusCode(), is(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION));

// The default image is running with the features under Elastic License
response = getClient(container).performRequest(new Request("GET", "/_xpack/"));
Expand All @@ -81,6 +107,26 @@ public void elasticsearchDefaultTest() throws IOException {
}
}

@Test
public void elasticsearchSecuredTest() throws IOException {
try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
.withEnv("ELASTIC_PASSWORD", ELASTICSEARCH_PASSWORD)
.withEnv("xpack.security.enabled", "true")
) {
container.start();

// The cluster should be secured so it must fail when we try to access / without credentials
assertThrows("We should not be able to access / URI with an anonymous client.",
ResponseException.class,
() -> getAnonymousClient(container).performRequest(new Request("GET", "/")));

// But it should work when we try to access / with the proper login and password
Response response = getClient(container).performRequest(new Request("GET", "/"));
assertThat(response.getStatusLine().getStatusCode(), is(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION));
}
}

@Test
public void elasticsearchVersion() throws IOException {
try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) {
Expand Down Expand Up @@ -113,12 +159,11 @@ public void elasticsearchOssImage() throws IOException {
}
}

@SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility
@Test
public void restClientClusterHealth() throws IOException {
// httpClientContainer {
// Create the elasticsearch container.
try (ElasticsearchContainer container = new ElasticsearchContainer()) {
try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) {
// Start the container. This step might take some time...
container.start();

Expand All @@ -140,15 +185,41 @@ public void restClientClusterHealth() throws IOException {
// }
}

@Test
public void restClientSecuredClusterHealth() throws IOException {
// httpClientSecuredContainer {
// Create the elasticsearch container.
try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
// With a password
.withEnv("ELASTIC_PASSWORD", ELASTICSEARCH_PASSWORD)
.withEnv("xpack.security.enabled", "true")) {
// Start the container. This step might take some time...
container.start();

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

client = RestClient.builder(HttpHost.create(container.getHttpHostAddress()))
.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider))
.build();

Response response = client.performRequest(new Request("GET", "/_cluster/health"));
// }}
assertThat(response.getStatusLine().getStatusCode(), is(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name"));
// httpClientSecuredContainer {{
}
// }
}

@SuppressWarnings("deprecation") // The TransportClient will be removed in Elasticsearch 8.
@Test
public void transportClientClusterHealth() {
// transportClientContainer {
// Create the elasticsearch container.
try (ElasticsearchContainer container = new ElasticsearchContainer(
DockerImageName
.parse("docker.elastic.co/elasticsearch/elasticsearch")
.withTag("6.4.1")
)){
try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)){
// Start the container. This step might take some time...
container.start();

Expand Down Expand Up @@ -182,4 +253,11 @@ private RestClient getClient(ElasticsearchContainer container) {
return client;
}

private RestClient getAnonymousClient(ElasticsearchContainer container) {
if (anonymousClient == null) {
anonymousClient = RestClient.builder(HttpHost.create(container.getHttpHostAddress())).build();
}

return anonymousClient;
}
}

0 comments on commit 85f1e5e

Please sign in to comment.