summaryrefslogtreecommitdiff
path: root/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java
diff options
context:
space:
mode:
authorTanguy Leroux <tlrx.dev@gmail.com>2017-02-02 17:00:16 +0100
committerGitHub <noreply@github.com>2017-02-02 17:00:16 +0100
commitf86fd628212a13830605695f3d9c6d0b5671286e (patch)
tree7f63b46522d4053cbad7f7c5a6cf2816ee7621bd /core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java
parent73bf29072fd313d657b80fe015214b2f1256c5da (diff)
Parse elasticsearch exception's root causes (#22924)
This commit change ElasticsearchException.failureFromXContent() method so that it now parses root causes which were ignored before, and adds them as suppressed exceptions of the returned exception.
Diffstat (limited to 'core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java')
-rw-r--r--core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java229
1 files changed, 197 insertions, 32 deletions
diff --git a/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java b/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java
index 4dbadfeb53..bca9e7d243 100644
--- a/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java
+++ b/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java
@@ -20,11 +20,14 @@
package org.elasticsearch;
import org.apache.lucene.util.Constants;
+import org.elasticsearch.action.NoShardAvailableActionException;
import org.elasticsearch.action.RoutingMissingException;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.action.support.broadcast.BroadcastShardOperationFailedException;
+import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.cluster.block.ClusterBlockException;
+import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.breaker.CircuitBreakingException;
@@ -45,7 +48,11 @@ import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.QueryShardException;
import org.elasticsearch.index.shard.IndexShardRecoveringException;
import org.elasticsearch.index.shard.ShardId;
+import org.elasticsearch.node.NodeClosedException;
+import org.elasticsearch.repositories.RepositoryException;
import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.script.ScriptException;
+import org.elasticsearch.search.SearchContextMissingException;
import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.test.ESTestCase;
@@ -56,6 +63,7 @@ import org.hamcrest.Matcher;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.nio.file.FileAlreadyExistsException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -64,6 +72,7 @@ import java.util.Map;
import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
+import static java.util.Collections.singletonList;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.hasItems;
@@ -536,20 +545,31 @@ public class ElasticsearchExceptionTests extends ESTestCase {
// of other types than list of strings.
BytesReference originalBytes;
try (XContentBuilder builder = XContentBuilder.builder(xContent)) {
- builder.startObject();
- builder.field("metadata_int", 1);
- builder.array("metadata_array_of_ints", new int[]{8, 13, 21});
- builder.field("reason", "Custom reason");
- builder.array("metadata_array_of_boolean", new boolean[]{false, false});
- builder.field("type", "custom_exception");
- builder.field("metadata_long", 1L);
- builder.array("metadata_array_of_longs", new long[]{2L, 3L, 5L});
- builder.field("metadata_other", "some metadata");
- builder.startObject("header");
- builder.field("header_string", "some header");
- builder.array("header_array_of_strings", new String[]{"foo", "bar", "baz"});
- builder.endObject();
- builder.endObject();
+ builder.startObject()
+ .field("metadata_int", 1)
+ .array("metadata_array_of_ints", new int[]{8, 13, 21})
+ .field("reason", "Custom reason")
+ .array("metadata_array_of_boolean", new boolean[]{false, false})
+ .startArray("metadata_array_of_objects")
+ .startObject()
+ .field("object_array_one", "value_one")
+ .endObject()
+ .startObject()
+ .field("object_array_two", "value_two")
+ .endObject()
+ .endArray()
+ .field("type", "custom_exception")
+ .field("metadata_long", 1L)
+ .array("metadata_array_of_longs", new long[]{2L, 3L, 5L})
+ .field("metadata_other", "some metadata")
+ .startObject("header")
+ .field("header_string", "some header")
+ .array("header_array_of_strings", new String[]{"foo", "bar", "baz"})
+ .endObject()
+ .startObject("metadata_object")
+ .field("object_field", "value")
+ .endObject()
+ .endObject();
originalBytes = builder.bytes();
}
@@ -616,17 +636,19 @@ public class ElasticsearchExceptionTests extends ESTestCase {
assertEquals(0, parsedFailure.getMetadata().size());
}
- public void testFailureToAndFromXContent() throws IOException {
+ public void testFailureToAndFromXContentWithNoDetails() throws IOException {
final XContent xContent = randomFrom(XContentType.values()).xContent();
- final Tuple<Throwable, ElasticsearchException> exceptions = randomExceptions();
- final boolean detailed = randomBoolean();
- Exception failure = (Exception) exceptions.v1();
+ final Exception failure = (Exception) randomExceptions().v1();
BytesReference failureBytes = XContentHelper.toXContent((builder, params) -> {
- ElasticsearchException.generateFailureXContent(builder, params, failure, detailed);
+ ElasticsearchException.generateFailureXContent(builder, params, failure, false);
return builder;
}, xContent.type(), randomBoolean());
+ try (XContentParser parser = createParser(xContent, failureBytes)) {
+ failureBytes = shuffleXContent(parser, randomBoolean()).bytes();
+ }
+
ElasticsearchException parsedFailure;
try (XContentParser parser = createParser(xContent, failureBytes)) {
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
@@ -637,21 +659,151 @@ public class ElasticsearchExceptionTests extends ESTestCase {
}
assertNotNull(parsedFailure);
- ElasticsearchException expected = exceptions.v2();
- if (detailed) {
- assertDeepEquals(expected, parsedFailure);
+ String reason;
+ if (failure instanceof ElasticsearchException) {
+ reason = failure.getClass().getSimpleName() + "[" + failure.getMessage() + "]";
} else {
- String reason;
- if (failure instanceof ElasticsearchException) {
- reason = failure.getClass().getSimpleName() + "[" + failure.getMessage() + "]";
- } else {
- reason = "No ElasticsearchException found";
- }
- assertEquals(ElasticsearchException.buildMessage("exception", reason, null), parsedFailure.getMessage());
- assertEquals(0, parsedFailure.getHeaders().size());
- assertEquals(0, parsedFailure.getMetadata().size());
- assertNull(parsedFailure.getCause());
+ reason = "No ElasticsearchException found";
}
+ assertEquals(ElasticsearchException.buildMessage("exception", reason, null), parsedFailure.getMessage());
+ assertEquals(0, parsedFailure.getHeaders().size());
+ assertEquals(0, parsedFailure.getMetadata().size());
+ assertNull(parsedFailure.getCause());
+ }
+
+ public void testFailureToAndFromXContentWithDetails() throws IOException {
+ final XContent xContent = randomFrom(XContentType.values()).xContent();
+
+ Exception failure;
+ Throwable failureCause;
+ ElasticsearchException expected;
+ ElasticsearchException expectedCause;
+ ElasticsearchException suppressed;
+
+ switch (randomIntBetween(0, 6)) {
+ case 0: // Simple elasticsearch exception without cause
+ failure = new NoNodeAvailableException("A");
+
+ expected = new ElasticsearchException("Elasticsearch exception [type=no_node_available_exception, reason=A]");
+ expected.addSuppressed(new ElasticsearchException("Elasticsearch exception [type=no_node_available_exception, reason=A]"));
+ break;
+
+ case 1: // Simple elasticsearch exception with headers (other metadata of type number are not parsed)
+ failure = new CircuitBreakingException("B", 5_000, 2_000);
+ ((ElasticsearchException) failure).addHeader("header_name", "0", "1");
+ expected = new ElasticsearchException("Elasticsearch exception [type=circuit_breaking_exception, reason=B]");
+ expected.addHeader("header_name", "0", "1");
+ suppressed = new ElasticsearchException("Elasticsearch exception [type=circuit_breaking_exception, reason=B]");
+ suppressed.addHeader("header_name", "0", "1");
+ expected.addSuppressed(suppressed);
+ break;
+
+ case 2: // Elasticsearch exception with a cause, headers and parsable metadata
+ failureCause = new NullPointerException("var is null");
+ failure = new ScriptException("C", failureCause, singletonList("stack"), "test", "painless");
+ ((ElasticsearchException) failure).addHeader("script_name", "my_script");
+
+ expectedCause = new ElasticsearchException("Elasticsearch exception [type=null_pointer_exception, reason=var is null]");
+ expected = new ElasticsearchException("Elasticsearch exception [type=script_exception, reason=C]", expectedCause);
+ expected.addHeader("script_name", "my_script");
+ expected.addMetadata("es.lang", "painless");
+ expected.addMetadata("es.script", "test");
+ expected.addMetadata("es.script_stack", "stack");
+ suppressed = new ElasticsearchException("Elasticsearch exception [type=script_exception, reason=C]");
+ suppressed.addHeader("script_name", "my_script");
+ suppressed.addMetadata("es.lang", "painless");
+ suppressed.addMetadata("es.script", "test");
+ suppressed.addMetadata("es.script_stack", "stack");
+ expected.addSuppressed(suppressed);
+ break;
+
+ case 3: // JDK exception without cause
+ failure = new IllegalStateException("D");
+
+ expected = new ElasticsearchException("Elasticsearch exception [type=illegal_state_exception, reason=D]");
+ suppressed = new ElasticsearchException("Elasticsearch exception [type=illegal_state_exception, reason=D]");
+ expected.addSuppressed(suppressed);
+ break;
+
+ case 4: // JDK exception with cause
+ failureCause = new RoutingMissingException("idx", "type", "id");
+ failure = new RuntimeException("E", failureCause);
+
+ expectedCause = new ElasticsearchException("Elasticsearch exception [type=routing_missing_exception, " +
+ "reason=routing is required for [idx]/[type]/[id]]");
+ expectedCause.addMetadata("es.index", "idx");
+ expectedCause.addMetadata("es.index_uuid", "_na_");
+ expected = new ElasticsearchException("Elasticsearch exception [type=runtime_exception, reason=E]", expectedCause);
+ suppressed = new ElasticsearchException("Elasticsearch exception [type=runtime_exception, reason=E]");
+ expected.addSuppressed(suppressed);
+ break;
+
+ case 5: // Wrapped exception with cause
+ failureCause = new FileAlreadyExistsException("File exists");
+ failure = new BroadcastShardOperationFailedException(new ShardId("_index", "_uuid", 5), "F", failureCause);
+
+ expected = new ElasticsearchException("Elasticsearch exception [type=file_already_exists_exception, reason=File exists]");
+ // strangely, the wrapped exception appears as the root cause...
+ suppressed = new ElasticsearchException("Elasticsearch exception [type=broadcast_shard_operation_failed_exception, " +
+ "reason=F]");
+ expected.addSuppressed(suppressed);
+ break;
+
+ case 6: // SearchPhaseExecutionException with cause and multiple failures
+ DiscoveryNode node = new DiscoveryNode("node_g", buildNewFakeTransportAddress(), Version.CURRENT);
+ failureCause = new NodeClosedException(node);
+ failureCause = new NoShardAvailableActionException(new ShardId("_index_g", "_uuid_g", 6), "node_g", failureCause);
+ ShardSearchFailure[] shardFailures = new ShardSearchFailure[]{
+ new ShardSearchFailure(new ParsingException(0, 0, "Parsing g", null),
+ new SearchShardTarget("node_g", new ShardId(new Index("_index_g", "_uuid_g"), 61))),
+ new ShardSearchFailure(new RepositoryException("repository_g", "Repo"),
+ new SearchShardTarget("node_g", new ShardId(new Index("_index_g", "_uuid_g"), 62))),
+ new ShardSearchFailure(new SearchContextMissingException(0L), null)
+ };
+ failure = new SearchPhaseExecutionException("phase_g", "G", failureCause, shardFailures);
+
+ expectedCause = new ElasticsearchException("Elasticsearch exception [type=node_closed_exception, " +
+ "reason=node closed " + node + "]");
+ expectedCause = new ElasticsearchException("Elasticsearch exception [type=no_shard_available_action_exception, " +
+ "reason=node_g]", expectedCause);
+ expectedCause.addMetadata("es.index", "_index_g");
+ expectedCause.addMetadata("es.index_uuid", "_uuid_g");
+ expectedCause.addMetadata("es.shard", "6");
+
+ expected = new ElasticsearchException("Elasticsearch exception [type=search_phase_execution_exception, " +
+ "reason=G]", expectedCause);
+ expected.addMetadata("es.phase", "phase_g");
+
+ expected.addSuppressed(new ElasticsearchException("Elasticsearch exception [type=parsing_exception, reason=Parsing g]"));
+ expected.addSuppressed(new ElasticsearchException("Elasticsearch exception [type=repository_exception, " +
+ "reason=[repository_g] Repo]"));
+ expected.addSuppressed(new ElasticsearchException("Elasticsearch exception [type=search_context_missing_exception, " +
+ "reason=No search context found for id [0]]"));
+ break;
+ default:
+ throw new UnsupportedOperationException("Failed to generate randomized failure");
+ }
+
+ Exception finalFailure = failure;
+ BytesReference failureBytes = XContentHelper.toXContent((builder, params) -> {
+ ElasticsearchException.generateFailureXContent(builder, params, finalFailure, true);
+ return builder;
+ }, xContent.type(), randomBoolean());
+
+ try (XContentParser parser = createParser(xContent, failureBytes)) {
+ failureBytes = shuffleXContent(parser, randomBoolean()).bytes();
+ }
+
+ ElasticsearchException parsedFailure;
+ try (XContentParser parser = createParser(xContent, failureBytes)) {
+ assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
+ assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
+ parsedFailure = ElasticsearchException.failureFromXContent(parser);
+ assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
+ assertNull(parser.nextToken());
+ }
+
+ assertDeepEquals(expected, parsedFailure);
}
/**
@@ -686,6 +838,19 @@ public class ElasticsearchExceptionTests extends ESTestCase {
assertEquals(expected.getResourceType(), actual.getResourceType());
assertEquals(expected.getResourceId(), actual.getResourceId());
+ Throwable[] expectedSuppressed = expected.getSuppressed();
+ Throwable[] actualSuppressed = actual.getSuppressed();
+
+ if (expectedSuppressed == null) {
+ assertNull(actualSuppressed);
+ } else {
+ assertNotNull(actualSuppressed);
+ assertEquals(expectedSuppressed.length, actualSuppressed.length);
+ for (int i = 0; i < expectedSuppressed.length; i++) {
+ assertDeepEquals((ElasticsearchException) expectedSuppressed[i], (ElasticsearchException) actualSuppressed[i]);
+ }
+ }
+
expected = (ElasticsearchException) expected.getCause();
actual = (ElasticsearchException) actual.getCause();
if (expected == null) {