From 49b2aaa87832035d7b6c96523718efaeb457730f Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Thu, 23 Nov 2017 18:16:47 +0000 Subject: [PATCH] Adds more tests --- .../xpack/indexlifecycle/DeleteAction.java | 3 +- .../IndexLifecycleInitialisationService.java | 5 +- .../xpack/indexlifecycle/LifecyclePolicy.java | 5 +- .../xpack/indexlifecycle/Phase.java | 46 ++- .../indexlifecycle/DeleteActionTests.java | 43 ++ .../xpack/indexlifecycle/MockAction.java | 99 +++++ .../xpack/indexlifecycle/PhaseTests.java | 378 ++++++++++++++++++ 7 files changed, 553 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockAction.java diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java index 98dfd1b4564..56dca29d757 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.indexlifecycle; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.Strings; @@ -54,7 +55,7 @@ public class DeleteAction extends LifecycleAction { @Override protected void execute(Index index, Client client) { - client.admin().indices().prepareDelete(index.getName()).execute(new ActionListener() { + client.admin().indices().delete(new DeleteIndexRequest(index.getName()), new ActionListener() { @Override public void onResponse(DeleteIndexResponse deleteIndexResponse) { logger.error(deleteIndexResponse); diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationService.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationService.java index 800a2b588d9..375552bfd8f 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationService.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationService.java @@ -29,6 +29,7 @@ import java.io.Closeable; import java.io.IOException; import java.time.Clock; import java.util.SortedMap; +import java.util.function.Supplier; import static org.elasticsearch.xpack.indexlifecycle.IndexLifecycle.NAME; @@ -41,6 +42,7 @@ public class IndexLifecycleInitialisationService extends AbstractComponent private Client client; private ClusterService clusterService; private ThreadPool threadPool; + private Supplier nowSupplier; public IndexLifecycleInitialisationService(Settings settings, Client client, ClusterService clusterService, Clock clock, ThreadPool threadPool) { @@ -49,6 +51,7 @@ public class IndexLifecycleInitialisationService extends AbstractComponent this.clusterService = clusterService; this.clock = clock; this.threadPool = threadPool; + this.nowSupplier = () -> System.currentTimeMillis(); // NOCOMMIT use ES now supplier. clusterService.addListener(this); } @@ -71,7 +74,7 @@ public class IndexLifecycleInitialisationService extends AbstractComponent if (Strings.isNullOrEmpty(policyName) == false) { logger.error("Checking index for next action: " + idxMeta.getIndex().getName() + " (" + policyName + ")"); LifecyclePolicy policy = policies.get(policyName); - policy.execute(idxMeta, client); + policy.execute(idxMeta, client, nowSupplier); } }); } diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java index 0b1111293d4..1c3f2a16e16 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; public class LifecyclePolicy extends AbstractDiffable implements ToXContentObject, Writeable { private static final Logger logger = ESLoggerFactory.getLogger(LifecyclePolicy.class); @@ -88,7 +89,7 @@ public class LifecyclePolicy extends AbstractDiffable implement return builder; } - public void execute(IndexMetaData idxMeta, Client client) { + public void execute(IndexMetaData idxMeta, Client client, Supplier nowSupplier) { String currentPhaseName = IndexLifecycle.LIFECYCLE_TIMESERIES_PHASE_SETTING.get(idxMeta.getSettings()); boolean currentPhaseActionsComplete = IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.get(idxMeta.getSettings()) .equals(Phase.PHASE_COMPLETED); @@ -123,7 +124,7 @@ public class LifecyclePolicy extends AbstractDiffable implement } if (currentPhaseIndex < phases.size() - 1) { Phase nextPhase = phases.get(currentPhaseIndex + 1); - if (nextPhase.canExecute(idxMeta)) { + if (nextPhase.canExecute(idxMeta, nowSupplier)) { String nextPhaseName = nextPhase.getName(); client.admin().indices().prepareUpdateSettings(indexName) .setSettings(Settings.builder() diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java index 99ff3b9d3d4..9d5ca4d8073 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.indexlifecycle; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -29,6 +30,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; public class Phase implements ToXContentObject, Writeable { public static final String PHASE_COMPLETED = "ACTIONS COMPLETED"; @@ -89,13 +91,9 @@ public class Phase implements ToXContentObject, Writeable { return actions; } - protected boolean canExecute(IndexMetaData idxMeta) { - long now = System.currentTimeMillis(); // NOCOMMIT use ES way for - // getting current time. + protected boolean canExecute(IndexMetaData idxMeta, Supplier nowSupplier) { + long now = nowSupplier.get(); long indexCreated = idxMeta.getCreationDate(); - logger.error("canExecute: now: " + now + ", indexCreated: " + indexCreated + ", (indexCreated + after.millis()): " - + (indexCreated + after.millis()) + ", (indexCreated + after.millis()) >= now: " - + ((indexCreated + after.millis()) >= now)); return (indexCreated + after.millis()) <= now; } @@ -109,24 +107,28 @@ public class Phase implements ToXContentObject, Writeable { } else { firstActionName = actions.get(0).getWriteableName(); } - client.admin().indices().prepareUpdateSettings(indexName) - .setSettings(Settings.builder().put(IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.getKey(), firstActionName)) - .execute(new ActionListener() { + client.admin().indices() + .updateSettings( + new UpdateSettingsRequest(Settings.builder() + .put(IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.getKey(), firstActionName).build(), indexName), + new ActionListener() { - @Override - public void onResponse(UpdateSettingsResponse response) { - if (response.isAcknowledged()) { - logger.info("Successfully initialised action [" + firstActionName + "] for index [" + indexName + "]"); - } else { - logger.error("Failed to initialised action [" + firstActionName + "] for index [" + indexName + "]"); - } - } + @Override + public void onResponse(UpdateSettingsResponse response) { + if (response.isAcknowledged()) { + logger.info( + "Successfully initialised action [" + firstActionName + "] for index [" + indexName + "]"); + } else { + logger.error( + "Failed to initialised action [" + firstActionName + "] for index [" + indexName + "]"); + } + } - @Override - public void onFailure(Exception e) { - logger.error("Failed to initialised action [" + firstActionName + "] for index [" + indexName + "]", e); - } - }); + @Override + public void onFailure(Exception e) { + logger.error("Failed to initialised action [" + firstActionName + "] for index [" + indexName + "]", e); + } + }); } else if (currentActionName.equals(PHASE_COMPLETED) == false) { LifecycleAction currentAction = actions.stream().filter(action -> action.getWriteableName().equals(currentActionName)).findAny() .orElseThrow(() -> new IllegalStateException("Current action [" + currentActionName + "] not found in phase [" diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/DeleteActionTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/DeleteActionTests.java index 2dc0e1b1c19..c5d1262e992 100644 --- a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/DeleteActionTests.java +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/DeleteActionTests.java @@ -5,9 +5,19 @@ */ package org.elasticsearch.xpack.indexlifecycle; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; +import org.elasticsearch.client.AdminClient; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.IndicesAdminClient; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.Index; import org.elasticsearch.test.AbstractSerializingTestCase; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.io.IOException; @@ -28,4 +38,37 @@ public class DeleteActionTests extends AbstractSerializingTestCase return DeleteAction::new; } + public void testExecute() throws Exception { + Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + Mockito.doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + DeleteIndexRequest request = (DeleteIndexRequest) invocation.getArguments()[0]; + @SuppressWarnings("unchecked") + ActionListener listener = (ActionListener) invocation.getArguments()[1]; + assertNotNull(request); + assertEquals(1, request.indices().length); + assertEquals(index.getName(), request.indices()[0]); + listener.onResponse(null); + return null; + } + + }).when(indicesClient).delete(Mockito.any(), Mockito.any()); + + DeleteAction action = new DeleteAction(); + action.execute(index, client); + + Mockito.verify(client, Mockito.only()).admin(); + Mockito.verify(adminClient, Mockito.only()).indices(); + Mockito.verify(indicesClient, Mockito.only()).delete(Mockito.any(), Mockito.any()); + } + } diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockAction.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockAction.java new file mode 100644 index 00000000000..bfd1e899dce --- /dev/null +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockAction.java @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.indexlifecycle; + +import org.apache.lucene.util.SetOnce; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.Index; + +import java.io.IOException; +import java.util.Objects; + +public class MockAction extends LifecycleAction { + public static final ParseField EXECUTED_FIELD = new ParseField("executed"); + public static final ParseField NAME_FIELD = new ParseField("name"); + public static final String NAME = "TEST_ACTION"; + private final SetOnce executed = new SetOnce<>(); + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(NAME, + a -> new MockAction((Boolean) a[1])); + static { + PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), EXECUTED_FIELD); + } + + public static MockAction parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + public MockAction() { + } + + private MockAction(Boolean executed) { + if (executed != null) { + this.executed.set(executed); + } + } + + public MockAction(StreamInput in) throws IOException { + Boolean executed = in.readOptionalBoolean(); + if (executed != null) { + this.executed.set(executed); + } + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (executed.get() != null) { + builder.field("executed", executed.get()); + } + builder.endObject(); + return builder; + } + + @Override + public String getWriteableName() { + return NAME; + } + + public Boolean wasExecuted() { + return executed.get() != null && executed.get(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeOptionalBoolean(executed.get()); + } + + @Override + protected void execute(Index index, Client client) { + executed.set(true); + } + + @Override + public int hashCode() { + return Objects.hash(executed.get()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj.getClass() == getClass()) { + return false; + } + MockAction other = (MockAction) obj; + return Objects.equals(executed.get(), other.executed.get()); + } + +} \ No newline at end of file diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/PhaseTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/PhaseTests.java index 5bb917ecb99..07a70a99685 100644 --- a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/PhaseTests.java +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/PhaseTests.java @@ -5,19 +5,31 @@ */ package org.elasticsearch.xpack.indexlifecycle; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; +import org.elasticsearch.client.AdminClient; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.IndicesAdminClient; +import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.AbstractSerializingTestCase; import org.junit.Before; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class PhaseTests extends AbstractSerializingTestCase { @@ -82,4 +94,370 @@ public class PhaseTests extends AbstractSerializingTestCase { return new Phase(name, after, actions); } + public void testCanExecuteBeforeTrigger() throws Exception { + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(0, 100000)); + long creationDate = randomNonNegativeLong(); + long now = random().longs(creationDate, creationDate + after.millis()).iterator().nextLong(); + + IndexMetaData idxMeta = IndexMetaData.builder("test") + .settings(Settings.builder().put("index.version.created", 7000001L).put("index.creation_date", creationDate).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Phase phase = new Phase("test_phase", after, Collections.emptyList()); + + assertFalse(phase.canExecute(idxMeta, () -> now)); + } + + public void testCanExecuteOnTrigger() throws Exception { + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(0, 100000)); + long creationDate = randomNonNegativeLong(); + long now = creationDate + after.millis(); + + IndexMetaData idxMeta = IndexMetaData.builder("test") + .settings(Settings.builder().put("index.version.created", 7000001L).put("index.creation_date", creationDate).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Phase phase = new Phase("test_phase", after, Collections.emptyList()); + + assertTrue(phase.canExecute(idxMeta, () -> now)); + } + + public void testCanExecuteAfterTrigger() throws Exception { + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(0, 100000)); + long creationDate = randomNonNegativeLong(); + long now = random().longs(creationDate + after.millis(), Long.MAX_VALUE).iterator().nextLong(); + + IndexMetaData idxMeta = IndexMetaData.builder("test").settings(Settings.builder() + .put("index.version.created", 7000001L) + .put("index.creation_date", creationDate) + .build()) + .numberOfShards(randomIntBetween(1, 5)) + .numberOfReplicas(randomIntBetween(0, 5)).build(); + + Phase phase = new Phase("test_phase", after, Collections.emptyList()); + + assertTrue(phase.canExecute(idxMeta, () -> now)); + } + + public void testExecuteNewIndex() throws Exception { + String indexName = randomAlphaOfLengthBetween(1, 20); + String phaseName = randomAlphaOfLengthBetween(1, 20); + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(10, 100)); + List actions = new ArrayList<>(); + MockAction firstAction = new MockAction(); + actions.add(firstAction); + MockAction secondAction = new MockAction(); + actions.add(secondAction); + MockAction thirdAction = new MockAction(); + actions.add(thirdAction); + Phase phase = new Phase(phaseName, after, actions); + + IndexMetaData idxMeta = IndexMetaData.builder(indexName).settings(Settings.builder().put("index.version.created", 7000001L).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + Mockito.doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + UpdateSettingsRequest request = (UpdateSettingsRequest) invocation.getArguments()[0]; + @SuppressWarnings("unchecked") + ActionListener listener = (ActionListener) invocation.getArguments()[1]; + assertNotNull(request); + assertEquals(1, request.indices().length); + assertEquals(indexName, request.indices()[0]); + // NOCOMMIT Need to check the settings in the request are + // correct (i.e adds the name of the first action) + return null; + } + + }).when(indicesClient).updateSettings(Mockito.any(), Mockito.any()); + + phase.execute(idxMeta, client); + + assertFalse(firstAction.wasExecuted()); + assertFalse(secondAction.wasExecuted()); + assertFalse(thirdAction.wasExecuted()); + + Mockito.verify(client, Mockito.only()).admin(); + Mockito.verify(adminClient, Mockito.only()).indices(); + Mockito.verify(indicesClient, Mockito.only()).updateSettings(Mockito.any(), Mockito.any()); + } + + public void testExecuteNewIndexNoActions() throws Exception { + String indexName = randomAlphaOfLengthBetween(1, 20); + String phaseName = randomAlphaOfLengthBetween(1, 20); + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(10, 100)); + Phase phase = new Phase(phaseName, after, Collections.emptyList()); + + IndexMetaData idxMeta = IndexMetaData.builder(indexName).settings(Settings.builder().put("index.version.created", 7000001L).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + Mockito.doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + UpdateSettingsRequest request = (UpdateSettingsRequest) invocation.getArguments()[0]; + @SuppressWarnings("unchecked") + ActionListener listener = (ActionListener) invocation.getArguments()[1]; + assertNotNull(request); + assertEquals(1, request.indices().length); + assertEquals(indexName, request.indices()[0]); + // NOCOMMIT Need to check the settings in the request are + // correct (i.e. sets the action to `ACTIONS COMPLETED`) + return null; + } + + }).when(indicesClient).updateSettings(Mockito.any(), Mockito.any()); + + phase.execute(idxMeta, client); + + Mockito.verify(client, Mockito.only()).admin(); + Mockito.verify(adminClient, Mockito.only()).indices(); + Mockito.verify(indicesClient, Mockito.only()).updateSettings(Mockito.any(), Mockito.any()); + } + + public void testExecutePhaseAlreadyComplete() throws Exception { + String indexName = randomAlphaOfLengthBetween(1, 20); + String phaseName = randomAlphaOfLengthBetween(1, 20); + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(10, 100)); + List actions = new ArrayList<>(); + MockAction firstAction = new MockAction(); + actions.add(firstAction); + MockAction secondAction = new MockAction(); + actions.add(secondAction); + MockAction thirdAction = new MockAction(); + actions.add(thirdAction); + Phase phase = new Phase(phaseName, after, Collections.emptyList()); + + IndexMetaData idxMeta = IndexMetaData.builder(indexName) + .settings(Settings.builder().put("index.version.created", 7000001L) + .put(IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.getKey(), Phase.PHASE_COMPLETED).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + + phase.execute(idxMeta, client); + + assertFalse(firstAction.wasExecuted()); + assertFalse(secondAction.wasExecuted()); + assertFalse(thirdAction.wasExecuted()); + + Mockito.verifyZeroInteractions(client, adminClient, indicesClient); + } + + public void testExecuteFirstAction() throws Exception { + String indexName = randomAlphaOfLengthBetween(1, 20); + String phaseName = randomAlphaOfLengthBetween(1, 20); + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(10, 100)); + List actions = new ArrayList<>(); + MockAction firstAction = new MockAction() { + @Override + public String getWriteableName() { + return "first_action"; + } + }; + actions.add(firstAction); + MockAction secondAction = new MockAction() { + @Override + public String getWriteableName() { + return "second_action"; + } + }; + actions.add(secondAction); + MockAction thirdAction = new MockAction() { + @Override + public String getWriteableName() { + return "third_action"; + } + }; + actions.add(thirdAction); + Phase phase = new Phase(phaseName, after, actions); + + IndexMetaData idxMeta = IndexMetaData.builder(indexName) + .settings(Settings.builder().put("index.version.created", 7000001L) + .put(IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.getKey(), firstAction.getWriteableName()).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + + phase.execute(idxMeta, client); + + assertTrue(firstAction.wasExecuted()); + assertFalse(secondAction.wasExecuted()); + assertFalse(thirdAction.wasExecuted()); + + Mockito.verifyZeroInteractions(client, adminClient, indicesClient); + } + + public void testExecuteSecondAction() throws Exception { + String indexName = randomAlphaOfLengthBetween(1, 20); + String phaseName = randomAlphaOfLengthBetween(1, 20); + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(10, 100)); + List actions = new ArrayList<>(); + MockAction firstAction = new MockAction() { + @Override + public String getWriteableName() { + return "first_action"; + } + }; + actions.add(firstAction); + MockAction secondAction = new MockAction() { + @Override + public String getWriteableName() { + return "second_action"; + } + }; + actions.add(secondAction); + MockAction thirdAction = new MockAction() { + @Override + public String getWriteableName() { + return "third_action"; + } + }; + actions.add(thirdAction); + Phase phase = new Phase(phaseName, after, actions); + + IndexMetaData idxMeta = IndexMetaData.builder(indexName) + .settings(Settings.builder().put("index.version.created", 7000001L) + .put(IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.getKey(), secondAction.getWriteableName()).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + + phase.execute(idxMeta, client); + + assertFalse(firstAction.wasExecuted()); + assertTrue(secondAction.wasExecuted()); + assertFalse(thirdAction.wasExecuted()); + + Mockito.verifyZeroInteractions(client, adminClient, indicesClient); + } + + public void testExecuteThirdAction() throws Exception { + String indexName = randomAlphaOfLengthBetween(1, 20); + String phaseName = randomAlphaOfLengthBetween(1, 20); + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(10, 100)); + List actions = new ArrayList<>(); + MockAction firstAction = new MockAction() { + @Override + public String getWriteableName() { + return "first_action"; + } + }; + actions.add(firstAction); + MockAction secondAction = new MockAction() { + @Override + public String getWriteableName() { + return "second_action"; + } + }; + actions.add(secondAction); + MockAction thirdAction = new MockAction() { + @Override + public String getWriteableName() { + return "third_action"; + } + }; + actions.add(thirdAction); + Phase phase = new Phase(phaseName, after, actions); + + IndexMetaData idxMeta = IndexMetaData.builder(indexName) + .settings(Settings.builder().put("index.version.created", 7000001L) + .put(IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.getKey(), thirdAction.getWriteableName()).build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + + phase.execute(idxMeta, client); + + assertFalse(firstAction.wasExecuted()); + assertFalse(secondAction.wasExecuted()); + assertTrue(thirdAction.wasExecuted()); + + Mockito.verifyZeroInteractions(client, adminClient, indicesClient); + } + + public void testExecuteMissingAction() throws Exception { + String indexName = randomAlphaOfLengthBetween(1, 20); + String phaseName = randomAlphaOfLengthBetween(1, 20); + TimeValue after = TimeValue.timeValueSeconds(randomIntBetween(10, 100)); + List actions = new ArrayList<>(); + MockAction firstAction = new MockAction() { + @Override + public String getWriteableName() { + return "first_action"; + } + }; + actions.add(firstAction); + MockAction secondAction = new MockAction() { + @Override + public String getWriteableName() { + return "second_action"; + } + }; + actions.add(secondAction); + MockAction thirdAction = new MockAction() { + @Override + public String getWriteableName() { + return "third_action"; + } + }; + actions.add(thirdAction); + Phase phase = new Phase(phaseName, after, actions); + + IndexMetaData idxMeta = IndexMetaData.builder(indexName) + .settings(Settings.builder().put("index.version.created", 7000001L) + .put(IndexLifecycle.LIFECYCLE_TIMESERIES_ACTION_SETTING.getKey(), "does_not_exist").build()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + + Client client = Mockito.mock(Client.class); + AdminClient adminClient = Mockito.mock(AdminClient.class); + IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); + + Mockito.when(client.admin()).thenReturn(adminClient); + Mockito.when(adminClient.indices()).thenReturn(indicesClient); + + IllegalStateException exception = expectThrows(IllegalStateException.class, () -> phase.execute(idxMeta, client)); + assertEquals("Current action [" + "does_not_exist" + "] not found in phase [" + phaseName + "] for index [" + indexName + "]", + exception.getMessage()); + + assertFalse(firstAction.wasExecuted()); + assertFalse(secondAction.wasExecuted()); + assertFalse(thirdAction.wasExecuted()); + + Mockito.verifyZeroInteractions(client, adminClient, indicesClient); + } + }