HLRC: Convert xpack methods to client side objects (#40705)

This commit fixes a problem with BWC that was brought up in #40511. A
newer version of the code was emitting a new value for an enum to an
older version, and the older version could not handle that. It caused
the response to error. The MainResponse is now relaxed, and will accept
whatever values the server expose, and holds most of them as Strings
instead of complex objects.

Fixes #40511
This commit is contained in:
Michael Basnight 2019-04-04 09:49:12 -05:00
parent 9e8499d20b
commit fb5a0652a8
10 changed files with 361 additions and 38 deletions

View File

@ -44,8 +44,6 @@ import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.main.MainRequest;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.MultiSearchRequest;
@ -58,6 +56,8 @@ import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.core.MainRequest;
import org.elasticsearch.client.core.MainResponse;
import org.elasticsearch.client.core.MultiTermVectorsRequest;
import org.elasticsearch.client.core.MultiTermVectorsResponse;
import org.elasticsearch.client.core.TermVectorsRequest;

View File

@ -0,0 +1,25 @@
/*
* 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.client.core;
import org.elasticsearch.client.Validatable;
public class MainRequest implements Validatable {
}

View File

@ -0,0 +1,204 @@
/*
* 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.client.core;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.util.Objects;
public class MainResponse {
@SuppressWarnings("unchecked")
private static ConstructingObjectParser<MainResponse, Void> PARSER =
new ConstructingObjectParser<>(MainResponse.class.getName(), true,
args -> {
return new MainResponse((String) args[0], (Version) args[1], (String) args[2], (String) args[3], (String) args[4]);
}
);
static {
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("name"));
PARSER.declareObject(ConstructingObjectParser.constructorArg(), Version.PARSER, new ParseField("version"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("cluster_name"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("cluster_uuid"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("tagline"));
}
private final String nodeName;
private final Version version;
private final String clusterName;
private final String clusterUuid;
private final String tagline;
public MainResponse(String nodeName, Version version, String clusterName, String clusterUuid, String tagline) {
this.nodeName = nodeName;
this.version = version;
this.clusterName = clusterName;
this.clusterUuid = clusterUuid;
this.tagline = tagline;
}
public String getNodeName() {
return nodeName;
}
public Version getVersion() {
return version;
}
public String getClusterName() {
return clusterName;
}
public String getClusterUuid() {
return clusterUuid;
}
public String getTagline() {
return tagline;
}
public static MainResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MainResponse that = (MainResponse) o;
return nodeName.equals(that.nodeName) &&
version.equals(that.version) &&
clusterName.equals(that.clusterName) &&
clusterUuid.equals(that.clusterUuid) &&
tagline.equals(that.tagline);
}
@Override
public int hashCode() {
return Objects.hash(nodeName, version, clusterName, clusterUuid, tagline);
}
public static class Version {
@SuppressWarnings("unchecked")
private static ConstructingObjectParser<Version, Void> PARSER =
new ConstructingObjectParser<>(Version.class.getName(), true,
args -> {
return new Version((String) args[0], (String) args[1], (String) args[2], (String) args[3], (String) args[4],
(Boolean) args[5], (String) args[6], (String) args[7], (String) args[8]);
}
);
static {
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("number"));
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("build_flavor"));
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("build_type"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("build_hash"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("build_date"));
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), new ParseField("build_snapshot"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("lucene_version"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("minimum_wire_compatibility_version"));
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("minimum_index_compatibility_version"));
}
private final String number;
private final String buildFlavor;
private final String buildType;
private final String buildHash;
private final String buildDate;
private final boolean isSnapshot;
private final String luceneVersion;
private final String minimumWireCompatibilityVersion;
private final String minimumIndexCompatibilityVersion;
public Version(String number, String buildFlavor, String buildType, String buildHash, String buildDate, boolean isSnapshot,
String luceneVersion, String minimumWireCompatibilityVersion, String minimumIndexCompatibilityVersion) {
this.number = number;
this.buildFlavor = buildFlavor;
this.buildType = buildType;
this.buildHash = buildHash;
this.buildDate = buildDate;
this.isSnapshot = isSnapshot;
this.luceneVersion = luceneVersion;
this.minimumWireCompatibilityVersion = minimumWireCompatibilityVersion;
this.minimumIndexCompatibilityVersion = minimumIndexCompatibilityVersion;
}
public String getNumber() {
return number;
}
public String getBuildFlavor() {
return buildFlavor;
}
public String getBuildType() {
return buildType;
}
public String getBuildHash() {
return buildHash;
}
public String getBuildDate() {
return buildDate;
}
public boolean isSnapshot() {
return isSnapshot;
}
public String getLuceneVersion() {
return luceneVersion;
}
public String getMinimumWireCompatibilityVersion() {
return minimumWireCompatibilityVersion;
}
public String getMinimumIndexCompatibilityVersion() {
return minimumIndexCompatibilityVersion;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Version version = (Version) o;
return isSnapshot == version.isSnapshot &&
number.equals(version.number) &&
Objects.equals(buildFlavor, version.buildFlavor) &&
Objects.equals(buildType, version.buildType) &&
buildHash.equals(version.buildHash) &&
buildDate.equals(version.buildDate) &&
luceneVersion.equals(version.luceneVersion) &&
minimumWireCompatibilityVersion.equals(version.minimumWireCompatibilityVersion) &&
minimumIndexCompatibilityVersion.equals(version.minimumIndexCompatibilityVersion);
}
@Override
public int hashCode() {
return Objects.hash(number, buildFlavor, buildType, buildHash, buildDate, isSnapshot, luceneVersion,
minimumWireCompatibilityVersion, minimumIndexCompatibilityVersion);
}
}
}

View File

@ -228,7 +228,7 @@ public class CCRIT extends ESRestHighLevelClientTestCase {
assertTrue(putFollowResponse.isFollowIndexShardsAcked());
assertTrue(putFollowResponse.isIndexFollowingStarted());
final String clusterName = highLevelClient().info(RequestOptions.DEFAULT).getClusterName().value();
final String clusterName = highLevelClient().info(RequestOptions.DEFAULT).getClusterName();
final Request statsRequest = new Request("GET", "/follower/_stats");
final Response statsResponse = client().performRequest(statsRequest);

View File

@ -20,7 +20,7 @@
package org.elasticsearch.client;
import org.apache.http.client.methods.HttpGet;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.client.core.MainResponse;
import org.elasticsearch.client.xpack.XPackInfoRequest;
import org.elasticsearch.client.xpack.XPackInfoResponse;
import org.elasticsearch.client.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
@ -40,20 +40,20 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
MainResponse info = highLevelClient().info(RequestOptions.DEFAULT);
// compare with what the low level client outputs
Map<String, Object> infoAsMap = entityAsMap(adminClient().performRequest(new Request(HttpGet.METHOD_NAME, "/")));
assertEquals(infoAsMap.get("cluster_name"), info.getClusterName().value());
assertEquals(infoAsMap.get("cluster_name"), info.getClusterName());
assertEquals(infoAsMap.get("cluster_uuid"), info.getClusterUuid());
// only check node name existence, might be a different one from what was hit by low level client in multi-node cluster
assertNotNull(info.getNodeName());
@SuppressWarnings("unchecked")
Map<String, Object> versionMap = (Map<String, Object>) infoAsMap.get("version");
assertEquals(versionMap.get("build_flavor"), info.getBuild().flavor().displayName());
assertEquals(versionMap.get("build_type"), info.getBuild().type().displayName());
assertEquals(versionMap.get("build_hash"), info.getBuild().shortHash());
assertEquals(versionMap.get("build_date"), info.getBuild().date());
assertEquals(versionMap.get("build_snapshot"), info.getBuild().isSnapshot());
assertTrue(versionMap.get("number").toString().startsWith(info.getVersion().toString()));
assertEquals(versionMap.get("lucene_version"), info.getVersion().luceneVersion.toString());
assertEquals(versionMap.get("build_flavor"), info.getVersion().getBuildFlavor());
assertEquals(versionMap.get("build_type"), info.getVersion().getBuildType());
assertEquals(versionMap.get("build_hash"), info.getVersion().getBuildHash());
assertEquals(versionMap.get("build_date"), info.getVersion().getBuildDate());
assertEquals(versionMap.get("build_snapshot"), info.getVersion().isSnapshot());
assertTrue(versionMap.get("number").toString().startsWith(info.getVersion().getNumber()));
assertEquals(versionMap.get("lucene_version"), info.getVersion().getLuceneVersion());
}
public void testXPackInfo() throws IOException {
@ -64,7 +64,7 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
MainResponse mainResponse = highLevelClient().info(RequestOptions.DEFAULT);
assertEquals(mainResponse.getBuild().shortHash(), info.getBuildInfo().getHash());
assertEquals(mainResponse.getVersion().getBuildHash(), info.getBuildInfo().getHash());
assertEquals("trial", info.getLicenseInfo().getType());
assertEquals("trial", info.getLicenseInfo().getMode());
@ -84,7 +84,7 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
assertNotNull(ml.description());
assertTrue(ml.available());
assertTrue(ml.enabled());
assertEquals(mainResponse.getBuild().getQualifiedVersion(), ml.nativeCodeInfo().get("version").toString());
assertEquals(mainResponse.getVersion().getNumber(), ml.nativeCodeInfo().get("version").toString());
}
public void testXPackInfoEmptyRequest() throws IOException {

View File

@ -33,20 +33,18 @@ import org.apache.http.message.BasicRequestLine;
import org.apache.http.message.BasicStatusLine;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.Build;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.main.MainRequest;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchResponseSections;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.client.core.MainRequest;
import org.elasticsearch.client.core.MainResponse;
import org.elasticsearch.client.indexlifecycle.AllocateAction;
import org.elasticsearch.client.indexlifecycle.DeleteAction;
import org.elasticsearch.client.indexlifecycle.ForceMergeAction;
@ -57,7 +55,6 @@ import org.elasticsearch.client.indexlifecycle.RolloverAction;
import org.elasticsearch.client.indexlifecycle.SetPriorityAction;
import org.elasticsearch.client.indexlifecycle.ShrinkAction;
import org.elasticsearch.client.indexlifecycle.UnfollowAction;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
@ -176,9 +173,28 @@ public class RestHighLevelClientTests extends ESTestCase {
}
public void testInfo() throws IOException {
MainResponse testInfo = new MainResponse("nodeName", Version.CURRENT, new ClusterName("clusterName"), "clusterUuid",
Build.CURRENT);
mockResponse(testInfo);
MainResponse testInfo = new MainResponse("nodeName", new MainResponse.Version("number", "buildFlavor", "buildType", "buildHash",
"buildDate", true, "luceneVersion", "minimumWireCompatibilityVersion", "minimumIndexCompatibilityVersion"),
"clusterName", "clusterUuid", "You Know, for Search");
mockResponse((builder, params) -> {
// taken from the server side MainResponse
builder.field("name", testInfo.getNodeName());
builder.field("cluster_name", testInfo.getClusterName());
builder.field("cluster_uuid", testInfo.getClusterUuid());
builder.startObject("version")
.field("number", testInfo.getVersion().getNumber())
.field("build_flavor", testInfo.getVersion().getBuildFlavor())
.field("build_type", testInfo.getVersion().getBuildType())
.field("build_hash", testInfo.getVersion().getBuildHash())
.field("build_date", testInfo.getVersion().getBuildDate())
.field("build_snapshot", testInfo.getVersion().isSnapshot())
.field("lucene_version", testInfo.getVersion().getLuceneVersion())
.field("minimum_wire_compatibility_version", testInfo.getVersion().getMinimumWireCompatibilityVersion())
.field("minimum_index_compatibility_version", testInfo.getVersion().getMinimumIndexCompatibilityVersion())
.endObject();
builder.field("tagline", testInfo.getTagline());
return builder;
});
MainResponse receivedInfo = restHighLevelClient.info(RequestOptions.DEFAULT);
assertEquals(testInfo, receivedInfo);
}

View File

@ -0,0 +1,72 @@
/*
* 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.client.core;
import org.elasticsearch.Build;
import org.elasticsearch.Version;
import org.elasticsearch.client.AbstractResponseTestCase;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.VersionUtils;
import java.io.IOException;
import java.util.Date;
import static org.hamcrest.Matchers.equalTo;
public class MainResponseTests extends AbstractResponseTestCase<org.elasticsearch.action.main.MainResponse, MainResponse> {
@Override
protected org.elasticsearch.action.main.MainResponse createServerTestInstance() {
String clusterUuid = randomAlphaOfLength(10);
ClusterName clusterName = new ClusterName(randomAlphaOfLength(10));
String nodeName = randomAlphaOfLength(10);
final String date = new Date(randomNonNegativeLong()).toString();
Version version = VersionUtils.randomVersionBetween(random(), Version.V_6_0_1, Version.CURRENT);
Build build = new Build(
Build.Flavor.UNKNOWN, Build.Type.UNKNOWN, randomAlphaOfLength(8), date, randomBoolean(),
version.toString()
);
return new org.elasticsearch.action.main.MainResponse(nodeName, version, clusterName, clusterUuid , build);
}
@Override
protected MainResponse doParseToClientInstance(XContentParser parser) throws IOException {
return MainResponse.fromXContent(parser);
}
@Override
protected void assertInstances(org.elasticsearch.action.main.MainResponse serverTestInstance, MainResponse clientInstance) {
assertThat(serverTestInstance.getClusterName().value(), equalTo(clientInstance.getClusterName()));
assertThat(serverTestInstance.getClusterUuid(), equalTo(clientInstance.getClusterUuid()));
assertThat(serverTestInstance.getNodeName(), equalTo(clientInstance.getNodeName()));
assertThat("You Know, for Search", equalTo(clientInstance.getTagline()));
assertThat(serverTestInstance.getBuild().shortHash(), equalTo(clientInstance.getVersion().getBuildHash()));
assertThat(serverTestInstance.getVersion().toString(), equalTo(clientInstance.getVersion().getNumber()));
assertThat(serverTestInstance.getBuild().date(), equalTo(clientInstance.getVersion().getBuildDate()));
assertThat(serverTestInstance.getBuild().flavor().displayName(), equalTo(clientInstance.getVersion().getBuildFlavor()));
assertThat(serverTestInstance.getBuild().type().displayName(), equalTo(clientInstance.getVersion().getBuildType()));
assertThat(serverTestInstance.getVersion().luceneVersion.toString(), equalTo(clientInstance.getVersion().getLuceneVersion()));
assertThat(serverTestInstance.getVersion().minimumIndexCompatibilityVersion().toString(),
equalTo(clientInstance.getVersion().getMinimumIndexCompatibilityVersion()));
assertThat(serverTestInstance.getVersion().minimumCompatibilityVersion().toString(),
equalTo(clientInstance.getVersion().getMinimumWireCompatibilityVersion()));
}
}

View File

@ -425,7 +425,7 @@ public class CCRDocumentationIT extends ESRestHighLevelClientTestCase {
AcknowledgedResponse pauseFollowResponse = client.ccr().pauseFollow(pauseFollowRequest, RequestOptions.DEFAULT);
assertTrue(pauseFollowResponse.isAcknowledged());
final String followerCluster = highLevelClient().info(RequestOptions.DEFAULT).getClusterName().value();
final String followerCluster = highLevelClient().info(RequestOptions.DEFAULT).getClusterName();
final Request statsRequest = new Request("GET", "/follower/_stats");
final Response statsResponse = client().performRequest(statsRequest);
final ObjectPath statsObjectPath = ObjectPath.createFromResponse(statsResponse);

View File

@ -20,16 +20,13 @@
package org.elasticsearch.client.documentation;
import org.apache.http.HttpHost;
import org.elasticsearch.Build;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.LatchedActionListener;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.client.core.MainResponse;
import org.elasticsearch.client.xpack.XPackInfoRequest;
import org.elasticsearch.client.xpack.XPackInfoResponse;
import org.elasticsearch.client.xpack.XPackInfoResponse.BuildInfo;
@ -61,17 +58,31 @@ public class MiscellaneousDocumentationIT extends ESRestHighLevelClientTestCase
MainResponse response = client.info(RequestOptions.DEFAULT);
//end::main-execute
//tag::main-response
ClusterName clusterName = response.getClusterName(); // <1>
String clusterUuid = response.getClusterUuid(); // <2>
String nodeName = response.getNodeName(); // <3>
Version version = response.getVersion(); // <4>
Build build = response.getBuild(); // <5>
String clusterName = response.getClusterName();
String clusterUuid = response.getClusterUuid();
String nodeName = response.getNodeName();
MainResponse.Version version = response.getVersion();
String buildDate = version.getBuildDate();
String buildFlavor = version.getBuildFlavor();
String buildHash = version.getBuildHash();
String buildType = version.getBuildType();
String luceneVersion = version.getLuceneVersion();
String minimumIndexCompatibilityVersion= version.getMinimumIndexCompatibilityVersion();
String minimumWireCompatibilityVersion = version.getMinimumWireCompatibilityVersion();
String number = version.getNumber();
//end::main-response
assertNotNull(clusterName);
assertNotNull(clusterUuid);
assertNotNull(nodeName);
assertNotNull(version);
assertNotNull(build);
assertNotNull(buildDate);
assertNotNull(buildFlavor);
assertNotNull(buildHash);
assertNotNull(buildType);
assertNotNull(luceneVersion);
assertNotNull(minimumIndexCompatibilityVersion);
assertNotNull(minimumWireCompatibilityVersion);
assertNotNull(number);
}
}

View File

@ -20,8 +20,3 @@ The returned `MainResponse` provides various kinds of information about the clus
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[main-response]
--------------------------------------------------
<1> Retrieve the name of the cluster as a `ClusterName`
<2> Retrieve the unique identifier of the cluster
<3> Retrieve the name of the node the request has been executed on
<4> Retrieve the version of the node the request has been executed on
<5> Retrieve the build information of the node the request has been executed on