5555import org .elasticsearch .common .settings .SecureString ;
5656import org .elasticsearch .common .settings .Settings ;
5757import org .elasticsearch .common .ssl .PemUtils ;
58+ import org .elasticsearch .common .util .CollectionUtils ;
5859import org .elasticsearch .common .util .concurrent .ThreadContext ;
5960import org .elasticsearch .common .util .set .Sets ;
6061import org .elasticsearch .common .xcontent .XContentHelper ;
9293import org .junit .After ;
9394import org .junit .AfterClass ;
9495import org .junit .Before ;
96+ import org .junit .BeforeClass ;
9597
9698import java .io .BufferedReader ;
9799import java .io .IOException ;
118120import java .util .HashMap ;
119121import java .util .HashSet ;
120122import java .util .List ;
123+ import java .util .Locale ;
121124import java .util .Map ;
122125import java .util .Objects ;
123126import java .util .Optional ;
@@ -266,6 +269,9 @@ public static boolean hasXPack() {
266269 private static RestClient cleanupClient ;
267270
268271 private static boolean multiProjectEnabled ;
272+ private static String activeProject ;
273+ private static Set <String > extraProjects ;
274+ private static boolean projectsConfigured = false ;
269275
270276 public enum ProductFeature {
271277 XPACK ,
@@ -357,6 +363,15 @@ protected static boolean testFeatureServiceInitialized() {
357363 return testFeatureService != ALL_FEATURES ;
358364 }
359365
366+ @ BeforeClass
367+ public static void initializeProjectIds () {
368+ // The active project-id is slightly longer, and has a fixed prefix so that it's easier to pick in error messages etc.
369+ activeProject = "active00" + randomAlphaOfLength (8 ).toLowerCase (Locale .ROOT );
370+ extraProjects = randomSet (1 , 3 , () -> randomAlphaOfLength (12 ).toLowerCase (Locale .ROOT ));
371+ // TODO do this in a different way
372+ multiProjectEnabled = Objects .equals (System .getProperty ("test.multi_project.enabled" ), "true" );
373+ }
374+
360375 @ Before
361376 public void initClient () throws IOException {
362377 if (client == null ) {
@@ -367,17 +382,17 @@ public void initClient() throws IOException {
367382 assert testFeatureServiceInitialized () == false ;
368383 clusterHosts = parseClusterHosts (getTestRestCluster ());
369384 logger .info ("initializing REST clients against {}" , clusterHosts );
370- var clientSettings = restClientSettings ();
385+ var clientSettings = addProjectIdToSettings ( restClientSettings () );
371386 var adminSettings = restAdminSettings ();
387+ var cleanupSettings = cleanupClientSettings ();
372388 var hosts = clusterHosts .toArray (new HttpHost [0 ]);
373389 client = buildClient (clientSettings , hosts );
374390 adminClient = clientSettings .equals (adminSettings ) ? client : buildClient (adminSettings , hosts );
375- cleanupClient = getCleanupClient ( );
391+ cleanupClient = adminSettings . equals ( cleanupSettings ) ? adminClient : buildClient ( cleanupSettings , hosts );
376392
377393 availableFeatures = EnumSet .of (ProductFeature .LEGACY_TEMPLATES );
378394 Set <String > versions = new HashSet <>();
379395 boolean serverless = false ;
380- String multiProjectPluginVariant = null ;
381396
382397 for (Map <?, ?> nodeInfo : getNodesInfo (adminClient ).values ()) {
383398 var nodeVersion = nodeInfo .get ("version" ).toString ();
@@ -407,11 +422,6 @@ public void initClient() throws IOException {
407422 if (moduleName .startsWith ("serverless-" )) {
408423 serverless = true ;
409424 }
410- if (moduleName .contains ("test-multi-project" )) {
411- multiProjectPluginVariant = "test" ;
412- } else if (moduleName .contains ("serverless-multi-project" )) {
413- multiProjectPluginVariant = "serverless" ;
414- }
415425 }
416426 if (serverless ) {
417427 availableFeatures .removeAll (
@@ -432,22 +442,11 @@ public void initClient() throws IOException {
432442 .flatMap (Optional ::stream )
433443 .collect (Collectors .toSet ());
434444 assert semanticNodeVersions .isEmpty () == false || serverless ;
435-
436- if (multiProjectPluginVariant != null ) {
437- final Request settingRequest = new Request (
438- "GET" ,
439- "/_cluster/settings?include_defaults&filter_path=*." + multiProjectPluginVariant + ".multi_project.enabled"
440- );
441- settingRequest .setOptions (RequestOptions .DEFAULT .toBuilder ().setWarningsHandler (WarningsHandler .PERMISSIVE ));
442- final var response = entityAsMap (adminClient .performRequest (settingRequest ));
443- multiProjectEnabled = Boolean .parseBoolean (
444- ObjectPath .evaluate (response , "defaults." + multiProjectPluginVariant + ".multi_project.enabled" )
445- );
446- }
447-
448445 testFeatureService = createTestFeatureService (getClusterStateFeatures (adminClient ), semanticNodeVersions );
449446 }
450447
448+ configureProjects ();
449+
451450 assert testFeatureServiceInitialized ();
452451 assert client != null ;
453452 assert adminClient != null ;
@@ -456,6 +455,40 @@ public void initClient() throws IOException {
456455 assert nodesVersions != null ;
457456 }
458457
458+ private void configureProjects () throws IOException {
459+ if (projectsConfigured || multiProjectEnabled == false ) {
460+ return ;
461+ }
462+ projectsConfigured = true ;
463+ createProject (activeProject );
464+ for (var project : extraProjects ) {
465+ createProject (project );
466+ }
467+
468+ // The admin client does not set a project id, and can see all projects
469+ assertProjectIds (
470+ adminClient (),
471+ CollectionUtils .concatLists (List .of (Metadata .DEFAULT_PROJECT_ID .id (), activeProject ), extraProjects )
472+ );
473+ // The test client can only see the project it targets
474+ assertProjectIds (client (), List .of (activeProject ));
475+ }
476+
477+ @ After
478+ public final void assertEmptyProjects () throws Exception {
479+ if (multiProjectEnabled == false ) {
480+ return ;
481+ }
482+ assertEmptyProject (Metadata .DEFAULT_PROJECT_ID .id ());
483+ for (var project : extraProjects ) {
484+ assertEmptyProject (project );
485+ }
486+ }
487+
488+ public static String activeProject () {
489+ return activeProject ;
490+ }
491+
459492 protected final TestFeatureService createTestFeatureService (
460493 Map <String , Set <String >> clusterStateFeatures ,
461494 Set <Version > semanticNodeVersions
@@ -1604,10 +1637,6 @@ protected Settings restClientSettings() {
16041637 String token = basicAuthHeaderValue (username , new SecureString (password .toCharArray ()));
16051638 builder .put (ThreadContext .PREFIX + ".Authorization" , token );
16061639 }
1607- if (System .getProperty ("tests.rest.project.id" ) != null ) {
1608- final var projectId = System .getProperty ("tests.rest.project.id" );
1609- builder .put (ThreadContext .PREFIX + ".X-Elastic-Project-Id" , projectId );
1610- }
16111640 return builder .build ();
16121641 }
16131642
@@ -1621,9 +1650,21 @@ protected Settings restAdminSettings() {
16211650 /**
16221651 * Returns the REST client used for cleaning up the cluster.
16231652 */
1624- protected RestClient getCleanupClient () {
1625- assert adminClient != null ;
1626- return adminClient ;
1653+ protected Settings cleanupClientSettings () {
1654+ if (multiProjectEnabled == false ) {
1655+ return restAdminSettings ();
1656+ }
1657+ return addProjectIdToSettings (restAdminSettings ());
1658+ }
1659+
1660+ private Settings addProjectIdToSettings (Settings settings ) {
1661+ if (multiProjectEnabled == false ) {
1662+ return settings ;
1663+ }
1664+ return Settings .builder ()
1665+ .put (settings )
1666+ .put (ThreadContext .PREFIX + "." + Task .X_ELASTIC_PROJECT_ID_HTTP_HEADER , activeProject )
1667+ .build ();
16271668 }
16281669
16291670 /**
@@ -2716,10 +2757,9 @@ protected static void assertResultMap(
27162757
27172758 protected void createProject (String project ) throws IOException {
27182759 assert multiProjectEnabled ;
2719- RestClient client = adminClient ();
27202760 final Request request = new Request ("PUT" , "/_project/" + project );
27212761 try {
2722- final Response response = client .performRequest (request );
2762+ final Response response = adminClient () .performRequest (request );
27232763 logger .info ("Created project {} : {}" , project , response .getStatusLine ());
27242764 } catch (ResponseException e ) {
27252765 logger .error ("Failed to create project: {}" , project );
@@ -2790,16 +2830,12 @@ protected void assertEmptyProject(String projectId) throws IOException {
27902830 if (indexTemplates != null ) {
27912831 var templateNames = indexTemplates .keySet ().stream ().filter (name -> isXPackTemplate (name ) == false ).toList ();
27922832 assertThat ("Project [" + projectId + "] should not have index templates" , templateNames , empty ());
2793- } else if (projectId .equals (Metadata .DEFAULT_PROJECT_ID .id ())) {
2794- fail ("Expected default project to have standard templates, but was null" );
27952833 }
27962834
27972835 final Map <String , Object > componentTemplates = state .evaluate ("metadata.component_template.component_template" );
27982836 if (componentTemplates != null ) {
27992837 var templateNames = componentTemplates .keySet ().stream ().filter (name -> isXPackTemplate (name ) == false ).toList ();
28002838 assertThat ("Project [" + projectId + "] should not have component templates" , templateNames , empty ());
2801- } else if (projectId .equals (Metadata .DEFAULT_PROJECT_ID .id ())) {
2802- fail ("Expected default project to have standard component templates, but was null" );
28032839 }
28042840
28052841 final List <Map <String , ?>> pipelines = state .evaluate ("metadata.ingest.pipeline" );
@@ -2809,8 +2845,6 @@ protected void assertEmptyProject(String projectId) throws IOException {
28092845 .filter (id -> isXPackIngestPipeline (id ) == false )
28102846 .toList ();
28112847 assertThat ("Project [" + projectId + "] should not have ingest pipelines" , pipelineNames , empty ());
2812- } else if (projectId .equals (Metadata .DEFAULT_PROJECT_ID .id ())) {
2813- fail ("Expected default project to have standard ingest pipelines, but was null" );
28142848 }
28152849
28162850 if (has (ProductFeature .ILM )) {
@@ -2819,8 +2853,6 @@ protected void assertEmptyProject(String projectId) throws IOException {
28192853 var policyNames = new HashSet <>(ilmPolicies .keySet ());
28202854 policyNames .removeAll (preserveILMPolicyIds ());
28212855 assertThat ("Project [" + projectId + "] should not have ILM Policies" , policyNames , empty ());
2822- } else if (projectId .equals (Metadata .DEFAULT_PROJECT_ID .id ())) {
2823- fail ("Expected default project to have standard ILM policies, but was null" );
28242856 }
28252857 }
28262858 }
0 commit comments