Check lenient_expand_open after aliases have been resolved
We fail today with ClusterBlockExceptions if an alias expands to a closed index during search since we miss to check the index option down the road after we expanded aliases. Closes #13278
This commit is contained in:
parent
39bc001e33
commit
d32d16f117
|
@ -237,7 +237,7 @@ public class IndexNameExpressionResolver extends AbstractComponent {
|
||||||
public String[] filteringAliases(ClusterState state, String index, String... expressions) {
|
public String[] filteringAliases(ClusterState state, String index, String... expressions) {
|
||||||
// expand the aliases wildcard
|
// expand the aliases wildcard
|
||||||
List<String> resolvedExpressions = expressions != null ? Arrays.asList(expressions) : Collections.<String>emptyList();
|
List<String> resolvedExpressions = expressions != null ? Arrays.asList(expressions) : Collections.<String>emptyList();
|
||||||
Context context = new Context(state, IndicesOptions.lenientExpandOpen());
|
Context context = new Context(state, IndicesOptions.lenientExpandOpen(), true);
|
||||||
for (ExpressionResolver expressionResolver : expressionResolvers) {
|
for (ExpressionResolver expressionResolver : expressionResolvers) {
|
||||||
resolvedExpressions = expressionResolver.resolve(context, resolvedExpressions);
|
resolvedExpressions = expressionResolver.resolve(context, resolvedExpressions);
|
||||||
}
|
}
|
||||||
|
@ -459,17 +459,25 @@ public class IndexNameExpressionResolver extends AbstractComponent {
|
||||||
private final ClusterState state;
|
private final ClusterState state;
|
||||||
private final IndicesOptions options;
|
private final IndicesOptions options;
|
||||||
private final long startTime;
|
private final long startTime;
|
||||||
|
private final boolean preserveAliases;
|
||||||
|
|
||||||
Context(ClusterState state, IndicesOptions options) {
|
Context(ClusterState state, IndicesOptions options) {
|
||||||
this.state = state;
|
this(state, options, System.currentTimeMillis());
|
||||||
this.options = options;
|
}
|
||||||
startTime = System.currentTimeMillis();
|
|
||||||
|
Context(ClusterState state, IndicesOptions options, boolean preserveAliases) {
|
||||||
|
this(state, options, System.currentTimeMillis(), preserveAliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context(ClusterState state, IndicesOptions options, long startTime) {
|
public Context(ClusterState state, IndicesOptions options, long startTime) {
|
||||||
|
this(state, options, startTime, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context(ClusterState state, IndicesOptions options, long startTime, boolean preserveAliases) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.startTime = startTime;
|
this.startTime = startTime;
|
||||||
|
this.preserveAliases = preserveAliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterState getState() {
|
public ClusterState getState() {
|
||||||
|
@ -483,6 +491,15 @@ public class IndexNameExpressionResolver extends AbstractComponent {
|
||||||
public long getStartTime() {
|
public long getStartTime() {
|
||||||
return startTime;
|
return startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to prevent resolving aliases to concrete indices but this also means
|
||||||
|
* that we might return aliases that point to a closed index. This is currently only used
|
||||||
|
* by {@link #filteringAliases(ClusterState, String, String...)} since it's the only one that needs aliases
|
||||||
|
*/
|
||||||
|
boolean isPreserveAliases() {
|
||||||
|
return preserveAliases;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ExpressionResolver {
|
private interface ExpressionResolver {
|
||||||
|
@ -531,6 +548,9 @@ public class IndexNameExpressionResolver extends AbstractComponent {
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (Strings.isEmpty(expression)) {
|
||||||
|
throw new IndexNotFoundException(expression);
|
||||||
|
}
|
||||||
boolean add = true;
|
boolean add = true;
|
||||||
if (expression.charAt(0) == '+') {
|
if (expression.charAt(0) == '+') {
|
||||||
// if its the first, add empty result set
|
// if its the first, add empty result set
|
||||||
|
@ -612,20 +632,23 @@ public class IndexNameExpressionResolver extends AbstractComponent {
|
||||||
.filter(e -> Regex.simpleMatch(pattern, e.getKey()))
|
.filter(e -> Regex.simpleMatch(pattern, e.getKey()))
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
}
|
}
|
||||||
|
Set<String> expand = new HashSet<>();
|
||||||
for (Map.Entry<String, AliasOrIndex> entry : matches.entrySet()) {
|
for (Map.Entry<String, AliasOrIndex> entry : matches.entrySet()) {
|
||||||
AliasOrIndex aliasOrIndex = entry.getValue();
|
AliasOrIndex aliasOrIndex = entry.getValue();
|
||||||
if (aliasOrIndex.isAlias() == false) {
|
if (context.isPreserveAliases() && aliasOrIndex.isAlias()) {
|
||||||
AliasOrIndex.Index index = (AliasOrIndex.Index) aliasOrIndex;
|
expand.add(entry.getKey());
|
||||||
if (excludeState != null && index.getIndex().getState() == excludeState) {
|
} else {
|
||||||
continue;
|
for (IndexMetaData meta : aliasOrIndex.getIndices()) {
|
||||||
|
if (excludeState == null || meta.getState() != excludeState) {
|
||||||
|
expand.add(meta.getIndex());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (add) {
|
if (add) {
|
||||||
result.add(entry.getKey());
|
result.addAll(expand);
|
||||||
} else {
|
} else {
|
||||||
result.remove(entry.getKey());
|
result.removeAll(expand);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches.isEmpty() && options.allowNoIndices() == false) {
|
if (matches.isEmpty() && options.allowNoIndices() == false) {
|
||||||
|
|
|
@ -192,7 +192,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
|
||||||
|
|
||||||
context = new IndexNameExpressionResolver.Context(state, lenientExpand);
|
context = new IndexNameExpressionResolver.Context(state, lenientExpand);
|
||||||
results = indexNameExpressionResolver.concreteIndices(context, Strings.EMPTY_ARRAY);
|
results = indexNameExpressionResolver.concreteIndices(context, Strings.EMPTY_ARRAY);
|
||||||
assertEquals(4, results.length);
|
assertEquals(Arrays.toString(results), 4, results.length);
|
||||||
|
|
||||||
context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen());
|
context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen());
|
||||||
results = indexNameExpressionResolver.concreteIndices(context, "foofoo*");
|
results = indexNameExpressionResolver.concreteIndices(context, "foofoo*");
|
||||||
|
@ -867,4 +867,37 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
return mdBuilder.build();
|
return mdBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testFilterClosedIndicesOnAliases() {
|
||||||
|
MetaData.Builder mdBuilder = MetaData.builder()
|
||||||
|
.put(indexBuilder("test-0").state(State.OPEN).putAlias(AliasMetaData.builder("alias-0")))
|
||||||
|
.put(indexBuilder("test-1").state(IndexMetaData.State.CLOSE).putAlias(AliasMetaData.builder("alias-1")));
|
||||||
|
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
|
||||||
|
|
||||||
|
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen());
|
||||||
|
String[] strings = indexNameExpressionResolver.concreteIndices(context, "alias-*");
|
||||||
|
assertArrayEquals(new String[] {"test-0"}, strings);
|
||||||
|
|
||||||
|
context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen());
|
||||||
|
strings = indexNameExpressionResolver.concreteIndices(context, "alias-*");
|
||||||
|
|
||||||
|
assertArrayEquals(new String[] {"test-0"}, strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFilteringAliases() {
|
||||||
|
MetaData.Builder mdBuilder = MetaData.builder()
|
||||||
|
.put(indexBuilder("test-0").state(State.OPEN).putAlias(AliasMetaData.builder("alias-0").filter("{ \"term\": \"foo\"}")))
|
||||||
|
.put(indexBuilder("test-1").state(State.OPEN).putAlias(AliasMetaData.builder("alias-1")));
|
||||||
|
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
|
||||||
|
|
||||||
|
String[] strings = indexNameExpressionResolver.filteringAliases(state, "test-0", "alias-*");
|
||||||
|
assertArrayEquals(new String[] {"alias-0"}, strings);
|
||||||
|
|
||||||
|
// concrete index supersedes filtering alias
|
||||||
|
strings = indexNameExpressionResolver.filteringAliases(state, "test-0", "test-0,alias-*");
|
||||||
|
assertNull(strings);
|
||||||
|
|
||||||
|
strings = indexNameExpressionResolver.filteringAliases(state, "test-0", "test-*,alias-*");
|
||||||
|
assertNull(strings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
|
||||||
IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver();
|
IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver();
|
||||||
|
|
||||||
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen());
|
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen());
|
||||||
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testYY*", "alias*"))), equalTo(newHashSet("alias1", "alias2", "alias3", "testYYY")));
|
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testYY*", "alias*"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
|
||||||
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("-kuku"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
|
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("-kuku"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
|
||||||
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+test*", "-testYYY"))), equalTo(newHashSet("testXXX", "testXYY")));
|
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+test*", "-testYYY"))), equalTo(newHashSet("testXXX", "testXYY")));
|
||||||
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+testX*", "+testYYY"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
|
assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+testX*", "+testYYY"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.routing;
|
package org.elasticsearch.routing;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.common.Priority;
|
import org.elasticsearch.common.Priority;
|
||||||
|
@ -27,9 +28,12 @@ import org.elasticsearch.test.ESIntegTestCase;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static org.elasticsearch.cluster.metadata.AliasAction.newAddAliasAction;
|
import static org.elasticsearch.cluster.metadata.AliasAction.newAddAliasAction;
|
||||||
import static org.elasticsearch.common.util.set.Sets.newHashSet;
|
import static org.elasticsearch.common.util.set.Sets.newHashSet;
|
||||||
|
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
|
||||||
|
@ -37,6 +41,22 @@ import static org.hamcrest.Matchers.nullValue;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class AliasResolveRoutingIT extends ESIntegTestCase {
|
public class AliasResolveRoutingIT extends ESIntegTestCase {
|
||||||
|
|
||||||
|
|
||||||
|
// see https://github.com/elastic/elasticsearch/issues/13278
|
||||||
|
public void testSearchClosedWildcardIndex() throws ExecutionException, InterruptedException {
|
||||||
|
createIndex("test-0");
|
||||||
|
createIndex("test-1");
|
||||||
|
ensureGreen();
|
||||||
|
client().admin().indices().prepareAliases().addAlias("test-0", "alias-0").addAlias("test-1", "alias-1").get();
|
||||||
|
client().admin().indices().prepareClose("test-1").get();
|
||||||
|
indexRandom(true, client().prepareIndex("test-0", "type1", "1").setSource("field1", "the quick brown fox jumps"),
|
||||||
|
client().prepareIndex("test-0", "type1", "2").setSource("field1", "quick brown"),
|
||||||
|
client().prepareIndex("test-0", "type1", "3").setSource("field1", "quick"));
|
||||||
|
refresh("test-*");
|
||||||
|
assertHitCount(client().prepareSearch().setIndices("alias-*").setIndicesOptions(IndicesOptions.lenientExpandOpen()).setQuery(matchQuery("_all", "quick")).get(), 3l);
|
||||||
|
}
|
||||||
|
|
||||||
public void testResolveIndexRouting() throws Exception {
|
public void testResolveIndexRouting() throws Exception {
|
||||||
createIndex("test1");
|
createIndex("test1");
|
||||||
createIndex("test2");
|
createIndex("test2");
|
||||||
|
|
Loading…
Reference in New Issue