Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

High-level client: list tasks failure to not lose nodeId #31001

Merged
merged 2 commits into from
Jun 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ public String getDetailedMessage() {
}
}


/**
* Retrieve the innermost cause of this exception, if none, returns the current exception.
*/
Expand All @@ -292,7 +291,7 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeMapOfLists(headers, StreamOutput::writeString, StreamOutput::writeString);
out.writeMapOfLists(metadata, StreamOutput::writeString, StreamOutput::writeString);
} else {
HashMap<String, List<String>> finalHeaders = new HashMap<>(headers.size() + metadata.size());
Map<String, List<String>> finalHeaders = new HashMap<>(headers.size() + metadata.size());
finalHeaders.putAll(headers);
finalHeaders.putAll(metadata);
out.writeMapOfLists(finalHeaders, StreamOutput::writeString, StreamOutput::writeString);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;

Expand All @@ -48,4 +49,9 @@ public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeOptionalString(nodeId);
}

@Override
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("node_id", nodeId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,6 @@ public static ListTasksResponse fromXContent(XContentParser parser) {

@Override
public String toString() {
return Strings.toString(this);
return Strings.toString(this, true, true);
}
}
3 changes: 1 addition & 2 deletions server/src/main/java/org/elasticsearch/tasks/TaskInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParserHelper;
import org.elasticsearch.common.xcontent.ToXContent.Params;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
Expand Down Expand Up @@ -259,7 +258,7 @@ public static TaskInfo fromXContent(XContentParser parser) {

@Override
public String toString() {
return Strings.toString(this);
return Strings.toString(this, true, true);
}

// Implements equals and hashCode for testing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,78 +23,143 @@
import org.elasticsearch.action.FailedNodeException;
import org.elasticsearch.action.TaskOperationFailure;
import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.AbstractXContentTestCase;

import java.io.IOException;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.instanceOf;

public class ListTasksResponseTests extends AbstractXContentTestCase<ListTasksResponse> {

public void testEmptyToString() {
assertEquals("{\"tasks\":[]}", new ListTasksResponse().toString());
assertEquals("{\n" +
" \"tasks\" : [ ]\n" +
"}", new ListTasksResponse().toString());
}

public void testNonEmptyToString() {
TaskInfo info = new TaskInfo(
new TaskId("node1", 1), "dummy-type", "dummy-action", "dummy-description", null, 0, 1, true, new TaskId("node1", 0),
Collections.singletonMap("foo", "bar"));
ListTasksResponse tasksResponse = new ListTasksResponse(singletonList(info), emptyList(), emptyList());
assertEquals("{\"tasks\":[{\"node\":\"node1\",\"id\":1,\"type\":\"dummy-type\",\"action\":\"dummy-action\","
+ "\"description\":\"dummy-description\",\"start_time_in_millis\":0,\"running_time_in_nanos\":1,\"cancellable\":true,"
+ "\"parent_task_id\":\"node1:0\",\"headers\":{\"foo\":\"bar\"}}]}", tasksResponse.toString());
assertEquals("{\n" +
" \"tasks\" : [\n" +
" {\n" +
" \"node\" : \"node1\",\n" +
" \"id\" : 1,\n" +
" \"type\" : \"dummy-type\",\n" +
" \"action\" : \"dummy-action\",\n" +
" \"description\" : \"dummy-description\",\n" +
" \"start_time\" : \"1970-01-01T00:00:00.000Z\",\n" +
" \"start_time_in_millis\" : 0,\n" +
" \"running_time\" : \"1nanos\",\n" +
" \"running_time_in_nanos\" : 1,\n" +
" \"cancellable\" : true,\n" +
" \"parent_task_id\" : \"node1:0\",\n" +
" \"headers\" : {\n" +
" \"foo\" : \"bar\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}", tasksResponse.toString());
}

@Override
protected ListTasksResponse createTestInstance() {
//failures are tested separately, so we can test xcontent equivalence at least when we have no failures
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

return new ListTasksResponse(randomTasks(), Collections.emptyList(), Collections.emptyList());
}

private static List<TaskInfo> randomTasks() {
List<TaskInfo> tasks = new ArrayList<>();
for (int i = 0; i < randomInt(10); i++) {
tasks.add(TaskInfoTests.randomTaskInfo());
}
List<TaskOperationFailure> taskFailures = new ArrayList<>();
for (int i = 0; i < randomInt(5); i++) {
taskFailures.add(new TaskOperationFailure(
randomAlphaOfLength(5), randomNonNegativeLong(), new IllegalStateException("message")));
}
return new ListTasksResponse(tasks, taskFailures, Collections.singletonList(new FailedNodeException("", "message", null)));
return tasks;
}

@Override
protected ListTasksResponse doParseInstance(XContentParser parser) throws IOException {
protected ListTasksResponse doParseInstance(XContentParser parser) {
return ListTasksResponse.fromXContent(parser);
}

@Override
protected boolean supportsUnknownFields() {
return false;
return true;
}

@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
//status and headers hold arbitrary content, we can't inject random fields in them
return field -> field.endsWith("status") || field.endsWith("headers");
}

@Override
protected void assertEqualInstances(ListTasksResponse expectedInstance, ListTasksResponse newInstance) {
assertNotSame(expectedInstance, newInstance);
assertThat(newInstance.getTasks(), equalTo(expectedInstance.getTasks()));
assertThat(newInstance.getNodeFailures().size(), equalTo(1));
for (ElasticsearchException failure : newInstance.getNodeFailures()) {
assertThat(failure, notNullValue());
assertThat(failure.getMessage(), equalTo("Elasticsearch exception [type=failed_node_exception, reason=message]"));
assertThat(newInstance.getNodeFailures().size(), equalTo(expectedInstance.getNodeFailures().size()));
for (int i = 0; i < newInstance.getNodeFailures().size(); i++) {
ElasticsearchException newException = newInstance.getNodeFailures().get(i);
ElasticsearchException expectedException = expectedInstance.getNodeFailures().get(i);
assertThat(newException.getMetadata("es.node_id").get(0), equalTo(((FailedNodeException)expectedException).nodeId()));
assertThat(newException.getMessage(), equalTo("Elasticsearch exception [type=failed_node_exception, reason=error message]"));
assertThat(newException.getCause(), instanceOf(ElasticsearchException.class));
ElasticsearchException cause = (ElasticsearchException) newException.getCause();
assertThat(cause.getMessage(), equalTo("Elasticsearch exception [type=connect_exception, reason=null]"));
}
assertThat(newInstance.getTaskFailures().size(), equalTo(expectedInstance.getTaskFailures().size()));
for (int i = 0; i < newInstance.getTaskFailures().size(); i++) {
TaskOperationFailure newFailure = newInstance.getTaskFailures().get(i);
TaskOperationFailure expectedFailure = expectedInstance.getTaskFailures().get(i);
assertThat(newFailure.getNodeId(), equalTo(expectedFailure.getNodeId()));
assertThat(newFailure.getTaskId(), equalTo(expectedFailure.getTaskId()));
assertThat(newFailure.getStatus(), equalTo(expectedFailure.getStatus()));
assertThat(newFailure.getCause(), instanceOf(ElasticsearchException.class));
ElasticsearchException cause = (ElasticsearchException) newFailure.getCause();
assertThat(cause.getMessage(), equalTo("Elasticsearch exception [type=illegal_state_exception, reason=null]"));
}
}

@Override
protected boolean assertToXContentEquivalence() {
return false;
/**
* Test parsing {@link ListTasksResponse} with inner failures as they don't support asserting on xcontent equivalence, given that
* exceptions are not parsed back as the same original class. We run the usual {@link AbstractXContentTestCase#testFromXContent()}
* without failures, and this other test with failures where we disable asserting on xcontent equivalence at the end.
*/
public void testFromXContentWithFailures() throws IOException {
Supplier<ListTasksResponse> instanceSupplier = ListTasksResponseTests::createTestInstanceWithFailures;
//with random fields insertion in the inner exceptions, some random stuff may be parsed back as metadata,
//but that does not bother our assertions, as we only want to test that we don't break.
boolean supportsUnknownFields = true;
//exceptions are not of the same type whenever parsed back
boolean assertToXContentEquivalence = false;
AbstractXContentTestCase.testFromXContent(NUMBER_OF_TEST_RUNS, instanceSupplier, supportsUnknownFields, Strings.EMPTY_ARRAY,
getRandomFieldsExcludeFilter(), this::createParser, this::doParseInstance,
this::assertEqualInstances, assertToXContentEquivalence);
}

private static ListTasksResponse createTestInstanceWithFailures() {
int numNodeFailures = randomIntBetween(0, 3);
List<FailedNodeException> nodeFailures = new ArrayList<>(numNodeFailures);
for (int i = 0; i < numNodeFailures; i++) {
nodeFailures.add(new FailedNodeException(randomAlphaOfLength(5), "error message", new ConnectException()));
}
int numTaskFailures = randomIntBetween(0, 3);
List<TaskOperationFailure> taskFailures = new ArrayList<>(numTaskFailures);
for (int i = 0; i < numTaskFailures; i++) {
taskFailures.add(new TaskOperationFailure(randomAlphaOfLength(5), randomLong(), new IllegalStateException()));
}
return new ListTasksResponse(randomTasks(), taskFailures, nodeFailures);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,12 @@ protected boolean supportsUnknownFields() {

@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
//status and headers hold arbitrary content, we can't inject random fields in them
return field -> "status".equals(field) || "headers".equals(field);
}

@Override
protected TaskInfo mutateInstance(TaskInfo info) throws IOException {
protected TaskInfo mutateInstance(TaskInfo info) {
switch (between(0, 9)) {
case 0:
TaskId taskId = new TaskId(info.getTaskId().getNodeId() + randomAlphaOfLength(5), info.getTaskId().getId());
Expand Down