Modify state of VerifyRepositoryResponse for bwc (#30762)
The VerifyRepositoryResponse class holds a DiscoveryNode[], but the nodes themselves are not serialized to a REST API consumer. Since we do not want to put all of a DiscoveryNode over the wire, be it REST or Transport since its unused, this change introduces a BWC compatible change in ser/deser of the Response. Anything 6.4 and above will read/write a NodeView, and anything prior will read/write a DiscoveryNode. Further changes to 7.0 will be introduced to remove the BWC shim and only read/write NodeView, and hold a List<NodeView> as the VerifyRepositoryResponse internal state.
This commit is contained in:
parent
0fc22de336
commit
a8cea90e10
|
@ -19,23 +19,112 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.admin.cluster.repositories.verify;
|
package org.elasticsearch.action.admin.cluster.repositories.verify;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionResponse;
|
import org.elasticsearch.action.ActionResponse;
|
||||||
import org.elasticsearch.cluster.ClusterName;
|
import org.elasticsearch.cluster.ClusterName;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister repository response
|
* Verify repository response
|
||||||
*/
|
*/
|
||||||
public class VerifyRepositoryResponse extends ActionResponse implements ToXContentObject {
|
public class VerifyRepositoryResponse extends ActionResponse implements ToXContentObject {
|
||||||
|
|
||||||
private DiscoveryNode[] nodes;
|
static final String NODES = "nodes";
|
||||||
|
static final String NAME = "name";
|
||||||
|
|
||||||
|
public static class NodeView implements Writeable, ToXContentObject {
|
||||||
|
private static final ObjectParser.NamedObjectParser<NodeView, Void> PARSER;
|
||||||
|
static {
|
||||||
|
ObjectParser<NodeView, Void> internalParser = new ObjectParser<>(NODES);
|
||||||
|
internalParser.declareString(NodeView::setName, new ParseField(NAME));
|
||||||
|
PARSER = (p, v, name) -> internalParser.parse(p, new NodeView(name), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String nodeId;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
public NodeView(String nodeId) { this.nodeId = nodeId; }
|
||||||
|
|
||||||
|
public NodeView(String nodeId, String name) {
|
||||||
|
this(nodeId);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeView(StreamInput in) throws IOException {
|
||||||
|
this(in.readString(), in.readString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
out.writeString(nodeId);
|
||||||
|
out.writeString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public String getNodeId() { return nodeId; }
|
||||||
|
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startObject(nodeId);
|
||||||
|
{
|
||||||
|
builder.field(NAME, name);
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary method that allows turning a {@link NodeView} into a {@link DiscoveryNode}. This representation will never be used in
|
||||||
|
* practice, because in >= 6.4 a consumer of the response will only be able to retrieve a representation of {@link NodeView}
|
||||||
|
* objects.
|
||||||
|
*
|
||||||
|
* Effectively this will be used to hold the state of the object in 6.x so there is no need to have 2 backing objects that
|
||||||
|
* represent the state of the Response. In practice these will always be read by a consumer as a NodeView, but it eases the
|
||||||
|
* transition to master which will not contain any representation of a {@link DiscoveryNode}.
|
||||||
|
*/
|
||||||
|
DiscoveryNode convertToDiscoveryNode() {
|
||||||
|
return new DiscoveryNode(name, nodeId, "", "", "", new TransportAddress(TransportAddress.META_ADDRESS, 0),
|
||||||
|
Collections.emptyMap(), Collections.emptySet(), Version.CURRENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NodeView other = (NodeView) obj;
|
||||||
|
return Objects.equals(nodeId, other.nodeId) &&
|
||||||
|
Objects.equals(name, other.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(nodeId, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DiscoveryNode> nodes;
|
||||||
|
|
||||||
private ClusterName clusterName;
|
private ClusterName clusterName;
|
||||||
|
|
||||||
|
@ -45,52 +134,55 @@ public class VerifyRepositoryResponse extends ActionResponse implements ToXConte
|
||||||
|
|
||||||
public VerifyRepositoryResponse(ClusterName clusterName, DiscoveryNode[] nodes) {
|
public VerifyRepositoryResponse(ClusterName clusterName, DiscoveryNode[] nodes) {
|
||||||
this.clusterName = clusterName;
|
this.clusterName = clusterName;
|
||||||
this.nodes = nodes;
|
this.nodes = Arrays.asList(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
this.nodes = in.readList(NodeView::new).stream().map(n -> n.convertToDiscoveryNode()).collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
clusterName = new ClusterName(in);
|
clusterName = new ClusterName(in);
|
||||||
nodes = new DiscoveryNode[in.readVInt()];
|
this.nodes = in.readList(DiscoveryNode::new);
|
||||||
for (int i=0; i<nodes.length; i++){
|
|
||||||
nodes[i] = new DiscoveryNode(in);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
super.writeTo(out);
|
super.writeTo(out);
|
||||||
|
if (Version.CURRENT.onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
out.writeList(getNodes());
|
||||||
|
} else {
|
||||||
clusterName.writeTo(out);
|
clusterName.writeTo(out);
|
||||||
out.writeVInt(nodes.length);
|
out.writeList(nodes);
|
||||||
for (DiscoveryNode node : nodes) {
|
|
||||||
node.writeTo(out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscoveryNode[] getNodes() {
|
public List<NodeView> getNodes() {
|
||||||
return nodes;
|
return nodes.stream().map(dn -> new NodeView(dn.getId(), dn.getName())).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterName getClusterName() {
|
public ClusterName getClusterName() {
|
||||||
return clusterName;
|
return clusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Fields {
|
|
||||||
static final String NODES = "nodes";
|
|
||||||
static final String NAME = "name";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
builder.startObject(Fields.NODES);
|
{
|
||||||
|
builder.startObject(NODES);
|
||||||
|
{
|
||||||
for (DiscoveryNode node : nodes) {
|
for (DiscoveryNode node : nodes) {
|
||||||
builder.startObject(node.getId());
|
builder.startObject(node.getId());
|
||||||
builder.field(Fields.NAME, node.getName());
|
{
|
||||||
builder.endObject();
|
builder.field(NAME, node.getName());
|
||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ public class RepositoryBlocksIT extends ESIntegTestCase {
|
||||||
try {
|
try {
|
||||||
setClusterReadOnly(true);
|
setClusterReadOnly(true);
|
||||||
VerifyRepositoryResponse response = client().admin().cluster().prepareVerifyRepository("test-repo-blocks").execute().actionGet();
|
VerifyRepositoryResponse response = client().admin().cluster().prepareVerifyRepository("test-repo-blocks").execute().actionGet();
|
||||||
assertThat(response.getNodes().length, equalTo(cluster().numDataAndMasterNodes()));
|
assertThat(response.getNodes().size(), equalTo(cluster().numDataAndMasterNodes()));
|
||||||
} finally {
|
} finally {
|
||||||
setClusterReadOnly(false);
|
setClusterReadOnly(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class SnapshotBlocksIT extends ESIntegTestCase {
|
||||||
|
|
||||||
logger.info("--> verify the repository");
|
logger.info("--> verify the repository");
|
||||||
VerifyRepositoryResponse verifyResponse = client().admin().cluster().prepareVerifyRepository(REPOSITORY_NAME).get();
|
VerifyRepositoryResponse verifyResponse = client().admin().cluster().prepareVerifyRepository(REPOSITORY_NAME).get();
|
||||||
assertThat(verifyResponse.getNodes().length, equalTo(cluster().numDataAndMasterNodes()));
|
assertThat(verifyResponse.getNodes().size(), equalTo(cluster().numDataAndMasterNodes()));
|
||||||
|
|
||||||
logger.info("--> create a snapshot");
|
logger.info("--> create a snapshot");
|
||||||
CreateSnapshotResponse snapshotResponse = client().admin().cluster().prepareCreateSnapshot(REPOSITORY_NAME, SNAPSHOT_NAME)
|
CreateSnapshotResponse snapshotResponse = client().admin().cluster().prepareCreateSnapshot(REPOSITORY_NAME, SNAPSHOT_NAME)
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class RepositoriesIT extends AbstractSnapshotIntegTestCase {
|
||||||
logger.info("--> verify the repository");
|
logger.info("--> verify the repository");
|
||||||
int numberOfFiles = FileSystemUtils.files(location).length;
|
int numberOfFiles = FileSystemUtils.files(location).length;
|
||||||
VerifyRepositoryResponse verifyRepositoryResponse = client.admin().cluster().prepareVerifyRepository("test-repo-1").get();
|
VerifyRepositoryResponse verifyRepositoryResponse = client.admin().cluster().prepareVerifyRepository("test-repo-1").get();
|
||||||
assertThat(verifyRepositoryResponse.getNodes().length, equalTo(cluster().numDataAndMasterNodes()));
|
assertThat(verifyRepositoryResponse.getNodes().size(), equalTo(cluster().numDataAndMasterNodes()));
|
||||||
|
|
||||||
logger.info("--> verify that we didn't leave any files as a result of verification");
|
logger.info("--> verify that we didn't leave any files as a result of verification");
|
||||||
assertThat(FileSystemUtils.files(location).length, equalTo(numberOfFiles));
|
assertThat(FileSystemUtils.files(location).length, equalTo(numberOfFiles));
|
||||||
|
|
Loading…
Reference in New Issue