Detect when security index is closed (#42740)

If the security index is closed, it should be treated as unavailable
for security purposes.

Prior to 8.0 (or in a mixed cluster) a closed security index has
no routing data, which would cause a NPE in the cluster change
handler, and the index state would not be updated correctly.
This commit fixes that problem

Backport of: #42191
This commit is contained in:
Tim Vernum 2019-06-04 14:25:20 +10:00 committed by GitHub
parent 87cc6a974c
commit 9035e61825
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 61 deletions

View File

@ -45,8 +45,10 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.indices.IndexClosedException;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.core.template.TemplateUtils; import org.elasticsearch.xpack.core.template.TemplateUtils;
@ -173,7 +175,9 @@ public class SecurityIndexManager implements ClusterStateListener {
throw new IllegalStateException("caller must make sure to use a frozen state and check indexAvailable"); throw new IllegalStateException("caller must make sure to use a frozen state and check indexAvailable");
} }
if (localState.indexExists()) { if (localState.indexState == IndexMetaData.State.CLOSE) {
return new IndexClosedException(new Index(localState.concreteIndexName, ClusterState.UNKNOWN_UUID));
} else if (localState.indexExists()) {
return new UnavailableShardsException(null, return new UnavailableShardsException(null,
"at least one primary shard for the index [" + localState.concreteIndexName + "] is unavailable"); "at least one primary shard for the index [" + localState.concreteIndexName + "] is unavailable");
} else { } else {
@ -206,11 +210,24 @@ public class SecurityIndexManager implements ClusterStateListener {
final boolean indexAvailable = checkIndexAvailable(event.state()); final boolean indexAvailable = checkIndexAvailable(event.state());
final boolean mappingIsUpToDate = indexMetaData == null || checkIndexMappingUpToDate(event.state()); final boolean mappingIsUpToDate = indexMetaData == null || checkIndexMappingUpToDate(event.state());
final Version mappingVersion = oldestIndexMappingVersion(event.state()); final Version mappingVersion = oldestIndexMappingVersion(event.state());
final ClusterHealthStatus indexStatus = indexMetaData == null ? null :
new ClusterIndexHealth(indexMetaData, event.state().getRoutingTable().index(indexMetaData.getIndex())).getStatus();
final String concreteIndexName = indexMetaData == null ? internalIndexName : indexMetaData.getIndex().getName(); final String concreteIndexName = indexMetaData == null ? internalIndexName : indexMetaData.getIndex().getName();
final ClusterHealthStatus indexHealth;
final IndexMetaData.State indexState;
if (indexMetaData == null) {
// Index does not exist
indexState = null;
indexHealth = null;
} else if (indexMetaData.getState() == IndexMetaData.State.CLOSE) {
indexState = IndexMetaData.State.CLOSE;
indexHealth = null;
logger.warn("Index [{}] is closed. This is likely to prevent security from functioning correctly", concreteIndexName);
} else {
indexState = IndexMetaData.State.OPEN;
final IndexRoutingTable routingTable = event.state().getRoutingTable().index(indexMetaData.getIndex());
indexHealth = new ClusterIndexHealth(indexMetaData, routingTable).getStatus();
}
final State newState = new State(creationTime, isIndexUpToDate, indexAvailable, mappingIsUpToDate, mappingVersion, final State newState = new State(creationTime, isIndexUpToDate, indexAvailable, mappingIsUpToDate, mappingVersion,
concreteIndexName, indexStatus); concreteIndexName, indexHealth, indexState);
this.indexState = newState; this.indexState = newState;
if (newState.equals(previousState) == false) { if (newState.equals(previousState) == false) {
@ -221,23 +238,21 @@ public class SecurityIndexManager implements ClusterStateListener {
} }
private boolean checkIndexAvailable(ClusterState state) { private boolean checkIndexAvailable(ClusterState state) {
final IndexRoutingTable routingTable = getIndexRoutingTable(state); IndexMetaData metaData = resolveConcreteIndex(aliasName, state.metaData());
if (routingTable != null && routingTable.allPrimaryShardsActive()) { if (metaData == null) {
return true; logger.debug("Index [{}] is not available - no metadata", aliasName);
}
logger.debug("Index [{}] is not yet active", aliasName);
return false; return false;
} }
if (metaData.getState() == IndexMetaData.State.CLOSE) {
/** logger.warn("Index [{}] is closed", aliasName);
* Returns the routing-table for this index, or <code>null</code> if the index does not exist. return false;
*/ }
private IndexRoutingTable getIndexRoutingTable(ClusterState clusterState) { final IndexRoutingTable routingTable = state.routingTable().index(metaData.getIndex());
IndexMetaData metaData = resolveConcreteIndex(aliasName, clusterState.metaData()); if (routingTable == null || routingTable.allPrimaryShardsActive() == false) {
if (metaData == null) { logger.debug("Index [{}] is not yet active", aliasName);
return null; return false;
} else { } else {
return clusterState.routingTable().index(metaData.getIndex()); return true;
} }
} }
@ -402,15 +417,15 @@ public class SecurityIndexManager implements ClusterStateListener {
* Return true if the state moves from an unhealthy ("RED") index state to a healthy ("non-RED") state. * Return true if the state moves from an unhealthy ("RED") index state to a healthy ("non-RED") state.
*/ */
public static boolean isMoveFromRedToNonRed(State previousState, State currentState) { public static boolean isMoveFromRedToNonRed(State previousState, State currentState) {
return (previousState.indexStatus == null || previousState.indexStatus == ClusterHealthStatus.RED) return (previousState.indexHealth == null || previousState.indexHealth == ClusterHealthStatus.RED)
&& currentState.indexStatus != null && currentState.indexStatus != ClusterHealthStatus.RED; && currentState.indexHealth != null && currentState.indexHealth != ClusterHealthStatus.RED;
} }
/** /**
* Return true if the state moves from the index existing to the index not existing. * Return true if the state moves from the index existing to the index not existing.
*/ */
public static boolean isIndexDeleted(State previousState, State currentState) { public static boolean isIndexDeleted(State previousState, State currentState) {
return previousState.indexStatus != null && currentState.indexStatus == null; return previousState.indexHealth != null && currentState.indexHealth == null;
} }
private static byte[] readTemplateAsBytes(String templateName) { private static byte[] readTemplateAsBytes(String templateName) {
@ -440,24 +455,27 @@ public class SecurityIndexManager implements ClusterStateListener {
* State of the security index. * State of the security index.
*/ */
public static class State { public static class State {
public static final State UNRECOVERED_STATE = new State(null, false, false, false, null, null, null); public static final State UNRECOVERED_STATE = new State(null, false, false, false, null, null, null, null);
public final Instant creationTime; public final Instant creationTime;
public final boolean isIndexUpToDate; public final boolean isIndexUpToDate;
public final boolean indexAvailable; public final boolean indexAvailable;
public final boolean mappingUpToDate; public final boolean mappingUpToDate;
public final Version mappingVersion; public final Version mappingVersion;
public final String concreteIndexName; public final String concreteIndexName;
public final ClusterHealthStatus indexStatus; public final ClusterHealthStatus indexHealth;
public final IndexMetaData.State indexState;
public State(Instant creationTime, boolean isIndexUpToDate, boolean indexAvailable, public State(Instant creationTime, boolean isIndexUpToDate, boolean indexAvailable,
boolean mappingUpToDate, Version mappingVersion, String concreteIndexName, ClusterHealthStatus indexStatus) { boolean mappingUpToDate, Version mappingVersion, String concreteIndexName, ClusterHealthStatus indexHealth,
IndexMetaData.State indexState) {
this.creationTime = creationTime; this.creationTime = creationTime;
this.isIndexUpToDate = isIndexUpToDate; this.isIndexUpToDate = isIndexUpToDate;
this.indexAvailable = indexAvailable; this.indexAvailable = indexAvailable;
this.mappingUpToDate = mappingUpToDate; this.mappingUpToDate = mappingUpToDate;
this.mappingVersion = mappingVersion; this.mappingVersion = mappingVersion;
this.concreteIndexName = concreteIndexName; this.concreteIndexName = concreteIndexName;
this.indexStatus = indexStatus; this.indexHealth = indexHealth;
this.indexState = indexState;
} }
@Override @Override
@ -471,7 +489,8 @@ public class SecurityIndexManager implements ClusterStateListener {
mappingUpToDate == state.mappingUpToDate && mappingUpToDate == state.mappingUpToDate &&
Objects.equals(mappingVersion, state.mappingVersion) && Objects.equals(mappingVersion, state.mappingVersion) &&
Objects.equals(concreteIndexName, state.concreteIndexName) && Objects.equals(concreteIndexName, state.concreteIndexName) &&
indexStatus == state.indexStatus; indexHealth == state.indexHealth &&
indexState == state.indexState;
} }
public boolean indexExists() { public boolean indexExists() {
@ -481,7 +500,7 @@ public class SecurityIndexManager implements ClusterStateListener {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(creationTime, isIndexUpToDate, indexAvailable, mappingUpToDate, mappingVersion, concreteIndexName, return Objects.hash(creationTime, isIndexUpToDate, indexAvailable, mappingUpToDate, mappingVersion, concreteIndexName,
indexStatus); indexHealth);
} }
} }
} }

View File

@ -25,6 +25,7 @@ import org.elasticsearch.action.update.UpdateAction;
import org.elasticsearch.action.update.UpdateRequestBuilder; import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.UUIDs;
@ -364,7 +365,7 @@ public class AuthenticationServiceTests extends ESTestCase {
// green to yellow or yellow to green // green to yellow or yellow to green
previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW)); previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW));
currentState = dummyState(previousState.indexStatus == ClusterHealthStatus.GREEN ? currentState = dummyState(previousState.indexHealth == ClusterHealthStatus.GREEN ?
ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN); ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN);
service.onSecurityIndexStateChange(previousState, currentState); service.onSecurityIndexStateChange(previousState, currentState);
assertEquals(expectedInvalidation, service.getNumInvalidation()); assertEquals(expectedInvalidation, service.getNumInvalidation());
@ -1402,6 +1403,7 @@ public class AuthenticationServiceTests extends ESTestCase {
} }
private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) { private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) {
return new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, indexStatus); return new SecurityIndexManager.State(
Instant.now(), true, true, true, null, concreteSecurityIndexName, indexStatus, IndexMetaData.State.OPEN);
} }
} }

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.security.authc.esnative; package org.elasticsearch.xpack.security.authc.esnative;
import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.env.TestEnvironment;
@ -27,7 +28,8 @@ public class NativeRealmTests extends ESTestCase {
RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6, RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7); RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6, RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7);
private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) { private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) {
return new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, indexStatus); return new SecurityIndexManager.State(
Instant.now(), true, true, true, null, concreteSecurityIndexName, indexStatus, IndexMetaData.State.OPEN);
} }
public void testCacheClearOnIndexHealthChange() { public void testCacheClearOnIndexHealthChange() {
@ -72,7 +74,7 @@ public class NativeRealmTests extends ESTestCase {
// green to yellow or yellow to green // green to yellow or yellow to green
previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW)); previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW));
currentState = dummyState(previousState.indexStatus == ClusterHealthStatus.GREEN ? currentState = dummyState(previousState.indexHealth == ClusterHealthStatus.GREEN ?
ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN); ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN);
nativeRealm.onSecurityIndexStateChange(previousState, currentState); nativeRealm.onSecurityIndexStateChange(previousState, currentState);
assertEquals(expectedInvalidation, numInvalidation.get()); assertEquals(expectedInvalidation, numInvalidation.get());

