diff --git a/docs/changelog/106975.yaml b/docs/changelog/106975.yaml new file mode 100644 index 0000000000000..bd32b3574c4f9 --- /dev/null +++ b/docs/changelog/106975.yaml @@ -0,0 +1,5 @@ +pr: 106975 +summary: GET /_all should return hidden indices with visible aliases +area: Indices APIs +type: bug +issues: [] diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java index 0446b479b191d..f61cc73cfc753 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java @@ -1151,32 +1151,36 @@ private WildcardExpressionResolver() { } /** - * Returns all the indices and all the datastreams, considering the open/closed, system, and hidden context parameters. + * Returns all the indices, datastreams, and aliases, considering the open/closed, system, and hidden context parameters. * Depending on the context, returns the names of the datastreams themselves or their backing indices. */ public static Collection resolveAll(Context context) { - List resolvedExpressions = resolveEmptyOrTrivialWildcard(context); - if (context.includeDataStreams() == false) { - return resolvedExpressions; - } else { - Stream dataStreamsAbstractions = context.getState() - .metadata() - .getIndicesLookup() - .values() - .stream() - .filter(indexAbstraction -> indexAbstraction.getType() == Type.DATA_STREAM) - .filter( - indexAbstraction -> indexAbstraction.isSystem() == false - || context.systemIndexAccessPredicate.test(indexAbstraction.getName()) - ); - if (context.getOptions().expandWildcardsHidden() == false) { - dataStreamsAbstractions = dataStreamsAbstractions.filter(indexAbstraction -> indexAbstraction.isHidden() == false); - } - // dedup backing indices if expand hidden indices option is true - Set resolvedIncludingDataStreams = expandToOpenClosed(context, dataStreamsAbstractions).collect(Collectors.toSet()); - resolvedIncludingDataStreams.addAll(resolvedExpressions); - return resolvedIncludingDataStreams; + List concreteIndices = resolveEmptyOrTrivialWildcard(context); + + if (context.includeDataStreams() == false && context.getOptions().ignoreAliases()) { + return concreteIndices; } + + Stream ias = context.getState() + .metadata() + .getIndicesLookup() + .values() + .stream() + .filter(ia -> context.getOptions().expandWildcardsHidden() || ia.isHidden() == false) + .filter(ia -> shouldIncludeIfDataStream(ia, context) || shouldIncludeIfAlias(ia, context)) + .filter(ia -> ia.isSystem() == false || context.systemIndexAccessPredicate.test(ia.getName())); + + Set resolved = expandToOpenClosed(context, ias).collect(Collectors.toSet()); + resolved.addAll(concreteIndices); + return resolved; + } + + private static boolean shouldIncludeIfDataStream(IndexAbstraction ia, IndexNameExpressionResolver.Context context) { + return context.includeDataStreams() && ia.getType() == Type.DATA_STREAM; + } + + private static boolean shouldIncludeIfAlias(IndexAbstraction ia, IndexNameExpressionResolver.Context context) { + return context.getOptions().ignoreAliases() == false && ia.getType() == Type.ALIAS; } /** diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java index c043734d15cdf..39ff2ff67f903 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java @@ -1215,9 +1215,9 @@ public void testHiddenAliasAndHiddenIndexResolution() { indexNames = indexNameExpressionResolver.concreteIndexNames(state, includeHiddenOptions, visibleAlias); assertThat(Arrays.asList(indexNames), containsInAnyOrder(visibleIndex, hiddenIndex)); - // A total wildcards does not resolve the hidden index in this case + // total wildcards should also resolve both visible and hidden indices if there is a visible alias indexNames = indexNameExpressionResolver.concreteIndexNames(state, excludeHiddenOptions, "*"); - assertThat(Arrays.asList(indexNames), containsInAnyOrder(visibleIndex)); + assertThat(Arrays.asList(indexNames), containsInAnyOrder(visibleIndex, hiddenIndex)); } { diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java index 2406eb8e76ab9..745b006a911b7 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java @@ -280,6 +280,129 @@ public void testAll() { assertThat(IndexNameExpressionResolver.resolveExpressions(noExpandContext, "_all").size(), equalTo(0)); } + public void testAllAliases() { + { + // hidden index with hidden alias should not be returned + Metadata.Builder mdBuilder = Metadata.builder() + .put( + indexBuilder("index-hidden-alias", true) // index hidden + .state(State.OPEN) + .putAlias(AliasMetadata.builder("alias-hidden").isHidden(true)) // alias hidden + ); + + ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build(); + + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context( + state, + IndicesOptions.lenientExpandOpen(), // don't include hidden + SystemIndexAccessLevel.NONE + ); + assertThat(newHashSet(IndexNameExpressionResolver.WildcardExpressionResolver.resolveAll(context)), equalTo(newHashSet())); + } + + { + // hidden index with visible alias should be returned + Metadata.Builder mdBuilder = Metadata.builder() + .put( + indexBuilder("index-visible-alias", true) // index hidden + .state(State.OPEN) + .putAlias(AliasMetadata.builder("alias-visible").isHidden(false)) // alias visible + ); + + ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build(); + + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context( + state, + IndicesOptions.lenientExpandOpen(), // don't include hidden + SystemIndexAccessLevel.NONE + ); + assertThat( + newHashSet(IndexNameExpressionResolver.WildcardExpressionResolver.resolveAll(context)), + equalTo(newHashSet("index-visible-alias")) + ); + } + } + + public void testAllDataStreams() { + + String dataStreamName = "foo_logs"; + long epochMillis = randomLongBetween(1580536800000L, 1583042400000L); + IndexMetadata firstBackingIndexMetadata = createBackingIndex(dataStreamName, 1, epochMillis).build(); + + IndicesOptions indicesAndAliasesOptions = IndicesOptions.fromOptions( + randomBoolean(), + randomBoolean(), + true, + false, + true, + false, + false, + false + ); + + { + // visible data streams should be returned by _all even show backing indices are hidden + Metadata.Builder mdBuilder = Metadata.builder() + .put(firstBackingIndexMetadata, true) + .put(DataStreamTestHelper.newInstance(dataStreamName, List.of(firstBackingIndexMetadata.getIndex()))); + + ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build(); + + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context( + state, + indicesAndAliasesOptions, + false, + false, + true, + SystemIndexAccessLevel.NONE, + NONE, + NONE + ); + + assertThat( + newHashSet(IndexNameExpressionResolver.WildcardExpressionResolver.resolveAll(context)), + equalTo(newHashSet(DataStream.getDefaultBackingIndexName("foo_logs", 1, epochMillis))) + ); + } + + { + // if data stream itself is hidden, backing indices should not be returned + boolean hidden = true; + var dataStream = new DataStream( + dataStreamName, + List.of(firstBackingIndexMetadata.getIndex()), + 1, + null, + hidden, + false, + false, + false, + null, + null, + false, + List.of(), + false + ); + + Metadata.Builder mdBuilder = Metadata.builder().put(firstBackingIndexMetadata, true).put(dataStream); + + ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build(); + + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context( + state, + indicesAndAliasesOptions, + false, + false, + true, + SystemIndexAccessLevel.NONE, + NONE, + NONE + ); + + assertThat(newHashSet(IndexNameExpressionResolver.WildcardExpressionResolver.resolveAll(context)), equalTo(newHashSet())); + } + } + public void testResolveEmpty() { Metadata.Builder mdBuilder = Metadata.builder() .put(