Skip to content

Commit

Permalink
Add toXContent and fromXContent for DiscoveryNode and DiscoveryNodes
Browse files Browse the repository at this point in the history
Signed-off-by: Shivansh Arora <[email protected]>
  • Loading branch information
shiv0408 committed May 7, 2024
1 parent a228dfc commit 7d1a599
Show file tree
Hide file tree
Showing 5 changed files with 416 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,14 @@ public String toString() {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.value(toString());
}

public static TransportAddress fromString(String address) throws UnknownHostException {
String[] addressSplit = address.split(":");
if (addressSplit.length != 2) {
throw new IllegalArgumentException("address must be of the form [hostname/ip]:[port]");
}
String hostname = addressSplit[0];
int port = Integer.parseInt(addressSplit[1]);
return new TransportAddress(InetAddress.getByName(hostname), port);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
package org.opensearch.cluster.node;

import org.opensearch.Version;
import org.opensearch.cluster.metadata.Metadata;
import org.opensearch.common.UUIDs;
import org.opensearch.common.annotation.PublicApi;
import org.opensearch.common.settings.Setting;
Expand All @@ -43,6 +44,7 @@
import org.opensearch.core.common.transport.TransportAddress;
import org.opensearch.core.xcontent.ToXContentFragment;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.node.Node;

import java.io.IOException;
Expand All @@ -60,6 +62,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_API;
import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_PARAM;
import static org.opensearch.node.NodeRoleSettings.NODE_ROLES_SETTING;
import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_NODE_ATTRIBUTE_KEY_PREFIX;

Expand All @@ -72,6 +76,14 @@
public class DiscoveryNode implements Writeable, ToXContentFragment {

static final String COORDINATING_ONLY = "coordinating_only";
static final String KEY_NAME = "name";
static final String KEY_EPHEMERAL_ID = "ephemeral_id";
static final String KEY_HOST_NAME = "host_name";
static final String KEY_HOST_ADDRESS = "host_address";
static final String KEY_TRANSPORT_ADDRESS = "transport_address";
static final String KEY_ATTRIBUTES = "attributes";
static final String KEY_VERSION = "version";
static final String KEY_ROLES = "roles";

public static boolean nodeRequiresLocalStorage(Settings settings) {
boolean localStorageEnable = Node.NODE_LOCAL_STORAGE_SETTING.get(settings);
Expand Down Expand Up @@ -544,21 +556,97 @@ public String toString() {

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
Metadata.XContentContext context = Metadata.XContentContext.valueOf(params.param(CONTEXT_MODE_PARAM, CONTEXT_MODE_API));
builder.startObject(getId());
builder.field("name", getName());
builder.field("ephemeral_id", getEphemeralId());
builder.field("transport_address", getAddress().toString());
builder.field(KEY_NAME, getName());
builder.field(KEY_EPHEMERAL_ID, getEphemeralId());
builder.field(KEY_TRANSPORT_ADDRESS, getAddress().toString());

builder.startObject("attributes");
builder.startObject(KEY_ATTRIBUTES);
for (Map.Entry<String, String> entry : attributes.entrySet()) {
builder.field(entry.getKey(), entry.getValue());
}
builder.endObject();
if (context == Metadata.XContentContext.GATEWAY) {
builder.field(KEY_HOST_NAME, getHostName());
builder.field(KEY_HOST_ADDRESS, getHostAddress());
builder.field(KEY_VERSION, getVersion().toString());
builder.startArray(KEY_ROLES);
for (DiscoveryNodeRole role : roles) {
builder.value(role.roleName());
}
builder.endArray();
}

builder.endObject();
return builder;
}

public static DiscoveryNode fromXContent(XContentParser parser, String nodeId) throws IOException {
if (parser.currentToken() == null) {
parser.nextToken();
}
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
parser.nextToken();
}
if (parser.currentToken() != XContentParser.Token.FIELD_NAME) {
throw new IllegalArgumentException("expected field name but got a " + parser.currentToken());
}
String nodeName = null;
String hostName = null;
String hostAddress = null;
String ephemeralId = null;
TransportAddress transportAddress = null;
Map<String, String> attributes = new HashMap<>();
Set<DiscoveryNodeRole> roles = new HashSet<>();
Version version = null;
String currentFieldName = parser.currentName();
// token should be start object at this point
// XContentParser.Token token = parser.nextToken();
// if (token != XContentParser.Token.START_OBJECT) {
// throw new IllegalArgumentException("expected object but got a " + token);
// }
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (KEY_NAME.equals(currentFieldName)) {
nodeName = parser.text();
} else if (KEY_EPHEMERAL_ID.equals(currentFieldName)) {
ephemeralId = parser.text();
} else if (KEY_TRANSPORT_ADDRESS.equals(currentFieldName)) {
transportAddress = TransportAddress.fromString(parser.text());
} else if (KEY_HOST_NAME.equals(currentFieldName)) {
hostName = parser.text();
} else if (KEY_HOST_ADDRESS.equals(currentFieldName)) {
hostAddress = parser.text();
} else if (KEY_VERSION.equals(currentFieldName)) {
version = Version.fromString(parser.text());
}
} else if (token == XContentParser.Token.START_OBJECT) {
if (KEY_ATTRIBUTES.equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
attributes.put(currentFieldName, parser.text());
}
}
}
} else if (token == XContentParser.Token.START_ARRAY) {
if (KEY_ROLES.equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
roles.add(getRoleFromRoleName(parser.text()));
}
}
} else {
throw new IllegalArgumentException("unexpected token " + token);
}
}
return new DiscoveryNode(nodeName, nodeId, ephemeralId, hostName, hostAddress, transportAddress, attributes, roles, version);
}

