Skip to content

Commit 9533f6f

Browse files
committed
Use index setting to track migration version index created on
1 parent 1e65bf7 commit 9533f6f

File tree

7 files changed

+87
-8
lines changed

7 files changed

+87
-8
lines changed

server/src/main/java/org/elasticsearch/index/IndexVersions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ private static IndexVersion def(int id, Version luceneVersion) {
118118
public static final IndexVersion MERGE_ON_RECOVERY_VERSION = def(8_515_00_0, Version.LUCENE_9_11_1);
119119
public static final IndexVersion UPGRADE_TO_LUCENE_9_12 = def(8_516_00_0, Version.LUCENE_9_12_0);
120120
public static final IndexVersion ENABLE_IGNORE_ABOVE_LOGSDB = def(8_517_00_0, Version.LUCENE_9_12_0);
121-
public static final IndexVersion ADD_ROLE_MAPPING_CLEANUP_MIGRATION = def(8_518_00_0, Version.LUCENE_9_12_0);
121+
122122
/*
123123
* STOP! READ THIS FIRST! No, really,
124124
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _

x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/support/CleanupRoleMappingDuplicatesMigrationIT.java

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
package org.elasticsearch.xpack.security.support;
99

1010
import org.elasticsearch.action.ActionFuture;
11+
import org.elasticsearch.cluster.ClusterChangedEvent;
12+
import org.elasticsearch.cluster.ClusterState;
13+
import org.elasticsearch.cluster.ClusterStateListener;
1114
import org.elasticsearch.cluster.metadata.IndexMetadata;
1215
import org.elasticsearch.cluster.service.ClusterService;
1316
import org.elasticsearch.core.TimeValue;
@@ -31,7 +34,9 @@
3134
import java.util.Arrays;
3235
import java.util.Collections;
3336
import java.util.List;
37+
import java.util.concurrent.CountDownLatch;
3438
import java.util.concurrent.TimeUnit;
39+
import java.util.concurrent.atomic.AtomicInteger;
3540
import java.util.concurrent.atomic.AtomicLong;
3641

3742
import static org.elasticsearch.integration.RoleMappingFileSettingsIT.setupClusterStateListener;
@@ -43,6 +48,7 @@
4348
import static org.hamcrest.Matchers.equalTo;
4449
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
4550
import static org.hamcrest.Matchers.lessThan;
51+
import static org.hamcrest.Matchers.lessThanOrEqualTo;
4652

4753
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false)
4854
public 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
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,7 @@ public static List<Setting<?>> getSettings(List<SecurityExtension> securityExten
14441444
settingsList.add(CachingServiceAccountTokenStore.CACHE_MAX_TOKENS_SETTING);
14451445
settingsList.add(SimpleRole.CACHE_SIZE_SETTING);
14461446
settingsList.add(NativeRoleMappingStore.LAST_LOAD_CACHE_ENABLED_SETTING);
1447+
settingsList.add(SecuritySystemIndices.MAIN_INDEX_CREATED_ON_MIGRATION_VERSION);
14471448

14481449
// hide settings
14491450
settingsList.add(Setting.stringListSetting(SecurityField.setting("hide_settings"), Property.NodeScope, Property.Filtered));

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/SecurityIndexManager.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,19 @@ private SystemIndexDescriptor.MappingsVersion getMinSecurityIndexMappingVersion(
271271
* Check if the index was created on the latest index version available in the cluster
272272
*/
273273
private static boolean isCreatedOnLatestVersion(IndexMetadata indexMetadata) {
274-
final IndexVersion indexVersionCreated = indexMetadata != null
275-
? SETTING_INDEX_VERSION_CREATED.get(indexMetadata.getSettings())
276-
: null;
277-
return indexVersionCreated != null && indexVersionCreated.onOrAfter(IndexVersion.current());
274+
if (indexMetadata == null) {
275+
return false;
276+
}
277+
278+
final Integer indexCreatedOnMigrationVersion = SecuritySystemIndices.MAIN_INDEX_CREATED_ON_MIGRATION_VERSION.get(
279+
indexMetadata.getSettings()
280+
);
281+
282+
// 1 is the first migration, before the MAIN_INDEX_CREATED_ON_MIGRATION_VERSION was introduced
283+
if (indexCreatedOnMigrationVersion > 1) {
284+
return indexCreatedOnMigrationVersion.equals(SecurityMigrations.MIGRATIONS_BY_VERSION.lastKey());
285+
}
286+
return SETTING_INDEX_VERSION_CREATED.get(indexMetadata.getSettings()).onOrAfter(IndexVersion.current());
278287
}
279288

280289
/**

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/SecurityMigrations.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
import static org.elasticsearch.xpack.security.support.SecuritySystemIndices.SecurityMainIndexMappingVersion.ADD_MANAGE_ROLES_PRIVILEGE;
4646
import static org.elasticsearch.xpack.security.support.SecuritySystemIndices.SecurityMainIndexMappingVersion.ADD_REMOTE_CLUSTER_AND_DESCRIPTION_FIELDS;
4747

48+
/**
49+
* Interface for creating SecurityMigrations that will be automatically applied once to existing .security indices
50+
*/
4851
public class SecurityMigrations {
4952

5053
/**

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/SecuritySystemIndices.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.cluster.routing.allocation.DataTier;
1717
import org.elasticsearch.cluster.service.ClusterService;
1818
import org.elasticsearch.common.VersionId;
19+
import org.elasticsearch.common.settings.Setting;
1920
import org.elasticsearch.common.settings.Settings;
2021
import org.elasticsearch.features.FeatureService;
2122
import org.elasticsearch.features.NodeFeature;
@@ -63,6 +64,16 @@ public class SecuritySystemIndices {
6364
public static final NodeFeature SECURITY_ROLES_METADATA_FLATTENED = new NodeFeature("security.roles_metadata_flattened");
6465
public static final NodeFeature SECURITY_ROLE_MAPPING_CLEANUP = new NodeFeature("security.role_mapping_cleanup");
6566

67+
/**
68+
* The latest version of the security migration when the main security index was created
69+
*/
70+
public static final Setting<Integer> MAIN_INDEX_CREATED_ON_MIGRATION_VERSION = Setting.intSetting(
71+
"index.security.migration.version",
72+
0,
73+
Setting.Property.IndexScope,
74+
Setting.Property.Final
75+
);
76+
6677
/**
6778
* Security managed index mappings used to be updated based on the product version. They are now updated based on per-index mappings
6879
* versions. However, older nodes will still look for a product version in the mappings metadata, so we have to put <em>something</em>
@@ -159,6 +170,7 @@ private static Settings getMainIndexSettings() {
159170
.put(DataTier.TIER_PREFERENCE, "data_hot,data_content")
160171
.put(IndexMetadata.SETTING_PRIORITY, 1000)
161172
.put(IndexMetadata.INDEX_FORMAT_SETTING.getKey(), INTERNAL_MAIN_INDEX_FORMAT)
173+
.put(MAIN_INDEX_CREATED_ON_MIGRATION_VERSION.getKey(), SecurityMigrations.MIGRATIONS_BY_VERSION.lastKey())
162174
.put("analysis.filter.email.type", "pattern_capture")
163175
.put("analysis.filter.email.preserve_original", true)
164176
.putList("analysis.filter.email.patterns", List.of("([^@]+)", "(\\p{L}+)", "(\\d+)", "@(.+)"))

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,10 @@ private Collection<Object> createComponentsUtil(Settings settings) throws Except
209209
settings = Security.additionalSettings(settings, true);
210210
Set<Setting<?>> allowedSettings = new HashSet<>(Security.getSettings(null));
211211
allowedSettings.addAll(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
212-
ClusterSettings clusterSettings = new ClusterSettings(settings, allowedSettings);
212+
ClusterSettings clusterSettings = new ClusterSettings(
213+
settings,
214+
allowedSettings.stream().filter(Setting::hasNodeScope).collect(Collectors.toSet())
215+
);
213216
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
214217
when(threadPool.relativeTimeInMillis()).thenReturn(1L);
215218
threadContext = new ThreadContext(Settings.EMPTY);

0 commit comments

Comments
 (0)