From 9cecc89fedacb8a7f47cbb85a40d07345b01aba6 Mon Sep 17 00:00:00 2001 From: Niels Bauman <33722607+nielsbauman@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:43:13 +0100 Subject: [PATCH] Run `TransportExplainLifecycleAction` on local node (#122885) This action solely needs the cluster state, it can run on any node. Additionally, it needs to be cancellable to avoid doing unnecessary work after a client failure or timeout. Relates #101805 --- docs/changelog/122885.yaml | 5 + .../core/ilm/ExplainLifecycleRequest.java | 60 ++++++++++-- .../core/ilm/ExplainLifecycleResponse.java | 19 ++-- .../ilm/ExplainLifecycleRequestTests.java | 87 ----------------- .../ilm/ExplainLifecycleResponseTests.java | 96 ------------------- .../action/RestExplainLifecycleAction.java | 7 +- .../TransportExplainLifecycleAction.java | 70 +++++++++----- .../TransportExplainLifecycleActionTests.java | 62 ++++++------ 8 files changed, 151 insertions(+), 255 deletions(-) create mode 100644 docs/changelog/122885.yaml delete mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequestTests.java delete mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponseTests.java diff --git a/docs/changelog/122885.yaml b/docs/changelog/122885.yaml new file mode 100644 index 000000000000..818b2fbd4a00 --- /dev/null +++ b/docs/changelog/122885.yaml @@ -0,0 +1,5 @@ +pr: 122885 +summary: Run `TransportExplainLifecycleAction` on local node +area: ILM+SLM +type: enhancement +issues: [] diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequest.java index 913bd1753e6d..1b898b3b8d19 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequest.java @@ -7,15 +7,22 @@ package org.elasticsearch.xpack.core.ilm; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.action.support.master.info.ClusterInfoRequest; +import org.elasticsearch.action.support.local.LocalClusterStateRequest; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.core.UpdateForV10; +import org.elasticsearch.tasks.CancellableTask; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.tasks.TaskId; import java.io.IOException; import java.util.Arrays; +import java.util.Map; import java.util.Objects; /** @@ -24,26 +31,58 @@ import java.util.Objects; * Multiple indices may be queried in the same request using the * {@link #indices(String...)} method */ -public class ExplainLifecycleRequest extends ClusterInfoRequest { +public class ExplainLifecycleRequest extends LocalClusterStateRequest implements IndicesRequest.Replaceable { + private String[] indices = Strings.EMPTY_ARRAY; + private IndicesOptions indicesOptions; private boolean onlyErrors = false; private boolean onlyManaged = false; public ExplainLifecycleRequest(TimeValue masterTimeout) { - super(masterTimeout, IndicesOptions.strictExpandOpen()); + super(masterTimeout); + indicesOptions = IndicesOptions.strictExpandOpen(); } + /** + * NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC we must remain able to read these requests until + * we no longer need to support calling this action remotely. + */ + @UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT) public ExplainLifecycleRequest(StreamInput in) throws IOException { super(in); + indices = in.readStringArray(); + if (in.getTransportVersion().before(TransportVersions.V_8_0_0)) { + in.readStringArray(); + } + indicesOptions = IndicesOptions.readIndicesOptions(in); onlyErrors = in.readBoolean(); onlyManaged = in.readBoolean(); } @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - out.writeBoolean(onlyErrors); - out.writeBoolean(onlyManaged); + public ExplainLifecycleRequest indices(String... indices) { + this.indices = indices; + return this; + } + + public ExplainLifecycleRequest indicesOptions(IndicesOptions indicesOptions) { + this.indicesOptions = indicesOptions; + return this; + } + + @Override + public String[] indices() { + return indices; + } + + @Override + public IndicesOptions indicesOptions() { + return indicesOptions; + } + + @Override + public boolean includeDataStreams() { + return true; } public boolean onlyErrors() { @@ -69,6 +108,11 @@ public class ExplainLifecycleRequest extends ClusterInfoRequest headers) { + return new CancellableTask(id, type, action, "", parentTaskId, headers); + } + @Override public int hashCode() { return Objects.hash(Arrays.hashCode(indices()), indicesOptions(), onlyErrors, onlyManaged); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponse.java index 914a025e35c2..1f1753903702 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponse.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponse.java @@ -9,9 +9,8 @@ package org.elasticsearch.xpack.core.ilm; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.util.Maps; +import org.elasticsearch.core.UpdateForV10; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; @@ -32,17 +31,6 @@ public class ExplainLifecycleResponse extends ActionResponse implements ToXConte private final Map indexResponses; - public ExplainLifecycleResponse(StreamInput in) throws IOException { - super(in); - int size = in.readVInt(); - Map indexResponses = Maps.newMapWithExpectedSize(size); - for (int i = 0; i < size; i++) { - IndexLifecycleExplainResponse indexResponse = new IndexLifecycleExplainResponse(in); - indexResponses.put(indexResponse.getIndex(), indexResponse); - } - this.indexResponses = indexResponses; - } - public ExplainLifecycleResponse(Map indexResponses) { this.indexResponses = indexResponses; } @@ -69,6 +57,11 @@ public class ExplainLifecycleResponse extends ActionResponse implements ToXConte return builder; } + /** + * NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC we must remain able to write these responses until + * we no longer need to support calling this action remotely. + */ + @UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT) @Override public void writeTo(StreamOutput out) throws IOException { out.writeCollection(indexResponses.values()); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequestTests.java deleted file mode 100644 index 6cea7100a9da..000000000000 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleRequestTests.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.xpack.core.ilm; - -import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.common.io.stream.Writeable.Reader; -import org.elasticsearch.test.AbstractWireSerializingTestCase; - -import java.util.Arrays; - -public class ExplainLifecycleRequestTests extends AbstractWireSerializingTestCase { - - @Override - protected ExplainLifecycleRequest createTestInstance() { - ExplainLifecycleRequest request = new ExplainLifecycleRequest(TEST_REQUEST_TIMEOUT); - if (randomBoolean()) { - request.indices(generateRandomStringArray(20, 20, false, false)); - } - if (randomBoolean()) { - IndicesOptions indicesOptions = IndicesOptions.fromOptions( - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean() - ); - request.indicesOptions(indicesOptions); - } - if (randomBoolean()) { - request.onlyErrors(randomBoolean()); - } - if (randomBoolean()) { - request.onlyManaged(randomBoolean()); - } - return request; - } - - @Override - protected ExplainLifecycleRequest mutateInstance(ExplainLifecycleRequest instance) { - String[] indices = instance.indices(); - IndicesOptions indicesOptions = instance.indicesOptions(); - boolean onlyErrors = instance.onlyErrors(); - boolean onlyManaged = instance.onlyManaged(); - switch (between(0, 3)) { - case 0 -> indices = randomValueOtherThanMany( - i -> Arrays.equals(i, instance.indices()), - () -> generateRandomStringArray(20, 10, false, false) - ); - case 1 -> indicesOptions = randomValueOtherThan( - indicesOptions, - () -> IndicesOptions.fromOptions( - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean() - ) - ); - case 2 -> onlyErrors = onlyErrors == false; - case 3 -> onlyManaged = onlyManaged == false; - default -> throw new AssertionError("Illegal randomisation branch"); - } - ExplainLifecycleRequest newRequest = new ExplainLifecycleRequest(TEST_REQUEST_TIMEOUT); - newRequest.indices(indices); - newRequest.indicesOptions(indicesOptions); - newRequest.onlyErrors(onlyErrors); - newRequest.onlyManaged(onlyManaged); - return newRequest; - } - - @Override - protected Reader instanceReader() { - return ExplainLifecycleRequest::new; - } - -} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponseTests.java deleted file mode 100644 index c4138d228719..000000000000 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ExplainLifecycleResponseTests.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.xpack.core.ilm; - -import org.elasticsearch.cluster.ClusterModule; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.util.CollectionUtils; -import org.elasticsearch.test.AbstractXContentSerializingTestCase; -import org.elasticsearch.xcontent.ConstructingObjectParser; -import org.elasticsearch.xcontent.NamedXContentRegistry; -import org.elasticsearch.xcontent.ParseField; -import org.elasticsearch.xcontent.XContentParser; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class ExplainLifecycleResponseTests extends AbstractXContentSerializingTestCase { - - @SuppressWarnings("unchecked") - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "explain_lifecycle_response", - a -> new ExplainLifecycleResponse( - ((List) a[0]).stream() - .collect(Collectors.toMap(IndexLifecycleExplainResponse::getIndex, Function.identity())) - ) - ); - static { - PARSER.declareNamedObjects( - ConstructingObjectParser.constructorArg(), - (p, c, n) -> IndexLifecycleExplainResponse.PARSER.apply(p, c), - ExplainLifecycleResponse.INDICES_FIELD - ); - } - - @Override - protected ExplainLifecycleResponse createTestInstance() { - Map indexResponses = new HashMap<>(); - long now = System.currentTimeMillis(); - for (int i = 0; i < randomIntBetween(0, 2); i++) { - IndexLifecycleExplainResponse indexResponse = IndexLifecycleExplainResponseTests.randomIndexExplainResponse(); - // Since the age is calculated from now, we make now constant so that we don't get changes in age during the run of the test: - indexResponse.nowSupplier = () -> now; - indexResponses.put(indexResponse.getIndex(), indexResponse); - } - return new ExplainLifecycleResponse(indexResponses); - } - - @Override - protected Writeable.Reader instanceReader() { - return ExplainLifecycleResponse::new; - } - - @Override - protected ExplainLifecycleResponse mutateInstance(ExplainLifecycleResponse response) { - Map indexResponses = new HashMap<>(response.getIndexResponses()); - IndexLifecycleExplainResponse indexResponse = IndexLifecycleExplainResponseTests.randomIndexExplainResponse(); - indexResponses.put(indexResponse.getIndex(), indexResponse); - return new ExplainLifecycleResponse(indexResponses); - } - - @Override - protected ExplainLifecycleResponse doParseInstance(XContentParser parser) throws IOException { - return PARSER.apply(parser, null); - } - - @Override - protected boolean assertToXContentEquivalence() { - return false; - } - - protected NamedWriteableRegistry getNamedWriteableRegistry() { - return new NamedWriteableRegistry( - List.of(new NamedWriteableRegistry.Entry(LifecycleAction.class, MockAction.NAME, MockAction::new)) - ); - } - - @Override - protected NamedXContentRegistry xContentRegistry() { - return new NamedXContentRegistry( - CollectionUtils.appendToCopy( - ClusterModule.getNamedXWriteables(), - new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MockAction.NAME), MockAction::parse) - ) - ); - } -} diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/RestExplainLifecycleAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/RestExplainLifecycleAction.java index 3950edf648e1..757bf7291d16 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/RestExplainLifecycleAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/RestExplainLifecycleAction.java @@ -12,6 +12,7 @@ import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.action.RestCancellableNodeClient; import org.elasticsearch.rest.action.RestToXContentListener; import org.elasticsearch.xpack.core.ilm.ExplainLifecycleRequest; import org.elasticsearch.xpack.core.ilm.action.ExplainLifecycleAction; @@ -41,6 +42,10 @@ public class RestExplainLifecycleAction extends BaseRestHandler { explainLifecycleRequest.indicesOptions(IndicesOptions.fromRequest(restRequest, IndicesOptions.strictExpandOpen())); explainLifecycleRequest.onlyManaged(restRequest.paramAsBoolean("only_managed", false)); explainLifecycleRequest.onlyErrors(restRequest.paramAsBoolean("only_errors", false)); - return channel -> client.execute(ExplainLifecycleAction.INSTANCE, explainLifecycleRequest, new RestToXContentListener<>(channel)); + return channel -> new RestCancellableNodeClient(client, restRequest.getHttpChannel()).execute( + ExplainLifecycleAction.INSTANCE, + explainLifecycleRequest, + new RestToXContentListener<>(channel) + ); } } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleAction.java index 8a44d1fa2198..0360a19b7919 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleAction.java @@ -10,8 +10,11 @@ package org.elasticsearch.xpack.ilm.action; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; -import org.elasticsearch.action.support.master.info.TransportClusterInfoAction; +import org.elasticsearch.action.support.ChannelActionListener; +import org.elasticsearch.action.support.local.TransportLocalClusterStateAction; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.LifecycleExecutionState; @@ -22,7 +25,9 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.UpdateForV10; import org.elasticsearch.injection.guice.Inject; +import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -34,11 +39,11 @@ import org.elasticsearch.xpack.core.ilm.ErrorStep; import org.elasticsearch.xpack.core.ilm.ExplainLifecycleRequest; import org.elasticsearch.xpack.core.ilm.ExplainLifecycleResponse; import org.elasticsearch.xpack.core.ilm.IndexLifecycleExplainResponse; +import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; import org.elasticsearch.xpack.core.ilm.PhaseExecutionInfo; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.action.ExplainLifecycleAction; -import org.elasticsearch.xpack.ilm.IndexLifecycleService; import java.io.IOException; import java.util.Map; @@ -47,11 +52,17 @@ import java.util.TreeMap; import static org.elasticsearch.index.IndexSettings.LIFECYCLE_ORIGINATION_DATE; import static org.elasticsearch.xpack.core.ilm.WaitForRolloverReadyStep.applyDefaultConditions; -public class TransportExplainLifecycleAction extends TransportClusterInfoAction { +public class TransportExplainLifecycleAction extends TransportLocalClusterStateAction { private final NamedXContentRegistry xContentRegistry; - private final IndexLifecycleService indexLifecycleService; + private final IndexNameExpressionResolver indexNameExpressionResolver; + /** + * NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC it must be registered with the TransportService until + * we no longer need to support calling this action remotely. + */ + @UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT) + @SuppressWarnings("this-escape") @Inject public TransportExplainLifecycleAction( TransportService transportService, @@ -60,32 +71,43 @@ public class TransportExplainLifecycleAction extends TransportClusterInfoAction< ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, NamedXContentRegistry xContentRegistry, - IndexLifecycleService indexLifecycleService, ProjectResolver projectResolver ) { super( ExplainLifecycleAction.NAME, - transportService, - clusterService, - threadPool, actionFilters, - ExplainLifecycleRequest::new, - indexNameExpressionResolver, - ExplainLifecycleResponse::new, - projectResolver + transportService.getTaskManager(), + clusterService, + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.xContentRegistry = xContentRegistry; - this.indexLifecycleService = indexLifecycleService; + this.indexNameExpressionResolver = indexNameExpressionResolver; + + transportService.registerRequestHandler( + actionName, + executor, + false, + true, + ExplainLifecycleRequest::new, + (request, channel, task) -> executeDirect(task, request, new ChannelActionListener<>(channel)) + ); + } @Override - protected void doMasterOperation( + protected ClusterBlockException checkBlock(ExplainLifecycleRequest request, ClusterState state) { + return state.blocks() + .indicesBlockedException(ClusterBlockLevel.METADATA_READ, indexNameExpressionResolver.concreteIndexNames(state, request)); + } + + @Override + protected void localClusterStateOperation( Task task, ExplainLifecycleRequest request, - String[] concreteIndices, - ClusterState state, + final ClusterState state, ActionListener listener ) { + String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request); boolean rolloverOnlyIfHasDocuments = LifecycleSettings.LIFECYCLE_ROLLOVER_ONLY_IF_HAS_DOCUMENTS_SETTING.get( state.metadata().settings() ); @@ -98,7 +120,6 @@ public class TransportExplainLifecycleAction extends TransportClusterInfoAction< state.metadata(), request.onlyErrors(), request.onlyManaged(), - indexLifecycleService, xContentRegistry, rolloverOnlyIfHasDocuments ); @@ -111,6 +132,8 @@ public class TransportExplainLifecycleAction extends TransportClusterInfoAction< indexResponses.put(indexResponse.getIndex(), indexResponse); } } + // Ensure not cancelled before building XContent. + ((CancellableTask) task).ensureNotCancelled(); listener.onResponse(new ExplainLifecycleResponse(indexResponses)); } @@ -120,11 +143,11 @@ public class TransportExplainLifecycleAction extends TransportClusterInfoAction< Metadata metadata, boolean onlyErrors, boolean onlyManaged, - IndexLifecycleService indexLifecycleService, NamedXContentRegistry xContentRegistry, boolean rolloverOnlyIfHasDocuments ) throws IOException { - IndexMetadata indexMetadata = metadata.getProject().index(indexName); + final var project = metadata.getProject(); + IndexMetadata indexMetadata = project.index(indexName); Settings idxSettings = indexMetadata.getSettings(); LifecycleExecutionState lifecycleState = indexMetadata.getLifecycleExecutionState(); String policyName = indexMetadata.getLifecyclePolicyName(); @@ -167,10 +190,11 @@ public class TransportExplainLifecycleAction extends TransportClusterInfoAction< } final IndexLifecycleExplainResponse indexResponse; - if (metadata.getProject().isIndexManagedByILM(indexMetadata)) { + if (project.isIndexManagedByILM(indexMetadata)) { + final IndexLifecycleMetadata indexLifecycleMetadata = project.custom(IndexLifecycleMetadata.TYPE, IndexLifecycleMetadata.EMPTY); + final boolean policyExists = indexLifecycleMetadata.getPolicies().containsKey(policyName); // If this is requesting only errors, only include indices in the error step or which are using a nonexistent policy - if (onlyErrors == false - || (ErrorStep.NAME.equals(lifecycleState.step()) || indexLifecycleService.policyExists(policyName) == false)) { + if (onlyErrors == false || (ErrorStep.NAME.equals(lifecycleState.step()) || policyExists == false)) { Long originationDate = idxSettings.getAsLong(LIFECYCLE_ORIGINATION_DATE, -1L); indexResponse = IndexLifecycleExplainResponse.newManagedIndexResponse( indexName, @@ -180,7 +204,7 @@ public class TransportExplainLifecycleAction extends TransportClusterInfoAction< lifecycleState.phase(), lifecycleState.action(), // treat a missing policy as if the index is in the error step - indexLifecycleService.policyExists(policyName) == false ? ErrorStep.NAME : lifecycleState.step(), + policyExists == false ? ErrorStep.NAME : lifecycleState.step(), lifecycleState.failedStep(), lifecycleState.isAutoRetryableError(), lifecycleState.failedStepRetryCount(), diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleActionTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleActionTests.java index e705fe162466..e89adafe9176 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleActionTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleActionTests.java @@ -16,26 +16,30 @@ import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xpack.core.ilm.ErrorStep; import org.elasticsearch.xpack.core.ilm.IndexLifecycleExplainResponse; +import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.ilm.LifecycleAction; +import org.elasticsearch.xpack.core.ilm.LifecyclePolicyMetadataTests; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; +import org.elasticsearch.xpack.core.ilm.OperationMode; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.WaitForRolloverReadyStep; import org.elasticsearch.xpack.ilm.IndexLifecycleService; import java.io.IOException; import java.util.List; +import java.util.Map; import static org.elasticsearch.cluster.metadata.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY; import static org.elasticsearch.xpack.ilm.action.TransportExplainLifecycleAction.getIndexLifecycleExplainResponse; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class TransportExplainLifecycleActionTests extends ESTestCase { + private static final String POLICY_NAME = "my-policy"; public static final String PHASE_DEFINITION = """ { "policy" : "my-policy", @@ -62,9 +66,6 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { public void testGetIndexLifecycleExplainResponse() throws IOException { { // only errors index - IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class); - when(indexLifecycleService.policyExists("my-policy")).thenReturn(true); - LifecycleExecutionState.Builder errorStepState = LifecycleExecutionState.builder() .setPhase("hot") .setAction("rollover") @@ -72,19 +73,21 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { .setPhaseDefinition(PHASE_DEFINITION); String indexInErrorStep = "index_in_error"; IndexMetadata indexMetadata = IndexMetadata.builder(indexInErrorStep) - .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, "my-policy")) + .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, POLICY_NAME)) .numberOfShards(randomIntBetween(1, 5)) .numberOfReplicas(randomIntBetween(0, 5)) .putCustom(ILM_CUSTOM_METADATA_KEY, errorStepState.build().asMap()) .build(); - Metadata metadata = Metadata.builder().put(indexMetadata, true).build(); + Metadata metadata = Metadata.builder() + .put(indexMetadata, true) + .putCustom(IndexLifecycleMetadata.TYPE, createIndexLifecycleMetadata()) + .build(); IndexLifecycleExplainResponse onlyErrorsResponse = getIndexLifecycleExplainResponse( indexInErrorStep, metadata, true, true, - indexLifecycleService, REGISTRY, randomBoolean() ); @@ -95,9 +98,6 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { { // only managed index - IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class); - when(indexLifecycleService.policyExists("my-policy")).thenReturn(true); - LifecycleExecutionState.Builder checkRolloverReadyStepState = LifecycleExecutionState.builder() .setPhase("hot") .setAction("rollover") @@ -106,19 +106,21 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { String indexInCheckRolloverStep = "index_in_check_rollover"; IndexMetadata indexMetadata = IndexMetadata.builder(indexInCheckRolloverStep) - .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, "my-policy")) + .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, POLICY_NAME)) .numberOfShards(randomIntBetween(1, 5)) .numberOfReplicas(randomIntBetween(0, 5)) .putCustom(ILM_CUSTOM_METADATA_KEY, checkRolloverReadyStepState.build().asMap()) .build(); - Metadata metadata = Metadata.builder().put(indexMetadata, true).build(); + Metadata metadata = Metadata.builder() + .put(indexMetadata, true) + .putCustom(IndexLifecycleMetadata.TYPE, createIndexLifecycleMetadata()) + .build(); IndexLifecycleExplainResponse onlyErrorsResponse = getIndexLifecycleExplainResponse( indexInCheckRolloverStep, metadata, true, true, - indexLifecycleService, REGISTRY, randomBoolean() ); @@ -129,7 +131,6 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { metadata, false, true, - indexLifecycleService, REGISTRY, randomBoolean() ); @@ -149,14 +150,16 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { .numberOfShards(randomIntBetween(1, 5)) .numberOfReplicas(randomIntBetween(0, 5)) .build(); - Metadata metadata = Metadata.builder().put(indexMetadata, true).build(); + Metadata metadata = Metadata.builder() + .put(indexMetadata, true) + .putCustom(IndexLifecycleMetadata.TYPE, createIndexLifecycleMetadata()) + .build(); IndexLifecycleExplainResponse onlyErrorsResponse = getIndexLifecycleExplainResponse( indexWithMissingPolicy, metadata, true, true, - indexLifecycleService, REGISTRY, randomBoolean() ); @@ -167,22 +170,21 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { { // not managed index - IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class); - when(indexLifecycleService.policyExists(anyString())).thenReturn(true); - IndexMetadata indexMetadata = IndexMetadata.builder("index") .settings(settings(IndexVersion.current())) .numberOfShards(randomIntBetween(1, 5)) .numberOfReplicas(randomIntBetween(0, 5)) .build(); - Metadata metadata = Metadata.builder().put(indexMetadata, true).build(); + Metadata metadata = Metadata.builder() + .put(indexMetadata, true) + .putCustom(IndexLifecycleMetadata.TYPE, createIndexLifecycleMetadata()) + .build(); IndexLifecycleExplainResponse onlyManaged = getIndexLifecycleExplainResponse( "index", metadata, false, true, - indexLifecycleService, REGISTRY, randomBoolean() ); @@ -191,9 +193,6 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { { // validate addition of default condition with `rolloverOnlyIfHasDocuments` true - IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class); - when(indexLifecycleService.policyExists("my-policy")).thenReturn(true); - LifecycleExecutionState.Builder checkRolloverReadyStepState = LifecycleExecutionState.builder() .setPhase("hot") .setAction("rollover") @@ -201,19 +200,21 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { .setPhaseDefinition(PHASE_DEFINITION); String indexInCheckRolloverStep = "index_in_check_rollover"; IndexMetadata indexMetadata = IndexMetadata.builder(indexInCheckRolloverStep) - .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, "my-policy")) + .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, POLICY_NAME)) .numberOfShards(randomIntBetween(1, 5)) .numberOfReplicas(randomIntBetween(0, 5)) .putCustom(ILM_CUSTOM_METADATA_KEY, checkRolloverReadyStepState.build().asMap()) .build(); - Metadata metadata = Metadata.builder().put(indexMetadata, true).build(); + Metadata metadata = Metadata.builder() + .put(indexMetadata, true) + .putCustom(IndexLifecycleMetadata.TYPE, createIndexLifecycleMetadata()) + .build(); IndexLifecycleExplainResponse response = getIndexLifecycleExplainResponse( indexInCheckRolloverStep, metadata, false, true, - indexLifecycleService, REGISTRY, true ); @@ -222,4 +223,11 @@ public class TransportExplainLifecycleActionTests extends ESTestCase { assertThat(rolloverAction.getConditions().getMinDocs(), is(1L)); } } + + private static IndexLifecycleMetadata createIndexLifecycleMetadata() { + return new IndexLifecycleMetadata( + Map.of(POLICY_NAME, LifecyclePolicyMetadataTests.createRandomPolicyMetadata(POLICY_NAME)), + randomFrom(OperationMode.values()) + ); + } }