Skip to content

Commit e2b02ef

Browse files
Support Elasticsearch 8 with @Serviceconnection and Testcontainers
1 parent f22c1ba commit e2b02ef

File tree

7 files changed

+276
-5
lines changed

7 files changed

+276
-5
lines changed

spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import static org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.importedAutoConfiguration;
3838

3939
/**
40-
* Sample test for {@link DataElasticsearchTest @DataElasticsearchTest}
40+
* Integration tests for {@link DataElasticsearchTest @DataElasticsearchTest}.
4141
*
4242
* @author Eddú Meléndez
4343
* @author Moritz Halbritter

spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
import static org.assertj.core.api.Assertions.assertThat;
3232

3333
/**
34-
* Sample tests for {@link DataElasticsearchTest @DataElasticsearchTest} using reactive
35-
* repositories.
34+
* Integration tests for {@link DataElasticsearchTest @DataElasticsearchTest} using
35+
* reactive repositories.
3636
*
3737
* @author Eddú Meléndez
3838
* @author Moritz Halbritter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.data.elasticsearch;
18+
19+
import java.time.Duration;
20+
import java.util.UUID;
21+
22+
import org.junit.jupiter.api.Test;
23+
import org.testcontainers.elasticsearch.ElasticsearchContainer;
24+
import org.testcontainers.junit.jupiter.Container;
25+
import org.testcontainers.junit.jupiter.Testcontainers;
26+
27+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.boot.test.context.TestConfiguration;
30+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
31+
import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration;
32+
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
33+
import org.springframework.context.ApplicationContext;
34+
import org.springframework.context.annotation.Bean;
35+
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
36+
37+
import static org.assertj.core.api.Assertions.assertThat;
38+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
39+
import static org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.importedAutoConfiguration;
40+
41+
/**
42+
* Integration tests for {@link DataElasticsearchTest @DataElasticsearchTest} with
43+
* Elasticsearch 8.
44+
*
45+
* @author Scott Frederick
46+
*/
47+
@Testcontainers(disabledWithoutDocker = true)
48+
@DataElasticsearchTest(properties = { "spring.elasticsearch.restclient.ssl.bundle=elasticsearch-container" })
49+
class DataElasticsearchTestVersion8IntegrationTests {
50+
51+
@Container
52+
@ServiceConnection
53+
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch8())
54+
.withEnv("ES_JAVA_OPTS", "-Xms32m -Xmx512m")
55+
.withStartupAttempts(5)
56+
.withStartupTimeout(Duration.ofMinutes(10));
57+
58+
@Autowired
59+
private ElasticsearchTemplate elasticsearchTemplate;
60+
61+
@Autowired
62+
private ExampleRepository exampleRepository;
63+
64+
@Autowired
65+
private ApplicationContext applicationContext;
66+
67+
@Test
68+
void didNotInjectExampleService() {
69+
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
70+
.isThrownBy(() -> this.applicationContext.getBean(ExampleService.class));
71+
}
72+
73+
@Test
74+
void testRepository() {
75+
ExampleDocument document = new ExampleDocument();
76+
document.setText("Look, new @DataElasticsearchTest!");
77+
String id = UUID.randomUUID().toString();
78+
document.setId(id);
79+
ExampleDocument savedDocument = this.exampleRepository.save(document);
80+
ExampleDocument getDocument = this.elasticsearchTemplate.get(id, ExampleDocument.class);
81+
assertThat(getDocument).isNotNull();
82+
assertThat(getDocument.getId()).isNotNull();
83+
assertThat(getDocument.getId()).isEqualTo(savedDocument.getId());
84+
this.exampleRepository.deleteAll();
85+
}
86+
87+
@Test
88+
void serviceConnectionAutoConfigurationWasImported() {
89+
assertThat(this.applicationContext).has(importedAutoConfiguration(ServiceConnectionAutoConfiguration.class));
90+
}
91+
92+
@TestConfiguration
93+
static class ElasticsearchContainerSslBundleConfiguration {
94+
95+
@Bean
96+
ElasticsearchContainerSslBundleRegistrar elasticsearchContainerSslBundleRegistrar() {
97+
return new ElasticsearchContainerSslBundleRegistrar("elasticsearch-container", elasticsearch);
98+
}
99+
100+
}
101+
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.data.elasticsearch;
18+
19+
import java.time.Duration;
20+
21+
import org.junit.jupiter.api.Test;
22+
import org.testcontainers.elasticsearch.ElasticsearchContainer;
23+
import org.testcontainers.junit.jupiter.Container;
24+
import org.testcontainers.junit.jupiter.Testcontainers;
25+
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.boot.test.context.TestConfiguration;
28+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
29+
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate;
32+
33+
import static org.assertj.core.api.Assertions.assertThat;
34+
35+
/**
36+
* Integration tests for {@link DataElasticsearchTest @DataElasticsearchTest} with
37+
* Elasticsearch 8 using reactive repositories.
38+
*
39+
* @author Scott Frederick
40+
*/
41+
@Testcontainers(disabledWithoutDocker = true)
42+
@DataElasticsearchTest(properties = { "spring.elasticsearch.restclient.ssl.bundle=elasticsearch-container" })
43+
class DataElasticsearchTestVersion8ReactiveIntegrationTests {
44+
45+
@Container
46+
@ServiceConnection
47+
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch8())
48+
.withEnv("ES_JAVA_OPTS", "-Xms32m -Xmx512m")
49+
.withStartupAttempts(5)
50+
.withStartupTimeout(Duration.ofMinutes(10));
51+
52+
@Autowired
53+
private ReactiveElasticsearchTemplate elasticsearchTemplate;
54+
55+
@Autowired
56+
private ExampleReactiveRepository exampleReactiveRepository;
57+
58+
@Test
59+
void testRepository() {
60+
ExampleDocument exampleDocument = new ExampleDocument();
61+
exampleDocument.setText("Look, new @DataElasticsearchTest!");
62+
exampleDocument = this.exampleReactiveRepository.save(exampleDocument).block(Duration.ofSeconds(30));
63+
assertThat(exampleDocument.getId()).isNotNull();
64+
assertThat(this.elasticsearchTemplate.exists(exampleDocument.getId(), ExampleDocument.class)
65+
.block(Duration.ofSeconds(30))).isTrue();
66+
}
67+
68+
@TestConfiguration
69+
static class ElasticsearchContainerSslBundleConfiguration {
70+
71+
@Bean
72+
ElasticsearchContainerSslBundleRegistrar elasticsearchContainerSslBundleRegistrar() {
73+
return new ElasticsearchContainerSslBundleRegistrar("elasticsearch-container", elasticsearch);
74+
}
75+
76+
}
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.data.elasticsearch;
18+
19+
import javax.net.ssl.KeyManagerFactory;
20+
import javax.net.ssl.SSLContext;
21+
import javax.net.ssl.TrustManagerFactory;
22+
23+
import org.testcontainers.elasticsearch.ElasticsearchContainer;
24+
25+
import org.springframework.boot.autoconfigure.ssl.SslBundleRegistrar;
26+
import org.springframework.boot.ssl.SslBundle;
27+
import org.springframework.boot.ssl.SslBundleRegistry;
28+
import org.springframework.boot.ssl.SslManagerBundle;
29+
import org.springframework.boot.ssl.SslOptions;
30+
31+
class ElasticsearchContainerSslBundleRegistrar implements SslBundleRegistrar {
32+
33+
private final String bundleName;
34+
35+
private final ElasticsearchContainer container;
36+
37+
ElasticsearchContainerSslBundleRegistrar(String bundleName, ElasticsearchContainer container) {
38+
this.bundleName = bundleName;
39+
this.container = container;
40+
}
41+
42+
@Override
43+
public void registerBundles(SslBundleRegistry registry) {
44+
registry.registerBundle(this.bundleName,
45+
SslBundle.of(null, null, SslOptions.NONE, null, new ElasticsearchContainerSslManagerBundle()));
46+
}
47+
48+
private class ElasticsearchContainerSslManagerBundle implements SslManagerBundle {
49+
50+
@Override
51+
public KeyManagerFactory getKeyManagerFactory() {
52+
return null;
53+
}
54+
55+
@Override
56+
public TrustManagerFactory getTrustManagerFactory() {
57+
return null;
58+
}
59+
60+
@Override
61+
public SSLContext createSslContext(String protocol) {
62+
return ElasticsearchContainerSslBundleRegistrar.this.container.createSslContextFromCa();
63+
}
64+
65+
}
66+
67+
}

spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/elasticsearch/ElasticsearchContainerConnectionDetailsFactory.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.List;
2020

2121
import org.testcontainers.elasticsearch.ElasticsearchContainer;
22+
import org.testcontainers.utility.ComparableVersion;
23+
import org.testcontainers.utility.DockerImageName;
2224

2325
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails;
2426
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol;
@@ -40,6 +42,10 @@ class ElasticsearchContainerConnectionDetailsFactory
4042

4143
private static final int DEFAULT_PORT = 9200;
4244

45+
public static final String DEFAULT_USERNAME = "elastic";
46+
47+
public static final String PASSWORD_ENV_KEY = "ELASTIC_PASSWORD";
48+
4349
@Override
4450
protected ElasticsearchConnectionDetails getContainerConnectionDetails(
4551
ContainerConnectionSource<ElasticsearchContainer> source) {
@@ -61,7 +67,23 @@ private ElasticsearchContainerConnectionDetails(ContainerConnectionSource<Elasti
6167
public List<Node> getNodes() {
6268
String host = getContainer().getHost();
6369
Integer port = getContainer().getMappedPort(DEFAULT_PORT);
64-
return List.of(new Node(host, port, Protocol.HTTP, null, null));
70+
Protocol protocol = isVersion8OrGreater() ? Protocol.HTTPS : Protocol.HTTP;
71+
return List.of(new Node(host, port, protocol, null, null));
72+
}
73+
74+
@Override
75+
public String getUsername() {
76+
return isVersion8OrGreater() ? DEFAULT_USERNAME : null;
77+
}
78+
79+
@Override
80+
public String getPassword() {
81+
return isVersion8OrGreater() ? getContainer().getEnvMap().get(PASSWORD_ENV_KEY) : null;
82+
}
83+
84+
private boolean isVersion8OrGreater() {
85+
DockerImageName dockerImageName = DockerImageName.parse(getContainer().getDockerImageName());
86+
return new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0");
6587
}
6688

6789
}

spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ public static DockerImageName elasticsearch() {
106106
* @return a docker image name for running elasticsearch
107107
*/
108108
public static DockerImageName elasticsearch8() {
109-
return DockerImageName.parse("elasticsearch").withTag(ELASTICSEARCH_8_VERSION);
109+
return DockerImageName.parse("elasticsearch")
110+
.withTag(ELASTICSEARCH_8_VERSION)
111+
.asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch");
110112
}
111113

112114
/**

0 commit comments

Comments
 (0)