88package org .elasticsearch .xpack .security .support ;
99
1010import org .elasticsearch .action .ActionFuture ;
11+ import org .elasticsearch .cluster .ClusterChangedEvent ;
12+ import org .elasticsearch .cluster .ClusterState ;
13+ import org .elasticsearch .cluster .ClusterStateListener ;
1114import org .elasticsearch .cluster .metadata .IndexMetadata ;
1215import org .elasticsearch .cluster .service .ClusterService ;
1316import org .elasticsearch .core .TimeValue ;
3134import java .util .Arrays ;
3235import java .util .Collections ;
3336import java .util .List ;
37+ import java .util .concurrent .CountDownLatch ;
3438import java .util .concurrent .TimeUnit ;
39+ import java .util .concurrent .atomic .AtomicInteger ;
3540import java .util .concurrent .atomic .AtomicLong ;
3641
3742import static org .elasticsearch .integration .RoleMappingFileSettingsIT .setupClusterStateListener ;
4348import static org .hamcrest .Matchers .equalTo ;
4449import static org .hamcrest .Matchers .greaterThanOrEqualTo ;
4550import static org .hamcrest .Matchers .lessThan ;
51+ import static org .hamcrest .Matchers .lessThanOrEqualTo ;
4652
4753@ ESIntegTestCase .ClusterScope (scope = ESIntegTestCase .Scope .TEST , numDataNodes = 0 , autoManageMasterNodes = false )
4854public class CleanupRoleMappingDuplicatesMigrationIT extends SecurityIntegTestCase {
@@ -255,6 +261,20 @@ public void testSkipMigrationNoFileBasedMappings() throws Exception {
255261 assertAllRoleMappings ("everyone_kibana_alone" , "everyone_fleet_alone" );
256262 }
257263
264+ public void testNewIndexSkipMigration () {
265+ internalCluster ().setBootstrapMasterNodeIndex (0 );
266+ final String masterNode = internalCluster ().getMasterName ();
267+ ensureGreen ();
268+ CountDownLatch awaitMigrations = awaitMigrationVersionUpdates (
269+ masterNode ,
270+ SecurityMigrations .CLEANUP_ROLE_MAPPING_DUPLICATES_MIGRATION_VERSION
271+ );
272+ // Create a native role mapping to create security index and trigger migration
273+ createNativeRoleMapping ("everyone_kibana_alone" );
274+ // Make sure no migration ran (set to current version without applying prior migrations)
275+ safeAwait (awaitMigrations );
276+ }
277+
258278 private void assertAllRoleMappings (String ... roleMappingNames ) {
259279 GetRoleMappingsResponse response = client ().execute (GetRoleMappingsAction .INSTANCE , new GetRoleMappingsRequest ()).actionGet ();
260280
@@ -270,6 +290,34 @@ private void assertAllRoleMappings(String... roleMappingNames) {
270290 );
271291 }
272292
293+ /**
294+ * Make sure all versions are applied to cluster state sequentially
295+ */
296+ private CountDownLatch awaitMigrationVersionUpdates (String node , final int ... versions ) {
297+ final ClusterService clusterService = internalCluster ().clusterService (node );
298+ final CountDownLatch allVersionsCountDown = new CountDownLatch (1 );
299+ final AtomicInteger currentVersionIdx = new AtomicInteger (0 );
300+ clusterService .addListener (new ClusterStateListener () {
301+ @ Override
302+ public void clusterChanged (ClusterChangedEvent event ) {
303+ int currentMigrationVersion = getCurrentMigrationVersion (event .state ());
304+ if (currentMigrationVersion > 0 ) {
305+ assertThat (versions [currentVersionIdx .get ()], lessThanOrEqualTo (currentMigrationVersion ));
306+ if (versions [currentVersionIdx .get ()] == currentMigrationVersion ) {
307+ currentVersionIdx .incrementAndGet ();
308+ }
309+
310+ if (currentVersionIdx .get () >= versions .length ) {
311+ clusterService .removeListener (this );
312+ allVersionsCountDown .countDown ();
313+ }
314+ }
315+ }
316+ });
317+
318+ return allVersionsCountDown ;
319+ }
320+
273321 private void awaitFileSettingsWatcher () throws Exception {
274322 final String masterNode = internalCluster ().getMasterName ();
275323 FileSettingsService masterFileSettingsService = internalCluster ().getInstance (FileSettingsService .class , masterNode );
@@ -311,8 +359,11 @@ private void assertMigrationLessThan(int expectedVersion) {
311359 }
312360
313361 private int getCurrentMigrationVersion () {
314- ClusterService clusterService = internalCluster ().getInstance (ClusterService .class );
315- IndexMetadata indexMetadata = clusterService .state ().metadata ().getIndices ().get (INTERNAL_SECURITY_MAIN_INDEX_7 );
362+ return getCurrentMigrationVersion (internalCluster ().getInstance (ClusterService .class ).state ());
363+ }
364+
365+ private int getCurrentMigrationVersion (ClusterState state ) {
366+ IndexMetadata indexMetadata = state .metadata ().getIndices ().get (INTERNAL_SECURITY_MAIN_INDEX_7 );
316367 if (indexMetadata == null || indexMetadata .getCustomData (MIGRATION_VERSION_CUSTOM_KEY ) == null ) {
317368 return 0 ;
318369 }
0 commit comments