2929import co .elastic .clients .transport .Version ;
3030import co .elastic .clients .transport .endpoints .DelegatingJsonEndpoint ;
3131import co .elastic .clients .transport .rest_client .RestClientTransport ;
32+ import org .apache .commons .io .FileUtils ;
3233import org .apache .http .HttpHost ;
3334import org .apache .http .auth .AuthScope ;
3435import org .apache .http .auth .UsernamePasswordCredentials ;
4041import org .testcontainers .utility .DockerImageName ;
4142
4243import javax .net .ssl .SSLContext ;
44+ import java .io .File ;
4345import java .io .IOException ;
4446import java .io .InputStream ;
4547import java .net .HttpURLConnection ;
4648import java .net .URL ;
4749import java .nio .charset .StandardCharsets ;
4850import java .time .Duration ;
51+ import java .time .Instant ;
52+ import java .time .temporal .ChronoUnit ;
4953import java .util .Base64 ;
5054
5155public class ElasticsearchTestServer implements AutoCloseable {
@@ -58,6 +62,7 @@ public class ElasticsearchTestServer implements AutoCloseable {
5862 private ElasticsearchClient client ;
5963
6064 private static ElasticsearchTestServer global ;
65+ private static final String artifactsApiUrl = "https://artifacts-api.elastic.co/v1/versions/" ;
6166
6267 public static synchronized ElasticsearchTestServer global () {
6368 if (global == null ) {
@@ -112,14 +117,70 @@ AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme")
112117 client = new ElasticsearchClient (transport );
113118 }
114119
120+ private Version selectLatestVersion (Version version , String info ) {
121+ if (info .contains (version .toString ())) {
122+ return version ;
123+ }
124+ // if no version X.Y.0 was found, we give up
125+ if (version .maintenance () == 0 ) {
126+ throw new RuntimeException ("Elasticsearch server container version: " + version + " not yet " +
127+ "available" );
128+ }
129+ return selectLatestVersion (new Version (version .major (), version .minor (), version .maintenance () - 1 ,
130+ false ), info );
131+ }
132+
133+ private String fetchAndWriteVersionInfo (File file ) throws IOException {
134+ String versionInfo = IOUtils .toString (new URL (artifactsApiUrl ), StandardCharsets .UTF_8 );
135+ FileUtils .writeStringToFile (file , versionInfo , StandardCharsets .UTF_8 );
136+ return versionInfo ;
137+ }
138+
139+ private Version getLatestAvailableServerVersion (Version version ) {
140+ try {
141+ // check if there's cached information
142+ ClassLoader classLoader = getClass ().getClassLoader ();
143+ URL location = classLoader .getResource ("./co/elastic/clients/version.json" );
144+
145+ // writing the info on file before returning
146+ if (location == null ) {
147+ File file = new File (classLoader .getResource ("./co/elastic/clients" ).getFile () + "/version" +
148+ ".json" );
149+ String versionInfo = fetchAndWriteVersionInfo (file );
150+ return selectLatestVersion (version , versionInfo );
151+ }
152+
153+ File file = new File (location .getFile ());
154+
155+ // info file was found, but it's expired
156+ if (Instant .ofEpochMilli (file .lastModified ()).isBefore (Instant .now ().minus (24 ,
157+ ChronoUnit .HOURS ))) {
158+ String versionInfo = fetchAndWriteVersionInfo (file );
159+ return selectLatestVersion (version , versionInfo );
160+ }
161+
162+ // info file exists and it has new info
163+ String versionInfo = FileUtils .readFileToString (file , StandardCharsets .UTF_8 );
164+ return selectLatestVersion (version , versionInfo );
165+
166+ } catch (IOException e ) {
167+ throw new RuntimeException (e );
168+ }
169+ }
170+
115171 public synchronized ElasticsearchTestServer start () {
116172 if (this .client != null ) {
117173 return this ;
118174 }
119175
120- Version version = Version .VERSION .major () < 8 ? new Version (7 ,17 ,5 ,false ) : new Version (8 ,12 ,0 ,false );
176+ Version version = getLatestAvailableServerVersion (Version .VERSION );
177+
178+ // using specific stable version for tests with plugins
179+ if (plugins .length > 0 ) {
180+ version = Version .VERSION .major () < 8 ? new Version (7 , 17 , 25 , false ) : new Version (8 , 16 , 0 ,
181+ false );
182+ }
121183
122- // Note we could use version.major() + "." + version.minor() + "-SNAPSHOT" but plugins won't install on a snapshot version
123184 String esImage = "docker.elastic.co/elasticsearch/elasticsearch:" + version ;
124185
125186 DockerImageName image ;
@@ -166,10 +227,11 @@ public static <Req> JsonData getJsonResponse(ElasticsearchClient client, Req req
166227
167228 try {
168229 @ SuppressWarnings ("unchecked" )
169- JsonEndpoint <Req , JsonData , ErrorResponse > endpoint0 = (JsonEndpoint <Req , JsonData , ErrorResponse >) request .getClass ()
230+ JsonEndpoint <Req , JsonData , ErrorResponse > endpoint0 = (JsonEndpoint <Req , JsonData ,
231+ ErrorResponse >) request .getClass ()
170232 .getDeclaredField ("_ENDPOINT" ).get (null );
171233 endpoint = endpoint0 ;
172- } catch (IllegalAccessException | NoSuchFieldException e ) {
234+ } catch (IllegalAccessException | NoSuchFieldException e ) {
173235 throw new RuntimeException (e );
174236 }
175237
0 commit comments