|
5 | 5 | */
|
6 | 6 | package org.elasticsearch.xpack.watcher.execution;
|
7 | 7 |
|
8 |
| -import org.elasticsearch.ElasticsearchException; |
9 | 8 | import org.elasticsearch.Version;
|
| 9 | +import org.elasticsearch.action.ActionFuture; |
10 | 10 | import org.elasticsearch.action.ActionListener;
|
| 11 | +import org.elasticsearch.action.delete.DeleteRequest; |
11 | 12 | import org.elasticsearch.action.get.GetRequest;
|
12 | 13 | import org.elasticsearch.action.get.GetResponse;
|
| 14 | +import org.elasticsearch.action.index.IndexRequest; |
13 | 15 | import org.elasticsearch.action.support.PlainActionFuture;
|
14 | 16 | import org.elasticsearch.action.update.UpdateRequest;
|
15 | 17 | import org.elasticsearch.action.update.UpdateResponse;
|
|
31 | 33 | import org.elasticsearch.common.xcontent.XContentFactory;
|
32 | 34 | import org.elasticsearch.common.xcontent.XContentParser;
|
33 | 35 | import org.elasticsearch.common.xcontent.XContentType;
|
| 36 | +import org.elasticsearch.index.Index; |
34 | 37 | import org.elasticsearch.index.IndexNotFoundException;
|
| 38 | +import org.elasticsearch.index.engine.VersionConflictEngineException; |
35 | 39 | import org.elasticsearch.index.get.GetResult;
|
| 40 | +import org.elasticsearch.index.shard.ShardId; |
36 | 41 | import org.elasticsearch.test.ESTestCase;
|
37 | 42 | import org.elasticsearch.threadpool.ThreadPool;
|
38 | 43 | import org.elasticsearch.xpack.core.security.authc.Authentication;
|
|
51 | 56 | import org.elasticsearch.xpack.core.watcher.execution.ExecutionPhase;
|
52 | 57 | import org.elasticsearch.xpack.core.watcher.execution.ExecutionState;
|
53 | 58 | import org.elasticsearch.xpack.core.watcher.execution.QueuedWatch;
|
| 59 | +import org.elasticsearch.xpack.core.watcher.execution.TriggeredWatchStoreField; |
54 | 60 | import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
|
55 | 61 | import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionSnapshot;
|
56 | 62 | import org.elasticsearch.xpack.core.watcher.execution.Wid;
|
|
92 | 98 | import static java.util.Collections.singletonMap;
|
93 | 99 | import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
|
94 | 100 | import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
|
| 101 | +import static org.hamcrest.Matchers.containsString; |
95 | 102 | import static org.hamcrest.Matchers.equalTo;
|
96 | 103 | import static org.hamcrest.Matchers.hasSize;
|
97 | 104 | import static org.hamcrest.Matchers.instanceOf;
|
@@ -834,21 +841,73 @@ public void testThatTriggeredWatchDeletionWorksOnExecutionRejection() throws Exc
|
834 | 841 | when(getResponse.isExists()).thenReturn(true);
|
835 | 842 | when(getResponse.getId()).thenReturn("foo");
|
836 | 843 | mockGetWatchResponse(client, "foo", getResponse);
|
| 844 | + ActionFuture actionFuture = mock(ActionFuture.class); |
| 845 | + when(actionFuture.get()).thenReturn(""); |
| 846 | + when(client.index(any())).thenReturn(actionFuture); |
| 847 | + when(client.delete(any())).thenReturn(actionFuture); |
| 848 | + |
837 | 849 | when(parser.parseWithSecrets(eq("foo"), eq(true), any(), any(), any(), anyLong(), anyLong())).thenReturn(watch);
|
838 | 850 |
|
839 |
| - // execute needs to fail as well as storing the history |
| 851 | + // execute needs to fail |
840 | 852 | doThrow(new EsRejectedExecutionException()).when(executor).execute(any());
|
841 |
| - doThrow(new ElasticsearchException("whatever")).when(historyStore).forcePut(any()); |
842 | 853 |
|
843 | 854 | Wid wid = new Wid(watch.id(), now());
|
844 | 855 |
|
845 | 856 | TriggeredWatch triggeredWatch = new TriggeredWatch(wid, new ScheduleTriggerEvent(now() ,now()));
|
846 | 857 | executionService.executeTriggeredWatches(Collections.singleton(triggeredWatch));
|
847 | 858 |
|
848 |
| - verify(triggeredWatchStore, times(1)).delete(wid); |
849 |
| - ArgumentCaptor<WatchRecord> captor = ArgumentCaptor.forClass(WatchRecord.class); |
850 |
| - verify(historyStore, times(1)).forcePut(captor.capture()); |
851 |
| - assertThat(captor.getValue().state(), is(ExecutionState.THREADPOOL_REJECTION)); |
| 859 | + ArgumentCaptor<DeleteRequest> deleteCaptor = ArgumentCaptor.forClass(DeleteRequest.class); |
| 860 | + verify(client).delete(deleteCaptor.capture()); |
| 861 | + assertThat(deleteCaptor.getValue().index(), equalTo(TriggeredWatchStoreField.INDEX_NAME)); |
| 862 | + assertThat(deleteCaptor.getValue().id(), equalTo(wid.value())); |
| 863 | + |
| 864 | + ArgumentCaptor<IndexRequest> watchHistoryCaptor = ArgumentCaptor.forClass(IndexRequest.class); |
| 865 | + verify(client).index(watchHistoryCaptor.capture()); |
| 866 | + |
| 867 | + assertThat(watchHistoryCaptor.getValue().source().utf8ToString(), containsString(ExecutionState.THREADPOOL_REJECTION.toString())); |
| 868 | + assertThat(watchHistoryCaptor.getValue().index(), containsString(".watcher-history")); |
| 869 | + } |
| 870 | + |
| 871 | + public void testForcePutHistoryOnExecutionRejection() throws Exception { |
| 872 | + Watch watch = mock(Watch.class); |
| 873 | + when(watch.id()).thenReturn("foo"); |
| 874 | + WatchStatus status = new WatchStatus(ZonedDateTime.now(ZoneOffset.UTC), Collections.emptyMap()); |
| 875 | + when(watch.status()).thenReturn(status); |
| 876 | + GetResponse getResponse = mock(GetResponse.class); |
| 877 | + when(getResponse.isExists()).thenReturn(true); |
| 878 | + when(getResponse.getId()).thenReturn("foo"); |
| 879 | + mockGetWatchResponse(client, "foo", getResponse); |
| 880 | + ActionFuture actionFuture = mock(ActionFuture.class); |
| 881 | + when(actionFuture.get()).thenReturn(""); |
| 882 | + when(client.index(any())) |
| 883 | + .thenThrow(new VersionConflictEngineException( |
| 884 | + new ShardId(new Index("mockindex", "mockuuid"), 0), "id", "explaination")) |
| 885 | + .thenReturn(actionFuture); |
| 886 | + when(client.delete(any())).thenReturn(actionFuture); |
| 887 | + |
| 888 | + when(parser.parseWithSecrets(eq("foo"), eq(true), any(), any(), any(), anyLong(), anyLong())).thenReturn(watch); |
| 889 | + |
| 890 | + // execute needs to fail |
| 891 | + doThrow(new EsRejectedExecutionException()).when(executor).execute(any()); |
| 892 | + |
| 893 | + Wid wid = new Wid(watch.id(), ZonedDateTime.now(ZoneOffset.UTC)); |
| 894 | + |
| 895 | + TriggeredWatch triggeredWatch = new TriggeredWatch(wid, |
| 896 | + new ScheduleTriggerEvent(ZonedDateTime.now(ZoneOffset.UTC), ZonedDateTime.now(ZoneOffset.UTC))); |
| 897 | + executionService.executeTriggeredWatches(Collections.singleton(triggeredWatch)); |
| 898 | + |
| 899 | + ArgumentCaptor<DeleteRequest> deleteCaptor = ArgumentCaptor.forClass(DeleteRequest.class); |
| 900 | + verify(client).delete(deleteCaptor.capture()); |
| 901 | + assertThat(deleteCaptor.getValue().index(), equalTo(TriggeredWatchStoreField.INDEX_NAME)); |
| 902 | + assertThat(deleteCaptor.getValue().id(), equalTo(wid.value())); |
| 903 | + |
| 904 | + ArgumentCaptor<IndexRequest> watchHistoryCaptor = ArgumentCaptor.forClass(IndexRequest.class); |
| 905 | + verify(client, times(2)).index(watchHistoryCaptor.capture()); |
| 906 | + List<IndexRequest> indexRequests = watchHistoryCaptor.getAllValues(); |
| 907 | + |
| 908 | + assertThat(indexRequests.get(0).id(), equalTo(indexRequests.get(1).id())); |
| 909 | + assertThat(indexRequests.get(0).source().utf8ToString(), containsString(ExecutionState.THREADPOOL_REJECTION.toString())); |
| 910 | + assertThat(indexRequests.get(1).source().utf8ToString(), containsString(ExecutionState.EXECUTED_MULTIPLE_TIMES.toString())); |
852 | 911 | }
|
853 | 912 |
|
854 | 913 | public void testThatTriggeredWatchDeletionHappensOnlyIfWatchExists() throws Exception {
|
@@ -887,7 +946,7 @@ public void testThatTriggeredWatchDeletionHappensOnlyIfWatchExists() throws Exce
|
887 | 946 | when(watch.status()).thenReturn(watchStatus);
|
888 | 947 |
|
889 | 948 | executionService.execute(context);
|
890 |
| - verify(triggeredWatchStore, never()).delete(any()); |
| 949 | + verify(client, never()).delete(any()); |
891 | 950 | }
|
892 | 951 |
|
893 | 952 | public void testThatSingleWatchCannotBeExecutedConcurrently() throws Exception {
|
|
0 commit comments