View File

@ -10,6 +10,7 @@ import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.ThreadContext;
@ -138,7 +139,12 @@ public class NativeRoleMappingStoreTests extends ESTestCase {
} }
private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) { private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) {
return new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, indexStatus); return indexState(true, indexStatus);
}
private SecurityIndexManager.State indexState(boolean isUpToDate, ClusterHealthStatus healthStatus) {
return new SecurityIndexManager.State(
Instant.now(), isUpToDate, true, true, null, concreteSecurityIndexName, healthStatus, IndexMetaData.State.OPEN);
} }
public void testCacheClearOnIndexHealthChange() { public void testCacheClearOnIndexHealthChange() {
@ -172,7 +178,7 @@ public class NativeRoleMappingStoreTests extends ESTestCase {
// green to yellow or yellow to green // green to yellow or yellow to green
previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW)); previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW));
currentState = dummyState(previousState.indexStatus == ClusterHealthStatus.GREEN ? currentState = dummyState(previousState.indexHealth == ClusterHealthStatus.GREEN ?
ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN); ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN);
store.onSecurityIndexStateChange(previousState, currentState); store.onSecurityIndexStateChange(previousState, currentState);
assertEquals(expectedInvalidation, numInvalidation.get()); assertEquals(expectedInvalidation, numInvalidation.get());
@ -182,14 +188,10 @@ public class NativeRoleMappingStoreTests extends ESTestCase {
final AtomicInteger numInvalidation = new AtomicInteger(0); final AtomicInteger numInvalidation = new AtomicInteger(0);
final NativeRoleMappingStore store = buildRoleMappingStoreForInvalidationTesting(numInvalidation, true); final NativeRoleMappingStore store = buildRoleMappingStoreForInvalidationTesting(numInvalidation, true);
store.onSecurityIndexStateChange( store.onSecurityIndexStateChange(indexState(false, null), indexState(true, null));
new SecurityIndexManager.State(Instant.now(), false, true, true, null, concreteSecurityIndexName, null),
new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, null));
assertEquals(1, numInvalidation.get()); assertEquals(1, numInvalidation.get());
store.onSecurityIndexStateChange( store.onSecurityIndexStateChange(indexState(true, null), indexState(false, null));
new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, null),
new SecurityIndexManager.State(Instant.now(), false, true, true, null, concreteSecurityIndexName, null));
assertEquals(2, numInvalidation.get()); assertEquals(2, numInvalidation.get());
} }

