Skip to content

Commit a48d19d

Browse files
authored
Add remote info to the HLRC (#50482)
Unreverts the commit that added the remote info api to HLRC (#49657). The additional change to the original PR, is that `org.elasticsearch.client.cluster.RemoteConnectionInfo` now parses the initial_connect_timeout field as a string instead of a TimeValue instance. The reason that this is needed is because that the initial_connect_timeout field in the remote connection api is serialized for human consumption, but not for parsing purposes. Therefore the HLRC can't parse it correctly (which caused test failures in CI, but not in the PR CI :( ). The way this field is serialized needs to be changed in the remote connection api, but that is a breaking change. We should wait making this change until rest api versioning is introduced. Co-Authored-By: j-bean [email protected]
1 parent b7ac732 commit a48d19d

File tree

23 files changed

+773
-74
lines changed

23 files changed

+773
-74
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.elasticsearch.action.admin.cluster.settings.ClusterGetSettingsResponse;
2727
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
2828
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse;
29+
import org.elasticsearch.client.cluster.RemoteInfoRequest;
30+
import org.elasticsearch.client.cluster.RemoteInfoResponse;
2931
import org.elasticsearch.rest.RestStatus;
3032

3133
import java.io.IOException;
@@ -138,4 +140,33 @@ public Cancellable healthAsync(ClusterHealthRequest healthRequest, RequestOption
138140
return restHighLevelClient.performRequestAsyncAndParseEntity(healthRequest, ClusterRequestConverters::clusterHealth, options,
139141
ClusterHealthResponse::fromXContent, listener, singleton(RestStatus.REQUEST_TIMEOUT.getStatus()));
140142
}
143+
144+
/**
145+
* Get the remote cluster information using the Remote cluster info API.
146+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-remote-info.html"> Remote cluster info
147+
* API on elastic.co</a>
148+
* @param request the request
149+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
150+
* @return the response
151+
* @throws IOException in case there is a problem sending the request or parsing back the response
152+
*/
153+
public RemoteInfoResponse remoteInfo(RemoteInfoRequest request, RequestOptions options) throws IOException {
154+
return restHighLevelClient.performRequestAndParseEntity(request, ClusterRequestConverters::remoteInfo, options,
155+
RemoteInfoResponse::fromXContent, singleton(RestStatus.REQUEST_TIMEOUT.getStatus()));
156+
}
157+
158+
/**
159+
* Asynchronously get remote cluster information using the Remote cluster info API.
160+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-remote-info.html"> Remote cluster info
161+
* API on elastic.co</a>
162+
* @param request the request
163+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
164+
* @param listener the listener to be notified upon request completion
165+
* @return cancellable that may be used to cancel the request
166+
*/
167+
public Cancellable remoteInfoAsync(RemoteInfoRequest request, RequestOptions options,
168+
ActionListener<RemoteInfoResponse> listener) {
169+
return restHighLevelClient.performRequestAsyncAndParseEntity(request, ClusterRequestConverters::remoteInfo, options,
170+
RemoteInfoResponse::fromXContent, listener, singleton(RestStatus.REQUEST_TIMEOUT.getStatus()));
171+
}
141172
}

client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterRequestConverters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.action.admin.cluster.settings.ClusterGetSettingsRequest;
2626
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
2727
import org.elasticsearch.action.support.ActiveShardCount;
28+
import org.elasticsearch.client.cluster.RemoteInfoRequest;
2829
import org.elasticsearch.common.Strings;
2930

3031
import java.io.IOException;
@@ -76,4 +77,8 @@ static Request clusterHealth(ClusterHealthRequest healthRequest) {
7677
request.addParameters(params.asMap());
7778
return request;
7879
}
80+
81+
static Request remoteInfo(RemoteInfoRequest remoteInfoRequest) {
82+
return new Request(HttpGet.METHOD_NAME, "/_remote/info");
83+
}
7984
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client.cluster;
21+
22+
import java.util.Objects;
23+
24+
public class ProxyModeInfo implements RemoteConnectionInfo.ModeInfo {
25+
static final String NAME = "proxy";
26+
static final String ADDRESS = "address";
27+
static final String NUM_SOCKETS_CONNECTED = "num_sockets_connected";
28+
static final String MAX_SOCKET_CONNECTIONS = "max_socket_connections";
29+
private final String address;
30+
private final int maxSocketConnections;
31+
private final int numSocketsConnected;
32+
33+
ProxyModeInfo(String address, int maxSocketConnections, int numSocketsConnected) {
34+
this.address = address;
35+
this.maxSocketConnections = maxSocketConnections;
36+
this.numSocketsConnected = numSocketsConnected;
37+
}
38+
39+
@Override
40+
public boolean isConnected() {
41+
return numSocketsConnected > 0;
42+
}
43+
44+
@Override
45+
public String modeName() {
46+
return NAME;
47+
}
48+
49+
public String getAddress() {
50+
return address;
51+
}
52+
53+
public int getMaxSocketConnections() {
54+
return maxSocketConnections;
55+
}
56+
57+
public int getNumSocketsConnected() {
58+
return numSocketsConnected;
59+
}
60+
61+
@Override
62+
public boolean equals(Object o) {
63+
if (this == o) return true;
64+
if (o == null || getClass() != o.getClass()) return false;
65+
ProxyModeInfo otherProxy = (ProxyModeInfo) o;
66+
return maxSocketConnections == otherProxy.maxSocketConnections &&
67+
numSocketsConnected == otherProxy.numSocketsConnected &&
68+
Objects.equals(address, otherProxy.address);
69+
}
70+
71+
@Override
72+
public int hashCode() {
73+
return Objects.hash(address, maxSocketConnections, numSocketsConnected);
74+
}
75+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client.cluster;
21+
22+
import org.elasticsearch.common.ParseField;
23+
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
24+
import org.elasticsearch.common.xcontent.XContentParser;
25+
26+
import java.io.IOException;
27+
import java.util.List;
28+
import java.util.Objects;
29+
30+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
31+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
32+
33+
/**
34+
* This class encapsulates all remote cluster information to be rendered on
35+
* {@code _remote/info} requests.
36+
*/
37+
public final class RemoteConnectionInfo {
38+
private static final String CONNECTED = "connected";
39+
private static final String MODE = "mode";
40+
private static final String INITIAL_CONNECT_TIMEOUT = "initial_connect_timeout";
41+
private static final String SKIP_UNAVAILABLE = "skip_unavailable";
42+
43+
@SuppressWarnings("unchecked")
44+
private static final ConstructingObjectParser<RemoteConnectionInfo, String> PARSER = new ConstructingObjectParser<>(
45+
"RemoteConnectionInfoObjectParser",
46+
false,
47+
(args, clusterAlias) -> {
48+
String mode = (String) args[1];
49+
ModeInfo modeInfo;
50+
if (mode.equals(ProxyModeInfo.NAME)) {
51+
modeInfo = new ProxyModeInfo((String) args[4], (int) args[5], (int) args[6]);
52+
} else if (mode.equals(SniffModeInfo.NAME)) {
53+
modeInfo = new SniffModeInfo((List<String>) args[7], (int) args[8], (int) args[9]);
54+
} else {
55+
throw new IllegalArgumentException("mode cannot be " + mode);
56+
}
57+
return new RemoteConnectionInfo(clusterAlias,
58+
modeInfo,
59+
(String) args[2],
60+
(boolean) args[3]);
61+
});
62+
63+
static {
64+
PARSER.declareBoolean(constructorArg(), new ParseField(CONNECTED));
65+
PARSER.declareString(constructorArg(), new ParseField(MODE));
66+
PARSER.declareString(constructorArg(), new ParseField(INITIAL_CONNECT_TIMEOUT));
67+
PARSER.declareBoolean(constructorArg(), new ParseField(SKIP_UNAVAILABLE));
68+
69+
PARSER.declareString(optionalConstructorArg(), new ParseField(ProxyModeInfo.ADDRESS));
70+
PARSER.declareInt(optionalConstructorArg(), new ParseField(ProxyModeInfo.MAX_SOCKET_CONNECTIONS));
71+
PARSER.declareInt(optionalConstructorArg(), new ParseField(ProxyModeInfo.NUM_SOCKETS_CONNECTED));
72+
73+
PARSER.declareStringArray(optionalConstructorArg(), new ParseField(SniffModeInfo.SEEDS));
74+
PARSER.declareInt(optionalConstructorArg(), new ParseField(SniffModeInfo.MAX_CONNECTIONS_PER_CLUSTER));
75+
PARSER.declareInt(optionalConstructorArg(), new ParseField(SniffModeInfo.NUM_NODES_CONNECTED));
76+
}
77+
78+
private final ModeInfo modeInfo;
79+
// TODO: deprecate and remove this field in favor of initialConnectionTimeout field that is of type TimeValue.
80+
// When rest api versioning exists then change org.elasticsearch.transport.RemoteConnectionInfo to properly serialize
81+
// the initialConnectionTimeout field so that we can properly parse initialConnectionTimeout as TimeValue
82+
private final String initialConnectionTimeoutString;
83+
private final String clusterAlias;
84+
private final boolean skipUnavailable;
85+
86+
RemoteConnectionInfo(String clusterAlias, ModeInfo modeInfo, String initialConnectionTimeoutString, boolean skipUnavailable) {
87+
this.clusterAlias = clusterAlias;
88+
this.modeInfo = modeInfo;
89+
this.initialConnectionTimeoutString = initialConnectionTimeoutString;
90+
this.skipUnavailable = skipUnavailable;
91+
}
92+
93+
public boolean isConnected() {
94+
return modeInfo.isConnected();
95+
}
96+
97+
public String getClusterAlias() {
98+
return clusterAlias;
99+
}
100+
101+
public ModeInfo getModeInfo() {
102+
return modeInfo;
103+
}
104+
105+
public String getInitialConnectionTimeoutString() {
106+
return initialConnectionTimeoutString;
107+
}
108+
109+
public boolean isSkipUnavailable() {
110+
return skipUnavailable;
111+
}
112+
113+
public static RemoteConnectionInfo fromXContent(XContentParser parser, String clusterAlias) throws IOException {
114+
return PARSER.parse(parser, clusterAlias);
115+
}
116+
117+
@Override
118+
public boolean equals(Object o) {
119+
if (this == o) return true;
120+
if (o == null || getClass() != o.getClass()) return false;
121+
RemoteConnectionInfo that = (RemoteConnectionInfo) o;
122+
return skipUnavailable == that.skipUnavailable &&
123+
Objects.equals(modeInfo, that.modeInfo) &&
124+
Objects.equals(initialConnectionTimeoutString, that.initialConnectionTimeoutString) &&
125+
Objects.equals(clusterAlias, that.clusterAlias);
126+
}
127+
128+
@Override
129+
public int hashCode() {
130+
return Objects.hash(modeInfo, initialConnectionTimeoutString, clusterAlias, skipUnavailable);
131+
}
132+
133+
public interface ModeInfo {
134+
135+
boolean isConnected();
136+
137+
String modeName();
138+
}
139+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.cluster;
20+
21+
import org.elasticsearch.client.Validatable;
22+
23+
/**
24+
* The request object used by the Remote cluster info API.
25+
*/
26+
public final class RemoteInfoRequest implements Validatable {
27+
28+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.cluster;
20+
21+
import org.elasticsearch.common.xcontent.XContentParser;
22+
23+
import java.io.IOException;
24+
import java.util.ArrayList;
25+
import java.util.Collection;
26+
import java.util.List;
27+
28+
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
29+
30+
/**
31+
* A response to _remote/info API request.
32+
*/
33+
public final class RemoteInfoResponse {
34+
35+
private List<RemoteConnectionInfo> infos;
36+
37+
RemoteInfoResponse(Collection<RemoteConnectionInfo> infos) {
38+
this.infos = List.copyOf(infos);
39+
}
40+
41+
public List<RemoteConnectionInfo> getInfos() {
42+
return infos;
43+
}
44+
45+
public static RemoteInfoResponse fromXContent(XContentParser parser) throws IOException {
46+
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
47+
48+
List<RemoteConnectionInfo> infos = new ArrayList<>();
49+
50+
XContentParser.Token token;
51+
while ((token = parser.nextToken()) == XContentParser.Token.FIELD_NAME) {
52+
String clusterAlias = parser.currentName();
53+
RemoteConnectionInfo info = RemoteConnectionInfo.fromXContent(parser, clusterAlias);
54+
infos.add(info);
55+
}
56+
ensureExpectedToken(XContentParser.Token.END_OBJECT, token, parser::getTokenLocation);
57+
return new RemoteInfoResponse(infos);
58+
}
59+
}

0 commit comments

Comments
 (0)