|  | @@ -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<ExplainLifecycleRequest, ExplainLifecycleResponse> {
 | 
	
		
			
				|  |  | +public class TransportExplainLifecycleAction extends TransportLocalClusterStateAction<ExplainLifecycleRequest, ExplainLifecycleResponse> {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      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 ClusterBlockException checkBlock(ExplainLifecycleRequest request, ClusterState state) {
 | 
	
		
			
				|  |  | +        return state.blocks()
 | 
	
		
			
				|  |  | +            .indicesBlockedException(ClusterBlockLevel.METADATA_READ, indexNameExpressionResolver.concreteIndexNames(state, request));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | -    protected void doMasterOperation(
 | 
	
		
			
				|  |  | +    protected void localClusterStateOperation(
 | 
	
		
			
				|  |  |          Task task,
 | 
	
		
			
				|  |  |          ExplainLifecycleRequest request,
 | 
	
		
			
				|  |  | -        String[] concreteIndices,
 | 
	
		
			
				|  |  | -        ClusterState state,
 | 
	
		
			
				|  |  | +        final ClusterState state,
 | 
	
		
			
				|  |  |          ActionListener<ExplainLifecycleResponse> 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(),
 |