View File

@ -763,7 +763,12 @@ public class CompositeRolesStoreTests extends ESTestCase {
} }
private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) { private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) {
return new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, indexStatus); return dummyIndexState(true, indexStatus);
}
public SecurityIndexManager.State dummyIndexState(boolean isIndexUpToDate, ClusterHealthStatus healthStatus) {
return new SecurityIndexManager.State(
Instant.now(), isIndexUpToDate, true, true, null, concreteSecurityIndexName, healthStatus, IndexMetaData.State.OPEN);
} }
public void testCacheClearOnIndexHealthChange() { public void testCacheClearOnIndexHealthChange() {
@ -812,7 +817,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
// green to yellow or yellow to green // green to yellow or yellow to green
previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW)); previousState = dummyState(randomFrom(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW));
currentState = dummyState(previousState.indexStatus == ClusterHealthStatus.GREEN ? currentState = dummyState(previousState.indexHealth == ClusterHealthStatus.GREEN ?
ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN); ClusterHealthStatus.YELLOW : ClusterHealthStatus.GREEN);
compositeRolesStore.onSecurityIndexStateChange(previousState, currentState); compositeRolesStore.onSecurityIndexStateChange(previousState, currentState);
assertEquals(expectedInvalidation, numInvalidation.get()); assertEquals(expectedInvalidation, numInvalidation.get());
@ -837,14 +842,10 @@ public class CompositeRolesStoreTests extends ESTestCase {
} }
}; };
compositeRolesStore.onSecurityIndexStateChange( compositeRolesStore.onSecurityIndexStateChange(dummyIndexState(false, null), dummyIndexState(true, null));
new SecurityIndexManager.State(Instant.now(), false, true, true, null, concreteSecurityIndexName, null),
new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, null));
assertEquals(1, numInvalidation.get()); assertEquals(1, numInvalidation.get());
compositeRolesStore.onSecurityIndexStateChange( compositeRolesStore.onSecurityIndexStateChange(dummyIndexState(true, null), dummyIndexState(false, null));
new SecurityIndexManager.State(Instant.now(), true, true, true, null, concreteSecurityIndexName, null),
new SecurityIndexManager.State(Instant.now(), false, true, true, null, concreteSecurityIndexName, null));
assertEquals(2, numInvalidation.get()); assertEquals(2, numInvalidation.get());
} }