private static Map<String, DiscoveryNodeRole> rolesToMap(final Stream<DiscoveryNodeRole> roles) {
return Collections.unmodifiableMap(roles.collect(Collectors.toMap(DiscoveryNodeRole::roleName, Function.identity())));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.opensearch.Version;
import org.opensearch.cluster.AbstractDiffable;
import org.opensearch.cluster.Diff;
import org.opensearch.cluster.metadata.Metadata;
import org.opensearch.common.Booleans;
import org.opensearch.common.Nullable;
import org.opensearch.common.annotation.PublicApi;
Expand All @@ -44,6 +45,9 @@
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.common.transport.TransportAddress;
import org.opensearch.core.xcontent.ToXContentFragment;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;

import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -59,14 +63,17 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_API;
import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_PARAM;

/**
* This class holds all {@link DiscoveryNode} in the cluster and provides convenience methods to
* access, modify merge / diff discovery nodes.
*
* @opensearch.api
*/
@PublicApi(since = "1.0.0")
public class DiscoveryNodes extends AbstractDiffable<DiscoveryNodes> implements Iterable<DiscoveryNode> {
public class DiscoveryNodes extends AbstractDiffable<DiscoveryNodes> implements Iterable<DiscoveryNode>, ToXContentFragment {

public static final DiscoveryNodes EMPTY_NODES = builder().build();

Expand Down Expand Up @@ -566,6 +573,66 @@ public String toString() {
return sb.toString();
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("nodes");
for (DiscoveryNode node : this) {
node.toXContent(builder, params);
}
builder.endObject();
Metadata.XContentContext context = Metadata.XContentContext.valueOf(params.param(CONTEXT_MODE_PARAM, CONTEXT_MODE_API));
if (context == Metadata.XContentContext.GATEWAY && clusterManagerNodeId != null) {
builder.field("cluster_manager", clusterManagerNodeId);
}
return builder;
}

public static DiscoveryNodes fromXContent(XContentParser parser) throws IOException {
Builder builder = new Builder();
if (parser.currentToken() == null) {
parser.nextToken();
}
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
parser.nextToken();
}
if (parser.currentToken() != XContentParser.Token.FIELD_NAME) {
throw new IllegalArgumentException("expected field name but got a " + parser.currentToken());
}
XContentParser.Token token;
String currentFieldName = parser.currentName();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
if ("nodes".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
String nodeId = currentFieldName;
DiscoveryNode node = DiscoveryNode.fromXContent(parser, nodeId);
builder.add(node);
}
}
} else {
throw new IllegalArgumentException("unexpected object field " + currentFieldName);
}
} else if (token.isValue()) {
if ("cluster_manager".equals(currentFieldName)) {
String clusterManagerNodeId = parser.text();
if (clusterManagerNodeId != null) {
builder.clusterManagerNodeId(clusterManagerNodeId);
}
} else {
throw new IllegalArgumentException("unexpected value field " + currentFieldName);
}
} else {
throw new IllegalArgumentException("unexpected token " + token);
}
}
return builder.build();
}

