diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicy.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicy.java index aa16473f2111f..795a83f64444a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicy.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicy.java @@ -174,9 +174,7 @@ public List toSteps(Client client) { List orderedPhases = type.getOrderedPhases(phases); ListIterator phaseIterator = orderedPhases.listIterator(orderedPhases.size()); - // final step so that policy can properly update cluster-state with last action completed - steps.add(TerminalPolicyStep.INSTANCE); - Step.StepKey lastStepKey = TerminalPolicyStep.KEY; + Step.StepKey lastStepKey = null; Phase phase = null; // add steps for each phase, in reverse @@ -185,7 +183,7 @@ public List toSteps(Client client) { Phase previousPhase = phaseIterator.previous(); // add `after` step for phase before next - if (phase != null) { + if (previousPhase != null) { // after step should have the name of the previous phase since the index is still in the // previous phase until the after condition is reached Step.StepKey afterStepKey = new Step.StepKey(previousPhase.getName(), PhaseCompleteStep.NAME, PhaseCompleteStep.NAME); @@ -210,13 +208,11 @@ public List toSteps(Client client) { } } - if (phase != null) { - // The very first after step is in a phase before the hot phase so call this "new" - Step.StepKey afterStepKey = new Step.StepKey("new", PhaseCompleteStep.NAME, PhaseCompleteStep.NAME); - Step phaseAfterStep = new PhaseCompleteStep(afterStepKey, lastStepKey); - steps.add(phaseAfterStep); - lastStepKey = phaseAfterStep.getKey(); - } + // The very first after step is in a phase before the hot phase so call this "new" + Step.StepKey afterStepKey = new Step.StepKey("new", PhaseCompleteStep.NAME, PhaseCompleteStep.NAME); + Step phaseAfterStep = new PhaseCompleteStep(afterStepKey, lastStepKey); + steps.add(phaseAfterStep); + lastStepKey = phaseAfterStep.getKey(); // init step so that policy is guaranteed to have steps.add(new InitializePolicyContextStep(InitializePolicyContextStep.KEY, lastStepKey)); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/PhaseCompleteStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/PhaseCompleteStep.java index 51210a38637f2..36795816a4b09 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/PhaseCompleteStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/PhaseCompleteStep.java @@ -15,4 +15,8 @@ public class PhaseCompleteStep extends Step { public PhaseCompleteStep(StepKey key, StepKey nextStepKey) { super(key, nextStepKey); } + + public static PhaseCompleteStep finalStep(String phase) { + return new PhaseCompleteStep(new StepKey(phase, NAME, NAME), null); + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java index 37b268c499dbb..f965ee509e135 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java @@ -247,14 +247,14 @@ public void testFirstAndLastSteps() { assertThat(steps.size(), equalTo(2)); assertThat(steps.get(0), instanceOf(InitializePolicyContextStep.class)); assertThat(steps.get(0).getKey(), equalTo(new StepKey("new", "init", "init"))); - assertThat(steps.get(0).getNextStepKey(), equalTo(TerminalPolicyStep.KEY)); - assertSame(steps.get(1), TerminalPolicyStep.INSTANCE); + assertThat(steps.get(0).getNextStepKey(), equalTo(PhaseCompleteStep.finalStep("new").getKey())); + assertThat(steps.get(1), equalTo(PhaseCompleteStep.finalStep("new"))); } public void testToStepsWithOneStep() { Client client = mock(Client.class); MockStep mockStep = new MockStep( - new Step.StepKey("test", "test", "test"), TerminalPolicyStep.KEY); + new Step.StepKey("test", "test", "test"), PhaseCompleteStep.finalStep("test").getKey()); lifecycleName = randomAlphaOfLengthBetween(1, 20); Map phases = new LinkedHashMap<>(); @@ -264,7 +264,7 @@ public void testToStepsWithOneStep() { phases.put(firstPhase.getName(), firstPhase); LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases); StepKey firstStepKey = InitializePolicyContextStep.KEY; - StepKey secondStepKey = new StepKey("new", PhaseCompleteStep.NAME, PhaseCompleteStep.NAME); + StepKey secondStepKey = PhaseCompleteStep.finalStep("new").getKey(); List steps = policy.toSteps(client); assertThat(steps.size(), equalTo(4)); assertSame(steps.get(0).getKey(), firstStepKey); @@ -272,13 +272,14 @@ public void testToStepsWithOneStep() { assertThat(steps.get(1).getKey(), equalTo(secondStepKey)); assertThat(steps.get(1).getNextStepKey(), equalTo(mockStep.getKey())); assertThat(steps.get(2).getKey(), equalTo(mockStep.getKey())); - assertThat(steps.get(2).getNextStepKey(), equalTo(TerminalPolicyStep.KEY)); - assertSame(steps.get(3), TerminalPolicyStep.INSTANCE); + assertThat(steps.get(2).getNextStepKey(), equalTo(PhaseCompleteStep.finalStep("test").getKey())); + assertThat(steps.get(3), equalTo(PhaseCompleteStep.finalStep("test"))); } public void testToStepsWithTwoPhases() { Client client = mock(Client.class); - MockStep secondActionStep = new MockStep(new StepKey("second_phase", "test2", "test"), TerminalPolicyStep.KEY); + MockStep secondActionStep = new MockStep(new StepKey("second_phase", "test2", "test"), + PhaseCompleteStep.finalStep("second_phase").getKey()); MockStep secondAfter = new MockStep(new StepKey("first_phase", PhaseCompleteStep.NAME, PhaseCompleteStep.NAME), secondActionStep.getKey()); MockStep firstActionAnotherStep = new MockStep(new StepKey("first_phase", "test", "bar"), secondAfter.getKey()); @@ -312,7 +313,7 @@ public void testToStepsWithTwoPhases() { assertThat(steps.get(4).getKey(), equalTo(secondAfter.getKey())); assertThat(steps.get(4).getNextStepKey(), equalTo(secondAfter.getNextStepKey())); assertThat(steps.get(5), equalTo(secondActionStep)); - assertSame(steps.get(6), TerminalPolicyStep.INSTANCE); + assertThat(steps.get(6), equalTo(PhaseCompleteStep.finalStep("second_phase"))); } public void testIsActionSafe() { diff --git a/x-pack/plugin/ilm/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ilm/CCRIndexLifecycleIT.java b/x-pack/plugin/ilm/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ilm/CCRIndexLifecycleIT.java index 24123735091d5..80910b848e0a3 100644 --- a/x-pack/plugin/ilm/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ilm/CCRIndexLifecycleIT.java +++ b/x-pack/plugin/ilm/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ilm/CCRIndexLifecycleIT.java @@ -146,7 +146,8 @@ public void testCCRUnfollowDuringSnapshot() throws Exception { .build()); // start snapshot - request = new Request("PUT", "/_snapshot/repo/snapshot"); + String snapName = "snapshot-" + randomAlphaOfLength(10).toLowerCase(Locale.ROOT); + request = new Request("PUT", "/_snapshot/repo/" + snapName); request.addParameter("wait_for_completion", "false"); request.setJsonEntity("{\"indices\": \"" + indexName + "\"}"); assertOK(client().performRequest(request)); @@ -165,14 +166,14 @@ public void testCCRUnfollowDuringSnapshot() throws Exception { // Following index should have the document assertDocumentExists(client(), indexName, "1"); // ILM should have completed the unfollow - assertILMPolicy(client(), indexName, "unfollow-only", "completed"); + assertILMPolicy(client(), indexName, "unfollow-only", "hot", "complete", "complete"); }, 2, TimeUnit.MINUTES); // assert that snapshot succeeded - assertThat(getSnapshotState("snapshot"), equalTo("SUCCESS")); - assertOK(client().performRequest(new Request("DELETE", "/_snapshot/repo/snapshot"))); + assertThat(getSnapshotState(snapName), equalTo("SUCCESS")); + assertOK(client().performRequest(new Request("DELETE", "/_snapshot/repo/" + snapName))); ResponseException e = expectThrows(ResponseException.class, - () -> client().performRequest(new Request("GET", "/_snapshot/repo/snapshot"))); + () -> client().performRequest(new Request("GET", "/_snapshot/repo/" + snapName))); assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(404)); } } else { @@ -344,7 +345,7 @@ public void testAliasReplicatedOnShrink() throws Exception { assertBusy(() -> assertOK(client().performRequest(new Request("HEAD", "/" + shrunkenIndexName + "/_alias/" + indexName)))); // Wait for the index to complete its policy - assertBusy(() -> assertILMPolicy(client(), shrunkenIndexName, policyName, "completed", "completed", "completed")); + assertBusy(() -> assertILMPolicy(client(), shrunkenIndexName, policyName, null, "complete", "complete")); } } @@ -391,7 +392,7 @@ public void testUnfollowInjectedBeforeShrink() throws Exception { assertBusy(() -> assertTrue(indexExists(shrunkenIndexName))); // Wait for the index to complete its policy - assertBusy(() -> assertILMPolicy(client(), shrunkenIndexName, policyName, "completed", "completed", "completed")); + assertBusy(() -> assertILMPolicy(client(), shrunkenIndexName, policyName, null, "complete", "complete")); } } @@ -461,8 +462,8 @@ public void testCannotShrinkLeaderIndex() throws Exception { assertEquals(RestStatus.OK.getStatus(), shrunkenIndexExistsResponse.getStatusLine().getStatusCode()); // And both of these should now finish their policies - assertILMPolicy(leaderClient, shrunkenIndexName, policyName, "completed"); - assertILMPolicy(client(), indexName, policyName, "completed"); + assertILMPolicy(leaderClient, shrunkenIndexName, policyName, null, "complete", "complete"); + assertILMPolicy(client(), indexName, policyName, "hot", "complete", "complete"); }); } } else { @@ -542,7 +543,7 @@ public void testILMUnfollowFailsToRemoveRetentionLeases() throws Exception { client().performRequest(new Request("POST", "/_ilm/start")); // Wait for the policy to be complete assertBusy(() -> { - assertILMPolicy(client(), followerIndex, policyName, "completed", "completed", "completed"); + assertILMPolicy(client(), followerIndex, policyName, "hot", "complete", "complete"); }); // Ensure the "follower" index has successfully unfollowed diff --git a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/ChangePolicyforIndexIT.java b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/ChangePolicyforIndexIT.java index b3d5052d042e7..761a43e11261c 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/ChangePolicyforIndexIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/ChangePolicyforIndexIT.java @@ -20,9 +20,9 @@ import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; import org.elasticsearch.xpack.core.ilm.Phase; +import org.elasticsearch.xpack.core.ilm.PhaseCompleteStep; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.Step.StepKey; -import org.elasticsearch.xpack.core.ilm.TerminalPolicyStep; import org.elasticsearch.xpack.core.ilm.WaitForRolloverReadyStep; import java.io.IOException; @@ -113,7 +113,7 @@ public void testChangePolicyForIndex() throws Exception { assertOK(client().performRequest(request)); // Check the index goes to the warm phase and completes - assertBusy(() -> assertStep(indexName, TerminalPolicyStep.KEY), 30, TimeUnit.SECONDS); + assertBusy(() -> assertStep(indexName, PhaseCompleteStep.finalStep("warm").getKey()), 30, TimeUnit.SECONDS); // Check index is allocated on integTest-1 and integTest-2 as per policy_2 Request getSettingsRequest = new Request("GET", "/" + indexName + "/_settings"); diff --git a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java index 01cf7dccef43d..a9451fabaccb0 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java @@ -35,6 +35,7 @@ import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; import org.elasticsearch.xpack.core.ilm.Phase; +import org.elasticsearch.xpack.core.ilm.PhaseCompleteStep; import org.elasticsearch.xpack.core.ilm.ReadOnlyAction; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.SetPriorityAction; @@ -42,7 +43,6 @@ import org.elasticsearch.xpack.core.ilm.ShrinkStep; import org.elasticsearch.xpack.core.ilm.Step; import org.elasticsearch.xpack.core.ilm.Step.StepKey; -import org.elasticsearch.xpack.core.ilm.TerminalPolicyStep; import org.elasticsearch.xpack.core.ilm.UpdateRolloverLifecycleDateStep; import org.elasticsearch.xpack.core.ilm.WaitForActiveShardsStep; import org.elasticsearch.xpack.core.ilm.WaitForRolloverReadyStep; @@ -229,7 +229,7 @@ public void testRetryFailedShrinkAction() throws Exception { // assert corrected policy is picked up and index is shrunken assertBusy(() -> assertTrue(indexExists(shrunkenIndex)), 30, TimeUnit.SECONDS); assertBusy(() -> assertTrue(aliasExists(shrunkenIndex, index))); - assertBusy(() -> assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(PhaseCompleteStep.finalStep("warm").getKey()))); assertBusy(() -> { Map settings = getOnlyIndexSettings(shrunkenIndex); assertThat(settings.get(IndexMetaData.SETTING_NUMBER_OF_SHARDS), equalTo(String.valueOf(expectedFinalShards))); @@ -291,7 +291,7 @@ public void testRolloverActionWithIndexingComplete() throws Exception { updatePolicy(originalIndex, policy); // index document {"foo": "bar"} to trigger rollover index(client(), originalIndex, "_id", "foo", "bar"); - assertBusy(() -> assertEquals(TerminalPolicyStep.KEY, getStepKeyForIndex(originalIndex))); + assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); assertBusy(() -> assertTrue(indexExists(originalIndex))); assertBusy(() -> assertFalse(indexExists(secondIndex))); assertBusy(() -> assertEquals("true", getOnlyIndexSettings(originalIndex).get(LifecycleSettings.LIFECYCLE_INDEXING_COMPLETE))); @@ -302,10 +302,11 @@ public void testAllocateOnlyAllocation() throws Exception { .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)); String allocateNodeName = "integTest-" + randomFrom(0, 1); AllocateAction allocateAction = new AllocateAction(null, null, null, singletonMap("_name", allocateNodeName)); - createNewSingletonPolicy(randomFrom("warm", "cold"), allocateAction); + String endPhase = randomFrom("warm", "cold"); + createNewSingletonPolicy(endPhase, allocateAction); updatePolicy(index, policy); assertBusy(() -> { - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep(endPhase).getKey())); }); ensureGreen(index); } @@ -317,11 +318,12 @@ public void testAllocateActionOnlyReplicas() throws Exception { createIndexWithSettings(index, Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, numShards) .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, numReplicas)); AllocateAction allocateAction = new AllocateAction(finalNumReplicas, null, null, null); - createNewSingletonPolicy(randomFrom("warm", "cold"), allocateAction); + String endPhase = randomFrom("warm", "cold"); + createNewSingletonPolicy(endPhase, allocateAction); updatePolicy(index, policy); assertBusy(() -> { Map settings = getOnlyIndexSettings(index); - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep(endPhase).getKey())); assertThat(settings.get(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey()), equalTo(String.valueOf(finalNumReplicas))); }); } @@ -453,7 +455,7 @@ public void testReadOnly() throws Exception { updatePolicy(index, policy); assertBusy(() -> { Map settings = getOnlyIndexSettings(index); - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("warm").getKey())); assertThat(settings.get(IndexMetaData.INDEX_BLOCKS_WRITE_SETTING.getKey()), equalTo("true")); }); } @@ -487,7 +489,7 @@ public void testForceMergeAction() throws Exception { updatePolicy(index, policy); assertBusy(() -> { - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("warm").getKey())); Map settings = getOnlyIndexSettings(index); assertThat(numSegments.get(), equalTo(1)); assertThat(settings.get(IndexMetaData.INDEX_BLOCKS_WRITE_SETTING.getKey()), equalTo("true")); @@ -506,7 +508,7 @@ public void testShrinkAction() throws Exception { updatePolicy(index, policy); assertBusy(() -> assertTrue(indexExists(shrunkenIndex)), 30, TimeUnit.SECONDS); assertBusy(() -> assertTrue(aliasExists(shrunkenIndex, index))); - assertBusy(() -> assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(PhaseCompleteStep.finalStep("warm").getKey()))); assertBusy(() -> { Map settings = getOnlyIndexSettings(shrunkenIndex); assertThat(settings.get(IndexMetaData.SETTING_NUMBER_OF_SHARDS), equalTo(String.valueOf(expectedFinalShards))); @@ -528,7 +530,7 @@ public void testShrinkSameShards() throws Exception { assertFalse(indexExists(shrunkenIndex)); assertFalse(aliasExists(shrunkenIndex, index)); Map settings = getOnlyIndexSettings(index); - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("warm").getKey())); assertThat(settings.get(IndexMetaData.SETTING_NUMBER_OF_SHARDS), equalTo(String.valueOf(numberOfShards))); assertNull(settings.get(IndexMetaData.INDEX_BLOCKS_WRITE_SETTING.getKey())); assertThat(settings.get(IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_id"), nullValue()); @@ -572,7 +574,7 @@ public void testShrinkDuringSnapshot() throws Exception { assertTrue(indexExists(shrunkenIndex)); assertTrue(aliasExists(shrunkenIndex, index)); Map settings = getOnlyIndexSettings(shrunkenIndex); - assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(PhaseCompleteStep.finalStep("warm").getKey())); assertThat(settings.get(IndexMetaData.SETTING_NUMBER_OF_SHARDS), equalTo(String.valueOf(1))); assertThat(settings.get(IndexMetaData.INDEX_BLOCKS_WRITE_SETTING.getKey()), equalTo("true")); assertThat(settings.get(IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_id"), nullValue()); @@ -593,7 +595,7 @@ public void testFreezeAction() throws Exception { updatePolicy(index, policy); assertBusy(() -> { Map settings = getOnlyIndexSettings(index); - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("cold").getKey())); assertThat(settings.get(IndexMetaData.INDEX_BLOCKS_WRITE_SETTING.getKey()), equalTo("true")); assertThat(settings.get(IndexSettings.INDEX_SEARCH_THROTTLED.getKey()), equalTo("true")); assertThat(settings.get("index.frozen"), equalTo("true")); @@ -631,7 +633,7 @@ public void testFreezeDuringSnapshot() throws Exception { // assert that the index froze assertBusy(() -> { Map settings = getOnlyIndexSettings(index); - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("cold").getKey())); assertThat(settings.get(IndexMetaData.INDEX_BLOCKS_WRITE_SETTING.getKey()), equalTo("true")); assertThat(settings.get(IndexSettings.INDEX_SEARCH_THROTTLED.getKey()), equalTo("true")); assertThat(settings.get("index.frozen"), equalTo("true")); @@ -652,7 +654,7 @@ public void testSetPriority() throws Exception { updatePolicy(index, policy); assertBusy(() -> { Map settings = getOnlyIndexSettings(index); - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("warm").getKey())); assertThat(settings.get(IndexMetaData.INDEX_PRIORITY_SETTING.getKey()), equalTo(String.valueOf(priority))); }); } @@ -664,7 +666,7 @@ public void testSetNullPriority() throws Exception { updatePolicy(index, policy); assertBusy(() -> { Map settings = getOnlyIndexSettings(index); - assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("warm").getKey())); assertNull(settings.get(IndexMetaData.INDEX_PRIORITY_SETTING.getKey())); }); } @@ -812,7 +814,7 @@ public void testRemoveAndReaddPolicy() throws Exception { assertBusy(() -> assertTrue((boolean) explainIndex(originalIndex).getOrDefault("managed", false))); // Wait for everything to be copacetic - assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); } public void testMoveToInjectedStep() throws Exception { @@ -847,7 +849,7 @@ public void testMoveToInjectedStep() throws Exception { assertBusy(() -> { assertTrue(indexExists(shrunkenIndex)); assertTrue(aliasExists(shrunkenIndex, index)); - assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(TerminalPolicyStep.KEY)); + assertThat(getStepKeyForIndex(shrunkenIndex), equalTo(PhaseCompleteStep.finalStep("warm").getKey())); }); } @@ -1012,7 +1014,7 @@ public void testILMRolloverRetriesOnReadOnlyBlock() throws Exception { client().performRequest(allowWritesOnIndexSettingUpdate); // index is not readonly so the ILM should complete successfully - assertBusy(() -> assertThat(getStepKeyForIndex(firstIndex), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(firstIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); } public void testILMRolloverOnManuallyRolledIndex() throws Exception { @@ -1062,7 +1064,7 @@ public void testILMRolloverOnManuallyRolledIndex() throws Exception { client().performRequest(refreshOriginalIndex); // Wait for the rollover policy to execute - assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); // ILM should manage the second index after attempting (and skipping) rolling the original index assertBusy(() -> assertTrue((boolean) explainIndex(secondIndex).getOrDefault("managed", true))); @@ -1075,7 +1077,7 @@ public void testILMRolloverOnManuallyRolledIndex() throws Exception { client().performRequest(refreshSecondIndex).getStatusLine(); // ILM should rollover the second index even though it skipped the first one - assertBusy(() -> assertThat(getStepKeyForIndex(secondIndex), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(secondIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); assertBusy(() -> assertTrue(indexExists(thirdIndex))); } @@ -1149,7 +1151,7 @@ public void testRolloverStepRetriesUntilRolledOverIndexIsDeleted() throws Except // the rollover step should eventually succeed assertBusy(() -> assertThat(indexExists(rolledIndex), is(true))); - assertBusy(() -> assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); } public void testUpdateRolloverLifecycleDateStepRetriesWhenRolloverInfoIsMissing() throws Exception { @@ -1210,7 +1212,7 @@ public void testUpdateRolloverLifecycleDateStepRetriesWhenRolloverInfoIsMissing( "}" ); client().performRequest(rolloverRequest); - assertBusy(() -> assertThat(getStepKeyForIndex(index), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); } public void testWaitForActiveShardsStep() throws Exception { @@ -1245,7 +1247,7 @@ public void testWaitForActiveShardsStep() throws Exception { // reset the number of replicas to 0 so that the second index wait for active shard condition can be met updateIndexSettings(secondIndex, Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)); - assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); } @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/50353") @@ -1275,7 +1277,7 @@ public void testHistoryIsWrittenWithSuccess() throws Exception { Request refreshIndex = new Request("POST", "/" + index + "-1/_refresh"); client().performRequest(refreshIndex); - assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1"), equalTo(TerminalPolicyStep.KEY))); + assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1"), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "wait-for-indexing-complete"), 30, TimeUnit.SECONDS); assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "wait-for-follow-shard-tasks"), 30, TimeUnit.SECONDS); @@ -1398,7 +1400,7 @@ public void testRetryableInitializationStep() throws Exception { assertBusy(() -> { Map explainResp = explainIndex(index); String phase = (String) explainResp.get("phase"); - assertThat(phase, equalTo(TerminalPolicyStep.COMPLETED_PHASE)); + assertThat(phase, equalTo("hot")); }); } @@ -1434,7 +1436,44 @@ public void testRefreshablePhaseJson() throws Exception { // Index should now have been able to roll over, creating the new index and proceeding to the "complete" step assertBusy(() -> assertThat(indexExists(index + "-000002"), is(true))); - assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1").getName(), equalTo(TerminalPolicyStep.KEY.getName()))); + assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1").getName(), equalTo(PhaseCompleteStep.NAME))); + } + + public void testHaltAtEndOfPhase() throws Exception { + String index = "halt-index"; + + createNewSingletonPolicy("hot", new SetPriorityAction(100)); + + createIndexWithSettings(index, + Settings.builder() + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put(LifecycleSettings.LIFECYCLE_NAME, policy), + randomBoolean()); + + // Wait for the index to finish the "hot" phase + assertBusy(() -> assertThat(getStepKeyForIndex(index), equalTo(PhaseCompleteStep.finalStep("hot").getKey()))); + + // Update the policy to add a delete phase + { + Map hotActions = new HashMap<>(); + hotActions.put(SetPriorityAction.NAME, new SetPriorityAction(100)); + Map phases = new HashMap<>(); + phases.put("hot", new Phase("hot", TimeValue.ZERO, hotActions)); + phases.put("delete", new Phase("delete", TimeValue.ZERO, singletonMap(DeleteAction.NAME, new DeleteAction()))); + LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, phases); + // PUT policy + XContentBuilder builder = jsonBuilder(); + lifecyclePolicy.toXContent(builder, null); + final StringEntity entity = new StringEntity( + "{ \"policy\":" + Strings.toString(builder) + "}", ContentType.APPLICATION_JSON); + Request request = new Request("PUT", "_ilm/policy/" + policy); + request.setEntity(entity); + assertOK(client().performRequest(request)); + } + + // The index should move into the deleted phase and be deleted + assertBusy(() -> assertFalse("expected " + index + " to be deleted by ILM", indexExists(index))); } // This method should be called inside an assertBusy, it has no retry logic of its own diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/ExecuteStepsUpdateTask.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/ExecuteStepsUpdateTask.java index b97944fe67bf7..f7710436bcdb4 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/ExecuteStepsUpdateTask.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/ExecuteStepsUpdateTask.java @@ -140,7 +140,7 @@ public ClusterState execute(final ClusterState currentState) throws IOException if (logger.isTraceEnabled()) { logger.trace("[{}] condition not met ({}) [{}], returning existing state (info: {})", index.getName(), currentStep.getClass().getSimpleName(), currentStep.getKey(), - Strings.toString(stepInfo)); + stepInfo == null ? "null" : Strings.toString(stepInfo)); } // We may have executed a step and set "nextStepKey" to // a value, but in this case, since the condition was diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunner.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunner.java index f18bd7c17c5ab..cf0e15838e3d0 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunner.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunner.java @@ -143,6 +143,11 @@ void runPeriodicStep(String policy, IndexMetaData indexMetaData) { index, currentStep.getClass().getSimpleName(), currentStep.getKey()); // Only phase changing and async wait steps should be run through periodic polling if (currentStep instanceof PhaseCompleteStep) { + if (currentStep.getNextStepKey() == null) { + logger.debug("[{}] stopping in the current phase ({}) as there are no more steps in the policy", + index, currentStep.getKey().getPhase()); + return; + } // Only proceed to the next step if enough time has elapsed to go into the next phase if (isReadyToTransitionToThisPhase(policy, indexMetaData, currentStep.getNextStepKey().getPhase())) { moveToStep(indexMetaData.getIndex(), policy, currentStep.getKey(), currentStep.getNextStepKey()); @@ -314,6 +319,11 @@ void runPolicyAfterStateChange(String policy, IndexMetaData indexMetaData) { logger.trace("[{}] maybe running step ({}) after state change: {}", index, currentStep.getClass().getSimpleName(), currentStep.getKey()); if (currentStep instanceof PhaseCompleteStep) { + if (currentStep.getNextStepKey() == null) { + logger.debug("[{}] stopping in the current phase ({}) as there are no more steps in the policy", + index, currentStep.getKey().getPhase()); + return; + } // Only proceed to the next step if enough time has elapsed to go into the next phase if (isReadyToTransitionToThisPhase(policy, indexMetaData, currentStep.getNextStepKey().getPhase())) { moveToStep(indexMetaData.getIndex(), policy, currentStep.getKey(), currentStep.getNextStepKey()); diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransition.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransition.java index 0f76cd1630ea5..6b38515b10e0a 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransition.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransition.java @@ -78,7 +78,8 @@ public static void validateTransition(IndexMetaData idxMeta, Step.StepKey curren "], currently: [" + realKey + "]"); } - if (stepRegistry.stepExists(indexPolicySetting, newStepKey) == false) { + // Always allow moving to the terminal step, even if it doesn't exist in the policy + if (stepRegistry.stepExists(indexPolicySetting, newStepKey) == false && newStepKey.equals(TerminalPolicyStep.KEY) == false) { throw new IllegalArgumentException("step [" + newStepKey + "] for index [" + idxMeta.getIndex().getName() + "] with policy [" + indexPolicySetting + "] does not exist"); } diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleInitialisationTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleInitialisationTests.java index 2179898b5a4a7..3679696a8ea19 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleInitialisationTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleInitialisationTests.java @@ -5,11 +5,8 @@ */ package org.elasticsearch.xpack.ilm; -import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; -import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.routing.RoutingNode; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.NamedWriteable; @@ -40,10 +37,10 @@ import org.elasticsearch.xpack.core.ilm.MockAction; import org.elasticsearch.xpack.core.ilm.OperationMode; import org.elasticsearch.xpack.core.ilm.Phase; +import org.elasticsearch.xpack.core.ilm.PhaseCompleteStep; import org.elasticsearch.xpack.core.ilm.PhaseExecutionInfo; import org.elasticsearch.xpack.core.ilm.Step; import org.elasticsearch.xpack.core.ilm.StopILMRequest; -import org.elasticsearch.xpack.core.ilm.TerminalPolicyStep; import org.elasticsearch.xpack.core.ilm.action.ExplainLifecycleAction; import org.elasticsearch.xpack.core.ilm.action.GetLifecycleAction; import org.elasticsearch.xpack.core.ilm.action.GetStatusAction; @@ -65,7 +62,6 @@ import java.util.stream.Collectors; import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; -import static org.elasticsearch.client.Requests.clusterHealthRequest; import static org.elasticsearch.client.Requests.createIndexRequest; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; @@ -91,7 +87,8 @@ public class IndexLifecycleInitialisationTests extends ESIntegTestCase { static { List steps = new ArrayList<>(); Step.StepKey key = new Step.StepKey("mock", ObservableAction.NAME, ObservableClusterStateWaitStep.NAME); - steps.add(new ObservableClusterStateWaitStep(key, TerminalPolicyStep.KEY)); + Step.StepKey compKey = new Step.StepKey("mock", "complete", "complete"); + steps.add(new ObservableClusterStateWaitStep(key, compKey)); OBSERVABLE_ACTION = new ObservableAction(steps, true); } @@ -147,7 +144,9 @@ public void init() { .put(SETTING_NUMBER_OF_REPLICAS, 0).put(LifecycleSettings.LIFECYCLE_NAME, "test").build(); List steps = new ArrayList<>(); Step.StepKey key = new Step.StepKey("mock", ObservableAction.NAME, ObservableClusterStateWaitStep.NAME); - steps.add(new ObservableClusterStateWaitStep(key, TerminalPolicyStep.KEY)); + Step.StepKey compKey = new Step.StepKey("mock", "complete", "complete"); + steps.add(new ObservableClusterStateWaitStep(key, compKey)); + steps.add(new PhaseCompleteStep(compKey, null)); Map actions = Collections.singletonMap(ObservableAction.NAME, OBSERVABLE_ACTION); mockPhase = new Phase("mock", TimeValue.timeValueSeconds(0), actions); Map phases = Collections.singletonMap("mock", mockPhase); @@ -203,7 +202,7 @@ public void testSingleNodeCluster() throws Exception { assertBusy(() -> { LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(client().admin().cluster() .prepareState().execute().actionGet().getState().getMetaData().index("test")); - assertThat(lifecycleState.getStep(), equalTo(TerminalPolicyStep.KEY.getName())); + assertThat(lifecycleState.getStep(), equalTo("complete")); }); } @@ -270,11 +269,12 @@ public void testExplainExecution() throws Exception { .setSettings(Collections.singletonMap("index.lifecycle.test.complete", true)).get(); { - PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), null, 1L, actualModifiedDate); + Phase phase = new Phase("mock", TimeValue.ZERO, Collections.singletonMap("TEST_ACTION", OBSERVABLE_ACTION)); + PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), phase, 1L, actualModifiedDate); assertBusy(() -> { IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse("test"); - assertThat(indexResponse.getPhase(), equalTo(TerminalPolicyStep.COMPLETED_PHASE)); - assertThat(indexResponse.getStep(), equalTo(TerminalPolicyStep.KEY.getName())); + assertThat("expected to be in the 'mock' phase", indexResponse.getPhase(), equalTo("mock")); + assertThat("expected to be in the mock phase complete step", indexResponse.getStep(), equalTo("complete")); assertThat(indexResponse.getPhaseExecutionInfo(), equalTo(expectedExecutionInfo)); }); } @@ -395,80 +395,7 @@ public void testMasterDedicatedDataDedicated() throws Exception { assertBusy(() -> { LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(client().admin().cluster() .prepareState().execute().actionGet().getState().getMetaData().index("test")); - assertThat(lifecycleState.getStep(), equalTo(TerminalPolicyStep.KEY.getName())); - }); - } - - public void testMasterFailover() throws Exception { - // start one server - logger.info("Starting sever1"); - final String server_1 = internalCluster().startNode(); - final String node1 = getLocalNodeId(server_1); - - logger.info("Creating lifecycle [test_lifecycle]"); - PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); - PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); - assertAcked(putLifecycleResponse); - - logger.info("Creating index [test]"); - CreateIndexResponse createIndexResponse = client().admin().indices().create(createIndexRequest("test").settings(settings)) - .actionGet(); - assertAcked(createIndexResponse); - - ClusterState clusterState = client().admin().cluster().prepareState().get().getState(); - RoutingNode routingNodeEntry1 = clusterState.getRoutingNodes().node(node1); - assertThat(routingNodeEntry1.numberOfShardsWithState(STARTED), equalTo(1)); - - logger.info("Starting server2"); - // start another server - internalCluster().startNode(); - - // first wait for 2 nodes in the cluster - logger.info("Waiting for replicas to be assigned"); - ClusterHealthResponse clusterHealth = client().admin().cluster() - .health(clusterHealthRequest().waitForGreenStatus().waitForNodes("2")).actionGet(); - logger.info("Done Cluster Health, status {}", clusterHealth.getStatus()); - assertThat(clusterHealth.isTimedOut(), equalTo(false)); - assertThat(clusterHealth.getStatus(), equalTo(ClusterHealthStatus.GREEN)); - - // check step in progress in lifecycle - assertBusy(() -> { - LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(client().admin().cluster() - .prepareState().execute().actionGet().getState().getMetaData().index("test")); - assertThat(lifecycleState.getStep(), equalTo(ObservableClusterStateWaitStep.NAME)); - }); - - if (randomBoolean()) { - // this checks that the phase execution is picked up from the phase definition settings - logger.info("updating lifecycle [test_lifecycle] to be empty"); - PutLifecycleAction.Request updateLifecycleRequest = new PutLifecycleAction.Request - (newLockableLifecyclePolicy(lifecyclePolicy.getName(), Collections.emptyMap())); - PutLifecycleAction.Response updateLifecycleResponse = client() - .execute(PutLifecycleAction.INSTANCE, updateLifecycleRequest).get(); - assertAcked(updateLifecycleResponse); - } - - - logger.info("Closing server1"); - // kill the first server - internalCluster().stopCurrentMasterNode(); - - // check that index lifecycle picked back up where it - assertBusy(() -> { - LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(client().admin().cluster() - .prepareState().execute().actionGet().getState().getMetaData().index("test")); - assertThat(lifecycleState.getStep(), equalTo(ObservableClusterStateWaitStep.NAME)); - }); - - logger.info("new master is operation"); - // complete the step - AcknowledgedResponse repsonse = client().admin().indices().prepareUpdateSettings("test") - .setSettings(Collections.singletonMap("index.lifecycle.test.complete", true)).get(); - - assertBusy(() -> { - LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(client().admin().cluster() - .prepareState().execute().actionGet().getState().getMetaData().index("test")); - assertThat(lifecycleState.getStep(), equalTo(TerminalPolicyStep.KEY.getName())); + assertThat(lifecycleState.getStep(), equalTo("complete")); }); } diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java index 61c64d4fe501b..03c2b7b82fc9f 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java @@ -48,6 +48,7 @@ import org.elasticsearch.xpack.core.ilm.MockStep; import org.elasticsearch.xpack.core.ilm.OperationMode; import org.elasticsearch.xpack.core.ilm.Phase; +import org.elasticsearch.xpack.core.ilm.PhaseCompleteStep; import org.elasticsearch.xpack.core.ilm.PhaseExecutionInfo; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.RolloverActionTests; @@ -131,6 +132,38 @@ public void testRunPolicyTerminalPolicyStep() { Mockito.verifyZeroInteractions(clusterService); } + public void testRunPolicyPhaseCompletePolicyStep() { + String policyName = "async_action_policy"; + PhaseCompleteStep step = PhaseCompleteStep.finalStep(randomAlphaOfLength(4)); + PolicyStepsRegistry stepRegistry = createOneStepPolicyStepRegistry(policyName, step); + ClusterService clusterService = mock(ClusterService.class); + IndexLifecycleRunner runner = new IndexLifecycleRunner(stepRegistry, historyStore, clusterService, threadPool, () -> 0L); + IndexMetaData indexMetaData = IndexMetaData.builder("my_index").settings(settings(Version.CURRENT)) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + runner.runPolicyAfterStateChange(policyName, indexMetaData); + runner.runPeriodicStep(policyName, indexMetaData); + + Mockito.verifyZeroInteractions(clusterService); + } + + public void testRunPolicyPhaseCompleteWithMoreStepsPolicyStep() { + String policyName = "async_action_policy"; + TerminalPolicyStep stop = TerminalPolicyStep.INSTANCE; + PhaseCompleteStep step = new PhaseCompleteStep(new StepKey("cold", "complete", "complete"), stop.getKey()); + PolicyStepsRegistry stepRegistry = createOneStepPolicyStepRegistry(policyName, step); + ClusterService clusterService = mock(ClusterService.class); + IndexLifecycleRunner runner = new IndexLifecycleRunner(stepRegistry, historyStore, clusterService, threadPool, () -> 0L); + IndexMetaData indexMetaData = IndexMetaData.builder("my_index").settings(settings(Version.CURRENT)) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + runner.runPolicyAfterStateChange(policyName, indexMetaData); + runner.runPeriodicStep(policyName, indexMetaData); + + Mockito.verify(clusterService, times(2)).submitStateUpdateTask(any(), any()); + + } + public void testRunPolicyErrorStep() { String policyName = "async_action_policy"; LifecyclePolicy policy = LifecyclePolicyTests.randomTimeseriesLifecyclePolicyWithAllPhases(policyName);