|
22 | 22 | import org.elasticsearch.Version; |
23 | 23 | import org.elasticsearch.action.ActionListener; |
24 | 24 | import org.elasticsearch.action.DocWriteRequest; |
| 25 | +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; |
25 | 26 | import org.elasticsearch.action.index.IndexAction; |
26 | 27 | import org.elasticsearch.action.index.IndexRequest; |
27 | 28 | import org.elasticsearch.action.index.IndexResponse; |
28 | 29 | import org.elasticsearch.action.support.ActionFilters; |
| 30 | +import org.elasticsearch.action.support.AutoCreateIndex; |
29 | 31 | import org.elasticsearch.cluster.ClusterChangedEvent; |
30 | 32 | import org.elasticsearch.cluster.ClusterState; |
31 | 33 | import org.elasticsearch.cluster.ClusterStateApplier; |
32 | 34 | import org.elasticsearch.cluster.metadata.IndexMetaData; |
| 35 | +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; |
33 | 36 | import org.elasticsearch.cluster.metadata.MetaData; |
34 | 37 | import org.elasticsearch.cluster.node.DiscoveryNode; |
35 | 38 | import org.elasticsearch.cluster.node.DiscoveryNodes; |
36 | 39 | import org.elasticsearch.cluster.service.ClusterService; |
37 | 40 | import org.elasticsearch.common.collect.ImmutableOpenMap; |
| 41 | +import org.elasticsearch.common.settings.ClusterSettings; |
38 | 42 | import org.elasticsearch.common.settings.Settings; |
| 43 | +import org.elasticsearch.common.unit.TimeValue; |
39 | 44 | import org.elasticsearch.common.util.concurrent.AtomicArray; |
40 | 45 | import org.elasticsearch.index.IndexNotFoundException; |
41 | 46 | import org.elasticsearch.index.IndexSettings; |
@@ -77,6 +82,9 @@ public class TransportBulkActionIngestTests extends ESTestCase { |
77 | 82 | */ |
78 | 83 | private static final String WITH_DEFAULT_PIPELINE = "index_with_default_pipeline"; |
79 | 84 |
|
| 85 | + private static final Settings SETTINGS = |
| 86 | + Settings.builder().put(AutoCreateIndex.AUTO_CREATE_INDEX_SETTING.getKey(), true).build(); |
| 87 | + |
80 | 88 | /** Services needed by bulk action */ |
81 | 89 | TransportService transportService; |
82 | 90 | ClusterService clusterService; |
@@ -112,25 +120,42 @@ public class TransportBulkActionIngestTests extends ESTestCase { |
112 | 120 | /** A subclass of the real bulk action to allow skipping real bulk indexing, and marking when it would have happened. */ |
113 | 121 | class TestTransportBulkAction extends TransportBulkAction { |
114 | 122 | boolean isExecuted = false; // set when the "real" bulk execution happens |
| 123 | + |
| 124 | + boolean needToCheck; // pluggable return value for `needToCheck` |
| 125 | + |
| 126 | + boolean indexCreated = true; // If set to false, will be set to true by call to createIndex |
| 127 | + |
115 | 128 | TestTransportBulkAction() { |
116 | | - super(Settings.EMPTY, null, transportService, clusterService, ingestService, |
117 | | - null, null, new ActionFilters(Collections.emptySet()), null, null); |
| 129 | + super(SETTINGS, null, transportService, clusterService, ingestService, |
| 130 | + null, null, new ActionFilters(Collections.emptySet()), null, |
| 131 | + new AutoCreateIndex( |
| 132 | + SETTINGS, new ClusterSettings(SETTINGS, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), |
| 133 | + new IndexNameExpressionResolver(SETTINGS) |
| 134 | + ) |
| 135 | + ); |
118 | 136 | } |
119 | 137 | @Override |
120 | 138 | protected boolean needToCheck() { |
121 | | - return false; |
| 139 | + return needToCheck; |
122 | 140 | } |
123 | 141 | @Override |
124 | 142 | void executeBulk(Task task, final BulkRequest bulkRequest, final long startTimeNanos, final ActionListener<BulkResponse> listener, |
125 | 143 | final AtomicArray<BulkItemResponse> responses, Map<String, IndexNotFoundException> indicesThatCannotBeCreated) { |
| 144 | + assertTrue(indexCreated); |
126 | 145 | isExecuted = true; |
127 | 146 | } |
| 147 | + |
| 148 | + @Override |
| 149 | + void createIndex(String index, TimeValue timeout, ActionListener<CreateIndexResponse> listener) { |
| 150 | + indexCreated = true; |
| 151 | + listener.onResponse(null); |
| 152 | + } |
128 | 153 | } |
129 | 154 |
|
130 | 155 | class TestSingleItemBulkWriteAction extends TransportSingleItemBulkWriteAction<IndexRequest, IndexResponse> { |
131 | 156 |
|
132 | 157 | TestSingleItemBulkWriteAction(TestTransportBulkAction bulkAction) { |
133 | | - super(Settings.EMPTY, IndexAction.NAME, TransportBulkActionIngestTests.this.transportService, |
| 158 | + super(SETTINGS, IndexAction.NAME, TransportBulkActionIngestTests.this.transportService, |
134 | 159 | TransportBulkActionIngestTests.this.clusterService, |
135 | 160 | null, null, null, new ActionFilters(Collections.emptySet()), null, |
136 | 161 | IndexRequest::new, IndexRequest::new, ThreadPool.Names.WRITE, bulkAction, null); |
@@ -162,15 +187,17 @@ public void setupAction() { |
162 | 187 | when(nodes.getIngestNodes()).thenReturn(ingestNodes); |
163 | 188 | ClusterState state = mock(ClusterState.class); |
164 | 189 | when(state.getNodes()).thenReturn(nodes); |
165 | | - when(state.getMetaData()).thenReturn(MetaData.builder().indices(ImmutableOpenMap.<String, IndexMetaData>builder() |
| 190 | + MetaData metaData = MetaData.builder().indices(ImmutableOpenMap.<String, IndexMetaData>builder() |
166 | 191 | .putAll( |
167 | 192 | Collections.singletonMap( |
168 | 193 | WITH_DEFAULT_PIPELINE, |
169 | 194 | IndexMetaData.builder(WITH_DEFAULT_PIPELINE).settings( |
170 | 195 | settings(Version.CURRENT).put(IndexSettings.DEFAULT_PIPELINE.getKey(), "default_pipeline") |
171 | 196 | .build() |
172 | 197 | ).numberOfShards(1).numberOfReplicas(1).build())) |
173 | | - .build()).build()); |
| 198 | + .build()).build(); |
| 199 | + when(state.getMetaData()).thenReturn(metaData); |
| 200 | + when(state.metaData()).thenReturn(metaData); |
174 | 201 | when(clusterService.state()).thenReturn(state); |
175 | 202 | doAnswer(invocation -> { |
176 | 203 | ClusterChangedEvent event = mock(ClusterChangedEvent.class); |
@@ -408,4 +435,36 @@ public void testUseDefaultPipeline() throws Exception { |
408 | 435 | verifyZeroInteractions(transportService); |
409 | 436 | } |
410 | 437 |
|
| 438 | + public void testCreateIndexBeforeRunPipeline() throws Exception { |
| 439 | + Exception exception = new Exception("fake exception"); |
| 440 | + IndexRequest indexRequest = new IndexRequest("missing_index", "type", "id"); |
| 441 | + indexRequest.setPipeline("testpipeline"); |
| 442 | + indexRequest.source(Collections.emptyMap()); |
| 443 | + AtomicBoolean responseCalled = new AtomicBoolean(false); |
| 444 | + AtomicBoolean failureCalled = new AtomicBoolean(false); |
| 445 | + action.needToCheck = true; |
| 446 | + action.indexCreated = false; |
| 447 | + singleItemBulkWriteAction.execute(null, indexRequest, ActionListener.wrap( |
| 448 | + response -> responseCalled.set(true), |
| 449 | + e -> { |
| 450 | + assertThat(e, sameInstance(exception)); |
| 451 | + failureCalled.set(true); |
| 452 | + })); |
| 453 | + |
| 454 | + // check failure works, and passes through to the listener |
| 455 | + assertFalse(action.isExecuted); // haven't executed yet |
| 456 | + assertFalse(responseCalled.get()); |
| 457 | + assertFalse(failureCalled.get()); |
| 458 | + verify(executionService).executeBulkRequest(bulkDocsItr.capture(), failureHandler.capture(), completionHandler.capture()); |
| 459 | + completionHandler.getValue().accept(exception); |
| 460 | + assertTrue(failureCalled.get()); |
| 461 | + |
| 462 | + // now check success |
| 463 | + indexRequest.setPipeline(IngestService.NOOP_PIPELINE_NAME); // this is done by the real pipeline execution service when processing |
| 464 | + completionHandler.getValue().accept(null); |
| 465 | + assertTrue(action.isExecuted); |
| 466 | + assertFalse(responseCalled.get()); // listener would only be called by real index action, not our mocked one |
| 467 | + verifyZeroInteractions(transportService); |
| 468 | + } |
| 469 | + |
411 | 470 | } |
0 commit comments