49
49
import org .elasticsearch .client .RestClientBuilder ;
50
50
import org .elasticsearch .client .WarningsHandler ;
51
51
import org .elasticsearch .cluster .metadata .Metadata ;
52
+ import org .elasticsearch .cluster .metadata .ProjectId ;
52
53
import org .elasticsearch .common .Strings ;
53
54
import org .elasticsearch .common .bytes .BytesArray ;
54
55
import org .elasticsearch .common .bytes .BytesReference ;
55
56
import org .elasticsearch .common .settings .SecureString ;
56
57
import org .elasticsearch .common .settings .Settings ;
57
58
import org .elasticsearch .common .ssl .PemUtils ;
59
+ import org .elasticsearch .common .util .CollectionUtils ;
58
60
import org .elasticsearch .common .util .concurrent .ThreadContext ;
59
61
import org .elasticsearch .common .util .set .Sets ;
60
62
import org .elasticsearch .common .xcontent .XContentHelper ;
92
94
import org .junit .After ;
93
95
import org .junit .AfterClass ;
94
96
import org .junit .Before ;
97
+ import org .junit .BeforeClass ;
95
98
96
99
import java .io .BufferedReader ;
97
100
import java .io .IOException ;
118
121
import java .util .HashMap ;
119
122
import java .util .HashSet ;
120
123
import java .util .List ;
124
+ import java .util .Locale ;
121
125
import java .util .Map ;
122
126
import java .util .Objects ;
123
127
import java .util .Optional ;
@@ -266,6 +270,9 @@ public static boolean hasXPack() {
266
270
private static RestClient cleanupClient ;
267
271
268
272
private static boolean multiProjectEnabled ;
273
+ private static String activeProject ;
274
+ private static Set <String > extraProjects ;
275
+ private static boolean projectsConfigured = false ;
269
276
270
277
public enum ProductFeature {
271
278
XPACK ,
@@ -357,6 +364,15 @@ protected static boolean testFeatureServiceInitialized() {
357
364
return testFeatureService != ALL_FEATURES ;
358
365
}
359
366
367
+ @ BeforeClass
368
+ public static void initializeProjectIds () {
369
+ // The active project-id is slightly longer, and has a fixed prefix so that it's easier to pick in error messages etc.
370
+ activeProject = "active00" + randomAlphaOfLength (8 ).toLowerCase (Locale .ROOT );
371
+ extraProjects = randomSet (1 , 3 , () -> randomAlphaOfLength (12 ).toLowerCase (Locale .ROOT ));
372
+ // TODO do this in a different way
373
+ multiProjectEnabled = Objects .equals (System .getProperty ("test.multi_project.enabled" ), "true" );
374
+ }
375
+
360
376
@ Before
361
377
public void initClient () throws IOException {
362
378
if (client == null ) {
@@ -367,17 +383,17 @@ public void initClient() throws IOException {
367
383
assert testFeatureServiceInitialized () == false ;
368
384
clusterHosts = parseClusterHosts (getTestRestCluster ());
369
385
logger .info ("initializing REST clients against {}" , clusterHosts );
370
- var clientSettings = restClientSettings ();
386
+ var clientSettings = addProjectIdToSettings ( restClientSettings () );
371
387
var adminSettings = restAdminSettings ();
388
+ var cleanupSettings = cleanupClientSettings ();
372
389
var hosts = clusterHosts .toArray (new HttpHost [0 ]);
373
390
client = buildClient (clientSettings , hosts );
374
391
adminClient = clientSettings .equals (adminSettings ) ? client : buildClient (adminSettings , hosts );
375
- cleanupClient = getCleanupClient ( );
392
+ cleanupClient = adminSettings . equals ( cleanupSettings ) ? adminClient : buildClient ( cleanupSettings , hosts );
376
393
377
394
availableFeatures = EnumSet .of (ProductFeature .LEGACY_TEMPLATES );
378
395
Set <String > versions = new HashSet <>();
379
396
boolean serverless = false ;
380
- String multiProjectPluginVariant = null ;
381
397
382
398
for (Map <?, ?> nodeInfo : getNodesInfo (adminClient ).values ()) {
383
399
var nodeVersion = nodeInfo .get ("version" ).toString ();
@@ -407,11 +423,6 @@ public void initClient() throws IOException {
407
423
if (moduleName .startsWith ("serverless-" )) {
408
424
serverless = true ;
409
425
}
410
- if (moduleName .contains ("test-multi-project" )) {
411
- multiProjectPluginVariant = "test" ;
412
- } else if (moduleName .contains ("serverless-multi-project" )) {
413
- multiProjectPluginVariant = "serverless" ;
414
- }
415
426
}
416
427
if (serverless ) {
417
428
availableFeatures .removeAll (
@@ -432,22 +443,11 @@ public void initClient() throws IOException {
432
443
.flatMap (Optional ::stream )
433
444
.collect (Collectors .toSet ());
434
445
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
-
448
446
testFeatureService = createTestFeatureService (getClusterStateFeatures (adminClient ), semanticNodeVersions );
449
447
}
450
448
449
+ configureProjects ();
450
+
451
451
assert testFeatureServiceInitialized ();
452
452
assert client != null ;
453
453
assert adminClient != null ;
@@ -456,6 +456,40 @@ public void initClient() throws IOException {
456
456
assert nodesVersions != null ;
457
457
}
458
458
459
+ private void configureProjects () throws IOException {
460
+ if (projectsConfigured || multiProjectEnabled == false ) {
461
+ return ;
462
+ }
463
+ projectsConfigured = true ;
464
+ createProject (activeProject );
465
+ for (var project : extraProjects ) {
466
+ createProject (project );
467
+ }
468
+
469
+ // The admin client does not set a project id, and can see all projects
470
+ assertProjectIds (
471
+ adminClient (),
472
+ CollectionUtils .concatLists (List .of (Metadata .DEFAULT_PROJECT_ID .id (), activeProject ), extraProjects )
473
+ );
474
+ // The test client can only see the project it targets
475
+ assertProjectIds (client (), List .of (activeProject ));
476
+ }
477
+
478
+ @ After
479
+ public final void assertEmptyProjects () throws Exception {
480
+ if (multiProjectEnabled == false ) {
481
+ return ;
482
+ }
483
+ assertEmptyProject (Metadata .DEFAULT_PROJECT_ID .id ());
484
+ for (var project : extraProjects ) {
485
+ assertEmptyProject (project );
486
+ }
487
+ }
488
+
489
+ public static String activeProject () {
490
+ return activeProject ;
491
+ }
492
+
459
493
protected final TestFeatureService createTestFeatureService (
460
494
Map <String , Set <String >> clusterStateFeatures ,
461
495
Set <Version > semanticNodeVersions
@@ -1604,10 +1638,6 @@ protected Settings restClientSettings() {
1604
1638
String token = basicAuthHeaderValue (username , new SecureString (password .toCharArray ()));
1605
1639
builder .put (ThreadContext .PREFIX + ".Authorization" , token );
1606
1640
}
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
- }
1611
1641
return builder .build ();
1612
1642
}
1613
1643
@@ -1621,9 +1651,21 @@ protected Settings restAdminSettings() {
1621
1651
/**
1622
1652
* Returns the REST client used for cleaning up the cluster.
1623
1653
*/
1624
- protected RestClient getCleanupClient () {
1625
- assert adminClient != null ;
1626
- return adminClient ;
1654
+ protected Settings cleanupClientSettings () {
1655
+ if (multiProjectEnabled == false ) {
1656
+ return restAdminSettings ();
1657
+ }
1658
+ return addProjectIdToSettings (restAdminSettings ());
1659
+ }
1660
+
1661
+ private Settings addProjectIdToSettings (Settings settings ) {
1662
+ if (multiProjectEnabled == false ) {
1663
+ return settings ;
1664
+ }
1665
+ return Settings .builder ()
1666
+ .put (settings )
1667
+ .put (ThreadContext .PREFIX + "." + Task .X_ELASTIC_PROJECT_ID_HTTP_HEADER , activeProject )
1668
+ .build ();
1627
1669
}
1628
1670
1629
1671
/**
@@ -2716,10 +2758,9 @@ protected static void assertResultMap(
2716
2758
2717
2759
protected void createProject (String project ) throws IOException {
2718
2760
assert multiProjectEnabled ;
2719
- RestClient client = adminClient ();
2720
2761
final Request request = new Request ("PUT" , "/_project/" + project );
2721
2762
try {
2722
- final Response response = client .performRequest (request );
2763
+ final Response response = adminClient () .performRequest (request );
2723
2764
logger .info ("Created project {} : {}" , project , response .getStatusLine ());
2724
2765
} catch (ResponseException e ) {
2725
2766
logger .error ("Failed to create project: {}" , project );
@@ -2750,6 +2791,21 @@ private Collection<String> getProjectIds(RestClient client) throws IOException {
2750
2791
}
2751
2792
}
2752
2793
2794
+ protected void cleanUpProjects () throws IOException {
2795
+ final var projectIds = getProjectIds (adminClient ());
2796
+ for (String projectId : projectIds ) {
2797
+ if (projectId .equals (ProjectId .DEFAULT .id ())) {
2798
+ continue ;
2799
+ }
2800
+ deleteProject (projectId );
2801
+ }
2802
+ }
2803
+
2804
+ private void deleteProject (String project ) throws IOException {
2805
+ final Request request = new Request ("DELETE" , "/_project/" + project );
2806
+ cleanupClient ().performRequest (request );
2807
+ }
2808
+
2753
2809
protected void assertEmptyProject (String projectId ) throws IOException {
2754
2810
assert multiProjectEnabled ;
2755
2811
final Request request = new Request ("GET" , "_cluster/state/metadata,routing_table,customs" );
@@ -2790,16 +2846,12 @@ protected void assertEmptyProject(String projectId) throws IOException {
2790
2846
if (indexTemplates != null ) {
2791
2847
var templateNames = indexTemplates .keySet ().stream ().filter (name -> isXPackTemplate (name ) == false ).toList ();
2792
2848
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" );
2795
2849
}
2796
2850
2797
2851
final Map <String , Object > componentTemplates = state .evaluate ("metadata.component_template.component_template" );
2798
2852
if (componentTemplates != null ) {
2799
2853
var templateNames = componentTemplates .keySet ().stream ().filter (name -> isXPackTemplate (name ) == false ).toList ();
2800
2854
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" );
2803
2855
}
2804
2856
2805
2857
final List <Map <String , ?>> pipelines = state .evaluate ("metadata.ingest.pipeline" );
@@ -2809,8 +2861,6 @@ protected void assertEmptyProject(String projectId) throws IOException {
2809
2861
.filter (id -> isXPackIngestPipeline (id ) == false )
2810
2862
.toList ();
2811
2863
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" );
2814
2864
}
2815
2865
2816
2866
if (has (ProductFeature .ILM )) {
@@ -2819,8 +2869,6 @@ protected void assertEmptyProject(String projectId) throws IOException {
2819
2869
var policyNames = new HashSet <>(ilmPolicies .keySet ());
2820
2870
policyNames .removeAll (preserveILMPolicyIds ());
2821
2871
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" );
2824
2872
}
2825
2873
}
2826
2874
}
0 commit comments