diff --git a/docs/changelog/133616.yaml b/docs/changelog/133616.yaml new file mode 100644 index 0000000000000..0eddbc4f7e168 --- /dev/null +++ b/docs/changelog/133616.yaml @@ -0,0 +1,5 @@ +pr: 133616 +summary: Add mode filter to _resolve/index +area: Indices APIs +type: enhancement +issues: [] diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.resolve_index.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.resolve_index.json index 770aa8c5ec595..4e3f1879002a9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.resolve_index.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.resolve_index.json @@ -47,6 +47,10 @@ "type":"boolean", "description":"Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)", "default":true + }, + "mode": { + "type": "string", + "description": "Filter indices by index mode" } } } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.resolve_index/30_resolve_index_mode_filter.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.resolve_index/30_resolve_index_mode_filter.yml new file mode 100644 index 0000000000000..64ef050365981 --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.resolve_index/30_resolve_index_mode_filter.yml @@ -0,0 +1,108 @@ +--- +setup: + - do: + indices.create: + index: my-std-index + body: + settings: + number_of_shards: 1 + number_of_replicas: 0 + aliases: + my-std-alias: { } + + # Create a time-series index + - do: + indices.create: + index: my-ts-index + body: + settings: + index.mode: time_series + number_of_shards: 1 + number_of_replicas: 0 + index.routing_path: [ "host" ] + mappings: + properties: + "@timestamp": + type: date + host: + type: keyword + time_series_dimension: true + metric: + type: keyword + value: + type: double + aliases: + my-ts-alias: { } + # Create a lookup index + - do: + indices.create: + index: my-lookup-index + body: + settings: + index.mode: lookup + aliases: + my-lookup-alias: { } + +--- +"resolve index filters by mode": + - requires: + test_runner_features: [ capabilities ] + capabilities: + - method: GET + path: /_resolve/index/* + capabilities: [ mode_filter ] + reason: "Need support for mode filtering in indices.resolve_index" + + - do: + indices.resolve_index: + name: 'my-*' + mode: lookup + - length: { indices: 1 } + - match: { indices.0.name: "my-lookup-index" } + - match: { indices.0.mode: "lookup" } + - match: { indices.0.aliases: [ "my-lookup-alias" ] } + - length: { aliases: 1 } + - match: { aliases.0.name: "my-lookup-alias" } + - match: { aliases.0.indices: [ "my-lookup-index" ] } + +--- +"resolve index filters by mode, multiple modes": + - requires: + test_runner_features: [ capabilities ] + capabilities: + - method: GET + path: /_resolve/index/* + capabilities: [ mode_filter ] + reason: "Need support for mode filtering in indices.resolve_index" + + - do: + indices.resolve_index: + name: 'my-*' + mode: time_series,standard + - length: { indices: 2 } + - match: { indices.0.name: "my-std-index" } + - match: { indices.0.mode: "standard" } + - match: { indices.0.aliases: [ "my-std-alias" ] } + - match: { indices.1.name: "my-ts-index" } + - match: { indices.1.mode: "time_series" } + - match: { indices.1.aliases: [ "my-ts-alias" ] } + - length: { aliases: 2 } + - match: { aliases.0.name: "my-std-alias" } + - match: { aliases.0.indices: [ "my-std-index" ] } + - match: { aliases.1.name: "my-ts-alias" } + - match: { aliases.1.indices: [ "my-ts-index" ] } + +--- +teardown: + - do: + indices.delete: + index: my-std-index + ignore_unavailable: true + - do: + indices.delete: + index: my-ts-index + ignore_unavailable: true + - do: + indices.delete: + index: my-lookup-index + ignore_unavailable: true diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index e5a8012f45c38..7c5ae10bbedbc 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -355,6 +355,7 @@ static TransportVersion def(int id) { public static final TransportVersion ESQL_SAMPLE_OPERATOR_STATUS = def(9_127_0_00); public static final TransportVersion PROJECT_RESERVED_STATE_MOVE_TO_REGISTRY = def(9_147_0_00); public static final TransportVersion STREAMS_ENDPOINT_PARAM_RESTRICTIONS = def(9_148_0_00); + public static final TransportVersion RESOLVE_INDEX_MODE_FILTER = def(9_149_0_00); /* * STOP! READ THIS FIRST! No, really, diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexAction.java index 4d0864bb8caae..ec86ffd975940 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexAction.java @@ -10,6 +10,7 @@ package org.elasticsearch.action.admin.indices.resolve; import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; @@ -56,6 +57,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -63,6 +65,8 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.elasticsearch.action.search.TransportSearchHelper.checkCCSVersionCompatibility; @@ -85,6 +89,7 @@ public static class Request extends LegacyActionRequest implements IndicesReques private String[] names; private IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS; + private EnumSet indexModes = EnumSet.noneOf(IndexMode.class); public Request(String[] names) { this.names = names; @@ -95,6 +100,14 @@ public Request(String[] names, IndicesOptions indicesOptions) { this.indicesOptions = indicesOptions; } + public Request(String[] names, IndicesOptions indicesOptions, @Nullable EnumSet indexModes) { + this.names = names; + this.indicesOptions = indicesOptions; + if (indexModes != null) { + this.indexModes = indexModes; + } + } + @Override public ActionRequestValidationException validate() { return null; @@ -104,6 +117,11 @@ public Request(StreamInput in) throws IOException { super(in); this.names = in.readStringArray(); this.indicesOptions = IndicesOptions.readIndicesOptions(in); + if (in.getTransportVersion().onOrAfter(TransportVersions.RESOLVE_INDEX_MODE_FILTER)) { + this.indexModes = in.readEnumSet(IndexMode.class); + } else { + this.indexModes = EnumSet.noneOf(IndexMode.class); + } } @Override @@ -111,6 +129,9 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeStringArray(names); indicesOptions.writeIndicesOptions(out); + if (out.getTransportVersion().onOrAfter(TransportVersions.RESOLVE_INDEX_MODE_FILTER)) { + out.writeEnumSet(indexModes); + } } @Override @@ -118,12 +139,12 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Request request = (Request) o; - return Arrays.equals(names, request.names); + return Arrays.equals(names, request.names) && indexModes.equals(request.indexModes); } @Override public int hashCode() { - return Arrays.hashCode(names); + return Objects.hash(Arrays.hashCode(names), indexModes); } @Override @@ -276,6 +297,19 @@ public int hashCode() { result = 31 * result + Arrays.hashCode(attributes); return result; } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "ResolvedIndex{name=%s, aliases=%s, attributes=%s, dataStream=%s, mode=%s}", + getName(), + Arrays.toString(aliases), + Arrays.toString(attributes), + dataStream, + mode + ); + } } public static class ResolvedAlias extends ResolvedIndexAbstraction implements Writeable, ToXContentObject { @@ -333,6 +367,11 @@ public int hashCode() { result = 31 * result + Arrays.hashCode(indices); return result; } + + @Override + public String toString() { + return String.format(Locale.ROOT, "ResolvedAlias{name=%s, indices=%s}", getName(), Arrays.toString(indices)); + } } public static class ResolvedDataStream extends ResolvedIndexAbstraction implements Writeable, ToXContentObject { @@ -400,6 +439,17 @@ public int hashCode() { result = 31 * result + Arrays.hashCode(backingIndices); return result; } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "ResolvedDataStream{name=%s, backingIndices=%s, timestampField=%s}", + getName(), + Arrays.toString(backingIndices), + timestampField + ); + } } public static class Response extends ActionResponse implements ToXContentObject { @@ -505,7 +555,7 @@ protected void doExecute(Task task, Request request, final ActionListener indices = new ArrayList<>(); List aliases = new ArrayList<>(); List dataStreams = new ArrayList<>(); - resolveIndices(localIndices, projectState, indexNameExpressionResolver, indices, aliases, dataStreams); + resolveIndices(localIndices, projectState, indexNameExpressionResolver, indices, aliases, dataStreams, request.indexModes); if (remoteClusterIndices.size() > 0) { final int remoteRequests = remoteClusterIndices.size(); @@ -513,7 +563,7 @@ protected void doExecute(Task task, Request request, final ActionListener remoteResponses = Collections.synchronizedSortedMap(new TreeMap<>()); final Runnable terminalHandler = () -> { if (completionCounter.countDown()) { - mergeResults(remoteResponses, indices, aliases, dataStreams); + mergeResults(remoteResponses, indices, aliases, dataStreams, request.indexModes); listener.onResponse(new Response(indices, aliases, dataStreams)); } }; @@ -554,12 +604,35 @@ static void resolveIndices( IndexNameExpressionResolver resolver, List indices, List aliases, - List dataStreams + List dataStreams, + Set indexModes ) { if (localIndices == null) { return; } - resolveIndices(localIndices.indices(), localIndices.indicesOptions(), projectState, resolver, indices, aliases, dataStreams); + resolveIndices( + localIndices.indices(), + localIndices.indicesOptions(), + projectState, + resolver, + indices, + aliases, + dataStreams, + indexModes + ); + } + + // Shortcut for tests that don't need index mode filtering + static void resolveIndices( + String[] names, + IndicesOptions indicesOptions, + ProjectState projectState, + IndexNameExpressionResolver resolver, + List indices, + List aliases, + List dataStreams + ) { + resolveIndices(names, indicesOptions, projectState, resolver, indices, aliases, dataStreams, Collections.emptySet()); } /** @@ -580,7 +653,8 @@ static void resolveIndices( IndexNameExpressionResolver resolver, List indices, List aliases, - List dataStreams + List dataStreams, + Set indexModes ) { // redundant check to ensure that we don't resolve the list of empty names to "all" in this context if (names.length == 0) { @@ -603,47 +677,104 @@ static void resolveIndices( names ); for (ResolvedExpression s : resolvedIndexAbstractions) { - enrichIndexAbstraction(projectState, s, indices, aliases, dataStreams); + enrichIndexAbstraction(projectState, s, indices, aliases, dataStreams, indexModes); } indices.sort(Comparator.comparing(ResolvedIndexAbstraction::getName)); aliases.sort(Comparator.comparing(ResolvedIndexAbstraction::getName)); dataStreams.sort(Comparator.comparing(ResolvedIndexAbstraction::getName)); } + /** + * Merge the results from remote clusters into the local results lists. + * This will also do index mode filtering (if requested), as the remote cluster might be too old to do it itself. + */ private static void mergeResults( Map remoteResponses, List indices, List aliases, - List dataStreams + List dataStreams, + Set indexModes ) { for (Map.Entry responseEntry : remoteResponses.entrySet()) { String clusterAlias = responseEntry.getKey(); Response response = responseEntry.getValue(); for (ResolvedIndex index : response.indices) { + // We want to filter by mode here because the linked cluster might be too old to be able to filter + if (indexModes.isEmpty() == false && indexModes.contains(index.getMode()) == false) { + continue; + } indices.add(index.copy(RemoteClusterAware.buildRemoteIndexName(clusterAlias, index.getName()))); } + Set indexNames = indices.stream().map(ResolvedIndexAbstraction::getName).collect(Collectors.toSet()); for (ResolvedAlias alias : response.aliases) { - aliases.add(alias.copy(RemoteClusterAware.buildRemoteIndexName(clusterAlias, alias.getName()))); + if (indexModes.isEmpty() == false) { + // We filter out indices that are not included in the main index list after index mode filtering + String[] filteredIndices = Arrays.stream(alias.getIndices()) + .filter(idxName -> indexNames.contains(RemoteClusterAware.buildRemoteIndexName(clusterAlias, idxName))) + .toArray(String[]::new); + if (filteredIndices.length == 0) { + // If this alias points to no indices after filtering, we skip it + continue; + } + alias = new ResolvedAlias(RemoteClusterAware.buildRemoteIndexName(clusterAlias, alias.getName()), filteredIndices); + } else { + alias = alias.copy(RemoteClusterAware.buildRemoteIndexName(clusterAlias, alias.getName())); + } + aliases.add(alias); } for (ResolvedDataStream dataStream : response.dataStreams) { - dataStreams.add(dataStream.copy(RemoteClusterAware.buildRemoteIndexName(clusterAlias, dataStream.getName()))); + if (indexModes.isEmpty() == false) { + // We filter out indices that are not included in the main index list after index mode filtering + String[] filteredBackingIndices = Arrays.stream(dataStream.getBackingIndices()) + .filter(idxName -> indexNames.contains(RemoteClusterAware.buildRemoteIndexName(clusterAlias, idxName))) + .toArray(String[]::new); + if (filteredBackingIndices.length == 0) { + // If this data stream points to no backing indices after filtering, we skip it + continue; + } + dataStream = new ResolvedDataStream( + RemoteClusterAware.buildRemoteIndexName(clusterAlias, dataStream.getName()), + filteredBackingIndices, + dataStream.getTimestampField() + ); + } else { + dataStream = dataStream.copy(RemoteClusterAware.buildRemoteIndexName(clusterAlias, dataStream.getName())); + } + dataStreams.add(dataStream); } } } + private static Predicate indexModeFilter(ProjectState projectState, Set indexModes) { + if (indexModes.isEmpty()) { + return index -> true; + } + return index -> { + IndexMetadata indexMetadata = projectState.metadata().index(index); + IndexMode mode = indexMetadata.getIndexMode() == null ? IndexMode.STANDARD : indexMetadata.getIndexMode(); + return indexModes.contains(mode); + }; + } + private static void enrichIndexAbstraction( ProjectState projectState, ResolvedExpression resolvedExpression, List indices, List aliases, - List dataStreams + List dataStreams, + Set indexModes ) { SortedMap indicesLookup = projectState.metadata().getIndicesLookup(); IndexAbstraction ia = indicesLookup.get(resolvedExpression.resource()); + var filterPredicate = indexModeFilter(projectState, indexModes); if (ia != null) { switch (ia.getType()) { case CONCRETE_INDEX -> { + if (filterPredicate.test(ia.getWriteIndex()) == false) { + return; + } IndexMetadata writeIndex = projectState.metadata().index(ia.getWriteIndex()); + IndexMode mode = writeIndex.getIndexMode() == null ? IndexMode.STANDARD : writeIndex.getIndexMode(); String[] aliasNames = writeIndex.getAliases().keySet().stream().sorted().toArray(String[]::new); List attributes = new ArrayList<>(); attributes.add(writeIndex.getState() == IndexMetadata.State.OPEN ? Attribute.OPEN : Attribute.CLOSED); @@ -664,13 +795,17 @@ private static void enrichIndexAbstraction( aliasNames, attributes.stream().map(Enum::name).map(e -> e.toLowerCase(Locale.ROOT)).toArray(String[]::new), ia.getParentDataStream() == null ? null : ia.getParentDataStream().getName(), - writeIndex.getIndexMode() == null ? IndexMode.STANDARD : writeIndex.getIndexMode() + mode ) ); } case ALIAS -> { - String[] indexNames = getAliasIndexStream(resolvedExpression, ia, projectState.metadata()).map(Index::getName) + String[] indexNames = getAliasIndexStream(resolvedExpression, ia, projectState.metadata()).filter(filterPredicate) + .map(Index::getName) .toArray(String[]::new); + if (indexModes.isEmpty() == false && indexNames.length == 0) { + return; + } Arrays.sort(indexNames); aliases.add(new ResolvedAlias(ia.getName(), indexNames)); } @@ -682,7 +817,10 @@ private static void enrichIndexAbstraction( case DATA -> dataStream.getDataComponent().getIndices().stream(); case FAILURES -> dataStream.getFailureIndices().stream(); }; - String[] backingIndices = dataStreamIndices.map(Index::getName).toArray(String[]::new); + String[] backingIndices = dataStreamIndices.filter(filterPredicate).map(Index::getName).toArray(String[]::new); + if (indexModes.isEmpty() == false && backingIndices.length == 0) { + return; + } dataStreams.add(new ResolvedDataStream(dataStream.getName(), backingIndices, DataStream.TIMESTAMP_FIELD_NAME)); } default -> throw new IllegalStateException("unknown index abstraction type: " + ia.getType()); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveIndexAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveIndexAction.java index a37f4f54cfa5a..4e1b956e10b59 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveIndexAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveIndexAction.java @@ -13,6 +13,7 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.common.Strings; +import org.elasticsearch.index.IndexMode; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.Scope; @@ -20,12 +21,16 @@ import org.elasticsearch.rest.action.RestToXContentListener; import java.io.IOException; +import java.util.Arrays; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import static org.elasticsearch.rest.RestRequest.Method.GET; @ServerlessScope(Scope.PUBLIC) public class RestResolveIndexAction extends BaseRestHandler { + private static final Set CAPABILITIES = Set.of("mode_filter"); @Override public String getName() { @@ -37,12 +42,23 @@ public List routes() { return List.of(new Route(GET, "/_resolve/index/{name}")); } + @Override + public Set supportedCapabilities() { + return CAPABILITIES; + } + @Override protected BaseRestHandler.RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { String[] indices = Strings.splitStringByCommaToArray(request.param("name")); + String modeParam = request.param("mode"); ResolveIndexAction.Request resolveRequest = new ResolveIndexAction.Request( indices, - IndicesOptions.fromRequest(request, ResolveIndexAction.Request.DEFAULT_INDICES_OPTIONS) + IndicesOptions.fromRequest(request, ResolveIndexAction.Request.DEFAULT_INDICES_OPTIONS), + modeParam == null + ? null + : Arrays.stream(modeParam.split(",")) + .map(IndexMode::fromString) + .collect(() -> EnumSet.noneOf(IndexMode.class), EnumSet::add, EnumSet::addAll) ); return channel -> client.admin().indices().resolveIndex(resolveRequest, new RestToXContentListener<>(channel)); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexRequestTests.java index c2d00a6751c58..0bd9b1174ec4d 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexRequestTests.java @@ -12,8 +12,12 @@ import org.elasticsearch.action.admin.indices.resolve.ResolveIndexAction.Request; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.index.IndexMode; import org.elasticsearch.test.AbstractWireSerializingTestCase; +import java.util.Arrays; +import java.util.EnumSet; + public class ResolveIndexRequestTests extends AbstractWireSerializingTestCase { @Override @@ -35,7 +39,12 @@ protected Request createTestInstance() { randomBoolean(), randomBoolean() ); - return new Request(names, indicesOptions); + if (randomBoolean()) { + return new Request(names, indicesOptions); + } else { + EnumSet randomModes = EnumSet.copyOf(randomNonEmptySubsetOf(Arrays.asList(IndexMode.values()))); + return new Request(names, indicesOptions, randomBoolean() ? null : randomModes); + } } @Override diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexTests.java index 8dbc50aae0eb7..c1abc120d4cb1 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveIndexTests.java @@ -46,6 +46,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -170,6 +171,84 @@ public void testResolveStarWithAllOptions() { validateDataStreams(dataStreams, "logs-mysql-prod", "logs-mysql-test"); } + public void testResolveModesFilter() { + String[] names = randomFrom(new String[] { "*" }, new String[] { "_all" }); + List indices = new ArrayList<>(); + List aliases = new ArrayList<>(); + List dataStreams = new ArrayList<>(); + + // One type + TransportAction.resolveIndices( + names, + IndicesOptions.STRICT_EXPAND_OPEN_CLOSED_HIDDEN, + projectState, + resolver, + indices, + aliases, + dataStreams, + EnumSet.of(IndexMode.TIME_SERIES) + ); + validateIndices(indices, "logs-pgsql-prod-20200102"); + assertThat(aliases.size(), equalTo(2)); + var alias1 = aliases.get(0); + assertThat(alias1.getName(), equalTo("logs-pgsql-prod")); + assertThat(alias1.getIndices(), arrayContaining("logs-pgsql-prod-20200102")); + var alias2 = aliases.get(1); + assertThat(alias2.getName(), equalTo("one-off-alias")); + assertThat(alias2.getIndices(), arrayContaining("logs-pgsql-prod-20200102")); + assertThat(dataStreams.size(), equalTo(0)); + + indices = new ArrayList<>(); + aliases = new ArrayList<>(); + dataStreams = new ArrayList<>(); + // Everything + TransportAction.resolveIndices( + names, + IndicesOptions.STRICT_EXPAND_OPEN_CLOSED_HIDDEN, + projectState, + resolver, + indices, + aliases, + dataStreams, + EnumSet.of(IndexMode.TIME_SERIES, IndexMode.STANDARD) + ); + validateIndices( + indices, + ".ds-logs-mysql-prod-" + dateString + "-000001", + ".ds-logs-mysql-prod-" + dateString + "-000002", + ".ds-logs-mysql-prod-" + dateString + "-000003", + ".ds-logs-mysql-prod-" + dateString + "-000004", + ".ds-logs-mysql-test-" + dateString + "-000001", + ".ds-logs-mysql-test-" + dateString + "-000002", + ".test-system-index", + "logs-pgsql-prod-20200101", + "logs-pgsql-prod-20200102", + "logs-pgsql-prod-20200103", + "logs-pgsql-test-20200101", + "logs-pgsql-test-20200102", + "logs-pgsql-test-20200103" + ); + validateAliases(aliases, "logs-pgsql-prod", "logs-pgsql-test", "one-off-alias"); + validateDataStreams(dataStreams, "logs-mysql-prod", "logs-mysql-test"); + // Not matched + indices = new ArrayList<>(); + aliases = new ArrayList<>(); + dataStreams = new ArrayList<>(); + TransportAction.resolveIndices( + names, + IndicesOptions.STRICT_EXPAND_OPEN_CLOSED_HIDDEN, + projectState, + resolver, + indices, + aliases, + dataStreams, + EnumSet.of(IndexMode.LOOKUP) + ); + assertThat(indices.size(), equalTo(0)); + assertThat(aliases.size(), equalTo(0)); + assertThat(dataStreams.size(), equalTo(0)); + } + public void testResolveWithPattern() { String[] names = new String[] { "logs-pgsql*" }; IndicesOptions indicesOptions = Request.DEFAULT_INDICES_OPTIONS;