4747import java .util .Map ;
4848import java .util .Optional ;
4949import java .util .Set ;
50+ import java .util .concurrent .atomic .AtomicBoolean ;
5051import java .util .concurrent .atomic .AtomicReference ;
5152import java .util .stream .Collectors ;
5253import java .util .stream .Stream ;
@@ -75,6 +76,11 @@ public class RcsCcsCommonYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
7576 // the remote cluster is the one we write index operations etc... to
7677 private static final String REMOTE_CLUSTER_NAME = "remote_cluster" ;
7778 private static final AtomicReference <Map <String , Object >> API_KEY_MAP_REF = new AtomicReference <>();
79+ private static final AtomicBoolean isRemoteConfigured = new AtomicBoolean (false );
80+ private static final AtomicBoolean isCombinedComputed = new AtomicBoolean (false );
81+ private static final AtomicReference <TestFeatureService > combinedTestFeatureServiceRef = new AtomicReference <>();
82+ private static final AtomicReference <Set <String >> combinedOsSetRef = new AtomicReference <>();
83+ private static final AtomicReference <Set <String >> combinedNodeVersionsRef = new AtomicReference <>();
7884
7985 private static LocalClusterConfigProvider commonClusterConfig = cluster -> cluster .module ("x-pack-async-search" )
8086 .module ("aggregations" )
@@ -217,7 +223,7 @@ public void initSearchClient() throws IOException {
217223 basicAuthHeaderValue ("remote_search_user" , new SecureString ("x-pack-test-password" .toCharArray ()))
218224 )
219225 .build (),
220- clusterHosts .toArray (new HttpHost [clusterHosts . size () ])
226+ clusterHosts .toArray (new HttpHost [0 ])
221227 );
222228 adminSearchClient = buildClient (
223229 Settings .builder ()
@@ -226,13 +232,18 @@ public void initSearchClient() throws IOException {
226232 basicAuthHeaderValue ("test_admin" , new SecureString ("x-pack-test-password" .toCharArray ()))
227233 )
228234 .build (),
229- clusterHosts .toArray (new HttpHost [clusterHosts . size () ])
235+ clusterHosts .toArray (new HttpHost [0 ])
230236 );
231237
232238 searchYamlTestClient = new TestCandidateAwareClient (getRestSpec (), searchClient , hosts , this ::getClientBuilderWithSniffedHosts );
239+ }
240+
241+ assert searchClient != null ;
242+ assert adminSearchClient != null ;
243+ assert clusterHosts != null ;
233244
245+ if (isRemoteConfigured .compareAndSet (false , true )) {
234246 configureRemoteCluster ();
235- // check that we have an established CCS connection
236247 Request request = new Request ("GET" , "_remote/info" );
237248 Response response = adminSearchClient .performRequest (request );
238249 assertOK (response );
@@ -241,11 +252,6 @@ public void initSearchClient() throws IOException {
241252 assertEquals ("::es_redacted::" , responseObject .evaluate (REMOTE_CLUSTER_NAME + ".cluster_credentials" ));
242253 logger .info ("Established connection to remote cluster [" + REMOTE_CLUSTER_NAME + "]" );
243254 }
244-
245- assert searchClient != null ;
246- assert adminSearchClient != null ;
247- assert clusterHosts != null ;
248-
249255 searchYamlTestClient .setTestCandidate (getTestCandidate ());
250256 }
251257
@@ -288,42 +294,47 @@ protected ClientYamlTestExecutionContext createRestTestExecutionContext(
288294 // Ensure the test specific initialization is run by calling it explicitly (@Before annotations on base-derived class may
289295 // be called in a different order)
290296 initSearchClient ();
291- // Reconcile and provide unified features, os, version(s), based on both clientYamlTestClient and searchYamlTestClient
292- var searchOs = readOsFromNodesInfo (adminSearchClient );
293- var searchNodeVersions = readVersionsFromNodesInfo (adminSearchClient );
294- var semanticNodeVersions = searchNodeVersions .stream ()
295- .map (ESRestTestCase ::parseLegacyVersion )
296- .flatMap (Optional ::stream )
297- .collect (Collectors .toSet ());
298- final TestFeatureService searchTestFeatureService = createTestFeatureService (
299- getClusterStateFeatures (adminSearchClient ),
300- semanticNodeVersions
301- );
302- final TestFeatureService combinedTestFeatureService = (featureId , any ) -> {
303- boolean adminFeature = testFeatureService .clusterHasFeature (featureId , any );
304- boolean searchFeature = searchTestFeatureService .clusterHasFeature (featureId , any );
305- return any ? adminFeature || searchFeature : adminFeature && searchFeature ;
306- };
307297
308- final Set <String > combinedOsSet = Stream .concat (osSet .stream (), Stream .of (searchOs )).collect (Collectors .toSet ());
309- final Set <String > combinedNodeVersions = Stream .concat (nodesVersions .stream (), searchNodeVersions .stream ())
310- .collect (Collectors .toSet ());
298+ // Compute & cache combined features/OS/versions
299+ if (isCombinedComputed .compareAndSet (false , true )) {
300+ var searchOs = readOsFromNodesInfo (adminSearchClient );
301+ var searchNodeVersions = readVersionsFromNodesInfo (adminSearchClient );
302+
303+ var semanticNodeVersions = searchNodeVersions .stream ()
304+ .map (ESRestTestCase ::parseLegacyVersion )
305+ .flatMap (Optional ::stream )
306+ .collect (Collectors .toSet ());
307+
308+ final TestFeatureService searchTestFeatureService = createTestFeatureService (
309+ getClusterStateFeatures (adminSearchClient ),
310+ semanticNodeVersions
311+ );
312+
313+ final TestFeatureService combinedTestFeatureService = (featureId , any ) -> {
314+ boolean adminFeature = testFeatureService .clusterHasFeature (featureId , any );
315+ boolean searchFeature = searchTestFeatureService .clusterHasFeature (featureId , any );
316+ return any ? (adminFeature || searchFeature ) : (adminFeature && searchFeature );
317+ };
318+ final Set <String > combinedOsSet = Stream .concat (osSet .stream (), Stream .of (searchOs )).collect (Collectors .toSet ());
319+ final Set <String > combinedNodeVersions = Stream .concat (nodesVersions .stream (), searchNodeVersions .stream ())
320+ .collect (Collectors .toSet ());
321+
322+ combinedTestFeatureServiceRef .set (combinedTestFeatureService );
323+ combinedOsSetRef .set (combinedOsSet );
324+ combinedNodeVersionsRef .set (combinedNodeVersions );
325+ }
311326
312327 return new ClientYamlTestExecutionContext (
313328 clientYamlTestCandidate ,
314329 clientYamlTestClient ,
315330 randomizeContentType (),
316- combinedNodeVersions ,
317- combinedTestFeatureService ,
318- combinedOsSet
331+ combinedNodeVersionsRef . get () ,
332+ combinedTestFeatureServiceRef . get () ,
333+ combinedOsSetRef . get ()
319334 ) {
320335 // depending on the API called, we either return the client running against the "write" or the "search" cluster here
321336 protected ClientYamlTestClient clientYamlTestClient (String apiName ) {
322- if (CCS_APIS .contains (apiName )) {
323- return searchYamlTestClient ;
324- } else {
325- return super .clientYamlTestClient (apiName );
326- }
337+ return CCS_APIS .contains (apiName ) ? searchYamlTestClient : super .clientYamlTestClient (apiName );
327338 }
328339 };
329340 } catch (IOException e ) {
0 commit comments