/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.cluster.reroute;
import org.elasticsearch.action.support.master.AcknowledgedRequest;
import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.cluster.routing.allocation.command.AllocateEmptyPrimaryAllocationCommand;
import org.elasticsearch.cluster.routing.allocation.command.AllocateReplicaAllocationCommand;
import org.elasticsearch.cluster.routing.allocation.command.AllocateStalePrimaryAllocationCommand;
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommand;
import org.elasticsearch.cluster.routing.allocation.command.CancelAllocationCommand;
import org.elasticsearch.cluster.routing.allocation.command.MoveAllocationCommand;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.admin.cluster.RestClusterRerouteAction;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.rest.FakeRestRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import static java.util.Collections.unmodifiableList;
import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;
/**
* Test for serialization and parsing of {@link ClusterRerouteRequest} and its commands. See the superclass for, well, everything.
*/
public class ClusterRerouteRequestTests extends ESTestCase {
private static final int ROUNDS = 30;
private final List<Supplier<AllocationCommand>> RANDOM_COMMAND_GENERATORS = unmodifiableList(
Arrays.<Supplier<AllocationCommand>> asList(
() -> new AllocateReplicaAllocationCommand(randomAlphaOfLengthBetween(2, 10), between(0, 1000),
randomAlphaOfLengthBetween(2, 10)),
() -> new AllocateEmptyPrimaryAllocationCommand(randomAlphaOfLengthBetween(2, 10), between(0, 1000),
randomAlphaOfLengthBetween(2, 10), randomBoolean()),
() -> new AllocateStalePrimaryAllocationCommand(randomAlphaOfLengthBetween(2, 10), between(0, 1000),
randomAlphaOfLengthBetween(2, 10), randomBoolean()),
() -> new CancelAllocationCommand(randomAlphaOfLengthBetween(2, 10), between(0, 1000),
randomAlphaOfLengthBetween(2, 10), randomBoolean()),
() -> new MoveAllocationCommand(randomAlphaOfLengthBetween(2, 10), between(0, 1000),
randomAlphaOfLengthBetween(2, 10), randomAlphaOfLengthBetween(2, 10))));
private final NamedWriteableRegistry namedWriteableRegistry;
public ClusterRerouteRequestTests() {
namedWriteableRegistry = new NamedWriteableRegistry(NetworkModule.getNamedWriteables());
}
private ClusterRerouteRequest randomRequest() {
ClusterRerouteRequest request = new ClusterRerouteRequest();
int commands = between(0, 10);
for (int i = 0; i < commands; i++) {
request.add(randomFrom(RANDOM_COMMAND_GENERATORS).get());
}
request.dryRun(randomBoolean());
request.explain(randomBoolean());
request.setRetryFailed(randomBoolean());
return request;
}
public void testEqualsAndHashCode() {
for (int round = 0; round < ROUNDS; round++) {
ClusterRerouteRequest request = randomRequest();
assertEquals(request, request);
assertEquals(request.hashCode(), request.hashCode());
ClusterRerouteRequest copy = new ClusterRerouteRequest()
.add(request.getCommands().commands().toArray(new AllocationCommand[0]));
copy.dryRun(request.dryRun()).explain(request.explain()).timeout(request.timeout()).setRetryFailed(request.isRetryFailed());
copy.masterNodeTimeout(request.masterNodeTimeout());
assertEquals(request, copy);
assertEquals(copy, request); // Commutative
assertEquals(request.hashCode(), copy.hashCode());
// Changing dryRun makes requests not equal
copy.dryRun(!copy.dryRun());
assertNotEquals(request, copy);
assertNotEquals(request.hashCode(), copy.hashCode());
copy.dryRun(!copy.dryRun());
assertEquals(request, copy);
assertEquals(request.hashCode(), copy.hashCode());
// Changing explain makes requests not equal
copy.explain(!copy.explain());
assertNotEquals(request, copy);
assertNotEquals(request.hashCode(), copy.hashCode());
copy.explain(!copy.explain());
assertEquals(request, copy);
assertEquals(request.hashCode(), copy.hashCode());
// Changing timeout makes requests not equal
copy.timeout(timeValueMillis(request.timeout().millis() + 1));
assertNotEquals(request, copy);
assertNotEquals(request.hashCode(), copy.hashCode());
copy.timeout(request.timeout());
assertEquals(request, copy);
assertEquals(request.hashCode(), copy.hashCode());
// Changing masterNodeTime makes requests not equal
copy.masterNodeTimeout(timeValueMillis(request.masterNodeTimeout().millis() + 1));
assertNotEquals(request, copy);
assertNotEquals(request.hashCode(), copy.hashCode());
copy.masterNodeTimeout(request.masterNodeTimeout());
assertEquals(request, copy);
assertEquals(request.hashCode(), copy.hashCode());
// Changing commands makes requests not equal
copy.add(randomFrom(RANDOM_COMMAND_GENERATORS).get());
assertNotEquals(request, copy);
// Can't check hashCode because we can't be sure that changing commands changes the hashCode. It usually does but might not.
}
}
public void testSerialization() throws IOException {
for (int round = 0; round < ROUNDS; round++) {
ClusterRerouteRequest request = randomRequest();
ClusterRerouteRequest copy = roundTripThroughBytes(request);
assertEquals(request, copy);
assertEquals(request.hashCode(), copy.hashCode());
assertNotSame(request, copy);
}
}
public void testParsing() throws IOException {
for (int round = 0; round < ROUNDS; round++) {
ClusterRerouteRequest request = randomRequest();
ClusterRerouteRequest copy = roundTripThroughRestRequest(request);
assertEquals(request, copy);
assertEquals(request.hashCode(), copy.hashCode());
assertNotSame(request, copy);
}
}
private ClusterRerouteRequest roundTripThroughBytes(ClusterRerouteRequest original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) {
original.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
ClusterRerouteRequest copy = new ClusterRerouteRequest();
copy.readFrom(in);
return copy;
}
}
}
private ClusterRerouteRequest roundTripThroughRestRequest(ClusterRerouteRequest original) throws IOException {
RestRequest restRequest = toRestRequest(original);
return RestClusterRerouteAction.createRequest(restRequest);
}
private RestRequest toRestRequest(ClusterRerouteRequest original) throws IOException {
Map<String, String> params = new HashMap<>();
XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()));
boolean hasBody = false;
if (randomBoolean()) {
builder.prettyPrint();
}
builder.startObject();
if (randomBoolean()) {
params.put("dry_run", Boolean.toString(original.dryRun()));
} else {
hasBody = true;
builder.field("dry_run", original.dryRun());
}
params.put("explain", Boolean.toString(original.explain()));
if (false == original.timeout().equals(AcknowledgedRequest.DEFAULT_ACK_TIMEOUT) || randomBoolean()) {
params.put("timeout", original.timeout().toString());
}
if (original.isRetryFailed() || randomBoolean()) {
params.put("retry_failed", Boolean.toString(original.isRetryFailed()));
}
if (false == original.masterNodeTimeout().equals(MasterNodeRequest.DEFAULT_MASTER_NODE_TIMEOUT) || randomBoolean()) {
params.put("master_timeout", original.masterNodeTimeout().toString());
}
if (original.getCommands() != null) {
hasBody = true;
original.getCommands().toXContent(builder, ToXContent.EMPTY_PARAMS);
}
builder.endObject();
FakeRestRequest.Builder requestBuilder = new FakeRestRequest.Builder(xContentRegistry());
requestBuilder.withParams(params);
if (hasBody) {
requestBuilder.withContent(builder.bytes(), builder.contentType());
}
return requestBuilder.build();
}
@Override
protected NamedXContentRegistry xContentRegistry() {
return new NamedXContentRegistry(NetworkModule.getNamedXContents());
}
}