View File

@ -155,8 +155,8 @@ public class SecurityIndexManagerTests extends ESTestCase {
manager.clusterChanged(event(clusterStateBuilder)); manager.clusterChanged(event(clusterStateBuilder));
assertTrue(listenerCalled.get()); assertTrue(listenerCalled.get());
assertNull(previousState.get().indexStatus); assertNull(previousState.get().indexHealth);
assertEquals(ClusterHealthStatus.GREEN, currentState.get().indexStatus); assertEquals(ClusterHealthStatus.GREEN, currentState.get().indexHealth);
// reset and call with no change to the index // reset and call with no change to the index
listenerCalled.set(false); listenerCalled.set(false);
@ -191,8 +191,8 @@ public class SecurityIndexManagerTests extends ESTestCase {
event = new ClusterChangedEvent("different index health", clusterStateBuilder.build(), previousClusterState); event = new ClusterChangedEvent("different index health", clusterStateBuilder.build(), previousClusterState);
manager.clusterChanged(event); manager.clusterChanged(event);
assertTrue(listenerCalled.get()); assertTrue(listenerCalled.get());
assertEquals(ClusterHealthStatus.GREEN, previousState.get().indexStatus); assertEquals(ClusterHealthStatus.GREEN, previousState.get().indexHealth);
assertEquals(ClusterHealthStatus.RED, currentState.get().indexStatus); assertEquals(ClusterHealthStatus.RED, currentState.get().indexHealth);
// swap prev and current // swap prev and current
listenerCalled.set(false); listenerCalled.set(false);
@ -201,8 +201,8 @@ public class SecurityIndexManagerTests extends ESTestCase {
event = new ClusterChangedEvent("different index health swapped", previousClusterState, clusterStateBuilder.build()); event = new ClusterChangedEvent("different index health swapped", previousClusterState, clusterStateBuilder.build());
manager.clusterChanged(event); manager.clusterChanged(event);
assertTrue(listenerCalled.get()); assertTrue(listenerCalled.get());
assertEquals(ClusterHealthStatus.RED, previousState.get().indexStatus); assertEquals(ClusterHealthStatus.RED, previousState.get().indexHealth);
assertEquals(ClusterHealthStatus.GREEN, currentState.get().indexStatus); assertEquals(ClusterHealthStatus.GREEN, currentState.get().indexHealth);
} }
public void testWriteBeforeStateNotRecovered() throws Exception { public void testWriteBeforeStateNotRecovered() throws Exception {
@ -247,7 +247,7 @@ public class SecurityIndexManagerTests extends ESTestCase {
assertThat(prepareRunnableCalled.get(), is(true)); assertThat(prepareRunnableCalled.get(), is(true));
} }
public void testListeneredNotCalledBeforeStateNotRecovered() throws Exception { public void testListenerNotCalledBeforeStateNotRecovered() throws Exception {
final AtomicBoolean listenerCalled = new AtomicBoolean(false); final AtomicBoolean listenerCalled = new AtomicBoolean(false);
manager.addIndexStateListener((prev, current) -> { manager.addIndexStateListener((prev, current) -> {
listenerCalled.set(true); listenerCalled.set(true);
@ -307,6 +307,31 @@ public class SecurityIndexManagerTests extends ESTestCase {
assertTrue(manager.isIndexUpToDate()); assertTrue(manager.isIndexUpToDate());
} }
public void testProcessClosedIndexState() throws Exception {
// Index initially exists
final ClusterState.Builder indexAvailable = createClusterState(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7,
RestrictedIndicesNames.SECURITY_MAIN_ALIAS, TEMPLATE_NAME, IndexMetaData.State.OPEN);
markShardsAvailable(indexAvailable);
manager.clusterChanged(event(indexAvailable));
assertThat(manager.indexExists(), is(true));
assertThat(manager.isAvailable(), is(true));
// Now close it
final ClusterState.Builder indexClosed = createClusterState(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7,
RestrictedIndicesNames.SECURITY_MAIN_ALIAS, TEMPLATE_NAME, IndexMetaData.State.CLOSE);
if (randomBoolean()) {
// In old/mixed cluster versions closed indices have no routing table
indexClosed.routingTable(RoutingTable.EMPTY_ROUTING_TABLE);
} else {
markShardsAvailable(indexClosed);
}
manager.clusterChanged(event(indexClosed));
assertThat(manager.indexExists(), is(true));
assertThat(manager.isAvailable(), is(false));
}
private void assertInitialState() { private void assertInitialState() {
assertThat(manager.indexExists(), Matchers.equalTo(false)); assertThat(manager.indexExists(), Matchers.equalTo(false));
assertThat(manager.isAvailable(), Matchers.equalTo(false)); assertThat(manager.isAvailable(), Matchers.equalTo(false));
@ -322,18 +347,23 @@ public class SecurityIndexManagerTests extends ESTestCase {
} }
public static ClusterState.Builder createClusterState(String indexName, String aliasName, String templateName) throws IOException { public static ClusterState.Builder createClusterState(String indexName, String aliasName, String templateName) throws IOException {
return createClusterState(indexName, aliasName, templateName, templateName, SecurityIndexManager.INTERNAL_MAIN_INDEX_FORMAT); return createClusterState(indexName, aliasName, templateName, IndexMetaData.State.OPEN);
}
public static ClusterState.Builder createClusterState(String indexName, String aliasName, String templateName,
IndexMetaData.State state) throws IOException {
return createClusterState(indexName, aliasName, templateName, templateName, SecurityIndexManager.INTERNAL_MAIN_INDEX_FORMAT, state);
} }
public static ClusterState.Builder createClusterState(String indexName, String aliasName, String templateName, int format) public static ClusterState.Builder createClusterState(String indexName, String aliasName, String templateName, int format)
throws IOException { throws IOException {
return createClusterState(indexName, aliasName, templateName, templateName, format); return createClusterState(indexName, aliasName, templateName, templateName, format, IndexMetaData.State.OPEN);
} }
private static ClusterState.Builder createClusterState(String indexName, String aliasName, String templateName, String buildMappingFrom, private static ClusterState.Builder createClusterState(String indexName, String aliasName, String templateName, String buildMappingFrom,
int format) throws IOException { int format, IndexMetaData.State state) throws IOException {
IndexTemplateMetaData.Builder templateBuilder = getIndexTemplateMetaData(templateName); IndexTemplateMetaData.Builder templateBuilder = getIndexTemplateMetaData(templateName);
IndexMetaData.Builder indexMeta = getIndexMetadata(indexName, aliasName, buildMappingFrom, format); IndexMetaData.Builder indexMeta = getIndexMetadata(indexName, aliasName, buildMappingFrom, format, state);
MetaData.Builder metaDataBuilder = new MetaData.Builder(); MetaData.Builder metaDataBuilder = new MetaData.Builder();
metaDataBuilder.put(templateBuilder); metaDataBuilder.put(templateBuilder);
@ -354,7 +384,8 @@ public class SecurityIndexManagerTests extends ESTestCase {
.build(); .build();
} }
private static IndexMetaData.Builder getIndexMetadata(String indexName, String aliasName, String templateName, int format) private static IndexMetaData.Builder getIndexMetadata(String indexName, String aliasName, String templateName, int format,
IndexMetaData.State state)
throws IOException { throws IOException {
IndexMetaData.Builder indexMetaData = IndexMetaData.builder(indexName); IndexMetaData.Builder indexMetaData = IndexMetaData.builder(indexName);
indexMetaData.settings(Settings.builder() indexMetaData.settings(Settings.builder()
@ -364,6 +395,7 @@ public class SecurityIndexManagerTests extends ESTestCase {
.put(IndexMetaData.INDEX_FORMAT_SETTING.getKey(), format) .put(IndexMetaData.INDEX_FORMAT_SETTING.getKey(), format)
.build()); .build());
indexMetaData.putAlias(AliasMetaData.builder(aliasName).build()); indexMetaData.putAlias(AliasMetaData.builder(aliasName).build());
indexMetaData.state(state);
final Map<String, String> mappings = getTemplateMappings(templateName); final Map<String, String> mappings = getTemplateMappings(templateName);
for (Map.Entry<String, String> entry : mappings.entrySet()) { for (Map.Entry<String, String> entry : mappings.entrySet()) {
indexMetaData.putMapping(entry.getKey(), entry.getValue()); indexMetaData.putMapping(entry.getKey(), entry.getValue());