diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/CleanupTargetIndexStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/CleanupTargetIndexStep.java deleted file mode 100644 index 3105ed7e12a90..0000000000000 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/CleanupTargetIndexStep.java +++ /dev/null @@ -1,129 +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.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; -import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.metadata.IndexMetadata; -import org.elasticsearch.common.Strings; -import org.elasticsearch.core.TimeValue; -import org.elasticsearch.index.IndexNotFoundException; - -import java.util.Objects; -import java.util.function.Function; - -/** - * Deletes the target index created by an operation such as shrink or rollup and - * identified the target index name stored in the lifecycle state of the managed - * index (if any was generated) - */ -public class CleanupTargetIndexStep extends AsyncRetryDuringSnapshotActionStep { - public static final String NAME = "cleanup-target-index"; - private static final Logger logger = LogManager.getLogger(CleanupTargetIndexStep.class); - - private final Function sourceIndexNameSupplier; - private final Function targetIndexNameSupplier; - - public CleanupTargetIndexStep( - StepKey key, - StepKey nextStepKey, - Client client, - Function sourceIndexNameSupplier, - Function targetIndexNameSupplier - ) { - super(key, nextStepKey, client); - this.sourceIndexNameSupplier = sourceIndexNameSupplier; - this.targetIndexNameSupplier = targetIndexNameSupplier; - } - - @Override - public boolean isRetryable() { - return true; - } - - Function getSourceIndexNameSupplier() { - return sourceIndexNameSupplier; - } - - Function getTargetIndexNameSupplier() { - return targetIndexNameSupplier; - } - - @Override - void performDuringNoSnapshot(IndexMetadata indexMetadata, ClusterState currentClusterState, ActionListener listener) { - final String sourceIndexName = sourceIndexNameSupplier.apply(indexMetadata); - if (Strings.isNullOrEmpty(sourceIndexName) == false) { - // the current managed index is the target index - if (currentClusterState.metadata().getProject().index(sourceIndexName) == null) { - // if the source index does not exist, we'll skip deleting the - // (managed) target index as that will cause data loss - String policyName = indexMetadata.getLifecyclePolicyName(); - logger.warn( - "managed index [{}] has been created as part of policy [{}] and the source index [{}] does not exist " - + "anymore. will skip the [{}] step", - indexMetadata.getIndex().getName(), - policyName, - sourceIndexName, - NAME - ); - listener.onResponse(null); - return; - } - } - - final String targetIndexName = targetIndexNameSupplier.apply(indexMetadata); - // if the target index was not generated there is nothing to delete so we move on - if (Strings.hasText(targetIndexName) == false) { - listener.onResponse(null); - return; - } - getClient().admin() - .indices() - .delete(new DeleteIndexRequest(targetIndexName).masterNodeTimeout(TimeValue.MAX_VALUE), new ActionListener<>() { - @Override - public void onResponse(AcknowledgedResponse acknowledgedResponse) { - // even if not all nodes acked the delete request yet we can consider this operation as successful as - // we'll generate a new index name and attempt to create an index with the newly generated name - listener.onResponse(null); - } - - @Override - public void onFailure(Exception e) { - if (e instanceof IndexNotFoundException) { - // we can move on if the index was deleted in the meantime - listener.onResponse(null); - } else { - listener.onFailure(e); - } - } - }); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - CleanupTargetIndexStep that = (CleanupTargetIndexStep) o; - return super.equals(o) - && Objects.equals(targetIndexNameSupplier, that.targetIndexNameSupplier) - && Objects.equals(sourceIndexNameSupplier, that.sourceIndexNameSupplier); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), targetIndexNameSupplier, sourceIndexNameSupplier); - } -} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DownsampleAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DownsampleAction.java index e507ba6de863a..1cde2b3101f10 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DownsampleAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DownsampleAction.java @@ -52,6 +52,7 @@ public class DownsampleAction implements LifecycleAction { public static final TimeValue DEFAULT_WAIT_TIMEOUT = new TimeValue(1, TimeUnit.DAYS); private static final ParseField FIXED_INTERVAL_FIELD = new ParseField(DownsampleConfig.FIXED_INTERVAL); private static final ParseField WAIT_TIMEOUT_FIELD = new ParseField("wait_timeout"); + static final String BWC_CLEANUP_TARGET_INDEX_NAME = "cleanup-target-index"; private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( NAME, @@ -141,7 +142,7 @@ public List toSteps(Client client, String phase, StepKey nextStepKey) { StepKey waitForNoFollowerStepKey = new StepKey(phase, NAME, WaitForNoFollowersStep.NAME); StepKey waitTimeSeriesEndTimePassesKey = new StepKey(phase, NAME, WaitUntilTimeSeriesEndTimePassesStep.NAME); StepKey readOnlyKey = new StepKey(phase, NAME, ReadOnlyStep.NAME); - StepKey cleanupDownsampleIndexKey = new StepKey(phase, NAME, CleanupTargetIndexStep.NAME); + StepKey cleanupDownsampleIndexKey = new StepKey(phase, NAME, BWC_CLEANUP_TARGET_INDEX_NAME); StepKey generateDownsampleIndexNameKey = new StepKey(phase, NAME, DownsamplePrepareLifeCycleStateStep.NAME); StepKey downsampleKey = new StepKey(phase, NAME, DownsampleStep.NAME); StepKey waitForDownsampleIndexKey = new StepKey(phase, NAME, WaitForIndexColorStep.NAME); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/CleanupTargetIndexStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/CleanupTargetIndexStepTests.java deleted file mode 100644 index 679fd8835a648..0000000000000 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/CleanupTargetIndexStepTests.java +++ /dev/null @@ -1,197 +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.ActionListener; -import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.ActionType; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; -import org.elasticsearch.action.admin.indices.delete.TransportDeleteIndexAction; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.metadata.IndexMetadata; -import org.elasticsearch.cluster.metadata.LifecycleExecutionState; -import org.elasticsearch.cluster.metadata.Metadata; -import org.elasticsearch.index.IndexVersion; -import org.elasticsearch.test.client.NoOpClient; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.xpack.core.ilm.Step.StepKey; - -import java.util.Map; -import java.util.function.Function; - -import static org.elasticsearch.common.IndexNameGenerator.generateValidIndexName; -import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.is; - -public class CleanupTargetIndexStepTests extends AbstractStepTestCase { - - @Override - public CleanupTargetIndexStep createRandomInstance() { - StepKey stepKey = randomStepKey(); - StepKey nextStepKey = randomStepKey(); - - return new CleanupTargetIndexStep( - stepKey, - nextStepKey, - client, - (indexMetadata) -> randomAlphaOfLengthBetween(1, 10), - (indexMetadata) -> randomAlphaOfLengthBetween(1, 10) - ); - } - - @Override - protected CleanupTargetIndexStep copyInstance(CleanupTargetIndexStep instance) { - return new CleanupTargetIndexStep( - instance.getKey(), - instance.getNextStepKey(), - instance.getClient(), - instance.getSourceIndexNameSupplier(), - instance.getTargetIndexNameSupplier() - ); - } - - @Override - public CleanupTargetIndexStep mutateInstance(CleanupTargetIndexStep instance) { - StepKey key = instance.getKey(); - StepKey nextKey = instance.getNextStepKey(); - Function sourceIndexNameSupplier = instance.getSourceIndexNameSupplier(); - Function targetIndexNameSupplier = instance.getTargetIndexNameSupplier(); - - switch (between(0, 3)) { - case 0 -> key = new StepKey(key.phase(), key.action(), key.name() + randomAlphaOfLength(5)); - case 1 -> nextKey = new StepKey(nextKey.phase(), nextKey.action(), nextKey.name() + randomAlphaOfLength(5)); - case 2 -> sourceIndexNameSupplier = (indexMetadata) -> randomAlphaOfLengthBetween(11, 15) + indexMetadata.getIndex().getName(); - case 3 -> targetIndexNameSupplier = (indexMetadata) -> randomAlphaOfLengthBetween(11, 15) + indexMetadata.getIndex().getName(); - default -> throw new AssertionError("Illegal randomisation branch"); - } - return new CleanupTargetIndexStep(key, nextKey, instance.getClient(), sourceIndexNameSupplier, targetIndexNameSupplier); - } - - public void testPerformActionDoesntFailIfShrinkingIndexNameIsMissing() { - String indexName = randomAlphaOfLength(10); - String policyName = "test-ilm-policy"; - - IndexMetadata.Builder indexMetadataBuilder = IndexMetadata.builder(indexName) - .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, policyName)) - .numberOfShards(randomIntBetween(1, 5)) - .numberOfReplicas(randomIntBetween(0, 5)); - - IndexMetadata indexMetadata = indexMetadataBuilder.build(); - ClusterState clusterState = ClusterState.builder(emptyClusterState()) - .metadata(Metadata.builder().put(indexMetadata, true).build()) - .build(); - - CleanupTargetIndexStep cleanupShrinkIndexStep = createRandomInstance(); - cleanupShrinkIndexStep.performAction(indexMetadata, clusterState, null, new ActionListener<>() { - @Override - public void onResponse(Void unused) {} - - @Override - public void onFailure(Exception e) { - fail( - "expecting the step to not report any failure if there isn't any shrink index name stored in the ILM execution " - + "state but got:" - + e.getMessage() - ); - } - }); - } - - public void testPerformAction() { - String indexName = randomAlphaOfLength(10); - String policyName = "test-ilm-policy"; - String shrinkIndexName = generateValidIndexName("shrink-", indexName); - Map ilmCustom = Map.of("shrink_index_name", shrinkIndexName); - - IndexMetadata.Builder indexMetadataBuilder = IndexMetadata.builder(indexName) - .settings(settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, policyName)) - .putCustom(LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY, ilmCustom) - .numberOfShards(randomIntBetween(1, 5)) - .numberOfReplicas(randomIntBetween(0, 5)); - IndexMetadata indexMetadata = indexMetadataBuilder.build(); - - ClusterState clusterState = ClusterState.builder(emptyClusterState()) - .metadata(Metadata.builder().put(indexMetadata, true).build()) - .build(); - - try (var threadPool = createThreadPool()) { - final var client = getDeleteIndexRequestAssertingClient(threadPool, shrinkIndexName); - CleanupTargetIndexStep step = new CleanupTargetIndexStep( - randomStepKey(), - randomStepKey(), - client, - (metadata) -> indexName, - (metadata) -> shrinkIndexName - ); - step.performAction(indexMetadata, clusterState, null, ActionListener.noop()); - } - } - - public void testDeleteSkippedIfManagedIndexIsShrunkAndSourceDoesntExist() { - String sourceIndex = randomAlphaOfLength(10); - String policyName = "test-ilm-policy"; - String shrinkIndexName = generateValidIndexName("shrink-", sourceIndex); - Map ilmCustom = Map.of("shrink_index_name", shrinkIndexName); - - IndexMetadata.Builder shrunkIndexMetadataBuilder = IndexMetadata.builder(shrinkIndexName) - .settings( - settings(IndexVersion.current()).put(LifecycleSettings.LIFECYCLE_NAME, policyName) - .put(IndexMetadata.INDEX_RESIZE_SOURCE_NAME_KEY, sourceIndex) - ) - .putCustom(LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY, ilmCustom) - .numberOfShards(randomIntBetween(1, 5)) - .numberOfReplicas(randomIntBetween(0, 5)); - IndexMetadata shrunkIndexMetadata = shrunkIndexMetadataBuilder.build(); - - ClusterState clusterState = ClusterState.builder(emptyClusterState()) - .metadata(Metadata.builder().put(shrunkIndexMetadata, true).build()) - .build(); - - try (var threadPool = createThreadPool()) { - final var client = getFailingIfCalledClient(threadPool); - CleanupTargetIndexStep step = new CleanupTargetIndexStep( - randomStepKey(), - randomStepKey(), - client, - (metadata) -> sourceIndex, - (metadata) -> shrinkIndexName - ); - step.performAction(shrunkIndexMetadata, clusterState, null, ActionListener.noop()); - } - } - - private NoOpClient getDeleteIndexRequestAssertingClient(ThreadPool threadPool, String shrinkIndexName) { - return new NoOpClient(threadPool) { - @Override - protected void doExecute( - ActionType action, - Request request, - ActionListener listener - ) { - assertThat(action.name(), is(TransportDeleteIndexAction.TYPE.name())); - assertTrue(request instanceof DeleteIndexRequest); - assertThat(((DeleteIndexRequest) request).indices(), arrayContaining(shrinkIndexName)); - } - }; - } - - private NoOpClient getFailingIfCalledClient(ThreadPool threadPool) { - return new NoOpClient(threadPool) { - @Override - protected void doExecute( - ActionType action, - Request request, - ActionListener listener - ) { - throw new IllegalStateException( - "not expecting client to be called, but received request [" + request + "] for action [" + action + "]" - ); - } - }; - } -} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DownsampleActionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DownsampleActionTests.java index e030b9697902d..b8d5c0025d48a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DownsampleActionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DownsampleActionTests.java @@ -99,7 +99,7 @@ public void testToSteps() { assertThat(steps.get(4).getNextStepKey().name(), equalTo(DownsamplePrepareLifeCycleStateStep.NAME)); assertTrue(steps.get(5) instanceof NoopStep); - assertThat(steps.get(5).getKey().name(), equalTo(CleanupTargetIndexStep.NAME)); + assertThat(steps.get(5).getKey().name(), equalTo(DownsampleAction.BWC_CLEANUP_TARGET_INDEX_NAME)); assertThat(steps.get(5).getNextStepKey().name(), equalTo(DownsampleStep.NAME)); assertTrue(steps.get(6) instanceof DownsamplePrepareLifeCycleStateStep);