diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java index 5dac0cbf416a5..6d131b7375cc6 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java @@ -159,7 +159,8 @@ protected ClusterBlockException checkBlock(RolloverRequest request, ClusterState .matchOpen(request.indicesOptions().expandWildcardsOpen()) .matchClosed(request.indicesOptions().expandWildcardsClosed()) .build(), - IndicesOptions.GatekeeperOptions.DEFAULT + IndicesOptions.GatekeeperOptions.DEFAULT, + IndicesOptions.CrossProjectModeOptions.DEFAULT ); ResolvedExpression resolvedRolloverTarget = SelectorResolver.parseExpression(request.getRolloverTarget(), request.indicesOptions()); final IndexAbstraction indexAbstraction = projectMetadata.getIndicesLookup().get(resolvedRolloverTarget.resource()); @@ -255,7 +256,8 @@ protected void masterOperation( final var statsIndicesOptions = new IndicesOptions( IndicesOptions.ConcreteTargetOptions.ALLOW_UNAVAILABLE_TARGETS, IndicesOptions.WildcardOptions.builder().matchClosed(true).allowEmptyExpressions(false).build(), - IndicesOptions.GatekeeperOptions.DEFAULT + IndicesOptions.GatekeeperOptions.DEFAULT, + IndicesOptions.CrossProjectModeOptions.DEFAULT ); // Make sure to recombine any selectors on the stats request IndicesStatsRequest statsRequest = new IndicesStatsRequest().indices(resolvedRolloverTarget.combined()) diff --git a/server/src/main/java/org/elasticsearch/action/support/IndicesOptions.java b/server/src/main/java/org/elasticsearch/action/support/IndicesOptions.java index 916def1fc5952..d6c5122649e20 100644 --- a/server/src/main/java/org/elasticsearch/action/support/IndicesOptions.java +++ b/server/src/main/java/org/elasticsearch/action/support/IndicesOptions.java @@ -9,9 +9,11 @@ package org.elasticsearch.action.support; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.TransportVersion; import org.elasticsearch.TransportVersions; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.core.Nullable; @@ -45,11 +47,14 @@ * @param gatekeeperOptions, applies to all the resolved indices and defines if throttled will be included and if certain type of * aliases or indices are allowed, or they will throw an error. It acts as a gatekeeper when an action * does not support certain options. + * @param crossProjectModeOptions, applies to all the indices and adds logic specific for cross-project search. These options are + * internal-only and can change over the lifetime of a single request. */ public record IndicesOptions( ConcreteTargetOptions concreteTargetOptions, WildcardOptions wildcardOptions, - GatekeeperOptions gatekeeperOptions + GatekeeperOptions gatekeeperOptions, + CrossProjectModeOptions crossProjectModeOptions ) implements ToXContentFragment { public static IndicesOptions.Builder builder() { @@ -413,6 +418,37 @@ public static Builder builder(GatekeeperOptions gatekeeperOptions) { } } + /** + * The cross-project mode options are internal-only options that apply on all indices that have been selected by the other Options. + * These options may contextually change over the lifetime of the request. + * @param resolveIndexExpression determines that the index expression must be resolved for cross-project requests, defaults to false. + */ + public record CrossProjectModeOptions(boolean resolveIndexExpression) implements Writeable { + + public static final CrossProjectModeOptions DEFAULT = new CrossProjectModeOptions(false); + + private static final TransportVersion INDICES_OPTIONS_RESOLUTION_MODE = TransportVersion.fromName( + "indices_options_resolution_mode" + ); + + private static final String INDEX_EXPRESSION_NAME = "resolve_cross_project_index_expression"; + + @Override + public void writeTo(StreamOutput out) throws IOException { + if (out.getTransportVersion().supports(INDICES_OPTIONS_RESOLUTION_MODE)) { + out.writeBoolean(resolveIndexExpression); + } + } + + public static CrossProjectModeOptions readFrom(StreamInput in) throws IOException { + if (in.getTransportVersion().supports(INDICES_OPTIONS_RESOLUTION_MODE)) { + return new CrossProjectModeOptions(in.readBoolean()); + } else { + return CrossProjectModeOptions.DEFAULT; + } + } + } + /** * This class is maintained for backwards compatibility and performance purposes. We use it for serialisation along with {@link Option}. */ @@ -463,7 +499,8 @@ private enum Option { public static final IndicesOptions DEFAULT = new IndicesOptions( ConcreteTargetOptions.ERROR_WHEN_UNAVAILABLE_TARGETS, WildcardOptions.DEFAULT, - GatekeeperOptions.DEFAULT + GatekeeperOptions.DEFAULT, + CrossProjectModeOptions.DEFAULT ); public static final IndicesOptions STRICT_EXPAND_OPEN = IndicesOptions.builder() @@ -857,6 +894,13 @@ public boolean ignoreThrottled() { return gatekeeperOptions().ignoreThrottled(); } + /** + * @return whether indices will resolve to the cross-project "flat world" expression + */ + public boolean resolveCrossProjectIndexExpression() { + return crossProjectModeOptions().resolveIndexExpression(); + } + public void writeIndicesOptions(StreamOutput out) throws IOException { EnumSet