/**
* Delta between nodes.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,20 @@
package org.opensearch.cluster.node;

import org.opensearch.Version;
import org.opensearch.cluster.metadata.Metadata;
import org.opensearch.common.io.stream.BytesStreamOutput;
import org.opensearch.common.settings.Setting;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.xcontent.json.JsonXContent;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.transport.TransportAddress;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.node.remotestore.RemoteStoreNodeAttribute;
import org.opensearch.test.NodeRoles;
import org.opensearch.test.OpenSearchTestCase;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -53,6 +58,9 @@

import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonMap;
import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_API;
import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_GATEWAY;
import static org.opensearch.test.NodeRoles.nonRemoteClusterClientNode;
import static org.opensearch.test.NodeRoles.nonSearchNode;
import static org.opensearch.test.NodeRoles.remoteClusterClientNode;
Expand Down Expand Up @@ -249,4 +257,70 @@ public void testDiscoveryNodeIsSearchNode() {
final DiscoveryNode node = DiscoveryNode.createLocal(settingWithSearchRole, buildNewFakeTransportAddress(), "node");
assertThat(node.isSearchNode(), equalTo(true));
}

public void testToXContentInAPIMode() throws IOException {
final DiscoveryNode node = DiscoveryNode.createLocal(
Settings.EMPTY,
new TransportAddress(TransportAddress.META_ADDRESS, 9200),
"node_1"
);
XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint();
builder.startObject();
node.toXContent(builder, new ToXContent.MapParams(singletonMap(Metadata.CONTEXT_MODE_PARAM, CONTEXT_MODE_API)));
builder.endObject();

String expectedNodeAPUXContent = "{\n"
+ " \"node_1\" : {\n"
+ " \"name\" : \""
+ node.getName()
+ "\",\n"
+ " \"ephemeral_id\" : \""
+ node.getEphemeralId()
+ "\",\n"
+ " \"transport_address\" : \"0.0.0.0:9200\",\n"
+ " \"attributes\" : { }\n"
+ " }\n"
+ "}";

assertEquals(expectedNodeAPUXContent, builder.toString());
}

public void testToXContentInGatewayMode() throws IOException {
final DiscoveryNode node = DiscoveryNode.createLocal(
Settings.EMPTY,
new TransportAddress(TransportAddress.META_ADDRESS, 9200),
"node_1"
);
XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint();
builder.startObject();
node.toXContent(builder, new ToXContent.MapParams(singletonMap(Metadata.CONTEXT_MODE_PARAM, CONTEXT_MODE_GATEWAY)));
builder.endObject();

String expectedNodeAPUXContent = "{\n"
+ " \"node_1\" : {\n"
+ " \"name\" : \""
+ node.getName()
+ "\",\n"
+ " \"ephemeral_id\" : \""
+ node.getEphemeralId()
+ "\",\n"
+ " \"transport_address\" : \"0.0.0.0:9200\",\n"
+ " \"attributes\" : { },\n"
+ " \"host_name\" : \"0.0.0.0\",\n"
+ " \"host_address\" : \"0.0.0.0\",\n"
+ " \"version\" : \""
+ node.getVersion()
+ "\",\n"
+ " \"roles\" : [\n"
+ " \"cluster_manager\",\n"
+ " \"data\",\n"
+ " \"ingest\",\n"
+ " \"remote_cluster_client\"\n"
+ " ]\n"
+ " }\n"
+ "}";

assertEquals(expectedNodeAPUXContent, builder.toString());

}
}
Loading

0 comments on commit 7d1a599

Please sign in to comment.