|
58 | 58 | import org.opensearch.common.collect.Tuple;
|
59 | 59 | import org.opensearch.common.regex.Regex;
|
60 | 60 | import org.opensearch.common.settings.Settings;
|
| 61 | +import org.opensearch.common.util.io.Streams; |
61 | 62 | import org.opensearch.core.action.ActionListener;
|
62 | 63 | import org.opensearch.core.tasks.TaskId;
|
| 64 | +import org.opensearch.core.tasks.resourcetracker.TaskResourceStats; |
| 65 | +import org.opensearch.core.tasks.resourcetracker.TaskResourceUsage; |
| 66 | +import org.opensearch.core.tasks.resourcetracker.TaskThreadUsage; |
63 | 67 | import org.opensearch.core.xcontent.MediaTypeRegistry;
|
| 68 | +import org.opensearch.index.mapper.StrictDynamicMappingException; |
64 | 69 | import org.opensearch.index.query.QueryBuilders;
|
65 | 70 | import org.opensearch.search.builder.SearchSourceBuilder;
|
66 | 71 | import org.opensearch.tasks.Task;
|
|
73 | 78 | import org.opensearch.transport.ReceiveTimeoutTransportException;
|
74 | 79 | import org.opensearch.transport.TransportService;
|
75 | 80 |
|
| 81 | +import java.io.ByteArrayOutputStream; |
| 82 | +import java.io.IOException; |
| 83 | +import java.io.InputStream; |
| 84 | +import java.nio.charset.StandardCharsets; |
76 | 85 | import java.util.Collections;
|
77 | 86 | import java.util.HashMap;
|
78 | 87 | import java.util.List;
|
79 | 88 | import java.util.Map;
|
80 | 89 | import java.util.concurrent.BrokenBarrierException;
|
| 90 | +import java.util.concurrent.CompletableFuture; |
| 91 | +import java.util.concurrent.CompletionException; |
81 | 92 | import java.util.concurrent.CountDownLatch;
|
82 | 93 | import java.util.concurrent.CyclicBarrier;
|
83 | 94 | import java.util.concurrent.TimeUnit;
|
|
103 | 114 | import static org.hamcrest.Matchers.not;
|
104 | 115 | import static org.hamcrest.Matchers.notNullValue;
|
105 | 116 | import static org.hamcrest.Matchers.startsWith;
|
| 117 | +import static org.mockito.Mockito.doReturn; |
| 118 | +import static org.mockito.Mockito.spy; |
106 | 119 |
|
107 | 120 | /**
|
108 | 121 | * Integration tests for task management API
|
|
112 | 125 | @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, minNumDataNodes = 2)
|
113 | 126 | public class TasksIT extends AbstractTasksIT {
|
114 | 127 |
|
| 128 | + protected final TaskInfo taskInfo = new TaskInfo( |
| 129 | + new TaskId("fake", 1), |
| 130 | + "test_type", |
| 131 | + "test_action", |
| 132 | + "test_description", |
| 133 | + null, |
| 134 | + 0L, |
| 135 | + 1L, |
| 136 | + false, |
| 137 | + false, |
| 138 | + TaskId.EMPTY_TASK_ID, |
| 139 | + Collections.emptyMap(), |
| 140 | + new TaskResourceStats(new HashMap<>() { |
| 141 | + { |
| 142 | + put("dummy-type1", new TaskResourceUsage(10, 20)); |
| 143 | + } |
| 144 | + }, new TaskThreadUsage(30, 40)), |
| 145 | + 2L |
| 146 | + ); |
| 147 | + |
115 | 148 | public void testTaskCounts() {
|
116 | 149 | // Run only on data nodes
|
117 | 150 | ListTasksResponse response = client().admin()
|
@@ -879,46 +912,77 @@ public void testNodeNotFoundButTaskFound() throws Exception {
|
879 | 912 | // Save a fake task that looks like it is from a node that isn't part of the cluster
|
880 | 913 | CyclicBarrier b = new CyclicBarrier(2);
|
881 | 914 | TaskResultsService resultsService = internalCluster().getInstance(TaskResultsService.class);
|
882 |
| - resultsService.storeResult( |
883 |
| - new TaskResult( |
884 |
| - new TaskInfo( |
885 |
| - new TaskId("fake", 1), |
886 |
| - "test", |
887 |
| - "test", |
888 |
| - "", |
889 |
| - null, |
890 |
| - 0, |
891 |
| - 0, |
892 |
| - false, |
893 |
| - false, |
894 |
| - TaskId.EMPTY_TASK_ID, |
895 |
| - Collections.emptyMap(), |
896 |
| - null |
897 |
| - ), |
898 |
| - new RuntimeException("test") |
899 |
| - ), |
900 |
| - new ActionListener<Void>() { |
| 915 | + resultsService.storeResult(new TaskResult(taskInfo, new RuntimeException("test")), new ActionListener<Void>() { |
| 916 | + @Override |
| 917 | + public void onResponse(Void response) { |
| 918 | + try { |
| 919 | + b.await(); |
| 920 | + } catch (InterruptedException | BrokenBarrierException e) { |
| 921 | + onFailure(e); |
| 922 | + } |
| 923 | + } |
| 924 | + |
| 925 | + @Override |
| 926 | + public void onFailure(Exception e) { |
| 927 | + throw new RuntimeException(e); |
| 928 | + } |
| 929 | + }); |
| 930 | + b.await(); |
| 931 | + |
| 932 | + // Now we can find it! |
| 933 | + GetTaskResponse response = expectFinishedTask(new TaskId("fake:1")); |
| 934 | + TaskResult taskResult = response.getTask(); |
| 935 | + TaskInfo task = taskResult.getTask(); |
| 936 | + |
| 937 | + assertEquals("fake", task.getTaskId().getNodeId()); |
| 938 | + assertEquals(1, task.getTaskId().getId()); |
| 939 | + assertEquals("test_type", task.getType()); |
| 940 | + assertEquals("test_action", task.getAction()); |
| 941 | + assertEquals("test_description", task.getDescription()); |
| 942 | + assertEquals(0L, task.getStartTime()); |
| 943 | + assertEquals(1L, task.getRunningTimeNanos()); |
| 944 | + assertFalse(task.isCancellable()); |
| 945 | + assertFalse(task.isCancelled()); |
| 946 | + assertEquals(TaskId.EMPTY_TASK_ID, task.getParentTaskId()); |
| 947 | + assertEquals(1, task.getResourceStats().getResourceUsageInfo().size()); |
| 948 | + assertEquals(30, task.getResourceStats().getThreadUsage().getThreadExecutions()); |
| 949 | + assertEquals(40, task.getResourceStats().getThreadUsage().getActiveThreads()); |
| 950 | + assertEquals(Long.valueOf(2L), task.getCancellationStartTime()); |
| 951 | + |
| 952 | + assertNotNull(taskResult.getError()); |
| 953 | + assertNull(taskResult.getResponse()); |
| 954 | + } |
| 955 | + |
| 956 | + public void testStoreTaskResultFailsDueToMissingIndexMappingFields() throws IOException { |
| 957 | + // given |
| 958 | + TaskResultsService resultsService = spy(internalCluster().getInstance(TaskResultsService.class)); |
| 959 | + |
| 960 | + InputStream mockInputStream = getClass().getResourceAsStream("/org/opensearch/tasks/missing-fields-task-index-mapping.json"); |
| 961 | + ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| 962 | + Streams.copy(mockInputStream, out); |
| 963 | + String mockJsonString = out.toString(StandardCharsets.UTF_8.name()); |
| 964 | + |
| 965 | + // when & then |
| 966 | + doReturn(mockJsonString).when(resultsService).taskResultIndexMapping(); |
| 967 | + |
| 968 | + CompletionException thrown = assertThrows(CompletionException.class, () -> { |
| 969 | + CompletableFuture<Void> future = new CompletableFuture<>(); |
| 970 | + |
| 971 | + resultsService.storeResult(new TaskResult(taskInfo, new RuntimeException("test")), new ActionListener<Void>() { |
901 | 972 | @Override
|
902 | 973 | public void onResponse(Void response) {
|
903 |
| - try { |
904 |
| - b.await(); |
905 |
| - } catch (InterruptedException | BrokenBarrierException e) { |
906 |
| - onFailure(e); |
907 |
| - } |
| 974 | + future.complete(null); |
908 | 975 | }
|
909 | 976 |
|
910 | 977 | @Override
|
911 | 978 | public void onFailure(Exception e) {
|
912 |
| - throw new RuntimeException(e); |
| 979 | + future.completeExceptionally(e); |
913 | 980 | }
|
914 |
| - } |
915 |
| - ); |
916 |
| - b.await(); |
| 981 | + }); |
917 | 982 |
|
918 |
| - // Now we can find it! |
919 |
| - GetTaskResponse response = expectFinishedTask(new TaskId("fake:1")); |
920 |
| - assertEquals("test", response.getTask().getTask().getAction()); |
921 |
| - assertNotNull(response.getTask().getError()); |
922 |
| - assertNull(response.getTask().getResponse()); |
| 983 | + future.join(); |
| 984 | + }); |
| 985 | + |
| 986 | + assertTrue(thrown.getCause() instanceof StrictDynamicMappingException); |
923 | 987 | }
|
924 | 988 | }
|
0 commit comments