diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleService.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleService.java index c4faa717a2e..fd6183262e9 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleService.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleService.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.indexlifecycle; import com.google.common.base.Strings; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.FormattedMessage; import org.apache.lucene.util.SetOnce; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.client.Client; @@ -115,8 +116,8 @@ public class IndexLifecycleService extends AbstractComponent try { policy.execute(new InternalIndexLifecycleContext(idxMeta.getIndex(), client, clusterService, nowSupplier)); } catch (ElasticsearchException e) { - logger.error("Failed to execute lifecycle policy [{}] for index [{}]", policyName, - idxMeta.getIndex().getName()); + logger.error(new FormattedMessage("Failed to execute lifecycle policy [{}] for index [{}]", policyName, + idxMeta.getIndex().getName()), e); } } } diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleServiceTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleServiceTests.java index 76f85362a58..d3b5b27bfb2 100644 --- a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleServiceTests.java +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleServiceTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.xpack.scheduler.SchedulerEngine; import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule; import org.junit.After; import org.junit.Before; +import org.mockito.Mockito; import java.io.IOException; import java.time.Clock; @@ -91,6 +92,7 @@ public class IndexLifecycleServiceTests extends ESTestCase { indexLifecycleService = new IndexLifecycleService(Settings.EMPTY, client, clusterService, clock, threadPool, () -> randomMilli); + Mockito.verify(clusterService).addListener(indexLifecycleService); } public void testOnlyChangesStateOnMaster() throws Exception { @@ -167,6 +169,10 @@ public class IndexLifecycleServiceTests extends ESTestCase { assertThat(indexLifecycleService.getScheduler().jobCount(), equalTo(1)); assertThat(((IntervalSchedule)indexLifecycleService.getScheduledJob().getSchedule()).interval(), equalTo(new IntervalSchedule.Interval(pollInterval.seconds(), IntervalSchedule.Interval.Unit.SECONDS))); + indexLifecycleService.clusterChanged(new ClusterChangedEvent("_source", currentState, currentState)); + assertThat(indexLifecycleService.getScheduler().jobCount(), equalTo(1)); + assertThat(((IntervalSchedule) indexLifecycleService.getScheduledJob().getSchedule()).interval(), + equalTo(new IntervalSchedule.Interval(pollInterval.seconds(), IntervalSchedule.Interval.Unit.SECONDS))); verify(clusterService, only()).addListener(any()); verify(clusterService, never()).submitStateUpdateTask(anyString(), any(ClusterStateUpdateTask.class)); @@ -233,4 +239,97 @@ public class IndexLifecycleServiceTests extends ESTestCase { assertThat(mockAction.getExecutedCount(), equalTo(1L)); } + + /** + * Check that if an index has an unknown lifecycle policy set it does not + * execute any policy but does process other indexes. + */ + public void testTriggeredUnknownPolicyNameSet() { + String policyName = randomAlphaOfLengthBetween(1, 20); + MockAction mockAction = new MockAction(); + Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction)); + LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, + Collections.singletonMap(phase.getName(), phase)); + SortedMap policyMap = new TreeMap<>(); + policyMap.put(policyName, policy); + Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); + IndexMetaData indexMetadata = IndexMetaData.builder(index.getName()) + .settings(settings(Version.CURRENT).put(IndexLifecycle.LIFECYCLE_NAME_SETTING.getKey(), "foo")) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + Index index2 = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); + IndexMetaData indexMetadata2 = IndexMetaData.builder(index2.getName()) + .settings(settings(Version.CURRENT).put(IndexLifecycle.LIFECYCLE_NAME_SETTING.getKey(), policyName)) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + ImmutableOpenMap.Builder indices = ImmutableOpenMap. builder().fPut(index.getName(), + indexMetadata).fPut(index2.getName(), indexMetadata2); + MetaData metaData = MetaData.builder().putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) + .indices(indices.build()).persistentSettings(settings(Version.CURRENT).build()).build(); + ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData) + .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build()).build(); + + SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event(IndexLifecycle.NAME, randomLong(), randomLong()); + + when(clusterService.state()).thenReturn(currentState); + + doAnswer(invocationOnMock -> { + @SuppressWarnings("unchecked") + ActionListener listener = (ActionListener) invocationOnMock.getArguments()[1]; + listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true)); + return null; + + }).when(indicesClient).updateSettings(any(), any()); + + indexLifecycleService.triggered(schedulerEvent); + + assertThat(mockAction.getExecutedCount(), equalTo(1L)); + } + + /** + * Check that if an index has no lifecycle policy set it does not execute + * any policy but does process other indexes. + */ + public void testTriggeredNoPolicyNameSet() { + String policyName = randomAlphaOfLengthBetween(1, 20); + MockAction mockAction = new MockAction(); + Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction)); + LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, + Collections.singletonMap(phase.getName(), phase)); + SortedMap policyMap = new TreeMap<>(); + policyMap.put(policyName, policy); + Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); + IndexMetaData indexMetadata = IndexMetaData.builder(index.getName()).settings(settings(Version.CURRENT)) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + Index index2 = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); + IndexMetaData indexMetadata2 = IndexMetaData.builder(index2.getName()) + .settings(settings(Version.CURRENT).put(IndexLifecycle.LIFECYCLE_NAME_SETTING.getKey(), policyName)) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); + ImmutableOpenMap.Builder indices = ImmutableOpenMap. builder().fPut(index.getName(), + indexMetadata).fPut(index2.getName(), indexMetadata2); + MetaData metaData = MetaData.builder().putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) + .indices(indices.build()).persistentSettings(settings(Version.CURRENT).build()).build(); + ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData) + .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build()).build(); + + SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event(IndexLifecycle.NAME, randomLong(), randomLong()); + + when(clusterService.state()).thenReturn(currentState); + + doAnswer(invocationOnMock -> { + @SuppressWarnings("unchecked") + ActionListener listener = (ActionListener) invocationOnMock.getArguments()[1]; + listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true)); + return null; + + }).when(indicesClient).updateSettings(any(), any()); + + indexLifecycleService.triggered(schedulerEvent); + + assertThat(mockAction.getExecutedCount(), equalTo(1L)); + } + + public void testTriggeredDifferentJob() { + SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event("foo", randomLong(), randomLong()); + indexLifecycleService.triggered(schedulerEvent); + Mockito.verifyZeroInteractions(indicesClient, clusterService); + } } diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockActionTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockActionTests.java index 11c302df6e5..8744c7c288d 100644 --- a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockActionTests.java +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/MockActionTests.java @@ -102,6 +102,57 @@ public class MockActionTests extends AbstractSerializingTestCase { assertEquals(true, listenerCalled.get()); } + public void testResetComplete() { + + MockAction action = new MockAction(); + action.setCompleteOnExecute(true); + + assertFalse(action.wasCompleted()); + assertEquals(0L, action.getExecutedCount()); + + SetOnce listenerCalled = new SetOnce<>(); + + action.execute(null, null, null, new LifecycleAction.Listener() { + + @Override + public void onSuccess(boolean completed) { + listenerCalled.set(true); + } + + @Override + public void onFailure(Exception e) { + throw new AssertionError("Unexpected method call", e); + } + }); + + assertTrue(action.wasCompleted()); + assertEquals(1L, action.getExecutedCount()); + assertEquals(true, listenerCalled.get()); + + action.resetCompleted(); + + assertFalse(action.wasCompleted()); + + SetOnce listenerCalled2 = new SetOnce<>(); + + action.execute(null, null, null, new LifecycleAction.Listener() { + + @Override + public void onSuccess(boolean completed) { + listenerCalled2.set(true); + } + + @Override + public void onFailure(Exception e) { + throw new AssertionError("Unexpected method call", e); + } + }); + + assertTrue(action.wasCompleted()); + assertEquals(2L, action.getExecutedCount()); + assertEquals(true, listenerCalled2.get()); + } + public void testExecuteFailure() { Exception exception = new RuntimeException();